authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-18 11:49:22-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-09-24 20:01:18-07:00
log328280b566e13e11a1e1e9cb28f3670b7cc8c030
treefa1601fa5f2ce01971aa646e6b15888e5c035f40
parent1bdcdbd996a73a7270d6668fca8893b4fc701280

move translate-c helpers


23 files changed, 10612 insertions(+), 11578 deletions(-)

lib/compiler/translate-c/MacroTranslator.zig created+1307
...@@ -0,0 +1,1307 @@
1const std = @import("std");
2const math = std.math;
3const mem = std.mem;
4const assert = std.debug.assert;
5
6const aro = @import("aro");
7const CToken = aro.Tokenizer.Token;
8
9const ast = @import("ast.zig");
10const builtins = @import("builtins.zig");
11const ZigNode = ast.Node;
12const ZigTag = ZigNode.Tag;
13const Scope = @import("Scope.zig");
14const Translator = @import("Translator.zig");
15
16const Error = Translator.Error;
17pub const ParseError = Error || error{ParseError};
18
19const MacroTranslator = @This();
20
21t: *Translator,
22macro: aro.Preprocessor.Macro,
23name: []const u8,
24
25tokens: []const CToken,
26source: []const u8,
27i: usize = 0,
28/// If an object macro references a global var it needs to be converted into
29/// an inline function.
30refs_var_decl: bool = false,
31
32fn peek(mt: *MacroTranslator) CToken.Id {
33 if (mt.i >= mt.tokens.len) return .eof;
34 return mt.tokens[mt.i].id;
35}
36
37fn eat(mt: *MacroTranslator, expected_id: CToken.Id) bool {
38 if (mt.peek() == expected_id) {
39 mt.i += 1;
40 return true;
41 }
42 return false;
43}
44
45fn expect(mt: *MacroTranslator, expected_id: CToken.Id) ParseError!void {
46 const next_id = mt.peek();
47 if (next_id != expected_id and !(expected_id == .identifier and next_id == .extended_identifier)) {
48 try mt.fail(
49 "unable to translate C expr: expected '{s}' instead got '{s}'",
50 .{ expected_id.symbol(), next_id.symbol() },
51 );
52 return error.ParseError;
53 }
54 mt.i += 1;
55}
56
57fn fail(mt: *MacroTranslator, comptime fmt: []const u8, args: anytype) !void {
58 return mt.t.failDeclExtra(&mt.t.global_scope.base, mt.macro.loc, mt.name, fmt, args);
59}
60
61fn tokSlice(mt: *const MacroTranslator) []const u8 {
62 const tok = mt.tokens[mt.i];
63 return mt.source[tok.start..tok.end];
64}
65
66pub fn transFnMacro(mt: *MacroTranslator) ParseError!void {
67 var block_scope = try Scope.Block.init(mt.t, &mt.t.global_scope.base, false);
68 defer block_scope.deinit();
69 const scope = &block_scope.base;
70
71 const fn_params = try mt.t.arena.alloc(ast.Payload.Param, mt.macro.params.len);
72 for (fn_params, mt.macro.params) |*param, param_name| {
73 const mangled_name = try block_scope.makeMangledName(param_name);
74 param.* = .{
75 .is_noalias = false,
76 .name = mangled_name,
77 .type = ZigTag.@"anytype".init(),
78 };
79 try block_scope.discardVariable(mangled_name);
80 }
81
82 const expr = try mt.parseCExpr(scope);
83 const last = mt.peek();
84 if (last != .eof)
85 return mt.fail("unable to translate C expr: unexpected token '{s}'", .{last.symbol()});
86
87 const typeof_arg = if (expr.castTag(.block)) |some| blk: {
88 const stmts = some.data.stmts;
89 const blk_last = stmts[stmts.len - 1];
90 const br = blk_last.castTag(.break_val).?;
91 break :blk br.data.val;
92 } else expr;
93
94 const return_type = ret: {
95 if (typeof_arg.castTag(.helper_call)) |some| {
96 if (std.mem.eql(u8, some.data.name, "cast")) {
97 break :ret some.data.args[0];
98 }
99 }
100 if (typeof_arg.castTag(.std_mem_zeroinit)) |some| break :ret some.data.lhs;
101 if (typeof_arg.castTag(.std_mem_zeroes)) |some| break :ret some.data;
102 break :ret try ZigTag.typeof.create(mt.t.arena, typeof_arg);
103 };
104
105 const return_expr = try ZigTag.@"return".create(mt.t.arena, expr);
106 try block_scope.statements.append(mt.t.gpa, return_expr);
107
108 const fn_decl = try ZigTag.pub_inline_fn.create(mt.t.arena, .{
109 .name = mt.name,
110 .params = fn_params,
111 .return_type = return_type,
112 .body = try block_scope.complete(),
113 });
114 try mt.t.addTopLevelDecl(mt.name, fn_decl);
115}
116
117pub fn transMacro(mt: *MacroTranslator) ParseError!void {
118 const scope = &mt.t.global_scope.base;
119
120 // Check if the macro only uses other blank macros.
121 while (true) {
122 switch (mt.peek()) {
123 .identifier, .extended_identifier => {
124 if (mt.t.global_scope.blank_macros.contains(mt.tokSlice())) {
125 mt.i += 1;
126 continue;
127 }
128 },
129 .eof, .nl => {
130 try mt.t.global_scope.blank_macros.put(mt.t.gpa, mt.name, {});
131 const init_node = try ZigTag.string_literal.create(mt.t.arena, "\"\"");
132 const var_decl = try ZigTag.pub_var_simple.create(mt.t.arena, .{ .name = mt.name, .init = init_node });
133 try mt.t.addTopLevelDecl(mt.name, var_decl);
134 return;
135 },
136 else => {},
137 }
138 break;
139 }
140
141 const init_node = try mt.parseCExpr(scope);
142 const last = mt.peek();
143 if (last != .eof)
144 return mt.fail("unable to translate C expr: unexpected token '{s}'", .{last.symbol()});
145
146 const node = node: {
147 const var_decl = try ZigTag.pub_var_simple.create(mt.t.arena, .{ .name = mt.name, .init = init_node });
148
149 if (mt.t.getFnProto(var_decl)) |proto_node| {
150 // If a macro aliases a global variable which is a function pointer, we conclude that
151 // the macro is intended to represent a function that assumes the function pointer
152 // variable is non-null and calls it.
153 break :node try mt.createMacroFn(mt.name, var_decl, proto_node);
154 } else if (mt.refs_var_decl) {
155 const return_type = try ZigTag.typeof.create(mt.t.arena, init_node);
156 const return_expr = try ZigTag.@"return".create(mt.t.arena, init_node);
157 const block = try ZigTag.block_single.create(mt.t.arena, return_expr);
158
159 const loc_str = try mt.t.locStr(mt.macro.loc);
160 const value = try std.fmt.allocPrint(mt.t.arena, "\n// {s}: warning: macro '{s}' contains a runtime value, translated to function", .{ loc_str, mt.name });
161 try scope.appendNode(try ZigTag.warning.create(mt.t.arena, value));
162
163 break :node try ZigTag.pub_inline_fn.create(mt.t.arena, .{
164 .name = mt.name,
165 .params = &.{},
166 .return_type = return_type,
167 .body = block,
168 });
169 }
170
171 break :node var_decl;
172 };
173
174 try mt.t.addTopLevelDecl(mt.name, node);
175}
176
177fn createMacroFn(mt: *MacroTranslator, name: []const u8, ref: ZigNode, proto_alias: *ast.Payload.Func) !ZigNode {
178 var fn_params = std.ArrayList(ast.Payload.Param).init(mt.t.gpa);
179 defer fn_params.deinit();
180
181 var block_scope = try Scope.Block.init(mt.t, &mt.t.global_scope.base, false);
182 defer block_scope.deinit();
183
184 for (proto_alias.data.params) |param| {
185 const param_name = try block_scope.makeMangledName(param.name orelse "arg");
186
187 try fn_params.append(.{
188 .name = param_name,
189 .type = param.type,
190 .is_noalias = param.is_noalias,
191 });
192 }
193
194 const init = if (ref.castTag(.var_decl)) |v|
195 v.data.init.?
196 else if (ref.castTag(.var_simple) orelse ref.castTag(.pub_var_simple)) |v|
197 v.data.init
198 else
199 unreachable;
200
201 const unwrap_expr = try ZigTag.unwrap.create(mt.t.arena, init);
202 const args = try mt.t.arena.alloc(ZigNode, fn_params.items.len);
203 for (fn_params.items, 0..) |param, i| {
204 args[i] = try ZigTag.identifier.create(mt.t.arena, param.name.?);
205 }
206 const call_expr = try ZigTag.call.create(mt.t.arena, .{
207 .lhs = unwrap_expr,
208 .args = args,
209 });
210 const return_expr = try ZigTag.@"return".create(mt.t.arena, call_expr);
211 const block = try ZigTag.block_single.create(mt.t.arena, return_expr);
212
213 return ZigTag.pub_inline_fn.create(mt.t.arena, .{
214 .name = name,
215 .params = try mt.t.arena.dupe(ast.Payload.Param, fn_params.items),
216 .return_type = proto_alias.data.return_type,
217 .body = block,
218 });
219}
220
221fn parseCExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
222 // TODO parseCAssignExpr here
223 var block_scope = try Scope.Block.init(mt.t, scope, true);
224 defer block_scope.deinit();
225
226 const node = try mt.parseCCondExpr(&block_scope.base);
227 if (!mt.eat(.comma)) return node;
228
229 var last = node;
230 while (true) {
231 // suppress result
232 const ignore = try ZigTag.discard.create(mt.t.arena, .{ .should_skip = false, .value = last });
233 try block_scope.statements.append(mt.t.gpa, ignore);
234
235 last = try mt.parseCCondExpr(&block_scope.base);
236 if (!mt.eat(.comma)) break;
237 }
238
239 const break_node = try ZigTag.break_val.create(mt.t.arena, .{
240 .label = block_scope.label,
241 .val = last,
242 });
243 try block_scope.statements.append(mt.t.gpa, break_node);
244 return try block_scope.complete();
245}
246
247fn parseCNumLit(mt: *MacroTranslator) ParseError!ZigNode {
248 const lit_bytes = mt.tokSlice();
249 mt.i += 1;
250
251 var bytes = try std.ArrayListUnmanaged(u8).initCapacity(mt.t.arena, lit_bytes.len + 3);
252
253 const prefix = aro.Tree.Token.NumberPrefix.fromString(lit_bytes);
254 switch (prefix) {
255 .binary => bytes.appendSliceAssumeCapacity("0b"),
256 .octal => bytes.appendSliceAssumeCapacity("0o"),
257 .hex => bytes.appendSliceAssumeCapacity("0x"),
258 .decimal => {},
259 }
260
261 const after_prefix = lit_bytes[prefix.stringLen()..];
262 const after_int = for (after_prefix, 0..) |c, i| switch (c) {
263 '.' => {
264 if (i == 0) {
265 bytes.appendAssumeCapacity('0');
266 }
267 break after_prefix[i..];
268 },
269 'e', 'E' => {
270 if (prefix != .hex) break after_prefix[i..];
271 bytes.appendAssumeCapacity(c);
272 },
273 'p', 'P' => break after_prefix[i..],
274 '0'...'9', 'a'...'d', 'A'...'D', 'f', 'F' => {
275 if (!prefix.digitAllowed(c)) break after_prefix[i..];
276 bytes.appendAssumeCapacity(c);
277 },
278 '\'' => {
279 bytes.appendAssumeCapacity('_');
280 },
281 else => break after_prefix[i..],
282 } else "";
283
284 const after_frac = frac: {
285 if (after_int.len == 0 or after_int[0] != '.') break :frac after_int;
286 bytes.appendAssumeCapacity('.');
287 for (after_int[1..], 1..) |c, i| {
288 if (c == '\'') {
289 bytes.appendAssumeCapacity('_');
290 continue;
291 }
292 if (!prefix.digitAllowed(c)) break :frac after_int[i..];
293 bytes.appendAssumeCapacity(c);
294 }
295 break :frac "";
296 };
297
298 const suffix_str = exponent: {
299 if (after_frac.len == 0) break :exponent after_frac;
300 switch (after_frac[0]) {
301 'e', 'E' => {},
302 'p', 'P' => if (prefix != .hex) break :exponent after_frac,
303 else => break :exponent after_frac,
304 }
305 bytes.appendAssumeCapacity(after_frac[0]);
306 for (after_frac[1..], 1..) |c, i| switch (c) {
307 '+', '-', '0'...'9' => {
308 bytes.appendAssumeCapacity(c);
309 },
310 '\'' => {
311 bytes.appendAssumeCapacity('_');
312 },
313 else => break :exponent after_frac[i..],
314 };
315 break :exponent "";
316 };
317
318 const is_float = after_int.len != suffix_str.len;
319 const suffix = aro.Tree.Token.NumberSuffix.fromString(suffix_str, if (is_float) .float else .int) orelse {
320 try mt.fail("invalid number suffix: '{s}'", .{suffix_str});
321 return error.ParseError;
322 };
323 if (suffix.isImaginary()) {
324 try mt.fail("TODO: imaginary literals", .{});
325 return error.ParseError;
326 }
327 if (suffix.isBitInt()) {
328 try mt.fail("TODO: _BitInt literals", .{});
329 return error.ParseError;
330 }
331
332 if (is_float) {
333 const type_node = try ZigTag.type.create(mt.t.arena, switch (suffix) {
334 .F16 => "f16",
335 .F => "f32",
336 .None => "f64",
337 .L => "c_longdouble",
338 .W => "f80",
339 .Q, .F128 => "f128",
340 else => unreachable,
341 });
342 const rhs = try ZigTag.float_literal.create(mt.t.arena, bytes.items);
343 return ZigTag.as.create(mt.t.arena, .{ .lhs = type_node, .rhs = rhs });
344 } else {
345 const type_node = try ZigTag.type.create(mt.t.arena, switch (suffix) {
346 .None => "c_int",
347 .U => "c_uint",
348 .L => "c_long",
349 .UL => "c_ulong",
350 .LL => "c_longlong",
351 .ULL => "c_ulonglong",
352 else => unreachable,
353 });
354 const value = std.fmt.parseInt(i128, bytes.items, 0) catch math.maxInt(i128);
355
356 // make the output less noisy by skipping promoteIntLiteral where
357 // it's guaranteed to not be required because of C standard type constraints
358 const guaranteed_to_fit = switch (suffix) {
359 .None => math.cast(i16, value) != null,
360 .U => math.cast(u16, value) != null,
361 .L => math.cast(i32, value) != null,
362 .UL => math.cast(u32, value) != null,
363 .LL => math.cast(i64, value) != null,
364 .ULL => math.cast(u64, value) != null,
365 else => unreachable,
366 };
367
368 const literal_node = try ZigTag.integer_literal.create(mt.t.arena, bytes.items);
369 if (guaranteed_to_fit) {
370 return ZigTag.as.create(mt.t.arena, .{ .lhs = type_node, .rhs = literal_node });
371 } else {
372 return mt.t.createHelperCallNode(.promoteIntLiteral, &.{ type_node, literal_node, try ZigTag.enum_literal.create(mt.t.arena, @tagName(prefix)) });
373 }
374 }
375}
376
377fn zigifyEscapeSequences(mt: *MacroTranslator, slice: []const u8) ![]const u8 {
378 var source = slice;
379 for (source, 0..) |c, i| {
380 if (c == '\"' or c == '\'') {
381 source = source[i..];
382 break;
383 }
384 }
385 for (source) |c| {
386 if (c == '\\' or c == '\t') {
387 break;
388 }
389 } else return source;
390 const bytes = try mt.t.arena.alloc(u8, source.len * 2);
391 var state: enum {
392 start,
393 escape,
394 hex,
395 octal,
396 } = .start;
397 var i: usize = 0;
398 var count: u8 = 0;
399 var num: u8 = 0;
400 for (source) |c| {
401 switch (state) {
402 .escape => {
403 switch (c) {
404 'n', 'r', 't', '\\', '\'', '\"' => {
405 bytes[i] = c;
406 },
407 '0'...'7' => {
408 count += 1;
409 num += c - '0';
410 state = .octal;
411 bytes[i] = 'x';
412 },
413 'x' => {
414 state = .hex;
415 bytes[i] = 'x';
416 },
417 'a' => {
418 bytes[i] = 'x';
419 i += 1;
420 bytes[i] = '0';
421 i += 1;
422 bytes[i] = '7';
423 },
424 'b' => {
425 bytes[i] = 'x';
426 i += 1;
427 bytes[i] = '0';
428 i += 1;
429 bytes[i] = '8';
430 },
431 'f' => {
432 bytes[i] = 'x';
433 i += 1;
434 bytes[i] = '0';
435 i += 1;
436 bytes[i] = 'C';
437 },
438 'v' => {
439 bytes[i] = 'x';
440 i += 1;
441 bytes[i] = '0';
442 i += 1;
443 bytes[i] = 'B';
444 },
445 '?' => {
446 i -= 1;
447 bytes[i] = '?';
448 },
449 'u', 'U' => {
450 try mt.fail("macro tokenizing failed: TODO unicode escape sequences", .{});
451 return error.ParseError;
452 },
453 else => {
454 try mt.fail("macro tokenizing failed: unknown escape sequence", .{});
455 return error.ParseError;
456 },
457 }
458 i += 1;
459 if (state == .escape)
460 state = .start;
461 },
462 .start => {
463 if (c == '\t') {
464 bytes[i] = '\\';
465 i += 1;
466 bytes[i] = 't';
467 i += 1;
468 continue;
469 }
470 if (c == '\\') {
471 state = .escape;
472 }
473 bytes[i] = c;
474 i += 1;
475 },
476 .hex => {
477 switch (c) {
478 '0'...'9' => {
479 num = std.math.mul(u8, num, 16) catch {
480 try mt.fail("macro tokenizing failed: hex literal overflowed", .{});
481 return error.ParseError;
482 };
483 num += c - '0';
484 },
485 'a'...'f' => {
486 num = std.math.mul(u8, num, 16) catch {
487 try mt.fail("macro tokenizing failed: hex literal overflowed", .{});
488 return error.ParseError;
489 };
490 num += c - 'a' + 10;
491 },
492 'A'...'F' => {
493 num = std.math.mul(u8, num, 16) catch {
494 try mt.fail("macro tokenizing failed: hex literal overflowed", .{});
495 return error.ParseError;
496 };
497 num += c - 'A' + 10;
498 },
499 else => {
500 i += std.fmt.printInt(bytes[i..], num, 16, .lower, .{ .fill = '0', .width = 2 });
501 num = 0;
502 if (c == '\\')
503 state = .escape
504 else
505 state = .start;
506 bytes[i] = c;
507 i += 1;
508 },
509 }
510 },
511 .octal => {
512 const accept_digit = switch (c) {
513 // The maximum length of a octal literal is 3 digits
514 '0'...'7' => count < 3,
515 else => false,
516 };
517
518 if (accept_digit) {
519 count += 1;
520 num = std.math.mul(u8, num, 8) catch {
521 try mt.fail("macro tokenizing failed: octal literal overflowed", .{});
522 return error.ParseError;
523 };
524 num += c - '0';
525 } else {
526 i += std.fmt.printInt(bytes[i..], num, 16, .lower, .{ .fill = '0', .width = 2 });
527 num = 0;
528 count = 0;
529 if (c == '\\')
530 state = .escape
531 else
532 state = .start;
533 bytes[i] = c;
534 i += 1;
535 }
536 },
537 }
538 }
539 if (state == .hex or state == .octal) {
540 i += std.fmt.printInt(bytes[i..], num, 16, .lower, .{ .fill = '0', .width = 2 });
541 }
542
543 return bytes[0..i];
544}
545
546/// non-ASCII characters (mt > 127) are also treated as non-printable by fmtSliceEscapeLower.
547/// If a C string literal or char literal in a macro is not valid UTF-8, we need to escape
548/// non-ASCII characters so that the Zig source we output will itself be UTF-8.
549fn escapeUnprintables(mt: *MacroTranslator) ![]const u8 {
550 const slice = mt.tokSlice();
551 mt.i += 1;
552
553 const zigified = try mt.zigifyEscapeSequences(slice);
554 if (std.unicode.utf8ValidateSlice(zigified)) return zigified;
555
556 const formatter = std.ascii.hexEscape(zigified, .lower);
557 const encoded_size = @as(usize, @intCast(std.fmt.count("{f}", .{formatter})));
558 const output = try mt.t.arena.alloc(u8, encoded_size);
559 return std.fmt.bufPrint(output, "{f}", .{formatter}) catch |err| switch (err) {
560 error.NoSpaceLeft => unreachable,
561 else => |e| return e,
562 };
563}
564
565fn parseCPrimaryExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
566 const tok = mt.peek();
567 switch (tok) {
568 .char_literal,
569 .char_literal_utf_8,
570 .char_literal_utf_16,
571 .char_literal_utf_32,
572 .char_literal_wide,
573 => {
574 const slice = mt.tokSlice();
575 if (slice[0] != '\'' or slice[1] == '\\' or slice.len == 3) {
576 return ZigTag.char_literal.create(mt.t.arena, try mt.escapeUnprintables());
577 } else {
578 mt.i += 1;
579
580 const str = try std.fmt.allocPrint(mt.t.arena, "0x{x}", .{slice[1 .. slice.len - 1]});
581 return ZigTag.integer_literal.create(mt.t.arena, str);
582 }
583 },
584 .string_literal,
585 .string_literal_utf_16,
586 .string_literal_utf_8,
587 .string_literal_utf_32,
588 .string_literal_wide,
589 => return ZigTag.string_literal.create(mt.t.arena, try mt.escapeUnprintables()),
590 .pp_num => return mt.parseCNumLit(),
591 .l_paren => {
592 mt.i += 1;
593 const inner_node = try mt.parseCExpr(scope);
594
595 try mt.expect(.r_paren);
596 return inner_node;
597 },
598 .macro_param, .macro_param_no_expand => {
599 const param = mt.macro.params[mt.tokens[mt.i].end];
600 mt.i += 1;
601
602 const mangled_name = scope.getAlias(param) orelse param;
603 return try ZigTag.identifier.create(mt.t.arena, mangled_name);
604 },
605 .identifier, .extended_identifier => {
606 const slice = mt.tokSlice();
607 mt.i += 1;
608
609 const mangled_name = scope.getAlias(slice) orelse slice;
610 if (Translator.builtin_typedef_map.get(mangled_name)) |ty| {
611 return ZigTag.type.create(mt.t.arena, ty);
612 }
613 if (builtins.map.get(mangled_name)) |builtin| {
614 const builtin_identifier = try ZigTag.identifier.create(mt.t.arena, "__builtin");
615 return ZigTag.field_access.create(mt.t.arena, .{
616 .lhs = builtin_identifier,
617 .field_name = builtin.name,
618 });
619 }
620
621 const identifier = try ZigTag.identifier.create(mt.t.arena, mangled_name);
622 scope.skipVariableDiscard(mangled_name);
623 refs_var: {
624 const ident_node = mt.t.global_scope.sym_table.get(slice) orelse break :refs_var;
625 const var_decl_node = ident_node.castTag(.var_decl) orelse break :refs_var;
626 if (!var_decl_node.data.is_const) mt.refs_var_decl = true;
627 }
628 return identifier;
629 },
630 else => {},
631 }
632
633 // for handling type macros (EVIL)
634 // TODO maybe detect and treat type macros as typedefs in parseCSpecifierQualifierList?
635 if (try mt.parseCTypeName(scope, true)) |type_name| {
636 return type_name;
637 }
638
639 try mt.fail("unable to translate C expr: unexpected token '{s}'", .{tok.symbol()});
640 return error.ParseError;
641}
642
643fn macroIntFromBool(mt: *MacroTranslator, node: ZigNode) !ZigNode {
644 if (!node.isBoolRes()) return node;
645
646 return ZigTag.int_from_bool.create(mt.t.arena, node);
647}
648
649fn macroIntToBool(mt: *MacroTranslator, node: ZigNode) !ZigNode {
650 if (node.isBoolRes()) return node;
651
652 if (node.tag() == .string_literal) {
653 // @intFromPtr(node) != 0
654 const int_from_ptr = try ZigTag.int_from_ptr.create(mt.t.arena, node);
655 return ZigTag.not_equal.create(mt.t.arena, .{ .lhs = int_from_ptr, .rhs = ZigTag.zero_literal.init() });
656 }
657 // node != 0
658 return ZigTag.not_equal.create(mt.t.arena, .{ .lhs = node, .rhs = ZigTag.zero_literal.init() });
659}
660
661fn parseCCondExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
662 const node = try mt.parseCOrExpr(scope);
663 if (!mt.eat(.question_mark)) return node;
664
665 const then_body = try mt.parseCOrExpr(scope);
666 try mt.expect(.colon);
667 const else_body = try mt.parseCCondExpr(scope);
668 return ZigTag.@"if".create(mt.t.arena, .{ .cond = node, .then = then_body, .@"else" = else_body });
669}
670
671fn parseCOrExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
672 var node = try mt.parseCAndExpr(scope);
673 while (mt.eat(.pipe_pipe)) {
674 const lhs = try mt.macroIntToBool(node);
675 const rhs = try mt.macroIntToBool(try mt.parseCAndExpr(scope));
676 node = try ZigTag.@"or".create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
677 }
678 return node;
679}
680
681fn parseCAndExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
682 var node = try mt.parseCBitOrExpr(scope);
683 while (mt.eat(.ampersand_ampersand)) {
684 const lhs = try mt.macroIntToBool(node);
685 const rhs = try mt.macroIntToBool(try mt.parseCBitOrExpr(scope));
686 node = try ZigTag.@"and".create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
687 }
688 return node;
689}
690
691fn parseCBitOrExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
692 var node = try mt.parseCBitXorExpr(scope);
693 while (mt.eat(.pipe)) {
694 const lhs = try mt.macroIntFromBool(node);
695 const rhs = try mt.macroIntFromBool(try mt.parseCBitXorExpr(scope));
696 node = try ZigTag.bit_or.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
697 }
698 return node;
699}
700
701fn parseCBitXorExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
702 var node = try mt.parseCBitAndExpr(scope);
703 while (mt.eat(.caret)) {
704 const lhs = try mt.macroIntFromBool(node);
705 const rhs = try mt.macroIntFromBool(try mt.parseCBitAndExpr(scope));
706 node = try ZigTag.bit_xor.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
707 }
708 return node;
709}
710
711fn parseCBitAndExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
712 var node = try mt.parseCEqExpr(scope);
713 while (mt.eat(.ampersand)) {
714 const lhs = try mt.macroIntFromBool(node);
715 const rhs = try mt.macroIntFromBool(try mt.parseCEqExpr(scope));
716 node = try ZigTag.bit_and.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
717 }
718 return node;
719}
720
721fn parseCEqExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
722 var node = try mt.parseCRelExpr(scope);
723 while (true) {
724 switch (mt.peek()) {
725 .bang_equal => {
726 mt.i += 1;
727 const lhs = try mt.macroIntFromBool(node);
728 const rhs = try mt.macroIntFromBool(try mt.parseCRelExpr(scope));
729 node = try ZigTag.not_equal.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
730 },
731 .equal_equal => {
732 mt.i += 1;
733 const lhs = try mt.macroIntFromBool(node);
734 const rhs = try mt.macroIntFromBool(try mt.parseCRelExpr(scope));
735 node = try ZigTag.equal.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
736 },
737 else => return node,
738 }
739 }
740}
741
742fn parseCRelExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
743 var node = try mt.parseCShiftExpr(scope);
744 while (true) {
745 switch (mt.peek()) {
746 .angle_bracket_right => {
747 mt.i += 1;
748 const lhs = try mt.macroIntFromBool(node);
749 const rhs = try mt.macroIntFromBool(try mt.parseCShiftExpr(scope));
750 node = try ZigTag.greater_than.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
751 },
752 .angle_bracket_right_equal => {
753 mt.i += 1;
754 const lhs = try mt.macroIntFromBool(node);
755 const rhs = try mt.macroIntFromBool(try mt.parseCShiftExpr(scope));
756 node = try ZigTag.greater_than_equal.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
757 },
758 .angle_bracket_left => {
759 mt.i += 1;
760 const lhs = try mt.macroIntFromBool(node);
761 const rhs = try mt.macroIntFromBool(try mt.parseCShiftExpr(scope));
762 node = try ZigTag.less_than.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
763 },
764 .angle_bracket_left_equal => {
765 mt.i += 1;
766 const lhs = try mt.macroIntFromBool(node);
767 const rhs = try mt.macroIntFromBool(try mt.parseCShiftExpr(scope));
768 node = try ZigTag.less_than_equal.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
769 },
770 else => return node,
771 }
772 }
773}
774
775fn parseCShiftExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
776 var node = try mt.parseCAddSubExpr(scope);
777 while (true) {
778 switch (mt.peek()) {
779 .angle_bracket_angle_bracket_left => {
780 mt.i += 1;
781 const lhs = try mt.macroIntFromBool(node);
782 const rhs = try mt.macroIntFromBool(try mt.parseCAddSubExpr(scope));
783 node = try ZigTag.shl.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
784 },
785 .angle_bracket_angle_bracket_right => {
786 mt.i += 1;
787 const lhs = try mt.macroIntFromBool(node);
788 const rhs = try mt.macroIntFromBool(try mt.parseCAddSubExpr(scope));
789 node = try ZigTag.shr.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
790 },
791 else => return node,
792 }
793 }
794}
795
796fn parseCAddSubExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
797 var node = try mt.parseCMulExpr(scope);
798 while (true) {
799 switch (mt.peek()) {
800 .plus => {
801 mt.i += 1;
802 const lhs = try mt.macroIntFromBool(node);
803 const rhs = try mt.macroIntFromBool(try mt.parseCMulExpr(scope));
804 node = try ZigTag.add.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
805 },
806 .minus => {
807 mt.i += 1;
808 const lhs = try mt.macroIntFromBool(node);
809 const rhs = try mt.macroIntFromBool(try mt.parseCMulExpr(scope));
810 node = try ZigTag.sub.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
811 },
812 else => return node,
813 }
814 }
815}
816
817fn parseCMulExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
818 var node = try mt.parseCCastExpr(scope);
819 while (true) {
820 switch (mt.peek()) {
821 .asterisk => {
822 mt.i += 1;
823 const lhs = try mt.macroIntFromBool(node);
824 const rhs = try mt.macroIntFromBool(try mt.parseCCastExpr(scope));
825 node = try ZigTag.mul.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
826 },
827 .slash => {
828 mt.i += 1;
829 const lhs = try mt.macroIntFromBool(node);
830 const rhs = try mt.macroIntFromBool(try mt.parseCCastExpr(scope));
831 node = try mt.t.createHelperCallNode(.div, &.{ lhs, rhs });
832 },
833 .percent => {
834 mt.i += 1;
835 const lhs = try mt.macroIntFromBool(node);
836 const rhs = try mt.macroIntFromBool(try mt.parseCCastExpr(scope));
837 node = try mt.t.createHelperCallNode(.rem, &.{ lhs, rhs });
838 },
839 else => return node,
840 }
841 }
842}
843
844fn parseCCastExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
845 if (mt.eat(.l_paren)) {
846 if (try mt.parseCTypeName(scope, true)) |type_name| {
847 while (true) {
848 const next_tok = mt.peek();
849 if (next_tok == .r_paren) {
850 mt.i += 1;
851 break;
852 }
853 // Skip trailing blank defined before the RParen.
854 if ((next_tok == .identifier or next_tok == .extended_identifier) and
855 mt.t.global_scope.blank_macros.contains(mt.tokSlice()))
856 {
857 mt.i += 1;
858 continue;
859 }
860
861 try mt.fail(
862 "unable to translate C expr: expected ')' instead got '{s}'",
863 .{next_tok.symbol()},
864 );
865 return error.ParseError;
866 }
867 if (mt.peek() == .l_brace) {
868 // initializer list
869 return mt.parseCPostfixExpr(scope, type_name);
870 }
871 const node_to_cast = try mt.parseCCastExpr(scope);
872 return mt.t.createHelperCallNode(.cast, &.{ type_name, node_to_cast });
873 }
874 mt.i -= 1; // l_paren
875 }
876 return mt.parseCUnaryExpr(scope);
877}
878
879// allow_fail is set when unsure if we are parsing a type-name
880fn parseCTypeName(mt: *MacroTranslator, scope: *Scope, allow_fail: bool) ParseError!?ZigNode {
881 if (try mt.parseCSpecifierQualifierList(scope, allow_fail)) |node| {
882 return try mt.parseCAbstractDeclarator(node);
883 }
884 return null;
885}
886
887fn parseCSpecifierQualifierList(mt: *MacroTranslator, scope: *Scope, allow_fail: bool) ParseError!?ZigNode {
888 const tok = mt.peek();
889 switch (tok) {
890 .macro_param, .macro_param_no_expand => {
891 const param = mt.macro.params[mt.tokens[mt.i].end];
892
893 // Assume that this is only a cast if the next token is ')'
894 // e.g. param)identifier
895 if (allow_fail and (mt.macro.tokens.len < mt.i + 3 or
896 mt.macro.tokens[mt.i + 1].id != .r_paren or
897 mt.macro.tokens[mt.i + 2].id != .identifier))
898 return null;
899
900 mt.i += 1;
901 const mangled_name = scope.getAlias(param) orelse param;
902 return try ZigTag.identifier.create(mt.t.arena, mangled_name);
903 },
904 .identifier, .extended_identifier => {
905 const slice = mt.tokSlice();
906 const mangled_name = scope.getAlias(slice) orelse slice;
907
908 if (mt.t.global_scope.blank_macros.contains(slice)) {
909 mt.i += 1;
910 return try mt.parseCSpecifierQualifierList(scope, allow_fail);
911 }
912
913 if (!allow_fail or mt.t.typedefs.contains(mangled_name)) {
914 mt.i += 1;
915 if (Translator.builtin_typedef_map.get(mangled_name)) |ty| {
916 return try ZigTag.type.create(mt.t.arena, ty);
917 }
918 if (builtins.map.get(mangled_name)) |builtin| {
919 const builtin_identifier = try ZigTag.identifier.create(mt.t.arena, "__builtin");
920 return try ZigTag.field_access.create(mt.t.arena, .{
921 .lhs = builtin_identifier,
922 .field_name = builtin.name,
923 });
924 }
925
926 return try ZigTag.identifier.create(mt.t.arena, mangled_name);
927 }
928 },
929 .keyword_void => {
930 mt.i += 1;
931 return try ZigTag.type.create(mt.t.arena, "anyopaque");
932 },
933 .keyword_bool => {
934 mt.i += 1;
935 return try ZigTag.type.create(mt.t.arena, "bool");
936 },
937 .keyword_char,
938 .keyword_int,
939 .keyword_short,
940 .keyword_long,
941 .keyword_float,
942 .keyword_double,
943 .keyword_signed,
944 .keyword_unsigned,
945 .keyword_complex,
946 => return try mt.parseCNumericType(),
947 .keyword_enum, .keyword_struct, .keyword_union => {
948 const tag_name = mt.tokSlice();
949 mt.i += 1;
950
951 // struct Foo will be declared as struct_Foo by transRecordDecl
952 const identifier = mt.tokSlice();
953 try mt.expect(.identifier);
954
955 const name = try std.fmt.allocPrint(mt.t.arena, "{s}_{s}", .{ tag_name, identifier });
956 return try ZigTag.identifier.create(mt.t.arena, name);
957 },
958 else => {},
959 }
960
961 if (allow_fail) return null;
962
963 try mt.fail("unable to translate C expr: unexpected token '{s}'", .{tok.symbol()});
964 return error.ParseError;
965}
966
967fn parseCNumericType(mt: *MacroTranslator) ParseError!ZigNode {
968 const KwCounter = struct {
969 double: u8 = 0,
970 long: u8 = 0,
971 int: u8 = 0,
972 float: u8 = 0,
973 short: u8 = 0,
974 char: u8 = 0,
975 unsigned: u8 = 0,
976 signed: u8 = 0,
977 complex: u8 = 0,
978
979 fn eql(self: @This(), other: @This()) bool {
980 return std.meta.eql(self, other);
981 }
982 };
983
984 // Yes, these can be in *any* order
985 // This still doesn't cover cases where for example volatile is intermixed
986
987 var kw = KwCounter{};
988 // prevent overflow
989 var i: u8 = 0;
990 while (i < math.maxInt(u8)) : (i += 1) {
991 switch (mt.peek()) {
992 .keyword_double => kw.double += 1,
993 .keyword_long => kw.long += 1,
994 .keyword_int => kw.int += 1,
995 .keyword_float => kw.float += 1,
996 .keyword_short => kw.short += 1,
997 .keyword_char => kw.char += 1,
998 .keyword_unsigned => kw.unsigned += 1,
999 .keyword_signed => kw.signed += 1,
1000 .keyword_complex => kw.complex += 1,
1001 else => break,
1002 }
1003 mt.i += 1;
1004 }
1005
1006 if (kw.eql(.{ .int = 1 }) or kw.eql(.{ .signed = 1 }) or kw.eql(.{ .signed = 1, .int = 1 }))
1007 return ZigTag.type.create(mt.t.arena, "c_int");
1008
1009 if (kw.eql(.{ .unsigned = 1 }) or kw.eql(.{ .unsigned = 1, .int = 1 }))
1010 return ZigTag.type.create(mt.t.arena, "c_uint");
1011
1012 if (kw.eql(.{ .long = 1 }) or kw.eql(.{ .signed = 1, .long = 1 }) or kw.eql(.{ .long = 1, .int = 1 }) or kw.eql(.{ .signed = 1, .long = 1, .int = 1 }))
1013 return ZigTag.type.create(mt.t.arena, "c_long");
1014
1015 if (kw.eql(.{ .unsigned = 1, .long = 1 }) or kw.eql(.{ .unsigned = 1, .long = 1, .int = 1 }))
1016 return ZigTag.type.create(mt.t.arena, "c_ulong");
1017
1018 if (kw.eql(.{ .long = 2 }) or kw.eql(.{ .signed = 1, .long = 2 }) or kw.eql(.{ .long = 2, .int = 1 }) or kw.eql(.{ .signed = 1, .long = 2, .int = 1 }))
1019 return ZigTag.type.create(mt.t.arena, "c_longlong");
1020
1021 if (kw.eql(.{ .unsigned = 1, .long = 2 }) or kw.eql(.{ .unsigned = 1, .long = 2, .int = 1 }))
1022 return ZigTag.type.create(mt.t.arena, "c_ulonglong");
1023
1024 if (kw.eql(.{ .signed = 1, .char = 1 }))
1025 return ZigTag.type.create(mt.t.arena, "i8");
1026
1027 if (kw.eql(.{ .char = 1 }) or kw.eql(.{ .unsigned = 1, .char = 1 }))
1028 return ZigTag.type.create(mt.t.arena, "u8");
1029
1030 if (kw.eql(.{ .short = 1 }) or kw.eql(.{ .signed = 1, .short = 1 }) or kw.eql(.{ .short = 1, .int = 1 }) or kw.eql(.{ .signed = 1, .short = 1, .int = 1 }))
1031 return ZigTag.type.create(mt.t.arena, "c_short");
1032
1033 if (kw.eql(.{ .unsigned = 1, .short = 1 }) or kw.eql(.{ .unsigned = 1, .short = 1, .int = 1 }))
1034 return ZigTag.type.create(mt.t.arena, "c_ushort");
1035
1036 if (kw.eql(.{ .float = 1 }))
1037 return ZigTag.type.create(mt.t.arena, "f32");
1038
1039 if (kw.eql(.{ .double = 1 }))
1040 return ZigTag.type.create(mt.t.arena, "f64");
1041
1042 if (kw.eql(.{ .long = 1, .double = 1 })) {
1043 try mt.fail("unable to translate: TODO long double", .{});
1044 return error.ParseError;
1045 }
1046
1047 if (kw.eql(.{ .float = 1, .complex = 1 })) {
1048 try mt.fail("unable to translate: TODO _Complex", .{});
1049 return error.ParseError;
1050 }
1051
1052 if (kw.eql(.{ .double = 1, .complex = 1 })) {
1053 try mt.fail("unable to translate: TODO _Complex", .{});
1054 return error.ParseError;
1055 }
1056
1057 if (kw.eql(.{ .long = 1, .double = 1, .complex = 1 })) {
1058 try mt.fail("unable to translate: TODO _Complex", .{});
1059 return error.ParseError;
1060 }
1061
1062 try mt.fail("unable to translate: invalid numeric type", .{});
1063 return error.ParseError;
1064}
1065
1066fn parseCAbstractDeclarator(mt: *MacroTranslator, node: ZigNode) ParseError!ZigNode {
1067 if (mt.eat(.asterisk)) {
1068 if (node.castTag(.type)) |some| {
1069 if (std.mem.eql(u8, some.data, "anyopaque")) {
1070 const ptr = try ZigTag.single_pointer.create(mt.t.arena, .{
1071 .is_const = false,
1072 .is_volatile = false,
1073 .is_allowzero = false,
1074 .elem_type = node,
1075 });
1076 return ZigTag.optional_type.create(mt.t.arena, ptr);
1077 }
1078 }
1079 return ZigTag.c_pointer.create(mt.t.arena, .{
1080 .is_const = false,
1081 .is_volatile = false,
1082 .is_allowzero = false,
1083 .elem_type = node,
1084 });
1085 }
1086 return node;
1087}
1088
1089fn parseCPostfixExpr(mt: *MacroTranslator, scope: *Scope, type_name: ?ZigNode) ParseError!ZigNode {
1090 var node = try mt.parseCPostfixExprInner(scope, type_name);
1091 // In C the preprocessor would handle concatting strings while expanding macros.
1092 // This should do approximately the same by concatting any strings and identifiers
1093 // after a primary or postfix expression.
1094 while (true) {
1095 switch (mt.peek()) {
1096 .string_literal,
1097 .string_literal_utf_16,
1098 .string_literal_utf_8,
1099 .string_literal_utf_32,
1100 .string_literal_wide,
1101 => {},
1102 .identifier, .extended_identifier => {
1103 if (mt.t.global_scope.blank_macros.contains(mt.tokSlice())) {
1104 mt.i += 1;
1105 continue;
1106 }
1107 },
1108 else => break,
1109 }
1110 const rhs = try mt.parseCPostfixExprInner(scope, type_name);
1111 node = try ZigTag.array_cat.create(mt.t.arena, .{ .lhs = node, .rhs = rhs });
1112 }
1113 return node;
1114}
1115
1116fn parseCPostfixExprInner(mt: *MacroTranslator, scope: *Scope, type_name: ?ZigNode) ParseError!ZigNode {
1117 var node = type_name orelse try mt.parseCPrimaryExpr(scope);
1118 while (true) {
1119 switch (mt.peek()) {
1120 .period => {
1121 mt.i += 1;
1122 const field_name = mt.tokSlice();
1123 try mt.expect(.identifier);
1124
1125 node = try ZigTag.field_access.create(mt.t.arena, .{ .lhs = node, .field_name = field_name });
1126 },
1127 .arrow => {
1128 mt.i += 1;
1129 const field_name = mt.tokSlice();
1130 try mt.expect(.identifier);
1131
1132 const deref = try ZigTag.deref.create(mt.t.arena, node);
1133 node = try ZigTag.field_access.create(mt.t.arena, .{ .lhs = deref, .field_name = field_name });
1134 },
1135 .l_bracket => {
1136 mt.i += 1;
1137
1138 const index_val = try mt.macroIntFromBool(try mt.parseCExpr(scope));
1139 const index = try ZigTag.as.create(mt.t.arena, .{
1140 .lhs = try ZigTag.type.create(mt.t.arena, "usize"),
1141 .rhs = try ZigTag.int_cast.create(mt.t.arena, index_val),
1142 });
1143 node = try ZigTag.array_access.create(mt.t.arena, .{ .lhs = node, .rhs = index });
1144 try mt.expect(.r_bracket);
1145 },
1146 .l_paren => {
1147 mt.i += 1;
1148
1149 if (mt.eat(.r_paren)) {
1150 node = try ZigTag.call.create(mt.t.arena, .{ .lhs = node, .args = &.{} });
1151 } else {
1152 var args = std.ArrayList(ZigNode).init(mt.t.gpa);
1153 defer args.deinit();
1154
1155 while (true) {
1156 const arg = try mt.parseCCondExpr(scope);
1157 try args.append(arg);
1158
1159 const next_id = mt.peek();
1160 switch (next_id) {
1161 .comma => {
1162 mt.i += 1;
1163 },
1164 .r_paren => {
1165 mt.i += 1;
1166 break;
1167 },
1168 else => {
1169 try mt.fail("unable to translate C expr: expected ',' or ')' instead got '{s}'", .{next_id.symbol()});
1170 return error.ParseError;
1171 },
1172 }
1173 }
1174 node = try ZigTag.call.create(mt.t.arena, .{ .lhs = node, .args = try mt.t.arena.dupe(ZigNode, args.items) });
1175 }
1176 },
1177 .l_brace => {
1178 mt.i += 1;
1179
1180 // Check for designated field initializers
1181 if (mt.peek() == .period) {
1182 var init_vals = std.ArrayList(ast.Payload.ContainerInitDot.Initializer).init(mt.t.gpa);
1183 defer init_vals.deinit();
1184
1185 while (true) {
1186 try mt.expect(.period);
1187 const name = mt.tokSlice();
1188 try mt.expect(.identifier);
1189 try mt.expect(.equal);
1190
1191 const val = try mt.parseCCondExpr(scope);
1192 try init_vals.append(.{ .name = name, .value = val });
1193
1194 const next_id = mt.peek();
1195 switch (next_id) {
1196 .comma => {
1197 mt.i += 1;
1198 },
1199 .r_brace => {
1200 mt.i += 1;
1201 break;
1202 },
1203 else => {
1204 try mt.fail("unable to translate C expr: expected ',' or '}}' instead got '{s}'", .{next_id.symbol()});
1205 return error.ParseError;
1206 },
1207 }
1208 }
1209 const tuple_node = try ZigTag.container_init_dot.create(mt.t.arena, try mt.t.arena.dupe(ast.Payload.ContainerInitDot.Initializer, init_vals.items));
1210 node = try ZigTag.std_mem_zeroinit.create(mt.t.arena, .{ .lhs = node, .rhs = tuple_node });
1211 continue;
1212 }
1213
1214 var init_vals = std.ArrayList(ZigNode).init(mt.t.gpa);
1215 defer init_vals.deinit();
1216
1217 while (true) {
1218 const val = try mt.parseCCondExpr(scope);
1219 try init_vals.append(val);
1220
1221 const next_id = mt.peek();
1222 switch (next_id) {
1223 .comma => {
1224 mt.i += 1;
1225 },
1226 .r_brace => {
1227 mt.i += 1;
1228 break;
1229 },
1230 else => {
1231 try mt.fail("unable to translate C expr: expected ',' or '}}' instead got '{s}'", .{next_id.symbol()});
1232 return error.ParseError;
1233 },
1234 }
1235 }
1236 const tuple_node = try ZigTag.tuple.create(mt.t.arena, try mt.t.arena.dupe(ZigNode, init_vals.items));
1237 node = try ZigTag.std_mem_zeroinit.create(mt.t.arena, .{ .lhs = node, .rhs = tuple_node });
1238 },
1239 .plus_plus, .minus_minus => {
1240 try mt.fail("TODO postfix inc/dec expr", .{});
1241 return error.ParseError;
1242 },
1243 else => return node,
1244 }
1245 }
1246}
1247
1248fn parseCUnaryExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
1249 switch (mt.peek()) {
1250 .bang => {
1251 mt.i += 1;
1252 const operand = try mt.macroIntToBool(try mt.parseCCastExpr(scope));
1253 return ZigTag.not.create(mt.t.arena, operand);
1254 },
1255 .minus => {
1256 mt.i += 1;
1257 const operand = try mt.macroIntFromBool(try mt.parseCCastExpr(scope));
1258 return ZigTag.negate.create(mt.t.arena, operand);
1259 },
1260 .plus => {
1261 mt.i += 1;
1262 return try mt.parseCCastExpr(scope);
1263 },
1264 .tilde => {
1265 mt.i += 1;
1266 const operand = try mt.macroIntFromBool(try mt.parseCCastExpr(scope));
1267 return ZigTag.bit_not.create(mt.t.arena, operand);
1268 },
1269 .asterisk => {
1270 mt.i += 1;
1271 const operand = try mt.parseCCastExpr(scope);
1272 return ZigTag.deref.create(mt.t.arena, operand);
1273 },
1274 .ampersand => {
1275 mt.i += 1;
1276 const operand = try mt.parseCCastExpr(scope);
1277 return ZigTag.address_of.create(mt.t.arena, operand);
1278 },
1279 .keyword_sizeof => {
1280 mt.i += 1;
1281 const operand = if (mt.eat(.l_paren)) blk: {
1282 const inner = (try mt.parseCTypeName(scope, false)).?;
1283 try mt.expect(.r_paren);
1284 break :blk inner;
1285 } else try mt.parseCUnaryExpr(scope);
1286
1287 return mt.t.createHelperCallNode(.sizeof, &.{operand});
1288 },
1289 .keyword_alignof => {
1290 mt.i += 1;
1291 // TODO this won't work if using <stdalign.h>'s
1292 // #define alignof _Alignof
1293 try mt.expect(.l_paren);
1294 const operand = (try mt.parseCTypeName(scope, false)).?;
1295 try mt.expect(.r_paren);
1296
1297 return ZigTag.alignof.create(mt.t.arena, operand);
1298 },
1299 .plus_plus, .minus_minus => {
1300 try mt.fail("TODO unary inc/dec expr", .{});
1301 return error.ParseError;
1302 },
1303 else => {},
1304 }
1305
1306 return try mt.parseCPostfixExpr(scope, null);
1307}
lib/compiler/translate-c/PatternList.zig created+288
...@@ -0,0 +1,288 @@
1const std = @import("std");
2const mem = std.mem;
3const assert = std.debug.assert;
4
5const aro = @import("aro");
6const CToken = aro.Tokenizer.Token;
7
8const helpers = @import("helpers.zig");
9const Translator = @import("Translator.zig");
10const Error = Translator.Error;
11pub const MacroProcessingError = Error || error{UnexpectedMacroToken};
12
13const Impl = std.meta.DeclEnum(@import("helpers"));
14const Template = struct { []const u8, Impl };
15
16/// Templates must be function-like macros
17/// first element is macro source, second element is the name of the function
18/// in __helpers which implements it
19const templates = [_]Template{
20 .{ "f_SUFFIX(X) (X ## f)", .F_SUFFIX },
21 .{ "F_SUFFIX(X) (X ## F)", .F_SUFFIX },
22
23 .{ "u_SUFFIX(X) (X ## u)", .U_SUFFIX },
24 .{ "U_SUFFIX(X) (X ## U)", .U_SUFFIX },
25
26 .{ "l_SUFFIX(X) (X ## l)", .L_SUFFIX },
27 .{ "L_SUFFIX(X) (X ## L)", .L_SUFFIX },
28
29 .{ "ul_SUFFIX(X) (X ## ul)", .UL_SUFFIX },
30 .{ "uL_SUFFIX(X) (X ## uL)", .UL_SUFFIX },
31 .{ "Ul_SUFFIX(X) (X ## Ul)", .UL_SUFFIX },
32 .{ "UL_SUFFIX(X) (X ## UL)", .UL_SUFFIX },
33
34 .{ "ll_SUFFIX(X) (X ## ll)", .LL_SUFFIX },
35 .{ "LL_SUFFIX(X) (X ## LL)", .LL_SUFFIX },
36
37 .{ "ull_SUFFIX(X) (X ## ull)", .ULL_SUFFIX },
38 .{ "uLL_SUFFIX(X) (X ## uLL)", .ULL_SUFFIX },
39 .{ "Ull_SUFFIX(X) (X ## Ull)", .ULL_SUFFIX },
40 .{ "ULL_SUFFIX(X) (X ## ULL)", .ULL_SUFFIX },
41
42 .{ "f_SUFFIX(X) X ## f", .F_SUFFIX },
43 .{ "F_SUFFIX(X) X ## F", .F_SUFFIX },
44
45 .{ "u_SUFFIX(X) X ## u", .U_SUFFIX },
46 .{ "U_SUFFIX(X) X ## U", .U_SUFFIX },
47
48 .{ "l_SUFFIX(X) X ## l", .L_SUFFIX },
49 .{ "L_SUFFIX(X) X ## L", .L_SUFFIX },
50
51 .{ "ul_SUFFIX(X) X ## ul", .UL_SUFFIX },
52 .{ "uL_SUFFIX(X) X ## uL", .UL_SUFFIX },
53 .{ "Ul_SUFFIX(X) X ## Ul", .UL_SUFFIX },
54 .{ "UL_SUFFIX(X) X ## UL", .UL_SUFFIX },
55
56 .{ "ll_SUFFIX(X) X ## ll", .LL_SUFFIX },
57 .{ "LL_SUFFIX(X) X ## LL", .LL_SUFFIX },
58
59 .{ "ull_SUFFIX(X) X ## ull", .ULL_SUFFIX },
60 .{ "uLL_SUFFIX(X) X ## uLL", .ULL_SUFFIX },
61 .{ "Ull_SUFFIX(X) X ## Ull", .ULL_SUFFIX },
62 .{ "ULL_SUFFIX(X) X ## ULL", .ULL_SUFFIX },
63
64 .{ "CAST_OR_CALL(X, Y) (X)(Y)", .CAST_OR_CALL },
65 .{ "CAST_OR_CALL(X, Y) ((X)(Y))", .CAST_OR_CALL },
66
67 .{
68 \\wl_container_of(ptr, sample, member) \
69 \\(__typeof__(sample))((char *)(ptr) - \
70 \\ offsetof(__typeof__(*sample), member))
71 ,
72 .WL_CONTAINER_OF,
73 },
74
75 .{ "IGNORE_ME(X) ((void)(X))", .DISCARD },
76 .{ "IGNORE_ME(X) (void)(X)", .DISCARD },
77 .{ "IGNORE_ME(X) ((const void)(X))", .DISCARD },
78 .{ "IGNORE_ME(X) (const void)(X)", .DISCARD },
79 .{ "IGNORE_ME(X) ((volatile void)(X))", .DISCARD },
80 .{ "IGNORE_ME(X) (volatile void)(X)", .DISCARD },
81 .{ "IGNORE_ME(X) ((const volatile void)(X))", .DISCARD },
82 .{ "IGNORE_ME(X) (const volatile void)(X)", .DISCARD },
83 .{ "IGNORE_ME(X) ((volatile const void)(X))", .DISCARD },
84 .{ "IGNORE_ME(X) (volatile const void)(X)", .DISCARD },
85};
86
87const Pattern = struct {
88 slicer: MacroSlicer,
89 impl: Impl,
90
91 fn init(pl: *Pattern, allocator: mem.Allocator, template: Template) Error!void {
92 const source = template[0];
93 const impl = template[1];
94 var tok_list = std.ArrayList(CToken).init(allocator);
95 defer tok_list.deinit();
96
97 pl.* = .{
98 .slicer = try tokenizeMacro(source, &tok_list),
99 .impl = impl,
100 };
101 }
102
103 fn deinit(pl: *Pattern, allocator: mem.Allocator) void {
104 allocator.free(pl.slicer.tokens);
105 pl.* = undefined;
106 }
107
108 /// This function assumes that `ms` has already been validated to contain a function-like
109 /// macro, and that the parsed template macro in `pl` also contains a function-like
110 /// macro. Please review this logic carefully if changing that assumption. Two
111 /// function-like macros are considered equivalent if and only if they contain the same
112 /// list of tokens, modulo parameter names.
113 fn matches(pat: Pattern, ms: MacroSlicer) bool {
114 if (ms.params != pat.slicer.params) return false;
115 if (ms.tokens.len != pat.slicer.tokens.len) return false;
116
117 for (ms.tokens, pat.slicer.tokens) |macro_tok, pat_tok| {
118 if (macro_tok.id != pat_tok.id) return false;
119 switch (macro_tok.id) {
120 .macro_param, .macro_param_no_expand => {
121 // `.end` is the parameter index.
122 if (macro_tok.end != pat_tok.end) return false;
123 },
124 .identifier, .extended_identifier, .string_literal, .char_literal, .pp_num => {
125 const macro_bytes = ms.slice(macro_tok);
126 const pattern_bytes = pat.slicer.slice(pat_tok);
127
128 if (!mem.eql(u8, pattern_bytes, macro_bytes)) return false;
129 },
130 else => {
131 // other tags correspond to keywords and operators that do not contain a "payload"
132 // that can vary
133 },
134 }
135 }
136 return true;
137 }
138};
139
140const PatternList = @This();
141
142patterns: []Pattern,
143
144pub const MacroSlicer = struct {
145 source: []const u8,
146 tokens: []const CToken,
147 params: u32,
148
149 fn slice(pl: MacroSlicer, token: CToken) []const u8 {
150 return pl.source[token.start..token.end];
151 }
152};
153
154pub fn init(allocator: mem.Allocator) Error!PatternList {
155 const patterns = try allocator.alloc(Pattern, templates.len);
156 for (patterns, templates) |*pattern, template| {
157 try pattern.init(allocator, template);
158 }
159 return .{ .patterns = patterns };
160}
161
162pub fn deinit(pl: *PatternList, allocator: mem.Allocator) void {
163 for (pl.patterns) |*pattern| pattern.deinit(allocator);
164 allocator.free(pl.patterns);
165 pl.* = undefined;
166}
167
168pub fn match(pl: PatternList, ms: MacroSlicer) Error!?Impl {
169 for (pl.patterns) |pattern| if (pattern.matches(ms)) return pattern.impl;
170 return null;
171}
172
173fn tokenizeMacro(source: []const u8, tok_list: *std.ArrayList(CToken)) Error!MacroSlicer {
174 var param_count: u32 = 0;
175 var param_buf: [8][]const u8 = undefined;
176
177 var tokenizer: aro.Tokenizer = .{
178 .buf = source,
179 .source = .unused,
180 .langopts = .{},
181 };
182 {
183 const name_tok = tokenizer.nextNoWS();
184 assert(name_tok.id == .identifier);
185 const l_paren = tokenizer.nextNoWS();
186 assert(l_paren.id == .l_paren);
187 }
188
189 while (true) {
190 const param = tokenizer.nextNoWS();
191 if (param.id == .r_paren) break;
192 assert(param.id == .identifier);
193 const slice = source[param.start..param.end];
194 param_buf[param_count] = slice;
195 param_count += 1;
196
197 const comma = tokenizer.nextNoWS();
198 if (comma.id == .r_paren) break;
199 assert(comma.id == .comma);
200 }
201
202 outer: while (true) {
203 const tok = tokenizer.next();
204 switch (tok.id) {
205 .whitespace, .comment => continue,
206 .identifier => {
207 const slice = source[tok.start..tok.end];
208 for (param_buf[0..param_count], 0..) |param, i| {
209 if (std.mem.eql(u8, param, slice)) {
210 try tok_list.append(.{
211 .id = .macro_param,
212 .source = .unused,
213 .end = @intCast(i),
214 });
215 continue :outer;
216 }
217 }
218 },
219 .hash_hash => {
220 if (tok_list.items[tok_list.items.len - 1].id == .macro_param) {
221 tok_list.items[tok_list.items.len - 1].id = .macro_param_no_expand;
222 }
223 },
224 .nl, .eof => break,
225 else => {},
226 }
227 try tok_list.append(tok);
228 }
229
230 return .{
231 .source = source,
232 .tokens = try tok_list.toOwnedSlice(),
233 .params = param_count,
234 };
235}
236
237test "Macro matching" {
238 const testing = std.testing;
239 const helper = struct {
240 fn checkMacro(
241 allocator: mem.Allocator,
242 pattern_list: PatternList,
243 source: []const u8,
244 comptime expected_match: ?Impl,
245 ) !void {
246 var tok_list = std.ArrayList(CToken).init(allocator);
247 defer tok_list.deinit();
248 const ms = try tokenizeMacro(source, &tok_list);
249 defer allocator.free(ms.tokens);
250
251 const matched = try pattern_list.match(ms);
252 if (expected_match) |expected| {
253 try testing.expectEqual(expected, matched);
254 } else {
255 try testing.expectEqual(@as(@TypeOf(matched), null), matched);
256 }
257 }
258 };
259 const allocator = std.testing.allocator;
260 var pattern_list = try PatternList.init(allocator);
261 defer pattern_list.deinit(allocator);
262
263 try helper.checkMacro(allocator, pattern_list, "BAR(Z) (Z ## F)", .F_SUFFIX);
264 try helper.checkMacro(allocator, pattern_list, "BAR(Z) (Z ## U)", .U_SUFFIX);
265 try helper.checkMacro(allocator, pattern_list, "BAR(Z) (Z ## L)", .L_SUFFIX);
266 try helper.checkMacro(allocator, pattern_list, "BAR(Z) (Z ## LL)", .LL_SUFFIX);
267 try helper.checkMacro(allocator, pattern_list, "BAR(Z) (Z ## UL)", .UL_SUFFIX);
268 try helper.checkMacro(allocator, pattern_list, "BAR(Z) (Z ## ULL)", .ULL_SUFFIX);
269 try helper.checkMacro(allocator, pattern_list,
270 \\container_of(a, b, c) \
271 \\(__typeof__(b))((char *)(a) - \
272 \\ offsetof(__typeof__(*b), c))
273 , .WL_CONTAINER_OF);
274
275 try helper.checkMacro(allocator, pattern_list, "NO_MATCH(X, Y) (X + Y)", null);
276 try helper.checkMacro(allocator, pattern_list, "CAST_OR_CALL(X, Y) (X)(Y)", .CAST_OR_CALL);
277 try helper.checkMacro(allocator, pattern_list, "CAST_OR_CALL(X, Y) ((X)(Y))", .CAST_OR_CALL);
278 try helper.checkMacro(allocator, pattern_list, "IGNORE_ME(X) (void)(X)", .DISCARD);
279 try helper.checkMacro(allocator, pattern_list, "IGNORE_ME(X) ((void)(X))", .DISCARD);
280 try helper.checkMacro(allocator, pattern_list, "IGNORE_ME(X) (const void)(X)", .DISCARD);
281 try helper.checkMacro(allocator, pattern_list, "IGNORE_ME(X) ((const void)(X))", .DISCARD);
282 try helper.checkMacro(allocator, pattern_list, "IGNORE_ME(X) (volatile void)(X)", .DISCARD);
283 try helper.checkMacro(allocator, pattern_list, "IGNORE_ME(X) ((volatile void)(X))", .DISCARD);
284 try helper.checkMacro(allocator, pattern_list, "IGNORE_ME(X) (const volatile void)(X)", .DISCARD);
285 try helper.checkMacro(allocator, pattern_list, "IGNORE_ME(X) ((const volatile void)(X))", .DISCARD);
286 try helper.checkMacro(allocator, pattern_list, "IGNORE_ME(X) (volatile const void)(X)", .DISCARD);
287 try helper.checkMacro(allocator, pattern_list, "IGNORE_ME(X) ((volatile const void)(X))", .DISCARD);
288}
lib/compiler/translate-c/Scope.zig created+399
...@@ -0,0 +1,399 @@
1const std = @import("std");
2
3const aro = @import("aro");
4
5const ast = @import("ast.zig");
6const Translator = @import("Translator.zig");
7
8const Scope = @This();
9
10pub const SymbolTable = std.StringArrayHashMapUnmanaged(ast.Node);
11pub const AliasList = std.ArrayListUnmanaged(struct {
12 alias: []const u8,
13 name: []const u8,
14});
15
16/// Associates a container (structure or union) with its relevant member functions.
17pub const ContainerMemberFns = struct {
18 container_decl_ptr: *ast.Node,
19 member_fns: std.ArrayListUnmanaged(*ast.Payload.Func) = .empty,
20};
21pub const ContainerMemberFnsHashMap = std.AutoArrayHashMapUnmanaged(aro.QualType, ContainerMemberFns);
22
23id: Id,
24parent: ?*Scope,
25
26pub const Id = enum {
27 block,
28 root,
29 condition,
30 loop,
31 do_loop,
32};
33
34/// Used for the scope of condition expressions, for example `if (cond)`.
35/// The block is lazily initialized because it is only needed for rare
36/// cases of comma operators being used.
37pub const Condition = struct {
38 base: Scope,
39 block: ?Block = null,
40
41 fn getBlockScope(cond: *Condition, t: *Translator) !*Block {
42 if (cond.block) |*b| return b;
43 cond.block = try Block.init(t, &cond.base, true);
44 return &cond.block.?;
45 }
46
47 pub fn deinit(cond: *Condition) void {
48 if (cond.block) |*b| b.deinit();
49 }
50};
51
52/// Represents an in-progress Node.Block. This struct is stack-allocated.
53/// When it is deinitialized, it produces an Node.Block which is allocated
54/// into the main arena.
55pub const Block = struct {
56 base: Scope,
57 translator: *Translator,
58 statements: std.ArrayListUnmanaged(ast.Node),
59 variables: AliasList,
60 mangle_count: u32 = 0,
61 label: ?[]const u8 = null,
62
63 /// By default all variables are discarded, since we do not know in advance if they
64 /// will be used. This maps the variable's name to the Discard payload, so that if
65 /// the variable is subsequently referenced we can indicate that the discard should
66 /// be skipped during the intermediate AST -> Zig AST render step.
67 variable_discards: std.StringArrayHashMapUnmanaged(*ast.Payload.Discard),
68
69 /// When the block corresponds to a function, keep track of the return type
70 /// so that the return expression can be cast, if necessary
71 return_type: ?aro.QualType = null,
72
73 /// C static local variables are wrapped in a block-local struct. The struct
74 /// is named `mangle(static_local_ + name)` and the Zig variable within the
75 /// struct keeps the name of the C variable.
76 pub const static_local_prefix = "static_local";
77
78 /// C extern local variables are wrapped in a block-local struct. The struct
79 /// is named `mangle(extern_local + name)` and the Zig variable within the
80 /// struct keeps the name of the C variable.
81 pub const extern_local_prefix = "extern_local";
82
83 pub fn init(t: *Translator, parent: *Scope, labeled: bool) !Block {
84 var blk: Block = .{
85 .base = .{
86 .id = .block,
87 .parent = parent,
88 },
89 .translator = t,
90 .statements = .empty,
91 .variables = .empty,
92 .variable_discards = .empty,
93 };
94 if (labeled) {
95 blk.label = try blk.makeMangledName("blk");
96 }
97 return blk;
98 }
99
100 pub fn deinit(block: *Block) void {
101 block.statements.deinit(block.translator.gpa);
102 block.variables.deinit(block.translator.gpa);
103 block.variable_discards.deinit(block.translator.gpa);
104 block.* = undefined;
105 }
106
107 pub fn complete(block: *Block) !ast.Node {
108 const arena = block.translator.arena;
109 if (block.base.parent.?.id == .do_loop) {
110 // We reserve 1 extra statement if the parent is a do_loop. This is in case of
111 // do while, we want to put `if (cond) break;` at the end.
112 const alloc_len = block.statements.items.len + @intFromBool(block.base.parent.?.id == .do_loop);
113 var stmts = try arena.alloc(ast.Node, alloc_len);
114 stmts.len = block.statements.items.len;
115 @memcpy(stmts[0..block.statements.items.len], block.statements.items);
116 return ast.Node.Tag.block.create(arena, .{
117 .label = block.label,
118 .stmts = stmts,
119 });
120 }
121 if (block.statements.items.len == 0) return ast.Node.Tag.empty_block.init();
122 return ast.Node.Tag.block.create(arena, .{
123 .label = block.label,
124 .stmts = try arena.dupe(ast.Node, block.statements.items),
125 });
126 }
127
128 /// Given the desired name, return a name that does not shadow anything from outer scopes.
129 /// Inserts the returned name into the scope.
130 /// The name will not be visible to callers of getAlias.
131 pub fn reserveMangledName(block: *Block, name: []const u8) ![]const u8 {
132 return block.createMangledName(name, true, null);
133 }
134
135 /// Same as reserveMangledName, but enables the alias immediately.
136 pub fn makeMangledName(block: *Block, name: []const u8) ![]const u8 {
137 return block.createMangledName(name, false, null);
138 }
139
140 pub fn createMangledName(block: *Block, name: []const u8, reservation: bool, prefix_opt: ?[]const u8) ![]const u8 {
141 const arena = block.translator.arena;
142 const name_copy = try arena.dupe(u8, name);
143 const alias_base = if (prefix_opt) |prefix|
144 try std.fmt.allocPrint(arena, "{s}_{s}", .{ prefix, name })
145 else
146 name;
147 var proposed_name = alias_base;
148 while (block.contains(proposed_name)) {
149 block.mangle_count += 1;
150 proposed_name = try std.fmt.allocPrint(arena, "{s}_{d}", .{ alias_base, block.mangle_count });
151 }
152 const new_mangle = try block.variables.addOne(block.translator.gpa);
153 if (reservation) {
154 new_mangle.* = .{ .name = name_copy, .alias = name_copy };
155 } else {
156 new_mangle.* = .{ .name = name_copy, .alias = proposed_name };
157 }
158 return proposed_name;
159 }
160
161 fn getAlias(block: *Block, name: []const u8) ?[]const u8 {
162 for (block.variables.items) |p| {
163 if (std.mem.eql(u8, p.name, name))
164 return p.alias;
165 }
166 return block.base.parent.?.getAlias(name);
167 }
168
169 fn localContains(block: *Block, name: []const u8) bool {
170 for (block.variables.items) |p| {
171 if (std.mem.eql(u8, p.alias, name))
172 return true;
173 }
174 return false;
175 }
176
177 fn contains(block: *Block, name: []const u8) bool {
178 if (block.localContains(name))
179 return true;
180 return block.base.parent.?.contains(name);
181 }
182
183 pub fn discardVariable(block: *Block, name: []const u8) Translator.Error!void {
184 const gpa = block.translator.gpa;
185 const arena = block.translator.arena;
186 const name_node = try ast.Node.Tag.identifier.create(arena, name);
187 const discard = try ast.Node.Tag.discard.create(arena, .{ .should_skip = false, .value = name_node });
188 try block.statements.append(gpa, discard);
189 try block.variable_discards.putNoClobber(gpa, name, discard.castTag(.discard).?);
190 }
191};
192
193pub const Root = struct {
194 base: Scope,
195 translator: *Translator,
196 sym_table: SymbolTable,
197 blank_macros: std.StringArrayHashMapUnmanaged(void),
198 nodes: std.ArrayListUnmanaged(ast.Node),
199 container_member_fns_map: ContainerMemberFnsHashMap,
200
201 pub fn init(t: *Translator) Root {
202 return .{
203 .base = .{
204 .id = .root,
205 .parent = null,
206 },
207 .translator = t,
208 .sym_table = .empty,
209 .blank_macros = .empty,
210 .nodes = .empty,
211 .container_member_fns_map = .empty,
212 };
213 }
214
215 pub fn deinit(root: *Root) void {
216 root.sym_table.deinit(root.translator.gpa);
217 root.blank_macros.deinit(root.translator.gpa);
218 root.nodes.deinit(root.translator.gpa);
219 for (root.container_member_fns_map.values()) |*members| {
220 members.member_fns.deinit(root.translator.gpa);
221 }
222 root.container_member_fns_map.deinit(root.translator.gpa);
223 }
224
225 /// Check if the global scope contains this name, without looking into the "future", e.g.
226 /// ignore the preprocessed decl and macro names.
227 pub fn containsNow(root: *Root, name: []const u8) bool {
228 return root.sym_table.contains(name);
229 }
230
231 /// Check if the global scope contains the name, includes all decls that haven't been translated yet.
232 pub fn contains(root: *Root, name: []const u8) bool {
233 return root.containsNow(name) or root.translator.global_names.contains(name) or root.translator.weak_global_names.contains(name);
234 }
235
236 pub fn addMemberFunction(root: *Root, func_ty: aro.Type.Func, func: *ast.Payload.Func) !void {
237 std.debug.assert(func.data.name != null);
238 if (func_ty.params.len == 0) return;
239
240 const param1_base = func_ty.params[0].qt.base(root.translator.comp);
241 const container_qt = if (param1_base.type == .pointer)
242 param1_base.type.pointer.child.base(root.translator.comp).qt
243 else
244 param1_base.qt;
245
246 if (root.container_member_fns_map.getPtr(container_qt)) |members| {
247 try members.member_fns.append(root.translator.gpa, func);
248 }
249 }
250
251 pub fn processContainerMemberFns(root: *Root) !void {
252 const gpa = root.translator.gpa;
253 const arena = root.translator.arena;
254
255 var member_names: std.StringArrayHashMapUnmanaged(u32) = .empty;
256 defer member_names.deinit(gpa);
257 for (root.container_member_fns_map.values()) |members| {
258 member_names.clearRetainingCapacity();
259 const decls_ptr = switch (members.container_decl_ptr.tag()) {
260 .@"struct", .@"union" => blk_record: {
261 const payload: *ast.Payload.Container = @alignCast(@fieldParentPtr("base", members.container_decl_ptr.ptr_otherwise));
262 // Avoid duplication with field names
263 for (payload.data.fields) |field| {
264 try member_names.put(gpa, field.name, 0);
265 }
266 break :blk_record &payload.data.decls;
267 },
268 .opaque_literal => blk_opaque: {
269 const container_decl = try ast.Node.Tag.@"opaque".create(arena, .{
270 .layout = .none,
271 .fields = &.{},
272 .decls = &.{},
273 });
274 members.container_decl_ptr.* = container_decl;
275 break :blk_opaque &container_decl.castTag(.@"opaque").?.data.decls;
276 },
277 else => return,
278 };
279
280 const old_decls = decls_ptr.*;
281 const new_decls = try arena.alloc(ast.Node, old_decls.len + members.member_fns.items.len);
282 @memcpy(new_decls[0..old_decls.len], old_decls);
283 // Assume the allocator of payload.data.decls is arena,
284 // so don't add arena.free(old_variables).
285 const func_ref_vars = new_decls[old_decls.len..];
286 var count: u32 = 0;
287 for (members.member_fns.items) |func| {
288 const func_name = func.data.name.?;
289
290 const last_index = std.mem.lastIndexOf(u8, func_name, "_");
291 const last_name = if (last_index) |index| func_name[index + 1 ..] else continue;
292 var same_count: u32 = 0;
293 const gop = try member_names.getOrPutValue(gpa, last_name, same_count);
294 if (gop.found_existing) {
295 gop.value_ptr.* += 1;
296 same_count = gop.value_ptr.*;
297 }
298 const var_name = if (same_count == 0)
299 last_name
300 else
301 try std.fmt.allocPrint(arena, "{s}{d}", .{ last_name, same_count });
302
303 func_ref_vars[count] = try ast.Node.Tag.pub_var_simple.create(arena, .{
304 .name = var_name,
305 .init = try ast.Node.Tag.identifier.create(arena, func_name),
306 });
307 count += 1;
308 }
309 decls_ptr.* = new_decls[0 .. old_decls.len + count];
310 }
311 }
312};
313
314pub fn findBlockScope(inner: *Scope, t: *Translator) !*Block {
315 var scope = inner;
316 while (true) {
317 switch (scope.id) {
318 .root => unreachable,
319 .block => return @fieldParentPtr("base", scope),
320 .condition => return @as(*Condition, @fieldParentPtr("base", scope)).getBlockScope(t),
321 else => scope = scope.parent.?,
322 }
323 }
324}
325
326pub fn findBlockReturnType(inner: *Scope) aro.QualType {
327 var scope = inner;
328 while (true) {
329 switch (scope.id) {
330 .root => unreachable,
331 .block => {
332 const block: *Block = @fieldParentPtr("base", scope);
333 if (block.return_type) |qt| return qt;
334 scope = scope.parent.?;
335 },
336 else => scope = scope.parent.?,
337 }
338 }
339}
340
341pub fn getAlias(scope: *Scope, name: []const u8) ?[]const u8 {
342 return switch (scope.id) {
343 .root => null,
344 .block => @as(*Block, @fieldParentPtr("base", scope)).getAlias(name),
345 .loop, .do_loop, .condition => scope.parent.?.getAlias(name),
346 };
347}
348
349fn contains(scope: *Scope, name: []const u8) bool {
350 return switch (scope.id) {
351 .root => @as(*Root, @fieldParentPtr("base", scope)).contains(name),
352 .block => @as(*Block, @fieldParentPtr("base", scope)).contains(name),
353 .loop, .do_loop, .condition => scope.parent.?.contains(name),
354 };
355}
356
357/// Appends a node to the first block scope if inside a function, or to the root tree if not.
358pub fn appendNode(inner: *Scope, node: ast.Node) !void {
359 var scope = inner;
360 while (true) {
361 switch (scope.id) {
362 .root => {
363 const root: *Root = @fieldParentPtr("base", scope);
364 return root.nodes.append(root.translator.gpa, node);
365 },
366 .block => {
367 const block: *Block = @fieldParentPtr("base", scope);
368 return block.statements.append(block.translator.gpa, node);
369 },
370 else => scope = scope.parent.?,
371 }
372 }
373}
374
375pub fn skipVariableDiscard(inner: *Scope, name: []const u8) void {
376 if (true) {
377 // TODO: due to 'local variable is never mutated' errors, we can
378 // only skip discards if a variable is used as an lvalue, which
379 // we don't currently have detection for in translate-c.
380 // Once #17584 is completed, perhaps we can do away with this
381 // logic entirely, and instead rely on render to fixup code.
382 return;
383 }
384 var scope = inner;
385 while (true) {
386 switch (scope.id) {
387 .root => return,
388 .block => {
389 const block: *Block = @fieldParentPtr("base", scope);
390 if (block.variable_discards.get(name)) |discard| {
391 discard.data.should_skip = true;
392 return;
393 }
394 },
395 else => {},
396 }
397 scope = scope.parent.?;
398 }
399}
lib/compiler/translate-c/Translator.zig created+4183
...@@ -0,0 +1,4183 @@
1const std = @import("std");
2const mem = std.mem;
3const assert = std.debug.assert;
4const CallingConvention = std.builtin.CallingConvention;
5
6const aro = @import("aro");
7const CToken = aro.Tokenizer.Token;
8const Tree = aro.Tree;
9const Node = Tree.Node;
10const TokenIndex = Tree.TokenIndex;
11const QualType = aro.QualType;
12
13const ast = @import("ast.zig");
14const ZigNode = ast.Node;
15const ZigTag = ZigNode.Tag;
16const builtins = @import("builtins.zig");
17const helpers = @import("helpers.zig");
18const MacroTranslator = @import("MacroTranslator.zig");
19const PatternList = @import("PatternList.zig");
20const Scope = @import("Scope.zig");
21
22pub const Error = std.mem.Allocator.Error;
23pub const MacroProcessingError = Error || error{UnexpectedMacroToken};
24pub const TypeError = Error || error{UnsupportedType};
25pub const TransError = TypeError || error{UnsupportedTranslation};
26
27const Translator = @This();
28
29/// The C AST to be translated.
30tree: *const Tree,
31/// The compilation corresponding to the AST.
32comp: *aro.Compilation,
33/// The Preprocessor that produced the source for `tree`.
34pp: *const aro.Preprocessor,
35
36gpa: mem.Allocator,
37arena: mem.Allocator,
38
39alias_list: Scope.AliasList,
40global_scope: *Scope.Root,
41/// Running number used for creating new unique identifiers.
42mangle_count: u32 = 0,
43
44/// Table of declarations for enum, struct, union and typedef types.
45type_decls: std.AutoArrayHashMapUnmanaged(Node.Index, []const u8) = .empty,
46/// Table of record decls that have been demoted to opaques.
47opaque_demotes: std.AutoHashMapUnmanaged(QualType, void) = .empty,
48/// Table of unnamed enums and records that are child types of typedefs.
49unnamed_typedefs: std.AutoHashMapUnmanaged(QualType, []const u8) = .empty,
50/// Table of anonymous record to generated field names.
51anonymous_record_field_names: std.AutoHashMapUnmanaged(struct {
52 parent: QualType,
53 field: QualType,
54}, []const u8) = .empty,
55
56/// This one is different than the root scope's name table. This contains
57/// a list of names that we found by visiting all the top level decls without
58/// translating them. The other maps are updated as we translate; this one is updated
59/// up front in a pre-processing step.
60global_names: std.StringArrayHashMapUnmanaged(void) = .empty,
61
62/// This is similar to `global_names`, but contains names which we would
63/// *like* to use, but do not strictly *have* to if they are unavailable.
64/// These are relevant to types, which ideally we would name like
65/// 'struct_foo' with an alias 'foo', but if either of those names is taken,
66/// may be mangled.
67/// This is distinct from `global_names` so we can detect at a type
68/// declaration whether or not the name is available.
69weak_global_names: std.StringArrayHashMapUnmanaged(void) = .empty,
70
71/// Set of identifiers known to refer to typedef declarations.
72/// Used when parsing macros.
73typedefs: std.StringArrayHashMapUnmanaged(void) = .empty,
74
75/// The lhs lval of a compound assignment expression.
76compound_assign_dummy: ?ZigNode = null,
77
78pub fn getMangle(t: *Translator) u32 {
79 t.mangle_count += 1;
80 return t.mangle_count;
81}
82
83/// Convert an `aro.Source.Location` to a 'file:line:column' string.
84pub fn locStr(t: *Translator, loc: aro.Source.Location) ![]const u8 {
85 const source = t.comp.getSource(loc.id);
86 const line_col = source.lineCol(loc);
87 const filename = source.path;
88
89 const line = source.physicalLine(loc);
90 const col = line_col.col;
91
92 return std.fmt.allocPrint(t.arena, "{s}:{d}:{d}", .{ filename, line, col });
93}
94
95fn maybeSuppressResult(t: *Translator, used: ResultUsed, result: ZigNode) TransError!ZigNode {
96 if (used == .used) return result;
97 return ZigTag.discard.create(t.arena, .{ .should_skip = false, .value = result });
98}
99
100pub fn addTopLevelDecl(t: *Translator, name: []const u8, decl_node: ZigNode) !void {
101 const gop = try t.global_scope.sym_table.getOrPut(t.gpa, name);
102 if (!gop.found_existing) {
103 gop.value_ptr.* = decl_node;
104 try t.global_scope.nodes.append(t.gpa, decl_node);
105 }
106}
107
108fn fail(
109 t: *Translator,
110 err: anytype,
111 source_loc: TokenIndex,
112 comptime format: []const u8,
113 args: anytype,
114) (@TypeOf(err) || error{OutOfMemory}) {
115 try t.warn(&t.global_scope.base, source_loc, format, args);
116 return err;
117}
118
119pub fn failDecl(
120 t: *Translator,
121 scope: *Scope,
122 tok_idx: TokenIndex,
123 name: []const u8,
124 comptime format: []const u8,
125 args: anytype,
126) Error!void {
127 const loc = t.tree.tokens.items(.loc)[tok_idx];
128 return t.failDeclExtra(scope, loc, name, format, args);
129}
130
131pub fn failDeclExtra(
132 t: *Translator,
133 scope: *Scope,
134 loc: aro.Source.Location,
135 name: []const u8,
136 comptime format: []const u8,
137 args: anytype,
138) Error!void {
139 // location
140 // pub const name = @compileError(msg);
141 const fail_msg = try std.fmt.allocPrint(t.arena, format, args);
142 const fail_decl = try ZigTag.fail_decl.create(t.arena, .{ .actual = name, .mangled = fail_msg });
143
144 const str = try t.locStr(loc);
145 const location_comment = try std.fmt.allocPrint(t.arena, "// {s}", .{str});
146 const loc_node = try ZigTag.warning.create(t.arena, location_comment);
147
148 if (scope.id == .root) {
149 try t.addTopLevelDecl(name, fail_decl);
150 try scope.appendNode(loc_node);
151 } else {
152 try scope.appendNode(fail_decl);
153 try scope.appendNode(loc_node);
154
155 const bs = try scope.findBlockScope(t);
156 try bs.discardVariable(name);
157 }
158}
159
160fn warn(t: *Translator, scope: *Scope, tok_idx: TokenIndex, comptime format: []const u8, args: anytype) !void {
161 const loc = t.tree.tokens.items(.loc)[tok_idx];
162 const str = try t.locStr(loc);
163 const value = try std.fmt.allocPrint(t.arena, "// {s}: warning: " ++ format, .{str} ++ args);
164 try scope.appendNode(try ZigTag.warning.create(t.arena, value));
165}
166
167pub const Options = struct {
168 gpa: mem.Allocator,
169 comp: *aro.Compilation,
170 pp: *const aro.Preprocessor,
171 tree: *const aro.Tree,
172 module_libs: bool,
173};
174
175pub fn translate(options: Options) ![]u8 {
176 const gpa = options.gpa;
177 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
178 defer arena_allocator.deinit();
179 const arena = arena_allocator.allocator();
180
181 var translator: Translator = .{
182 .gpa = gpa,
183 .arena = arena,
184 .alias_list = .empty,
185 .global_scope = try arena.create(Scope.Root),
186 .comp = options.comp,
187 .pp = options.pp,
188 .tree = options.tree,
189 };
190 translator.global_scope.* = Scope.Root.init(&translator);
191 defer {
192 translator.type_decls.deinit(gpa);
193 translator.alias_list.deinit(gpa);
194 translator.global_names.deinit(gpa);
195 translator.weak_global_names.deinit(gpa);
196 translator.opaque_demotes.deinit(gpa);
197 translator.unnamed_typedefs.deinit(gpa);
198 translator.anonymous_record_field_names.deinit(gpa);
199 translator.typedefs.deinit(gpa);
200 translator.global_scope.deinit();
201 }
202
203 try translator.prepopulateGlobalNameTable();
204 try translator.transTopLevelDecls();
205
206 // Insert empty line before macros.
207 try translator.global_scope.nodes.append(gpa, try ZigTag.warning.create(arena, "\n"));
208
209 try translator.transMacros();
210
211 for (translator.alias_list.items) |alias| {
212 if (!translator.global_scope.sym_table.contains(alias.alias)) {
213 const node = try ZigTag.alias.create(arena, .{ .actual = alias.alias, .mangled = alias.name });
214 try translator.addTopLevelDecl(alias.alias, node);
215 }
216 }
217
218 try translator.global_scope.processContainerMemberFns();
219
220 var buf: std.ArrayList(u8) = .init(gpa);
221 defer buf.deinit();
222
223 if (options.module_libs) {
224 try buf.appendSlice(
225 \\pub const __builtin = @import("c_builtins");
226 \\pub const __helpers = @import("helpers");
227 \\
228 \\
229 );
230 } else {
231 try buf.appendSlice(
232 \\pub const __builtin = @import("c_builtins.zig");
233 \\pub const __helpers = @import("helpers.zig");
234 \\
235 \\
236 );
237 }
238
239 var zig_ast = try ast.render(gpa, translator.global_scope.nodes.items);
240 defer {
241 gpa.free(zig_ast.source);
242 zig_ast.deinit(gpa);
243 }
244 try zig_ast.renderToArrayList(&buf, .{});
245 return buf.toOwnedSlice();
246}
247
248fn prepopulateGlobalNameTable(t: *Translator) !void {
249 for (t.tree.root_decls.items) |decl| {
250 switch (decl.get(t.tree)) {
251 .typedef => |typedef_decl| {
252 const decl_name = t.tree.tokSlice(typedef_decl.name_tok);
253 try t.global_names.put(t.gpa, decl_name, {});
254
255 // Check for typedefs with unnamed enum/record child types.
256 const base = typedef_decl.qt.base(t.comp);
257 switch (base.type) {
258 .@"enum" => |enum_ty| {
259 if (enum_ty.name.lookup(t.comp)[0] != '(') continue;
260 },
261 .@"struct", .@"union" => |record_ty| {
262 if (record_ty.name.lookup(t.comp)[0] != '(') continue;
263 },
264 else => continue,
265 }
266
267 const gop = try t.unnamed_typedefs.getOrPut(t.gpa, base.qt);
268 if (gop.found_existing) {
269 // One typedef can declare multiple names.
270 // TODO Don't put this one in `decl_table` so it's processed later.
271 continue;
272 }
273 gop.value_ptr.* = decl_name;
274 },
275
276 .struct_decl,
277 .union_decl,
278 .struct_forward_decl,
279 .union_forward_decl,
280 .enum_decl,
281 .enum_forward_decl,
282 => {
283 const decl_qt = decl.qt(t.tree);
284 const prefix, const name = switch (decl_qt.base(t.comp).type) {
285 .@"struct" => |struct_ty| .{ "struct", struct_ty.name.lookup(t.comp) },
286 .@"union" => |union_ty| .{ "union", union_ty.name.lookup(t.comp) },
287 .@"enum" => |enum_ty| .{ "enum", enum_ty.name.lookup(t.comp) },
288 else => unreachable,
289 };
290 const prefixed_name = try std.fmt.allocPrint(t.arena, "{s}_{s}", .{ prefix, name });
291 // `name` and `prefixed_name` are the preferred names for this type.
292 // However, we can name it anything else if necessary, so these are "weak names".
293 try t.weak_global_names.ensureUnusedCapacity(t.gpa, 2);
294 t.weak_global_names.putAssumeCapacity(name, {});
295 t.weak_global_names.putAssumeCapacity(prefixed_name, {});
296 },
297
298 .function, .variable => {
299 const decl_name = t.tree.tokSlice(decl.tok(t.tree));
300 try t.global_names.put(t.gpa, decl_name, {});
301 },
302 .static_assert => {},
303 .empty_decl => {},
304 .global_asm => {},
305 else => unreachable,
306 }
307 }
308
309 for (t.pp.defines.keys(), t.pp.defines.values()) |name, macro| {
310 if (macro.is_builtin) continue;
311 if (!t.isSelfDefinedMacro(name, macro)) {
312 try t.global_names.put(t.gpa, name, {});
313 }
314 }
315}
316
317/// Determines whether macro is of the form: `#define FOO FOO` (Possibly with trailing tokens)
318/// Macros of this form will not be translated.
319fn isSelfDefinedMacro(t: *Translator, name: []const u8, macro: aro.Preprocessor.Macro) bool {
320 if (macro.is_func) return false;
321
322 if (macro.tokens.len < 1) return false;
323 const first_tok = macro.tokens[0];
324
325 const source = t.comp.getSource(macro.loc.id);
326 const slice = source.buf[first_tok.start..first_tok.end];
327
328 return std.mem.eql(u8, name, slice);
329}
330
331// =======================
332// Declaration translation
333// =======================
334
335fn transTopLevelDecls(t: *Translator) !void {
336 for (t.tree.root_decls.items) |decl| {
337 try t.transDecl(&t.global_scope.base, decl);
338 }
339}
340
341fn transDecl(t: *Translator, scope: *Scope, decl: Node.Index) !void {
342 switch (decl.get(t.tree)) {
343 .typedef => |typedef_decl| {
344 // Implicit typedefs are translated only if referenced.
345 if (typedef_decl.implicit) return;
346 try t.transTypeDef(scope, decl);
347 },
348
349 .struct_decl, .union_decl => |record_decl| {
350 try t.transRecordDecl(scope, record_decl.container_qt);
351 },
352
353 .enum_decl => |enum_decl| {
354 try t.transEnumDecl(scope, enum_decl.container_qt);
355 },
356
357 .enum_field,
358 .record_field,
359 .struct_forward_decl,
360 .union_forward_decl,
361 .enum_forward_decl,
362 => return,
363
364 .function => |function| {
365 if (function.definition) |definition| {
366 return t.transFnDecl(scope, definition.get(t.tree).function);
367 }
368 try t.transFnDecl(scope, function);
369 },
370
371 .variable => |variable| {
372 if (variable.definition != null) return;
373 try t.transVarDecl(scope, variable);
374 },
375 .static_assert => |static_assert| {
376 try t.transStaticAssert(&t.global_scope.base, static_assert);
377 },
378 .global_asm => |global_asm| {
379 try t.transGlobalAsm(&t.global_scope.base, global_asm);
380 },
381 .empty_decl => {},
382 else => unreachable,
383 }
384}
385
386pub const builtin_typedef_map = std.StaticStringMap([]const u8).initComptime(.{
387 .{ "uint8_t", "u8" },
388 .{ "int8_t", "i8" },
389 .{ "uint16_t", "u16" },
390 .{ "int16_t", "i16" },
391 .{ "uint32_t", "u32" },
392 .{ "int32_t", "i32" },
393 .{ "uint64_t", "u64" },
394 .{ "int64_t", "i64" },
395 .{ "intptr_t", "isize" },
396 .{ "uintptr_t", "usize" },
397 .{ "ssize_t", "isize" },
398 .{ "size_t", "usize" },
399});
400
401fn transTypeDef(t: *Translator, scope: *Scope, typedef_node: Node.Index) Error!void {
402 const typedef_decl = typedef_node.get(t.tree).typedef;
403 if (t.type_decls.get(typedef_node)) |_|
404 return; // Avoid processing this decl twice
405
406 const toplevel = scope.id == .root;
407 const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(t) else undefined;
408
409 var name: []const u8 = t.tree.tokSlice(typedef_decl.name_tok);
410 try t.typedefs.put(t.gpa, name, {});
411
412 if (builtin_typedef_map.get(name)) |builtin| {
413 return t.type_decls.putNoClobber(t.gpa, typedef_node, builtin);
414 }
415 if (!toplevel) name = try bs.makeMangledName(name);
416 try t.type_decls.putNoClobber(t.gpa, typedef_node, name);
417
418 const typedef_loc = typedef_decl.name_tok;
419 const init_node = t.transType(scope, typedef_decl.qt, typedef_loc) catch |err| switch (err) {
420 error.UnsupportedType => {
421 return t.failDecl(scope, typedef_loc, name, "unable to resolve typedef child type", .{});
422 },
423 error.OutOfMemory => |e| return e,
424 };
425
426 const payload = try t.arena.create(ast.Payload.SimpleVarDecl);
427 payload.* = .{
428 .base = .{ .tag = if (toplevel) .pub_var_simple else .var_simple },
429 .data = .{
430 .name = name,
431 .init = init_node,
432 },
433 };
434 const node = ZigNode.initPayload(&payload.base);
435
436 if (toplevel) {
437 try t.addTopLevelDecl(name, node);
438 } else {
439 try scope.appendNode(node);
440 try bs.discardVariable(name);
441 }
442}
443
444fn mangleWeakGlobalName(t: *Translator, want_name: []const u8) Error![]const u8 {
445 var cur_name = want_name;
446
447 if (!t.weak_global_names.contains(want_name)) {
448 // This type wasn't noticed by the name detection pass, so nothing has been treating this as
449 // a weak global name. We must mangle it to avoid conflicts with locals.
450 cur_name = try std.fmt.allocPrint(t.arena, "{s}_{d}", .{ want_name, t.getMangle() });
451 }
452
453 while (t.global_names.contains(cur_name)) {
454 cur_name = try std.fmt.allocPrint(t.arena, "{s}_{d}", .{ want_name, t.getMangle() });
455 }
456 return cur_name;
457}
458
459fn transRecordDecl(t: *Translator, scope: *Scope, record_qt: QualType) Error!void {
460 const base = record_qt.base(t.comp);
461 const record_ty = switch (base.type) {
462 .@"struct", .@"union" => |record_ty| record_ty,
463 else => unreachable,
464 };
465
466 if (t.type_decls.get(record_ty.decl_node)) |_|
467 return; // Avoid processing this decl twice
468
469 const toplevel = scope.id == .root;
470 const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(t) else undefined;
471
472 const container_kind: ZigTag = if (base.type == .@"union") .@"union" else .@"struct";
473 const container_kind_name = @tagName(container_kind);
474
475 var bare_name = record_ty.name.lookup(t.comp);
476 var is_unnamed = false;
477 var name = bare_name;
478
479 if (t.unnamed_typedefs.get(base.qt)) |typedef_name| {
480 bare_name = typedef_name;
481 name = typedef_name;
482 } else {
483 if (record_ty.isAnonymous(t.comp)) {
484 bare_name = try std.fmt.allocPrint(t.arena, "unnamed_{d}", .{t.getMangle()});
485 is_unnamed = true;
486 }
487 name = try std.fmt.allocPrint(t.arena, "{s}_{s}", .{ container_kind_name, bare_name });
488 if (toplevel and !is_unnamed) {
489 name = try t.mangleWeakGlobalName(name);
490 }
491 }
492 if (!toplevel) name = try bs.makeMangledName(name);
493 try t.type_decls.putNoClobber(t.gpa, record_ty.decl_node, name);
494
495 const is_pub = toplevel and !is_unnamed;
496 const init_node = init: {
497 if (record_ty.layout == null) {
498 try t.opaque_demotes.put(t.gpa, base.qt, {});
499 break :init ZigTag.opaque_literal.init();
500 }
501
502 var fields = try std.ArrayList(ast.Payload.Container.Field).initCapacity(t.gpa, record_ty.fields.len);
503 defer fields.deinit();
504
505 var functions = std.ArrayList(ZigNode).init(t.gpa);
506 defer functions.deinit();
507
508 var unnamed_field_count: u32 = 0;
509
510 // If a record doesn't have any attributes that would affect the alignment and
511 // layout, then we can just use a simple `extern` type. If it does have attributes,
512 // then we need to inspect the layout and assign an `align` value for each field.
513 const has_alignment_attributes = aligned: {
514 if (record_qt.hasAttribute(t.comp, .@"packed")) break :aligned true;
515 if (record_qt.hasAttribute(t.comp, .aligned)) break :aligned true;
516 for (record_ty.fields) |field| {
517 const field_attrs = field.attributes(t.comp);
518 for (field_attrs) |field_attr| {
519 switch (field_attr.tag) {
520 .@"packed", .aligned => break :aligned true,
521 else => {},
522 }
523 }
524 }
525 break :aligned false;
526 };
527 const head_field_alignment: ?c_uint = if (has_alignment_attributes) t.headFieldAlignment(record_ty) else null;
528
529 for (record_ty.fields, 0..) |field, field_index| {
530 const field_loc = field.name_tok;
531
532 // Demote record to opaque if it contains a bitfield
533 if (field.bit_width != .null) {
534 try t.opaque_demotes.put(t.gpa, base.qt, {});
535 try t.warn(scope, field_loc, "{s} demoted to opaque type - has bitfield", .{container_kind_name});
536 break :init ZigTag.opaque_literal.init();
537 }
538
539 var field_name = field.name.lookup(t.comp);
540 if (field.name_tok == 0) {
541 field_name = try std.fmt.allocPrint(t.arena, "unnamed_{d}", .{unnamed_field_count});
542 unnamed_field_count += 1;
543 try t.anonymous_record_field_names.put(t.gpa, .{
544 .parent = base.qt,
545 .field = field.qt,
546 }, field_name);
547 }
548
549 const field_alignment = if (has_alignment_attributes)
550 t.alignmentForField(record_ty, head_field_alignment, field_index)
551 else
552 null;
553
554 const field_type = field_type: {
555 // Check if this is a flexible array member.
556 flexible: {
557 if (field_index != record_ty.fields.len - 1 and container_kind != .@"union") break :flexible;
558 const array_ty = field.qt.get(t.comp, .array) orelse break :flexible;
559 if (array_ty.len != .incomplete and (array_ty.len != .fixed or array_ty.len.fixed != 0)) break :flexible;
560
561 const elem_type = t.transType(scope, array_ty.elem, field_loc) catch |err| switch (err) {
562 error.UnsupportedType => break :flexible,
563 else => |e| return e,
564 };
565 const zero_array = try ZigTag.array_type.create(t.arena, .{ .len = 0, .elem_type = elem_type });
566
567 const member_name = field_name;
568 field_name = try std.fmt.allocPrint(t.arena, "_{s}", .{field_name});
569
570 const member = try t.createFlexibleMemberFn(member_name, field_name);
571 try functions.append(member);
572
573 break :field_type zero_array;
574 }
575
576 break :field_type t.transType(scope, field.qt, field_loc) catch |err| switch (err) {
577 error.UnsupportedType => {
578 try t.opaque_demotes.put(t.gpa, base.qt, {});
579 try t.warn(scope, field.name_tok, "{s} demoted to opaque type - unable to translate type of field {s}", .{
580 container_kind_name,
581 field_name,
582 });
583 break :init ZigTag.opaque_literal.init();
584 },
585 else => |e| return e,
586 };
587 };
588
589 // C99 introduced designated initializers for structs. Omitted fields are implicitly
590 // initialized to zero. Some C APIs are designed with this in mind. Defaulting to zero
591 // values for translated struct fields permits Zig code to comfortably use such an API.
592 const default_value = if (container_kind == .@"struct")
593 try t.createZeroValueNode(field.qt, field_type, .no_as)
594 else
595 null;
596
597 fields.appendAssumeCapacity(.{
598 .name = field_name,
599 .type = field_type,
600 .alignment = field_alignment,
601 .default_value = default_value,
602 });
603 }
604
605 // A record is empty if it has no fields or only flexible array fields.
606 if (record_ty.fields.len == functions.items.len and
607 t.comp.target.os.tag == .windows and t.comp.target.abi == .msvc)
608 {
609 // In MSVC empty records have the same size as their alignment.
610 const padding_bits = record_ty.layout.?.size_bits;
611 const alignment_bits = record_ty.layout.?.field_alignment_bits;
612
613 try fields.append(.{
614 .name = "_padding",
615 .type = try ZigTag.type.create(t.arena, try std.fmt.allocPrint(t.arena, "u{d}", .{padding_bits})),
616 .alignment = @divExact(alignment_bits, 8),
617 .default_value = if (container_kind == .@"struct")
618 ZigTag.zero_literal.init()
619 else
620 null,
621 });
622 }
623
624 const container_payload = try t.arena.create(ast.Payload.Container);
625 container_payload.* = .{
626 .base = .{ .tag = container_kind },
627 .data = .{
628 .layout = .@"extern",
629 .fields = try t.arena.dupe(ast.Payload.Container.Field, fields.items),
630 .decls = try t.arena.dupe(ZigNode, functions.items),
631 },
632 };
633 break :init ZigNode.initPayload(&container_payload.base);
634 };
635
636 const payload = try t.arena.create(ast.Payload.SimpleVarDecl);
637 payload.* = .{
638 .base = .{ .tag = if (is_pub) .pub_var_simple else .var_simple },
639 .data = .{
640 .name = name,
641 .init = init_node,
642 },
643 };
644 const node = ZigNode.initPayload(&payload.base);
645 if (toplevel) {
646 try t.addTopLevelDecl(name, node);
647 // Only add the alias if the name is available *and* it was caught by
648 // name detection. Don't bother performing a weak mangle, since a
649 // mangled name is of no real use here.
650 if (!is_unnamed and !t.global_names.contains(bare_name) and t.weak_global_names.contains(bare_name))
651 try t.alias_list.append(t.gpa, .{ .alias = bare_name, .name = name });
652 try t.global_scope.container_member_fns_map.put(t.gpa, record_qt, .{
653 .container_decl_ptr = &payload.data.init,
654 });
655 } else {
656 try scope.appendNode(node);
657 try bs.discardVariable(name);
658 }
659}
660
661fn transFnDecl(t: *Translator, scope: *Scope, function: Node.Function) Error!void {
662 const func_ty = function.qt.get(t.comp, .func).?;
663
664 const is_pub = scope.id == .root;
665
666 const fn_name = t.tree.tokSlice(function.name_tok);
667 if (scope.getAlias(fn_name) != null or t.global_scope.containsNow(fn_name))
668 return; // Avoid processing this decl twice
669
670 const fn_decl_loc = function.name_tok;
671 const has_body = function.body != null and func_ty.kind != .variadic;
672 if (function.body != null and func_ty.kind == .variadic) {
673 try t.warn(scope, function.name_tok, "TODO unable to translate variadic function, demoted to extern", .{});
674 }
675
676 const is_always_inline = has_body and function.qt.getAttribute(t.comp, .always_inline) != null;
677 const proto_ctx: FnProtoContext = .{
678 .fn_name = fn_name,
679 .is_always_inline = is_always_inline,
680 .is_extern = !has_body,
681 .is_export = !function.static and has_body and !is_always_inline and !function.@"inline",
682 .is_pub = is_pub,
683 .has_body = has_body,
684 .cc = if (function.qt.getAttribute(t.comp, .calling_convention)) |some| switch (some.cc) {
685 .c => .c,
686 .stdcall => .x86_stdcall,
687 .thiscall => .x86_thiscall,
688 .fastcall => .x86_fastcall,
689 .regcall => .x86_regcall,
690 .riscv_vector => .riscv_vector,
691 .aarch64_sve_pcs => .aarch64_sve_pcs,
692 .aarch64_vector_pcs => .aarch64_vfabi,
693 .arm_aapcs => .arm_aapcs,
694 .arm_aapcs_vfp => .arm_aapcs_vfp,
695 .vectorcall => switch (t.comp.target.cpu.arch) {
696 .x86 => .x86_vectorcall,
697 .aarch64, .aarch64_be => .aarch64_vfabi,
698 else => .c,
699 },
700 .x86_64_sysv => .x86_64_sysv,
701 .x86_64_win => .x86_64_win,
702 } else .c,
703 };
704
705 const proto_node = t.transFnType(&t.global_scope.base, function.qt, func_ty, fn_decl_loc, proto_ctx) catch |err| switch (err) {
706 error.UnsupportedType => {
707 return t.failDecl(scope, fn_decl_loc, fn_name, "unable to resolve prototype of function", .{});
708 },
709 error.OutOfMemory => |e| return e,
710 };
711
712 const proto_payload = proto_node.castTag(.func).?;
713 if (!has_body) {
714 if (scope.id != .root) {
715 const bs: *Scope.Block = try scope.findBlockScope(t);
716 const mangled_name = try bs.createMangledName(fn_name, false, Scope.Block.extern_local_prefix);
717 const wrapped = try ZigTag.wrapped_local.create(t.arena, .{ .name = mangled_name, .init = proto_node });
718 try scope.appendNode(wrapped);
719 try bs.discardVariable(mangled_name);
720 return;
721 }
722 try t.global_scope.addMemberFunction(func_ty, proto_payload);
723 return t.addTopLevelDecl(fn_name, proto_node);
724 }
725
726 // actual function definition with body
727 const body_stmt = function.body.?.get(t.tree).compound_stmt;
728 var block_scope = try Scope.Block.init(t, &t.global_scope.base, false);
729 block_scope.return_type = func_ty.return_type;
730 defer block_scope.deinit();
731
732 var param_id: c_uint = 0;
733 for (proto_payload.data.params, func_ty.params) |*param, param_info| {
734 const param_name = param.name orelse {
735 proto_payload.data.is_extern = true;
736 proto_payload.data.is_export = false;
737 proto_payload.data.is_inline = false;
738 try t.warn(&t.global_scope.base, fn_decl_loc, "function {s} parameter has no name, demoted to extern", .{fn_name});
739 return t.addTopLevelDecl(fn_name, proto_node);
740 };
741
742 const is_const = param_info.qt.@"const";
743
744 const mangled_param_name = try block_scope.makeMangledName(param_name);
745 param.name = mangled_param_name;
746
747 if (!is_const) {
748 const bare_arg_name = try std.fmt.allocPrint(t.arena, "arg_{s}", .{mangled_param_name});
749 const arg_name = try block_scope.makeMangledName(bare_arg_name);
750 param.name = arg_name;
751
752 const redecl_node = try ZigTag.arg_redecl.create(t.arena, .{ .actual = mangled_param_name, .mangled = arg_name });
753 try block_scope.statements.append(t.gpa, redecl_node);
754 }
755 try block_scope.discardVariable(mangled_param_name);
756
757 param_id += 1;
758 }
759
760 t.transCompoundStmtInline(body_stmt, &block_scope) catch |err| switch (err) {
761 error.OutOfMemory => |e| return e,
762 error.UnsupportedTranslation,
763 error.UnsupportedType,
764 => {
765 proto_payload.data.is_extern = true;
766 proto_payload.data.is_export = false;
767 proto_payload.data.is_inline = false;
768 try t.warn(&t.global_scope.base, fn_decl_loc, "unable to translate function, demoted to extern", .{});
769 return t.addTopLevelDecl(fn_name, proto_node);
770 },
771 };
772
773 try t.global_scope.addMemberFunction(func_ty, proto_payload);
774 proto_payload.data.body = try block_scope.complete();
775 return t.addTopLevelDecl(fn_name, proto_node);
776}
777
778fn transVarDecl(t: *Translator, scope: *Scope, variable: Node.Variable) Error!void {
779 const base_name = t.tree.tokSlice(variable.name_tok);
780 const toplevel = scope.id == .root;
781 const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(t) else undefined;
782 const name, const use_base_name = blk: {
783 if (toplevel) break :blk .{ base_name, false };
784
785 // Local extern and static variables are wrapped in a struct.
786 const prefix: ?[]const u8 = switch (variable.storage_class) {
787 .@"extern" => Scope.Block.extern_local_prefix,
788 .static => Scope.Block.static_local_prefix,
789 else => null,
790 };
791 break :blk .{ try bs.createMangledName(base_name, false, prefix), prefix != null };
792 };
793
794 if (t.typeWasDemotedToOpaque(variable.qt)) {
795 if (variable.storage_class != .@"extern" and scope.id == .root) {
796 return t.failDecl(scope, variable.name_tok, name, "non-extern variable has opaque type", .{});
797 } else {
798 return t.failDecl(scope, variable.name_tok, name, "local variable has opaque type", .{});
799 }
800 }
801
802 const type_node = (if (variable.initializer) |init|
803 t.transTypeInit(scope, variable.qt, init, variable.name_tok)
804 else
805 t.transType(scope, variable.qt, variable.name_tok)) catch |err| switch (err) {
806 error.UnsupportedType => {
807 return t.failDecl(scope, variable.name_tok, name, "unable to translate variable declaration type", .{});
808 },
809 else => |e| return e,
810 };
811
812 const array_ty = variable.qt.get(t.comp, .array);
813 var is_const = variable.qt.@"const" or (array_ty != null and array_ty.?.elem.@"const");
814 var is_extern = variable.storage_class == .@"extern";
815
816 const init_node = init: {
817 if (variable.initializer) |init| {
818 const maybe_literal = init.get(t.tree);
819 const init_node = (if (maybe_literal == .string_literal_expr)
820 t.transStringLiteralInitializer(init, maybe_literal.string_literal_expr, type_node)
821 else
822 t.transExprCoercing(scope, init, .used)) catch |err| switch (err) {
823 error.UnsupportedTranslation, error.UnsupportedType => {
824 return t.failDecl(scope, variable.name_tok, name, "unable to resolve var init expr", .{});
825 },
826 else => |e| return e,
827 };
828
829 if (!variable.qt.is(t.comp, .bool) and init_node.isBoolRes()) {
830 break :init try ZigTag.int_from_bool.create(t.arena, init_node);
831 } else {
832 break :init init_node;
833 }
834 }
835 if (variable.storage_class == .@"extern") {
836 if (array_ty != null and array_ty.?.len == .incomplete) {
837 // Oh no, an extern array of unknown size! These are really fun because there's no
838 // direct equivalent in Zig. To translate correctly, we'll have to create a C-pointer
839 // to the data initialized via @extern.
840
841 // Since this is really a pointer to the underlying data, we tweak a few properties.
842 is_extern = false;
843 is_const = true;
844
845 const name_str = try std.fmt.allocPrint(t.arena, "\"{s}\"", .{base_name});
846 break :init try ZigTag.builtin_extern.create(t.arena, .{
847 .type = type_node,
848 .name = try ZigTag.string_literal.create(t.arena, name_str),
849 });
850 }
851 break :init null;
852 }
853 if (toplevel or variable.storage_class == .static or variable.thread_local) {
854 // The C language specification states that variables with static or threadlocal
855 // storage without an initializer are initialized to a zero value.
856 break :init try t.createZeroValueNode(variable.qt, type_node, .no_as);
857 }
858 break :init ZigTag.undefined_literal.init();
859 };
860
861 const linksection_string = blk: {
862 if (variable.qt.getAttribute(t.comp, .section)) |section| {
863 break :blk t.comp.interner.get(section.name.ref()).bytes;
864 }
865 break :blk null;
866 };
867
868 const alignment: ?c_uint = variable.qt.requestedAlignment(t.comp) orelse null;
869 var node = try ZigTag.var_decl.create(t.arena, .{
870 .is_pub = toplevel,
871 .is_const = is_const,
872 .is_extern = is_extern,
873 .is_export = toplevel and variable.storage_class == .auto,
874 .is_threadlocal = variable.thread_local,
875 .linksection_string = linksection_string,
876 .alignment = alignment,
877 .name = if (use_base_name) base_name else name,
878 .type = type_node,
879 .init = init_node,
880 });
881
882 if (toplevel) {
883 try t.addTopLevelDecl(name, node);
884 } else {
885 if (use_base_name) {
886 node = try ZigTag.wrapped_local.create(t.arena, .{ .name = name, .init = node });
887 }
888 try scope.appendNode(node);
889 try bs.discardVariable(name);
890
891 if (variable.qt.getAttribute(t.comp, .cleanup)) |cleanup_attr| {
892 const cleanup_fn_name = t.tree.tokSlice(cleanup_attr.function.tok);
893 const mangled_fn_name = scope.getAlias(cleanup_fn_name) orelse cleanup_fn_name;
894 const fn_id = try ZigTag.identifier.create(t.arena, mangled_fn_name);
895
896 const varname = try ZigTag.identifier.create(t.arena, name);
897 const args = try t.arena.alloc(ZigNode, 1);
898 args[0] = try ZigTag.address_of.create(t.arena, varname);
899
900 const cleanup_call = try ZigTag.call.create(t.arena, .{ .lhs = fn_id, .args = args });
901 const discard = try ZigTag.discard.create(t.arena, .{ .should_skip = false, .value = cleanup_call });
902 const deferred_cleanup = try ZigTag.@"defer".create(t.arena, discard);
903
904 try bs.statements.append(t.gpa, deferred_cleanup);
905 }
906 }
907}
908
909fn transEnumDecl(t: *Translator, scope: *Scope, enum_qt: QualType) Error!void {
910 const base = enum_qt.base(t.comp);
911 const enum_ty = base.type.@"enum";
912
913 if (t.type_decls.get(enum_ty.decl_node)) |_|
914 return; // Avoid processing this decl twice
915
916 const toplevel = scope.id == .root;
917 const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(t) else undefined;
918
919 var bare_name = enum_ty.name.lookup(t.comp);
920 var is_unnamed = false;
921 var name = bare_name;
922 if (t.unnamed_typedefs.get(base.qt)) |typedef_name| {
923 bare_name = typedef_name;
924 name = typedef_name;
925 } else {
926 if (enum_ty.isAnonymous(t.comp)) {
927 bare_name = try std.fmt.allocPrint(t.arena, "unnamed_{d}", .{t.getMangle()});
928 is_unnamed = true;
929 }
930 name = try std.fmt.allocPrint(t.arena, "enum_{s}", .{bare_name});
931 }
932 if (!toplevel) name = try bs.makeMangledName(name);
933 try t.type_decls.putNoClobber(t.gpa, enum_ty.decl_node, name);
934
935 const enum_type_node = if (!base.qt.hasIncompleteSize(t.comp)) blk: {
936 const enum_decl = enum_ty.decl_node.get(t.tree).enum_decl;
937 for (enum_ty.fields, enum_decl.fields) |field, field_node| {
938 var enum_val_name = field.name.lookup(t.comp);
939 if (!toplevel) {
940 enum_val_name = try bs.makeMangledName(enum_val_name);
941 }
942
943 const enum_const_type_node: ?ZigNode = t.transType(scope, field.qt, field.name_tok) catch |err| switch (err) {
944 error.UnsupportedType => null,
945 else => |e| return e,
946 };
947
948 const val = t.tree.value_map.get(field_node).?;
949 const enum_const_def = try ZigTag.enum_constant.create(t.arena, .{
950 .name = enum_val_name,
951 .is_public = toplevel,
952 .type = enum_const_type_node,
953 .value = try t.createIntNode(val),
954 });
955 if (toplevel)
956 try t.addTopLevelDecl(enum_val_name, enum_const_def)
957 else {
958 try scope.appendNode(enum_const_def);
959 try bs.discardVariable(enum_val_name);
960 }
961 }
962
963 break :blk t.transType(scope, enum_ty.tag.?, enum_decl.name_or_kind_tok) catch |err| switch (err) {
964 error.UnsupportedType => {
965 return t.failDecl(scope, enum_decl.name_or_kind_tok, name, "unable to translate enum integer type", .{});
966 },
967 else => |e| return e,
968 };
969 } else blk: {
970 try t.opaque_demotes.put(t.gpa, base.qt, {});
971 break :blk ZigTag.opaque_literal.init();
972 };
973
974 const is_pub = toplevel and !is_unnamed;
975 const payload = try t.arena.create(ast.Payload.SimpleVarDecl);
976 payload.* = .{
977 .base = .{ .tag = if (is_pub) .pub_var_simple else .var_simple },
978 .data = .{
979 .init = enum_type_node,
980 .name = name,
981 },
982 };
983 const node = ZigNode.initPayload(&payload.base);
984 if (toplevel) {
985 try t.addTopLevelDecl(name, node);
986 if (!is_unnamed)
987 try t.alias_list.append(t.gpa, .{ .alias = bare_name, .name = name });
988 } else {
989 try scope.appendNode(node);
990 try bs.discardVariable(name);
991 }
992}
993
994fn transStaticAssert(t: *Translator, scope: *Scope, static_assert: Node.StaticAssert) Error!void {
995 const condition = t.transExpr(scope, static_assert.cond, .used) catch |err| switch (err) {
996 error.UnsupportedTranslation, error.UnsupportedType => {
997 return try t.warn(&t.global_scope.base, static_assert.cond.tok(t.tree), "unable to translate _Static_assert condition", .{});
998 },
999 error.OutOfMemory => |e| return e,
1000 };
1001
1002 // generate @compileError message that matches C compiler output
1003 const diagnostic = if (static_assert.message) |message| str: {
1004 // Aro guarantees this to be a string literal.
1005 const str_val = t.tree.value_map.get(message).?;
1006 const str_qt = message.qt(t.tree);
1007
1008 const bytes = t.comp.interner.get(str_val.ref()).bytes;
1009 var allocating: std.Io.Writer.Allocating = .init(t.gpa);
1010 defer allocating.deinit();
1011
1012 allocating.writer.writeAll("\"static assertion failed \\") catch return error.OutOfMemory;
1013
1014 aro.Value.printString(bytes, str_qt, t.comp, &allocating.writer) catch return error.OutOfMemory;
1015 allocating.writer.end -= 1; // printString adds a terminating " so we need to remove it
1016 allocating.writer.writeAll("\\\"\"") catch return error.OutOfMemory;
1017
1018 break :str try ZigTag.string_literal.create(t.arena, try t.arena.dupe(u8, allocating.getWritten()));
1019 } else try ZigTag.string_literal.create(t.arena, "\"static assertion failed\"");
1020
1021 const assert_node = try ZigTag.static_assert.create(t.arena, .{ .lhs = condition, .rhs = diagnostic });
1022 try scope.appendNode(assert_node);
1023}
1024
1025fn transGlobalAsm(t: *Translator, scope: *Scope, global_asm: Node.SimpleAsm) Error!void {
1026 const asm_string = t.tree.value_map.get(global_asm.asm_str).?;
1027 const bytes = t.comp.interner.get(asm_string.ref()).bytes;
1028
1029 var allocating: std.Io.Writer.Allocating = try .initCapacity(t.gpa, bytes.len);
1030 defer allocating.deinit();
1031 aro.Value.printString(bytes, global_asm.asm_str.qt(t.tree), t.comp, &allocating.writer) catch return error.OutOfMemory;
1032
1033 const str_node = try ZigTag.string_literal.create(t.arena, try t.arena.dupe(u8, allocating.getWritten()));
1034
1035 const asm_node = try ZigTag.asm_simple.create(t.arena, str_node);
1036 const block = try ZigTag.block_single.create(t.arena, asm_node);
1037 const comptime_node = try ZigTag.@"comptime".create(t.arena, block);
1038
1039 try scope.appendNode(comptime_node);
1040}
1041
1042// ================
1043// Type translation
1044// ================
1045
1046fn getTypeStr(t: *Translator, qt: QualType) ![]const u8 {
1047 var allocating: std.Io.Writer.Allocating = .init(t.gpa);
1048 defer allocating.deinit();
1049 qt.print(t.comp, &allocating.writer) catch return error.OutOfMemory;
1050 return t.arena.dupe(u8, allocating.getWritten());
1051}
1052
1053fn transType(t: *Translator, scope: *Scope, qt: QualType, source_loc: TokenIndex) TypeError!ZigNode {
1054 loop: switch (qt.type(t.comp)) {
1055 .atomic => {
1056 const type_name = try t.getTypeStr(qt);
1057 return t.fail(error.UnsupportedType, source_loc, "TODO support atomic type: '{s}'", .{type_name});
1058 },
1059 .void => return ZigTag.type.create(t.arena, "anyopaque"),
1060 .bool => return ZigTag.type.create(t.arena, "bool"),
1061 .int => |int_ty| switch (int_ty) {
1062 //.char => return ZigTag.type.create(t.arena, "c_char"), // TODO: this is the preferred translation
1063 .char => return ZigTag.type.create(t.arena, "u8"),
1064 .schar => return ZigTag.type.create(t.arena, "i8"),
1065 .uchar => return ZigTag.type.create(t.arena, "u8"),
1066 .short => return ZigTag.type.create(t.arena, "c_short"),
1067 .ushort => return ZigTag.type.create(t.arena, "c_ushort"),
1068 .int => return ZigTag.type.create(t.arena, "c_int"),
1069 .uint => return ZigTag.type.create(t.arena, "c_uint"),
1070 .long => return ZigTag.type.create(t.arena, "c_long"),
1071 .ulong => return ZigTag.type.create(t.arena, "c_ulong"),
1072 .long_long => return ZigTag.type.create(t.arena, "c_longlong"),
1073 .ulong_long => return ZigTag.type.create(t.arena, "c_ulonglong"),
1074 .int128 => return ZigTag.type.create(t.arena, "i128"),
1075 .uint128 => return ZigTag.type.create(t.arena, "u128"),
1076 },
1077 .float => |float_ty| switch (float_ty) {
1078 .fp16, .float16 => return ZigTag.type.create(t.arena, "f16"),
1079 .float => return ZigTag.type.create(t.arena, "f32"),
1080 .double => return ZigTag.type.create(t.arena, "f64"),
1081 .long_double => return ZigTag.type.create(t.arena, "c_longdouble"),
1082 .float128 => return ZigTag.type.create(t.arena, "f128"),
1083 },
1084 .pointer => |pointer_ty| {
1085 const child_qt = pointer_ty.child;
1086
1087 const is_fn_proto = child_qt.is(t.comp, .func);
1088 const is_const = is_fn_proto or child_qt.@"const";
1089 const is_volatile = child_qt.@"volatile";
1090 const elem_type = try t.transType(scope, child_qt, source_loc);
1091 const ptr_info: @FieldType(ast.Payload.Pointer, "data") = .{
1092 .is_const = is_const,
1093 .is_volatile = is_volatile,
1094 .elem_type = elem_type,
1095 .is_allowzero = false,
1096 };
1097 if (is_fn_proto or
1098 t.typeIsOpaque(child_qt) or
1099 t.typeWasDemotedToOpaque(child_qt))
1100 {
1101 const ptr = try ZigTag.single_pointer.create(t.arena, ptr_info);
1102 return ZigTag.optional_type.create(t.arena, ptr);
1103 }
1104
1105 return ZigTag.c_pointer.create(t.arena, ptr_info);
1106 },
1107 .array => |array_ty| {
1108 const elem_qt = array_ty.elem;
1109 switch (array_ty.len) {
1110 .incomplete, .unspecified_variable => {
1111 const elem_type = try t.transType(scope, elem_qt, source_loc);
1112 return ZigTag.c_pointer.create(t.arena, .{
1113 .is_const = elem_qt.@"const",
1114 .is_volatile = elem_qt.@"volatile",
1115 .is_allowzero = false,
1116 .elem_type = elem_type,
1117 });
1118 },
1119 .fixed, .static => |len| {
1120 const elem_type = try t.transType(scope, elem_qt, source_loc);
1121 return ZigTag.array_type.create(t.arena, .{ .len = len, .elem_type = elem_type });
1122 },
1123 .variable => return t.fail(error.UnsupportedType, source_loc, "VLA unsupported '{s}'", .{try t.getTypeStr(qt)}),
1124 }
1125 },
1126 .func => |func_ty| return t.transFnType(scope, qt, func_ty, source_loc, .{}),
1127 .@"struct", .@"union" => |record_ty| {
1128 var trans_scope = scope;
1129 if (!record_ty.isAnonymous(t.comp)) {
1130 if (t.weak_global_names.contains(record_ty.name.lookup(t.comp))) trans_scope = &t.global_scope.base;
1131 }
1132 try t.transRecordDecl(trans_scope, qt);
1133 const name = t.type_decls.get(record_ty.decl_node).?;
1134 return ZigTag.identifier.create(t.arena, name);
1135 },
1136 .@"enum" => |enum_ty| {
1137 var trans_scope = scope;
1138 const is_anonymous = enum_ty.isAnonymous(t.comp);
1139 if (!is_anonymous) {
1140 if (t.weak_global_names.contains(enum_ty.name.lookup(t.comp))) trans_scope = &t.global_scope.base;
1141 }
1142 try t.transEnumDecl(trans_scope, qt);
1143 const name = t.type_decls.get(enum_ty.decl_node).?;
1144 return ZigTag.identifier.create(t.arena, name);
1145 },
1146 .typedef => |typedef_ty| {
1147 var trans_scope = scope;
1148 const typedef_name = typedef_ty.name.lookup(t.comp);
1149 if (builtin_typedef_map.get(typedef_name)) |builtin| return ZigTag.type.create(t.arena, builtin);
1150 if (t.global_names.contains(typedef_name)) trans_scope = &t.global_scope.base;
1151
1152 try t.transTypeDef(trans_scope, typedef_ty.decl_node);
1153 const name = t.type_decls.get(typedef_ty.decl_node).?;
1154 return ZigTag.identifier.create(t.arena, name);
1155 },
1156 .attributed => |attributed_ty| continue :loop attributed_ty.base.type(t.comp),
1157 .typeof => |typeof_ty| continue :loop typeof_ty.base.type(t.comp),
1158 .vector => |vector_ty| {
1159 const len = try t.createNumberNode(vector_ty.len, .int);
1160 const elem_type = try t.transType(scope, vector_ty.elem, source_loc);
1161 return ZigTag.vector.create(t.arena, .{ .lhs = len, .rhs = elem_type });
1162 },
1163 else => return t.fail(error.UnsupportedType, source_loc, "unsupported type: '{s}'", .{try t.getTypeStr(qt)}),
1164 }
1165}
1166
1167/// Look ahead through the fields of the record to determine what the alignment of the record
1168/// would be without any align/packed/etc. attributes. This helps us determine whether or not
1169/// the fields with 0 offset need an `align` qualifier. Strictly speaking, we could just
1170/// pedantically assign those fields the same alignment as the parent's pointer alignment,
1171/// but this helps the generated code to be a little less verbose.
1172fn headFieldAlignment(t: *Translator, record_decl: aro.Type.Record) ?c_uint {
1173 const bits_per_byte = 8;
1174 const parent_ptr_alignment_bits = record_decl.layout.?.pointer_alignment_bits;
1175 const parent_ptr_alignment = parent_ptr_alignment_bits / bits_per_byte;
1176 var max_field_alignment_bits: u64 = 0;
1177 for (record_decl.fields) |field| {
1178 if (field.qt.getRecord(t.comp)) |field_record_decl| {
1179 const child_record_alignment = field_record_decl.layout.?.field_alignment_bits;
1180 if (child_record_alignment > max_field_alignment_bits)
1181 max_field_alignment_bits = child_record_alignment;
1182 } else {
1183 const field_size = field.layout.size_bits;
1184 if (field_size > max_field_alignment_bits)
1185 max_field_alignment_bits = field_size;
1186 }
1187 }
1188 if (max_field_alignment_bits != parent_ptr_alignment_bits) {
1189 return parent_ptr_alignment;
1190 } else {
1191 return null;
1192 }
1193}
1194
1195/// This function inspects the generated layout of a record to determine the alignment for a
1196/// particular field. This approach is necessary because unlike Zig, a C compiler is not
1197/// required to fulfill the requested alignment, which means we'd risk generating different code
1198/// if we only look at the user-requested alignment.
1199///
1200/// Returns a ?c_uint to match Clang's behavior of using c_uint. The return type can be changed
1201/// after the Clang frontend for translate-c is removed. A null value indicates that a field is
1202/// 'naturally aligned'.
1203fn alignmentForField(
1204 t: *Translator,
1205 record_decl: aro.Type.Record,
1206 head_field_alignment: ?c_uint,
1207 field_index: usize,
1208) ?c_uint {
1209 const fields = record_decl.fields;
1210 assert(fields.len != 0);
1211 const field = fields[field_index];
1212
1213 const bits_per_byte = 8;
1214 const parent_ptr_alignment_bits = record_decl.layout.?.pointer_alignment_bits;
1215 const parent_ptr_alignment = parent_ptr_alignment_bits / bits_per_byte;
1216
1217 // bitfields aren't supported yet. Until support is added, records with bitfields
1218 // should be demoted to opaque, and this function shouldn't be called for them.
1219 if (field.bit_width != .null) {
1220 @panic("TODO: add bitfield support for records");
1221 }
1222
1223 const field_offset_bits: u64 = field.layout.offset_bits;
1224 const field_size_bits: u64 = field.layout.size_bits;
1225
1226 // Fields with zero width always have an alignment of 1
1227 if (field_size_bits == 0) {
1228 return 1;
1229 }
1230
1231 // Fields with 0 offset inherit the parent's pointer alignment.
1232 if (field_offset_bits == 0) {
1233 return head_field_alignment;
1234 }
1235
1236 // Records have a natural alignment when used as a field, and their size is
1237 // a multiple of this alignment value. For all other types, the natural alignment
1238 // is their size.
1239 const field_natural_alignment_bits: u64 = if (field.qt.getRecord(t.comp)) |record|
1240 record.layout.?.field_alignment_bits
1241 else
1242 field_size_bits;
1243 const rem_bits = field_offset_bits % field_natural_alignment_bits;
1244
1245 // If there's a remainder, then the alignment is smaller than the field's
1246 // natural alignment
1247 if (rem_bits > 0) {
1248 const rem_alignment = rem_bits / bits_per_byte;
1249 if (rem_alignment > 0 and std.math.isPowerOfTwo(rem_alignment)) {
1250 const actual_alignment = @min(rem_alignment, parent_ptr_alignment);
1251 return @as(c_uint, @truncate(actual_alignment));
1252 } else {
1253 return 1;
1254 }
1255 }
1256
1257 // A field may have an offset which positions it to be naturally aligned, but the
1258 // parent's pointer alignment determines if this is actually true, so we take the minimum
1259 // value.
1260 // For example, a float field (4 bytes wide) with a 4 byte offset is positioned to have natural
1261 // alignment, but if the parent pointer alignment is 2, then the actual alignment of the
1262 // float is 2.
1263 const field_natural_alignment: u64 = field_natural_alignment_bits / bits_per_byte;
1264 const offset_alignment = field_offset_bits / bits_per_byte;
1265 const possible_alignment = @min(parent_ptr_alignment, offset_alignment);
1266 if (possible_alignment == field_natural_alignment) {
1267 return null;
1268 } else if (possible_alignment < field_natural_alignment) {
1269 if (std.math.isPowerOfTwo(possible_alignment)) {
1270 return possible_alignment;
1271 } else {
1272 return 1;
1273 }
1274 } else { // possible_alignment > field_natural_alignment
1275 // Here, the field is positioned be at a higher alignment than it's natural alignment. This means we
1276 // need to determine whether it's a specified alignment. We can determine that from the padding preceding
1277 // the field.
1278 const padding_from_prev_field: u64 = blk: {
1279 if (field_offset_bits != 0) {
1280 const previous_field = fields[field_index - 1];
1281 break :blk (field_offset_bits - previous_field.layout.offset_bits) - previous_field.layout.size_bits;
1282 } else {
1283 break :blk 0;
1284 }
1285 };
1286 if (padding_from_prev_field < field_natural_alignment_bits) {
1287 return null;
1288 } else {
1289 return possible_alignment;
1290 }
1291 }
1292}
1293
1294const FnProtoContext = struct {
1295 is_pub: bool = false,
1296 is_export: bool = false,
1297 is_extern: bool = false,
1298 is_always_inline: bool = false,
1299 fn_name: ?[]const u8 = null,
1300 has_body: bool = false,
1301 cc: ast.Payload.Func.CallingConvention = .c,
1302};
1303
1304fn transFnType(
1305 t: *Translator,
1306 scope: *Scope,
1307 func_qt: QualType,
1308 func_ty: aro.Type.Func,
1309 source_loc: TokenIndex,
1310 ctx: FnProtoContext,
1311) !ZigNode {
1312 const param_count: usize = func_ty.params.len;
1313 const fn_params = try t.arena.alloc(ast.Payload.Param, param_count);
1314
1315 for (func_ty.params, fn_params) |param_info, *param_node| {
1316 const param_qt = param_info.qt;
1317 const is_noalias = param_qt.restrict;
1318
1319 const param_name: ?[]const u8 = if (param_info.name == .empty)
1320 null
1321 else
1322 param_info.name.lookup(t.comp);
1323
1324 const type_node = try t.transType(scope, param_qt, param_info.name_tok);
1325 param_node.* = .{
1326 .is_noalias = is_noalias,
1327 .name = param_name,
1328 .type = type_node,
1329 };
1330 }
1331
1332 const linksection_string = blk: {
1333 if (func_qt.getAttribute(t.comp, .section)) |section| {
1334 break :blk t.comp.interner.get(section.name.ref()).bytes;
1335 }
1336 break :blk null;
1337 };
1338
1339 const alignment: ?c_uint = func_qt.requestedAlignment(t.comp) orelse null;
1340
1341 const explicit_callconv = if ((ctx.is_always_inline or ctx.is_export or ctx.is_extern) and ctx.cc == .c) null else ctx.cc;
1342
1343 const return_type_node = blk: {
1344 if (func_qt.getAttribute(t.comp, .noreturn) != null) {
1345 break :blk ZigTag.noreturn_type.init();
1346 } else {
1347 const return_qt = func_ty.return_type;
1348 if (return_qt.is(t.comp, .void)) {
1349 // convert primitive anyopaque to actual void (only for return type)
1350 break :blk ZigTag.void_type.init();
1351 } else {
1352 break :blk t.transType(scope, return_qt, source_loc) catch |err| switch (err) {
1353 error.UnsupportedType => {
1354 try t.warn(scope, source_loc, "unsupported function proto return type", .{});
1355 return err;
1356 },
1357 error.OutOfMemory => |e| return e,
1358 };
1359 }
1360 }
1361 };
1362
1363 const payload = try t.arena.create(ast.Payload.Func);
1364 payload.* = .{
1365 .base = .{ .tag = .func },
1366 .data = .{
1367 .is_pub = ctx.is_pub,
1368 .is_extern = ctx.is_extern,
1369 .is_export = ctx.is_export,
1370 .is_inline = ctx.is_always_inline,
1371 .is_var_args = switch (func_ty.kind) {
1372 .normal => false,
1373 .variadic => true,
1374 .old_style => !ctx.is_export and !ctx.is_always_inline and !ctx.has_body,
1375 },
1376 .name = ctx.fn_name,
1377 .linksection_string = linksection_string,
1378 .explicit_callconv = explicit_callconv,
1379 .params = fn_params,
1380 .return_type = return_type_node,
1381 .body = null,
1382 .alignment = alignment,
1383 },
1384 };
1385 return ZigNode.initPayload(&payload.base);
1386}
1387
1388/// Produces a Zig AST node by translating a Type, respecting the width, but modifying the signed-ness.
1389/// Asserts the type is an integer.
1390fn transTypeIntWidthOf(t: *Translator, qt: QualType, is_signed: bool) TypeError!ZigNode {
1391 return ZigTag.type.create(t.arena, loop: switch (qt.base(t.comp).type) {
1392 .int => |int_ty| switch (int_ty) {
1393 .char, .schar, .uchar => if (is_signed) "i8" else "u8",
1394 .short, .ushort => if (is_signed) "c_short" else "c_ushort",
1395 .int, .uint => if (is_signed) "c_int" else "c_uint",
1396 .long, .ulong => if (is_signed) "c_long" else "c_ulong",
1397 .long_long, .ulong_long => if (is_signed) "c_longlong" else "c_ulonglong",
1398 .int128, .uint128 => if (is_signed) "i128" else "u128",
1399 },
1400 .bit_int => |bit_int_ty| try std.fmt.allocPrint(t.arena, "{s}{d}", .{
1401 if (is_signed) "i" else "u",
1402 bit_int_ty.bits,
1403 }),
1404 .@"enum" => |enum_ty| blk: {
1405 const tag_ty = enum_ty.tag orelse
1406 break :blk if (is_signed) "c_int" else "c_uint";
1407
1408 continue :loop tag_ty.base(t.comp).type;
1409 },
1410 else => unreachable, // only call this function when it has already been determined the type is int
1411 });
1412}
1413
1414fn transTypeInit(
1415 t: *Translator,
1416 scope: *Scope,
1417 qt: QualType,
1418 init: Node.Index,
1419 source_loc: TokenIndex,
1420) TypeError!ZigNode {
1421 switch (init.get(t.tree)) {
1422 .string_literal_expr => |literal| {
1423 const elem_ty = try t.transType(scope, qt.childType(t.comp), source_loc);
1424
1425 const string_lit_size = literal.qt.arrayLen(t.comp).?;
1426 const array_size = qt.arrayLen(t.comp).?;
1427
1428 if (array_size == string_lit_size) {
1429 return ZigTag.null_sentinel_array_type.create(t.arena, .{ .len = array_size - 1, .elem_type = elem_ty });
1430 } else {
1431 return ZigTag.array_type.create(t.arena, .{ .len = array_size, .elem_type = elem_ty });
1432 }
1433 },
1434 else => {},
1435 }
1436 return t.transType(scope, qt, source_loc);
1437}
1438
1439// ============
1440// Type helpers
1441// ============
1442
1443fn typeIsOpaque(t: *Translator, qt: QualType) bool {
1444 return switch (qt.base(t.comp).type) {
1445 .void => true,
1446 .@"struct", .@"union" => |record_ty| {
1447 if (record_ty.layout == null) return true;
1448 for (record_ty.fields) |field| {
1449 if (field.bit_width != .null) return true;
1450 }
1451 return false;
1452 },
1453 else => false,
1454 };
1455}
1456
1457fn typeWasDemotedToOpaque(t: *Translator, qt: QualType) bool {
1458 const base = qt.base(t.comp);
1459 switch (base.type) {
1460 .@"struct", .@"union" => |record_ty| {
1461 if (t.opaque_demotes.contains(base.qt)) return true;
1462 for (record_ty.fields) |field| {
1463 if (t.typeWasDemotedToOpaque(field.qt)) return true;
1464 }
1465 return false;
1466 },
1467 .@"enum" => return t.opaque_demotes.contains(base.qt),
1468 else => return false,
1469 }
1470}
1471
1472fn typeHasWrappingOverflow(t: *Translator, qt: QualType) bool {
1473 if (t.signedness(qt) == .unsigned) {
1474 // unsigned integer overflow wraps around.
1475 return true;
1476 } else {
1477 // float, signed integer, and pointer overflow is undefined behavior.
1478 return false;
1479 }
1480}
1481
1482/// Signedness of type when translated to Zig.
1483/// Different from `QualType.signedness()` for `char` and enums.
1484/// Returns null for non-int types.
1485fn signedness(t: *Translator, qt: QualType) ?std.builtin.Signedness {
1486 return loop: switch (qt.base(t.comp).type) {
1487 .bool => .unsigned,
1488 .bit_int => |bit_int| bit_int.signedness,
1489 .int => |int_ty| switch (int_ty) {
1490 .char => .unsigned, // Always translated as u8
1491 .schar, .short, .int, .long, .long_long, .int128 => .signed,
1492 .uchar, .ushort, .uint, .ulong, .ulong_long, .uint128 => .unsigned,
1493 },
1494 .@"enum" => |enum_ty| {
1495 const tag_qt = enum_ty.tag orelse return .signed;
1496 continue :loop tag_qt.base(t.comp).type;
1497 },
1498 else => return null,
1499 };
1500}
1501
1502// =====================
1503// Statement translation
1504// =====================
1505
1506fn transStmt(t: *Translator, scope: *Scope, stmt: Node.Index) TransError!ZigNode {
1507 switch (stmt.get(t.tree)) {
1508 .compound_stmt => |compound| {
1509 return t.transCompoundStmt(scope, compound);
1510 },
1511 .static_assert => |static_assert| {
1512 try t.transStaticAssert(scope, static_assert);
1513 return ZigTag.declaration.init();
1514 },
1515 .return_stmt => |return_stmt| return t.transReturnStmt(scope, return_stmt),
1516 .null_stmt => return ZigTag.empty_block.init(),
1517 .if_stmt => |if_stmt| return t.transIfStmt(scope, if_stmt),
1518 .while_stmt => |while_stmt| return t.transWhileStmt(scope, while_stmt),
1519 .do_while_stmt => |do_while_stmt| return t.transDoWhileStmt(scope, do_while_stmt),
1520 .for_stmt => |for_stmt| return t.transForStmt(scope, for_stmt),
1521 .continue_stmt => return ZigTag.@"continue".init(),
1522 .break_stmt => return ZigTag.@"break".init(),
1523 .typedef => |typedef_decl| {
1524 assert(!typedef_decl.implicit);
1525 try t.transTypeDef(scope, stmt);
1526 return ZigTag.declaration.init();
1527 },
1528 .struct_decl, .union_decl => |record_decl| {
1529 try t.transRecordDecl(scope, record_decl.container_qt);
1530 return ZigTag.declaration.init();
1531 },
1532 .enum_decl => |enum_decl| {
1533 try t.transEnumDecl(scope, enum_decl.container_qt);
1534 return ZigTag.declaration.init();
1535 },
1536 .function => |function| {
1537 try t.transFnDecl(scope, function);
1538 return ZigTag.declaration.init();
1539 },
1540 .variable => |variable| {
1541 try t.transVarDecl(scope, variable);
1542 return ZigTag.declaration.init();
1543 },
1544 .switch_stmt => |switch_stmt| return t.transSwitch(scope, switch_stmt),
1545 .case_stmt, .default_stmt => {
1546 return t.fail(error.UnsupportedTranslation, stmt.tok(t.tree), "TODO complex switch", .{});
1547 },
1548 .goto_stmt, .computed_goto_stmt, .labeled_stmt => {
1549 return t.fail(error.UnsupportedTranslation, stmt.tok(t.tree), "TODO goto", .{});
1550 },
1551 else => return t.transExprCoercing(scope, stmt, .unused),
1552 }
1553}
1554
1555fn transCompoundStmtInline(t: *Translator, compound: Node.CompoundStmt, block: *Scope.Block) TransError!void {
1556 for (compound.body) |stmt| {
1557 const result = try t.transStmt(&block.base, stmt);
1558 switch (result.tag()) {
1559 .declaration, .empty_block => {},
1560 else => try block.statements.append(t.gpa, result),
1561 }
1562 }
1563}
1564
1565fn transCompoundStmt(t: *Translator, scope: *Scope, compound: Node.CompoundStmt) TransError!ZigNode {
1566 var block_scope = try Scope.Block.init(t, scope, false);
1567 defer block_scope.deinit();
1568 try t.transCompoundStmtInline(compound, &block_scope);
1569 return try block_scope.complete();
1570}
1571
1572fn transReturnStmt(t: *Translator, scope: *Scope, return_stmt: Node.ReturnStmt) TransError!ZigNode {
1573 switch (return_stmt.operand) {
1574 .none => return ZigTag.return_void.init(),
1575 .expr => |operand| {
1576 var rhs = try t.transExprCoercing(scope, operand, .used);
1577 const return_qt = scope.findBlockReturnType();
1578 if (rhs.isBoolRes() and !return_qt.is(t.comp, .bool)) {
1579 rhs = try ZigTag.int_from_bool.create(t.arena, rhs);
1580 }
1581 return ZigTag.@"return".create(t.arena, rhs);
1582 },
1583 .implicit => |zero| {
1584 if (zero) return ZigTag.@"return".create(t.arena, ZigTag.zero_literal.init());
1585
1586 const return_qt = scope.findBlockReturnType();
1587 if (return_qt.is(t.comp, .void)) return ZigTag.empty_block.init();
1588
1589 return ZigTag.@"return".create(t.arena, ZigTag.undefined_literal.init());
1590 },
1591 }
1592}
1593
1594/// If a statement can possibly translate to a Zig assignment (either directly because it's
1595/// an assignment in C or indirectly via result assignment to `_`) AND it's the sole statement
1596/// in the body of an if statement or loop, then we need to put the statement into its own block.
1597/// The `else` case here corresponds to statements that could result in an assignment. If a statement
1598/// class never needs a block, add its enum to the top prong.
1599fn maybeBlockify(t: *Translator, scope: *Scope, stmt: Node.Index) TransError!ZigNode {
1600 switch (stmt.get(t.tree)) {
1601 .break_stmt,
1602 .continue_stmt,
1603 .compound_stmt,
1604 .decl_ref_expr,
1605 .enumeration_ref,
1606 .do_while_stmt,
1607 .for_stmt,
1608 .if_stmt,
1609 .return_stmt,
1610 .null_stmt,
1611 .while_stmt,
1612 => return t.transStmt(scope, stmt),
1613 else => return t.blockify(scope, stmt),
1614 }
1615}
1616
1617/// Translate statement and place it in its own block.
1618fn blockify(t: *Translator, scope: *Scope, stmt: Node.Index) TransError!ZigNode {
1619 var block_scope = try Scope.Block.init(t, scope, false);
1620 defer block_scope.deinit();
1621 const result = try t.transStmt(&block_scope.base, stmt);
1622 try block_scope.statements.append(t.gpa, result);
1623 return block_scope.complete();
1624}
1625
1626fn transIfStmt(t: *Translator, scope: *Scope, if_stmt: Node.IfStmt) TransError!ZigNode {
1627 var cond_scope: Scope.Condition = .{
1628 .base = .{
1629 .parent = scope,
1630 .id = .condition,
1631 },
1632 };
1633 defer cond_scope.deinit();
1634 const cond = try t.transBoolExpr(&cond_scope.base, if_stmt.cond);
1635
1636 // block needed to keep else statement from attaching to inner while
1637 const must_blockify = (if_stmt.else_body != null) and switch (if_stmt.then_body.get(t.tree)) {
1638 .while_stmt, .do_while_stmt, .for_stmt => true,
1639 else => false,
1640 };
1641
1642 const then_node = if (must_blockify)
1643 try t.blockify(scope, if_stmt.then_body)
1644 else
1645 try t.maybeBlockify(scope, if_stmt.then_body);
1646
1647 const else_node = if (if_stmt.else_body) |stmt|
1648 try t.maybeBlockify(scope, stmt)
1649 else
1650 null;
1651 return ZigTag.@"if".create(t.arena, .{ .cond = cond, .then = then_node, .@"else" = else_node });
1652}
1653
1654fn transWhileStmt(t: *Translator, scope: *Scope, while_stmt: Node.WhileStmt) TransError!ZigNode {
1655 var cond_scope: Scope.Condition = .{
1656 .base = .{
1657 .parent = scope,
1658 .id = .condition,
1659 },
1660 };
1661 defer cond_scope.deinit();
1662 const cond = try t.transBoolExpr(&cond_scope.base, while_stmt.cond);
1663
1664 var loop_scope: Scope = .{
1665 .parent = scope,
1666 .id = .loop,
1667 };
1668 const body = try t.maybeBlockify(&loop_scope, while_stmt.body);
1669 return ZigTag.@"while".create(t.arena, .{ .cond = cond, .body = body, .cont_expr = null });
1670}
1671
1672fn transDoWhileStmt(t: *Translator, scope: *Scope, do_stmt: Node.DoWhileStmt) TransError!ZigNode {
1673 var loop_scope: Scope = .{
1674 .parent = scope,
1675 .id = .do_loop,
1676 };
1677
1678 // if (!cond) break;
1679 var cond_scope: Scope.Condition = .{
1680 .base = .{
1681 .parent = scope,
1682 .id = .condition,
1683 },
1684 };
1685 defer cond_scope.deinit();
1686 const cond = try t.transBoolExpr(&cond_scope.base, do_stmt.cond);
1687 const if_not_break = switch (cond.tag()) {
1688 .true_literal => {
1689 const body_node = try t.maybeBlockify(scope, do_stmt.body);
1690 return ZigTag.while_true.create(t.arena, body_node);
1691 },
1692 else => try ZigTag.if_not_break.create(t.arena, cond),
1693 };
1694
1695 var body_node = try t.transStmt(&loop_scope, do_stmt.body);
1696 if (body_node.isNoreturn(true)) {
1697 // The body node ends in a noreturn statement. Simply put it in a while (true)
1698 // in case it contains breaks or continues.
1699 } else if (do_stmt.body.get(t.tree) == .compound_stmt) {
1700 // there's already a block in C, so we'll append our condition to it.
1701 // c: do {
1702 // c: a;
1703 // c: b;
1704 // c: } while(c);
1705 // zig: while (true) {
1706 // zig: a;
1707 // zig: b;
1708 // zig: if (!cond) break;
1709 // zig: }
1710 const block = body_node.castTag(.block).?;
1711 block.data.stmts.len += 1; // This is safe since we reserve one extra space in Scope.Block.complete.
1712 block.data.stmts[block.data.stmts.len - 1] = if_not_break;
1713 } else {
1714 // the C statement is without a block, so we need to create a block to contain it.
1715 // c: do
1716 // c: a;
1717 // c: while(c);
1718 // zig: while (true) {
1719 // zig: a;
1720 // zig: if (!cond) break;
1721 // zig: }
1722 const statements = try t.arena.alloc(ZigNode, 2);
1723 statements[0] = body_node;
1724 statements[1] = if_not_break;
1725 body_node = try ZigTag.block.create(t.arena, .{ .label = null, .stmts = statements });
1726 }
1727 return ZigTag.while_true.create(t.arena, body_node);
1728}
1729
1730fn transForStmt(t: *Translator, scope: *Scope, for_stmt: Node.ForStmt) TransError!ZigNode {
1731 var loop_scope: Scope = .{
1732 .parent = scope,
1733 .id = .loop,
1734 };
1735
1736 var block_scope: ?Scope.Block = null;
1737 defer if (block_scope) |*bs| bs.deinit();
1738
1739 switch (for_stmt.init) {
1740 .decls => |decls| {
1741 block_scope = try Scope.Block.init(t, scope, false);
1742 loop_scope.parent = &block_scope.?.base;
1743 for (decls) |decl| {
1744 try t.transDecl(&block_scope.?.base, decl);
1745 }
1746 },
1747 .expr => |maybe_init| if (maybe_init) |init| {
1748 block_scope = try Scope.Block.init(t, scope, false);
1749 loop_scope.parent = &block_scope.?.base;
1750 const init_node = try t.transStmt(&block_scope.?.base, init);
1751 try loop_scope.appendNode(init_node);
1752 },
1753 }
1754 var cond_scope: Scope.Condition = .{
1755 .base = .{
1756 .parent = &loop_scope,
1757 .id = .condition,
1758 },
1759 };
1760 defer cond_scope.deinit();
1761
1762 const cond = if (for_stmt.cond) |cond|
1763 try t.transBoolExpr(&cond_scope.base, cond)
1764 else
1765 ZigTag.true_literal.init();
1766
1767 const cont_expr = if (for_stmt.incr) |incr|
1768 try t.transExpr(&cond_scope.base, incr, .unused)
1769 else
1770 null;
1771
1772 const body = try t.maybeBlockify(&loop_scope, for_stmt.body);
1773 const while_node = try ZigTag.@"while".create(t.arena, .{ .cond = cond, .body = body, .cont_expr = cont_expr });
1774 if (block_scope) |*bs| {
1775 try bs.statements.append(t.gpa, while_node);
1776 return try bs.complete();
1777 } else {
1778 return while_node;
1779 }
1780}
1781
1782fn transSwitch(t: *Translator, scope: *Scope, switch_stmt: Node.SwitchStmt) TransError!ZigNode {
1783 var loop_scope: Scope = .{
1784 .parent = scope,
1785 .id = .loop,
1786 };
1787
1788 var block_scope = try Scope.Block.init(t, &loop_scope, false);
1789 defer block_scope.deinit();
1790
1791 const base_scope = &block_scope.base;
1792
1793 var cond_scope: Scope.Condition = .{
1794 .base = .{
1795 .parent = base_scope,
1796 .id = .condition,
1797 },
1798 };
1799 defer cond_scope.deinit();
1800 const switch_expr = try t.transExpr(&cond_scope.base, switch_stmt.cond, .used);
1801
1802 var cases = std.ArrayList(ZigNode).init(t.gpa);
1803 defer cases.deinit();
1804 var has_default = false;
1805
1806 const body_node = switch_stmt.body.get(t.tree);
1807 if (body_node != .compound_stmt) {
1808 return t.fail(error.UnsupportedTranslation, switch_stmt.switch_tok, "TODO complex switch", .{});
1809 }
1810 const body = body_node.compound_stmt.body;
1811 // Iterate over switch body and collect all cases.
1812 // Fallthrough is handled by duplicating statements.
1813 for (body, 0..) |stmt, i| {
1814 switch (stmt.get(t.tree)) {
1815 .case_stmt => {
1816 var items = std.ArrayList(ZigNode).init(t.gpa);
1817 defer items.deinit();
1818 const sub = try t.transCaseStmt(base_scope, stmt, &items);
1819 const res = try t.transSwitchProngStmt(base_scope, sub, body[i..]);
1820
1821 if (items.items.len == 0) {
1822 has_default = true;
1823 const switch_else = try ZigTag.switch_else.create(t.arena, res);
1824 try cases.append(switch_else);
1825 } else {
1826 const switch_prong = try ZigTag.switch_prong.create(t.arena, .{
1827 .cases = try t.arena.dupe(ZigNode, items.items),
1828 .cond = res,
1829 });
1830 try cases.append(switch_prong);
1831 }
1832 },
1833 .default_stmt => |default_stmt| {
1834 has_default = true;
1835
1836 var sub = default_stmt.body;
1837 while (true) switch (sub.get(t.tree)) {
1838 .case_stmt => |sub_case| sub = sub_case.body,
1839 .default_stmt => |sub_default| sub = sub_default.body,
1840 else => break,
1841 };
1842
1843 const res = try t.transSwitchProngStmt(base_scope, sub, body[i..]);
1844
1845 const switch_else = try ZigTag.switch_else.create(t.arena, res);
1846 try cases.append(switch_else);
1847 },
1848 else => {}, // collected in transSwitchProngStmt
1849 }
1850 }
1851
1852 if (!has_default) {
1853 const else_prong = try ZigTag.switch_else.create(t.arena, ZigTag.empty_block.init());
1854 try cases.append(else_prong);
1855 }
1856
1857 const switch_node = try ZigTag.@"switch".create(t.arena, .{
1858 .cond = switch_expr,
1859 .cases = try t.arena.dupe(ZigNode, cases.items),
1860 });
1861 try block_scope.statements.append(t.gpa, switch_node);
1862 try block_scope.statements.append(t.gpa, ZigTag.@"break".init());
1863 const while_body = try block_scope.complete();
1864
1865 return ZigTag.while_true.create(t.arena, while_body);
1866}
1867
1868/// Collects all items for this case, returns the first statement after the labels.
1869/// If items ends up empty, the prong should be translated as an else.
1870fn transCaseStmt(
1871 t: *Translator,
1872 scope: *Scope,
1873 stmt: Node.Index,
1874 items: *std.ArrayList(ZigNode),
1875) TransError!Node.Index {
1876 var sub = stmt;
1877 var seen_default = false;
1878 while (true) {
1879 switch (sub.get(t.tree)) {
1880 .default_stmt => |default_stmt| {
1881 seen_default = true;
1882 items.items.len = 0;
1883 sub = default_stmt.body;
1884 },
1885 .case_stmt => |case_stmt| {
1886 if (seen_default) {
1887 items.items.len = 0;
1888 sub = case_stmt.body;
1889 continue;
1890 }
1891
1892 const expr = if (case_stmt.end) |end| blk: {
1893 const start_node = try t.transExpr(scope, case_stmt.start, .used);
1894 const end_node = try t.transExpr(scope, end, .used);
1895
1896 break :blk try ZigTag.ellipsis3.create(t.arena, .{ .lhs = start_node, .rhs = end_node });
1897 } else try t.transExpr(scope, case_stmt.start, .used);
1898
1899 try items.append(expr);
1900 sub = case_stmt.body;
1901 },
1902 else => return sub,
1903 }
1904 }
1905}
1906
1907/// Collects all statements seen by this case into a block.
1908/// Avoids creating a block if the first statement is a break or return.
1909fn transSwitchProngStmt(
1910 t: *Translator,
1911 scope: *Scope,
1912 stmt: Node.Index,
1913 body: []const Node.Index,
1914) TransError!ZigNode {
1915 switch (stmt.get(t.tree)) {
1916 .break_stmt => return ZigTag.@"break".init(),
1917 .return_stmt => return t.transStmt(scope, stmt),
1918 .case_stmt, .default_stmt => unreachable,
1919 else => {
1920 var block_scope = try Scope.Block.init(t, scope, false);
1921 defer block_scope.deinit();
1922
1923 // we do not need to translate `stmt` since it is the first stmt of `body`
1924 try t.transSwitchProngStmtInline(&block_scope, body);
1925 return try block_scope.complete();
1926 },
1927 }
1928}
1929
1930/// Collects all statements seen by this case into a block.
1931fn transSwitchProngStmtInline(
1932 t: *Translator,
1933 block: *Scope.Block,
1934 body: []const Node.Index,
1935) TransError!void {
1936 for (body) |stmt| {
1937 switch (stmt.get(t.tree)) {
1938 .return_stmt => {
1939 const result = try t.transStmt(&block.base, stmt);
1940 try block.statements.append(t.gpa, result);
1941 return;
1942 },
1943 .break_stmt => {
1944 try block.statements.append(t.gpa, ZigTag.@"break".init());
1945 return;
1946 },
1947 .case_stmt => |case_stmt| {
1948 var sub = case_stmt.body;
1949 while (true) switch (sub.get(t.tree)) {
1950 .case_stmt => |sub_case| sub = sub_case.body,
1951 .default_stmt => |sub_default| sub = sub_default.body,
1952 else => break,
1953 };
1954 const result = try t.transStmt(&block.base, sub);
1955 assert(result.tag() != .declaration);
1956 try block.statements.append(t.gpa, result);
1957 if (result.isNoreturn(true)) return;
1958 },
1959 .default_stmt => |default_stmt| {
1960 var sub = default_stmt.body;
1961 while (true) switch (sub.get(t.tree)) {
1962 .case_stmt => |sub_case| sub = sub_case.body,
1963 .default_stmt => |sub_default| sub = sub_default.body,
1964 else => break,
1965 };
1966 const result = try t.transStmt(&block.base, sub);
1967 assert(result.tag() != .declaration);
1968 try block.statements.append(t.gpa, result);
1969 if (result.isNoreturn(true)) return;
1970 },
1971 .compound_stmt => |compound_stmt| {
1972 const result = try t.transCompoundStmt(&block.base, compound_stmt);
1973 try block.statements.append(t.gpa, result);
1974 if (result.isNoreturn(true)) return;
1975 },
1976 else => {
1977 const result = try t.transStmt(&block.base, stmt);
1978 switch (result.tag()) {
1979 .declaration, .empty_block => {},
1980 else => try block.statements.append(t.gpa, result),
1981 }
1982 },
1983 }
1984 }
1985}
1986
1987// ======================
1988// Expression translation
1989// ======================
1990
1991const ResultUsed = enum { used, unused };
1992
1993fn transExpr(t: *Translator, scope: *Scope, expr: Node.Index, used: ResultUsed) TransError!ZigNode {
1994 const qt = expr.qt(t.tree);
1995 return t.maybeSuppressResult(used, switch (expr.get(t.tree)) {
1996 .paren_expr => |paren_expr| {
1997 return t.transExpr(scope, paren_expr.operand, used);
1998 },
1999 .cast => |cast| return t.transCastExpr(scope, cast, cast.qt, used, .with_as),
2000 .decl_ref_expr => |decl_ref| try t.transDeclRefExpr(scope, decl_ref),
2001 .enumeration_ref => |enum_ref| try t.transDeclRefExpr(scope, enum_ref),
2002 .addr_of_expr => |addr_of_expr| try ZigTag.address_of.create(t.arena, try t.transExpr(scope, addr_of_expr.operand, .used)),
2003 .deref_expr => |deref_expr| res: {
2004 if (t.typeWasDemotedToOpaque(qt))
2005 return t.fail(error.UnsupportedTranslation, deref_expr.op_tok, "cannot dereference opaque type", .{});
2006
2007 // Dereferencing a function pointer is a no-op.
2008 if (qt.is(t.comp, .func)) return t.transExpr(scope, deref_expr.operand, used);
2009
2010 break :res try ZigTag.deref.create(t.arena, try t.transExpr(scope, deref_expr.operand, .used));
2011 },
2012 .bool_not_expr => |bool_not_expr| try ZigTag.not.create(t.arena, try t.transBoolExpr(scope, bool_not_expr.operand)),
2013 .bit_not_expr => |bit_not_expr| try ZigTag.bit_not.create(t.arena, try t.transExpr(scope, bit_not_expr.operand, .used)),
2014 .plus_expr => |plus_expr| return t.transExpr(scope, plus_expr.operand, used),
2015 .negate_expr => |negate_expr| res: {
2016 const operand_qt = negate_expr.operand.qt(t.tree);
2017 if (!t.typeHasWrappingOverflow(operand_qt)) {
2018 const sub_expr_node = try t.transExpr(scope, negate_expr.operand, .used);
2019 const to_negate = if (sub_expr_node.isBoolRes()) blk: {
2020 const ty_node = try ZigTag.type.create(t.arena, "c_int");
2021 const int_node = try ZigTag.int_from_bool.create(t.arena, sub_expr_node);
2022 break :blk try ZigTag.as.create(t.arena, .{ .lhs = ty_node, .rhs = int_node });
2023 } else sub_expr_node;
2024
2025 break :res try ZigTag.negate.create(t.arena, to_negate);
2026 } else if (t.signedness(operand_qt) == .unsigned) {
2027 // use -% x for unsigned integers
2028 break :res try ZigTag.negate_wrap.create(t.arena, try t.transExpr(scope, negate_expr.operand, .used));
2029 } else return t.fail(error.UnsupportedTranslation, negate_expr.op_tok, "C negation with non float non integer", .{});
2030 },
2031 .div_expr => |div_expr| res: {
2032 if (qt.isInt(t.comp) and t.signedness(qt) == .signed) {
2033 // signed integer division uses @divTrunc
2034 const lhs = try t.transExpr(scope, div_expr.lhs, .used);
2035 const rhs = try t.transExpr(scope, div_expr.rhs, .used);
2036 break :res try ZigTag.div_trunc.create(t.arena, .{ .lhs = lhs, .rhs = rhs });
2037 }
2038 // unsigned/float division uses the operator
2039 break :res try t.transBinExpr(scope, div_expr, .div);
2040 },
2041 .mod_expr => |mod_expr| res: {
2042 if (qt.isInt(t.comp) and t.signedness(qt) == .signed) {
2043 // signed integer remainder uses __helpers.signedRemainder
2044 const lhs = try t.transExpr(scope, mod_expr.lhs, .used);
2045 const rhs = try t.transExpr(scope, mod_expr.rhs, .used);
2046 break :res try t.createHelperCallNode(.signedRemainder, &.{ lhs, rhs });
2047 }
2048 // unsigned/float division uses the operator
2049 break :res try t.transBinExpr(scope, mod_expr, .mod);
2050 },
2051 .add_expr => |add_expr| res: {
2052 // `ptr + idx` and `idx + ptr` -> ptr + @as(usize, @bitCast(@as(isize, @intCast(idx))))
2053 const lhs_qt = add_expr.lhs.qt(t.tree);
2054 const rhs_qt = add_expr.rhs.qt(t.tree);
2055 if (qt.isPointer(t.comp) and (t.signedness(lhs_qt) == .signed or
2056 t.signedness(rhs_qt) == .signed))
2057 {
2058 break :res try t.transPointerArithmeticSignedOp(scope, add_expr, .add);
2059 }
2060
2061 if (t.signedness(qt) == .unsigned) {
2062 break :res try t.transBinExpr(scope, add_expr, .add_wrap);
2063 } else {
2064 break :res try t.transBinExpr(scope, add_expr, .add);
2065 }
2066 },
2067 .sub_expr => |sub_expr| res: {
2068 // `ptr - idx` -> ptr - @as(usize, @bitCast(@as(isize, @intCast(idx))))
2069 const lhs_qt = sub_expr.lhs.qt(t.tree);
2070 const rhs_qt = sub_expr.rhs.qt(t.tree);
2071 if (qt.isPointer(t.comp) and (t.signedness(lhs_qt) == .signed or
2072 t.signedness(rhs_qt) == .signed))
2073 {
2074 break :res try t.transPointerArithmeticSignedOp(scope, sub_expr, .sub);
2075 }
2076
2077 if (sub_expr.lhs.qt(t.tree).isPointer(t.comp) and sub_expr.rhs.qt(t.tree).isPointer(t.comp)) {
2078 break :res try t.transPtrDiffExpr(scope, sub_expr);
2079 } else if (t.signedness(qt) == .unsigned) {
2080 break :res try t.transBinExpr(scope, sub_expr, .sub_wrap);
2081 } else {
2082 break :res try t.transBinExpr(scope, sub_expr, .sub);
2083 }
2084 },
2085 .mul_expr => |mul_expr| if (t.signedness(qt) == .unsigned)
2086 try t.transBinExpr(scope, mul_expr, .mul_wrap)
2087 else
2088 try t.transBinExpr(scope, mul_expr, .mul),
2089
2090 .less_than_expr => |lt| try t.transBinExpr(scope, lt, .less_than),
2091 .greater_than_expr => |gt| try t.transBinExpr(scope, gt, .greater_than),
2092 .less_than_equal_expr => |lte| try t.transBinExpr(scope, lte, .less_than_equal),
2093 .greater_than_equal_expr => |gte| try t.transBinExpr(scope, gte, .greater_than_equal),
2094 .equal_expr => |equal_expr| try t.transBinExpr(scope, equal_expr, .equal),
2095 .not_equal_expr => |not_equal_expr| try t.transBinExpr(scope, not_equal_expr, .not_equal),
2096
2097 .bool_and_expr => |bool_and_expr| try t.transBoolBinExpr(scope, bool_and_expr, .@"and"),
2098 .bool_or_expr => |bool_or_expr| try t.transBoolBinExpr(scope, bool_or_expr, .@"or"),
2099
2100 .bit_and_expr => |bit_and_expr| try t.transBinExpr(scope, bit_and_expr, .bit_and),
2101 .bit_or_expr => |bit_or_expr| try t.transBinExpr(scope, bit_or_expr, .bit_or),
2102 .bit_xor_expr => |bit_xor_expr| try t.transBinExpr(scope, bit_xor_expr, .bit_xor),
2103
2104 .shl_expr => |shl_expr| try t.transShiftExpr(scope, shl_expr, .shl),
2105 .shr_expr => |shr_expr| try t.transShiftExpr(scope, shr_expr, .shr),
2106
2107 .member_access_expr => |member_access| try t.transMemberAccess(scope, .normal, member_access, null),
2108 .member_access_ptr_expr => |member_access| try t.transMemberAccess(scope, .ptr, member_access, null),
2109 .array_access_expr => |array_access| try t.transArrayAccess(scope, array_access, null),
2110
2111 .builtin_ref => unreachable,
2112 .builtin_call_expr => |call| return t.transBuiltinCall(scope, call, used),
2113 .call_expr => |call| return t.transCall(scope, call, used),
2114
2115 .builtin_types_compatible_p => |compatible| blk: {
2116 const lhs = try t.transType(scope, compatible.lhs, compatible.builtin_tok);
2117 const rhs = try t.transType(scope, compatible.rhs, compatible.builtin_tok);
2118
2119 break :blk try ZigTag.equal.create(t.arena, .{
2120 .lhs = lhs,
2121 .rhs = rhs,
2122 });
2123 },
2124 .builtin_choose_expr => |choose| return t.transCondExpr(scope, choose, used),
2125 .cond_expr => |cond_expr| return t.transCondExpr(scope, cond_expr, used),
2126 .binary_cond_expr => |conditional| return t.transBinaryCondExpr(scope, conditional, used),
2127 .cond_dummy_expr => unreachable,
2128
2129 .assign_expr => |assign| return t.transAssignExpr(scope, assign, used),
2130 .add_assign_expr => |assign| return t.transCompoundAssign(scope, assign, used),
2131 .sub_assign_expr => |assign| return t.transCompoundAssign(scope, assign, used),
2132 .mul_assign_expr => |assign| return t.transCompoundAssign(scope, assign, used),
2133 .div_assign_expr => |assign| return t.transCompoundAssign(scope, assign, used),
2134 .mod_assign_expr => |assign| return t.transCompoundAssign(scope, assign, used),
2135 .shl_assign_expr => |assign| return t.transCompoundAssign(scope, assign, used),
2136 .shr_assign_expr => |assign| return t.transCompoundAssign(scope, assign, used),
2137 .bit_and_assign_expr => |assign| return t.transCompoundAssign(scope, assign, used),
2138 .bit_xor_assign_expr => |assign| return t.transCompoundAssign(scope, assign, used),
2139 .bit_or_assign_expr => |assign| return t.transCompoundAssign(scope, assign, used),
2140 .compound_assign_dummy_expr => {
2141 assert(used == .used);
2142 return t.compound_assign_dummy.?;
2143 },
2144
2145 .comma_expr => |comma_expr| return t.transCommaExpr(scope, comma_expr, used),
2146 .pre_inc_expr => |un| return t.transIncDecExpr(scope, un, .pre, .inc, used),
2147 .pre_dec_expr => |un| return t.transIncDecExpr(scope, un, .pre, .dec, used),
2148 .post_inc_expr => |un| return t.transIncDecExpr(scope, un, .post, .inc, used),
2149 .post_dec_expr => |un| return t.transIncDecExpr(scope, un, .post, .dec, used),
2150
2151 .int_literal => return t.transIntLiteral(scope, expr, used, .with_as),
2152 .char_literal => return t.transCharLiteral(scope, expr, used, .with_as),
2153 .float_literal => return t.transFloatLiteral(scope, expr, used, .with_as),
2154 .string_literal_expr => |literal| try t.transStringLiteral(scope, expr, literal),
2155 .bool_literal => res: {
2156 const val = t.tree.value_map.get(expr).?;
2157 break :res if (val.toBool(t.comp))
2158 ZigTag.true_literal.init()
2159 else
2160 ZigTag.false_literal.init();
2161 },
2162 .nullptr_literal => ZigTag.null_literal.init(),
2163 .imaginary_literal => |literal| {
2164 return t.fail(error.UnsupportedTranslation, literal.op_tok, "TODO complex numbers", .{});
2165 },
2166 .compound_literal_expr => |literal| return t.transCompoundLiteral(scope, literal, used),
2167
2168 .default_init_expr => |default_init| return t.transDefaultInit(scope, default_init, used, .with_as),
2169 .array_init_expr => |array_init| return t.transArrayInit(scope, array_init, used),
2170 .union_init_expr => |union_init| return t.transUnionInit(scope, union_init, used),
2171 .struct_init_expr => |struct_init| return t.transStructInit(scope, struct_init, used),
2172 .array_filler_expr => unreachable,
2173
2174 .sizeof_expr => |sizeof| try t.transTypeInfo(scope, .sizeof, sizeof),
2175 .alignof_expr => |alignof| try t.transTypeInfo(scope, .alignof, alignof),
2176
2177 .imag_expr, .real_expr => |un| {
2178 return t.fail(error.UnsupportedTranslation, un.op_tok, "TODO complex numbers", .{});
2179 },
2180 .addr_of_label => |addr_of_label| {
2181 return t.fail(error.UnsupportedTranslation, addr_of_label.label_tok, "TODO computed goto", .{});
2182 },
2183
2184 .generic_expr => |generic| return t.transExpr(scope, generic.chosen, used),
2185 .generic_association_expr => |generic| return t.transExpr(scope, generic.expr, used),
2186 .generic_default_expr => |generic| return t.transExpr(scope, generic.expr, used),
2187
2188 .stmt_expr => |stmt_expr| return t.transStmtExpr(scope, stmt_expr, used),
2189
2190 .builtin_convertvector => |convertvector| try t.transConvertvectorExpr(scope, convertvector),
2191 .builtin_shufflevector => |shufflevector| try t.transShufflevectorExpr(scope, shufflevector),
2192
2193 .compound_stmt,
2194 .static_assert,
2195 .return_stmt,
2196 .null_stmt,
2197 .if_stmt,
2198 .while_stmt,
2199 .do_while_stmt,
2200 .for_stmt,
2201 .continue_stmt,
2202 .break_stmt,
2203 .labeled_stmt,
2204 .switch_stmt,
2205 .case_stmt,
2206 .default_stmt,
2207 .goto_stmt,
2208 .computed_goto_stmt,
2209 .gnu_asm_simple,
2210 .global_asm,
2211 .typedef,
2212 .struct_decl,
2213 .union_decl,
2214 .enum_decl,
2215 .function,
2216 .param,
2217 .variable,
2218 .enum_field,
2219 .record_field,
2220 .struct_forward_decl,
2221 .union_forward_decl,
2222 .enum_forward_decl,
2223 .empty_decl,
2224 => unreachable, // not an expression
2225 });
2226}
2227
2228/// Same as `transExpr` but with the knowledge that the operand will be type coerced, and therefore
2229/// an `@as` would be redundant. This is used to prevent redundant `@as` in integer literals.
2230fn transExprCoercing(t: *Translator, scope: *Scope, expr: Node.Index, used: ResultUsed) TransError!ZigNode {
2231 switch (expr.get(t.tree)) {
2232 .int_literal => return t.transIntLiteral(scope, expr, used, .no_as),
2233 .char_literal => return t.transCharLiteral(scope, expr, used, .no_as),
2234 .float_literal => return t.transFloatLiteral(scope, expr, used, .no_as),
2235 .cast => |cast| switch (cast.kind) {
2236 .no_op => {
2237 const operand = cast.operand.get(t.tree);
2238 if (operand == .cast) {
2239 return t.transCastExpr(scope, operand.cast, cast.qt, used, .no_as);
2240 }
2241 return t.transExprCoercing(scope, cast.operand, used);
2242 },
2243 .lval_to_rval => return t.transExprCoercing(scope, cast.operand, used),
2244 else => return t.transCastExpr(scope, cast, cast.qt, used, .no_as),
2245 },
2246 .default_init_expr => |default_init| return try t.transDefaultInit(scope, default_init, used, .no_as),
2247 .compound_literal_expr => |literal| {
2248 if (!literal.thread_local and literal.storage_class != .static) {
2249 return t.transExprCoercing(scope, literal.initializer, used);
2250 }
2251 },
2252 else => {},
2253 }
2254
2255 return t.transExpr(scope, expr, used);
2256}
2257
2258fn transBoolExpr(t: *Translator, scope: *Scope, expr: Node.Index) TransError!ZigNode {
2259 switch (expr.get(t.tree)) {
2260 .int_literal => {
2261 const int_val = t.tree.value_map.get(expr).?;
2262 return if (int_val.isZero(t.comp))
2263 ZigTag.false_literal.init()
2264 else
2265 ZigTag.true_literal.init();
2266 },
2267 .cast => |cast| switch (cast.kind) {
2268 .bool_to_int => return t.transExpr(scope, cast.operand, .used),
2269 .array_to_pointer => {
2270 const operand = cast.operand.get(t.tree);
2271 if (operand == .string_literal_expr) {
2272 // @intFromPtr("foo") != 0, always true
2273 const str = try t.transStringLiteral(scope, cast.operand, operand.string_literal_expr);
2274 const int_from_ptr = try ZigTag.int_from_ptr.create(t.arena, str);
2275 return ZigTag.not_equal.create(t.arena, .{ .lhs = int_from_ptr, .rhs = ZigTag.zero_literal.init() });
2276 }
2277 },
2278 else => {},
2279 },
2280 else => {},
2281 }
2282
2283 const maybe_bool_res = try t.transExpr(scope, expr, .used);
2284 if (maybe_bool_res.isBoolRes()) {
2285 return maybe_bool_res;
2286 }
2287
2288 return t.finishBoolExpr(expr.qt(t.tree), maybe_bool_res);
2289}
2290
2291fn finishBoolExpr(t: *Translator, qt: QualType, node: ZigNode) TransError!ZigNode {
2292 const sk = qt.scalarKind(t.comp);
2293 if (sk == .bool) return node;
2294 if (sk == .nullptr_t) {
2295 // node == null, always true
2296 return ZigTag.equal.create(t.arena, .{ .lhs = node, .rhs = ZigTag.null_literal.init() });
2297 }
2298 if (sk.isPointer()) {
2299 // node != null
2300 return ZigTag.not_equal.create(t.arena, .{ .lhs = node, .rhs = ZigTag.null_literal.init() });
2301 }
2302 if (sk != .none) {
2303 // node != 0
2304 return ZigTag.not_equal.create(t.arena, .{ .lhs = node, .rhs = ZigTag.zero_literal.init() });
2305 }
2306 unreachable; // Unexpected bool expression type
2307}
2308
2309fn transCastExpr(
2310 t: *Translator,
2311 scope: *Scope,
2312 cast: Node.Cast,
2313 dest_qt: QualType,
2314 used: ResultUsed,
2315 suppress_as: SuppressCast,
2316) TransError!ZigNode {
2317 const operand = switch (cast.kind) {
2318 .no_op => {
2319 const operand = cast.operand.get(t.tree);
2320 if (operand == .cast) {
2321 return t.transCastExpr(scope, operand.cast, cast.qt, used, suppress_as);
2322 }
2323 return t.transExpr(scope, cast.operand, used);
2324 },
2325 .lval_to_rval, .function_to_pointer => {
2326 return t.transExpr(scope, cast.operand, used);
2327 },
2328 .int_cast => int_cast: {
2329 const src_qt = cast.operand.qt(t.tree);
2330
2331 if (cast.implicit) {
2332 if (t.tree.value_map.get(cast.operand)) |val| {
2333 const max_int = try aro.Value.maxInt(dest_qt, t.comp);
2334 const min_int = try aro.Value.minInt(dest_qt, t.comp);
2335
2336 if (val.compare(.lte, max_int, t.comp) and val.compare(.gte, min_int, t.comp)) {
2337 break :int_cast try t.transExprCoercing(scope, cast.operand, .used);
2338 }
2339 }
2340 }
2341 const operand = try t.transExpr(scope, cast.operand, .used);
2342 break :int_cast try t.transIntCast(operand, src_qt, dest_qt);
2343 },
2344 .to_void => {
2345 assert(used == .unused);
2346 return try t.transExpr(scope, cast.operand, .unused);
2347 },
2348 .null_to_pointer => ZigTag.null_literal.init(),
2349 .array_to_pointer => array_to_pointer: {
2350 const child_qt = dest_qt.childType(t.comp);
2351
2352 loop: switch (cast.operand.get(t.tree)) {
2353 .string_literal_expr => |literal| {
2354 const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
2355
2356 const ref = if (literal.kind == .utf8 or literal.kind == .ascii)
2357 sub_expr_node
2358 else
2359 try ZigTag.address_of.create(t.arena, sub_expr_node);
2360
2361 const casted = if (child_qt.@"const")
2362 ref
2363 else
2364 try ZigTag.const_cast.create(t.arena, sub_expr_node);
2365
2366 return t.maybeSuppressResult(used, casted);
2367 },
2368 .paren_expr => |paren_expr| {
2369 continue :loop paren_expr.operand.get(t.tree);
2370 },
2371 .generic_expr => |generic| {
2372 continue :loop generic.chosen.get(t.tree);
2373 },
2374 .generic_association_expr => |generic| {
2375 continue :loop generic.expr.get(t.tree);
2376 },
2377 .generic_default_expr => |generic| {
2378 continue :loop generic.expr.get(t.tree);
2379 },
2380 else => {},
2381 }
2382
2383 if (cast.operand.qt(t.tree).arrayLen(t.comp) == null) {
2384 return try t.transExpr(scope, cast.operand, used);
2385 }
2386
2387 const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
2388 const ref = try ZigTag.address_of.create(t.arena, sub_expr_node);
2389 const align_cast = try ZigTag.align_cast.create(t.arena, ref);
2390 break :array_to_pointer try ZigTag.ptr_cast.create(t.arena, align_cast);
2391 },
2392 .int_to_pointer => int_to_pointer: {
2393 var sub_expr_node = try t.transExpr(scope, cast.operand, .used);
2394 const operand_qt = cast.operand.qt(t.tree);
2395 if (t.signedness(operand_qt) == .signed or operand_qt.bitSizeof(t.comp) > t.comp.target.ptrBitWidth()) {
2396 sub_expr_node = try ZigTag.as.create(t.arena, .{
2397 .lhs = try ZigTag.type.create(t.arena, "usize"),
2398 .rhs = try ZigTag.int_cast.create(t.arena, sub_expr_node),
2399 });
2400 }
2401 break :int_to_pointer try ZigTag.ptr_from_int.create(t.arena, sub_expr_node);
2402 },
2403 .int_to_bool => {
2404 const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
2405 if (sub_expr_node.isBoolRes()) return sub_expr_node;
2406 if (cast.operand.qt(t.tree).is(t.comp, .bool)) return sub_expr_node;
2407 const cmp_node = try ZigTag.not_equal.create(t.arena, .{ .lhs = sub_expr_node, .rhs = ZigTag.zero_literal.init() });
2408 return t.maybeSuppressResult(used, cmp_node);
2409 },
2410 .float_to_bool => {
2411 const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
2412 const cmp_node = try ZigTag.not_equal.create(t.arena, .{ .lhs = sub_expr_node, .rhs = ZigTag.zero_literal.init() });
2413 return t.maybeSuppressResult(used, cmp_node);
2414 },
2415 .pointer_to_bool => {
2416 const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
2417
2418 // Special case function pointers as @intFromPtr(expr) != 0
2419 if (cast.operand.qt(t.tree).get(t.comp, .pointer)) |ptr_ty| if (ptr_ty.child.is(t.comp, .func)) {
2420 const ptr_node = if (sub_expr_node.tag() == .identifier)
2421 try ZigTag.address_of.create(t.arena, sub_expr_node)
2422 else
2423 sub_expr_node;
2424 const int_from_ptr = try ZigTag.int_from_ptr.create(t.arena, ptr_node);
2425 const cmp_node = try ZigTag.not_equal.create(t.arena, .{ .lhs = int_from_ptr, .rhs = ZigTag.zero_literal.init() });
2426 return t.maybeSuppressResult(used, cmp_node);
2427 };
2428
2429 const cmp_node = try ZigTag.not_equal.create(t.arena, .{ .lhs = sub_expr_node, .rhs = ZigTag.null_literal.init() });
2430 return t.maybeSuppressResult(used, cmp_node);
2431 },
2432 .bool_to_int => bool_to_int: {
2433 const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
2434 break :bool_to_int try ZigTag.int_from_bool.create(t.arena, sub_expr_node);
2435 },
2436 .bool_to_float => bool_to_float: {
2437 const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
2438 const int_from_bool = try ZigTag.int_from_bool.create(t.arena, sub_expr_node);
2439 break :bool_to_float try ZigTag.float_from_int.create(t.arena, int_from_bool);
2440 },
2441 .bool_to_pointer => bool_to_pointer: {
2442 const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
2443 const int_from_bool = try ZigTag.int_from_bool.create(t.arena, sub_expr_node);
2444 break :bool_to_pointer try ZigTag.ptr_from_int.create(t.arena, int_from_bool);
2445 },
2446 .float_cast => float_cast: {
2447 const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
2448 break :float_cast try ZigTag.float_cast.create(t.arena, sub_expr_node);
2449 },
2450 .int_to_float => int_to_float: {
2451 const sub_expr_node = try t.transExpr(scope, cast.operand, used);
2452 const int_node = if (sub_expr_node.isBoolRes())
2453 try ZigTag.int_from_bool.create(t.arena, sub_expr_node)
2454 else
2455 sub_expr_node;
2456 break :int_to_float try ZigTag.float_from_int.create(t.arena, int_node);
2457 },
2458 .float_to_int => float_to_int: {
2459 const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
2460 break :float_to_int try ZigTag.int_from_float.create(t.arena, sub_expr_node);
2461 },
2462 .pointer_to_int => pointer_to_int: {
2463 const sub_expr_node = try t.transPointerCastExpr(scope, cast.operand);
2464 const ptr_node = try ZigTag.int_from_ptr.create(t.arena, sub_expr_node);
2465 break :pointer_to_int try ZigTag.int_cast.create(t.arena, ptr_node);
2466 },
2467 .bitcast => bitcast: {
2468 const sub_expr_node = try t.transPointerCastExpr(scope, cast.operand);
2469 const operand_qt = cast.operand.qt(t.tree);
2470 if (dest_qt.isPointer(t.comp) and operand_qt.isPointer(t.comp)) {
2471 var casted = try ZigTag.align_cast.create(t.arena, sub_expr_node);
2472 casted = try ZigTag.ptr_cast.create(t.arena, casted);
2473
2474 const src_elem = operand_qt.childType(t.comp);
2475 const dest_elem = dest_qt.childType(t.comp);
2476 if ((src_elem.@"const" or src_elem.is(t.comp, .func)) and !dest_elem.@"const") {
2477 casted = try ZigTag.const_cast.create(t.arena, casted);
2478 }
2479 if (src_elem.@"volatile" and !dest_elem.@"volatile") {
2480 casted = try ZigTag.volatile_cast.create(t.arena, casted);
2481 }
2482 break :bitcast casted;
2483 }
2484
2485 break :bitcast try ZigTag.bit_cast.create(t.arena, sub_expr_node);
2486 },
2487 .union_cast => union_cast: {
2488 const union_type = try t.transType(scope, dest_qt, cast.l_paren);
2489
2490 const operand_qt = cast.operand.qt(t.tree);
2491 const union_base = dest_qt.base(t.comp);
2492 const field = for (union_base.type.@"union".fields) |field| {
2493 if (field.qt.eql(operand_qt, t.comp)) break field;
2494 } else unreachable;
2495 const field_name = if (field.name_tok == 0) t.anonymous_record_field_names.get(.{
2496 .parent = union_base.qt,
2497 .field = field.qt,
2498 }).? else field.name.lookup(t.comp);
2499
2500 const field_init = try t.arena.create(ast.Payload.ContainerInit.Initializer);
2501 field_init.* = .{
2502 .name = field_name,
2503 .value = try t.transExpr(scope, cast.operand, .used),
2504 };
2505 break :union_cast try ZigTag.container_init.create(t.arena, .{
2506 .lhs = union_type,
2507 .inits = field_init[0..1],
2508 });
2509 },
2510 else => return t.fail(error.UnsupportedTranslation, cast.l_paren, "TODO translate {s} cast", .{@tagName(cast.kind)}),
2511 };
2512 if (suppress_as == .no_as) return t.maybeSuppressResult(used, operand);
2513 if (used == .unused) return t.maybeSuppressResult(used, operand);
2514 const as = try ZigTag.as.create(t.arena, .{
2515 .lhs = try t.transType(scope, dest_qt, cast.l_paren),
2516 .rhs = operand,
2517 });
2518 return as;
2519}
2520
2521fn transIntCast(t: *Translator, operand: ZigNode, src_qt: QualType, dest_qt: QualType) !ZigNode {
2522 const src_dest_order = src_qt.intRankOrder(dest_qt, t.comp);
2523 const different_sign = t.signedness(src_qt) != t.signedness(dest_qt);
2524 const needs_bitcast = different_sign and !(t.signedness(src_qt) == .unsigned and src_dest_order == .lt);
2525
2526 var casted = operand;
2527 if (casted.isBoolRes()) {
2528 casted = try ZigTag.int_from_bool.create(t.arena, casted);
2529 } else if (src_dest_order == .gt) {
2530 // No C type is smaller than the 1 bit from @intFromBool
2531 casted = try ZigTag.truncate.create(t.arena, casted);
2532 }
2533 if (needs_bitcast) {
2534 if (src_dest_order != .eq) {
2535 casted = try ZigTag.as.create(t.arena, .{
2536 .lhs = try t.transTypeIntWidthOf(dest_qt, t.signedness(src_qt) == .signed),
2537 .rhs = casted,
2538 });
2539 }
2540 return ZigTag.bit_cast.create(t.arena, casted);
2541 }
2542 return casted;
2543}
2544
2545/// Same as `transExpr` but adds a `&` if the expression is an identifier referencing a function type.
2546fn transPointerCastExpr(t: *Translator, scope: *Scope, expr: Node.Index) TransError!ZigNode {
2547 const sub_expr_node = try t.transExpr(scope, expr, .used);
2548 switch (expr.get(t.tree)) {
2549 .cast => |cast| if (cast.kind == .function_to_pointer and sub_expr_node.tag() == .identifier) {
2550 return ZigTag.address_of.create(t.arena, sub_expr_node);
2551 },
2552 else => {},
2553 }
2554 return sub_expr_node;
2555}
2556
2557fn transDeclRefExpr(t: *Translator, scope: *Scope, decl_ref: Node.DeclRef) TransError!ZigNode {
2558 const name = t.tree.tokSlice(decl_ref.name_tok);
2559 const maybe_alias = scope.getAlias(name);
2560 const mangled_name = maybe_alias orelse name;
2561
2562 switch (decl_ref.decl.get(t.tree)) {
2563 .function => |function| if (function.definition == null and function.body == null) {
2564 // Try translating the decl again in case of out of scope declaration.
2565 try t.transFnDecl(scope, function);
2566 },
2567 else => {},
2568 }
2569
2570 const decl = decl_ref.decl.get(t.tree);
2571 const ref_expr = blk: {
2572 const identifier = try ZigTag.identifier.create(t.arena, mangled_name);
2573 if (decl_ref.qt.is(t.comp, .func) and maybe_alias != null) {
2574 break :blk try ZigTag.field_access.create(t.arena, .{
2575 .lhs = identifier,
2576 .field_name = name,
2577 });
2578 }
2579 if (decl == .variable and maybe_alias != null) {
2580 switch (decl.variable.storage_class) {
2581 .@"extern", .static => {
2582 break :blk try ZigTag.field_access.create(t.arena, .{
2583 .lhs = identifier,
2584 .field_name = name,
2585 });
2586 },
2587 else => {},
2588 }
2589 }
2590 break :blk identifier;
2591 };
2592
2593 scope.skipVariableDiscard(mangled_name);
2594 return ref_expr;
2595}
2596
2597fn transBinExpr(t: *Translator, scope: *Scope, bin: Node.Binary, op_id: ZigTag) TransError!ZigNode {
2598 const lhs_uncasted = try t.transExpr(scope, bin.lhs, .used);
2599 const rhs_uncasted = try t.transExpr(scope, bin.rhs, .used);
2600
2601 const lhs = if (lhs_uncasted.isBoolRes())
2602 try ZigTag.int_from_bool.create(t.arena, lhs_uncasted)
2603 else
2604 lhs_uncasted;
2605
2606 const rhs = if (rhs_uncasted.isBoolRes())
2607 try ZigTag.int_from_bool.create(t.arena, rhs_uncasted)
2608 else
2609 rhs_uncasted;
2610
2611 return t.createBinOpNode(op_id, lhs, rhs);
2612}
2613
2614fn transBoolBinExpr(t: *Translator, scope: *Scope, bin: Node.Binary, op: ZigTag) !ZigNode {
2615 std.debug.assert(op == .@"and" or op == .@"or");
2616
2617 const lhs = try t.transBoolExpr(scope, bin.lhs);
2618 const rhs = try t.transBoolExpr(scope, bin.rhs);
2619
2620 return t.createBinOpNode(op, lhs, rhs);
2621}
2622
2623fn transShiftExpr(t: *Translator, scope: *Scope, bin: Node.Binary, op_id: ZigTag) !ZigNode {
2624 std.debug.assert(op_id == .shl or op_id == .shr);
2625
2626 // lhs >> @intCast(rh)
2627 const lhs = try t.transExpr(scope, bin.lhs, .used);
2628
2629 const rhs = try t.transExprCoercing(scope, bin.rhs, .used);
2630 const rhs_casted = try ZigTag.int_cast.create(t.arena, rhs);
2631
2632 return t.createBinOpNode(op_id, lhs, rhs_casted);
2633}
2634
2635fn transCondExpr(
2636 t: *Translator,
2637 scope: *Scope,
2638 conditional: Node.Conditional,
2639 used: ResultUsed,
2640) TransError!ZigNode {
2641 var cond_scope: Scope.Condition = .{
2642 .base = .{
2643 .parent = scope,
2644 .id = .condition,
2645 },
2646 };
2647 defer cond_scope.deinit();
2648
2649 const res_is_bool = conditional.qt.is(t.comp, .bool);
2650 const cond = try t.transBoolExpr(&cond_scope.base, conditional.cond);
2651
2652 var then_body = try t.transExpr(scope, conditional.then_expr, used);
2653 if (!res_is_bool and then_body.isBoolRes()) {
2654 then_body = try ZigTag.int_from_bool.create(t.arena, then_body);
2655 }
2656
2657 var else_body = try t.transExpr(scope, conditional.else_expr, used);
2658 if (!res_is_bool and else_body.isBoolRes()) {
2659 else_body = try ZigTag.int_from_bool.create(t.arena, else_body);
2660 }
2661
2662 // The `ResultUsed` is forwarded to both branches so no need to suppress the result here.
2663 return ZigTag.@"if".create(t.arena, .{ .cond = cond, .then = then_body, .@"else" = else_body });
2664}
2665
2666fn transBinaryCondExpr(
2667 t: *Translator,
2668 scope: *Scope,
2669 conditional: Node.Conditional,
2670 used: ResultUsed,
2671) TransError!ZigNode {
2672 // GNU extension of the ternary operator where the middle expression is
2673 // omitted, the condition itself is returned if it evaluates to true.
2674
2675 if (used == .unused) {
2676 // Result unused so this can be translated as
2677 // if (condition) else_expr;
2678 var cond_scope: Scope.Condition = .{
2679 .base = .{
2680 .parent = scope,
2681 .id = .condition,
2682 },
2683 };
2684 defer cond_scope.deinit();
2685
2686 return ZigTag.@"if".create(t.arena, .{
2687 .cond = try t.transBoolExpr(&cond_scope.base, conditional.cond),
2688 .then = try t.transExpr(scope, conditional.else_expr, .unused),
2689 .@"else" = null,
2690 });
2691 }
2692
2693 const res_is_bool = conditional.qt.is(t.comp, .bool);
2694 // c: (condition)?:(else_expr)
2695 // zig: (blk: {
2696 // const _cond_temp = (condition);
2697 // break :blk if (_cond_temp) _cond_temp else (else_expr);
2698 // })
2699 var block_scope = try Scope.Block.init(t, scope, true);
2700 defer block_scope.deinit();
2701
2702 const cond_temp = try block_scope.reserveMangledName("cond_temp");
2703 const init_node = try t.transExpr(&block_scope.base, conditional.cond, .used);
2704 const temp_decl = try ZigTag.var_simple.create(t.arena, .{ .name = cond_temp, .init = init_node });
2705 try block_scope.statements.append(t.gpa, temp_decl);
2706
2707 var cond_scope: Scope.Condition = .{
2708 .base = .{
2709 .parent = &block_scope.base,
2710 .id = .condition,
2711 },
2712 };
2713 defer cond_scope.deinit();
2714
2715 const cond_ident = try ZigTag.identifier.create(t.arena, cond_temp);
2716 const cond_node = try t.finishBoolExpr(conditional.cond.qt(t.tree), cond_ident);
2717 var then_body = cond_ident;
2718 if (!res_is_bool and init_node.isBoolRes()) {
2719 then_body = try ZigTag.int_from_bool.create(t.arena, then_body);
2720 }
2721
2722 var else_body = try t.transExpr(&block_scope.base, conditional.else_expr, .used);
2723 if (!res_is_bool and else_body.isBoolRes()) {
2724 else_body = try ZigTag.int_from_bool.create(t.arena, else_body);
2725 }
2726 const if_node = try ZigTag.@"if".create(t.arena, .{
2727 .cond = cond_node,
2728 .then = then_body,
2729 .@"else" = else_body,
2730 });
2731 const break_node = try ZigTag.break_val.create(t.arena, .{
2732 .label = block_scope.label,
2733 .val = if_node,
2734 });
2735 try block_scope.statements.append(t.gpa, break_node);
2736 return block_scope.complete();
2737}
2738
2739fn transCommaExpr(t: *Translator, scope: *Scope, bin: Node.Binary, used: ResultUsed) TransError!ZigNode {
2740 if (used == .unused) {
2741 const lhs = try t.transExprCoercing(scope, bin.lhs, .unused);
2742 try scope.appendNode(lhs);
2743 const rhs = try t.transExprCoercing(scope, bin.rhs, .unused);
2744 return rhs;
2745 }
2746
2747 var block_scope = try Scope.Block.init(t, scope, true);
2748 defer block_scope.deinit();
2749
2750 const lhs = try t.transExprCoercing(&block_scope.base, bin.lhs, .unused);
2751 try block_scope.statements.append(t.gpa, lhs);
2752
2753 const rhs = try t.transExprCoercing(&block_scope.base, bin.rhs, .used);
2754 const break_node = try ZigTag.break_val.create(t.arena, .{
2755 .label = block_scope.label,
2756 .val = rhs,
2757 });
2758 try block_scope.statements.append(t.gpa, break_node);
2759
2760 return try block_scope.complete();
2761}
2762
2763fn transAssignExpr(t: *Translator, scope: *Scope, bin: Node.Binary, used: ResultUsed) !ZigNode {
2764 if (used == .unused) {
2765 const lhs = try t.transExpr(scope, bin.lhs, .used);
2766 var rhs = try t.transExprCoercing(scope, bin.rhs, .used);
2767
2768 const lhs_qt = bin.lhs.qt(t.tree);
2769 if (rhs.isBoolRes() and !lhs_qt.is(t.comp, .bool)) {
2770 rhs = try ZigTag.int_from_bool.create(t.arena, rhs);
2771 }
2772
2773 return t.createBinOpNode(.assign, lhs, rhs);
2774 }
2775
2776 var block_scope = try Scope.Block.init(t, scope, true);
2777 defer block_scope.deinit();
2778
2779 const tmp = try block_scope.reserveMangledName("tmp");
2780
2781 var rhs = try t.transExpr(&block_scope.base, bin.rhs, .used);
2782 const lhs_qt = bin.lhs.qt(t.tree);
2783 if (rhs.isBoolRes() and !lhs_qt.is(t.comp, .bool)) {
2784 rhs = try ZigTag.int_from_bool.create(t.arena, rhs);
2785 }
2786
2787 const tmp_decl = try ZigTag.var_simple.create(t.arena, .{ .name = tmp, .init = rhs });
2788 try block_scope.statements.append(t.gpa, tmp_decl);
2789
2790 const lhs = try t.transExprCoercing(&block_scope.base, bin.lhs, .used);
2791 const tmp_ident = try ZigTag.identifier.create(t.arena, tmp);
2792
2793 const assign = try t.createBinOpNode(.assign, lhs, tmp_ident);
2794 try block_scope.statements.append(t.gpa, assign);
2795
2796 const break_node = try ZigTag.break_val.create(t.arena, .{
2797 .label = block_scope.label,
2798 .val = tmp_ident,
2799 });
2800 try block_scope.statements.append(t.gpa, break_node);
2801
2802 return try block_scope.complete();
2803}
2804
2805fn transCompoundAssign(
2806 t: *Translator,
2807 scope: *Scope,
2808 assign: Node.Binary,
2809 used: ResultUsed,
2810) !ZigNode {
2811 // If the result is unused we can try using the equivalent Zig operator
2812 // without a block
2813 if (used == .unused) {
2814 if (try t.transCompoundAssignSimple(scope, null, assign)) |some| {
2815 return some;
2816 }
2817 }
2818
2819 // Otherwise we need to wrap the the compound assignment in a block.
2820 var block_scope = try Scope.Block.init(t, scope, used == .used);
2821 defer block_scope.deinit();
2822 const ref = try block_scope.reserveMangledName("ref");
2823
2824 const lhs_expr = try t.transExpr(&block_scope.base, assign.lhs, .used);
2825 const addr_of = try ZigTag.address_of.create(t.arena, lhs_expr);
2826 const ref_decl = try ZigTag.var_simple.create(t.arena, .{ .name = ref, .init = addr_of });
2827 try block_scope.statements.append(t.gpa, ref_decl);
2828
2829 const lhs_node = try ZigTag.identifier.create(t.arena, ref);
2830 const ref_node = try ZigTag.deref.create(t.arena, lhs_node);
2831
2832 // Use the equivalent Zig operator if possible.
2833 if (try t.transCompoundAssignSimple(scope, ref_node, assign)) |some| {
2834 try block_scope.statements.append(t.gpa, some);
2835 } else {
2836 const old_dummy = t.compound_assign_dummy;
2837 defer t.compound_assign_dummy = old_dummy;
2838 t.compound_assign_dummy = ref_node;
2839
2840 // Otherwise do the operation and assignment separately.
2841 const rhs_node = try t.transExprCoercing(&block_scope.base, assign.rhs, .used);
2842 const assign_node = try t.createBinOpNode(.assign, ref_node, rhs_node);
2843 try block_scope.statements.append(t.gpa, assign_node);
2844 }
2845
2846 if (used == .used) {
2847 const break_node = try ZigTag.break_val.create(t.arena, .{
2848 .label = block_scope.label,
2849 .val = ref_node,
2850 });
2851 try block_scope.statements.append(t.gpa, break_node);
2852 }
2853 return block_scope.complete();
2854}
2855
2856/// Translates compound assignment using the equivalent Zig operator if possible.
2857fn transCompoundAssignSimple(t: *Translator, scope: *Scope, lhs_dummy_opt: ?ZigNode, assign: Node.Binary) TransError!?ZigNode {
2858 const assign_rhs = assign.rhs.get(t.tree);
2859 if (assign_rhs == .cast) return null;
2860
2861 const is_signed = t.signedness(assign.qt) == .signed;
2862 switch (assign_rhs) {
2863 .div_expr, .mod_expr => if (is_signed) return null,
2864 else => {},
2865 }
2866 const lhs_ptr = assign.qt.isPointer(t.comp);
2867
2868 const bin, const op: ZigTag, const cast: enum { none, shift, usize } = switch (assign_rhs) {
2869 .add_expr => |bin| .{
2870 bin,
2871 if (t.typeHasWrappingOverflow(bin.qt)) .add_wrap_assign else .add_assign,
2872 if (lhs_ptr and t.signedness(bin.rhs.qt(t.tree)) == .signed) .usize else .none,
2873 },
2874 .sub_expr => |bin| .{
2875 bin,
2876 if (t.typeHasWrappingOverflow(bin.qt)) .sub_wrap_assign else .sub_assign,
2877 if (lhs_ptr and t.signedness(bin.rhs.qt(t.tree)) == .signed) .usize else .none,
2878 },
2879 .mul_expr => |bin| .{
2880 bin,
2881 if (t.typeHasWrappingOverflow(bin.qt)) .mul_wrap_assign else .mul_assign,
2882 .none,
2883 },
2884 .mod_expr => |bin| .{ bin, .mod_assign, .none },
2885 .div_expr => |bin| .{ bin, .div_assign, .none },
2886 .shl_expr => |bin| .{ bin, .shl_assign, .shift },
2887 .shr_expr => |bin| .{ bin, .shr_assign, .shift },
2888 .bit_and_expr => |bin| .{ bin, .bit_and_assign, .none },
2889 .bit_xor_expr => |bin| .{ bin, .bit_xor_assign, .none },
2890 .bit_or_expr => |bin| .{ bin, .bit_or_assign, .none },
2891 else => unreachable,
2892 };
2893
2894 const lhs_node = blk: {
2895 const old_dummy = t.compound_assign_dummy;
2896 defer t.compound_assign_dummy = old_dummy;
2897 t.compound_assign_dummy = lhs_dummy_opt orelse try t.transExpr(scope, assign.lhs, .used);
2898
2899 break :blk try t.transExpr(scope, bin.lhs, .used);
2900 };
2901
2902 const rhs_node = try t.transExprCoercing(scope, bin.rhs, .used);
2903 const casted_rhs = switch (cast) {
2904 .none => rhs_node,
2905 .shift => try ZigTag.int_cast.create(t.arena, rhs_node),
2906 .usize => try t.usizeCastForWrappingPtrArithmetic(rhs_node),
2907 };
2908 return try t.createBinOpNode(op, lhs_node, casted_rhs);
2909}
2910
2911fn transIncDecExpr(
2912 t: *Translator,
2913 scope: *Scope,
2914 un: Node.Unary,
2915 position: enum { pre, post },
2916 kind: enum { inc, dec },
2917 used: ResultUsed,
2918) !ZigNode {
2919 const is_wrapping = t.typeHasWrappingOverflow(un.qt);
2920 const op: ZigTag = switch (kind) {
2921 .inc => if (is_wrapping) .add_wrap_assign else .add_assign,
2922 .dec => if (is_wrapping) .sub_wrap_assign else .sub_assign,
2923 };
2924
2925 const one_literal = ZigTag.one_literal.init();
2926 if (used == .unused) {
2927 const operand = try t.transExpr(scope, un.operand, .used);
2928 return try t.createBinOpNode(op, operand, one_literal);
2929 }
2930
2931 var block_scope = try Scope.Block.init(t, scope, true);
2932 defer block_scope.deinit();
2933
2934 const ref = try block_scope.reserveMangledName("ref");
2935 const operand = try t.transExprCoercing(&block_scope.base, un.operand, .used);
2936 const operand_ref = try ZigTag.address_of.create(t.arena, operand);
2937 const ref_decl = try ZigTag.var_simple.create(t.arena, .{ .name = ref, .init = operand_ref });
2938 try block_scope.statements.append(t.gpa, ref_decl);
2939
2940 const ref_ident = try ZigTag.identifier.create(t.arena, ref);
2941 const ref_deref = try ZigTag.deref.create(t.arena, ref_ident);
2942 const effect = try t.createBinOpNode(op, ref_deref, one_literal);
2943
2944 switch (position) {
2945 .pre => {
2946 try block_scope.statements.append(t.gpa, effect);
2947
2948 const break_node = try ZigTag.break_val.create(t.arena, .{
2949 .label = block_scope.label,
2950 .val = ref_deref,
2951 });
2952 try block_scope.statements.append(t.gpa, break_node);
2953 },
2954 .post => {
2955 const tmp = try block_scope.reserveMangledName("tmp");
2956 const tmp_decl = try ZigTag.var_simple.create(t.arena, .{ .name = tmp, .init = ref_deref });
2957 try block_scope.statements.append(t.gpa, tmp_decl);
2958
2959 try block_scope.statements.append(t.gpa, effect);
2960
2961 const tmp_ident = try ZigTag.identifier.create(t.arena, tmp);
2962 const break_node = try ZigTag.break_val.create(t.arena, .{
2963 .label = block_scope.label,
2964 .val = tmp_ident,
2965 });
2966 try block_scope.statements.append(t.gpa, break_node);
2967 },
2968 }
2969
2970 return try block_scope.complete();
2971}
2972
2973fn transPtrDiffExpr(t: *Translator, scope: *Scope, bin: Node.Binary) TransError!ZigNode {
2974 const lhs_uncasted = try t.transExpr(scope, bin.lhs, .used);
2975 const rhs_uncasted = try t.transExpr(scope, bin.rhs, .used);
2976
2977 const lhs = try ZigTag.int_from_ptr.create(t.arena, lhs_uncasted);
2978 const rhs = try ZigTag.int_from_ptr.create(t.arena, rhs_uncasted);
2979
2980 const sub_res = try t.createBinOpNode(.sub_wrap, lhs, rhs);
2981
2982 // @divExact(@as(<platform-ptrdiff_t>, @bitCast(@intFromPtr(lhs)) -% @intFromPtr(rhs)), @sizeOf(<lhs target type>))
2983 const ptrdiff_type = try t.transTypeIntWidthOf(bin.qt, true);
2984
2985 const bitcast = try ZigTag.as.create(t.arena, .{
2986 .lhs = ptrdiff_type,
2987 .rhs = try ZigTag.bit_cast.create(t.arena, sub_res),
2988 });
2989
2990 // C standard requires that pointer subtraction operands are of the same type,
2991 // otherwise it is undefined behavior. So we can assume the left and right
2992 // sides are the same Type and arbitrarily choose left.
2993 const lhs_ty = try t.transType(scope, bin.lhs.qt(t.tree), bin.lhs.tok(t.tree));
2994 const c_pointer = t.getContainer(lhs_ty).?;
2995
2996 if (c_pointer.castTag(.c_pointer)) |c_pointer_payload| {
2997 const sizeof = try ZigTag.sizeof.create(t.arena, c_pointer_payload.data.elem_type);
2998 return ZigTag.div_exact.create(t.arena, .{
2999 .lhs = bitcast,
3000 .rhs = sizeof,
3001 });
3002 } else {
3003 // This is an opaque/incomplete type. This subtraction exhibits Undefined Behavior by the C99 spec.
3004 // However, allowing subtraction on `void *` and function pointers is a commonly used extension.
3005 // So, just return the value in byte units, mirroring the behavior of this language extension as implemented by GCC and Clang.
3006 return bitcast;
3007 }
3008}
3009
3010/// Translate an arithmetic expression with a pointer operand and a signed-integer operand.
3011/// Zig requires a usize argument for pointer arithmetic, so we intCast to isize and then
3012/// bitcast to usize; pointer wraparound makes the math work.
3013/// Zig pointer addition is not commutative (unlike C); the pointer operand needs to be on the left.
3014/// The + operator in C is not a sequence point so it should be safe to switch the order if necessary.
3015fn transPointerArithmeticSignedOp(t: *Translator, scope: *Scope, bin: Node.Binary, op_id: ZigTag) TransError!ZigNode {
3016 std.debug.assert(op_id == .add or op_id == .sub);
3017
3018 const lhs_qt = bin.lhs.qt(t.tree);
3019 const swap_operands = op_id == .add and t.signedness(lhs_qt) == .signed;
3020
3021 const swizzled_lhs = if (swap_operands) bin.rhs else bin.lhs;
3022 const swizzled_rhs = if (swap_operands) bin.lhs else bin.rhs;
3023
3024 const lhs_node = try t.transExpr(scope, swizzled_lhs, .used);
3025 const rhs_node = try t.transExpr(scope, swizzled_rhs, .used);
3026
3027 const bitcast_node = try t.usizeCastForWrappingPtrArithmetic(rhs_node);
3028
3029 return t.createBinOpNode(op_id, lhs_node, bitcast_node);
3030}
3031
3032fn transMemberAccess(
3033 t: *Translator,
3034 scope: *Scope,
3035 kind: enum { normal, ptr },
3036 member_access: Node.MemberAccess,
3037 opt_base: ?ZigNode,
3038) TransError!ZigNode {
3039 const base_info = switch (kind) {
3040 .normal => member_access.base.qt(t.tree),
3041 .ptr => member_access.base.qt(t.tree).childType(t.comp),
3042 };
3043 const record = base_info.getRecord(t.comp).?;
3044 const field = record.fields[member_access.member_index];
3045 const field_name = if (field.name_tok == 0) t.anonymous_record_field_names.get(.{
3046 .parent = base_info.base(t.comp).qt,
3047 .field = field.qt,
3048 }).? else field.name.lookup(t.comp);
3049 const base_node = opt_base orelse try t.transExpr(scope, member_access.base, .used);
3050 const lhs = switch (kind) {
3051 .normal => base_node,
3052 .ptr => try ZigTag.deref.create(t.arena, base_node),
3053 };
3054 const field_access = try ZigTag.field_access.create(t.arena, .{
3055 .lhs = lhs,
3056 .field_name = field_name,
3057 });
3058
3059 // Flexible array members are translated as member functions.
3060 if (member_access.member_index == record.fields.len - 1 or base_info.base(t.comp).type == .@"union") {
3061 if (field.qt.get(t.comp, .array)) |array_ty| {
3062 if (array_ty.len == .incomplete or (array_ty.len == .fixed and array_ty.len.fixed == 0)) {
3063 return ZigTag.call.create(t.arena, .{ .lhs = field_access, .args = &.{} });
3064 }
3065 }
3066 }
3067
3068 return field_access;
3069}
3070
3071fn transArrayAccess(t: *Translator, scope: *Scope, array_access: Node.ArrayAccess, opt_base: ?ZigNode) TransError!ZigNode {
3072 // Unwrap the base statement if it's an array decayed to a bare pointer type
3073 // so that we index the array itself
3074 const base = base: {
3075 const base = array_access.base.get(t.tree);
3076 if (base != .cast) break :base array_access.base;
3077 if (base.cast.kind != .array_to_pointer) break :base array_access.base;
3078 break :base base.cast.operand;
3079 };
3080
3081 const base_node = opt_base orelse try t.transExpr(scope, base, .used);
3082 const index = index: {
3083 const index = try t.transExpr(scope, array_access.index, .used);
3084 const index_qt = array_access.index.qt(t.tree);
3085 const maybe_bigger_than_usize = switch (index_qt.base(t.comp).type) {
3086 .bool => {
3087 break :index try ZigTag.int_from_bool.create(t.arena, index);
3088 },
3089 .int => |int| switch (int) {
3090 .long_long, .ulong_long, .int128, .uint128 => true,
3091 else => false,
3092 },
3093 .bit_int => |bit_int| bit_int.bits > t.comp.target.ptrBitWidth(),
3094 else => unreachable,
3095 };
3096
3097 const is_nonnegative_int_literal = if (t.tree.value_map.get(array_access.index)) |val|
3098 val.compare(.gte, .zero, t.comp)
3099 else
3100 false;
3101 const is_signed = t.signedness(index_qt) == .signed;
3102
3103 if (is_signed and !is_nonnegative_int_literal) {
3104 // First cast to `isize` to get proper sign extension and
3105 // then @bitCast to `usize` to satisfy the compiler.
3106 const index_isize = try ZigTag.as.create(t.arena, .{
3107 .lhs = try ZigTag.type.create(t.arena, "isize"),
3108 .rhs = try ZigTag.int_cast.create(t.arena, index),
3109 });
3110 break :index try ZigTag.bit_cast.create(t.arena, index_isize);
3111 }
3112
3113 if (maybe_bigger_than_usize) {
3114 break :index try ZigTag.int_cast.create(t.arena, index);
3115 }
3116 break :index index;
3117 };
3118
3119 return ZigTag.array_access.create(t.arena, .{
3120 .lhs = base_node,
3121 .rhs = index,
3122 });
3123}
3124
3125fn transOffsetof(t: *Translator, scope: *Scope, arg: Node.Index) TransError!ZigNode {
3126 // Translate __builtin_offsetof(T, designator) as
3127 // @intFromPtr(&(@as(*allowzero T, @ptrFromInt(0)).designator))
3128 const member = try t.transMemberDesignator(scope, arg);
3129 const address = try ZigTag.address_of.create(t.arena, member);
3130 return ZigTag.int_from_ptr.create(t.arena, address);
3131}
3132
3133fn transMemberDesignator(t: *Translator, scope: *Scope, arg: Node.Index) TransError!ZigNode {
3134 switch (arg.get(t.tree)) {
3135 .default_init_expr => |default| {
3136 const elem_node = try t.transType(scope, default.qt, default.last_tok);
3137 const ptr_ty = try ZigTag.single_pointer.create(t.arena, .{
3138 .elem_type = elem_node,
3139 .is_allowzero = true,
3140 .is_const = false,
3141 .is_volatile = false,
3142 });
3143 const zero = try ZigTag.ptr_from_int.create(t.arena, ZigTag.zero_literal.init());
3144 return ZigTag.as.create(t.arena, .{ .lhs = ptr_ty, .rhs = zero });
3145 },
3146 .array_access_expr => |access| {
3147 const base = try t.transMemberDesignator(scope, access.base);
3148 return t.transArrayAccess(scope, access, base);
3149 },
3150 .member_access_expr => |access| {
3151 const base = try t.transMemberDesignator(scope, access.base);
3152 return t.transMemberAccess(scope, .normal, access, base);
3153 },
3154 .cast => |cast| {
3155 assert(cast.kind == .array_to_pointer);
3156 return t.transMemberDesignator(scope, cast.operand);
3157 },
3158 else => unreachable,
3159 }
3160}
3161
3162fn transBuiltinCall(
3163 t: *Translator,
3164 scope: *Scope,
3165 call: Node.BuiltinCall,
3166 used: ResultUsed,
3167) TransError!ZigNode {
3168 const builtin_name = t.tree.tokSlice(call.builtin_tok);
3169 if (std.mem.eql(u8, builtin_name, "__builtin_offsetof")) {
3170 const res = try t.transOffsetof(scope, call.args[0]);
3171 return t.maybeSuppressResult(used, res);
3172 }
3173
3174 const builtin = builtins.map.get(builtin_name) orelse
3175 return t.fail(error.UnsupportedTranslation, call.builtin_tok, "TODO implement function '{s}' in std.zig.c_builtins", .{builtin_name});
3176
3177 if (builtin.tag) |tag| switch (tag) {
3178 .byte_swap, .ceil, .cos, .sin, .exp, .exp2, .exp10, .abs, .log, .log2, .log10, .round, .sqrt, .trunc, .floor => {
3179 assert(call.args.len == 1);
3180 const arg = try t.transExprCoercing(scope, call.args[0], .used);
3181 const arg_ty = try t.transType(scope, call.args[0].qt(t.tree), call.args[0].tok(t.tree));
3182 const coerced = try ZigTag.as.create(t.arena, .{ .lhs = arg_ty, .rhs = arg });
3183
3184 const ptr = try t.arena.create(ast.Payload.UnOp);
3185 ptr.* = .{ .base = .{ .tag = tag }, .data = coerced };
3186 return t.maybeSuppressResult(used, ZigNode.initPayload(&ptr.base));
3187 },
3188 .@"unreachable" => return ZigTag.@"unreachable".init(),
3189 else => unreachable,
3190 };
3191
3192 const arg_nodes = try t.arena.alloc(ZigNode, call.args.len);
3193 for (call.args, arg_nodes) |c_arg, *zig_arg| {
3194 zig_arg.* = try t.transExprCoercing(scope, c_arg, .used);
3195 }
3196
3197 const builtin_identifier = try ZigTag.identifier.create(t.arena, "__builtin");
3198 const field_access = try ZigTag.field_access.create(t.arena, .{
3199 .lhs = builtin_identifier,
3200 .field_name = builtin.name,
3201 });
3202
3203 const res = try ZigTag.call.create(t.arena, .{
3204 .lhs = field_access,
3205 .args = arg_nodes,
3206 });
3207 if (call.qt.is(t.comp, .void)) return res;
3208 return t.maybeSuppressResult(used, res);
3209}
3210
3211fn transCall(
3212 t: *Translator,
3213 scope: *Scope,
3214 call: Node.Call,
3215 used: ResultUsed,
3216) TransError!ZigNode {
3217 const raw_fn_expr = try t.transExpr(scope, call.callee, .used);
3218 const fn_expr = blk: {
3219 loop: switch (call.callee.get(t.tree)) {
3220 .paren_expr => |paren_expr| {
3221 continue :loop paren_expr.operand.get(t.tree);
3222 },
3223 .decl_ref_expr => |decl_ref| {
3224 if (decl_ref.qt.is(t.comp, .func)) break :blk raw_fn_expr;
3225 },
3226 .cast => |cast| {
3227 if (cast.kind == .function_to_pointer) {
3228 continue :loop cast.operand.get(t.tree);
3229 }
3230 },
3231 .deref_expr, .addr_of_expr => |un| {
3232 continue :loop un.operand.get(t.tree);
3233 },
3234 .generic_expr => |generic| {
3235 continue :loop generic.chosen.get(t.tree);
3236 },
3237 .generic_association_expr => |generic| {
3238 continue :loop generic.expr.get(t.tree);
3239 },
3240 .generic_default_expr => |generic| {
3241 continue :loop generic.expr.get(t.tree);
3242 },
3243 else => {},
3244 }
3245 break :blk try ZigTag.unwrap.create(t.arena, raw_fn_expr);
3246 };
3247
3248 const callee_qt = call.callee.qt(t.tree);
3249 const maybe_ptr_ty = callee_qt.get(t.comp, .pointer);
3250 const func_qt = if (maybe_ptr_ty) |ptr| ptr.child else callee_qt;
3251 const func_ty = func_qt.get(t.comp, .func).?;
3252
3253 const arg_nodes = try t.arena.alloc(ZigNode, call.args.len);
3254 for (call.args, arg_nodes, 0..) |c_arg, *zig_arg, i| {
3255 if (i < func_ty.params.len) {
3256 zig_arg.* = try t.transExprCoercing(scope, c_arg, .used);
3257
3258 if (zig_arg.isBoolRes() and !func_ty.params[i].qt.is(t.comp, .bool)) {
3259 // In C the result type of a boolean expression is int. If this result is passed as
3260 // an argument to a function whose parameter is also int, there is no cast. Therefore
3261 // in Zig we'll need to cast it from bool to u1 (which will safely coerce to c_int).
3262 zig_arg.* = try ZigTag.int_from_bool.create(t.arena, zig_arg.*);
3263 }
3264 } else {
3265 zig_arg.* = try t.transExpr(scope, c_arg, .used);
3266
3267 if (zig_arg.isBoolRes()) {
3268 // Same as above but now we don't have a result type.
3269 const u1_node = try ZigTag.int_from_bool.create(t.arena, zig_arg.*);
3270 const c_int_node = try ZigTag.type.create(t.arena, "c_int");
3271 zig_arg.* = try ZigTag.as.create(t.arena, .{ .lhs = c_int_node, .rhs = u1_node });
3272 }
3273 }
3274 }
3275
3276 const res = try ZigTag.call.create(t.arena, .{
3277 .lhs = fn_expr,
3278 .args = arg_nodes,
3279 });
3280 if (call.qt.is(t.comp, .void)) return res;
3281 return t.maybeSuppressResult(used, res);
3282}
3283
3284const SuppressCast = enum { with_as, no_as };
3285
3286fn transIntLiteral(
3287 t: *Translator,
3288 scope: *Scope,
3289 literal_index: Node.Index,
3290 used: ResultUsed,
3291 suppress_as: SuppressCast,
3292) TransError!ZigNode {
3293 const val = t.tree.value_map.get(literal_index).?;
3294 const int_lit_node = try t.createIntNode(val);
3295 if (suppress_as == .no_as) {
3296 return t.maybeSuppressResult(used, int_lit_node);
3297 }
3298
3299 // Integer literals in C have types, and this can matter for several reasons.
3300 // For example, this is valid C:
3301 // unsigned char y = 256;
3302 // How this gets evaluated is the 256 is an integer, which gets truncated to signed char, then bit-casted
3303 // to unsigned char, resulting in 0. In order for this to work, we have to emit this zig code:
3304 // var y = @as(u8, @bitCast(@as(i8, @truncate(@as(c_int, 256)))));
3305
3306 // @as(T, x)
3307 const ty_node = try t.transType(scope, literal_index.qt(t.tree), literal_index.tok(t.tree));
3308 const as = try ZigTag.as.create(t.arena, .{ .lhs = ty_node, .rhs = int_lit_node });
3309 return t.maybeSuppressResult(used, as);
3310}
3311
3312fn transCharLiteral(
3313 t: *Translator,
3314 scope: *Scope,
3315 literal_index: Node.Index,
3316 used: ResultUsed,
3317 suppress_as: SuppressCast,
3318) TransError!ZigNode {
3319 const val = t.tree.value_map.get(literal_index).?;
3320 const char_literal = literal_index.get(t.tree).char_literal;
3321 const narrow = char_literal.kind == .ascii or char_literal.kind == .utf8;
3322
3323 // C has a somewhat obscure feature called multi-character character constant
3324 // e.g. 'abcd'
3325 const int_value = val.toInt(u32, t.comp).?;
3326 const int_lit_node = if (char_literal.kind == .ascii and int_value > 255)
3327 try t.createNumberNode(int_value, .int)
3328 else
3329 try t.createCharLiteralNode(narrow, int_value);
3330
3331 if (suppress_as == .no_as) {
3332 return t.maybeSuppressResult(used, int_lit_node);
3333 }
3334
3335 // See comment in `transIntLiteral` for why this code is here.
3336 // @as(T, x)
3337 const as_node = try ZigTag.as.create(t.arena, .{
3338 .lhs = try t.transType(scope, char_literal.qt, char_literal.literal_tok),
3339 .rhs = int_lit_node,
3340 });
3341 return t.maybeSuppressResult(used, as_node);
3342}
3343
3344fn transFloatLiteral(
3345 t: *Translator,
3346 scope: *Scope,
3347 literal_index: Node.Index,
3348 used: ResultUsed,
3349 suppress_as: SuppressCast,
3350) TransError!ZigNode {
3351 const val = t.tree.value_map.get(literal_index).?;
3352 const float_literal = literal_index.get(t.tree).float_literal;
3353
3354 var allocating: std.Io.Writer.Allocating = .init(t.gpa);
3355 defer allocating.deinit();
3356 _ = val.print(float_literal.qt, t.comp, &allocating.writer) catch return error.OutOfMemory;
3357
3358 const float_lit_node = try ZigTag.float_literal.create(t.arena, try t.arena.dupe(u8, allocating.getWritten()));
3359 if (suppress_as == .no_as) {
3360 return t.maybeSuppressResult(used, float_lit_node);
3361 }
3362
3363 const as_node = try ZigTag.as.create(t.arena, .{
3364 .lhs = try t.transType(scope, float_literal.qt, float_literal.literal_tok),
3365 .rhs = float_lit_node,
3366 });
3367 return t.maybeSuppressResult(used, as_node);
3368}
3369
3370fn transStringLiteral(
3371 t: *Translator,
3372 scope: *Scope,
3373 expr: Node.Index,
3374 literal: Node.CharLiteral,
3375) TransError!ZigNode {
3376 switch (literal.kind) {
3377 .ascii, .utf8 => return t.transNarrowStringLiteral(expr, literal),
3378 .utf16, .utf32, .wide => {
3379 const name = try std.fmt.allocPrint(t.arena, "{s}_string_{d}", .{ @tagName(literal.kind), t.getMangle() });
3380
3381 const array_type = try t.transTypeInit(scope, literal.qt, expr, literal.literal_tok);
3382 const lit_array = try t.transStringLiteralInitializer(expr, literal, array_type);
3383 const decl = try ZigTag.var_simple.create(t.arena, .{ .name = name, .init = lit_array });
3384 try scope.appendNode(decl);
3385 return ZigTag.identifier.create(t.arena, name);
3386 },
3387 }
3388}
3389
3390fn transNarrowStringLiteral(
3391 t: *Translator,
3392 expr: Node.Index,
3393 literal: Node.CharLiteral,
3394) TransError!ZigNode {
3395 const val = t.tree.value_map.get(expr).?;
3396
3397 const bytes = t.comp.interner.get(val.ref()).bytes;
3398 var allocating: std.Io.Writer.Allocating = try .initCapacity(t.gpa, bytes.len);
3399 defer allocating.deinit();
3400
3401 aro.Value.printString(bytes, literal.qt, t.comp, &allocating.writer) catch return error.OutOfMemory;
3402
3403 return ZigTag.string_literal.create(t.arena, try t.arena.dupe(u8, allocating.getWritten()));
3404}
3405
3406/// Translate a string literal that is initializing an array. In general narrow string
3407/// literals become `"<string>".*` or `"<string>"[0..<size>].*` if they need truncation.
3408/// Wide string literals become an array of integers. zero-fillers pad out the array to
3409/// the appropriate length, if necessary.
3410fn transStringLiteralInitializer(
3411 t: *Translator,
3412 expr: Node.Index,
3413 literal: Node.CharLiteral,
3414 array_type: ZigNode,
3415) TransError!ZigNode {
3416 assert(array_type.tag() == .array_type or array_type.tag() == .null_sentinel_array_type);
3417
3418 const is_narrow = literal.kind == .ascii or literal.kind == .utf8;
3419
3420 // The length of the string literal excluding the sentinel.
3421 const str_length = literal.qt.arrayLen(t.comp).? - 1;
3422
3423 const payload = (array_type.castTag(.array_type) orelse array_type.castTag(.null_sentinel_array_type).?).data;
3424 const array_size = payload.len;
3425 const elem_type = payload.elem_type;
3426
3427 if (array_size == 0) return ZigTag.empty_array.create(t.arena, array_type);
3428
3429 const num_inits = @min(str_length, array_size);
3430 if (num_inits == 0) {
3431 return ZigTag.array_filler.create(t.arena, .{
3432 .type = elem_type,
3433 .filler = ZigTag.zero_literal.init(),
3434 .count = array_size,
3435 });
3436 }
3437
3438 const init_node = if (is_narrow) blk: {
3439 // "string literal".* or string literal"[0..num_inits].*
3440 var str = try t.transNarrowStringLiteral(expr, literal);
3441 if (str_length != array_size) str = try ZigTag.string_slice.create(t.arena, .{ .string = str, .end = num_inits });
3442 break :blk try ZigTag.deref.create(t.arena, str);
3443 } else blk: {
3444 const size = literal.qt.childType(t.comp).sizeof(t.comp);
3445
3446 const val = t.tree.value_map.get(expr).?;
3447 const bytes = t.comp.interner.get(val.ref()).bytes;
3448
3449 const init_list = try t.arena.alloc(ZigNode, @intCast(num_inits));
3450 for (init_list, 0..) |*item, i| {
3451 const codepoint = switch (size) {
3452 2 => @as(*const u16, @alignCast(@ptrCast(bytes.ptr + i * 2))).*,
3453 4 => @as(*const u32, @alignCast(@ptrCast(bytes.ptr + i * 4))).*,
3454 else => unreachable,
3455 };
3456 item.* = try t.createCharLiteralNode(false, codepoint);
3457 }
3458 const init_args: ast.Payload.Array.ArrayTypeInfo = .{ .len = num_inits, .elem_type = elem_type };
3459 const init_array_type = if (array_type.tag() == .array_type)
3460 try ZigTag.array_type.create(t.arena, init_args)
3461 else
3462 try ZigTag.null_sentinel_array_type.create(t.arena, init_args);
3463 break :blk try ZigTag.array_init.create(t.arena, .{
3464 .cond = init_array_type,
3465 .cases = init_list,
3466 });
3467 };
3468
3469 if (num_inits == array_size) return init_node;
3470 assert(array_size > str_length); // If array_size <= str_length, `num_inits == array_size` and we've already returned.
3471
3472 const filler_node = try ZigTag.array_filler.create(t.arena, .{
3473 .type = elem_type,
3474 .filler = ZigTag.zero_literal.init(),
3475 .count = array_size - str_length,
3476 });
3477 return ZigTag.array_cat.create(t.arena, .{ .lhs = init_node, .rhs = filler_node });
3478}
3479
3480fn transCompoundLiteral(
3481 t: *Translator,
3482 scope: *Scope,
3483 literal: Node.CompoundLiteral,
3484 used: ResultUsed,
3485) TransError!ZigNode {
3486 if (used == .unused) {
3487 return t.transExpr(scope, literal.initializer, .unused);
3488 }
3489
3490 // TODO taking a reference to a compound literal should result in a mutable
3491 // pointer (unless the literal is const).
3492
3493 const initializer = try t.transExprCoercing(scope, literal.initializer, .used);
3494 const ty = try t.transType(scope, literal.qt, literal.l_paren_tok);
3495 if (!literal.thread_local and literal.storage_class != .static) {
3496 // In the simple case a compound literal can be translated
3497 // simply as `@as(type, initializer)`.
3498 return ZigTag.as.create(t.arena, .{ .lhs = ty, .rhs = initializer });
3499 }
3500
3501 // Otherwise static or thread local compound literals are translated as
3502 // a reference to a variable wrapped in a struct.
3503
3504 var block_scope = try Scope.Block.init(t, scope, true);
3505 defer block_scope.deinit();
3506
3507 const tmp = try block_scope.reserveMangledName("tmp");
3508 const wrapped_name = "compound_literal";
3509
3510 // const tmp = struct { var compound_literal = initializer };
3511 const temp_decl = try ZigTag.var_decl.create(t.arena, .{
3512 .is_pub = false,
3513 .is_const = literal.qt.@"const",
3514 .is_extern = false,
3515 .is_export = false,
3516 .is_threadlocal = literal.thread_local,
3517 .linksection_string = null,
3518 .alignment = null,
3519 .name = wrapped_name,
3520 .type = ty,
3521 .init = initializer,
3522 });
3523 const wrapped = try ZigTag.wrapped_local.create(t.arena, .{ .name = tmp, .init = temp_decl });
3524 try block_scope.statements.append(t.gpa, wrapped);
3525
3526 // break :blk tmp.compound_literal
3527 const static_tmp_ident = try ZigTag.identifier.create(t.arena, tmp);
3528 const field_access = try ZigTag.field_access.create(t.arena, .{
3529 .lhs = static_tmp_ident,
3530 .field_name = wrapped_name,
3531 });
3532 const break_node = try ZigTag.break_val.create(t.arena, .{
3533 .label = block_scope.label,
3534 .val = field_access,
3535 });
3536 try block_scope.statements.append(t.gpa, break_node);
3537
3538 return block_scope.complete();
3539}
3540
3541fn transDefaultInit(
3542 t: *Translator,
3543 scope: *Scope,
3544 default_init: Node.DefaultInit,
3545 used: ResultUsed,
3546 suppress_as: SuppressCast,
3547) TransError!ZigNode {
3548 assert(used == .used);
3549 const type_node = try t.transType(scope, default_init.qt, default_init.last_tok);
3550 return try t.createZeroValueNode(default_init.qt, type_node, suppress_as);
3551}
3552
3553fn transArrayInit(
3554 t: *Translator,
3555 scope: *Scope,
3556 array_init: Node.ContainerInit,
3557 used: ResultUsed,
3558) TransError!ZigNode {
3559 assert(used == .used);
3560 const array_item_qt = array_init.container_qt.childType(t.comp);
3561 const array_item_type = try t.transType(scope, array_item_qt, array_init.l_brace_tok);
3562 var maybe_lhs: ?ZigNode = null;
3563 var val_list: std.ArrayListUnmanaged(ZigNode) = .empty;
3564 defer val_list.deinit(t.gpa);
3565 var i: usize = 0;
3566 while (i < array_init.items.len) {
3567 const rhs = switch (array_init.items[i].get(t.tree)) {
3568 .array_filler_expr => |array_filler| blk: {
3569 const node = try ZigTag.array_filler.create(t.arena, .{
3570 .type = array_item_type,
3571 .filler = try t.createZeroValueNode(array_item_qt, array_item_type, .no_as),
3572 .count = @intCast(array_filler.count),
3573 });
3574 i += 1;
3575 break :blk node;
3576 },
3577 else => blk: {
3578 defer val_list.clearRetainingCapacity();
3579 while (i < array_init.items.len) : (i += 1) {
3580 if (array_init.items[i].get(t.tree) == .array_filler_expr) break;
3581 const expr = try t.transExprCoercing(scope, array_init.items[i], .used);
3582 try val_list.append(t.gpa, expr);
3583 }
3584 const array_type = try ZigTag.array_type.create(t.arena, .{
3585 .elem_type = array_item_type,
3586 .len = val_list.items.len,
3587 });
3588 const array_init_node = try ZigTag.array_init.create(t.arena, .{
3589 .cond = array_type,
3590 .cases = try t.arena.dupe(ZigNode, val_list.items),
3591 });
3592 break :blk array_init_node;
3593 },
3594 };
3595 maybe_lhs = if (maybe_lhs) |lhs| blk: {
3596 const cat = try ZigTag.array_cat.create(t.arena, .{
3597 .lhs = lhs,
3598 .rhs = rhs,
3599 });
3600 break :blk cat;
3601 } else rhs;
3602 }
3603 return maybe_lhs orelse try ZigTag.container_init_dot.create(t.arena, &.{});
3604}
3605
3606fn transUnionInit(
3607 t: *Translator,
3608 scope: *Scope,
3609 union_init: Node.UnionInit,
3610 used: ResultUsed,
3611) TransError!ZigNode {
3612 assert(used == .used);
3613 const init_expr = union_init.initializer orelse
3614 return ZigTag.undefined_literal.init();
3615
3616 if (init_expr.get(t.tree) == .default_init_expr) {
3617 return try t.transExpr(scope, init_expr, used);
3618 }
3619
3620 const union_type = try t.transType(scope, union_init.union_qt, union_init.l_brace_tok);
3621
3622 const union_base = union_init.union_qt.base(t.comp);
3623 const field = union_base.type.@"union".fields[union_init.field_index];
3624 const field_name = if (field.name_tok == 0) t.anonymous_record_field_names.get(.{
3625 .parent = union_base.qt,
3626 .field = field.qt,
3627 }).? else field.name.lookup(t.comp);
3628
3629 const field_init = try t.arena.create(ast.Payload.ContainerInit.Initializer);
3630 field_init.* = .{
3631 .name = field_name,
3632 .value = try t.transExprCoercing(scope, init_expr, .used),
3633 };
3634 const container_init = try ZigTag.container_init.create(t.arena, .{
3635 .lhs = union_type,
3636 .inits = field_init[0..1],
3637 });
3638 return container_init;
3639}
3640
3641fn transStructInit(
3642 t: *Translator,
3643 scope: *Scope,
3644 struct_init: Node.ContainerInit,
3645 used: ResultUsed,
3646) TransError!ZigNode {
3647 assert(used == .used);
3648 const struct_type = try t.transType(scope, struct_init.container_qt, struct_init.l_brace_tok);
3649 const field_inits = try t.arena.alloc(ast.Payload.ContainerInit.Initializer, struct_init.items.len);
3650
3651 const struct_base = struct_init.container_qt.base(t.comp);
3652 for (
3653 field_inits,
3654 struct_init.items,
3655 struct_base.type.@"struct".fields,
3656 ) |*init, field_expr, field| {
3657 const field_name = if (field.name_tok == 0) t.anonymous_record_field_names.get(.{
3658 .parent = struct_base.qt,
3659 .field = field.qt,
3660 }).? else field.name.lookup(t.comp);
3661 init.* = .{
3662 .name = field_name,
3663 .value = try t.transExprCoercing(scope, field_expr, .used),
3664 };
3665 }
3666
3667 const container_init = try ZigTag.container_init.create(t.arena, .{
3668 .lhs = struct_type,
3669 .inits = field_inits,
3670 });
3671 return container_init;
3672}
3673
3674fn transTypeInfo(
3675 t: *Translator,
3676 scope: *Scope,
3677 op: ZigTag,
3678 typeinfo: Node.TypeInfo,
3679) TransError!ZigNode {
3680 const operand = operand: {
3681 if (typeinfo.expr) |expr| {
3682 const operand = try t.transExpr(scope, expr, .used);
3683 break :operand try ZigTag.typeof.create(t.arena, operand);
3684 }
3685 break :operand try t.transType(scope, typeinfo.operand_qt, typeinfo.op_tok);
3686 };
3687
3688 const payload = try t.arena.create(ast.Payload.UnOp);
3689 payload.* = .{
3690 .base = .{ .tag = op },
3691 .data = operand,
3692 };
3693 return ZigNode.initPayload(&payload.base);
3694}
3695
3696fn transStmtExpr(
3697 t: *Translator,
3698 scope: *Scope,
3699 stmt_expr: Node.Unary,
3700 used: ResultUsed,
3701) TransError!ZigNode {
3702 const compound_stmt = stmt_expr.operand.get(t.tree).compound_stmt;
3703 if (used == .unused) {
3704 return t.transCompoundStmt(scope, compound_stmt);
3705 }
3706 var block_scope = try Scope.Block.init(t, scope, true);
3707 defer block_scope.deinit();
3708
3709 for (compound_stmt.body[0 .. compound_stmt.body.len - 1]) |stmt| {
3710 const result = try t.transStmt(&block_scope.base, stmt);
3711 switch (result.tag()) {
3712 .declaration, .empty_block => {},
3713 else => try block_scope.statements.append(t.gpa, result),
3714 }
3715 }
3716
3717 const last_result = try t.transExpr(&block_scope.base, compound_stmt.body[compound_stmt.body.len - 1], .used);
3718 switch (last_result.tag()) {
3719 .declaration, .empty_block => {},
3720 else => {
3721 const break_node = try ZigTag.break_val.create(t.arena, .{
3722 .label = block_scope.label,
3723 .val = last_result,
3724 });
3725 try block_scope.statements.append(t.gpa, break_node);
3726 },
3727 }
3728 return block_scope.complete();
3729}
3730
3731fn transConvertvectorExpr(
3732 t: *Translator,
3733 scope: *Scope,
3734 convertvector: Node.Convertvector,
3735) TransError!ZigNode {
3736 var block_scope = try Scope.Block.init(t, scope, true);
3737 defer block_scope.deinit();
3738
3739 const src_expr_node = try t.transExpr(&block_scope.base, convertvector.operand, .used);
3740 const tmp = try block_scope.reserveMangledName("tmp");
3741 const tmp_decl = try ZigTag.var_simple.create(t.arena, .{ .name = tmp, .init = src_expr_node });
3742 try block_scope.statements.append(t.gpa, tmp_decl);
3743 const tmp_ident = try ZigTag.identifier.create(t.arena, tmp);
3744
3745 const dest_type_node = try t.transType(&block_scope.base, convertvector.dest_qt, convertvector.builtin_tok);
3746 const dest_vec_ty = convertvector.dest_qt.get(t.comp, .vector).?;
3747 const src_vec_ty = convertvector.operand.qt(t.tree).get(t.comp, .vector).?;
3748
3749 const src_elem_sk = src_vec_ty.elem.scalarKind(t.comp);
3750 const dest_elem_sk = convertvector.dest_qt.childType(t.comp).scalarKind(t.comp);
3751
3752 const items = try t.arena.alloc(ZigNode, dest_vec_ty.len);
3753 for (items, 0..dest_vec_ty.len) |*item, i| {
3754 const value = try ZigTag.array_access.create(t.arena, .{
3755 .lhs = tmp_ident,
3756 .rhs = try t.createNumberNode(i, .int),
3757 });
3758
3759 if (src_elem_sk == .float and dest_elem_sk == .float) {
3760 item.* = try ZigTag.float_cast.create(t.arena, value);
3761 } else if (src_elem_sk == .float) {
3762 item.* = try ZigTag.int_from_float.create(t.arena, value);
3763 } else if (dest_elem_sk == .float) {
3764 item.* = try ZigTag.float_from_int.create(t.arena, value);
3765 } else {
3766 item.* = try t.transIntCast(value, src_vec_ty.elem, dest_vec_ty.elem);
3767 }
3768 }
3769
3770 const vec_init = try ZigTag.array_init.create(t.arena, .{
3771 .cond = dest_type_node,
3772 .cases = items,
3773 });
3774 const break_node = try ZigTag.break_val.create(t.arena, .{
3775 .label = block_scope.label,
3776 .val = vec_init,
3777 });
3778 try block_scope.statements.append(t.gpa, break_node);
3779
3780 return block_scope.complete();
3781}
3782
3783fn transShufflevectorExpr(
3784 t: *Translator,
3785 scope: *Scope,
3786 shufflevector: Node.Shufflevector,
3787) TransError!ZigNode {
3788 if (shufflevector.indexes.len == 0) {
3789 return t.fail(error.UnsupportedTranslation, shufflevector.builtin_tok, "@shuffle needs at least 1 index", .{});
3790 }
3791
3792 const a = try t.transExpr(scope, shufflevector.lhs, .used);
3793 const b = try t.transExpr(scope, shufflevector.rhs, .used);
3794
3795 // First two arguments to __builtin_shufflevector must be the same type
3796 const vector_child_type = try t.vectorTypeInfo(a, "child");
3797 const vector_len = try t.vectorTypeInfo(a, "len");
3798 const shuffle_mask = blk: {
3799 const mask_len = shufflevector.indexes.len;
3800
3801 const mask_type = try ZigTag.vector.create(t.arena, .{
3802 .lhs = try t.createNumberNode(mask_len, .int),
3803 .rhs = try ZigTag.type.create(t.arena, "i32"),
3804 });
3805
3806 const init_list = try t.arena.alloc(ZigNode, mask_len);
3807 for (init_list, shufflevector.indexes) |*init, index| {
3808 const index_expr = try t.transExprCoercing(scope, index, .used);
3809 const converted_index = try t.createHelperCallNode(.shuffleVectorIndex, &.{ index_expr, vector_len });
3810 init.* = converted_index;
3811 }
3812
3813 break :blk try ZigTag.array_init.create(t.arena, .{
3814 .cond = mask_type,
3815 .cases = init_list,
3816 });
3817 };
3818
3819 return ZigTag.shuffle.create(t.arena, .{
3820 .element_type = vector_child_type,
3821 .a = a,
3822 .b = b,
3823 .mask_vector = shuffle_mask,
3824 });
3825}
3826
3827// =====================
3828// Node creation helpers
3829// =====================
3830
3831fn createZeroValueNode(
3832 t: *Translator,
3833 qt: QualType,
3834 type_node: ZigNode,
3835 suppress_as: SuppressCast,
3836) !ZigNode {
3837 switch (qt.base(t.comp).type) {
3838 .bool => return ZigTag.false_literal.init(),
3839 .int, .bit_int, .float => {
3840 const zero_literal = ZigTag.zero_literal.init();
3841 return switch (suppress_as) {
3842 .with_as => try t.createBinOpNode(.as, type_node, zero_literal),
3843 .no_as => zero_literal,
3844 };
3845 },
3846 .pointer => {
3847 const null_literal = ZigTag.null_literal.init();
3848 return switch (suppress_as) {
3849 .with_as => try t.createBinOpNode(.as, type_node, null_literal),
3850 .no_as => null_literal,
3851 };
3852 },
3853 else => {},
3854 }
3855 return try ZigTag.std_mem_zeroes.create(t.arena, type_node);
3856}
3857
3858fn createIntNode(t: *Translator, int: aro.Value) !ZigNode {
3859 var space: aro.Interner.Tag.Int.BigIntSpace = undefined;
3860 var big = t.comp.interner.get(int.ref()).toBigInt(&space);
3861 const is_negative = !big.positive;
3862 big.positive = true;
3863
3864 const str = big.toStringAlloc(t.arena, 10, .lower) catch |err| switch (err) {
3865 error.OutOfMemory => return error.OutOfMemory,
3866 };
3867 const res = try ZigTag.integer_literal.create(t.arena, str);
3868 if (is_negative) return ZigTag.negate.create(t.arena, res);
3869 return res;
3870}
3871
3872fn createNumberNode(t: *Translator, num: anytype, num_kind: enum { int, float }) !ZigNode {
3873 const fmt_s = switch (@typeInfo(@TypeOf(num))) {
3874 .int, .comptime_int => "{d}",
3875 else => "{s}",
3876 };
3877 const str = try std.fmt.allocPrint(t.arena, fmt_s, .{num});
3878 if (num_kind == .float)
3879 return ZigTag.float_literal.create(t.arena, str)
3880 else
3881 return ZigTag.integer_literal.create(t.arena, str);
3882}
3883
3884fn createCharLiteralNode(t: *Translator, narrow: bool, val: u32) TransError!ZigNode {
3885 return ZigTag.char_literal.create(t.arena, if (narrow)
3886 try std.fmt.allocPrint(t.arena, "'{f}'", .{std.zig.fmtChar(&.{@as(u8, @intCast(val))})})
3887 else
3888 try std.fmt.allocPrint(t.arena, "'\\u{{{x}}}'", .{val}));
3889}
3890
3891fn createBinOpNode(
3892 t: *Translator,
3893 op: ZigTag,
3894 lhs: ZigNode,
3895 rhs: ZigNode,
3896) !ZigNode {
3897 const payload = try t.arena.create(ast.Payload.BinOp);
3898 payload.* = .{
3899 .base = .{ .tag = op },
3900 .data = .{
3901 .lhs = lhs,
3902 .rhs = rhs,
3903 },
3904 };
3905 return ZigNode.initPayload(&payload.base);
3906}
3907
3908pub fn createHelperCallNode(t: *Translator, name: std.meta.DeclEnum(@import("helpers")), args_opt: ?[]const ZigNode) !ZigNode {
3909 if (args_opt) |args| {
3910 return ZigTag.helper_call.create(t.arena, .{
3911 .name = @tagName(name),
3912 .args = try t.arena.dupe(ZigNode, args),
3913 });
3914 } else {
3915 return ZigTag.helper_ref.create(t.arena, @tagName(name));
3916 }
3917}
3918
3919/// Cast a signed integer node to a usize, for use in pointer arithmetic. Negative numbers
3920/// will become very large positive numbers but that is ok since we only use this in
3921/// pointer arithmetic expressions, where wraparound will ensure we get the correct value.
3922/// node -> @as(usize, @bitCast(@as(isize, @intCast(node))))
3923fn usizeCastForWrappingPtrArithmetic(t: *Translator, node: ZigNode) TransError!ZigNode {
3924 const intcast_node = try ZigTag.as.create(t.arena, .{
3925 .lhs = try ZigTag.type.create(t.arena, "isize"),
3926 .rhs = try ZigTag.int_cast.create(t.arena, node),
3927 });
3928
3929 return ZigTag.as.create(t.arena, .{
3930 .lhs = try ZigTag.type.create(t.arena, "usize"),
3931 .rhs = try ZigTag.bit_cast.create(t.arena, intcast_node),
3932 });
3933}
3934
3935/// @typeInfo(@TypeOf(vec_node)).vector.<field>
3936fn vectorTypeInfo(t: *Translator, vec_node: ZigNode, field: []const u8) TransError!ZigNode {
3937 const typeof_call = try ZigTag.typeof.create(t.arena, vec_node);
3938 const typeinfo_call = try ZigTag.typeinfo.create(t.arena, typeof_call);
3939 const vector_type_info = try ZigTag.field_access.create(t.arena, .{ .lhs = typeinfo_call, .field_name = "vector" });
3940 return ZigTag.field_access.create(t.arena, .{ .lhs = vector_type_info, .field_name = field });
3941}
3942
3943/// Build a getter function for a flexible array field in a C record
3944/// e.g. `T items[]` or `T items[0]`. The generated function returns a [*c] pointer
3945/// to the flexible array with the correct const and volatile qualifiers
3946fn createFlexibleMemberFn(
3947 t: *Translator,
3948 member_name: []const u8,
3949 field_name: []const u8,
3950) Error!ZigNode {
3951 const self_param_name = "self";
3952 const self_param = try ZigTag.identifier.create(t.arena, self_param_name);
3953 const self_type = try ZigTag.typeof.create(t.arena, self_param);
3954
3955 const fn_params = try t.arena.alloc(ast.Payload.Param, 1);
3956 fn_params[0] = .{
3957 .name = self_param_name,
3958 .type = ZigTag.@"anytype".init(),
3959 .is_noalias = false,
3960 };
3961
3962 // @typeInfo(@TypeOf(self.*.<field_name>)).pointer.child
3963 const dereffed = try ZigTag.deref.create(t.arena, self_param);
3964 const field_access = try ZigTag.field_access.create(t.arena, .{ .lhs = dereffed, .field_name = field_name });
3965 const type_of = try ZigTag.typeof.create(t.arena, field_access);
3966 const type_info = try ZigTag.typeinfo.create(t.arena, type_of);
3967 const array_info = try ZigTag.field_access.create(t.arena, .{ .lhs = type_info, .field_name = "array" });
3968 const child_info = try ZigTag.field_access.create(t.arena, .{ .lhs = array_info, .field_name = "child" });
3969
3970 const return_type = try t.createHelperCallNode(.FlexibleArrayType, &.{ self_type, child_info });
3971
3972 // return @ptrCast(&self.*.<field_name>);
3973 const address_of = try ZigTag.address_of.create(t.arena, field_access);
3974 const casted = try ZigTag.ptr_cast.create(t.arena, address_of);
3975 const return_stmt = try ZigTag.@"return".create(t.arena, casted);
3976 const body = try ZigTag.block_single.create(t.arena, return_stmt);
3977
3978 return ZigTag.func.create(t.arena, .{
3979 .is_pub = true,
3980 .is_extern = false,
3981 .is_export = false,
3982 .is_inline = false,
3983 .is_var_args = false,
3984 .name = member_name,
3985 .linksection_string = null,
3986 .explicit_callconv = null,
3987 .params = fn_params,
3988 .return_type = return_type,
3989 .body = body,
3990 .alignment = null,
3991 });
3992}
3993
3994// =================
3995// Macro translation
3996// =================
3997
3998fn transMacros(t: *Translator) !void {
3999 var tok_list = std.ArrayList(CToken).init(t.gpa);
4000 defer tok_list.deinit();
4001
4002 var pattern_list = try PatternList.init(t.gpa);
4003 defer pattern_list.deinit(t.gpa);
4004
4005 for (t.pp.defines.keys(), t.pp.defines.values()) |name, macro| {
4006 if (macro.is_builtin) continue;
4007 if (t.global_scope.containsNow(name)) {
4008 continue;
4009 }
4010
4011 tok_list.items.len = 0;
4012 try tok_list.ensureUnusedCapacity(macro.tokens.len);
4013 for (macro.tokens) |tok| {
4014 switch (tok.id) {
4015 .invalid => continue,
4016 .whitespace => continue,
4017 .comment => continue,
4018 .macro_ws => continue,
4019 else => {},
4020 }
4021 tok_list.appendAssumeCapacity(tok);
4022 }
4023
4024 if (macro.is_func) {
4025 const ms: PatternList.MacroSlicer = .{
4026 .tokens = tok_list.items,
4027 .source = t.comp.getSource(macro.loc.id).buf,
4028 .params = @intCast(macro.params.len),
4029 };
4030 if (try pattern_list.match(ms)) |impl| {
4031 const decl = try ZigTag.pub_var_simple.create(t.arena, .{
4032 .name = name,
4033 .init = try t.createHelperCallNode(impl, null),
4034 });
4035 try t.addTopLevelDecl(name, decl);
4036 continue;
4037 }
4038 }
4039
4040 if (t.checkTranslatableMacro(tok_list.items, macro.params)) |err| {
4041 switch (err) {
4042 .undefined_identifier => |ident| try t.failDeclExtra(&t.global_scope.base, macro.loc, name, "unable to translate macro: undefined identifier `{s}`", .{ident}),
4043 .invalid_arg_usage => |ident| try t.failDeclExtra(&t.global_scope.base, macro.loc, name, "unable to translate macro: untranslatable usage of arg `{s}`", .{ident}),
4044 }
4045 continue;
4046 }
4047
4048 var macro_translator: MacroTranslator = .{
4049 .t = t,
4050 .tokens = tok_list.items,
4051 .source = t.comp.getSource(macro.loc.id).buf,
4052 .name = name,
4053 .macro = macro,
4054 };
4055
4056 const res = if (macro.is_func)
4057 macro_translator.transFnMacro()
4058 else
4059 macro_translator.transMacro();
4060 res catch |err| switch (err) {
4061 error.ParseError => continue,
4062 error.OutOfMemory => |e| return e,
4063 };
4064 }
4065}
4066
4067const MacroTranslateError = union(enum) {
4068 undefined_identifier: []const u8,
4069 invalid_arg_usage: []const u8,
4070};
4071
4072fn checkTranslatableMacro(t: *Translator, tokens: []const CToken, params: []const []const u8) ?MacroTranslateError {
4073 var last_is_type_kw = false;
4074 var i: usize = 0;
4075 while (i < tokens.len) : (i += 1) {
4076 const token = tokens[i];
4077 switch (token.id) {
4078 .period, .arrow => i += 1, // skip next token since field identifiers can be unknown
4079 .keyword_struct, .keyword_union, .keyword_enum => if (!last_is_type_kw) {
4080 last_is_type_kw = true;
4081 continue;
4082 },
4083 .macro_param, .macro_param_no_expand => {
4084 if (last_is_type_kw) {
4085 return .{ .invalid_arg_usage = params[token.end] };
4086 }
4087 },
4088 .identifier, .extended_identifier => {
4089 const identifier = t.pp.tokSlice(token);
4090 if (!t.global_scope.contains(identifier) and !builtins.map.has(identifier)) {
4091 return .{ .undefined_identifier = identifier };
4092 }
4093 },
4094 else => {},
4095 }
4096 last_is_type_kw = false;
4097 }
4098 return null;
4099}
4100
4101fn getContainer(t: *Translator, node: ZigNode) ?ZigNode {
4102 switch (node.tag()) {
4103 .@"union",
4104 .@"struct",
4105 .address_of,
4106 .bit_not,
4107 .not,
4108 .optional_type,
4109 .negate,
4110 .negate_wrap,
4111 .array_type,
4112 .c_pointer,
4113 .single_pointer,
4114 => return node,
4115
4116 .identifier => {
4117 const ident = node.castTag(.identifier).?;
4118 if (t.global_scope.sym_table.get(ident.data)) |value| {
4119 if (value.castTag(.var_decl)) |var_decl|
4120 return t.getContainer(var_decl.data.init.?);
4121 if (value.castTag(.var_simple) orelse value.castTag(.pub_var_simple)) |var_decl|
4122 return t.getContainer(var_decl.data.init);
4123 }
4124 },
4125
4126 .field_access => {
4127 const field_access = node.castTag(.field_access).?;
4128
4129 if (t.getContainerTypeOf(field_access.data.lhs)) |ty_node| {
4130 if (ty_node.castTag(.@"struct") orelse ty_node.castTag(.@"union")) |container| {
4131 for (container.data.fields) |field| {
4132 if (mem.eql(u8, field.name, field_access.data.field_name)) {
4133 return t.getContainer(field.type);
4134 }
4135 }
4136 }
4137 }
4138 },
4139
4140 else => {},
4141 }
4142 return null;
4143}
4144
4145fn getContainerTypeOf(t: *Translator, ref: ZigNode) ?ZigNode {
4146 if (ref.castTag(.identifier)) |ident| {
4147 if (t.global_scope.sym_table.get(ident.data)) |value| {
4148 if (value.castTag(.var_decl)) |var_decl| {
4149 return t.getContainer(var_decl.data.type);
4150 }
4151 }
4152 } else if (ref.castTag(.field_access)) |field_access| {
4153 if (t.getContainerTypeOf(field_access.data.lhs)) |ty_node| {
4154 if (ty_node.castTag(.@"struct") orelse ty_node.castTag(.@"union")) |container| {
4155 for (container.data.fields) |field| {
4156 if (mem.eql(u8, field.name, field_access.data.field_name)) {
4157 return t.getContainer(field.type);
4158 }
4159 }
4160 } else return ty_node;
4161 }
4162 }
4163 return null;
4164}
4165
4166pub fn getFnProto(t: *Translator, ref: ZigNode) ?*ast.Payload.Func {
4167 const init = if (ref.castTag(.var_decl)) |v|
4168 v.data.init orelse return null
4169 else if (ref.castTag(.var_simple) orelse ref.castTag(.pub_var_simple)) |v|
4170 v.data.init
4171 else
4172 return null;
4173 if (t.getContainerTypeOf(init)) |ty_node| {
4174 if (ty_node.castTag(.optional_type)) |prefix| {
4175 if (prefix.data.castTag(.single_pointer)) |sp| {
4176 if (sp.data.elem_type.castTag(.func)) |fn_proto| {
4177 return fn_proto;
4178 }
4179 }
4180 }
4181 }
4182 return null;
4183}
lib/compiler/translate-c/ast.zig created+3063
...@@ -0,0 +1,3063 @@
1const std = @import("std");
2const Allocator = std.mem.Allocator;
3
4pub const Node = extern union {
5 /// If the tag value is less than Tag.no_payload_count, then no pointer
6 /// dereference is needed.
7 tag_if_small_enough: usize,
8 ptr_otherwise: *Payload,
9
10 pub const Tag = enum {
11 /// Declarations add themselves to the correct scopes and should not be emitted as this tag.
12 declaration,
13 null_literal,
14 undefined_literal,
15 /// opaque {}
16 opaque_literal,
17 true_literal,
18 false_literal,
19 empty_block,
20 return_void,
21 zero_literal,
22 one_literal,
23 @"unreachable",
24 void_type,
25 noreturn_type,
26 @"anytype",
27 @"continue",
28 @"break",
29 // After this, the tag requires a payload.
30
31 integer_literal,
32 float_literal,
33 string_literal,
34 char_literal,
35 enum_literal,
36 /// "string"[0..end]
37 string_slice,
38 identifier,
39 @"if",
40 /// if (!operand) break;
41 if_not_break,
42 @"while",
43 /// while (true) operand
44 while_true,
45 @"switch",
46 /// else => operand,
47 switch_else,
48 /// items => body,
49 switch_prong,
50 break_val,
51 @"return",
52 field_access,
53 array_access,
54 call,
55 var_decl,
56 /// const name = struct { init }
57 wrapped_local,
58 /// var name = init.*
59 mut_str,
60 func,
61 warning,
62 @"struct",
63 @"union",
64 @"opaque",
65 @"comptime",
66 @"defer",
67 array_init,
68 tuple,
69 container_init,
70 container_init_dot,
71 /// _ = operand;
72 discard,
73
74 // a + b
75 add,
76 // a = b
77 add_assign,
78 // c = (a = b)
79 add_wrap,
80 add_wrap_assign,
81 sub,
82 sub_assign,
83 sub_wrap,
84 sub_wrap_assign,
85 mul,
86 mul_assign,
87 mul_wrap,
88 mul_wrap_assign,
89 div,
90 div_assign,
91 shl,
92 shl_assign,
93 shr,
94 shr_assign,
95 mod,
96 mod_assign,
97 @"and",
98 @"or",
99 less_than,
100 less_than_equal,
101 greater_than,
102 greater_than_equal,
103 equal,
104 not_equal,
105 bit_and,
106 bit_and_assign,
107 bit_or,
108 bit_or_assign,
109 bit_xor,
110 bit_xor_assign,
111 array_cat,
112 ellipsis3,
113 assign,
114
115 /// @intCast(operand)
116 int_cast,
117 /// @constCast(operand)
118 const_cast,
119 /// @volatileCast(operand)
120 volatile_cast,
121 /// @divTrunc(lhs, rhs)
122 div_trunc,
123 /// @intFromBool(operand)
124 int_from_bool,
125 /// @as(lhs, rhs)
126 as,
127 /// @truncate(operand)
128 truncate,
129 /// @bitCast(operand)
130 bit_cast,
131 /// @floatCast(operand)
132 float_cast,
133 /// @intFromFloat(operand)
134 int_from_float,
135 /// @floatFromInt(operand)
136 float_from_int,
137 /// @ptrFromInt(operand)
138 ptr_from_int,
139 /// @intFromPtr(operand)
140 int_from_ptr,
141 /// @alignCast(operand)
142 align_cast,
143 /// @ptrCast(operand)
144 ptr_cast,
145 /// @divExact(lhs, rhs)
146 div_exact,
147 /// @offsetOf(lhs, rhs)
148 offset_of,
149 /// @splat(operand)
150 vector_zero_init,
151 /// @shuffle(type, a, b, mask)
152 shuffle,
153 /// @extern(ty, .{ .name = n })
154 builtin_extern,
155
156 /// @byteSwap(operand)
157 byte_swap,
158 /// @ceil(operand)
159 ceil,
160 /// @cos(operand)
161 cos,
162 /// @sin(operand)
163 sin,
164 /// @exp(operand)
165 exp,
166 /// @exp2(operand)
167 exp2,
168 /// @exp10(operand)
169 exp10,
170 /// @abs(operand)
171 abs,
172 /// @log(operand)
173 log,
174 /// @log2(operand)
175 log2,
176 /// @log10(operand)
177 log10,
178 /// @round(operand)
179 round,
180 /// @sqrt(operand)
181 sqrt,
182 /// @trunc(operand)
183 trunc,
184 /// @floor(operand)
185 floor,
186
187 /// __helpers.<name>(argshelper_call)
188 helper_call,
189 /// __helpers.<name>
190 helper_ref,
191
192 asm_simple,
193
194 negate,
195 negate_wrap,
196 bit_not,
197 not,
198 address_of,
199 /// .?
200 unwrap,
201 /// .*
202 deref,
203
204 block,
205 /// { operand }
206 block_single,
207
208 sizeof,
209 alignof,
210 typeof,
211 typeinfo,
212 type,
213
214 optional_type,
215 c_pointer,
216 single_pointer,
217 array_type,
218 null_sentinel_array_type,
219
220 /// @Vector(lhs, rhs)
221 vector,
222 /// @import("std").mem.zeroes(operand)
223 std_mem_zeroes,
224 /// @import("std").mem.zeroInit(lhs, rhs)
225 std_mem_zeroinit,
226 // pub const name = @compileError(msg);
227 fail_decl,
228 // var actual = mangled;
229 arg_redecl,
230 /// pub const alias = actual;
231 alias,
232 /// const name = init;
233 var_simple,
234 /// pub const name = init;
235 pub_var_simple,
236 /// pub? const name (: type)? = value
237 enum_constant,
238
239 /// pub inline fn name(params) return_type body
240 pub_inline_fn,
241
242 /// array_type{}
243 empty_array,
244 /// [1]type{val} ** count
245 array_filler,
246
247 /// comptime { if (!(lhs)) @compileError(rhs); }
248 static_assert,
249
250 pub const last_no_payload_tag = Tag.@"break";
251 pub const no_payload_count = @intFromEnum(last_no_payload_tag) + 1;
252
253 pub fn Type(comptime t: Tag) type {
254 return switch (t) {
255 .declaration,
256 .null_literal,
257 .undefined_literal,
258 .opaque_literal,
259 .true_literal,
260 .false_literal,
261 .empty_block,
262 .return_void,
263 .zero_literal,
264 .one_literal,
265 .void_type,
266 .noreturn_type,
267 .@"anytype",
268 .@"continue",
269 .@"break",
270 .@"unreachable",
271 => @compileError("Type Tag " ++ @tagName(t) ++ " has no payload"),
272
273 .std_mem_zeroes,
274 .@"return",
275 .@"comptime",
276 .@"defer",
277 .asm_simple,
278 .negate,
279 .negate_wrap,
280 .bit_not,
281 .not,
282 .optional_type,
283 .address_of,
284 .unwrap,
285 .deref,
286 .int_from_ptr,
287 .empty_array,
288 .while_true,
289 .if_not_break,
290 .switch_else,
291 .block_single,
292 .int_from_bool,
293 .sizeof,
294 .alignof,
295 .typeof,
296 .typeinfo,
297 .align_cast,
298 .truncate,
299 .bit_cast,
300 .float_cast,
301 .int_from_float,
302 .float_from_int,
303 .ptr_from_int,
304 .ptr_cast,
305 .int_cast,
306 .const_cast,
307 .volatile_cast,
308 .vector_zero_init,
309 .byte_swap,
310 .ceil,
311 .cos,
312 .sin,
313 .exp,
314 .exp2,
315 .exp10,
316 .abs,
317 .log,
318 .log2,
319 .log10,
320 .round,
321 .sqrt,
322 .trunc,
323 .floor,
324 => Payload.UnOp,
325
326 .add,
327 .add_assign,
328 .add_wrap,
329 .add_wrap_assign,
330 .sub,
331 .sub_assign,
332 .sub_wrap,
333 .sub_wrap_assign,
334 .mul,
335 .mul_assign,
336 .mul_wrap,
337 .mul_wrap_assign,
338 .div,
339 .div_assign,
340 .shl,
341 .shl_assign,
342 .shr,
343 .shr_assign,
344 .mod,
345 .mod_assign,
346 .@"and",
347 .@"or",
348 .less_than,
349 .less_than_equal,
350 .greater_than,
351 .greater_than_equal,
352 .equal,
353 .not_equal,
354 .bit_and,
355 .bit_and_assign,
356 .bit_or,
357 .bit_or_assign,
358 .bit_xor,
359 .bit_xor_assign,
360 .div_trunc,
361 .as,
362 .array_cat,
363 .ellipsis3,
364 .assign,
365 .array_access,
366 .std_mem_zeroinit,
367 .vector,
368 .div_exact,
369 .offset_of,
370 .static_assert,
371 => Payload.BinOp,
372
373 .integer_literal,
374 .float_literal,
375 .string_literal,
376 .char_literal,
377 .enum_literal,
378 .identifier,
379 .warning,
380 .type,
381 => Payload.Value,
382 .discard => Payload.Discard,
383 .@"if" => Payload.If,
384 .@"while" => Payload.While,
385 .@"switch", .array_init, .switch_prong => Payload.Switch,
386 .break_val => Payload.BreakVal,
387 .call => Payload.Call,
388 .var_decl => Payload.VarDecl,
389 .func => Payload.Func,
390 .@"struct", .@"union", .@"opaque" => Payload.Container,
391 .tuple => Payload.TupleInit,
392 .container_init => Payload.ContainerInit,
393 .container_init_dot => Payload.ContainerInitDot,
394 .block => Payload.Block,
395 .c_pointer, .single_pointer => Payload.Pointer,
396 .array_type, .null_sentinel_array_type => Payload.Array,
397 .arg_redecl, .alias, .fail_decl => Payload.ArgRedecl,
398 .var_simple, .pub_var_simple, .wrapped_local, .mut_str => Payload.SimpleVarDecl,
399 .enum_constant => Payload.EnumConstant,
400 .array_filler => Payload.ArrayFiller,
401 .pub_inline_fn => Payload.PubInlineFn,
402 .field_access => Payload.FieldAccess,
403 .string_slice => Payload.StringSlice,
404 .shuffle => Payload.Shuffle,
405 .builtin_extern => Payload.Extern,
406 .helper_call => Payload.HelperCall,
407 .helper_ref => Payload.HelperRef,
408 };
409 }
410
411 pub fn init(comptime t: Tag) Node {
412 comptime std.debug.assert(@intFromEnum(t) < Tag.no_payload_count);
413 return .{ .tag_if_small_enough = @intFromEnum(t) };
414 }
415
416 pub fn create(comptime t: Tag, ally: Allocator, data: Data(t)) error{OutOfMemory}!Node {
417 const ptr = try ally.create(t.Type());
418 ptr.* = .{
419 .base = .{ .tag = t },
420 .data = data,
421 };
422 return Node{ .ptr_otherwise = &ptr.base };
423 }
424
425 pub fn Data(comptime t: Tag) type {
426 return std.meta.fieldInfo(t.Type(), .data).type;
427 }
428 };
429
430 pub fn tag(self: Node) Tag {
431 if (self.tag_if_small_enough < Tag.no_payload_count) {
432 return @enumFromInt(@as(std.meta.Tag(Tag), @intCast(self.tag_if_small_enough)));
433 } else {
434 return self.ptr_otherwise.tag;
435 }
436 }
437
438 pub fn castTag(self: Node, comptime t: Tag) ?*t.Type() {
439 if (self.tag_if_small_enough < Tag.no_payload_count)
440 return null;
441
442 if (self.ptr_otherwise.tag == t)
443 return @alignCast(@fieldParentPtr("base", self.ptr_otherwise));
444
445 return null;
446 }
447
448 pub fn initPayload(payload: *Payload) Node {
449 std.debug.assert(@intFromEnum(payload.tag) >= Tag.no_payload_count);
450 return .{ .ptr_otherwise = payload };
451 }
452
453 pub fn isNoreturn(node: Node, break_counts: bool) bool {
454 switch (node.tag()) {
455 .block => {
456 const block_node = node.castTag(.block).?;
457 if (block_node.data.stmts.len == 0) return false;
458
459 const last = block_node.data.stmts[block_node.data.stmts.len - 1];
460 return last.isNoreturn(break_counts);
461 },
462 .@"switch" => {
463 const switch_node = node.castTag(.@"switch").?;
464
465 for (switch_node.data.cases) |case| {
466 const body = if (case.castTag(.switch_else)) |some|
467 some.data
468 else if (case.castTag(.switch_prong)) |some|
469 some.data.cond
470 else
471 unreachable;
472
473 if (!body.isNoreturn(break_counts)) return false;
474 }
475 return true;
476 },
477 .@"return", .return_void => return true,
478 .@"break" => if (break_counts) return true,
479 else => {},
480 }
481 return false;
482 }
483
484 pub fn isBoolRes(res: Node) bool {
485 switch (res.tag()) {
486 .@"or",
487 .@"and",
488 .equal,
489 .not_equal,
490 .less_than,
491 .less_than_equal,
492 .greater_than,
493 .greater_than_equal,
494 .not,
495 .false_literal,
496 .true_literal,
497 => return true,
498 else => return false,
499 }
500 }
501};
502
503pub const Payload = struct {
504 tag: Node.Tag,
505
506 pub const Value = struct {
507 base: Payload,
508 data: []const u8,
509 };
510
511 pub const UnOp = struct {
512 base: Payload,
513 data: Node,
514 };
515
516 pub const BinOp = struct {
517 base: Payload,
518 data: struct {
519 lhs: Node,
520 rhs: Node,
521 },
522 };
523
524 pub const Discard = struct {
525 base: Payload,
526 data: struct {
527 should_skip: bool,
528 value: Node,
529 },
530 };
531
532 pub const If = struct {
533 base: Payload,
534 data: struct {
535 cond: Node,
536 then: Node,
537 @"else": ?Node,
538 },
539 };
540
541 pub const While = struct {
542 base: Payload,
543 data: struct {
544 cond: Node,
545 body: Node,
546 cont_expr: ?Node,
547 },
548 };
549
550 pub const Switch = struct {
551 base: Payload,
552 data: struct {
553 cond: Node,
554 cases: []Node,
555 },
556 };
557
558 pub const BreakVal = struct {
559 base: Payload,
560 data: struct {
561 label: ?[]const u8,
562 val: Node,
563 },
564 };
565
566 pub const Call = struct {
567 base: Payload,
568 data: struct {
569 lhs: Node,
570 args: []Node,
571 },
572 };
573
574 pub const VarDecl = struct {
575 base: Payload,
576 data: struct {
577 is_pub: bool,
578 is_const: bool,
579 is_extern: bool,
580 is_export: bool,
581 is_threadlocal: bool,
582 alignment: ?c_uint,
583 linksection_string: ?[]const u8,
584 name: []const u8,
585 type: Node,
586 init: ?Node,
587 },
588 };
589
590 pub const Func = struct {
591 base: Payload,
592 data: struct {
593 is_pub: bool,
594 is_extern: bool,
595 is_export: bool,
596 is_inline: bool,
597 is_var_args: bool,
598 name: ?[]const u8,
599 linksection_string: ?[]const u8,
600 explicit_callconv: ?CallingConvention,
601 params: []Param,
602 return_type: Node,
603 body: ?Node,
604 alignment: ?c_uint,
605 },
606
607 pub const CallingConvention = enum {
608 c,
609 x86_64_sysv,
610 x86_64_win,
611 x86_stdcall,
612 x86_fastcall,
613 x86_thiscall,
614 x86_vectorcall,
615 x86_regcall,
616 aarch64_vfabi,
617 aarch64_sve_pcs,
618 arm_aapcs,
619 arm_aapcs_vfp,
620 m68k_rtd,
621 riscv_vector,
622 };
623 };
624
625 pub const Param = struct {
626 is_noalias: bool,
627 name: ?[]const u8,
628 type: Node,
629 };
630
631 pub const Container = struct {
632 base: Payload,
633 data: struct {
634 layout: enum { @"packed", @"extern", none },
635 fields: []Field,
636 decls: []Node,
637 },
638
639 pub const Field = struct {
640 name: []const u8,
641 type: Node,
642 alignment: ?c_uint,
643 default_value: ?Node,
644 };
645 };
646
647 pub const TupleInit = struct {
648 base: Payload,
649 data: []Node,
650 };
651
652 pub const ContainerInit = struct {
653 base: Payload,
654 data: struct {
655 lhs: Node,
656 inits: []Initializer,
657 },
658
659 pub const Initializer = struct {
660 name: []const u8,
661 value: Node,
662 };
663 };
664
665 pub const ContainerInitDot = struct {
666 base: Payload,
667 data: []Initializer,
668
669 pub const Initializer = struct {
670 name: []const u8,
671 value: Node,
672 };
673 };
674
675 pub const Block = struct {
676 base: Payload,
677 data: struct {
678 label: ?[]const u8,
679 stmts: []Node,
680 },
681 };
682
683 pub const Array = struct {
684 base: Payload,
685 data: ArrayTypeInfo,
686
687 pub const ArrayTypeInfo = struct {
688 elem_type: Node,
689 len: u64,
690 };
691 };
692
693 pub const Pointer = struct {
694 base: Payload,
695 data: struct {
696 elem_type: Node,
697 is_const: bool,
698 is_volatile: bool,
699 is_allowzero: bool,
700 },
701 };
702
703 pub const ArgRedecl = struct {
704 base: Payload,
705 data: struct {
706 actual: []const u8,
707 mangled: []const u8,
708 },
709 };
710
711 pub const SimpleVarDecl = struct {
712 base: Payload,
713 data: struct {
714 name: []const u8,
715 init: Node,
716 },
717 };
718
719 pub const EnumConstant = struct {
720 base: Payload,
721 data: struct {
722 name: []const u8,
723 is_public: bool,
724 type: ?Node,
725 value: Node,
726 },
727 };
728
729 pub const ArrayFiller = struct {
730 base: Payload,
731 data: struct {
732 type: Node,
733 filler: Node,
734 count: u64,
735 },
736 };
737
738 pub const PubInlineFn = struct {
739 base: Payload,
740 data: struct {
741 name: []const u8,
742 params: []Param,
743 return_type: Node,
744 body: Node,
745 },
746 };
747
748 pub const FieldAccess = struct {
749 base: Payload,
750 data: struct {
751 lhs: Node,
752 field_name: []const u8,
753 },
754 };
755
756 pub const StringSlice = struct {
757 base: Payload,
758 data: struct {
759 string: Node,
760 end: u64,
761 },
762 };
763
764 pub const Shuffle = struct {
765 base: Payload,
766 data: struct {
767 element_type: Node,
768 a: Node,
769 b: Node,
770 mask_vector: Node,
771 },
772 };
773
774 pub const Extern = struct {
775 base: Payload,
776 data: struct {
777 type: Node,
778 name: Node,
779 },
780 };
781
782 pub const HelperCall = struct {
783 base: Payload,
784 data: struct {
785 name: []const u8,
786 args: []const Node,
787 },
788 };
789
790 pub const HelperRef = struct {
791 base: Payload,
792 data: []const u8,
793 };
794};
795
796/// Converts the nodes into a Zig Ast.
797/// Caller must free the source slice.
798pub fn render(gpa: Allocator, nodes: []const Node) !std.zig.Ast {
799 var ctx: Context = .{
800 .gpa = gpa,
801 .buf = std.array_list.Managed(u8).init(gpa),
802 };
803 defer ctx.buf.deinit();
804 defer ctx.nodes.deinit(gpa);
805 defer ctx.extra_data.deinit(gpa);
806 defer ctx.tokens.deinit(gpa);
807
808 // Estimate that each top level node has 10 child nodes.
809 const estimated_node_count = nodes.len * 10 + 1; // +1 for the .root node
810 try ctx.nodes.ensureTotalCapacity(gpa, estimated_node_count);
811 // Estimate that each each node has 2 tokens.
812 const estimated_tokens_count = estimated_node_count * 2;
813 try ctx.tokens.ensureTotalCapacity(gpa, estimated_tokens_count);
814 // Estimate that each each token is 3 bytes long.
815 const estimated_buf_len = estimated_tokens_count * 3;
816 try ctx.buf.ensureTotalCapacity(estimated_buf_len);
817
818 ctx.nodes.appendAssumeCapacity(.{
819 .tag = .root,
820 .main_token = 0,
821 .data = undefined,
822 });
823
824 const root_members = blk: {
825 var result = std.array_list.Managed(NodeIndex).init(gpa);
826 defer result.deinit();
827
828 for (nodes) |node| {
829 const res = (try renderNodeOpt(&ctx, node)) orelse continue;
830 try result.append(res);
831 }
832 break :blk try ctx.listToSpan(result.items);
833 };
834
835 ctx.nodes.items(.data)[0] = .{ .extra_range = .{
836 .start = root_members.start,
837 .end = root_members.end,
838 } };
839
840 try ctx.tokens.append(gpa, .{
841 .tag = .eof,
842 .start = @as(u32, @intCast(ctx.buf.items.len)),
843 });
844
845 return .{
846 .source = try ctx.buf.toOwnedSliceSentinel(0),
847 .tokens = ctx.tokens.toOwnedSlice(),
848 .nodes = ctx.nodes.toOwnedSlice(),
849 .extra_data = try ctx.extra_data.toOwnedSlice(gpa),
850 .errors = &.{},
851 .mode = .zig,
852 };
853}
854
855const NodeIndex = std.zig.Ast.Node.Index;
856const NodeSubRange = std.zig.Ast.Node.SubRange;
857const TokenIndex = std.zig.Ast.TokenIndex;
858const TokenTag = std.zig.Token.Tag;
859
860const Context = struct {
861 gpa: Allocator,
862 buf: std.array_list.Managed(u8),
863 nodes: std.zig.Ast.NodeList = .{},
864 extra_data: std.ArrayListUnmanaged(u32) = .empty,
865 tokens: std.zig.Ast.TokenList = .{},
866
867 fn addTokenFmt(c: *Context, tag: TokenTag, comptime format: []const u8, args: anytype) Allocator.Error!TokenIndex {
868 const start_index = c.buf.items.len;
869 try c.buf.print(format ++ " ", args);
870
871 try c.tokens.append(c.gpa, .{
872 .tag = tag,
873 .start = @intCast(start_index),
874 });
875
876 return @intCast(c.tokens.len - 1);
877 }
878
879 fn addToken(c: *Context, tag: TokenTag, bytes: []const u8) Allocator.Error!TokenIndex {
880 return c.addTokenFmt(tag, "{s}", .{bytes});
881 }
882
883 fn addIdentifier(c: *Context, bytes: []const u8) Allocator.Error!TokenIndex {
884 if (std.zig.primitives.isPrimitive(bytes))
885 return c.addTokenFmt(.identifier, "@\"{s}\"", .{bytes});
886 return c.addTokenFmt(.identifier, "{f}", .{std.zig.fmtId(bytes)});
887 }
888
889 fn listToSpan(c: *Context, list: []const NodeIndex) Allocator.Error!NodeSubRange {
890 try c.extra_data.appendSlice(c.gpa, @ptrCast(list));
891 return .{
892 .start = @enumFromInt(c.extra_data.items.len - list.len),
893 .end = @enumFromInt(c.extra_data.items.len),
894 };
895 }
896
897 fn addNode(c: *Context, elem: std.zig.Ast.Node) Allocator.Error!NodeIndex {
898 const result: NodeIndex = @enumFromInt(c.nodes.len);
899 try c.nodes.append(c.gpa, elem);
900 return result;
901 }
902
903 fn addExtra(c: *Context, extra: anytype) Allocator.Error!std.zig.Ast.ExtraIndex {
904 const fields = std.meta.fields(@TypeOf(extra));
905 try c.extra_data.ensureUnusedCapacity(c.gpa, fields.len);
906 const result: std.zig.Ast.ExtraIndex = @enumFromInt(c.extra_data.items.len);
907 inline for (fields) |field| {
908 const data: u32 = switch (field.type) {
909 NodeIndex,
910 std.zig.Ast.Node.OptionalIndex,
911 std.zig.Ast.OptionalTokenIndex,
912 std.zig.Ast.ExtraIndex,
913 => @intFromEnum(@field(extra, field.name)),
914 TokenIndex,
915 => @field(extra, field.name),
916 else => @compileError("unexpected field type"),
917 };
918 c.extra_data.appendAssumeCapacity(data);
919 }
920 return result;
921 }
922};
923
924fn renderNodeOpt(c: *Context, node: Node) Allocator.Error!?NodeIndex {
925 switch (node.tag()) {
926 .warning => {
927 const payload = node.castTag(.warning).?.data;
928 try c.buf.appendSlice(payload);
929 try c.buf.append('\n');
930 return null;
931 },
932 .discard => {
933 const payload = node.castTag(.discard).?.data;
934 if (payload.should_skip) return null;
935
936 return try renderNode(c, node);
937 },
938 else => return try renderNode(c, node),
939 }
940}
941
942fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
943 switch (node.tag()) {
944 .declaration => unreachable,
945 .warning => unreachable,
946 .discard => {
947 const payload = node.castTag(.discard).?.data;
948 std.debug.assert(!payload.should_skip);
949
950 const lhs = try c.addNode(.{
951 .tag = .identifier,
952 .main_token = try c.addToken(.identifier, "_"),
953 .data = undefined,
954 });
955 const main_token = try c.addToken(.equal, "=");
956 if (payload.value.tag() == .identifier) {
957 // Render as `_ = &foo;` to avoid tripping "pointless discard" and "local variable never mutated" errors.
958 var addr_of_pl: Payload.UnOp = .{
959 .base = .{ .tag = .address_of },
960 .data = payload.value,
961 };
962 const addr_of: Node = .{ .ptr_otherwise = &addr_of_pl.base };
963 return try c.addNode(.{
964 .tag = .assign,
965 .main_token = main_token,
966 .data = .{ .node_and_node = .{
967 lhs, try renderNode(c, addr_of),
968 } },
969 });
970 } else {
971 return try c.addNode(.{
972 .tag = .assign,
973 .main_token = main_token,
974 .data = .{ .node_and_node = .{
975 lhs, try renderNode(c, payload.value),
976 } },
977 });
978 }
979 },
980 .std_mem_zeroes => {
981 const payload = node.castTag(.std_mem_zeroes).?.data;
982 const import_node = try renderStdImport(c, &.{ "mem", "zeroes" });
983 return renderCall(c, import_node, &.{payload});
984 },
985 .std_mem_zeroinit => {
986 const payload = node.castTag(.std_mem_zeroinit).?.data;
987 const import_node = try renderStdImport(c, &.{ "mem", "zeroInit" });
988 return renderCall(c, import_node, &.{ payload.lhs, payload.rhs });
989 },
990 .vector => {
991 const payload = node.castTag(.vector).?.data;
992 return renderBuiltinCall(c, "@Vector", &.{ payload.lhs, payload.rhs });
993 },
994 .call => {
995 const payload = node.castTag(.call).?.data;
996 const lhs = try renderNodeGrouped(c, payload.lhs);
997 return renderCall(c, lhs, payload.args);
998 },
999 .null_literal => return c.addNode(.{
1000 .tag = .identifier,
1001 .main_token = try c.addToken(.identifier, "null"),
1002 .data = undefined,
1003 }),
1004 .undefined_literal => return c.addNode(.{
1005 .tag = .identifier,
1006 .main_token = try c.addToken(.identifier, "undefined"),
1007 .data = undefined,
1008 }),
1009 .true_literal => return c.addNode(.{
1010 .tag = .identifier,
1011 .main_token = try c.addToken(.identifier, "true"),
1012 .data = undefined,
1013 }),
1014 .false_literal => return c.addNode(.{
1015 .tag = .identifier,
1016 .main_token = try c.addToken(.identifier, "false"),
1017 .data = undefined,
1018 }),
1019 .zero_literal => return c.addNode(.{
1020 .tag = .number_literal,
1021 .main_token = try c.addToken(.number_literal, "0"),
1022 .data = undefined,
1023 }),
1024 .one_literal => return c.addNode(.{
1025 .tag = .number_literal,
1026 .main_token = try c.addToken(.number_literal, "1"),
1027 .data = undefined,
1028 }),
1029 .@"unreachable" => return c.addNode(.{
1030 .tag = .unreachable_literal,
1031 .main_token = try c.addToken(.keyword_unreachable, "unreachable"),
1032 .data = undefined,
1033 }),
1034 .void_type => return c.addNode(.{
1035 .tag = .identifier,
1036 .main_token = try c.addToken(.identifier, "void"),
1037 .data = undefined,
1038 }),
1039 .noreturn_type => return c.addNode(.{
1040 .tag = .identifier,
1041 .main_token = try c.addToken(.identifier, "noreturn"),
1042 .data = undefined,
1043 }),
1044 .@"continue" => return c.addNode(.{
1045 .tag = .@"continue",
1046 .main_token = try c.addToken(.keyword_continue, "continue"),
1047 .data = .{ .opt_token_and_opt_node = .{
1048 .none, .none,
1049 } },
1050 }),
1051 .return_void => return c.addNode(.{
1052 .tag = .@"return",
1053 .main_token = try c.addToken(.keyword_return, "return"),
1054 .data = .{ .opt_node = .none },
1055 }),
1056 .@"break" => return c.addNode(.{
1057 .tag = .@"break",
1058 .main_token = try c.addToken(.keyword_break, "break"),
1059 .data = .{ .opt_token_and_opt_node = .{
1060 .none, .none,
1061 } },
1062 }),
1063 .break_val => {
1064 const payload = node.castTag(.break_val).?.data;
1065 const tok = try c.addToken(.keyword_break, "break");
1066 const break_label = if (payload.label) |some| blk: {
1067 _ = try c.addToken(.colon, ":");
1068 break :blk try c.addIdentifier(some);
1069 } else 0;
1070 return c.addNode(.{
1071 .tag = .@"break",
1072 .main_token = tok,
1073 .data = .{ .opt_token_and_opt_node = .{
1074 .fromToken(break_label), (try renderNode(c, payload.val)).toOptional(),
1075 } },
1076 });
1077 },
1078 .@"return" => {
1079 const payload = node.castTag(.@"return").?.data;
1080 return c.addNode(.{
1081 .tag = .@"return",
1082 .main_token = try c.addToken(.keyword_return, "return"),
1083 .data = .{ .opt_node = (try renderNode(c, payload)).toOptional() },
1084 });
1085 },
1086 .@"comptime" => {
1087 const payload = node.castTag(.@"comptime").?.data;
1088 return c.addNode(.{
1089 .tag = .@"comptime",
1090 .main_token = try c.addToken(.keyword_comptime, "comptime"),
1091 .data = .{
1092 .node = try renderNode(c, payload),
1093 },
1094 });
1095 },
1096 .@"defer" => {
1097 const payload = node.castTag(.@"defer").?.data;
1098 return c.addNode(.{
1099 .tag = .@"defer",
1100 .main_token = try c.addToken(.keyword_defer, "defer"),
1101 .data = .{
1102 .node = try renderNode(c, payload),
1103 },
1104 });
1105 },
1106 .asm_simple => {
1107 const payload = node.castTag(.asm_simple).?.data;
1108 const asm_token = try c.addToken(.keyword_asm, "asm");
1109 _ = try c.addToken(.l_paren, "(");
1110 return c.addNode(.{
1111 .tag = .asm_simple,
1112 .main_token = asm_token,
1113 .data = .{ .node_and_token = .{
1114 try renderNode(c, payload),
1115 try c.addToken(.r_paren, ")"),
1116 } },
1117 });
1118 },
1119 .type => {
1120 const payload = node.castTag(.type).?.data;
1121 return c.addNode(.{
1122 .tag = .identifier,
1123 .main_token = try c.addToken(.identifier, payload),
1124 .data = undefined,
1125 });
1126 },
1127 .identifier => {
1128 const payload = node.castTag(.identifier).?.data;
1129 return c.addNode(.{
1130 .tag = .identifier,
1131 .main_token = try c.addIdentifier(payload),
1132 .data = undefined,
1133 });
1134 },
1135 .float_literal => {
1136 const payload = node.castTag(.float_literal).?.data;
1137 return c.addNode(.{
1138 .tag = .number_literal,
1139 .main_token = try c.addToken(.number_literal, payload),
1140 .data = undefined,
1141 });
1142 },
1143 .integer_literal => {
1144 const payload = node.castTag(.integer_literal).?.data;
1145 return c.addNode(.{
1146 .tag = .number_literal,
1147 .main_token = try c.addToken(.number_literal, payload),
1148 .data = undefined,
1149 });
1150 },
1151 .string_literal => {
1152 const payload = node.castTag(.string_literal).?.data;
1153 return c.addNode(.{
1154 .tag = .string_literal,
1155 .main_token = try c.addToken(.string_literal, payload),
1156 .data = undefined,
1157 });
1158 },
1159 .char_literal => {
1160 const payload = node.castTag(.char_literal).?.data;
1161 return c.addNode(.{
1162 .tag = .char_literal,
1163 .main_token = try c.addToken(.char_literal, payload),
1164 .data = undefined,
1165 });
1166 },
1167 .enum_literal => {
1168 const payload = node.castTag(.enum_literal).?.data;
1169 _ = try c.addToken(.period, ".");
1170 return c.addNode(.{
1171 .tag = .enum_literal,
1172 .main_token = try c.addToken(.identifier, payload),
1173 .data = undefined,
1174 });
1175 },
1176 .string_slice => {
1177 const payload = node.castTag(.string_slice).?.data;
1178
1179 const string = try renderNode(c, payload.string);
1180 const l_bracket = try c.addToken(.l_bracket, "[");
1181 const start = try c.addNode(.{
1182 .tag = .number_literal,
1183 .main_token = try c.addToken(.number_literal, "0"),
1184 .data = undefined,
1185 });
1186 _ = try c.addToken(.ellipsis2, "..");
1187 const end = try c.addNode(.{
1188 .tag = .number_literal,
1189 .main_token = try c.addTokenFmt(.number_literal, "{d}", .{payload.end}),
1190 .data = undefined,
1191 });
1192 _ = try c.addToken(.r_bracket, "]");
1193
1194 return c.addNode(.{
1195 .tag = .slice,
1196 .main_token = l_bracket,
1197 .data = .{ .node_and_extra = .{
1198 string, try c.addExtra(std.zig.Ast.Node.Slice{
1199 .start = start,
1200 .end = end,
1201 }),
1202 } },
1203 });
1204 },
1205 .fail_decl => {
1206 const payload = node.castTag(.fail_decl).?.data;
1207 // pub const name = @compileError(msg);
1208 _ = try c.addToken(.keyword_pub, "pub");
1209 const const_tok = try c.addToken(.keyword_const, "const");
1210 _ = try c.addIdentifier(payload.actual);
1211 _ = try c.addToken(.equal, "=");
1212
1213 const compile_error_tok = try c.addToken(.builtin, "@compileError");
1214 _ = try c.addToken(.l_paren, "(");
1215 const err_msg_tok = try c.addTokenFmt(.string_literal, "\"{f}\"", .{std.zig.fmtString(payload.mangled)});
1216 const err_msg = try c.addNode(.{
1217 .tag = .string_literal,
1218 .main_token = err_msg_tok,
1219 .data = undefined,
1220 });
1221 _ = try c.addToken(.r_paren, ")");
1222 const compile_error = try c.addNode(.{
1223 .tag = .builtin_call_two,
1224 .main_token = compile_error_tok,
1225 .data = .{ .opt_node_and_opt_node = .{
1226 err_msg.toOptional(), .none,
1227 } },
1228 });
1229 _ = try c.addToken(.semicolon, ";");
1230
1231 return c.addNode(.{
1232 .tag = .simple_var_decl,
1233 .main_token = const_tok,
1234 .data = .{
1235 .opt_node_and_opt_node = .{
1236 .none, // Type expression
1237 compile_error.toOptional(), // Init expression
1238 },
1239 },
1240 });
1241 },
1242 .pub_var_simple, .var_simple => {
1243 const payload = @as(*Payload.SimpleVarDecl, @alignCast(@fieldParentPtr("base", node.ptr_otherwise))).data;
1244 if (node.tag() == .pub_var_simple) _ = try c.addToken(.keyword_pub, "pub");
1245 const const_tok = try c.addToken(.keyword_const, "const");
1246 _ = try c.addIdentifier(payload.name);
1247 _ = try c.addToken(.equal, "=");
1248
1249 const init = try renderNode(c, payload.init);
1250 _ = try c.addToken(.semicolon, ";");
1251
1252 return c.addNode(.{
1253 .tag = .simple_var_decl,
1254 .main_token = const_tok,
1255 .data = .{
1256 .opt_node_and_opt_node = .{
1257 .none, // Type expression
1258 init.toOptional(), // Init expression
1259 },
1260 },
1261 });
1262 },
1263 .wrapped_local => {
1264 const payload = node.castTag(.wrapped_local).?.data;
1265
1266 const const_tok = try c.addToken(.keyword_const, "const");
1267 _ = try c.addIdentifier(payload.name);
1268 _ = try c.addToken(.equal, "=");
1269
1270 const kind_tok = try c.addToken(.keyword_struct, "struct");
1271 _ = try c.addToken(.l_brace, "{");
1272
1273 const container_def = try c.addNode(.{
1274 .tag = .container_decl_two_trailing,
1275 .main_token = kind_tok,
1276 .data = .{ .opt_node_and_opt_node = .{
1277 (try renderNode(c, payload.init)).toOptional(), .none,
1278 } },
1279 });
1280 _ = try c.addToken(.r_brace, "}");
1281 _ = try c.addToken(.semicolon, ";");
1282
1283 return c.addNode(.{
1284 .tag = .simple_var_decl,
1285 .main_token = const_tok,
1286 .data = .{
1287 .opt_node_and_opt_node = .{
1288 .none, // Type expression
1289 container_def.toOptional(), // Init expression
1290 },
1291 },
1292 });
1293 },
1294 .mut_str => {
1295 const payload = node.castTag(.mut_str).?.data;
1296
1297 const var_tok = try c.addToken(.keyword_var, "var");
1298 _ = try c.addIdentifier(payload.name);
1299 _ = try c.addToken(.equal, "=");
1300
1301 const deref = try c.addNode(.{
1302 .tag = .deref,
1303 .data = .{
1304 .node = try renderNodeGrouped(c, payload.init),
1305 },
1306 .main_token = try c.addToken(.period_asterisk, ".*"),
1307 });
1308 _ = try c.addToken(.semicolon, ";");
1309
1310 return c.addNode(.{
1311 .tag = .simple_var_decl,
1312 .main_token = var_tok,
1313 .data = .{
1314 .opt_node_and_opt_node = .{
1315 .none, // Type expression
1316 deref.toOptional(), // Init expression
1317 },
1318 },
1319 });
1320 },
1321 .var_decl => return renderVar(c, node),
1322 .arg_redecl, .alias => {
1323 const payload = @as(*Payload.ArgRedecl, @alignCast(@fieldParentPtr("base", node.ptr_otherwise))).data;
1324 if (node.tag() == .alias) _ = try c.addToken(.keyword_pub, "pub");
1325 const mut_tok = if (node.tag() == .alias)
1326 try c.addToken(.keyword_const, "const")
1327 else
1328 try c.addToken(.keyword_var, "var");
1329 _ = try c.addIdentifier(payload.actual);
1330 _ = try c.addToken(.equal, "=");
1331
1332 const init = try c.addNode(.{
1333 .tag = .identifier,
1334 .main_token = try c.addIdentifier(payload.mangled),
1335 .data = undefined,
1336 });
1337 _ = try c.addToken(.semicolon, ";");
1338
1339 return c.addNode(.{
1340 .tag = .simple_var_decl,
1341 .main_token = mut_tok,
1342 .data = .{
1343 .opt_node_and_opt_node = .{
1344 .none, // Type expression
1345 init.toOptional(), // Init expression
1346 },
1347 },
1348 });
1349 },
1350 .int_cast => {
1351 const payload = node.castTag(.int_cast).?.data;
1352 return renderBuiltinCall(c, "@intCast", &.{payload});
1353 },
1354 .const_cast => {
1355 const payload = node.castTag(.const_cast).?.data;
1356 return renderBuiltinCall(c, "@constCast", &.{payload});
1357 },
1358 .volatile_cast => {
1359 const payload = node.castTag(.volatile_cast).?.data;
1360 return renderBuiltinCall(c, "@volatileCast", &.{payload});
1361 },
1362 .div_trunc => {
1363 const payload = node.castTag(.div_trunc).?.data;
1364 return renderBuiltinCall(c, "@divTrunc", &.{ payload.lhs, payload.rhs });
1365 },
1366 .int_from_bool => {
1367 const payload = node.castTag(.int_from_bool).?.data;
1368 return renderBuiltinCall(c, "@intFromBool", &.{payload});
1369 },
1370 .as => {
1371 const payload = node.castTag(.as).?.data;
1372 return renderBuiltinCall(c, "@as", &.{ payload.lhs, payload.rhs });
1373 },
1374 .truncate => {
1375 const payload = node.castTag(.truncate).?.data;
1376 return renderBuiltinCall(c, "@truncate", &.{payload});
1377 },
1378 .bit_cast => {
1379 const payload = node.castTag(.bit_cast).?.data;
1380 return renderBuiltinCall(c, "@bitCast", &.{payload});
1381 },
1382 .float_cast => {
1383 const payload = node.castTag(.float_cast).?.data;
1384 return renderBuiltinCall(c, "@floatCast", &.{payload});
1385 },
1386 .int_from_float => {
1387 const payload = node.castTag(.int_from_float).?.data;
1388 return renderBuiltinCall(c, "@intFromFloat", &.{payload});
1389 },
1390 .float_from_int => {
1391 const payload = node.castTag(.float_from_int).?.data;
1392 return renderBuiltinCall(c, "@floatFromInt", &.{payload});
1393 },
1394 .ptr_from_int => {
1395 const payload = node.castTag(.ptr_from_int).?.data;
1396 return renderBuiltinCall(c, "@ptrFromInt", &.{payload});
1397 },
1398 .int_from_ptr => {
1399 const payload = node.castTag(.int_from_ptr).?.data;
1400 return renderBuiltinCall(c, "@intFromPtr", &.{payload});
1401 },
1402 .align_cast => {
1403 const payload = node.castTag(.align_cast).?.data;
1404 return renderBuiltinCall(c, "@alignCast", &.{payload});
1405 },
1406 .ptr_cast => {
1407 const payload = node.castTag(.ptr_cast).?.data;
1408 return renderBuiltinCall(c, "@ptrCast", &.{payload});
1409 },
1410 .div_exact => {
1411 const payload = node.castTag(.div_exact).?.data;
1412 return renderBuiltinCall(c, "@divExact", &.{ payload.lhs, payload.rhs });
1413 },
1414 .offset_of => {
1415 const payload = node.castTag(.offset_of).?.data;
1416 return renderBuiltinCall(c, "@offsetOf", &.{ payload.lhs, payload.rhs });
1417 },
1418 .sizeof => {
1419 const payload = node.castTag(.sizeof).?.data;
1420 return renderBuiltinCall(c, "@sizeOf", &.{payload});
1421 },
1422 .shuffle => {
1423 const payload = node.castTag(.shuffle).?.data;
1424 return renderBuiltinCall(c, "@shuffle", &.{
1425 payload.element_type,
1426 payload.a,
1427 payload.b,
1428 payload.mask_vector,
1429 });
1430 },
1431 .builtin_extern => {
1432 const payload = node.castTag(.builtin_extern).?.data;
1433
1434 var info_inits: [1]Payload.ContainerInitDot.Initializer = .{
1435 .{ .name = "name", .value = payload.name },
1436 };
1437 var info_payload: Payload.ContainerInitDot = .{
1438 .base = .{ .tag = .container_init_dot },
1439 .data = &info_inits,
1440 };
1441
1442 return renderBuiltinCall(c, "@extern", &.{
1443 payload.type,
1444 .{ .ptr_otherwise = &info_payload.base },
1445 });
1446 },
1447 .helper_call => {
1448 const payload = node.castTag(.helper_call).?.data;
1449 const helpers_tok = try c.addNode(.{
1450 .tag = .identifier,
1451 .main_token = try c.addIdentifier("__helpers"),
1452 .data = undefined,
1453 });
1454 const func = try renderFieldAccess(c, helpers_tok, payload.name);
1455 return renderCall(c, func, payload.args);
1456 },
1457 .helper_ref => {
1458 const payload = node.castTag(.helper_ref).?.data;
1459 const helpers_tok = try c.addNode(.{
1460 .tag = .identifier,
1461 .main_token = try c.addIdentifier("__helpers"),
1462 .data = undefined,
1463 });
1464 return renderFieldAccess(c, helpers_tok, payload);
1465 },
1466 .alignof => {
1467 const payload = node.castTag(.alignof).?.data;
1468 return renderBuiltinCall(c, "@alignOf", &.{payload});
1469 },
1470 .typeof => {
1471 const payload = node.castTag(.typeof).?.data;
1472 return renderBuiltinCall(c, "@TypeOf", &.{payload});
1473 },
1474 .typeinfo => {
1475 const payload = node.castTag(.typeinfo).?.data;
1476 return renderBuiltinCall(c, "@typeInfo", &.{payload});
1477 },
1478 .byte_swap => {
1479 const payload = node.castTag(.byte_swap).?.data;
1480 return renderBuiltinCall(c, "@byteSwap", &.{payload});
1481 },
1482 .ceil => {
1483 const payload = node.castTag(.ceil).?.data;
1484 return renderBuiltinCall(c, "@ceil", &.{payload});
1485 },
1486 .cos => {
1487 const payload = node.castTag(.cos).?.data;
1488 return renderBuiltinCall(c, "@cos", &.{payload});
1489 },
1490 .sin => {
1491 const payload = node.castTag(.sin).?.data;
1492 return renderBuiltinCall(c, "@sin", &.{payload});
1493 },
1494 .exp => {
1495 const payload = node.castTag(.exp).?.data;
1496 return renderBuiltinCall(c, "@exp", &.{payload});
1497 },
1498 .exp2 => {
1499 const payload = node.castTag(.exp2).?.data;
1500 return renderBuiltinCall(c, "@exp2", &.{payload});
1501 },
1502 .exp10 => {
1503 const payload = node.castTag(.exp10).?.data;
1504 return renderBuiltinCall(c, "@exp10", &.{payload});
1505 },
1506 .abs => {
1507 const payload = node.castTag(.abs).?.data;
1508 return renderBuiltinCall(c, "@abs", &.{payload});
1509 },
1510 .log => {
1511 const payload = node.castTag(.log).?.data;
1512 return renderBuiltinCall(c, "@log", &.{payload});
1513 },
1514 .log2 => {
1515 const payload = node.castTag(.log2).?.data;
1516 return renderBuiltinCall(c, "@log2", &.{payload});
1517 },
1518 .log10 => {
1519 const payload = node.castTag(.log10).?.data;
1520 return renderBuiltinCall(c, "@log10", &.{payload});
1521 },
1522 .round => {
1523 const payload = node.castTag(.round).?.data;
1524 return renderBuiltinCall(c, "@round", &.{payload});
1525 },
1526 .sqrt => {
1527 const payload = node.castTag(.sqrt).?.data;
1528 return renderBuiltinCall(c, "@sqrt", &.{payload});
1529 },
1530 .trunc => {
1531 const payload = node.castTag(.trunc).?.data;
1532 return renderBuiltinCall(c, "@trunc", &.{payload});
1533 },
1534 .floor => {
1535 const payload = node.castTag(.floor).?.data;
1536 return renderBuiltinCall(c, "@floor", &.{payload});
1537 },
1538 .negate => return renderPrefixOp(c, node, .negation, .minus, "-"),
1539 .negate_wrap => return renderPrefixOp(c, node, .negation_wrap, .minus_percent, "-%"),
1540 .bit_not => return renderPrefixOp(c, node, .bit_not, .tilde, "~"),
1541 .not => return renderPrefixOp(c, node, .bool_not, .bang, "!"),
1542 .optional_type => return renderPrefixOp(c, node, .optional_type, .question_mark, "?"),
1543 .address_of => {
1544 const payload = node.castTag(.address_of).?.data;
1545
1546 const ampersand = try c.addToken(.ampersand, "&");
1547 const base = try renderNodeGrouped(c, payload);
1548 return c.addNode(.{
1549 .tag = .address_of,
1550 .main_token = ampersand,
1551 .data = .{
1552 .node = base,
1553 },
1554 });
1555 },
1556 .deref => {
1557 const payload = node.castTag(.deref).?.data;
1558 const operand = try renderNodeGrouped(c, payload);
1559 const deref_tok = try c.addToken(.period_asterisk, ".*");
1560 return c.addNode(.{
1561 .tag = .deref,
1562 .main_token = deref_tok,
1563 .data = .{
1564 .node = operand,
1565 },
1566 });
1567 },
1568 .unwrap => {
1569 const payload = node.castTag(.unwrap).?.data;
1570 const operand = try renderNodeGrouped(c, payload);
1571 const period = try c.addToken(.period, ".");
1572 const question_mark = try c.addToken(.question_mark, "?");
1573 return c.addNode(.{
1574 .tag = .unwrap_optional,
1575 .main_token = period,
1576 .data = .{ .node_and_token = .{
1577 operand, question_mark,
1578 } },
1579 });
1580 },
1581 .c_pointer, .single_pointer => {
1582 const payload = @as(*Payload.Pointer, @alignCast(@fieldParentPtr("base", node.ptr_otherwise))).data;
1583
1584 const main_token = if (node.tag() == .single_pointer)
1585 try c.addToken(.asterisk, "*")
1586 else blk: {
1587 const res = try c.addToken(.l_bracket, "[");
1588 _ = try c.addToken(.asterisk, "*");
1589 _ = try c.addIdentifier("c");
1590 _ = try c.addToken(.r_bracket, "]");
1591 break :blk res;
1592 };
1593 if (payload.is_const) _ = try c.addToken(.keyword_const, "const");
1594 if (payload.is_volatile) _ = try c.addToken(.keyword_volatile, "volatile");
1595 if (payload.is_allowzero) _ = try c.addToken(.keyword_allowzero, "allowzero");
1596 const elem_type = try renderNodeGrouped(c, payload.elem_type);
1597
1598 return c.addNode(.{
1599 .tag = .ptr_type_aligned,
1600 .main_token = main_token,
1601 .data = .{
1602 .opt_node_and_node = .{
1603 .none, // Align node
1604 elem_type,
1605 },
1606 },
1607 });
1608 },
1609 .add => return renderBinOpGrouped(c, node, .add, .plus, "+"),
1610 .add_assign => return renderBinOp(c, node, .assign_add, .plus_equal, "+="),
1611 .add_wrap => return renderBinOpGrouped(c, node, .add_wrap, .plus_percent, "+%"),
1612 .add_wrap_assign => return renderBinOp(c, node, .assign_add_wrap, .plus_percent_equal, "+%="),
1613 .sub => return renderBinOpGrouped(c, node, .sub, .minus, "-"),
1614 .sub_assign => return renderBinOp(c, node, .assign_sub, .minus_equal, "-="),
1615 .sub_wrap => return renderBinOpGrouped(c, node, .sub_wrap, .minus_percent, "-%"),
1616 .sub_wrap_assign => return renderBinOp(c, node, .assign_sub_wrap, .minus_percent_equal, "-%="),
1617 .mul => return renderBinOpGrouped(c, node, .mul, .asterisk, "*"),
1618 .mul_assign => return renderBinOp(c, node, .assign_mul, .asterisk_equal, "*="),
1619 .mul_wrap => return renderBinOpGrouped(c, node, .mul_wrap, .asterisk_percent, "*%"),
1620 .mul_wrap_assign => return renderBinOp(c, node, .assign_mul_wrap, .asterisk_percent_equal, "*%="),
1621 .div => return renderBinOpGrouped(c, node, .div, .slash, "/"),
1622 .div_assign => return renderBinOp(c, node, .assign_div, .slash_equal, "/="),
1623 .shl => return renderBinOpGrouped(c, node, .shl, .angle_bracket_angle_bracket_left, "<<"),
1624 .shl_assign => return renderBinOp(c, node, .assign_shl, .angle_bracket_angle_bracket_left_equal, "<<="),
1625 .shr => return renderBinOpGrouped(c, node, .shr, .angle_bracket_angle_bracket_right, ">>"),
1626 .shr_assign => return renderBinOp(c, node, .assign_shr, .angle_bracket_angle_bracket_right_equal, ">>="),
1627 .mod => return renderBinOpGrouped(c, node, .mod, .percent, "%"),
1628 .mod_assign => return renderBinOp(c, node, .assign_mod, .percent_equal, "%="),
1629 .@"and" => return renderBinOpGrouped(c, node, .bool_and, .keyword_and, "and"),
1630 .@"or" => return renderBinOpGrouped(c, node, .bool_or, .keyword_or, "or"),
1631 .less_than => return renderBinOpGrouped(c, node, .less_than, .angle_bracket_left, "<"),
1632 .less_than_equal => return renderBinOpGrouped(c, node, .less_or_equal, .angle_bracket_left_equal, "<="),
1633 .greater_than => return renderBinOpGrouped(c, node, .greater_than, .angle_bracket_right, ">="),
1634 .greater_than_equal => return renderBinOpGrouped(c, node, .greater_or_equal, .angle_bracket_right_equal, ">="),
1635 .equal => return renderBinOpGrouped(c, node, .equal_equal, .equal_equal, "=="),
1636 .not_equal => return renderBinOpGrouped(c, node, .bang_equal, .bang_equal, "!="),
1637 .bit_and => return renderBinOpGrouped(c, node, .bit_and, .ampersand, "&"),
1638 .bit_and_assign => return renderBinOp(c, node, .assign_bit_and, .ampersand_equal, "&="),
1639 .bit_or => return renderBinOpGrouped(c, node, .bit_or, .pipe, "|"),
1640 .bit_or_assign => return renderBinOp(c, node, .assign_bit_or, .pipe_equal, "|="),
1641 .bit_xor => return renderBinOpGrouped(c, node, .bit_xor, .caret, "^"),
1642 .bit_xor_assign => return renderBinOp(c, node, .assign_bit_xor, .caret_equal, "^="),
1643 .array_cat => return renderBinOp(c, node, .array_cat, .plus_plus, "++"),
1644 .ellipsis3 => return renderBinOpGrouped(c, node, .switch_range, .ellipsis3, "..."),
1645 .assign => return renderBinOp(c, node, .assign, .equal, "="),
1646 .empty_block => {
1647 const l_brace = try c.addToken(.l_brace, "{");
1648 _ = try c.addToken(.r_brace, "}");
1649 return c.addNode(.{
1650 .tag = .block_two,
1651 .main_token = l_brace,
1652 .data = .{ .opt_node_and_opt_node = .{
1653 .none, .none,
1654 } },
1655 });
1656 },
1657 .block_single => {
1658 const payload = node.castTag(.block_single).?.data;
1659 const l_brace = try c.addToken(.l_brace, "{");
1660
1661 const stmt = (try renderNodeOpt(c, payload)) orelse {
1662 _ = try c.addToken(.r_brace, "}");
1663 return c.addNode(.{
1664 .tag = .block_two,
1665 .main_token = l_brace,
1666 .data = .{ .opt_node_and_opt_node = .{
1667 .none, .none,
1668 } },
1669 });
1670 };
1671 try addSemicolonIfNeeded(c, payload);
1672
1673 _ = try c.addToken(.r_brace, "}");
1674 return c.addNode(.{
1675 .tag = .block_two_semicolon,
1676 .main_token = l_brace,
1677 .data = .{ .opt_node_and_opt_node = .{
1678 stmt.toOptional(), .none,
1679 } },
1680 });
1681 },
1682 .block => {
1683 const payload = node.castTag(.block).?.data;
1684 if (payload.label) |some| {
1685 _ = try c.addIdentifier(some);
1686 _ = try c.addToken(.colon, ":");
1687 }
1688 const l_brace = try c.addToken(.l_brace, "{");
1689
1690 var stmts = std.array_list.Managed(NodeIndex).init(c.gpa);
1691 defer stmts.deinit();
1692 for (payload.stmts) |stmt| {
1693 const res = (try renderNodeOpt(c, stmt)) orelse continue;
1694 try addSemicolonIfNeeded(c, stmt);
1695 try stmts.append(res);
1696 }
1697 const span = try c.listToSpan(stmts.items);
1698 _ = try c.addToken(.r_brace, "}");
1699
1700 const semicolon = c.tokens.items(.tag)[c.tokens.len - 2] == .semicolon;
1701 return c.addNode(.{
1702 .tag = if (semicolon) .block_semicolon else .block,
1703 .main_token = l_brace,
1704 .data = .{ .extra_range = span },
1705 });
1706 },
1707 .func => return renderFunc(c, node),
1708 .pub_inline_fn => return renderMacroFunc(c, node),
1709 .@"while" => {
1710 const payload = node.castTag(.@"while").?.data;
1711 const while_tok = try c.addToken(.keyword_while, "while");
1712 _ = try c.addToken(.l_paren, "(");
1713 const cond = try renderNode(c, payload.cond);
1714 _ = try c.addToken(.r_paren, ")");
1715
1716 const cont_expr_opt = if (payload.cont_expr) |some| blk: {
1717 _ = try c.addToken(.colon, ":");
1718 _ = try c.addToken(.l_paren, "(");
1719 const res = try renderNode(c, some);
1720 _ = try c.addToken(.r_paren, ")");
1721 break :blk res;
1722 } else null;
1723 const body = try renderNode(c, payload.body);
1724
1725 if (cont_expr_opt) |cont_expr| {
1726 return c.addNode(.{
1727 .tag = .while_cont,
1728 .main_token = while_tok,
1729 .data = .{ .node_and_extra = .{
1730 cond,
1731 try c.addExtra(std.zig.Ast.Node.WhileCont{
1732 .cont_expr = cont_expr,
1733 .then_expr = body,
1734 }),
1735 } },
1736 });
1737 } else {
1738 return c.addNode(.{
1739 .tag = .while_simple,
1740 .main_token = while_tok,
1741 .data = .{ .node_and_node = .{
1742 cond, body,
1743 } },
1744 });
1745 }
1746 },
1747 .while_true => {
1748 const payload = node.castTag(.while_true).?.data;
1749 const while_tok = try c.addToken(.keyword_while, "while");
1750 _ = try c.addToken(.l_paren, "(");
1751 const cond = try c.addNode(.{
1752 .tag = .identifier,
1753 .main_token = try c.addToken(.identifier, "true"),
1754 .data = undefined,
1755 });
1756 _ = try c.addToken(.r_paren, ")");
1757 const body = try renderNode(c, payload);
1758
1759 return c.addNode(.{
1760 .tag = .while_simple,
1761 .main_token = while_tok,
1762 .data = .{ .node_and_node = .{
1763 cond, body,
1764 } },
1765 });
1766 },
1767 .@"if" => {
1768 const payload = node.castTag(.@"if").?.data;
1769 const if_tok = try c.addToken(.keyword_if, "if");
1770 _ = try c.addToken(.l_paren, "(");
1771 const cond = try renderNode(c, payload.cond);
1772 _ = try c.addToken(.r_paren, ")");
1773
1774 const then_expr = try renderNode(c, payload.then);
1775 const else_node = payload.@"else" orelse return c.addNode(.{
1776 .tag = .if_simple,
1777 .main_token = if_tok,
1778 .data = .{ .node_and_node = .{
1779 cond, then_expr,
1780 } },
1781 });
1782 _ = try c.addToken(.keyword_else, "else");
1783 const else_expr = try renderNode(c, else_node);
1784
1785 return c.addNode(.{
1786 .tag = .@"if",
1787 .main_token = if_tok,
1788 .data = .{ .node_and_extra = .{
1789 cond,
1790 try c.addExtra(std.zig.Ast.Node.If{
1791 .then_expr = then_expr,
1792 .else_expr = else_expr,
1793 }),
1794 } },
1795 });
1796 },
1797 .if_not_break => {
1798 const payload = node.castTag(.if_not_break).?.data;
1799 const if_tok = try c.addToken(.keyword_if, "if");
1800 _ = try c.addToken(.l_paren, "(");
1801 const cond = try c.addNode(.{
1802 .tag = .bool_not,
1803 .main_token = try c.addToken(.bang, "!"),
1804 .data = .{
1805 .node = try renderNodeGrouped(c, payload),
1806 },
1807 });
1808 _ = try c.addToken(.r_paren, ")");
1809 const then_expr = try c.addNode(.{
1810 .tag = .@"break",
1811 .main_token = try c.addToken(.keyword_break, "break"),
1812 .data = .{ .opt_token_and_opt_node = .{
1813 .none, .none,
1814 } },
1815 });
1816
1817 return c.addNode(.{
1818 .tag = .if_simple,
1819 .main_token = if_tok,
1820 .data = .{ .node_and_node = .{
1821 cond, then_expr,
1822 } },
1823 });
1824 },
1825 .@"switch" => {
1826 const payload = node.castTag(.@"switch").?.data;
1827 const switch_tok = try c.addToken(.keyword_switch, "switch");
1828 _ = try c.addToken(.l_paren, "(");
1829 const cond = try renderNode(c, payload.cond);
1830 _ = try c.addToken(.r_paren, ")");
1831
1832 _ = try c.addToken(.l_brace, "{");
1833 var cases = try c.gpa.alloc(NodeIndex, payload.cases.len);
1834 defer c.gpa.free(cases);
1835 for (payload.cases, 0..) |case, i| {
1836 cases[i] = try renderNode(c, case);
1837 _ = try c.addToken(.comma, ",");
1838 }
1839 const span = try c.listToSpan(cases);
1840 _ = try c.addToken(.r_brace, "}");
1841 return c.addNode(.{
1842 .tag = .switch_comma,
1843 .main_token = switch_tok,
1844 .data = .{ .node_and_extra = .{
1845 cond,
1846 try c.addExtra(NodeSubRange{
1847 .start = span.start,
1848 .end = span.end,
1849 }),
1850 } },
1851 });
1852 },
1853 .switch_else => {
1854 const payload = node.castTag(.switch_else).?.data;
1855 _ = try c.addToken(.keyword_else, "else");
1856 return c.addNode(.{
1857 .tag = .switch_case_one,
1858 .main_token = try c.addToken(.equal_angle_bracket_right, "=>"),
1859 .data = .{ .opt_node_and_node = .{
1860 .none, try renderNode(c, payload),
1861 } },
1862 });
1863 },
1864 .switch_prong => {
1865 const payload = node.castTag(.switch_prong).?.data;
1866 var items = try c.gpa.alloc(NodeIndex, payload.cases.len);
1867 defer c.gpa.free(items);
1868
1869 for (payload.cases, 0..) |item, i| {
1870 if (i != 0) _ = try c.addToken(.comma, ",");
1871 items[i] = try renderNode(c, item);
1872 }
1873 _ = try c.addToken(.r_brace, "}");
1874 if (items.len < 2) {
1875 return c.addNode(.{
1876 .tag = .switch_case_one,
1877 .main_token = try c.addToken(.equal_angle_bracket_right, "=>"),
1878 .data = .{ .opt_node_and_node = .{
1879 if (payload.cases.len == 1) items[0].toOptional() else .none,
1880 try renderNode(c, payload.cond),
1881 } },
1882 });
1883 } else {
1884 return c.addNode(.{
1885 .tag = .switch_case,
1886 .main_token = try c.addToken(.equal_angle_bracket_right, "=>"),
1887 .data = .{ .extra_and_node = .{
1888 try c.addExtra(try c.listToSpan(items)),
1889 try renderNode(c, payload.cond),
1890 } },
1891 });
1892 }
1893 },
1894 .opaque_literal => {
1895 const opaque_tok = try c.addToken(.keyword_opaque, "opaque");
1896 _ = try c.addToken(.l_brace, "{");
1897 _ = try c.addToken(.r_brace, "}");
1898
1899 return c.addNode(.{
1900 .tag = .container_decl_two,
1901 .main_token = opaque_tok,
1902 .data = .{ .opt_node_and_opt_node = .{
1903 .none, .none,
1904 } },
1905 });
1906 },
1907 .array_access => {
1908 const payload = node.castTag(.array_access).?.data;
1909 const lhs = try renderNodeGrouped(c, payload.lhs);
1910 const l_bracket = try c.addToken(.l_bracket, "[");
1911 const index_expr = try renderNode(c, payload.rhs);
1912 _ = try c.addToken(.r_bracket, "]");
1913 return c.addNode(.{
1914 .tag = .array_access,
1915 .main_token = l_bracket,
1916 .data = .{ .node_and_node = .{
1917 lhs, index_expr,
1918 } },
1919 });
1920 },
1921 .array_type => {
1922 const payload = node.castTag(.array_type).?.data;
1923 return renderArrayType(c, payload.len, payload.elem_type);
1924 },
1925 .null_sentinel_array_type => {
1926 const payload = node.castTag(.null_sentinel_array_type).?.data;
1927 return renderNullSentinelArrayType(c, payload.len, payload.elem_type);
1928 },
1929 .array_filler => {
1930 const payload = node.castTag(.array_filler).?.data;
1931
1932 const type_expr = try renderArrayType(c, 1, payload.type);
1933 const l_brace = try c.addToken(.l_brace, "{");
1934 const val = try renderNode(c, payload.filler);
1935 _ = try c.addToken(.r_brace, "}");
1936
1937 const init = try c.addNode(.{
1938 .tag = .array_init_one,
1939 .main_token = l_brace,
1940 .data = .{ .node_and_node = .{
1941 type_expr, val,
1942 } },
1943 });
1944 return c.addNode(.{
1945 .tag = .array_cat,
1946 .main_token = try c.addToken(.asterisk_asterisk, "**"),
1947 .data = .{ .node_and_node = .{
1948 init,
1949 try c.addNode(.{
1950 .tag = .number_literal,
1951 .main_token = try c.addTokenFmt(.number_literal, "{d}", .{payload.count}),
1952 .data = undefined,
1953 }),
1954 } },
1955 });
1956 },
1957 .empty_array => {
1958 const payload = node.castTag(.empty_array).?.data;
1959
1960 const type_expr = try renderNode(c, payload);
1961 return renderArrayInit(c, type_expr, &.{});
1962 },
1963 .array_init => {
1964 const payload = node.castTag(.array_init).?.data;
1965 const type_expr = try renderNode(c, payload.cond);
1966 return renderArrayInit(c, type_expr, payload.cases);
1967 },
1968 .vector_zero_init => {
1969 const payload = node.castTag(.vector_zero_init).?.data;
1970 return renderBuiltinCall(c, "@splat", &.{payload});
1971 },
1972 .field_access => {
1973 const payload = node.castTag(.field_access).?.data;
1974 const lhs = try renderNodeGrouped(c, payload.lhs);
1975 return renderFieldAccess(c, lhs, payload.field_name);
1976 },
1977 .@"struct", .@"union", .@"opaque" => return renderContainer(c, node),
1978 .enum_constant => {
1979 const payload = node.castTag(.enum_constant).?.data;
1980
1981 if (payload.is_public) _ = try c.addToken(.keyword_pub, "pub");
1982 const const_tok = try c.addToken(.keyword_const, "const");
1983 _ = try c.addIdentifier(payload.name);
1984
1985 const type_node_opt = if (payload.type) |enum_const_type| blk: {
1986 _ = try c.addToken(.colon, ":");
1987 break :blk try renderNode(c, enum_const_type);
1988 } else null;
1989
1990 _ = try c.addToken(.equal, "=");
1991
1992 const init_node = try renderNode(c, payload.value);
1993 _ = try c.addToken(.semicolon, ";");
1994
1995 return c.addNode(.{
1996 .tag = .simple_var_decl,
1997 .main_token = const_tok,
1998 .data = .{ .opt_node_and_opt_node = .{
1999 .fromOptional(type_node_opt),
2000 init_node.toOptional(),
2001 } },
2002 });
2003 },
2004 .tuple => {
2005 const payload = node.castTag(.tuple).?.data;
2006 _ = try c.addToken(.period, ".");
2007 const l_brace = try c.addToken(.l_brace, "{");
2008 var inits = try c.gpa.alloc(NodeIndex, payload.len);
2009 defer c.gpa.free(inits);
2010
2011 for (payload, 0..) |init, i| {
2012 if (i != 0) _ = try c.addToken(.comma, ",");
2013 inits[i] = try renderNode(c, init);
2014 }
2015 _ = try c.addToken(.r_brace, "}");
2016 if (payload.len < 3) {
2017 return c.addNode(.{
2018 .tag = .array_init_dot_two,
2019 .main_token = l_brace,
2020 .data = .{ .opt_node_and_opt_node = .{
2021 if (inits.len >= 1) inits[0].toOptional() else .none,
2022 if (inits.len >= 2) inits[1].toOptional() else .none,
2023 } },
2024 });
2025 } else {
2026 return c.addNode(.{
2027 .tag = .array_init_dot,
2028 .main_token = l_brace,
2029 .data = .{ .extra_range = try c.listToSpan(inits) },
2030 });
2031 }
2032 },
2033 .container_init_dot => {
2034 const payload = node.castTag(.container_init_dot).?.data;
2035 _ = try c.addToken(.period, ".");
2036 const l_brace = try c.addToken(.l_brace, "{");
2037 var inits = try c.gpa.alloc(NodeIndex, payload.len);
2038 defer c.gpa.free(inits);
2039
2040 for (payload, 0..) |init, i| {
2041 _ = try c.addToken(.period, ".");
2042 _ = try c.addIdentifier(init.name);
2043 _ = try c.addToken(.equal, "=");
2044 inits[i] = try renderNode(c, init.value);
2045 _ = try c.addToken(.comma, ",");
2046 }
2047 _ = try c.addToken(.r_brace, "}");
2048
2049 if (payload.len < 3) {
2050 return c.addNode(.{
2051 .tag = .struct_init_dot_two_comma,
2052 .main_token = l_brace,
2053 .data = .{ .opt_node_and_opt_node = .{
2054 if (inits.len >= 1) inits[0].toOptional() else .none,
2055 if (inits.len >= 2) inits[1].toOptional() else .none,
2056 } },
2057 });
2058 } else {
2059 return c.addNode(.{
2060 .tag = .struct_init_dot_comma,
2061 .main_token = l_brace,
2062 .data = .{ .extra_range = try c.listToSpan(inits) },
2063 });
2064 }
2065 },
2066 .container_init => {
2067 const payload = node.castTag(.container_init).?.data;
2068 const lhs = try renderNode(c, payload.lhs);
2069
2070 const l_brace = try c.addToken(.l_brace, "{");
2071 var inits = try c.gpa.alloc(NodeIndex, payload.inits.len);
2072 defer c.gpa.free(inits);
2073
2074 for (payload.inits, 0..) |init, i| {
2075 _ = try c.addToken(.period, ".");
2076 _ = try c.addIdentifier(init.name);
2077 _ = try c.addToken(.equal, "=");
2078 inits[i] = try renderNode(c, init.value);
2079 _ = try c.addToken(.comma, ",");
2080 }
2081 _ = try c.addToken(.r_brace, "}");
2082
2083 switch (inits.len) {
2084 0 => return c.addNode(.{
2085 .tag = .struct_init_one,
2086 .main_token = l_brace,
2087 .data = .{ .node_and_opt_node = .{
2088 lhs, .none,
2089 } },
2090 }),
2091 1 => return c.addNode(.{
2092 .tag = .struct_init_one_comma,
2093 .main_token = l_brace,
2094 .data = .{ .node_and_opt_node = .{
2095 lhs, inits[0].toOptional(),
2096 } },
2097 }),
2098 else => return c.addNode(.{
2099 .tag = .struct_init_comma,
2100 .main_token = l_brace,
2101 .data = .{ .node_and_extra = .{
2102 lhs,
2103 try c.addExtra(try c.listToSpan(inits)),
2104 } },
2105 }),
2106 }
2107 },
2108 .static_assert => {
2109 const payload = node.castTag(.static_assert).?.data;
2110 const comptime_tok = try c.addToken(.keyword_comptime, "comptime");
2111 const l_brace = try c.addToken(.l_brace, "{");
2112
2113 const if_tok = try c.addToken(.keyword_if, "if");
2114 _ = try c.addToken(.l_paren, "(");
2115 const cond = try c.addNode(.{
2116 .tag = .bool_not,
2117 .main_token = try c.addToken(.bang, "!"),
2118 .data = .{
2119 .node = try renderNodeGrouped(c, payload.lhs),
2120 },
2121 });
2122 _ = try c.addToken(.r_paren, ")");
2123
2124 const compile_error_tok = try c.addToken(.builtin, "@compileError");
2125 _ = try c.addToken(.l_paren, "(");
2126 const err_msg = try renderNode(c, payload.rhs);
2127 _ = try c.addToken(.r_paren, ")");
2128 const compile_error = try c.addNode(.{
2129 .tag = .builtin_call_two,
2130 .main_token = compile_error_tok,
2131 .data = .{ .opt_node_and_opt_node = .{
2132 err_msg.toOptional(), .none,
2133 } },
2134 });
2135
2136 const if_node = try c.addNode(.{
2137 .tag = .if_simple,
2138 .main_token = if_tok,
2139 .data = .{ .node_and_node = .{
2140 cond, compile_error,
2141 } },
2142 });
2143 _ = try c.addToken(.semicolon, ";");
2144 _ = try c.addToken(.r_brace, "}");
2145 const block_node = try c.addNode(.{
2146 .tag = .block_two_semicolon,
2147 .main_token = l_brace,
2148 .data = .{ .opt_node_and_opt_node = .{
2149 if_node.toOptional(), .none,
2150 } },
2151 });
2152
2153 return c.addNode(.{
2154 .tag = .@"comptime",
2155 .main_token = comptime_tok,
2156 .data = .{
2157 .node = block_node,
2158 },
2159 });
2160 },
2161 .@"anytype" => unreachable, // Handled in renderParams
2162 }
2163}
2164
2165fn renderContainer(c: *Context, node: Node) !NodeIndex {
2166 const payload = @as(*Payload.Container, @alignCast(@fieldParentPtr("base", node.ptr_otherwise))).data;
2167 if (payload.layout == .@"packed")
2168 _ = try c.addToken(.keyword_packed, "packed")
2169 else if (payload.layout == .@"extern")
2170 _ = try c.addToken(.keyword_extern, "extern");
2171 const kind_tok = if (node.tag() == .@"struct")
2172 try c.addToken(.keyword_struct, "struct")
2173 else if (node.tag() == .@"union")
2174 try c.addToken(.keyword_union, "union")
2175 else if (node.tag() == .@"opaque")
2176 try c.addToken(.keyword_opaque, "opaque")
2177 else
2178 unreachable;
2179
2180 _ = try c.addToken(.l_brace, "{");
2181
2182 const num_decls = payload.decls.len;
2183 const total_members = payload.fields.len + num_decls;
2184 const members = try c.gpa.alloc(NodeIndex, total_members);
2185 defer c.gpa.free(members);
2186
2187 for (payload.fields, 0..) |field, i| {
2188 const name_tok = try c.addTokenFmt(.identifier, "{f}", .{std.zig.fmtIdFlags(field.name, .{ .allow_primitive = true })});
2189 _ = try c.addToken(.colon, ":");
2190 const type_expr = try renderNode(c, field.type);
2191
2192 const align_expr_opt = if (field.alignment) |alignment| blk: {
2193 _ = try c.addToken(.keyword_align, "align");
2194 _ = try c.addToken(.l_paren, "(");
2195 const align_expr = try c.addNode(.{
2196 .tag = .number_literal,
2197 .main_token = try c.addTokenFmt(.number_literal, "{d}", .{alignment}),
2198 .data = undefined,
2199 });
2200 _ = try c.addToken(.r_paren, ")");
2201 break :blk align_expr;
2202 } else null;
2203
2204 const value_expr_opt = if (field.default_value) |value| blk: {
2205 _ = try c.addToken(.equal, "=");
2206 break :blk try renderNode(c, value);
2207 } else null;
2208
2209 if (align_expr_opt) |align_expr| {
2210 if (value_expr_opt) |value_expr| {
2211 members[i] = try c.addNode(.{
2212 .tag = .container_field,
2213 .main_token = name_tok,
2214 .data = .{ .node_and_extra = .{
2215 type_expr,
2216 try c.addExtra(std.zig.Ast.Node.ContainerField{
2217 .align_expr = align_expr,
2218 .value_expr = value_expr,
2219 }),
2220 } },
2221 });
2222 } else {
2223 members[i] = try c.addNode(.{
2224 .tag = .container_field_align,
2225 .main_token = name_tok,
2226 .data = .{ .node_and_node = .{
2227 type_expr,
2228 align_expr,
2229 } },
2230 });
2231 }
2232 } else {
2233 members[i] = try c.addNode(.{
2234 .tag = .container_field_init,
2235 .main_token = name_tok,
2236 .data = .{ .node_and_opt_node = .{
2237 type_expr,
2238 .fromOptional(value_expr_opt),
2239 } },
2240 });
2241 }
2242 _ = try c.addToken(.comma, ",");
2243 }
2244 for (members[payload.fields.len..], payload.decls) |*member, decl| {
2245 member.* = try renderNode(c, decl);
2246 }
2247 const trailing = switch (c.tokens.items(.tag)[c.tokens.len - 1]) {
2248 .comma, .semicolon => true,
2249 else => false,
2250 };
2251 _ = try c.addToken(.r_brace, "}");
2252
2253 if (total_members == 0) {
2254 return c.addNode(.{
2255 .tag = .container_decl_two,
2256 .main_token = kind_tok,
2257 .data = .{ .opt_node_and_opt_node = .{
2258 .none, .none,
2259 } },
2260 });
2261 } else if (total_members <= 2) {
2262 return c.addNode(.{
2263 .tag = if (trailing) .container_decl_two_trailing else .container_decl_two,
2264 .main_token = kind_tok,
2265 .data = .{ .opt_node_and_opt_node = .{
2266 if (members.len >= 1) members[0].toOptional() else .none,
2267 if (members.len >= 2) members[1].toOptional() else .none,
2268 } },
2269 });
2270 } else {
2271 const span = try c.listToSpan(members);
2272 return c.addNode(.{
2273 .tag = if (trailing) .container_decl_trailing else .container_decl,
2274 .main_token = kind_tok,
2275 .data = .{ .extra_range = span },
2276 });
2277 }
2278}
2279
2280fn renderFieldAccess(c: *Context, lhs: NodeIndex, field_name: []const u8) !NodeIndex {
2281 return c.addNode(.{
2282 .tag = .field_access,
2283 .main_token = try c.addToken(.period, "."),
2284 .data = .{ .node_and_token = .{
2285 lhs, try c.addTokenFmt(.identifier, "{f}", .{std.zig.fmtIdFlags(field_name, .{ .allow_primitive = true })}),
2286 } },
2287 });
2288}
2289
2290fn renderArrayInit(c: *Context, lhs: NodeIndex, inits: []const Node) !NodeIndex {
2291 const l_brace = try c.addToken(.l_brace, "{");
2292 var rendered = try c.gpa.alloc(NodeIndex, inits.len);
2293 defer c.gpa.free(rendered);
2294
2295 for (inits, 0..) |init, i| {
2296 rendered[i] = try renderNode(c, init);
2297 _ = try c.addToken(.comma, ",");
2298 }
2299 _ = try c.addToken(.r_brace, "}");
2300 switch (inits.len) {
2301 0 => return c.addNode(.{
2302 .tag = .struct_init_one,
2303 .main_token = l_brace,
2304 .data = .{ .node_and_opt_node = .{
2305 lhs, .none,
2306 } },
2307 }),
2308 1 => return c.addNode(.{
2309 .tag = .array_init_one_comma,
2310 .main_token = l_brace,
2311 .data = .{ .node_and_node = .{
2312 lhs, rendered[0],
2313 } },
2314 }),
2315 else => return c.addNode(.{
2316 .tag = .array_init_comma,
2317 .main_token = l_brace,
2318 .data = .{ .node_and_extra = .{
2319 lhs,
2320 try c.addExtra(try c.listToSpan(rendered)),
2321 } },
2322 }),
2323 }
2324}
2325
2326fn renderArrayType(c: *Context, len: u64, elem_type: Node) !NodeIndex {
2327 const l_bracket = try c.addToken(.l_bracket, "[");
2328 const len_expr = try c.addNode(.{
2329 .tag = .number_literal,
2330 .main_token = try c.addTokenFmt(.number_literal, "{d}", .{len}),
2331 .data = undefined,
2332 });
2333 _ = try c.addToken(.r_bracket, "]");
2334 const elem_type_expr = try renderNode(c, elem_type);
2335 return c.addNode(.{
2336 .tag = .array_type,
2337 .main_token = l_bracket,
2338 .data = .{ .node_and_node = .{
2339 len_expr, elem_type_expr,
2340 } },
2341 });
2342}
2343
2344fn renderNullSentinelArrayType(c: *Context, len: u64, elem_type: Node) !NodeIndex {
2345 const l_bracket = try c.addToken(.l_bracket, "[");
2346 const len_expr = try c.addNode(.{
2347 .tag = .number_literal,
2348 .main_token = try c.addTokenFmt(.number_literal, "{d}", .{len}),
2349 .data = undefined,
2350 });
2351 _ = try c.addToken(.colon, ":");
2352
2353 const sentinel_expr = try c.addNode(.{
2354 .tag = .number_literal,
2355 .main_token = try c.addToken(.number_literal, "0"),
2356 .data = undefined,
2357 });
2358
2359 _ = try c.addToken(.r_bracket, "]");
2360 const elem_type_expr = try renderNode(c, elem_type);
2361 return c.addNode(.{
2362 .tag = .array_type_sentinel,
2363 .main_token = l_bracket,
2364 .data = .{ .node_and_extra = .{
2365 len_expr,
2366 try c.addExtra(std.zig.Ast.Node.ArrayTypeSentinel{
2367 .sentinel = sentinel_expr,
2368 .elem_type = elem_type_expr,
2369 }),
2370 } },
2371 });
2372}
2373
2374fn addSemicolonIfNeeded(c: *Context, node: Node) !void {
2375 switch (node.tag()) {
2376 .warning => unreachable,
2377 .var_decl, .var_simple, .arg_redecl, .alias, .block, .empty_block, .block_single, .@"switch", .wrapped_local, .mut_str => {},
2378 .while_true => {
2379 const payload = node.castTag(.while_true).?.data;
2380 return addSemicolonIfNotBlock(c, payload);
2381 },
2382 .@"while" => {
2383 const payload = node.castTag(.@"while").?.data;
2384 return addSemicolonIfNotBlock(c, payload.body);
2385 },
2386 .@"if" => {
2387 const payload = node.castTag(.@"if").?.data;
2388 if (payload.@"else") |some|
2389 return addSemicolonIfNeeded(c, some);
2390 return addSemicolonIfNotBlock(c, payload.then);
2391 },
2392 else => _ = try c.addToken(.semicolon, ";"),
2393 }
2394}
2395
2396fn addSemicolonIfNotBlock(c: *Context, node: Node) !void {
2397 switch (node.tag()) {
2398 .block, .empty_block, .block_single => {},
2399 else => _ = try c.addToken(.semicolon, ";"),
2400 }
2401}
2402
2403fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
2404 switch (node.tag()) {
2405 .declaration => unreachable,
2406 .null_literal,
2407 .undefined_literal,
2408 .true_literal,
2409 .false_literal,
2410 .return_void,
2411 .zero_literal,
2412 .one_literal,
2413 .void_type,
2414 .noreturn_type,
2415 .@"anytype",
2416 .div_trunc,
2417 .int_cast,
2418 .const_cast,
2419 .volatile_cast,
2420 .as,
2421 .truncate,
2422 .bit_cast,
2423 .float_cast,
2424 .int_from_float,
2425 .float_from_int,
2426 .ptr_from_int,
2427 .std_mem_zeroes,
2428 .int_from_ptr,
2429 .sizeof,
2430 .alignof,
2431 .typeof,
2432 .typeinfo,
2433 .vector,
2434 .std_mem_zeroinit,
2435 .integer_literal,
2436 .float_literal,
2437 .string_literal,
2438 .string_slice,
2439 .char_literal,
2440 .enum_literal,
2441 .identifier,
2442 .field_access,
2443 .ptr_cast,
2444 .type,
2445 .array_access,
2446 .align_cast,
2447 .optional_type,
2448 .c_pointer,
2449 .single_pointer,
2450 .unwrap,
2451 .deref,
2452 .not,
2453 .negate,
2454 .negate_wrap,
2455 .bit_not,
2456 .func,
2457 .call,
2458 .array_type,
2459 .null_sentinel_array_type,
2460 .int_from_bool,
2461 .div_exact,
2462 .offset_of,
2463 .shuffle,
2464 .builtin_extern,
2465 .wrapped_local,
2466 .mut_str,
2467 .helper_call,
2468 .helper_ref,
2469 .byte_swap,
2470 .ceil,
2471 .cos,
2472 .sin,
2473 .exp,
2474 .exp2,
2475 .exp10,
2476 .abs,
2477 .log,
2478 .log2,
2479 .log10,
2480 .round,
2481 .sqrt,
2482 .trunc,
2483 .floor,
2484 => {
2485 // no grouping needed
2486 return renderNode(c, node);
2487 },
2488
2489 .opaque_literal,
2490 .@"opaque",
2491 .empty_array,
2492 .block_single,
2493 .add,
2494 .add_wrap,
2495 .sub,
2496 .sub_wrap,
2497 .mul,
2498 .mul_wrap,
2499 .div,
2500 .shl,
2501 .shr,
2502 .mod,
2503 .@"and",
2504 .@"or",
2505 .less_than,
2506 .less_than_equal,
2507 .greater_than,
2508 .greater_than_equal,
2509 .equal,
2510 .not_equal,
2511 .bit_and,
2512 .bit_or,
2513 .bit_xor,
2514 .empty_block,
2515 .array_cat,
2516 .array_filler,
2517 .@"if",
2518 .@"struct",
2519 .@"union",
2520 .array_init,
2521 .vector_zero_init,
2522 .tuple,
2523 .container_init,
2524 .container_init_dot,
2525 .block,
2526 .address_of,
2527 => return c.addNode(.{
2528 .tag = .grouped_expression,
2529 .main_token = try c.addToken(.l_paren, "("),
2530 .data = .{ .node_and_token = .{
2531 try renderNode(c, node),
2532 try c.addToken(.r_paren, ")"),
2533 } },
2534 }),
2535 .ellipsis3,
2536 .switch_prong,
2537 .warning,
2538 .var_decl,
2539 .fail_decl,
2540 .arg_redecl,
2541 .alias,
2542 .var_simple,
2543 .pub_var_simple,
2544 .enum_constant,
2545 .@"while",
2546 .@"switch",
2547 .@"break",
2548 .break_val,
2549 .pub_inline_fn,
2550 .discard,
2551 .@"continue",
2552 .@"return",
2553 .@"comptime",
2554 .@"defer",
2555 .asm_simple,
2556 .while_true,
2557 .if_not_break,
2558 .switch_else,
2559 .add_assign,
2560 .add_wrap_assign,
2561 .sub_assign,
2562 .sub_wrap_assign,
2563 .mul_assign,
2564 .mul_wrap_assign,
2565 .div_assign,
2566 .shl_assign,
2567 .shr_assign,
2568 .mod_assign,
2569 .bit_and_assign,
2570 .bit_or_assign,
2571 .bit_xor_assign,
2572 .assign,
2573 .static_assert,
2574 .@"unreachable",
2575 => {
2576 // these should never appear in places where grouping might be needed.
2577 unreachable;
2578 },
2579 }
2580}
2581
2582fn renderPrefixOp(c: *Context, node: Node, tag: std.zig.Ast.Node.Tag, tok_tag: TokenTag, bytes: []const u8) !NodeIndex {
2583 const payload = @as(*Payload.UnOp, @alignCast(@fieldParentPtr("base", node.ptr_otherwise))).data;
2584 return c.addNode(.{
2585 .tag = tag,
2586 .main_token = try c.addToken(tok_tag, bytes),
2587 .data = .{
2588 .node = try renderNodeGrouped(c, payload),
2589 },
2590 });
2591}
2592
2593fn renderBinOpGrouped(c: *Context, node: Node, tag: std.zig.Ast.Node.Tag, tok_tag: TokenTag, bytes: []const u8) !NodeIndex {
2594 const payload = @as(*Payload.BinOp, @alignCast(@fieldParentPtr("base", node.ptr_otherwise))).data;
2595 const lhs = try renderNodeGrouped(c, payload.lhs);
2596 return c.addNode(.{
2597 .tag = tag,
2598 .main_token = try c.addToken(tok_tag, bytes),
2599 .data = .{ .node_and_node = .{
2600 lhs, try renderNodeGrouped(c, payload.rhs),
2601 } },
2602 });
2603}
2604
2605fn renderBinOp(c: *Context, node: Node, tag: std.zig.Ast.Node.Tag, tok_tag: TokenTag, bytes: []const u8) !NodeIndex {
2606 const payload = @as(*Payload.BinOp, @alignCast(@fieldParentPtr("base", node.ptr_otherwise))).data;
2607 const lhs = try renderNode(c, payload.lhs);
2608 return c.addNode(.{
2609 .tag = tag,
2610 .main_token = try c.addToken(tok_tag, bytes),
2611 .data = .{ .node_and_node = .{
2612 lhs, try renderNode(c, payload.rhs),
2613 } },
2614 });
2615}
2616
2617fn renderStdImport(c: *Context, parts: []const []const u8) !NodeIndex {
2618 const import_tok = try c.addToken(.builtin, "@import");
2619 _ = try c.addToken(.l_paren, "(");
2620 const std_tok = try c.addToken(.string_literal, "\"std\"");
2621 const std_node = try c.addNode(.{
2622 .tag = .string_literal,
2623 .main_token = std_tok,
2624 .data = undefined,
2625 });
2626 _ = try c.addToken(.r_paren, ")");
2627
2628 const import_node = try c.addNode(.{
2629 .tag = .builtin_call_two,
2630 .main_token = import_tok,
2631 .data = .{ .opt_node_and_opt_node = .{
2632 std_node.toOptional(), .none,
2633 } },
2634 });
2635
2636 var access_chain = import_node;
2637 for (parts) |part| {
2638 access_chain = try renderFieldAccess(c, access_chain, part);
2639 }
2640 return access_chain;
2641}
2642
2643fn renderCall(c: *Context, lhs: NodeIndex, args: []const Node) !NodeIndex {
2644 const lparen = try c.addToken(.l_paren, "(");
2645 const res = switch (args.len) {
2646 0 => try c.addNode(.{
2647 .tag = .call_one,
2648 .main_token = lparen,
2649 .data = .{ .node_and_opt_node = .{
2650 lhs, .none,
2651 } },
2652 }),
2653 1 => try c.addNode(.{
2654 .tag = .call_one,
2655 .main_token = lparen,
2656 .data = .{ .node_and_opt_node = .{
2657 lhs, (try renderNode(c, args[0])).toOptional(),
2658 } },
2659 }),
2660 else => blk: {
2661 var rendered = try c.gpa.alloc(NodeIndex, args.len);
2662 defer c.gpa.free(rendered);
2663
2664 for (args, 0..) |arg, i| {
2665 if (i != 0) _ = try c.addToken(.comma, ",");
2666 rendered[i] = try renderNode(c, arg);
2667 }
2668 const span = try c.listToSpan(rendered);
2669 break :blk try c.addNode(.{
2670 .tag = .call,
2671 .main_token = lparen,
2672 .data = .{ .node_and_extra = .{
2673 lhs, try c.addExtra(NodeSubRange{
2674 .start = span.start,
2675 .end = span.end,
2676 }),
2677 } },
2678 });
2679 },
2680 };
2681 _ = try c.addToken(.r_paren, ")");
2682 return res;
2683}
2684
2685fn renderBuiltinCall(c: *Context, builtin: []const u8, args: []const Node) !NodeIndex {
2686 const builtin_tok = try c.addToken(.builtin, builtin);
2687 _ = try c.addToken(.l_paren, "(");
2688 var arg_1: ?NodeIndex = null;
2689 var arg_2: ?NodeIndex = null;
2690 var arg_3: ?NodeIndex = null;
2691 var arg_4: ?NodeIndex = null;
2692 switch (args.len) {
2693 0 => {},
2694 1 => {
2695 arg_1 = try renderNode(c, args[0]);
2696 },
2697 2 => {
2698 arg_1 = try renderNode(c, args[0]);
2699 _ = try c.addToken(.comma, ",");
2700 arg_2 = try renderNode(c, args[1]);
2701 },
2702 4 => {
2703 arg_1 = try renderNode(c, args[0]);
2704 _ = try c.addToken(.comma, ",");
2705 arg_2 = try renderNode(c, args[1]);
2706 _ = try c.addToken(.comma, ",");
2707 arg_3 = try renderNode(c, args[2]);
2708 _ = try c.addToken(.comma, ",");
2709 arg_4 = try renderNode(c, args[3]);
2710 },
2711 else => unreachable, // expand this function as needed.
2712 }
2713
2714 _ = try c.addToken(.r_paren, ")");
2715 if (args.len <= 2) {
2716 return c.addNode(.{
2717 .tag = .builtin_call_two,
2718 .main_token = builtin_tok,
2719 .data = .{ .opt_node_and_opt_node = .{
2720 .fromOptional(arg_1), .fromOptional(arg_2),
2721 } },
2722 });
2723 } else {
2724 std.debug.assert(args.len == 4);
2725
2726 const params = try c.listToSpan(&.{ arg_1.?, arg_2.?, arg_3.?, arg_4.? });
2727 return c.addNode(.{
2728 .tag = .builtin_call,
2729 .main_token = builtin_tok,
2730 .data = .{ .extra_range = .{
2731 .start = params.start,
2732 .end = params.end,
2733 } },
2734 });
2735 }
2736}
2737
2738fn renderVar(c: *Context, node: Node) !NodeIndex {
2739 const payload = node.castTag(.var_decl).?.data;
2740 if (payload.is_pub) _ = try c.addToken(.keyword_pub, "pub");
2741 if (payload.is_extern) _ = try c.addToken(.keyword_extern, "extern");
2742 if (payload.is_export) _ = try c.addToken(.keyword_export, "export");
2743 if (payload.is_threadlocal) _ = try c.addToken(.keyword_threadlocal, "threadlocal");
2744 const mut_tok = if (payload.is_const)
2745 try c.addToken(.keyword_const, "const")
2746 else
2747 try c.addToken(.keyword_var, "var");
2748 _ = try c.addIdentifier(payload.name);
2749 _ = try c.addToken(.colon, ":");
2750 const type_node = try renderNode(c, payload.type);
2751
2752 const align_node_opt = if (payload.alignment) |some| blk: {
2753 _ = try c.addToken(.keyword_align, "align");
2754 _ = try c.addToken(.l_paren, "(");
2755 const res = try c.addNode(.{
2756 .tag = .number_literal,
2757 .main_token = try c.addTokenFmt(.number_literal, "{d}", .{some}),
2758 .data = undefined,
2759 });
2760 _ = try c.addToken(.r_paren, ")");
2761 break :blk res;
2762 } else null;
2763
2764 const section_node_opt = if (payload.linksection_string) |some| blk: {
2765 _ = try c.addToken(.keyword_linksection, "linksection");
2766 _ = try c.addToken(.l_paren, "(");
2767 const res = try c.addNode(.{
2768 .tag = .string_literal,
2769 .main_token = try c.addTokenFmt(.string_literal, "\"{f}\"", .{std.zig.fmtString(some)}),
2770 .data = undefined,
2771 });
2772 _ = try c.addToken(.r_paren, ")");
2773 break :blk res;
2774 } else null;
2775
2776 const init_node_opt = if (payload.init) |some| blk: {
2777 _ = try c.addToken(.equal, "=");
2778 break :blk try renderNode(c, some);
2779 } else null;
2780 _ = try c.addToken(.semicolon, ";");
2781
2782 if (section_node_opt) |section_node| {
2783 return c.addNode(.{
2784 .tag = .global_var_decl,
2785 .main_token = mut_tok,
2786 .data = .{ .extra_and_opt_node = .{
2787 try c.addExtra(std.zig.Ast.Node.GlobalVarDecl{
2788 .type_node = type_node.toOptional(),
2789 .align_node = .fromOptional(align_node_opt),
2790 .section_node = section_node.toOptional(),
2791 .addrspace_node = .none,
2792 }),
2793 .fromOptional(init_node_opt),
2794 } },
2795 });
2796 } else {
2797 if (align_node_opt) |align_node| {
2798 return c.addNode(.{
2799 .tag = .local_var_decl,
2800 .main_token = mut_tok,
2801 .data = .{ .extra_and_opt_node = .{
2802 try c.addExtra(std.zig.Ast.Node.LocalVarDecl{
2803 .type_node = type_node,
2804 .align_node = align_node,
2805 }),
2806 .fromOptional(init_node_opt),
2807 } },
2808 });
2809 } else {
2810 return c.addNode(.{
2811 .tag = .simple_var_decl,
2812 .main_token = mut_tok,
2813 .data = .{
2814 .opt_node_and_opt_node = .{
2815 type_node.toOptional(), // Type expression
2816 .fromOptional(init_node_opt), // Init expression
2817 },
2818 },
2819 });
2820 }
2821 }
2822}
2823
2824fn renderFunc(c: *Context, node: Node) !NodeIndex {
2825 const payload = node.castTag(.func).?.data;
2826 if (payload.is_pub) _ = try c.addToken(.keyword_pub, "pub");
2827 if (payload.is_extern) _ = try c.addToken(.keyword_extern, "extern");
2828 if (payload.is_export) _ = try c.addToken(.keyword_export, "export");
2829 if (payload.is_inline) _ = try c.addToken(.keyword_inline, "inline");
2830 const fn_token = try c.addToken(.keyword_fn, "fn");
2831 if (payload.name) |some| _ = try c.addIdentifier(some);
2832
2833 const params = try renderParams(c, payload.params, payload.is_var_args);
2834 defer params.deinit();
2835 var span: NodeSubRange = undefined;
2836 if (params.items.len > 1) span = try c.listToSpan(params.items);
2837
2838 const align_expr_opt = if (payload.alignment) |some| blk: {
2839 _ = try c.addToken(.keyword_align, "align");
2840 _ = try c.addToken(.l_paren, "(");
2841 const res = try c.addNode(.{
2842 .tag = .number_literal,
2843 .main_token = try c.addTokenFmt(.number_literal, "{d}", .{some}),
2844 .data = undefined,
2845 });
2846 _ = try c.addToken(.r_paren, ")");
2847 break :blk res;
2848 } else null;
2849
2850 const section_expr_opt = if (payload.linksection_string) |some| blk: {
2851 _ = try c.addToken(.keyword_linksection, "linksection");
2852 _ = try c.addToken(.l_paren, "(");
2853 const res = try c.addNode(.{
2854 .tag = .string_literal,
2855 .main_token = try c.addTokenFmt(.string_literal, "\"{f}\"", .{std.zig.fmtString(some)}),
2856 .data = undefined,
2857 });
2858 _ = try c.addToken(.r_paren, ")");
2859 break :blk res;
2860 } else null;
2861
2862 const callconv_expr_opt = if (payload.explicit_callconv) |some| blk: {
2863 _ = try c.addToken(.keyword_callconv, "callconv");
2864 _ = try c.addToken(.l_paren, "(");
2865 const cc_node = switch (some) {
2866 .c => cc_node: {
2867 _ = try c.addToken(.period, ".");
2868 break :cc_node try c.addNode(.{
2869 .tag = .enum_literal,
2870 .main_token = try c.addToken(.identifier, "c"),
2871 .data = undefined,
2872 });
2873 },
2874 .x86_64_sysv,
2875 .x86_64_win,
2876 .x86_stdcall,
2877 .x86_fastcall,
2878 .x86_thiscall,
2879 .x86_vectorcall,
2880 .x86_regcall,
2881 .aarch64_vfabi,
2882 .aarch64_sve_pcs,
2883 .arm_aapcs,
2884 .arm_aapcs_vfp,
2885 .m68k_rtd,
2886 .riscv_vector,
2887 => cc_node: {
2888 // .{ .foo = .{} }
2889 _ = try c.addToken(.period, ".");
2890 const outer_lbrace = try c.addToken(.l_brace, "{");
2891 _ = try c.addToken(.period, ".");
2892 _ = try c.addToken(.identifier, @tagName(some));
2893 _ = try c.addToken(.equal, "=");
2894 _ = try c.addToken(.period, ".");
2895 const inner_lbrace = try c.addToken(.l_brace, "{");
2896 _ = try c.addToken(.r_brace, "}");
2897 _ = try c.addToken(.r_brace, "}");
2898 break :cc_node try c.addNode(.{
2899 .tag = .struct_init_dot_two,
2900 .main_token = outer_lbrace,
2901 .data = .{ .opt_node_and_opt_node = .{
2902 (try c.addNode(.{
2903 .tag = .struct_init_dot_two,
2904 .main_token = inner_lbrace,
2905 .data = .{ .opt_node_and_opt_node = .{
2906 .none, .none,
2907 } },
2908 })).toOptional(),
2909 .none,
2910 } },
2911 });
2912 },
2913 };
2914 _ = try c.addToken(.r_paren, ")");
2915 break :blk cc_node;
2916 } else null;
2917
2918 const return_type_expr = try renderNode(c, payload.return_type);
2919
2920 const fn_proto = try blk: {
2921 if (align_expr_opt == null and section_expr_opt == null and callconv_expr_opt == null) {
2922 if (params.items.len < 2)
2923 break :blk c.addNode(.{
2924 .tag = .fn_proto_simple,
2925 .main_token = fn_token,
2926 .data = .{ .opt_node_and_opt_node = .{
2927 if (params.items.len == 1) params.items[0].toOptional() else .none,
2928 return_type_expr.toOptional(),
2929 } },
2930 })
2931 else
2932 break :blk c.addNode(.{
2933 .tag = .fn_proto_multi,
2934 .main_token = fn_token,
2935 .data = .{ .extra_and_opt_node = .{
2936 try c.addExtra(span),
2937 return_type_expr.toOptional(),
2938 } },
2939 });
2940 }
2941 if (params.items.len < 2)
2942 break :blk c.addNode(.{
2943 .tag = .fn_proto_one,
2944 .main_token = fn_token,
2945 .data = .{
2946 .extra_and_opt_node = .{
2947 try c.addExtra(std.zig.Ast.Node.FnProtoOne{
2948 .param = if (params.items.len == 1) params.items[0].toOptional() else .none,
2949 .align_expr = .fromOptional(align_expr_opt),
2950 .addrspace_expr = .none, // TODO
2951 .section_expr = .fromOptional(section_expr_opt),
2952 .callconv_expr = .fromOptional(callconv_expr_opt),
2953 }),
2954 return_type_expr.toOptional(),
2955 },
2956 },
2957 })
2958 else
2959 break :blk c.addNode(.{
2960 .tag = .fn_proto,
2961 .main_token = fn_token,
2962 .data = .{
2963 .extra_and_opt_node = .{
2964 try c.addExtra(std.zig.Ast.Node.FnProto{
2965 .params_start = span.start,
2966 .params_end = span.end,
2967 .align_expr = .fromOptional(align_expr_opt),
2968 .addrspace_expr = .none, // TODO
2969 .section_expr = .fromOptional(section_expr_opt),
2970 .callconv_expr = .fromOptional(callconv_expr_opt),
2971 }),
2972 return_type_expr.toOptional(),
2973 },
2974 },
2975 });
2976 };
2977
2978 const payload_body = payload.body orelse {
2979 if (payload.is_extern) {
2980 _ = try c.addToken(.semicolon, ";");
2981 }
2982 return fn_proto;
2983 };
2984 const body = try renderNode(c, payload_body);
2985 return c.addNode(.{
2986 .tag = .fn_decl,
2987 .main_token = fn_token,
2988 .data = .{ .node_and_node = .{
2989 fn_proto, body,
2990 } },
2991 });
2992}
2993
2994fn renderMacroFunc(c: *Context, node: Node) !NodeIndex {
2995 const payload = node.castTag(.pub_inline_fn).?.data;
2996 _ = try c.addToken(.keyword_pub, "pub");
2997 _ = try c.addToken(.keyword_inline, "inline");
2998 const fn_token = try c.addToken(.keyword_fn, "fn");
2999 _ = try c.addIdentifier(payload.name);
3000
3001 const params = try renderParams(c, payload.params, false);
3002 defer params.deinit();
3003 var span: NodeSubRange = undefined;
3004 if (params.items.len > 1) span = try c.listToSpan(params.items);
3005
3006 const return_type_expr = try renderNodeGrouped(c, payload.return_type);
3007
3008 const fn_proto = blk: {
3009 if (params.items.len < 2) {
3010 break :blk try c.addNode(.{
3011 .tag = .fn_proto_simple,
3012 .main_token = fn_token,
3013 .data = .{ .opt_node_and_opt_node = .{
3014 if (params.items.len == 1) params.items[0].toOptional() else .none,
3015 return_type_expr.toOptional(),
3016 } },
3017 });
3018 } else {
3019 break :blk try c.addNode(.{
3020 .tag = .fn_proto_multi,
3021 .main_token = fn_token,
3022 .data = .{ .extra_and_opt_node = .{
3023 try c.addExtra(span),
3024 return_type_expr.toOptional(),
3025 } },
3026 });
3027 }
3028 };
3029 return c.addNode(.{
3030 .tag = .fn_decl,
3031 .main_token = fn_token,
3032 .data = .{ .node_and_node = .{
3033 fn_proto, try renderNode(c, payload.body),
3034 } },
3035 });
3036}
3037
3038fn renderParams(c: *Context, params: []Payload.Param, is_var_args: bool) !std.array_list.Managed(NodeIndex) {
3039 _ = try c.addToken(.l_paren, "(");
3040 var rendered = try std.array_list.Managed(NodeIndex).initCapacity(c.gpa, @max(params.len, 1));
3041 errdefer rendered.deinit();
3042
3043 for (params, 0..) |param, i| {
3044 if (i != 0) _ = try c.addToken(.comma, ",");
3045 if (param.is_noalias) _ = try c.addToken(.keyword_noalias, "noalias");
3046 if (param.name) |some| {
3047 _ = try c.addIdentifier(some);
3048 _ = try c.addToken(.colon, ":");
3049 }
3050 if (param.type.tag() == .@"anytype") {
3051 _ = try c.addToken(.keyword_anytype, "anytype");
3052 continue;
3053 }
3054 rendered.appendAssumeCapacity(try renderNode(c, param.type));
3055 }
3056 if (is_var_args) {
3057 if (params.len != 0) _ = try c.addToken(.comma, ",");
3058 _ = try c.addToken(.ellipsis3, "...");
3059 }
3060 _ = try c.addToken(.r_paren, ")");
3061
3062 return rendered;
3063}
lib/compiler/translate-c/builtins.zig created+76
...@@ -0,0 +1,76 @@
1const std = @import("std");
2
3const ast = @import("ast.zig");
4
5/// All builtins need to have a source so that macros can reference them
6/// but for some it is possible to directly call an equivalent Zig builtin
7/// which is preferrable.
8pub const Builtin = struct {
9 /// The name of the builtin in `c_builtins.zig`.
10 name: []const u8,
11 tag: ?ast.Node.Tag = null,
12};
13
14pub const map = std.StaticStringMap(Builtin).initComptime([_]struct { []const u8, Builtin }{
15 .{ "__builtin_abs", .{ .name = "abs" } },
16 .{ "__builtin_assume", .{ .name = "assume" } },
17 .{ "__builtin_bswap16", .{ .name = "bswap16", .tag = .byte_swap } },
18 .{ "__builtin_bswap32", .{ .name = "bswap32", .tag = .byte_swap } },
19 .{ "__builtin_bswap64", .{ .name = "bswap64", .tag = .byte_swap } },
20 .{ "__builtin_ceilf", .{ .name = "ceilf", .tag = .ceil } },
21 .{ "__builtin_ceil", .{ .name = "ceil", .tag = .ceil } },
22 .{ "__builtin_clz", .{ .name = "clz" } },
23 .{ "__builtin_constant_p", .{ .name = "constant_p" } },
24 .{ "__builtin_cosf", .{ .name = "cosf", .tag = .cos } },
25 .{ "__builtin_cos", .{ .name = "cos", .tag = .cos } },
26 .{ "__builtin_ctz", .{ .name = "ctz" } },
27 .{ "__builtin_exp2f", .{ .name = "exp2f", .tag = .exp2 } },
28 .{ "__builtin_exp2", .{ .name = "exp2", .tag = .exp2 } },
29 .{ "__builtin_expf", .{ .name = "expf", .tag = .exp } },
30 .{ "__builtin_exp", .{ .name = "exp", .tag = .exp } },
31 .{ "__builtin_expect", .{ .name = "expect" } },
32 .{ "__builtin_fabsf", .{ .name = "fabsf", .tag = .abs } },
33 .{ "__builtin_fabs", .{ .name = "fabs", .tag = .abs } },
34 .{ "__builtin_floorf", .{ .name = "floorf", .tag = .floor } },
35 .{ "__builtin_floor", .{ .name = "floor", .tag = .floor } },
36 .{ "__builtin_huge_valf", .{ .name = "huge_valf" } },
37 .{ "__builtin_inff", .{ .name = "inff" } },
38 .{ "__builtin_isinf_sign", .{ .name = "isinf_sign" } },
39 .{ "__builtin_isinf", .{ .name = "isinf" } },
40 .{ "__builtin_isnan", .{ .name = "isnan" } },
41 .{ "__builtin_labs", .{ .name = "labs" } },
42 .{ "__builtin_llabs", .{ .name = "llabs" } },
43 .{ "__builtin_log10f", .{ .name = "log10f", .tag = .log10 } },
44 .{ "__builtin_log10", .{ .name = "log10", .tag = .log10 } },
45 .{ "__builtin_log2f", .{ .name = "log2f", .tag = .log2 } },
46 .{ "__builtin_log2", .{ .name = "log2", .tag = .log2 } },
47 .{ "__builtin_logf", .{ .name = "logf", .tag = .log } },
48 .{ "__builtin_log", .{ .name = "log", .tag = .log } },
49 .{ "__builtin___memcpy_chk", .{ .name = "memcpy_chk" } },
50 .{ "__builtin_memcpy", .{ .name = "memcpy" } },
51 .{ "__builtin___memset_chk", .{ .name = "memset_chk" } },
52 .{ "__builtin_memset", .{ .name = "memset" } },
53 .{ "__builtin_mul_overflow", .{ .name = "mul_overflow" } },
54 .{ "__builtin_nanf", .{ .name = "nanf" } },
55 .{ "__builtin_object_size", .{ .name = "object_size" } },
56 .{ "__builtin_popcount", .{ .name = "popcount" } },
57 .{ "__builtin_roundf", .{ .name = "roundf", .tag = .round } },
58 .{ "__builtin_round", .{ .name = "round", .tag = .round } },
59 .{ "__builtin_signbitf", .{ .name = "signbitf" } },
60 .{ "__builtin_signbit", .{ .name = "signbit" } },
61 .{ "__builtin_sinf", .{ .name = "sinf", .tag = .sin } },
62 .{ "__builtin_sin", .{ .name = "sin", .tag = .sin } },
63 .{ "__builtin_sqrtf", .{ .name = "sqrtf", .tag = .sqrt } },
64 .{ "__builtin_sqrt", .{ .name = "sqrt", .tag = .sqrt } },
65 .{ "__builtin_strcmp", .{ .name = "strcmp" } },
66 .{ "__builtin_strlen", .{ .name = "strlen" } },
67 .{ "__builtin_truncf", .{ .name = "truncf", .tag = .trunc } },
68 .{ "__builtin_trunc", .{ .name = "trunc", .tag = .trunc } },
69 .{ "__builtin_unreachable", .{ .name = "unreachable", .tag = .@"unreachable" } },
70 .{ "__has_builtin", .{ .name = "has_builtin" } },
71
72 // __builtin_alloca_with_align is not currently implemented.
73 // It is used in a run and a translate test to ensure that non-implemented
74 // builtins are correctly demoted. If you implement __builtin_alloca_with_align,
75 // please update the tests to use a different non-implemented builtin.
76});
lib/compiler/translate-c/helpers.zig created+327
...@@ -0,0 +1,327 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const testing = std.testing;
4const math = std.math;
5
6const helpers = @import("helpers");
7
8const cast = helpers.cast;
9
10test cast {
11 var i = @as(i64, 10);
12
13 try testing.expect(cast(*u8, 16) == @as(*u8, @ptrFromInt(16)));
14 try testing.expect(cast(*u64, &i).* == @as(u64, 10));
15 try testing.expect(cast(*i64, @as(?*align(1) i64, &i)) == &i);
16
17 try testing.expect(cast(?*u8, 2) == @as(*u8, @ptrFromInt(2)));
18 try testing.expect(cast(?*i64, @as(*align(1) i64, &i)) == &i);
19 try testing.expect(cast(?*i64, @as(?*align(1) i64, &i)) == &i);
20
21 try testing.expectEqual(@as(u32, 4), cast(u32, @as(*u32, @ptrFromInt(4))));
22 try testing.expectEqual(@as(u32, 4), cast(u32, @as(?*u32, @ptrFromInt(4))));
23 try testing.expectEqual(@as(u32, 10), cast(u32, @as(u64, 10)));
24
25 try testing.expectEqual(@as(i32, @bitCast(@as(u32, 0x8000_0000))), cast(i32, @as(u32, 0x8000_0000)));
26
27 try testing.expectEqual(@as(*u8, @ptrFromInt(2)), cast(*u8, @as(*const u8, @ptrFromInt(2))));
28 try testing.expectEqual(@as(*u8, @ptrFromInt(2)), cast(*u8, @as(*volatile u8, @ptrFromInt(2))));
29
30 try testing.expectEqual(@as(?*anyopaque, @ptrFromInt(2)), cast(?*anyopaque, @as(*u8, @ptrFromInt(2))));
31
32 var foo: c_int = -1;
33 _ = &foo;
34 try testing.expect(cast(*anyopaque, -1) == @as(*anyopaque, @ptrFromInt(@as(usize, @bitCast(@as(isize, -1))))));
35 try testing.expect(cast(*anyopaque, foo) == @as(*anyopaque, @ptrFromInt(@as(usize, @bitCast(@as(isize, -1))))));
36 try testing.expect(cast(?*anyopaque, -1) == @as(?*anyopaque, @ptrFromInt(@as(usize, @bitCast(@as(isize, -1))))));
37 try testing.expect(cast(?*anyopaque, foo) == @as(?*anyopaque, @ptrFromInt(@as(usize, @bitCast(@as(isize, -1))))));
38
39 const FnPtr = ?*align(1) const fn (*anyopaque) void;
40 try testing.expect(cast(FnPtr, 0) == @as(FnPtr, @ptrFromInt(@as(usize, 0))));
41 try testing.expect(cast(FnPtr, foo) == @as(FnPtr, @ptrFromInt(@as(usize, @bitCast(@as(isize, -1))))));
42
43 const complexFunction = struct {
44 fn f(_: ?*anyopaque, _: c_uint, _: ?*const fn (?*anyopaque) callconv(.c) c_uint, _: ?*anyopaque, _: c_uint, _: [*c]c_uint) callconv(.c) usize {
45 return 0;
46 }
47 }.f;
48
49 const SDL_FunctionPointer = ?*const fn () callconv(.c) void;
50 const fn_ptr = cast(SDL_FunctionPointer, complexFunction);
51 try testing.expect(fn_ptr != null);
52}
53
54const sizeof = helpers.sizeof;
55
56test sizeof {
57 const S = extern struct { a: u32 };
58
59 const ptr_size = @sizeOf(*anyopaque);
60
61 try testing.expect(sizeof(u32) == 4);
62 try testing.expect(sizeof(@as(u32, 2)) == 4);
63 try testing.expect(sizeof(2) == @sizeOf(c_int));
64
65 try testing.expect(sizeof(2.0) == @sizeOf(f64));
66
67 try testing.expect(sizeof(S) == 4);
68
69 try testing.expect(sizeof([_]u32{ 4, 5, 6 }) == 12);
70 try testing.expect(sizeof([3]u32) == 12);
71 try testing.expect(sizeof([3:0]u32) == 16);
72 try testing.expect(sizeof(&[_]u32{ 4, 5, 6 }) == ptr_size);
73
74 try testing.expect(sizeof(*u32) == ptr_size);
75 try testing.expect(sizeof([*]u32) == ptr_size);
76 try testing.expect(sizeof([*c]u32) == ptr_size);
77 try testing.expect(sizeof(?*u32) == ptr_size);
78 try testing.expect(sizeof(?[*]u32) == ptr_size);
79 try testing.expect(sizeof(*anyopaque) == ptr_size);
80 try testing.expect(sizeof(*void) == ptr_size);
81 try testing.expect(sizeof(null) == ptr_size);
82
83 try testing.expect(sizeof("foobar") == 7);
84 try testing.expect(sizeof(&[_:0]u16{ 'f', 'o', 'o', 'b', 'a', 'r' }) == 14);
85 try testing.expect(sizeof(*const [4:0]u8) == 5);
86 try testing.expect(sizeof(*[4:0]u8) == ptr_size);
87 try testing.expect(sizeof([*]const [4:0]u8) == ptr_size);
88 try testing.expect(sizeof(*const *const [4:0]u8) == ptr_size);
89 try testing.expect(sizeof(*const [4]u8) == ptr_size);
90
91 if (false) { // TODO
92 try testing.expect(sizeof(&sizeof) == @sizeOf(@TypeOf(&sizeof)));
93 try testing.expect(sizeof(sizeof) == 1);
94 }
95
96 try testing.expect(sizeof(void) == 1);
97 try testing.expect(sizeof(anyopaque) == 1);
98}
99
100const promoteIntLiteral = helpers.promoteIntLiteral;
101
102test promoteIntLiteral {
103 const signed_hex = promoteIntLiteral(c_int, math.maxInt(c_int) + 1, .hex);
104 try testing.expectEqual(c_uint, @TypeOf(signed_hex));
105
106 if (math.maxInt(c_longlong) == math.maxInt(c_int)) return;
107
108 const signed_decimal = promoteIntLiteral(c_int, math.maxInt(c_int) + 1, .decimal);
109 const unsigned = promoteIntLiteral(c_uint, math.maxInt(c_uint) + 1, .hex);
110
111 if (math.maxInt(c_long) > math.maxInt(c_int)) {
112 try testing.expectEqual(c_long, @TypeOf(signed_decimal));
113 try testing.expectEqual(c_ulong, @TypeOf(unsigned));
114 } else {
115 try testing.expectEqual(c_longlong, @TypeOf(signed_decimal));
116 try testing.expectEqual(c_ulonglong, @TypeOf(unsigned));
117 }
118}
119
120const shuffleVectorIndex = helpers.shuffleVectorIndex;
121
122test shuffleVectorIndex {
123 const vector_len: usize = 4;
124
125 _ = shuffleVectorIndex(-1, vector_len);
126
127 try testing.expect(shuffleVectorIndex(0, vector_len) == 0);
128 try testing.expect(shuffleVectorIndex(1, vector_len) == 1);
129 try testing.expect(shuffleVectorIndex(2, vector_len) == 2);
130 try testing.expect(shuffleVectorIndex(3, vector_len) == 3);
131
132 try testing.expect(shuffleVectorIndex(4, vector_len) == -1);
133 try testing.expect(shuffleVectorIndex(5, vector_len) == -2);
134 try testing.expect(shuffleVectorIndex(6, vector_len) == -3);
135 try testing.expect(shuffleVectorIndex(7, vector_len) == -4);
136}
137
138const FlexibleArrayType = helpers.FlexibleArrayType;
139
140test FlexibleArrayType {
141 const Container = extern struct {
142 size: usize,
143 };
144
145 try testing.expectEqual(FlexibleArrayType(*Container, c_int), [*c]c_int);
146 try testing.expectEqual(FlexibleArrayType(*const Container, c_int), [*c]const c_int);
147 try testing.expectEqual(FlexibleArrayType(*volatile Container, c_int), [*c]volatile c_int);
148 try testing.expectEqual(FlexibleArrayType(*const volatile Container, c_int), [*c]const volatile c_int);
149}
150
151const signedRemainder = helpers.signedRemainder;
152
153test signedRemainder {
154 // TODO add test
155 return error.SkipZigTest;
156}
157
158const ArithmeticConversion = helpers.ArithmeticConversion;
159
160test ArithmeticConversion {
161 // Promotions not necessarily the same for other platforms
162 if (builtin.target.cpu.arch != .x86_64 or builtin.target.os.tag != .linux) return error.SkipZigTest;
163
164 const Test = struct {
165 /// Order of operands should not matter for arithmetic conversions
166 fn checkPromotion(comptime A: type, comptime B: type, comptime Expected: type) !void {
167 try std.testing.expect(ArithmeticConversion(A, B) == Expected);
168 try std.testing.expect(ArithmeticConversion(B, A) == Expected);
169 }
170 };
171
172 try Test.checkPromotion(c_longdouble, c_int, c_longdouble);
173 try Test.checkPromotion(c_int, f64, f64);
174 try Test.checkPromotion(f32, bool, f32);
175
176 try Test.checkPromotion(bool, c_short, c_int);
177 try Test.checkPromotion(c_int, c_int, c_int);
178 try Test.checkPromotion(c_short, c_int, c_int);
179
180 try Test.checkPromotion(c_int, c_long, c_long);
181
182 try Test.checkPromotion(c_ulonglong, c_uint, c_ulonglong);
183
184 try Test.checkPromotion(c_uint, c_int, c_uint);
185
186 try Test.checkPromotion(c_uint, c_long, c_long);
187
188 try Test.checkPromotion(c_ulong, c_longlong, c_ulonglong);
189
190 // stdint.h
191 try Test.checkPromotion(u8, i8, c_int);
192 try Test.checkPromotion(u16, i16, c_int);
193 try Test.checkPromotion(i32, c_int, c_int);
194 try Test.checkPromotion(u32, c_int, c_uint);
195 try Test.checkPromotion(i64, c_int, c_long);
196 try Test.checkPromotion(u64, c_int, c_ulong);
197 try Test.checkPromotion(isize, c_int, c_long);
198 try Test.checkPromotion(usize, c_int, c_ulong);
199}
200
201const F_SUFFIX = helpers.F_SUFFIX;
202
203test F_SUFFIX {
204 try testing.expect(@TypeOf(F_SUFFIX(1)) == f32);
205}
206
207const U_SUFFIX = helpers.U_SUFFIX;
208
209test U_SUFFIX {
210 try testing.expect(@TypeOf(U_SUFFIX(1)) == c_uint);
211 if (math.maxInt(c_ulong) > math.maxInt(c_uint)) {
212 try testing.expect(@TypeOf(U_SUFFIX(math.maxInt(c_uint) + 1)) == c_ulong);
213 }
214 if (math.maxInt(c_ulonglong) > math.maxInt(c_ulong)) {
215 try testing.expect(@TypeOf(U_SUFFIX(math.maxInt(c_ulong) + 1)) == c_ulonglong);
216 }
217}
218
219const L_SUFFIX = helpers.L_SUFFIX;
220
221test L_SUFFIX {
222 try testing.expect(@TypeOf(L_SUFFIX(1)) == c_long);
223 if (math.maxInt(c_long) > math.maxInt(c_int)) {
224 try testing.expect(@TypeOf(L_SUFFIX(math.maxInt(c_int) + 1)) == c_long);
225 }
226 if (math.maxInt(c_longlong) > math.maxInt(c_long)) {
227 try testing.expect(@TypeOf(L_SUFFIX(math.maxInt(c_long) + 1)) == c_longlong);
228 }
229}
230const UL_SUFFIX = helpers.UL_SUFFIX;
231
232test UL_SUFFIX {
233 try testing.expect(@TypeOf(UL_SUFFIX(1)) == c_ulong);
234 if (math.maxInt(c_ulonglong) > math.maxInt(c_ulong)) {
235 try testing.expect(@TypeOf(UL_SUFFIX(math.maxInt(c_ulong) + 1)) == c_ulonglong);
236 }
237}
238const LL_SUFFIX = helpers.LL_SUFFIX;
239
240test LL_SUFFIX {
241 try testing.expect(@TypeOf(LL_SUFFIX(1)) == c_longlong);
242}
243const ULL_SUFFIX = helpers.ULL_SUFFIX;
244
245test ULL_SUFFIX {
246 try testing.expect(@TypeOf(ULL_SUFFIX(1)) == c_ulonglong);
247}
248
249test "Extended C ABI casting" {
250 if (math.maxInt(c_long) > math.maxInt(c_char)) {
251 try testing.expect(@TypeOf(L_SUFFIX(@as(c_char, math.maxInt(c_char) - 1))) == c_long); // c_char
252 }
253 if (math.maxInt(c_long) > math.maxInt(c_short)) {
254 try testing.expect(@TypeOf(L_SUFFIX(@as(c_short, math.maxInt(c_short) - 1))) == c_long); // c_short
255 }
256
257 if (math.maxInt(c_long) > math.maxInt(c_ushort)) {
258 try testing.expect(@TypeOf(L_SUFFIX(@as(c_ushort, math.maxInt(c_ushort) - 1))) == c_long); //c_ushort
259 }
260
261 if (math.maxInt(c_long) > math.maxInt(c_int)) {
262 try testing.expect(@TypeOf(L_SUFFIX(@as(c_int, math.maxInt(c_int) - 1))) == c_long); // c_int
263 }
264
265 if (math.maxInt(c_long) > math.maxInt(c_uint)) {
266 try testing.expect(@TypeOf(L_SUFFIX(@as(c_uint, math.maxInt(c_uint) - 1))) == c_long); // c_uint
267 try testing.expect(@TypeOf(L_SUFFIX(math.maxInt(c_uint) + 1)) == c_long); // comptime_int -> c_long
268 }
269
270 if (math.maxInt(c_longlong) > math.maxInt(c_long)) {
271 try testing.expect(@TypeOf(L_SUFFIX(@as(c_long, math.maxInt(c_long) - 1))) == c_long); // c_long
272 try testing.expect(@TypeOf(L_SUFFIX(math.maxInt(c_long) + 1)) == c_longlong); // comptime_int -> c_longlong
273 }
274}
275
276const WL_CONTAINER_OF = helpers.WL_CONTAINER_OF;
277
278test WL_CONTAINER_OF {
279 const S = struct {
280 a: u32 = 0,
281 b: u32 = 0,
282 };
283 const x = S{};
284 const y = S{};
285 const ptr = WL_CONTAINER_OF(&x.b, &y, "b");
286 try testing.expectEqual(&x, ptr);
287}
288
289const CAST_OR_CALL = helpers.CAST_OR_CALL;
290
291test "CAST_OR_CALL casting" {
292 const arg: c_int = 1000;
293 const casted = CAST_OR_CALL(u8, arg);
294 try testing.expectEqual(cast(u8, arg), casted);
295
296 const S = struct {
297 x: u32 = 0,
298 };
299 var s: S = .{};
300 const casted_ptr = CAST_OR_CALL(*u8, &s);
301 try testing.expectEqual(cast(*u8, &s), casted_ptr);
302}
303
304test "CAST_OR_CALL calling" {
305 const Helper = struct {
306 var last_val: bool = false;
307 fn returnsVoid(val: bool) void {
308 last_val = val;
309 }
310 fn returnsBool(f: f32) bool {
311 return f > 0;
312 }
313 fn identity(self: c_uint) c_uint {
314 return self;
315 }
316 };
317
318 CAST_OR_CALL(Helper.returnsVoid, true);
319 try testing.expectEqual(true, Helper.last_val);
320 CAST_OR_CALL(Helper.returnsVoid, false);
321 try testing.expectEqual(false, Helper.last_val);
322
323 try testing.expectEqual(Helper.returnsBool(1), CAST_OR_CALL(Helper.returnsBool, @as(f32, 1)));
324 try testing.expectEqual(Helper.returnsBool(-1), CAST_OR_CALL(Helper.returnsBool, @as(f32, -1)));
325
326 try testing.expectEqual(Helper.identity(@as(c_uint, 100)), CAST_OR_CALL(Helper.identity, @as(c_uint, 100)));
327}
lib/compiler/translate-c/lib/c_builtins.zig deleted-301
...@@ -1,301 +0,0 @@
1const std = @import("std");
2
3/// Standard C Library bug: The absolute value of the most negative integer remains negative.
4pub inline fn abs(val: c_int) c_int {
5 return if (val == std.math.minInt(c_int)) val else @intCast(@abs(val));
6}
7
8pub inline fn assume(cond: bool) void {
9 if (!cond) unreachable;
10}
11
12pub inline fn bswap16(val: u16) u16 {
13 return @byteSwap(val);
14}
15
16pub inline fn bswap32(val: u32) u32 {
17 return @byteSwap(val);
18}
19
20pub inline fn bswap64(val: u64) u64 {
21 return @byteSwap(val);
22}
23
24pub inline fn ceilf(val: f32) f32 {
25 return @ceil(val);
26}
27
28pub inline fn ceil(val: f64) f64 {
29 return @ceil(val);
30}
31
32/// Returns the number of leading 0-bits in x, starting at the most significant bit position.
33/// In C if `val` is 0, the result is undefined; in zig it's the number of bits in a c_uint
34pub inline fn clz(val: c_uint) c_int {
35 @setRuntimeSafety(false);
36 return @as(c_int, @bitCast(@as(c_uint, @clz(val))));
37}
38
39pub inline fn constant_p(expr: anytype) c_int {
40 _ = expr;
41 return @intFromBool(false);
42}
43
44pub inline fn cosf(val: f32) f32 {
45 return @cos(val);
46}
47
48pub inline fn cos(val: f64) f64 {
49 return @cos(val);
50}
51
52/// Returns the number of trailing 0-bits in val, starting at the least significant bit position.
53/// In C if `val` is 0, the result is undefined; in zig it's the number of bits in a c_uint
54pub inline fn ctz(val: c_uint) c_int {
55 @setRuntimeSafety(false);
56 return @as(c_int, @bitCast(@as(c_uint, @ctz(val))));
57}
58
59pub inline fn exp2f(val: f32) f32 {
60 return @exp2(val);
61}
62
63pub inline fn exp2(val: f64) f64 {
64 return @exp2(val);
65}
66
67pub inline fn expf(val: f32) f32 {
68 return @exp(val);
69}
70
71pub inline fn exp(val: f64) f64 {
72 return @exp(val);
73}
74
75/// The return value of __builtin_expect is `expr`. `c` is the expected value
76/// of `expr` and is used as a hint to the compiler in C. Here it is unused.
77pub inline fn expect(expr: c_long, c: c_long) c_long {
78 _ = c;
79 return expr;
80}
81
82pub inline fn fabsf(val: f32) f32 {
83 return @abs(val);
84}
85
86pub inline fn fabs(val: f64) f64 {
87 return @abs(val);
88}
89
90pub inline fn floorf(val: f32) f32 {
91 return @floor(val);
92}
93
94pub inline fn floor(val: f64) f64 {
95 return @floor(val);
96}
97
98pub inline fn has_builtin(func: anytype) c_int {
99 _ = func;
100 return @intFromBool(true);
101}
102
103pub inline fn huge_valf() f32 {
104 return std.math.inf(f32);
105}
106
107pub inline fn inff() f32 {
108 return std.math.inf(f32);
109}
110
111/// Similar to isinf, except the return value is -1 for an argument of -Inf and 1 for an argument of +Inf.
112pub inline fn isinf_sign(x: anytype) c_int {
113 if (!std.math.isInf(x)) return 0;
114 return if (std.math.isPositiveInf(x)) 1 else -1;
115}
116
117pub inline fn isinf(x: anytype) c_int {
118 return @intFromBool(std.math.isInf(x));
119}
120
121pub inline fn isnan(x: anytype) c_int {
122 return @intFromBool(std.math.isNan(x));
123}
124
125/// Standard C Library bug: The absolute value of the most negative integer remains negative.
126pub inline fn labs(val: c_long) c_long {
127 return if (val == std.math.minInt(c_long)) val else @intCast(@abs(val));
128}
129
130/// Standard C Library bug: The absolute value of the most negative integer remains negative.
131pub inline fn llabs(val: c_longlong) c_longlong {
132 return if (val == std.math.minInt(c_longlong)) val else @intCast(@abs(val));
133}
134
135pub inline fn log10f(val: f32) f32 {
136 return @log10(val);
137}
138
139pub inline fn log10(val: f64) f64 {
140 return @log10(val);
141}
142
143pub inline fn log2f(val: f32) f32 {
144 return @log2(val);
145}
146
147pub inline fn log2(val: f64) f64 {
148 return @log2(val);
149}
150
151pub inline fn logf(val: f32) f32 {
152 return @log(val);
153}
154
155pub inline fn log(val: f64) f64 {
156 return @log(val);
157}
158
159pub inline fn memcpy_chk(
160 noalias dst: ?*anyopaque,
161 noalias src: ?*const anyopaque,
162 len: usize,
163 remaining: usize,
164) ?*anyopaque {
165 if (len > remaining) @panic("__builtin___memcpy_chk called with len > remaining");
166 if (len > 0) @memcpy(
167 @as([*]u8, @ptrCast(dst.?))[0..len],
168 @as([*]const u8, @ptrCast(src.?)),
169 );
170 return dst;
171}
172
173pub inline fn memcpy(
174 noalias dst: ?*anyopaque,
175 noalias src: ?*const anyopaque,
176 len: usize,
177) ?*anyopaque {
178 if (len > 0) @memcpy(
179 @as([*]u8, @ptrCast(dst.?))[0..len],
180 @as([*]const u8, @ptrCast(src.?)),
181 );
182 return dst;
183}
184
185pub inline fn memset_chk(
186 dst: ?*anyopaque,
187 val: c_int,
188 len: usize,
189 remaining: usize,
190) ?*anyopaque {
191 if (len > remaining) @panic("__builtin___memset_chk called with len > remaining");
192 const dst_cast = @as([*c]u8, @ptrCast(dst));
193 @memset(dst_cast[0..len], @as(u8, @bitCast(@as(i8, @truncate(val)))));
194 return dst;
195}
196
197pub inline fn memset(dst: ?*anyopaque, val: c_int, len: usize) ?*anyopaque {
198 const dst_cast = @as([*c]u8, @ptrCast(dst));
199 @memset(dst_cast[0..len], @as(u8, @bitCast(@as(i8, @truncate(val)))));
200 return dst;
201}
202
203pub fn mul_overflow(a: anytype, b: anytype, result: *@TypeOf(a, b)) c_int {
204 const res = @mulWithOverflow(a, b);
205 result.* = res[0];
206 return res[1];
207}
208
209/// returns a quiet NaN. Quiet NaNs have many representations; tagp is used to select one in an
210/// implementation-defined way.
211/// This implementation is based on the description for nan provided in the GCC docs at
212/// https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html#index-_005f_005fbuiltin_005fnan
213/// Comment is reproduced below:
214/// Since ISO C99 defines this function in terms of strtod, which we do not implement, a description
215/// of the parsing is in order.
216/// The string is parsed as by strtol; that is, the base is recognized by leading ‘0’ or ‘0x’ prefixes.
217/// The number parsed is placed in the significand such that the least significant bit of the number is
218/// at the least significant bit of the significand.
219/// The number is truncated to fit the significand field provided.
220/// The significand is forced to be a quiet NaN.
221///
222/// If tagp contains any non-numeric characters, the function returns a NaN whose significand is zero.
223/// If tagp is empty, the function returns a NaN whose significand is zero.
224pub inline fn nanf(tagp: []const u8) f32 {
225 const parsed = std.fmt.parseUnsigned(c_ulong, tagp, 0) catch 0;
226 const bits: u23 = @truncate(parsed); // single-precision float trailing significand is 23 bits
227 return @bitCast(@as(u32, bits) | @as(u32, @bitCast(std.math.nan(f32))));
228}
229
230pub inline fn object_size(ptr: ?*const anyopaque, ty: c_int) usize {
231 _ = ptr;
232 // clang semantics match gcc's: https://gcc.gnu.org/onlinedocs/gcc/Object-Size-Checking.html
233 // If it is not possible to determine which objects ptr points to at compile time,
234 // object_size should return (size_t) -1 for type 0 or 1 and (size_t) 0
235 // for type 2 or 3.
236 if (ty == 0 or ty == 1) return @as(usize, @bitCast(-@as(isize, 1)));
237 if (ty == 2 or ty == 3) return 0;
238 unreachable;
239}
240
241/// popcount of a c_uint will never exceed the capacity of a c_int
242pub inline fn popcount(val: c_uint) c_int {
243 @setRuntimeSafety(false);
244 return @as(c_int, @bitCast(@as(c_uint, @popCount(val))));
245}
246
247pub inline fn roundf(val: f32) f32 {
248 return @round(val);
249}
250
251pub inline fn round(val: f64) f64 {
252 return @round(val);
253}
254
255pub inline fn signbitf(val: f32) c_int {
256 return @intFromBool(std.math.signbit(val));
257}
258
259pub inline fn signbit(val: f64) c_int {
260 return @intFromBool(std.math.signbit(val));
261}
262
263pub inline fn sinf(val: f32) f32 {
264 return @sin(val);
265}
266
267pub inline fn sin(val: f64) f64 {
268 return @sin(val);
269}
270
271pub inline fn sqrtf(val: f32) f32 {
272 return @sqrt(val);
273}
274
275pub inline fn sqrt(val: f64) f64 {
276 return @sqrt(val);
277}
278
279pub inline fn strcmp(s1: [*c]const u8, s2: [*c]const u8) c_int {
280 return switch (std.mem.orderZ(u8, s1, s2)) {
281 .lt => -1,
282 .eq => 0,
283 .gt => 1,
284 };
285}
286
287pub inline fn strlen(s: [*c]const u8) usize {
288 return std.mem.sliceTo(s, 0).len;
289}
290
291pub inline fn truncf(val: f32) f32 {
292 return @trunc(val);
293}
294
295pub inline fn trunc(val: f64) f64 {
296 return @trunc(val);
297}
298
299pub inline fn @"unreachable"() noreturn {
300 unreachable;
301}
lib/compiler/translate-c/lib/helpers.zig deleted-413
...@@ -1,413 +0,0 @@
1const std = @import("std");
2
3/// "Usual arithmetic conversions" from C11 standard 6.3.1.8
4pub fn ArithmeticConversion(comptime A: type, comptime B: type) type {
5 if (A == c_longdouble or B == c_longdouble) return c_longdouble;
6 if (A == f80 or B == f80) return f80;
7 if (A == f64 or B == f64) return f64;
8 if (A == f32 or B == f32) return f32;
9
10 const A_Promoted = PromotedIntType(A);
11 const B_Promoted = PromotedIntType(B);
12 comptime {
13 std.debug.assert(integerRank(A_Promoted) >= integerRank(c_int));
14 std.debug.assert(integerRank(B_Promoted) >= integerRank(c_int));
15 }
16
17 if (A_Promoted == B_Promoted) return A_Promoted;
18
19 const a_signed = @typeInfo(A_Promoted).int.signedness == .signed;
20 const b_signed = @typeInfo(B_Promoted).int.signedness == .signed;
21
22 if (a_signed == b_signed) {
23 return if (integerRank(A_Promoted) > integerRank(B_Promoted)) A_Promoted else B_Promoted;
24 }
25
26 const SignedType = if (a_signed) A_Promoted else B_Promoted;
27 const UnsignedType = if (!a_signed) A_Promoted else B_Promoted;
28
29 if (integerRank(UnsignedType) >= integerRank(SignedType)) return UnsignedType;
30
31 if (std.math.maxInt(SignedType) >= std.math.maxInt(UnsignedType)) return SignedType;
32
33 return ToUnsigned(SignedType);
34}
35
36/// Integer promotion described in C11 6.3.1.1.2
37fn PromotedIntType(comptime T: type) type {
38 return switch (T) {
39 bool, c_short => c_int,
40 c_ushort => if (@sizeOf(c_ushort) == @sizeOf(c_int)) c_uint else c_int,
41 c_int, c_uint, c_long, c_ulong, c_longlong, c_ulonglong => T,
42 else => switch (@typeInfo(T)) {
43 .comptime_int => @compileError("Cannot promote `" ++ @typeName(T) ++ "`; a fixed-size number type is required"),
44 // promote to c_int if it can represent all values of T
45 .int => |int_info| if (int_info.bits < @bitSizeOf(c_int))
46 c_int
47 // otherwise, restore the original C type
48 else if (int_info.bits == @bitSizeOf(c_int))
49 if (int_info.signedness == .unsigned) c_uint else c_int
50 else if (int_info.bits <= @bitSizeOf(c_long))
51 if (int_info.signedness == .unsigned) c_ulong else c_long
52 else if (int_info.bits <= @bitSizeOf(c_longlong))
53 if (int_info.signedness == .unsigned) c_ulonglong else c_longlong
54 else
55 @compileError("Cannot promote `" ++ @typeName(T) ++ "`; a C ABI type is required"),
56 else => @compileError("Attempted to promote invalid type `" ++ @typeName(T) ++ "`"),
57 },
58 };
59}
60
61/// C11 6.3.1.1.1
62fn integerRank(comptime T: type) u8 {
63 return switch (T) {
64 bool => 0,
65 u8, i8 => 1,
66 c_short, c_ushort => 2,
67 c_int, c_uint => 3,
68 c_long, c_ulong => 4,
69 c_longlong, c_ulonglong => 5,
70 else => @compileError("integer rank not supported for `" ++ @typeName(T) ++ "`"),
71 };
72}
73
74fn ToUnsigned(comptime T: type) type {
75 return switch (T) {
76 c_int => c_uint,
77 c_long => c_ulong,
78 c_longlong => c_ulonglong,
79 else => @compileError("Cannot convert `" ++ @typeName(T) ++ "` to unsigned"),
80 };
81}
82
83/// Constructs a [*c] pointer with the const and volatile annotations
84/// from SelfType for pointing to a C flexible array of ElementType.
85pub fn FlexibleArrayType(comptime SelfType: type, comptime ElementType: type) type {
86 switch (@typeInfo(SelfType)) {
87 .pointer => |ptr| {
88 return @Type(.{ .pointer = .{
89 .size = .c,
90 .is_const = ptr.is_const,
91 .is_volatile = ptr.is_volatile,
92 .alignment = @alignOf(ElementType),
93 .address_space = .generic,
94 .child = ElementType,
95 .is_allowzero = true,
96 .sentinel_ptr = null,
97 } });
98 },
99 else => |info| @compileError("Invalid self type \"" ++ @tagName(info) ++ "\" for flexible array getter: " ++ @typeName(SelfType)),
100 }
101}
102
103/// Promote the type of an integer literal until it fits as C would.
104pub fn promoteIntLiteral(
105 comptime SuffixType: type,
106 comptime number: comptime_int,
107 comptime base: CIntLiteralBase,
108) PromoteIntLiteralReturnType(SuffixType, number, base) {
109 return number;
110}
111
112const CIntLiteralBase = enum { decimal, octal, hex };
113
114fn PromoteIntLiteralReturnType(comptime SuffixType: type, comptime number: comptime_int, comptime base: CIntLiteralBase) type {
115 const signed_decimal = [_]type{ c_int, c_long, c_longlong, c_ulonglong };
116 const signed_oct_hex = [_]type{ c_int, c_uint, c_long, c_ulong, c_longlong, c_ulonglong };
117 const unsigned = [_]type{ c_uint, c_ulong, c_ulonglong };
118
119 const list: []const type = if (@typeInfo(SuffixType).int.signedness == .unsigned)
120 &unsigned
121 else if (base == .decimal)
122 &signed_decimal
123 else
124 &signed_oct_hex;
125
126 var pos = std.mem.indexOfScalar(type, list, SuffixType).?;
127 while (pos < list.len) : (pos += 1) {
128 if (number >= std.math.minInt(list[pos]) and number <= std.math.maxInt(list[pos])) {
129 return list[pos];
130 }
131 }
132
133 @compileError("Integer literal is too large");
134}
135
136/// Convert from clang __builtin_shufflevector index to Zig @shuffle index
137/// clang requires __builtin_shufflevector index arguments to be integer constants.
138/// negative values for `this_index` indicate "don't care".
139/// clang enforces that `this_index` is less than the total number of vector elements
140/// See https://ziglang.org/documentation/master/#shuffle
141/// See https://clang.llvm.org/docs/LanguageExtensions.html#langext-builtin-shufflevector
142pub fn shuffleVectorIndex(comptime this_index: c_int, comptime source_vector_len: usize) i32 {
143 const positive_index = std.math.cast(usize, this_index) orelse return undefined;
144 if (positive_index < source_vector_len) return @as(i32, @intCast(this_index));
145 const b_index = positive_index - source_vector_len;
146 return ~@as(i32, @intCast(b_index));
147}
148
149/// C `%` operator for signed integers
150/// C standard states: "If the quotient a/b is representable, the expression (a/b)*b + a%b shall equal a"
151/// The quotient is not representable if denominator is zero, or if numerator is the minimum integer for
152/// the type and denominator is -1. C has undefined behavior for those two cases; this function has safety
153/// checked undefined behavior
154pub fn signedRemainder(numerator: anytype, denominator: anytype) @TypeOf(numerator, denominator) {
155 std.debug.assert(@typeInfo(@TypeOf(numerator, denominator)).int.signedness == .signed);
156 if (denominator > 0) return @rem(numerator, denominator);
157 return numerator - @divTrunc(numerator, denominator) * denominator;
158}
159
160/// Given a type and value, cast the value to the type as c would.
161pub fn cast(comptime DestType: type, target: anytype) DestType {
162 // this function should behave like transCCast in translate-c, except it's for macros
163 const SourceType = @TypeOf(target);
164 switch (@typeInfo(DestType)) {
165 .@"fn" => return castToPtr(*const DestType, SourceType, target),
166 .pointer => return castToPtr(DestType, SourceType, target),
167 .optional => |dest_opt| {
168 if (@typeInfo(dest_opt.child) == .pointer) {
169 return castToPtr(DestType, SourceType, target);
170 } else if (@typeInfo(dest_opt.child) == .@"fn") {
171 return castToPtr(?*const dest_opt.child, SourceType, target);
172 }
173 },
174 .int => {
175 switch (@typeInfo(SourceType)) {
176 .pointer => {
177 return castInt(DestType, @intFromPtr(target));
178 },
179 .optional => |opt| {
180 if (@typeInfo(opt.child) == .pointer) {
181 return castInt(DestType, @intFromPtr(target));
182 }
183 },
184 .int => {
185 return castInt(DestType, target);
186 },
187 .@"fn" => {
188 return castInt(DestType, @intFromPtr(&target));
189 },
190 .bool => {
191 return @intFromBool(target);
192 },
193 else => {},
194 }
195 },
196 .float => {
197 switch (@typeInfo(SourceType)) {
198 .int => return @as(DestType, @floatFromInt(target)),
199 .float => return @as(DestType, @floatCast(target)),
200 .bool => return @as(DestType, @floatFromInt(@intFromBool(target))),
201 else => {},
202 }
203 },
204 .@"union" => |info| {
205 inline for (info.fields) |field| {
206 if (field.type == SourceType) return @unionInit(DestType, field.name, target);
207 }
208
209 @compileError("cast to union type '" ++ @typeName(DestType) ++ "' from type '" ++ @typeName(SourceType) ++ "' which is not present in union");
210 },
211 .bool => return cast(usize, target) != 0,
212 else => {},
213 }
214
215 return @as(DestType, target);
216}
217
218fn castInt(comptime DestType: type, target: anytype) DestType {
219 const dest = @typeInfo(DestType).int;
220 const source = @typeInfo(@TypeOf(target)).int;
221
222 const Int = @Type(.{ .int = .{ .bits = dest.bits, .signedness = source.signedness } });
223
224 if (dest.bits < source.bits)
225 return @as(DestType, @bitCast(@as(Int, @truncate(target))))
226 else
227 return @as(DestType, @bitCast(@as(Int, target)));
228}
229
230fn castPtr(comptime DestType: type, target: anytype) DestType {
231 return @constCast(@volatileCast(@alignCast(@ptrCast(target))));
232}
233
234fn castToPtr(comptime DestType: type, comptime SourceType: type, target: anytype) DestType {
235 switch (@typeInfo(SourceType)) {
236 .int => {
237 return @as(DestType, @ptrFromInt(castInt(usize, target)));
238 },
239 .comptime_int => {
240 if (target < 0)
241 return @as(DestType, @ptrFromInt(@as(usize, @bitCast(@as(isize, @intCast(target))))))
242 else
243 return @as(DestType, @ptrFromInt(@as(usize, @intCast(target))));
244 },
245 .pointer => {
246 return castPtr(DestType, target);
247 },
248 .@"fn" => {
249 return castPtr(DestType, &target);
250 },
251 .optional => |target_opt| {
252 if (@typeInfo(target_opt.child) == .pointer) {
253 return castPtr(DestType, target);
254 }
255 },
256 else => {},
257 }
258
259 return @as(DestType, target);
260}
261
262/// Given a value returns its size as C's sizeof operator would.
263pub fn sizeof(target: anytype) usize {
264 const T: type = if (@TypeOf(target) == type) target else @TypeOf(target);
265 switch (@typeInfo(T)) {
266 .float, .int, .@"struct", .@"union", .array, .bool, .vector => return @sizeOf(T),
267 .@"fn" => {
268 // sizeof(main) in C returns 1
269 return 1;
270 },
271 .null => return @sizeOf(*anyopaque),
272 .void => {
273 // Note: sizeof(void) is 1 on clang/gcc and 0 on MSVC.
274 return 1;
275 },
276 .@"opaque" => {
277 if (T == anyopaque) {
278 // Note: sizeof(void) is 1 on clang/gcc and 0 on MSVC.
279 return 1;
280 } else {
281 @compileError("Cannot use C sizeof on opaque type " ++ @typeName(T));
282 }
283 },
284 .optional => |opt| {
285 if (@typeInfo(opt.child) == .pointer) {
286 return sizeof(opt.child);
287 } else {
288 @compileError("Cannot use C sizeof on non-pointer optional " ++ @typeName(T));
289 }
290 },
291 .pointer => |ptr| {
292 if (ptr.size == .slice) {
293 @compileError("Cannot use C sizeof on slice type " ++ @typeName(T));
294 }
295
296 // for strings, sizeof("a") returns 2.
297 // normal pointer decay scenarios from C are handled
298 // in the .array case above, but strings remain literals
299 // and are therefore always pointers, so they need to be
300 // specially handled here.
301 if (ptr.size == .one and ptr.is_const and @typeInfo(ptr.child) == .array) {
302 const array_info = @typeInfo(ptr.child).array;
303 if ((array_info.child == u8 or array_info.child == u16) and array_info.sentinel() == 0) {
304 // length of the string plus one for the null terminator.
305 return (array_info.len + 1) * @sizeOf(array_info.child);
306 }
307 }
308
309 // When zero sized pointers are removed, this case will no
310 // longer be reachable and can be deleted.
311 if (@sizeOf(T) == 0) {
312 return @sizeOf(*anyopaque);
313 }
314
315 return @sizeOf(T);
316 },
317 .comptime_float => return @sizeOf(f64), // TODO c_double #3999
318 .comptime_int => {
319 // TODO to get the correct result we have to translate
320 // `1073741824 * 4` as `int(1073741824) *% int(4)` since
321 // sizeof(1073741824 * 4) != sizeof(4294967296).
322
323 // TODO test if target fits in int, long or long long
324 return @sizeOf(c_int);
325 },
326 else => @compileError("__helpers.sizeof does not support type " ++ @typeName(T)),
327 }
328}
329
330pub fn div(a: anytype, b: anytype) ArithmeticConversion(@TypeOf(a), @TypeOf(b)) {
331 const ResType = ArithmeticConversion(@TypeOf(a), @TypeOf(b));
332 const a_casted = cast(ResType, a);
333 const b_casted = cast(ResType, b);
334 switch (@typeInfo(ResType)) {
335 .float => return a_casted / b_casted,
336 .int => return @divTrunc(a_casted, b_casted),
337 else => unreachable,
338 }
339}
340
341pub fn rem(a: anytype, b: anytype) ArithmeticConversion(@TypeOf(a), @TypeOf(b)) {
342 const ResType = ArithmeticConversion(@TypeOf(a), @TypeOf(b));
343 const a_casted = cast(ResType, a);
344 const b_casted = cast(ResType, b);
345 switch (@typeInfo(ResType)) {
346 .int => {
347 if (@typeInfo(ResType).int.signedness == .signed) {
348 return signedRemainder(a_casted, b_casted);
349 } else {
350 return a_casted % b_casted;
351 }
352 },
353 else => unreachable,
354 }
355}
356
357/// A 2-argument function-like macro defined as #define FOO(A, B) (A)(B)
358/// could be either: cast B to A, or call A with the value B.
359pub fn CAST_OR_CALL(a: anytype, b: anytype) switch (@typeInfo(@TypeOf(a))) {
360 .type => a,
361 .@"fn" => |fn_info| fn_info.return_type orelse void,
362 else => |info| @compileError("Unexpected argument type: " ++ @tagName(info)),
363} {
364 switch (@typeInfo(@TypeOf(a))) {
365 .type => return cast(a, b),
366 .@"fn" => return a(b),
367 else => unreachable, // return type will be a compile error otherwise
368 }
369}
370
371pub inline fn DISCARD(x: anytype) void {
372 _ = x;
373}
374
375pub fn F_SUFFIX(comptime f: comptime_float) f32 {
376 return @as(f32, f);
377}
378
379fn L_SUFFIX_ReturnType(comptime number: anytype) type {
380 switch (@typeInfo(@TypeOf(number))) {
381 .int, .comptime_int => return @TypeOf(promoteIntLiteral(c_long, number, .decimal)),
382 .float, .comptime_float => return c_longdouble,
383 else => @compileError("Invalid value for L suffix"),
384 }
385}
386
387pub fn L_SUFFIX(comptime number: anytype) L_SUFFIX_ReturnType(number) {
388 switch (@typeInfo(@TypeOf(number))) {
389 .int, .comptime_int => return promoteIntLiteral(c_long, number, .decimal),
390 .float, .comptime_float => @compileError("TODO: c_longdouble initialization from comptime_float not supported"),
391 else => @compileError("Invalid value for L suffix"),
392 }
393}
394
395pub fn LL_SUFFIX(comptime n: comptime_int) @TypeOf(promoteIntLiteral(c_longlong, n, .decimal)) {
396 return promoteIntLiteral(c_longlong, n, .decimal);
397}
398
399pub fn U_SUFFIX(comptime n: comptime_int) @TypeOf(promoteIntLiteral(c_uint, n, .decimal)) {
400 return promoteIntLiteral(c_uint, n, .decimal);
401}
402
403pub fn UL_SUFFIX(comptime n: comptime_int) @TypeOf(promoteIntLiteral(c_ulong, n, .decimal)) {
404 return promoteIntLiteral(c_ulong, n, .decimal);
405}
406
407pub fn ULL_SUFFIX(comptime n: comptime_int) @TypeOf(promoteIntLiteral(c_ulonglong, n, .decimal)) {
408 return promoteIntLiteral(c_ulonglong, n, .decimal);
409}
410
411pub fn WL_CONTAINER_OF(ptr: anytype, sample: anytype, comptime member: []const u8) @TypeOf(sample) {
412 return @fieldParentPtr(member, ptr);
413}
lib/compiler/translate-c/main.zig created+251
...@@ -0,0 +1,251 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const mem = std.mem;
4const process = std.process;
5const aro = @import("aro");
6const Translator = @import("Translator.zig");
7
8const fast_exit = @import("builtin").mode != .Debug;
9
10var general_purpose_allocator: std.heap.GeneralPurposeAllocator(.{}) = .init;
11
12pub fn main() u8 {
13 const gpa = general_purpose_allocator.allocator();
14 defer _ = general_purpose_allocator.deinit();
15
16 var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);
17 defer arena_instance.deinit();
18 const arena = arena_instance.allocator();
19
20 const args = process.argsAlloc(arena) catch {
21 std.debug.print("ran out of memory allocating arguments\n", .{});
22 if (fast_exit) process.exit(1);
23 return 1;
24 };
25
26 var stderr_buf: [1024]u8 = undefined;
27 var stderr = std.fs.File.stderr().writer(&stderr_buf);
28 var diagnostics: aro.Diagnostics = .{
29 .output = .{ .to_writer = .{
30 .color = .detect(stderr.file),
31 .writer = &stderr.interface,
32 } },
33 };
34
35 var comp = aro.Compilation.initDefault(gpa, arena, &diagnostics, std.fs.cwd()) catch |err| switch (err) {
36 error.OutOfMemory => {
37 std.debug.print("ran out of memory initializing C compilation\n", .{});
38 if (fast_exit) process.exit(1);
39 return 1;
40 },
41 };
42 defer comp.deinit();
43
44 const exe_name = std.fs.selfExePathAlloc(gpa) catch {
45 std.debug.print("unable to find translate-c executable path\n", .{});
46 if (fast_exit) process.exit(1);
47 return 1;
48 };
49 defer gpa.free(exe_name);
50
51 var driver: aro.Driver = .{ .comp = &comp, .diagnostics = &diagnostics, .aro_name = exe_name };
52 defer driver.deinit();
53
54 var toolchain: aro.Toolchain = .{ .driver = &driver, .filesystem = .{ .real = comp.cwd } };
55 defer toolchain.deinit();
56
57 translate(&driver, &toolchain, args) catch |err| switch (err) {
58 error.OutOfMemory => {
59 std.debug.print("ran out of memory translating\n", .{});
60 if (fast_exit) process.exit(1);
61 return 1;
62 },
63 error.FatalError => {
64 if (fast_exit) process.exit(1);
65 return 1;
66 },
67 error.WriteFailed => {
68 std.debug.print("unable to write to stdout\n", .{});
69 if (fast_exit) process.exit(1);
70 return 1;
71 },
72 };
73 if (fast_exit) process.exit(@intFromBool(comp.diagnostics.errors != 0));
74 return @intFromBool(comp.diagnostics.errors != 0);
75}
76
77pub const usage =
78 \\Usage {s}: [options] file [CC options]
79 \\
80 \\Options:
81 \\ --help Print this message
82 \\ --version Print translate-c version
83 \\ -fmodule-libs Import libraries as modules
84 \\ -fno-module-libs (default) Install libraries next to output file
85 \\
86 \\
87;
88
89fn translate(d: *aro.Driver, tc: *aro.Toolchain, args: [][:0]u8) !void {
90 const gpa = d.comp.gpa;
91
92 var module_libs = false;
93
94 const aro_args = args: {
95 var i: usize = 0;
96 for (args) |arg| {
97 args[i] = arg;
98 if (mem.eql(u8, arg, "--help")) {
99 var stdout_buf: [512]u8 = undefined;
100 var stdout = std.fs.File.stdout().writer(&stdout_buf);
101 try stdout.interface.print(usage, .{args[0]});
102 try stdout.interface.flush();
103 return;
104 } else if (mem.eql(u8, arg, "--version")) {
105 var stdout_buf: [512]u8 = undefined;
106 var stdout = std.fs.File.stdout().writer(&stdout_buf);
107 // TODO add version
108 try stdout.interface.writeAll("0.0.0-dev\n");
109 try stdout.interface.flush();
110 return;
111 } else if (mem.eql(u8, arg, "-fmodule-libs")) {
112 module_libs = true;
113 } else if (mem.eql(u8, arg, "-fno-module-libs")) {
114 module_libs = false;
115 } else {
116 i += 1;
117 }
118 }
119 break :args args[0..i];
120 };
121 const user_macros = macros: {
122 var macro_buf: std.ArrayListUnmanaged(u8) = .empty;
123 defer macro_buf.deinit(gpa);
124
125 try macro_buf.appendSlice(gpa, "#define __TRANSLATE_C__ 1\n");
126
127 var discard_buf: [256]u8 = undefined;
128 var discarding: std.io.Writer.Discarding = .init(&discard_buf);
129 assert(!try d.parseArgs(&discarding.writer, &macro_buf, aro_args));
130 if (macro_buf.items.len > std.math.maxInt(u32)) {
131 return d.fatal("user provided macro source exceeded max size", .{});
132 }
133
134 const content = try macro_buf.toOwnedSlice(gpa);
135 errdefer gpa.free(content);
136
137 break :macros try d.comp.addSourceFromOwnedBuffer("<command line>", content, .user);
138 };
139
140 if (d.inputs.items.len != 1) {
141 return d.fatal("expected exactly one input file", .{});
142 }
143 const source = d.inputs.items[0];
144
145 tc.discover() catch |er| switch (er) {
146 error.OutOfMemory => return error.OutOfMemory,
147 error.TooManyMultilibs => return d.fatal("found more than one multilib with the same priority", .{}),
148 };
149 tc.defineSystemIncludes() catch |er| switch (er) {
150 error.OutOfMemory => return error.OutOfMemory,
151 error.AroIncludeNotFound => return d.fatal("unable to find Aro builtin headers", .{}),
152 };
153
154 const builtin_macros = d.comp.generateBuiltinMacros(.include_system_defines) catch |err| switch (err) {
155 error.FileTooBig => return d.fatal("builtin macro source exceeded max size", .{}),
156 else => |e| return e,
157 };
158
159 var pp = try aro.Preprocessor.initDefault(d.comp);
160 defer pp.deinit();
161
162 try pp.preprocessSources(&.{ source, builtin_macros, user_macros });
163
164 var c_tree = try pp.parse();
165 defer c_tree.deinit();
166
167 if (d.diagnostics.errors != 0) {
168 if (fast_exit) process.exit(1);
169 return error.FatalError;
170 }
171
172 const rendered_zig = try Translator.translate(.{
173 .gpa = gpa,
174 .comp = d.comp,
175 .pp = &pp,
176 .tree = &c_tree,
177 .module_libs = module_libs,
178 });
179 defer gpa.free(rendered_zig);
180
181 var close_out_file = false;
182 var out_file_path: []const u8 = "<stdout>";
183 var out_file: std.fs.File = .stdout();
184 defer if (close_out_file) out_file.close();
185
186 if (d.output_name) |path| blk: {
187 if (std.mem.eql(u8, path, "-")) break :blk;
188 if (std.fs.path.dirname(path)) |dirname| {
189 std.fs.cwd().makePath(dirname) catch |err|
190 return d.fatal("failed to create path to '{s}': {s}", .{ path, aro.Driver.errorDescription(err) });
191 }
192 out_file = std.fs.cwd().createFile(path, .{}) catch |err| {
193 return d.fatal("failed to create output file '{s}': {s}", .{ path, aro.Driver.errorDescription(err) });
194 };
195 close_out_file = true;
196 out_file_path = path;
197 }
198
199 var out_buf: [4096]u8 = undefined;
200 var out_writer = out_file.writer(&out_buf);
201 out_writer.interface.writeAll(rendered_zig) catch
202 return d.fatal("failed to write result to '{s}': {s}", .{ out_file_path, aro.Driver.errorDescription(out_writer.err.?) });
203
204 if (!module_libs) {
205 const dest_path = if (d.output_name) |path| std.fs.path.dirname(path) else null;
206 installLibs(d, dest_path) catch |err|
207 return d.fatal("failed to install library files: {s}", .{aro.Driver.errorDescription(err)});
208 }
209
210 if (fast_exit) process.exit(0);
211}
212
213fn installLibs(d: *aro.Driver, dest_path: ?[]const u8) !void {
214 const gpa = d.comp.gpa;
215 const cwd = std.fs.cwd();
216
217 const self_exe_path = try std.fs.selfExePathAlloc(gpa);
218 defer gpa.free(self_exe_path);
219
220 var cur_dir: []const u8 = self_exe_path;
221 while (std.fs.path.dirname(cur_dir)) |dirname| : (cur_dir = dirname) {
222 var base_dir = cwd.openDir(dirname, .{}) catch continue;
223 defer base_dir.close();
224
225 var lib_dir = base_dir.openDir("lib", .{}) catch continue;
226 defer lib_dir.close();
227
228 lib_dir.access("c_builtins.zig", .{}) catch continue;
229
230 {
231 const install_path = try std.fs.path.join(gpa, &.{ dest_path orelse "", "c_builtins.zig" });
232 defer gpa.free(install_path);
233 try lib_dir.copyFile("c_builtins.zig", cwd, install_path, .{});
234 }
235 {
236 const install_path = try std.fs.path.join(gpa, &.{ dest_path orelse "", "helpers.zig" });
237 defer gpa.free(install_path);
238 try lib_dir.copyFile("helpers.zig", cwd, install_path, .{});
239 }
240 return;
241 }
242 return error.FileNotFound;
243}
244
245comptime {
246 if (@import("builtin").is_test) {
247 _ = Translator;
248 _ = @import("helpers.zig");
249 _ = @import("PatternList.zig");
250 }
251}
lib/compiler/translate-c/src/MacroTranslator.zig deleted-1307
...@@ -1,1307 +0,0 @@
1const std = @import("std");
2const math = std.math;
3const mem = std.mem;
4const assert = std.debug.assert;
5
6const aro = @import("aro");
7const CToken = aro.Tokenizer.Token;
8
9const ast = @import("ast.zig");
10const builtins = @import("builtins.zig");
11const ZigNode = ast.Node;
12const ZigTag = ZigNode.Tag;
13const Scope = @import("Scope.zig");
14const Translator = @import("Translator.zig");
15
16const Error = Translator.Error;
17pub const ParseError = Error || error{ParseError};
18
19const MacroTranslator = @This();
20
21t: *Translator,
22macro: aro.Preprocessor.Macro,
23name: []const u8,
24
25tokens: []const CToken,
26source: []const u8,
27i: usize = 0,
28/// If an object macro references a global var it needs to be converted into
29/// an inline function.
30refs_var_decl: bool = false,
31
32fn peek(mt: *MacroTranslator) CToken.Id {
33 if (mt.i >= mt.tokens.len) return .eof;
34 return mt.tokens[mt.i].id;
35}
36
37fn eat(mt: *MacroTranslator, expected_id: CToken.Id) bool {
38 if (mt.peek() == expected_id) {
39 mt.i += 1;
40 return true;
41 }
42 return false;
43}
44
45fn expect(mt: *MacroTranslator, expected_id: CToken.Id) ParseError!void {
46 const next_id = mt.peek();
47 if (next_id != expected_id and !(expected_id == .identifier and next_id == .extended_identifier)) {
48 try mt.fail(
49 "unable to translate C expr: expected '{s}' instead got '{s}'",
50 .{ expected_id.symbol(), next_id.symbol() },
51 );
52 return error.ParseError;
53 }
54 mt.i += 1;
55}
56
57fn fail(mt: *MacroTranslator, comptime fmt: []const u8, args: anytype) !void {
58 return mt.t.failDeclExtra(&mt.t.global_scope.base, mt.macro.loc, mt.name, fmt, args);
59}
60
61fn tokSlice(mt: *const MacroTranslator) []const u8 {
62 const tok = mt.tokens[mt.i];
63 return mt.source[tok.start..tok.end];
64}
65
66pub fn transFnMacro(mt: *MacroTranslator) ParseError!void {
67 var block_scope = try Scope.Block.init(mt.t, &mt.t.global_scope.base, false);
68 defer block_scope.deinit();
69 const scope = &block_scope.base;
70
71 const fn_params = try mt.t.arena.alloc(ast.Payload.Param, mt.macro.params.len);
72 for (fn_params, mt.macro.params) |*param, param_name| {
73 const mangled_name = try block_scope.makeMangledName(param_name);
74 param.* = .{
75 .is_noalias = false,
76 .name = mangled_name,
77 .type = ZigTag.@"anytype".init(),
78 };
79 try block_scope.discardVariable(mangled_name);
80 }
81
82 const expr = try mt.parseCExpr(scope);
83 const last = mt.peek();
84 if (last != .eof)
85 return mt.fail("unable to translate C expr: unexpected token '{s}'", .{last.symbol()});
86
87 const typeof_arg = if (expr.castTag(.block)) |some| blk: {
88 const stmts = some.data.stmts;
89 const blk_last = stmts[stmts.len - 1];
90 const br = blk_last.castTag(.break_val).?;
91 break :blk br.data.val;
92 } else expr;
93
94 const return_type = ret: {
95 if (typeof_arg.castTag(.helper_call)) |some| {
96 if (std.mem.eql(u8, some.data.name, "cast")) {
97 break :ret some.data.args[0];
98 }
99 }
100 if (typeof_arg.castTag(.std_mem_zeroinit)) |some| break :ret some.data.lhs;
101 if (typeof_arg.castTag(.std_mem_zeroes)) |some| break :ret some.data;
102 break :ret try ZigTag.typeof.create(mt.t.arena, typeof_arg);
103 };
104
105 const return_expr = try ZigTag.@"return".create(mt.t.arena, expr);
106 try block_scope.statements.append(mt.t.gpa, return_expr);
107
108 const fn_decl = try ZigTag.pub_inline_fn.create(mt.t.arena, .{
109 .name = mt.name,
110 .params = fn_params,
111 .return_type = return_type,
112 .body = try block_scope.complete(),
113 });
114 try mt.t.addTopLevelDecl(mt.name, fn_decl);
115}
116
117pub fn transMacro(mt: *MacroTranslator) ParseError!void {
118 const scope = &mt.t.global_scope.base;
119
120 // Check if the macro only uses other blank macros.
121 while (true) {
122 switch (mt.peek()) {
123 .identifier, .extended_identifier => {
124 if (mt.t.global_scope.blank_macros.contains(mt.tokSlice())) {
125 mt.i += 1;
126 continue;
127 }
128 },
129 .eof, .nl => {
130 try mt.t.global_scope.blank_macros.put(mt.t.gpa, mt.name, {});
131 const init_node = try ZigTag.string_literal.create(mt.t.arena, "\"\"");
132 const var_decl = try ZigTag.pub_var_simple.create(mt.t.arena, .{ .name = mt.name, .init = init_node });
133 try mt.t.addTopLevelDecl(mt.name, var_decl);
134 return;
135 },
136 else => {},
137 }
138 break;
139 }
140
141 const init_node = try mt.parseCExpr(scope);
142 const last = mt.peek();
143 if (last != .eof)
144 return mt.fail("unable to translate C expr: unexpected token '{s}'", .{last.symbol()});
145
146 const node = node: {
147 const var_decl = try ZigTag.pub_var_simple.create(mt.t.arena, .{ .name = mt.name, .init = init_node });
148
149 if (mt.t.getFnProto(var_decl)) |proto_node| {
150 // If a macro aliases a global variable which is a function pointer, we conclude that
151 // the macro is intended to represent a function that assumes the function pointer
152 // variable is non-null and calls it.
153 break :node try mt.createMacroFn(mt.name, var_decl, proto_node);
154 } else if (mt.refs_var_decl) {
155 const return_type = try ZigTag.typeof.create(mt.t.arena, init_node);
156 const return_expr = try ZigTag.@"return".create(mt.t.arena, init_node);
157 const block = try ZigTag.block_single.create(mt.t.arena, return_expr);
158
159 const loc_str = try mt.t.locStr(mt.macro.loc);
160 const value = try std.fmt.allocPrint(mt.t.arena, "\n// {s}: warning: macro '{s}' contains a runtime value, translated to function", .{ loc_str, mt.name });
161 try scope.appendNode(try ZigTag.warning.create(mt.t.arena, value));
162
163 break :node try ZigTag.pub_inline_fn.create(mt.t.arena, .{
164 .name = mt.name,
165 .params = &.{},
166 .return_type = return_type,
167 .body = block,
168 });
169 }
170
171 break :node var_decl;
172 };
173
174 try mt.t.addTopLevelDecl(mt.name, node);
175}
176
177fn createMacroFn(mt: *MacroTranslator, name: []const u8, ref: ZigNode, proto_alias: *ast.Payload.Func) !ZigNode {
178 var fn_params = std.ArrayList(ast.Payload.Param).init(mt.t.gpa);
179 defer fn_params.deinit();
180
181 var block_scope = try Scope.Block.init(mt.t, &mt.t.global_scope.base, false);
182 defer block_scope.deinit();
183
184 for (proto_alias.data.params) |param| {
185 const param_name = try block_scope.makeMangledName(param.name orelse "arg");
186
187 try fn_params.append(.{
188 .name = param_name,
189 .type = param.type,
190 .is_noalias = param.is_noalias,
191 });
192 }
193
194 const init = if (ref.castTag(.var_decl)) |v|
195 v.data.init.?
196 else if (ref.castTag(.var_simple) orelse ref.castTag(.pub_var_simple)) |v|
197 v.data.init
198 else
199 unreachable;
200
201 const unwrap_expr = try ZigTag.unwrap.create(mt.t.arena, init);
202 const args = try mt.t.arena.alloc(ZigNode, fn_params.items.len);
203 for (fn_params.items, 0..) |param, i| {
204 args[i] = try ZigTag.identifier.create(mt.t.arena, param.name.?);
205 }
206 const call_expr = try ZigTag.call.create(mt.t.arena, .{
207 .lhs = unwrap_expr,
208 .args = args,
209 });
210 const return_expr = try ZigTag.@"return".create(mt.t.arena, call_expr);
211 const block = try ZigTag.block_single.create(mt.t.arena, return_expr);
212
213 return ZigTag.pub_inline_fn.create(mt.t.arena, .{
214 .name = name,
215 .params = try mt.t.arena.dupe(ast.Payload.Param, fn_params.items),
216 .return_type = proto_alias.data.return_type,
217 .body = block,
218 });
219}
220
221fn parseCExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
222 // TODO parseCAssignExpr here
223 var block_scope = try Scope.Block.init(mt.t, scope, true);
224 defer block_scope.deinit();
225
226 const node = try mt.parseCCondExpr(&block_scope.base);
227 if (!mt.eat(.comma)) return node;
228
229 var last = node;
230 while (true) {
231 // suppress result
232 const ignore = try ZigTag.discard.create(mt.t.arena, .{ .should_skip = false, .value = last });
233 try block_scope.statements.append(mt.t.gpa, ignore);
234
235 last = try mt.parseCCondExpr(&block_scope.base);
236 if (!mt.eat(.comma)) break;
237 }
238
239 const break_node = try ZigTag.break_val.create(mt.t.arena, .{
240 .label = block_scope.label,
241 .val = last,
242 });
243 try block_scope.statements.append(mt.t.gpa, break_node);
244 return try block_scope.complete();
245}
246
247fn parseCNumLit(mt: *MacroTranslator) ParseError!ZigNode {
248 const lit_bytes = mt.tokSlice();
249 mt.i += 1;
250
251 var bytes = try std.ArrayListUnmanaged(u8).initCapacity(mt.t.arena, lit_bytes.len + 3);
252
253 const prefix = aro.Tree.Token.NumberPrefix.fromString(lit_bytes);
254 switch (prefix) {
255 .binary => bytes.appendSliceAssumeCapacity("0b"),
256 .octal => bytes.appendSliceAssumeCapacity("0o"),
257 .hex => bytes.appendSliceAssumeCapacity("0x"),
258 .decimal => {},
259 }
260
261 const after_prefix = lit_bytes[prefix.stringLen()..];
262 const after_int = for (after_prefix, 0..) |c, i| switch (c) {
263 '.' => {
264 if (i == 0) {
265 bytes.appendAssumeCapacity('0');
266 }
267 break after_prefix[i..];
268 },
269 'e', 'E' => {
270 if (prefix != .hex) break after_prefix[i..];
271 bytes.appendAssumeCapacity(c);
272 },
273 'p', 'P' => break after_prefix[i..],
274 '0'...'9', 'a'...'d', 'A'...'D', 'f', 'F' => {
275 if (!prefix.digitAllowed(c)) break after_prefix[i..];
276 bytes.appendAssumeCapacity(c);
277 },
278 '\'' => {
279 bytes.appendAssumeCapacity('_');
280 },
281 else => break after_prefix[i..],
282 } else "";
283
284 const after_frac = frac: {
285 if (after_int.len == 0 or after_int[0] != '.') break :frac after_int;
286 bytes.appendAssumeCapacity('.');
287 for (after_int[1..], 1..) |c, i| {
288 if (c == '\'') {
289 bytes.appendAssumeCapacity('_');
290 continue;
291 }
292 if (!prefix.digitAllowed(c)) break :frac after_int[i..];
293 bytes.appendAssumeCapacity(c);
294 }
295 break :frac "";
296 };
297
298 const suffix_str = exponent: {
299 if (after_frac.len == 0) break :exponent after_frac;
300 switch (after_frac[0]) {
301 'e', 'E' => {},
302 'p', 'P' => if (prefix != .hex) break :exponent after_frac,
303 else => break :exponent after_frac,
304 }
305 bytes.appendAssumeCapacity(after_frac[0]);
306 for (after_frac[1..], 1..) |c, i| switch (c) {
307 '+', '-', '0'...'9' => {
308 bytes.appendAssumeCapacity(c);
309 },
310 '\'' => {
311 bytes.appendAssumeCapacity('_');
312 },
313 else => break :exponent after_frac[i..],
314 };
315 break :exponent "";
316 };
317
318 const is_float = after_int.len != suffix_str.len;
319 const suffix = aro.Tree.Token.NumberSuffix.fromString(suffix_str, if (is_float) .float else .int) orelse {
320 try mt.fail("invalid number suffix: '{s}'", .{suffix_str});
321 return error.ParseError;
322 };
323 if (suffix.isImaginary()) {
324 try mt.fail("TODO: imaginary literals", .{});
325 return error.ParseError;
326 }
327 if (suffix.isBitInt()) {
328 try mt.fail("TODO: _BitInt literals", .{});
329 return error.ParseError;
330 }
331
332 if (is_float) {
333 const type_node = try ZigTag.type.create(mt.t.arena, switch (suffix) {
334 .F16 => "f16",
335 .F => "f32",
336 .None => "f64",
337 .L => "c_longdouble",
338 .W => "f80",
339 .Q, .F128 => "f128",
340 else => unreachable,
341 });
342 const rhs = try ZigTag.float_literal.create(mt.t.arena, bytes.items);
343 return ZigTag.as.create(mt.t.arena, .{ .lhs = type_node, .rhs = rhs });
344 } else {
345 const type_node = try ZigTag.type.create(mt.t.arena, switch (suffix) {
346 .None => "c_int",
347 .U => "c_uint",
348 .L => "c_long",
349 .UL => "c_ulong",
350 .LL => "c_longlong",
351 .ULL => "c_ulonglong",
352 else => unreachable,
353 });
354 const value = std.fmt.parseInt(i128, bytes.items, 0) catch math.maxInt(i128);
355
356 // make the output less noisy by skipping promoteIntLiteral where
357 // it's guaranteed to not be required because of C standard type constraints
358 const guaranteed_to_fit = switch (suffix) {
359 .None => math.cast(i16, value) != null,
360 .U => math.cast(u16, value) != null,
361 .L => math.cast(i32, value) != null,
362 .UL => math.cast(u32, value) != null,
363 .LL => math.cast(i64, value) != null,
364 .ULL => math.cast(u64, value) != null,
365 else => unreachable,
366 };
367
368 const literal_node = try ZigTag.integer_literal.create(mt.t.arena, bytes.items);
369 if (guaranteed_to_fit) {
370 return ZigTag.as.create(mt.t.arena, .{ .lhs = type_node, .rhs = literal_node });
371 } else {
372 return mt.t.createHelperCallNode(.promoteIntLiteral, &.{ type_node, literal_node, try ZigTag.enum_literal.create(mt.t.arena, @tagName(prefix)) });
373 }
374 }
375}
376
377fn zigifyEscapeSequences(mt: *MacroTranslator, slice: []const u8) ![]const u8 {
378 var source = slice;
379 for (source, 0..) |c, i| {
380 if (c == '\"' or c == '\'') {
381 source = source[i..];
382 break;
383 }
384 }
385 for (source) |c| {
386 if (c == '\\' or c == '\t') {
387 break;
388 }
389 } else return source;
390 const bytes = try mt.t.arena.alloc(u8, source.len * 2);
391 var state: enum {
392 start,
393 escape,
394 hex,
395 octal,
396 } = .start;
397 var i: usize = 0;
398 var count: u8 = 0;
399 var num: u8 = 0;
400 for (source) |c| {
401 switch (state) {
402 .escape => {
403 switch (c) {
404 'n', 'r', 't', '\\', '\'', '\"' => {
405 bytes[i] = c;
406 },
407 '0'...'7' => {
408 count += 1;
409 num += c - '0';
410 state = .octal;
411 bytes[i] = 'x';
412 },
413 'x' => {
414 state = .hex;
415 bytes[i] = 'x';
416 },
417 'a' => {
418 bytes[i] = 'x';
419 i += 1;
420 bytes[i] = '0';
421 i += 1;
422 bytes[i] = '7';
423 },
424 'b' => {
425 bytes[i] = 'x';
426 i += 1;
427 bytes[i] = '0';
428 i += 1;
429 bytes[i] = '8';
430 },
431 'f' => {
432 bytes[i] = 'x';
433 i += 1;
434 bytes[i] = '0';
435 i += 1;
436 bytes[i] = 'C';
437 },
438 'v' => {
439 bytes[i] = 'x';
440 i += 1;
441 bytes[i] = '0';
442 i += 1;
443 bytes[i] = 'B';
444 },
445 '?' => {
446 i -= 1;
447 bytes[i] = '?';
448 },
449 'u', 'U' => {
450 try mt.fail("macro tokenizing failed: TODO unicode escape sequences", .{});
451 return error.ParseError;
452 },
453 else => {
454 try mt.fail("macro tokenizing failed: unknown escape sequence", .{});
455 return error.ParseError;
456 },
457 }
458 i += 1;
459 if (state == .escape)
460 state = .start;
461 },
462 .start => {
463 if (c == '\t') {
464 bytes[i] = '\\';
465 i += 1;
466 bytes[i] = 't';
467 i += 1;
468 continue;
469 }
470 if (c == '\\') {
471 state = .escape;
472 }
473 bytes[i] = c;
474 i += 1;
475 },
476 .hex => {
477 switch (c) {
478 '0'...'9' => {
479 num = std.math.mul(u8, num, 16) catch {
480 try mt.fail("macro tokenizing failed: hex literal overflowed", .{});
481 return error.ParseError;
482 };
483 num += c - '0';
484 },
485 'a'...'f' => {
486 num = std.math.mul(u8, num, 16) catch {
487 try mt.fail("macro tokenizing failed: hex literal overflowed", .{});
488 return error.ParseError;
489 };
490 num += c - 'a' + 10;
491 },
492 'A'...'F' => {
493 num = std.math.mul(u8, num, 16) catch {
494 try mt.fail("macro tokenizing failed: hex literal overflowed", .{});
495 return error.ParseError;
496 };
497 num += c - 'A' + 10;
498 },
499 else => {
500 i += std.fmt.printInt(bytes[i..], num, 16, .lower, .{ .fill = '0', .width = 2 });
501 num = 0;
502 if (c == '\\')
503 state = .escape
504 else
505 state = .start;
506 bytes[i] = c;
507 i += 1;
508 },
509 }
510 },
511 .octal => {
512 const accept_digit = switch (c) {
513 // The maximum length of a octal literal is 3 digits
514 '0'...'7' => count < 3,
515 else => false,
516 };
517
518 if (accept_digit) {
519 count += 1;
520 num = std.math.mul(u8, num, 8) catch {
521 try mt.fail("macro tokenizing failed: octal literal overflowed", .{});
522 return error.ParseError;
523 };
524 num += c - '0';
525 } else {
526 i += std.fmt.printInt(bytes[i..], num, 16, .lower, .{ .fill = '0', .width = 2 });
527 num = 0;
528 count = 0;
529 if (c == '\\')
530 state = .escape
531 else
532 state = .start;
533 bytes[i] = c;
534 i += 1;
535 }
536 },
537 }
538 }
539 if (state == .hex or state == .octal) {
540 i += std.fmt.printInt(bytes[i..], num, 16, .lower, .{ .fill = '0', .width = 2 });
541 }
542
543 return bytes[0..i];
544}
545
546/// non-ASCII characters (mt > 127) are also treated as non-printable by fmtSliceEscapeLower.
547/// If a C string literal or char literal in a macro is not valid UTF-8, we need to escape
548/// non-ASCII characters so that the Zig source we output will itself be UTF-8.
549fn escapeUnprintables(mt: *MacroTranslator) ![]const u8 {
550 const slice = mt.tokSlice();
551 mt.i += 1;
552
553 const zigified = try mt.zigifyEscapeSequences(slice);
554 if (std.unicode.utf8ValidateSlice(zigified)) return zigified;
555
556 const formatter = std.ascii.hexEscape(zigified, .lower);
557 const encoded_size = @as(usize, @intCast(std.fmt.count("{f}", .{formatter})));
558 const output = try mt.t.arena.alloc(u8, encoded_size);
559 return std.fmt.bufPrint(output, "{f}", .{formatter}) catch |err| switch (err) {
560 error.NoSpaceLeft => unreachable,
561 else => |e| return e,
562 };
563}
564
565fn parseCPrimaryExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
566 const tok = mt.peek();
567 switch (tok) {
568 .char_literal,
569 .char_literal_utf_8,
570 .char_literal_utf_16,
571 .char_literal_utf_32,
572 .char_literal_wide,
573 => {
574 const slice = mt.tokSlice();
575 if (slice[0] != '\'' or slice[1] == '\\' or slice.len == 3) {
576 return ZigTag.char_literal.create(mt.t.arena, try mt.escapeUnprintables());
577 } else {
578 mt.i += 1;
579
580 const str = try std.fmt.allocPrint(mt.t.arena, "0x{x}", .{slice[1 .. slice.len - 1]});
581 return ZigTag.integer_literal.create(mt.t.arena, str);
582 }
583 },
584 .string_literal,
585 .string_literal_utf_16,
586 .string_literal_utf_8,
587 .string_literal_utf_32,
588 .string_literal_wide,
589 => return ZigTag.string_literal.create(mt.t.arena, try mt.escapeUnprintables()),
590 .pp_num => return mt.parseCNumLit(),
591 .l_paren => {
592 mt.i += 1;
593 const inner_node = try mt.parseCExpr(scope);
594
595 try mt.expect(.r_paren);
596 return inner_node;
597 },
598 .macro_param, .macro_param_no_expand => {
599 const param = mt.macro.params[mt.tokens[mt.i].end];
600 mt.i += 1;
601
602 const mangled_name = scope.getAlias(param) orelse param;
603 return try ZigTag.identifier.create(mt.t.arena, mangled_name);
604 },
605 .identifier, .extended_identifier => {
606 const slice = mt.tokSlice();
607 mt.i += 1;
608
609 const mangled_name = scope.getAlias(slice) orelse slice;
610 if (Translator.builtin_typedef_map.get(mangled_name)) |ty| {
611 return ZigTag.type.create(mt.t.arena, ty);
612 }
613 if (builtins.map.get(mangled_name)) |builtin| {
614 const builtin_identifier = try ZigTag.identifier.create(mt.t.arena, "__builtin");
615 return ZigTag.field_access.create(mt.t.arena, .{
616 .lhs = builtin_identifier,
617 .field_name = builtin.name,
618 });
619 }
620
621 const identifier = try ZigTag.identifier.create(mt.t.arena, mangled_name);
622 scope.skipVariableDiscard(mangled_name);
623 refs_var: {
624 const ident_node = mt.t.global_scope.sym_table.get(slice) orelse break :refs_var;
625 const var_decl_node = ident_node.castTag(.var_decl) orelse break :refs_var;
626 if (!var_decl_node.data.is_const) mt.refs_var_decl = true;
627 }
628 return identifier;
629 },
630 else => {},
631 }
632
633 // for handling type macros (EVIL)
634 // TODO maybe detect and treat type macros as typedefs in parseCSpecifierQualifierList?
635 if (try mt.parseCTypeName(scope, true)) |type_name| {
636 return type_name;
637 }
638
639 try mt.fail("unable to translate C expr: unexpected token '{s}'", .{tok.symbol()});
640 return error.ParseError;
641}
642
643fn macroIntFromBool(mt: *MacroTranslator, node: ZigNode) !ZigNode {
644 if (!node.isBoolRes()) return node;
645
646 return ZigTag.int_from_bool.create(mt.t.arena, node);
647}
648
649fn macroIntToBool(mt: *MacroTranslator, node: ZigNode) !ZigNode {
650 if (node.isBoolRes()) return node;
651
652 if (node.tag() == .string_literal) {
653 // @intFromPtr(node) != 0
654 const int_from_ptr = try ZigTag.int_from_ptr.create(mt.t.arena, node);
655 return ZigTag.not_equal.create(mt.t.arena, .{ .lhs = int_from_ptr, .rhs = ZigTag.zero_literal.init() });
656 }
657 // node != 0
658 return ZigTag.not_equal.create(mt.t.arena, .{ .lhs = node, .rhs = ZigTag.zero_literal.init() });
659}
660
661fn parseCCondExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
662 const node = try mt.parseCOrExpr(scope);
663 if (!mt.eat(.question_mark)) return node;
664
665 const then_body = try mt.parseCOrExpr(scope);
666 try mt.expect(.colon);
667 const else_body = try mt.parseCCondExpr(scope);
668 return ZigTag.@"if".create(mt.t.arena, .{ .cond = node, .then = then_body, .@"else" = else_body });
669}
670
671fn parseCOrExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
672 var node = try mt.parseCAndExpr(scope);
673 while (mt.eat(.pipe_pipe)) {
674 const lhs = try mt.macroIntToBool(node);
675 const rhs = try mt.macroIntToBool(try mt.parseCAndExpr(scope));
676 node = try ZigTag.@"or".create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
677 }
678 return node;
679}
680
681fn parseCAndExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
682 var node = try mt.parseCBitOrExpr(scope);
683 while (mt.eat(.ampersand_ampersand)) {
684 const lhs = try mt.macroIntToBool(node);
685 const rhs = try mt.macroIntToBool(try mt.parseCBitOrExpr(scope));
686 node = try ZigTag.@"and".create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
687 }
688 return node;
689}
690
691fn parseCBitOrExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
692 var node = try mt.parseCBitXorExpr(scope);
693 while (mt.eat(.pipe)) {
694 const lhs = try mt.macroIntFromBool(node);
695 const rhs = try mt.macroIntFromBool(try mt.parseCBitXorExpr(scope));
696 node = try ZigTag.bit_or.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
697 }
698 return node;
699}
700
701fn parseCBitXorExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
702 var node = try mt.parseCBitAndExpr(scope);
703 while (mt.eat(.caret)) {
704 const lhs = try mt.macroIntFromBool(node);
705 const rhs = try mt.macroIntFromBool(try mt.parseCBitAndExpr(scope));
706 node = try ZigTag.bit_xor.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
707 }
708 return node;
709}
710
711fn parseCBitAndExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
712 var node = try mt.parseCEqExpr(scope);
713 while (mt.eat(.ampersand)) {
714 const lhs = try mt.macroIntFromBool(node);
715 const rhs = try mt.macroIntFromBool(try mt.parseCEqExpr(scope));
716 node = try ZigTag.bit_and.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
717 }
718 return node;
719}
720
721fn parseCEqExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
722 var node = try mt.parseCRelExpr(scope);
723 while (true) {
724 switch (mt.peek()) {
725 .bang_equal => {
726 mt.i += 1;
727 const lhs = try mt.macroIntFromBool(node);
728 const rhs = try mt.macroIntFromBool(try mt.parseCRelExpr(scope));
729 node = try ZigTag.not_equal.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
730 },
731 .equal_equal => {
732 mt.i += 1;
733 const lhs = try mt.macroIntFromBool(node);
734 const rhs = try mt.macroIntFromBool(try mt.parseCRelExpr(scope));
735 node = try ZigTag.equal.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
736 },
737 else => return node,
738 }
739 }
740}
741
742fn parseCRelExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
743 var node = try mt.parseCShiftExpr(scope);
744 while (true) {
745 switch (mt.peek()) {
746 .angle_bracket_right => {
747 mt.i += 1;
748 const lhs = try mt.macroIntFromBool(node);
749 const rhs = try mt.macroIntFromBool(try mt.parseCShiftExpr(scope));
750 node = try ZigTag.greater_than.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
751 },
752 .angle_bracket_right_equal => {
753 mt.i += 1;
754 const lhs = try mt.macroIntFromBool(node);
755 const rhs = try mt.macroIntFromBool(try mt.parseCShiftExpr(scope));
756 node = try ZigTag.greater_than_equal.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
757 },
758 .angle_bracket_left => {
759 mt.i += 1;
760 const lhs = try mt.macroIntFromBool(node);
761 const rhs = try mt.macroIntFromBool(try mt.parseCShiftExpr(scope));
762 node = try ZigTag.less_than.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
763 },
764 .angle_bracket_left_equal => {
765 mt.i += 1;
766 const lhs = try mt.macroIntFromBool(node);
767 const rhs = try mt.macroIntFromBool(try mt.parseCShiftExpr(scope));
768 node = try ZigTag.less_than_equal.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
769 },
770 else => return node,
771 }
772 }
773}
774
775fn parseCShiftExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
776 var node = try mt.parseCAddSubExpr(scope);
777 while (true) {
778 switch (mt.peek()) {
779 .angle_bracket_angle_bracket_left => {
780 mt.i += 1;
781 const lhs = try mt.macroIntFromBool(node);
782 const rhs = try mt.macroIntFromBool(try mt.parseCAddSubExpr(scope));
783 node = try ZigTag.shl.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
784 },
785 .angle_bracket_angle_bracket_right => {
786 mt.i += 1;
787 const lhs = try mt.macroIntFromBool(node);
788 const rhs = try mt.macroIntFromBool(try mt.parseCAddSubExpr(scope));
789 node = try ZigTag.shr.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
790 },
791 else => return node,
792 }
793 }
794}
795
796fn parseCAddSubExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
797 var node = try mt.parseCMulExpr(scope);
798 while (true) {
799 switch (mt.peek()) {
800 .plus => {
801 mt.i += 1;
802 const lhs = try mt.macroIntFromBool(node);
803 const rhs = try mt.macroIntFromBool(try mt.parseCMulExpr(scope));
804 node = try ZigTag.add.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
805 },
806 .minus => {
807 mt.i += 1;
808 const lhs = try mt.macroIntFromBool(node);
809 const rhs = try mt.macroIntFromBool(try mt.parseCMulExpr(scope));
810 node = try ZigTag.sub.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
811 },
812 else => return node,
813 }
814 }
815}
816
817fn parseCMulExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
818 var node = try mt.parseCCastExpr(scope);
819 while (true) {
820 switch (mt.peek()) {
821 .asterisk => {
822 mt.i += 1;
823 const lhs = try mt.macroIntFromBool(node);
824 const rhs = try mt.macroIntFromBool(try mt.parseCCastExpr(scope));
825 node = try ZigTag.mul.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
826 },
827 .slash => {
828 mt.i += 1;
829 const lhs = try mt.macroIntFromBool(node);
830 const rhs = try mt.macroIntFromBool(try mt.parseCCastExpr(scope));
831 node = try mt.t.createHelperCallNode(.div, &.{ lhs, rhs });
832 },
833 .percent => {
834 mt.i += 1;
835 const lhs = try mt.macroIntFromBool(node);
836 const rhs = try mt.macroIntFromBool(try mt.parseCCastExpr(scope));
837 node = try mt.t.createHelperCallNode(.rem, &.{ lhs, rhs });
838 },
839 else => return node,
840 }
841 }
842}
843
844fn parseCCastExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
845 if (mt.eat(.l_paren)) {
846 if (try mt.parseCTypeName(scope, true)) |type_name| {
847 while (true) {
848 const next_tok = mt.peek();
849 if (next_tok == .r_paren) {
850 mt.i += 1;
851 break;
852 }
853 // Skip trailing blank defined before the RParen.
854 if ((next_tok == .identifier or next_tok == .extended_identifier) and
855 mt.t.global_scope.blank_macros.contains(mt.tokSlice()))
856 {
857 mt.i += 1;
858 continue;
859 }
860
861 try mt.fail(
862 "unable to translate C expr: expected ')' instead got '{s}'",
863 .{next_tok.symbol()},
864 );
865 return error.ParseError;
866 }
867 if (mt.peek() == .l_brace) {
868 // initializer list
869 return mt.parseCPostfixExpr(scope, type_name);
870 }
871 const node_to_cast = try mt.parseCCastExpr(scope);
872 return mt.t.createHelperCallNode(.cast, &.{ type_name, node_to_cast });
873 }
874 mt.i -= 1; // l_paren
875 }
876 return mt.parseCUnaryExpr(scope);
877}
878
879// allow_fail is set when unsure if we are parsing a type-name
880fn parseCTypeName(mt: *MacroTranslator, scope: *Scope, allow_fail: bool) ParseError!?ZigNode {
881 if (try mt.parseCSpecifierQualifierList(scope, allow_fail)) |node| {
882 return try mt.parseCAbstractDeclarator(node);
883 }
884 return null;
885}
886
887fn parseCSpecifierQualifierList(mt: *MacroTranslator, scope: *Scope, allow_fail: bool) ParseError!?ZigNode {
888 const tok = mt.peek();
889 switch (tok) {
890 .macro_param, .macro_param_no_expand => {
891 const param = mt.macro.params[mt.tokens[mt.i].end];
892
893 // Assume that this is only a cast if the next token is ')'
894 // e.g. param)identifier
895 if (allow_fail and (mt.macro.tokens.len < mt.i + 3 or
896 mt.macro.tokens[mt.i + 1].id != .r_paren or
897 mt.macro.tokens[mt.i + 2].id != .identifier))
898 return null;
899
900 mt.i += 1;
901 const mangled_name = scope.getAlias(param) orelse param;
902 return try ZigTag.identifier.create(mt.t.arena, mangled_name);
903 },
904 .identifier, .extended_identifier => {
905 const slice = mt.tokSlice();
906 const mangled_name = scope.getAlias(slice) orelse slice;
907
908 if (mt.t.global_scope.blank_macros.contains(slice)) {
909 mt.i += 1;
910 return try mt.parseCSpecifierQualifierList(scope, allow_fail);
911 }
912
913 if (!allow_fail or mt.t.typedefs.contains(mangled_name)) {
914 mt.i += 1;
915 if (Translator.builtin_typedef_map.get(mangled_name)) |ty| {
916 return try ZigTag.type.create(mt.t.arena, ty);
917 }
918 if (builtins.map.get(mangled_name)) |builtin| {
919 const builtin_identifier = try ZigTag.identifier.create(mt.t.arena, "__builtin");
920 return try ZigTag.field_access.create(mt.t.arena, .{
921 .lhs = builtin_identifier,
922 .field_name = builtin.name,
923 });
924 }
925
926 return try ZigTag.identifier.create(mt.t.arena, mangled_name);
927 }
928 },
929 .keyword_void => {
930 mt.i += 1;
931 return try ZigTag.type.create(mt.t.arena, "anyopaque");
932 },
933 .keyword_bool => {
934 mt.i += 1;
935 return try ZigTag.type.create(mt.t.arena, "bool");
936 },
937 .keyword_char,
938 .keyword_int,
939 .keyword_short,
940 .keyword_long,
941 .keyword_float,
942 .keyword_double,
943 .keyword_signed,
944 .keyword_unsigned,
945 .keyword_complex,
946 => return try mt.parseCNumericType(),
947 .keyword_enum, .keyword_struct, .keyword_union => {
948 const tag_name = mt.tokSlice();
949 mt.i += 1;
950
951 // struct Foo will be declared as struct_Foo by transRecordDecl
952 const identifier = mt.tokSlice();
953 try mt.expect(.identifier);
954
955 const name = try std.fmt.allocPrint(mt.t.arena, "{s}_{s}", .{ tag_name, identifier });
956 return try ZigTag.identifier.create(mt.t.arena, name);
957 },
958 else => {},
959 }
960
961 if (allow_fail) return null;
962
963 try mt.fail("unable to translate C expr: unexpected token '{s}'", .{tok.symbol()});
964 return error.ParseError;
965}
966
967fn parseCNumericType(mt: *MacroTranslator) ParseError!ZigNode {
968 const KwCounter = struct {
969 double: u8 = 0,
970 long: u8 = 0,
971 int: u8 = 0,
972 float: u8 = 0,
973 short: u8 = 0,
974 char: u8 = 0,
975 unsigned: u8 = 0,
976 signed: u8 = 0,
977 complex: u8 = 0,
978
979 fn eql(self: @This(), other: @This()) bool {
980 return std.meta.eql(self, other);
981 }
982 };
983
984 // Yes, these can be in *any* order
985 // This still doesn't cover cases where for example volatile is intermixed
986
987 var kw = KwCounter{};
988 // prevent overflow
989 var i: u8 = 0;
990 while (i < math.maxInt(u8)) : (i += 1) {
991 switch (mt.peek()) {
992 .keyword_double => kw.double += 1,
993 .keyword_long => kw.long += 1,
994 .keyword_int => kw.int += 1,
995 .keyword_float => kw.float += 1,
996 .keyword_short => kw.short += 1,
997 .keyword_char => kw.char += 1,
998 .keyword_unsigned => kw.unsigned += 1,
999 .keyword_signed => kw.signed += 1,
1000 .keyword_complex => kw.complex += 1,
1001 else => break,
1002 }
1003 mt.i += 1;
1004 }
1005
1006 if (kw.eql(.{ .int = 1 }) or kw.eql(.{ .signed = 1 }) or kw.eql(.{ .signed = 1, .int = 1 }))
1007 return ZigTag.type.create(mt.t.arena, "c_int");
1008
1009 if (kw.eql(.{ .unsigned = 1 }) or kw.eql(.{ .unsigned = 1, .int = 1 }))
1010 return ZigTag.type.create(mt.t.arena, "c_uint");
1011
1012 if (kw.eql(.{ .long = 1 }) or kw.eql(.{ .signed = 1, .long = 1 }) or kw.eql(.{ .long = 1, .int = 1 }) or kw.eql(.{ .signed = 1, .long = 1, .int = 1 }))
1013 return ZigTag.type.create(mt.t.arena, "c_long");
1014
1015 if (kw.eql(.{ .unsigned = 1, .long = 1 }) or kw.eql(.{ .unsigned = 1, .long = 1, .int = 1 }))
1016 return ZigTag.type.create(mt.t.arena, "c_ulong");
1017
1018 if (kw.eql(.{ .long = 2 }) or kw.eql(.{ .signed = 1, .long = 2 }) or kw.eql(.{ .long = 2, .int = 1 }) or kw.eql(.{ .signed = 1, .long = 2, .int = 1 }))
1019 return ZigTag.type.create(mt.t.arena, "c_longlong");
1020
1021 if (kw.eql(.{ .unsigned = 1, .long = 2 }) or kw.eql(.{ .unsigned = 1, .long = 2, .int = 1 }))
1022 return ZigTag.type.create(mt.t.arena, "c_ulonglong");
1023
1024 if (kw.eql(.{ .signed = 1, .char = 1 }))
1025 return ZigTag.type.create(mt.t.arena, "i8");
1026
1027 if (kw.eql(.{ .char = 1 }) or kw.eql(.{ .unsigned = 1, .char = 1 }))
1028 return ZigTag.type.create(mt.t.arena, "u8");
1029
1030 if (kw.eql(.{ .short = 1 }) or kw.eql(.{ .signed = 1, .short = 1 }) or kw.eql(.{ .short = 1, .int = 1 }) or kw.eql(.{ .signed = 1, .short = 1, .int = 1 }))
1031 return ZigTag.type.create(mt.t.arena, "c_short");
1032
1033 if (kw.eql(.{ .unsigned = 1, .short = 1 }) or kw.eql(.{ .unsigned = 1, .short = 1, .int = 1 }))
1034 return ZigTag.type.create(mt.t.arena, "c_ushort");
1035
1036 if (kw.eql(.{ .float = 1 }))
1037 return ZigTag.type.create(mt.t.arena, "f32");
1038
1039 if (kw.eql(.{ .double = 1 }))
1040 return ZigTag.type.create(mt.t.arena, "f64");
1041
1042 if (kw.eql(.{ .long = 1, .double = 1 })) {
1043 try mt.fail("unable to translate: TODO long double", .{});
1044 return error.ParseError;
1045 }
1046
1047 if (kw.eql(.{ .float = 1, .complex = 1 })) {
1048 try mt.fail("unable to translate: TODO _Complex", .{});
1049 return error.ParseError;
1050 }
1051
1052 if (kw.eql(.{ .double = 1, .complex = 1 })) {
1053 try mt.fail("unable to translate: TODO _Complex", .{});
1054 return error.ParseError;
1055 }
1056
1057 if (kw.eql(.{ .long = 1, .double = 1, .complex = 1 })) {
1058 try mt.fail("unable to translate: TODO _Complex", .{});
1059 return error.ParseError;
1060 }
1061
1062 try mt.fail("unable to translate: invalid numeric type", .{});
1063 return error.ParseError;
1064}
1065
1066fn parseCAbstractDeclarator(mt: *MacroTranslator, node: ZigNode) ParseError!ZigNode {
1067 if (mt.eat(.asterisk)) {
1068 if (node.castTag(.type)) |some| {
1069 if (std.mem.eql(u8, some.data, "anyopaque")) {
1070 const ptr = try ZigTag.single_pointer.create(mt.t.arena, .{
1071 .is_const = false,
1072 .is_volatile = false,
1073 .is_allowzero = false,
1074 .elem_type = node,
1075 });
1076 return ZigTag.optional_type.create(mt.t.arena, ptr);
1077 }
1078 }
1079 return ZigTag.c_pointer.create(mt.t.arena, .{
1080 .is_const = false,
1081 .is_volatile = false,
1082 .is_allowzero = false,
1083 .elem_type = node,
1084 });
1085 }
1086 return node;
1087}
1088
1089fn parseCPostfixExpr(mt: *MacroTranslator, scope: *Scope, type_name: ?ZigNode) ParseError!ZigNode {
1090 var node = try mt.parseCPostfixExprInner(scope, type_name);
1091 // In C the preprocessor would handle concatting strings while expanding macros.
1092 // This should do approximately the same by concatting any strings and identifiers
1093 // after a primary or postfix expression.
1094 while (true) {
1095 switch (mt.peek()) {
1096 .string_literal,
1097 .string_literal_utf_16,
1098 .string_literal_utf_8,
1099 .string_literal_utf_32,
1100 .string_literal_wide,
1101 => {},
1102 .identifier, .extended_identifier => {
1103 if (mt.t.global_scope.blank_macros.contains(mt.tokSlice())) {
1104 mt.i += 1;
1105 continue;
1106 }
1107 },
1108 else => break,
1109 }
1110 const rhs = try mt.parseCPostfixExprInner(scope, type_name);
1111 node = try ZigTag.array_cat.create(mt.t.arena, .{ .lhs = node, .rhs = rhs });
1112 }
1113 return node;
1114}
1115
1116fn parseCPostfixExprInner(mt: *MacroTranslator, scope: *Scope, type_name: ?ZigNode) ParseError!ZigNode {
1117 var node = type_name orelse try mt.parseCPrimaryExpr(scope);
1118 while (true) {
1119 switch (mt.peek()) {
1120 .period => {
1121 mt.i += 1;
1122 const field_name = mt.tokSlice();
1123 try mt.expect(.identifier);
1124
1125 node = try ZigTag.field_access.create(mt.t.arena, .{ .lhs = node, .field_name = field_name });
1126 },
1127 .arrow => {
1128 mt.i += 1;
1129 const field_name = mt.tokSlice();
1130 try mt.expect(.identifier);
1131
1132 const deref = try ZigTag.deref.create(mt.t.arena, node);
1133 node = try ZigTag.field_access.create(mt.t.arena, .{ .lhs = deref, .field_name = field_name });
1134 },
1135 .l_bracket => {
1136 mt.i += 1;
1137
1138 const index_val = try mt.macroIntFromBool(try mt.parseCExpr(scope));
1139 const index = try ZigTag.as.create(mt.t.arena, .{
1140 .lhs = try ZigTag.type.create(mt.t.arena, "usize"),
1141 .rhs = try ZigTag.int_cast.create(mt.t.arena, index_val),
1142 });
1143 node = try ZigTag.array_access.create(mt.t.arena, .{ .lhs = node, .rhs = index });
1144 try mt.expect(.r_bracket);
1145 },
1146 .l_paren => {
1147 mt.i += 1;
1148
1149 if (mt.eat(.r_paren)) {
1150 node = try ZigTag.call.create(mt.t.arena, .{ .lhs = node, .args = &.{} });
1151 } else {
1152 var args = std.ArrayList(ZigNode).init(mt.t.gpa);
1153 defer args.deinit();
1154
1155 while (true) {
1156 const arg = try mt.parseCCondExpr(scope);
1157 try args.append(arg);
1158
1159 const next_id = mt.peek();
1160 switch (next_id) {
1161 .comma => {
1162 mt.i += 1;
1163 },
1164 .r_paren => {
1165 mt.i += 1;
1166 break;
1167 },
1168 else => {
1169 try mt.fail("unable to translate C expr: expected ',' or ')' instead got '{s}'", .{next_id.symbol()});
1170 return error.ParseError;
1171 },
1172 }
1173 }
1174 node = try ZigTag.call.create(mt.t.arena, .{ .lhs = node, .args = try mt.t.arena.dupe(ZigNode, args.items) });
1175 }
1176 },
1177 .l_brace => {
1178 mt.i += 1;
1179
1180 // Check for designated field initializers
1181 if (mt.peek() == .period) {
1182 var init_vals = std.ArrayList(ast.Payload.ContainerInitDot.Initializer).init(mt.t.gpa);
1183 defer init_vals.deinit();
1184
1185 while (true) {
1186 try mt.expect(.period);
1187 const name = mt.tokSlice();
1188 try mt.expect(.identifier);
1189 try mt.expect(.equal);
1190
1191 const val = try mt.parseCCondExpr(scope);
1192 try init_vals.append(.{ .name = name, .value = val });
1193
1194 const next_id = mt.peek();
1195 switch (next_id) {
1196 .comma => {
1197 mt.i += 1;
1198 },
1199 .r_brace => {
1200 mt.i += 1;
1201 break;
1202 },
1203 else => {
1204 try mt.fail("unable to translate C expr: expected ',' or '}}' instead got '{s}'", .{next_id.symbol()});
1205 return error.ParseError;
1206 },
1207 }
1208 }
1209 const tuple_node = try ZigTag.container_init_dot.create(mt.t.arena, try mt.t.arena.dupe(ast.Payload.ContainerInitDot.Initializer, init_vals.items));
1210 node = try ZigTag.std_mem_zeroinit.create(mt.t.arena, .{ .lhs = node, .rhs = tuple_node });
1211 continue;
1212 }
1213
1214 var init_vals = std.ArrayList(ZigNode).init(mt.t.gpa);
1215 defer init_vals.deinit();
1216
1217 while (true) {
1218 const val = try mt.parseCCondExpr(scope);
1219 try init_vals.append(val);
1220
1221 const next_id = mt.peek();
1222 switch (next_id) {
1223 .comma => {
1224 mt.i += 1;
1225 },
1226 .r_brace => {
1227 mt.i += 1;
1228 break;
1229 },
1230 else => {
1231 try mt.fail("unable to translate C expr: expected ',' or '}}' instead got '{s}'", .{next_id.symbol()});
1232 return error.ParseError;
1233 },
1234 }
1235 }
1236 const tuple_node = try ZigTag.tuple.create(mt.t.arena, try mt.t.arena.dupe(ZigNode, init_vals.items));
1237 node = try ZigTag.std_mem_zeroinit.create(mt.t.arena, .{ .lhs = node, .rhs = tuple_node });
1238 },
1239 .plus_plus, .minus_minus => {
1240 try mt.fail("TODO postfix inc/dec expr", .{});
1241 return error.ParseError;
1242 },
1243 else => return node,
1244 }
1245 }
1246}
1247
1248fn parseCUnaryExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
1249 switch (mt.peek()) {
1250 .bang => {
1251 mt.i += 1;
1252 const operand = try mt.macroIntToBool(try mt.parseCCastExpr(scope));
1253 return ZigTag.not.create(mt.t.arena, operand);
1254 },
1255 .minus => {
1256 mt.i += 1;
1257 const operand = try mt.macroIntFromBool(try mt.parseCCastExpr(scope));
1258 return ZigTag.negate.create(mt.t.arena, operand);
1259 },
1260 .plus => {
1261 mt.i += 1;
1262 return try mt.parseCCastExpr(scope);
1263 },
1264 .tilde => {
1265 mt.i += 1;
1266 const operand = try mt.macroIntFromBool(try mt.parseCCastExpr(scope));
1267 return ZigTag.bit_not.create(mt.t.arena, operand);
1268 },
1269 .asterisk => {
1270 mt.i += 1;
1271 const operand = try mt.parseCCastExpr(scope);
1272 return ZigTag.deref.create(mt.t.arena, operand);
1273 },
1274 .ampersand => {
1275 mt.i += 1;
1276 const operand = try mt.parseCCastExpr(scope);
1277 return ZigTag.address_of.create(mt.t.arena, operand);
1278 },
1279 .keyword_sizeof => {
1280 mt.i += 1;
1281 const operand = if (mt.eat(.l_paren)) blk: {
1282 const inner = (try mt.parseCTypeName(scope, false)).?;
1283 try mt.expect(.r_paren);
1284 break :blk inner;
1285 } else try mt.parseCUnaryExpr(scope);
1286
1287 return mt.t.createHelperCallNode(.sizeof, &.{operand});
1288 },
1289 .keyword_alignof => {
1290 mt.i += 1;
1291 // TODO this won't work if using <stdalign.h>'s
1292 // #define alignof _Alignof
1293 try mt.expect(.l_paren);
1294 const operand = (try mt.parseCTypeName(scope, false)).?;
1295 try mt.expect(.r_paren);
1296
1297 return ZigTag.alignof.create(mt.t.arena, operand);
1298 },
1299 .plus_plus, .minus_minus => {
1300 try mt.fail("TODO unary inc/dec expr", .{});
1301 return error.ParseError;
1302 },
1303 else => {},
1304 }
1305
1306 return try mt.parseCPostfixExpr(scope, null);
1307}
lib/compiler/translate-c/src/PatternList.zig deleted-288
...@@ -1,288 +0,0 @@
1const std = @import("std");
2const mem = std.mem;
3const assert = std.debug.assert;
4
5const aro = @import("aro");
6const CToken = aro.Tokenizer.Token;
7
8const helpers = @import("helpers.zig");
9const Translator = @import("Translator.zig");
10const Error = Translator.Error;
11pub const MacroProcessingError = Error || error{UnexpectedMacroToken};
12
13const Impl = std.meta.DeclEnum(@import("helpers"));
14const Template = struct { []const u8, Impl };
15
16/// Templates must be function-like macros
17/// first element is macro source, second element is the name of the function
18/// in __helpers which implements it
19const templates = [_]Template{
20 .{ "f_SUFFIX(X) (X ## f)", .F_SUFFIX },
21 .{ "F_SUFFIX(X) (X ## F)", .F_SUFFIX },
22
23 .{ "u_SUFFIX(X) (X ## u)", .U_SUFFIX },
24 .{ "U_SUFFIX(X) (X ## U)", .U_SUFFIX },
25
26 .{ "l_SUFFIX(X) (X ## l)", .L_SUFFIX },
27 .{ "L_SUFFIX(X) (X ## L)", .L_SUFFIX },
28
29 .{ "ul_SUFFIX(X) (X ## ul)", .UL_SUFFIX },
30 .{ "uL_SUFFIX(X) (X ## uL)", .UL_SUFFIX },
31 .{ "Ul_SUFFIX(X) (X ## Ul)", .UL_SUFFIX },
32 .{ "UL_SUFFIX(X) (X ## UL)", .UL_SUFFIX },
33
34 .{ "ll_SUFFIX(X) (X ## ll)", .LL_SUFFIX },
35 .{ "LL_SUFFIX(X) (X ## LL)", .LL_SUFFIX },
36
37 .{ "ull_SUFFIX(X) (X ## ull)", .ULL_SUFFIX },
38 .{ "uLL_SUFFIX(X) (X ## uLL)", .ULL_SUFFIX },
39 .{ "Ull_SUFFIX(X) (X ## Ull)", .ULL_SUFFIX },
40 .{ "ULL_SUFFIX(X) (X ## ULL)", .ULL_SUFFIX },
41
42 .{ "f_SUFFIX(X) X ## f", .F_SUFFIX },
43 .{ "F_SUFFIX(X) X ## F", .F_SUFFIX },
44
45 .{ "u_SUFFIX(X) X ## u", .U_SUFFIX },
46 .{ "U_SUFFIX(X) X ## U", .U_SUFFIX },
47
48 .{ "l_SUFFIX(X) X ## l", .L_SUFFIX },
49 .{ "L_SUFFIX(X) X ## L", .L_SUFFIX },
50
51 .{ "ul_SUFFIX(X) X ## ul", .UL_SUFFIX },
52 .{ "uL_SUFFIX(X) X ## uL", .UL_SUFFIX },
53 .{ "Ul_SUFFIX(X) X ## Ul", .UL_SUFFIX },
54 .{ "UL_SUFFIX(X) X ## UL", .UL_SUFFIX },
55
56 .{ "ll_SUFFIX(X) X ## ll", .LL_SUFFIX },
57 .{ "LL_SUFFIX(X) X ## LL", .LL_SUFFIX },
58
59 .{ "ull_SUFFIX(X) X ## ull", .ULL_SUFFIX },
60 .{ "uLL_SUFFIX(X) X ## uLL", .ULL_SUFFIX },
61 .{ "Ull_SUFFIX(X) X ## Ull", .ULL_SUFFIX },
62 .{ "ULL_SUFFIX(X) X ## ULL", .ULL_SUFFIX },
63
64 .{ "CAST_OR_CALL(X, Y) (X)(Y)", .CAST_OR_CALL },
65 .{ "CAST_OR_CALL(X, Y) ((X)(Y))", .CAST_OR_CALL },
66
67 .{
68 \\wl_container_of(ptr, sample, member) \
69 \\(__typeof__(sample))((char *)(ptr) - \
70 \\ offsetof(__typeof__(*sample), member))
71 ,
72 .WL_CONTAINER_OF,
73 },
74
75 .{ "IGNORE_ME(X) ((void)(X))", .DISCARD },
76 .{ "IGNORE_ME(X) (void)(X)", .DISCARD },
77 .{ "IGNORE_ME(X) ((const void)(X))", .DISCARD },
78 .{ "IGNORE_ME(X) (const void)(X)", .DISCARD },
79 .{ "IGNORE_ME(X) ((volatile void)(X))", .DISCARD },
80 .{ "IGNORE_ME(X) (volatile void)(X)", .DISCARD },
81 .{ "IGNORE_ME(X) ((const volatile void)(X))", .DISCARD },
82 .{ "IGNORE_ME(X) (const volatile void)(X)", .DISCARD },
83 .{ "IGNORE_ME(X) ((volatile const void)(X))", .DISCARD },
84 .{ "IGNORE_ME(X) (volatile const void)(X)", .DISCARD },
85};
86
87const Pattern = struct {
88 slicer: MacroSlicer,
89 impl: Impl,
90
91 fn init(pl: *Pattern, allocator: mem.Allocator, template: Template) Error!void {
92 const source = template[0];
93 const impl = template[1];
94 var tok_list = std.ArrayList(CToken).init(allocator);
95 defer tok_list.deinit();
96
97 pl.* = .{
98 .slicer = try tokenizeMacro(source, &tok_list),
99 .impl = impl,
100 };
101 }
102
103 fn deinit(pl: *Pattern, allocator: mem.Allocator) void {
104 allocator.free(pl.slicer.tokens);
105 pl.* = undefined;
106 }
107
108 /// This function assumes that `ms` has already been validated to contain a function-like
109 /// macro, and that the parsed template macro in `pl` also contains a function-like
110 /// macro. Please review this logic carefully if changing that assumption. Two
111 /// function-like macros are considered equivalent if and only if they contain the same
112 /// list of tokens, modulo parameter names.
113 fn matches(pat: Pattern, ms: MacroSlicer) bool {
114 if (ms.params != pat.slicer.params) return false;
115 if (ms.tokens.len != pat.slicer.tokens.len) return false;
116
117 for (ms.tokens, pat.slicer.tokens) |macro_tok, pat_tok| {
118 if (macro_tok.id != pat_tok.id) return false;
119 switch (macro_tok.id) {
120 .macro_param, .macro_param_no_expand => {
121 // `.end` is the parameter index.
122 if (macro_tok.end != pat_tok.end) return false;
123 },
124 .identifier, .extended_identifier, .string_literal, .char_literal, .pp_num => {
125 const macro_bytes = ms.slice(macro_tok);
126 const pattern_bytes = pat.slicer.slice(pat_tok);
127
128 if (!mem.eql(u8, pattern_bytes, macro_bytes)) return false;
129 },
130 else => {
131 // other tags correspond to keywords and operators that do not contain a "payload"
132 // that can vary
133 },
134 }
135 }
136 return true;
137 }
138};
139
140const PatternList = @This();
141
142patterns: []Pattern,
143
144pub const MacroSlicer = struct {
145 source: []const u8,
146 tokens: []const CToken,
147 params: u32,
148
149 fn slice(pl: MacroSlicer, token: CToken) []const u8 {
150 return pl.source[token.start..token.end];
151 }
152};
153
154pub fn init(allocator: mem.Allocator) Error!PatternList {
155 const patterns = try allocator.alloc(Pattern, templates.len);
156 for (patterns, templates) |*pattern, template| {
157 try pattern.init(allocator, template);
158 }
159 return .{ .patterns = patterns };
160}
161
162pub fn deinit(pl: *PatternList, allocator: mem.Allocator) void {
163 for (pl.patterns) |*pattern| pattern.deinit(allocator);
164 allocator.free(pl.patterns);
165 pl.* = undefined;
166}
167
168pub fn match(pl: PatternList, ms: MacroSlicer) Error!?Impl {
169 for (pl.patterns) |pattern| if (pattern.matches(ms)) return pattern.impl;
170 return null;
171}
172
173fn tokenizeMacro(source: []const u8, tok_list: *std.ArrayList(CToken)) Error!MacroSlicer {
174 var param_count: u32 = 0;
175 var param_buf: [8][]const u8 = undefined;
176
177 var tokenizer: aro.Tokenizer = .{
178 .buf = source,
179 .source = .unused,
180 .langopts = .{},
181 };
182 {
183 const name_tok = tokenizer.nextNoWS();
184 assert(name_tok.id == .identifier);
185 const l_paren = tokenizer.nextNoWS();
186 assert(l_paren.id == .l_paren);
187 }
188
189 while (true) {
190 const param = tokenizer.nextNoWS();
191 if (param.id == .r_paren) break;
192 assert(param.id == .identifier);
193 const slice = source[param.start..param.end];
194 param_buf[param_count] = slice;
195 param_count += 1;
196
197 const comma = tokenizer.nextNoWS();
198 if (comma.id == .r_paren) break;
199 assert(comma.id == .comma);
200 }
201
202 outer: while (true) {
203 const tok = tokenizer.next();
204 switch (tok.id) {
205 .whitespace, .comment => continue,
206 .identifier => {
207 const slice = source[tok.start..tok.end];
208 for (param_buf[0..param_count], 0..) |param, i| {
209 if (std.mem.eql(u8, param, slice)) {
210 try tok_list.append(.{
211 .id = .macro_param,
212 .source = .unused,
213 .end = @intCast(i),
214 });
215 continue :outer;
216 }
217 }
218 },
219 .hash_hash => {
220 if (tok_list.items[tok_list.items.len - 1].id == .macro_param) {
221 tok_list.items[tok_list.items.len - 1].id = .macro_param_no_expand;
222 }
223 },
224 .nl, .eof => break,
225 else => {},
226 }
227 try tok_list.append(tok);
228 }
229
230 return .{
231 .source = source,
232 .tokens = try tok_list.toOwnedSlice(),
233 .params = param_count,
234 };
235}
236
237test "Macro matching" {
238 const testing = std.testing;
239 const helper = struct {
240 fn checkMacro(
241 allocator: mem.Allocator,
242 pattern_list: PatternList,
243 source: []const u8,
244 comptime expected_match: ?Impl,
245 ) !void {
246 var tok_list = std.ArrayList(CToken).init(allocator);
247 defer tok_list.deinit();
248 const ms = try tokenizeMacro(source, &tok_list);
249 defer allocator.free(ms.tokens);
250
251 const matched = try pattern_list.match(ms);
252 if (expected_match) |expected| {
253 try testing.expectEqual(expected, matched);
254 } else {
255 try testing.expectEqual(@as(@TypeOf(matched), null), matched);
256 }
257 }
258 };
259 const allocator = std.testing.allocator;
260 var pattern_list = try PatternList.init(allocator);
261 defer pattern_list.deinit(allocator);
262
263 try helper.checkMacro(allocator, pattern_list, "BAR(Z) (Z ## F)", .F_SUFFIX);
264 try helper.checkMacro(allocator, pattern_list, "BAR(Z) (Z ## U)", .U_SUFFIX);
265 try helper.checkMacro(allocator, pattern_list, "BAR(Z) (Z ## L)", .L_SUFFIX);
266 try helper.checkMacro(allocator, pattern_list, "BAR(Z) (Z ## LL)", .LL_SUFFIX);
267 try helper.checkMacro(allocator, pattern_list, "BAR(Z) (Z ## UL)", .UL_SUFFIX);
268 try helper.checkMacro(allocator, pattern_list, "BAR(Z) (Z ## ULL)", .ULL_SUFFIX);
269 try helper.checkMacro(allocator, pattern_list,
270 \\container_of(a, b, c) \
271 \\(__typeof__(b))((char *)(a) - \
272 \\ offsetof(__typeof__(*b), c))
273 , .WL_CONTAINER_OF);
274
275 try helper.checkMacro(allocator, pattern_list, "NO_MATCH(X, Y) (X + Y)", null);
276 try helper.checkMacro(allocator, pattern_list, "CAST_OR_CALL(X, Y) (X)(Y)", .CAST_OR_CALL);
277 try helper.checkMacro(allocator, pattern_list, "CAST_OR_CALL(X, Y) ((X)(Y))", .CAST_OR_CALL);
278 try helper.checkMacro(allocator, pattern_list, "IGNORE_ME(X) (void)(X)", .DISCARD);
279 try helper.checkMacro(allocator, pattern_list, "IGNORE_ME(X) ((void)(X))", .DISCARD);
280 try helper.checkMacro(allocator, pattern_list, "IGNORE_ME(X) (const void)(X)", .DISCARD);
281 try helper.checkMacro(allocator, pattern_list, "IGNORE_ME(X) ((const void)(X))", .DISCARD);
282 try helper.checkMacro(allocator, pattern_list, "IGNORE_ME(X) (volatile void)(X)", .DISCARD);
283 try helper.checkMacro(allocator, pattern_list, "IGNORE_ME(X) ((volatile void)(X))", .DISCARD);
284 try helper.checkMacro(allocator, pattern_list, "IGNORE_ME(X) (const volatile void)(X)", .DISCARD);
285 try helper.checkMacro(allocator, pattern_list, "IGNORE_ME(X) ((const volatile void)(X))", .DISCARD);
286 try helper.checkMacro(allocator, pattern_list, "IGNORE_ME(X) (volatile const void)(X)", .DISCARD);
287 try helper.checkMacro(allocator, pattern_list, "IGNORE_ME(X) ((volatile const void)(X))", .DISCARD);
288}
lib/compiler/translate-c/src/Scope.zig deleted-399
...@@ -1,399 +0,0 @@
1const std = @import("std");
2
3const aro = @import("aro");
4
5const ast = @import("ast.zig");
6const Translator = @import("Translator.zig");
7
8const Scope = @This();
9
10pub const SymbolTable = std.StringArrayHashMapUnmanaged(ast.Node);
11pub const AliasList = std.ArrayListUnmanaged(struct {
12 alias: []const u8,
13 name: []const u8,
14});
15
16/// Associates a container (structure or union) with its relevant member functions.
17pub const ContainerMemberFns = struct {
18 container_decl_ptr: *ast.Node,
19 member_fns: std.ArrayListUnmanaged(*ast.Payload.Func) = .empty,
20};
21pub const ContainerMemberFnsHashMap = std.AutoArrayHashMapUnmanaged(aro.QualType, ContainerMemberFns);
22
23id: Id,
24parent: ?*Scope,
25
26pub const Id = enum {
27 block,
28 root,
29 condition,
30 loop,
31 do_loop,
32};
33
34/// Used for the scope of condition expressions, for example `if (cond)`.
35/// The block is lazily initialized because it is only needed for rare
36/// cases of comma operators being used.
37pub const Condition = struct {
38 base: Scope,
39 block: ?Block = null,
40
41 fn getBlockScope(cond: *Condition, t: *Translator) !*Block {
42 if (cond.block) |*b| return b;
43 cond.block = try Block.init(t, &cond.base, true);
44 return &cond.block.?;
45 }
46
47 pub fn deinit(cond: *Condition) void {
48 if (cond.block) |*b| b.deinit();
49 }
50};
51
52/// Represents an in-progress Node.Block. This struct is stack-allocated.
53/// When it is deinitialized, it produces an Node.Block which is allocated
54/// into the main arena.
55pub const Block = struct {
56 base: Scope,
57 translator: *Translator,
58 statements: std.ArrayListUnmanaged(ast.Node),
59 variables: AliasList,
60 mangle_count: u32 = 0,
61 label: ?[]const u8 = null,
62
63 /// By default all variables are discarded, since we do not know in advance if they
64 /// will be used. This maps the variable's name to the Discard payload, so that if
65 /// the variable is subsequently referenced we can indicate that the discard should
66 /// be skipped during the intermediate AST -> Zig AST render step.
67 variable_discards: std.StringArrayHashMapUnmanaged(*ast.Payload.Discard),
68
69 /// When the block corresponds to a function, keep track of the return type
70 /// so that the return expression can be cast, if necessary
71 return_type: ?aro.QualType = null,
72
73 /// C static local variables are wrapped in a block-local struct. The struct
74 /// is named `mangle(static_local_ + name)` and the Zig variable within the
75 /// struct keeps the name of the C variable.
76 pub const static_local_prefix = "static_local";
77
78 /// C extern local variables are wrapped in a block-local struct. The struct
79 /// is named `mangle(extern_local + name)` and the Zig variable within the
80 /// struct keeps the name of the C variable.
81 pub const extern_local_prefix = "extern_local";
82
83 pub fn init(t: *Translator, parent: *Scope, labeled: bool) !Block {
84 var blk: Block = .{
85 .base = .{
86 .id = .block,
87 .parent = parent,
88 },
89 .translator = t,
90 .statements = .empty,
91 .variables = .empty,
92 .variable_discards = .empty,
93 };
94 if (labeled) {
95 blk.label = try blk.makeMangledName("blk");
96 }
97 return blk;
98 }
99
100 pub fn deinit(block: *Block) void {
101 block.statements.deinit(block.translator.gpa);
102 block.variables.deinit(block.translator.gpa);
103 block.variable_discards.deinit(block.translator.gpa);
104 block.* = undefined;
105 }
106
107 pub fn complete(block: *Block) !ast.Node {
108 const arena = block.translator.arena;
109 if (block.base.parent.?.id == .do_loop) {
110 // We reserve 1 extra statement if the parent is a do_loop. This is in case of
111 // do while, we want to put `if (cond) break;` at the end.
112 const alloc_len = block.statements.items.len + @intFromBool(block.base.parent.?.id == .do_loop);
113 var stmts = try arena.alloc(ast.Node, alloc_len);
114 stmts.len = block.statements.items.len;
115 @memcpy(stmts[0..block.statements.items.len], block.statements.items);
116 return ast.Node.Tag.block.create(arena, .{
117 .label = block.label,
118 .stmts = stmts,
119 });
120 }
121 if (block.statements.items.len == 0) return ast.Node.Tag.empty_block.init();
122 return ast.Node.Tag.block.create(arena, .{
123 .label = block.label,
124 .stmts = try arena.dupe(ast.Node, block.statements.items),
125 });
126 }
127
128 /// Given the desired name, return a name that does not shadow anything from outer scopes.
129 /// Inserts the returned name into the scope.
130 /// The name will not be visible to callers of getAlias.
131 pub fn reserveMangledName(block: *Block, name: []const u8) ![]const u8 {
132 return block.createMangledName(name, true, null);
133 }
134
135 /// Same as reserveMangledName, but enables the alias immediately.
136 pub fn makeMangledName(block: *Block, name: []const u8) ![]const u8 {
137 return block.createMangledName(name, false, null);
138 }
139
140 pub fn createMangledName(block: *Block, name: []const u8, reservation: bool, prefix_opt: ?[]const u8) ![]const u8 {
141 const arena = block.translator.arena;
142 const name_copy = try arena.dupe(u8, name);
143 const alias_base = if (prefix_opt) |prefix|
144 try std.fmt.allocPrint(arena, "{s}_{s}", .{ prefix, name })
145 else
146 name;
147 var proposed_name = alias_base;
148 while (block.contains(proposed_name)) {
149 block.mangle_count += 1;
150 proposed_name = try std.fmt.allocPrint(arena, "{s}_{d}", .{ alias_base, block.mangle_count });
151 }
152 const new_mangle = try block.variables.addOne(block.translator.gpa);
153 if (reservation) {
154 new_mangle.* = .{ .name = name_copy, .alias = name_copy };
155 } else {
156 new_mangle.* = .{ .name = name_copy, .alias = proposed_name };
157 }
158 return proposed_name;
159 }
160
161 fn getAlias(block: *Block, name: []const u8) ?[]const u8 {
162 for (block.variables.items) |p| {
163 if (std.mem.eql(u8, p.name, name))
164 return p.alias;
165 }
166 return block.base.parent.?.getAlias(name);
167 }
168
169 fn localContains(block: *Block, name: []const u8) bool {
170 for (block.variables.items) |p| {
171 if (std.mem.eql(u8, p.alias, name))
172 return true;
173 }
174 return false;
175 }
176
177 fn contains(block: *Block, name: []const u8) bool {
178 if (block.localContains(name))
179 return true;
180 return block.base.parent.?.contains(name);
181 }
182
183 pub fn discardVariable(block: *Block, name: []const u8) Translator.Error!void {
184 const gpa = block.translator.gpa;
185 const arena = block.translator.arena;
186 const name_node = try ast.Node.Tag.identifier.create(arena, name);
187 const discard = try ast.Node.Tag.discard.create(arena, .{ .should_skip = false, .value = name_node });
188 try block.statements.append(gpa, discard);
189 try block.variable_discards.putNoClobber(gpa, name, discard.castTag(.discard).?);
190 }
191};
192
193pub const Root = struct {
194 base: Scope,
195 translator: *Translator,
196 sym_table: SymbolTable,
197 blank_macros: std.StringArrayHashMapUnmanaged(void),
198 nodes: std.ArrayListUnmanaged(ast.Node),
199 container_member_fns_map: ContainerMemberFnsHashMap,
200
201 pub fn init(t: *Translator) Root {
202 return .{
203 .base = .{
204 .id = .root,
205 .parent = null,
206 },
207 .translator = t,
208 .sym_table = .empty,
209 .blank_macros = .empty,
210 .nodes = .empty,
211 .container_member_fns_map = .empty,
212 };
213 }
214
215 pub fn deinit(root: *Root) void {
216 root.sym_table.deinit(root.translator.gpa);
217 root.blank_macros.deinit(root.translator.gpa);
218 root.nodes.deinit(root.translator.gpa);
219 for (root.container_member_fns_map.values()) |*members| {
220 members.member_fns.deinit(root.translator.gpa);
221 }
222 root.container_member_fns_map.deinit(root.translator.gpa);
223 }
224
225 /// Check if the global scope contains this name, without looking into the "future", e.g.
226 /// ignore the preprocessed decl and macro names.
227 pub fn containsNow(root: *Root, name: []const u8) bool {
228 return root.sym_table.contains(name);
229 }
230
231 /// Check if the global scope contains the name, includes all decls that haven't been translated yet.
232 pub fn contains(root: *Root, name: []const u8) bool {
233 return root.containsNow(name) or root.translator.global_names.contains(name) or root.translator.weak_global_names.contains(name);
234 }
235
236 pub fn addMemberFunction(root: *Root, func_ty: aro.Type.Func, func: *ast.Payload.Func) !void {
237 std.debug.assert(func.data.name != null);
238 if (func_ty.params.len == 0) return;
239
240 const param1_base = func_ty.params[0].qt.base(root.translator.comp);
241 const container_qt = if (param1_base.type == .pointer)
242 param1_base.type.pointer.child.base(root.translator.comp).qt
243 else
244 param1_base.qt;
245
246 if (root.container_member_fns_map.getPtr(container_qt)) |members| {
247 try members.member_fns.append(root.translator.gpa, func);
248 }
249 }
250
251 pub fn processContainerMemberFns(root: *Root) !void {
252 const gpa = root.translator.gpa;
253 const arena = root.translator.arena;
254
255 var member_names: std.StringArrayHashMapUnmanaged(u32) = .empty;
256 defer member_names.deinit(gpa);
257 for (root.container_member_fns_map.values()) |members| {
258 member_names.clearRetainingCapacity();
259 const decls_ptr = switch (members.container_decl_ptr.tag()) {
260 .@"struct", .@"union" => blk_record: {
261 const payload: *ast.Payload.Container = @alignCast(@fieldParentPtr("base", members.container_decl_ptr.ptr_otherwise));
262 // Avoid duplication with field names
263 for (payload.data.fields) |field| {
264 try member_names.put(gpa, field.name, 0);
265 }
266 break :blk_record &payload.data.decls;
267 },
268 .opaque_literal => blk_opaque: {
269 const container_decl = try ast.Node.Tag.@"opaque".create(arena, .{
270 .layout = .none,
271 .fields = &.{},
272 .decls = &.{},
273 });
274 members.container_decl_ptr.* = container_decl;
275 break :blk_opaque &container_decl.castTag(.@"opaque").?.data.decls;
276 },
277 else => return,
278 };
279
280 const old_decls = decls_ptr.*;
281 const new_decls = try arena.alloc(ast.Node, old_decls.len + members.member_fns.items.len);
282 @memcpy(new_decls[0..old_decls.len], old_decls);
283 // Assume the allocator of payload.data.decls is arena,
284 // so don't add arena.free(old_variables).
285 const func_ref_vars = new_decls[old_decls.len..];
286 var count: u32 = 0;
287 for (members.member_fns.items) |func| {
288 const func_name = func.data.name.?;
289
290 const last_index = std.mem.lastIndexOf(u8, func_name, "_");
291 const last_name = if (last_index) |index| func_name[index + 1 ..] else continue;
292 var same_count: u32 = 0;
293 const gop = try member_names.getOrPutValue(gpa, last_name, same_count);
294 if (gop.found_existing) {
295 gop.value_ptr.* += 1;
296 same_count = gop.value_ptr.*;
297 }
298 const var_name = if (same_count == 0)
299 last_name
300 else
301 try std.fmt.allocPrint(arena, "{s}{d}", .{ last_name, same_count });
302
303 func_ref_vars[count] = try ast.Node.Tag.pub_var_simple.create(arena, .{
304 .name = var_name,
305 .init = try ast.Node.Tag.identifier.create(arena, func_name),
306 });
307 count += 1;
308 }
309 decls_ptr.* = new_decls[0 .. old_decls.len + count];
310 }
311 }
312};
313
314pub fn findBlockScope(inner: *Scope, t: *Translator) !*Block {
315 var scope = inner;
316 while (true) {
317 switch (scope.id) {
318 .root => unreachable,
319 .block => return @fieldParentPtr("base", scope),
320 .condition => return @as(*Condition, @fieldParentPtr("base", scope)).getBlockScope(t),
321 else => scope = scope.parent.?,
322 }
323 }
324}
325
326pub fn findBlockReturnType(inner: *Scope) aro.QualType {
327 var scope = inner;
328 while (true) {
329 switch (scope.id) {
330 .root => unreachable,
331 .block => {
332 const block: *Block = @fieldParentPtr("base", scope);
333 if (block.return_type) |qt| return qt;
334 scope = scope.parent.?;
335 },
336 else => scope = scope.parent.?,
337 }
338 }
339}
340
341pub fn getAlias(scope: *Scope, name: []const u8) ?[]const u8 {
342 return switch (scope.id) {
343 .root => null,
344 .block => @as(*Block, @fieldParentPtr("base", scope)).getAlias(name),
345 .loop, .do_loop, .condition => scope.parent.?.getAlias(name),
346 };
347}
348
349fn contains(scope: *Scope, name: []const u8) bool {
350 return switch (scope.id) {
351 .root => @as(*Root, @fieldParentPtr("base", scope)).contains(name),
352 .block => @as(*Block, @fieldParentPtr("base", scope)).contains(name),
353 .loop, .do_loop, .condition => scope.parent.?.contains(name),
354 };
355}
356
357/// Appends a node to the first block scope if inside a function, or to the root tree if not.
358pub fn appendNode(inner: *Scope, node: ast.Node) !void {
359 var scope = inner;
360 while (true) {
361 switch (scope.id) {
362 .root => {
363 const root: *Root = @fieldParentPtr("base", scope);
364 return root.nodes.append(root.translator.gpa, node);
365 },
366 .block => {
367 const block: *Block = @fieldParentPtr("base", scope);
368 return block.statements.append(block.translator.gpa, node);
369 },
370 else => scope = scope.parent.?,
371 }
372 }
373}
374
375pub fn skipVariableDiscard(inner: *Scope, name: []const u8) void {
376 if (true) {
377 // TODO: due to 'local variable is never mutated' errors, we can
378 // only skip discards if a variable is used as an lvalue, which
379 // we don't currently have detection for in translate-c.
380 // Once #17584 is completed, perhaps we can do away with this
381 // logic entirely, and instead rely on render to fixup code.
382 return;
383 }
384 var scope = inner;
385 while (true) {
386 switch (scope.id) {
387 .root => return,
388 .block => {
389 const block: *Block = @fieldParentPtr("base", scope);
390 if (block.variable_discards.get(name)) |discard| {
391 discard.data.should_skip = true;
392 return;
393 }
394 },
395 else => {},
396 }
397 scope = scope.parent.?;
398 }
399}
lib/compiler/translate-c/src/Translator.zig deleted-4183
...@@ -1,4183 +0,0 @@
1const std = @import("std");
2const mem = std.mem;
3const assert = std.debug.assert;
4const CallingConvention = std.builtin.CallingConvention;
5
6const aro = @import("aro");
7const CToken = aro.Tokenizer.Token;
8const Tree = aro.Tree;
9const Node = Tree.Node;
10const TokenIndex = Tree.TokenIndex;
11const QualType = aro.QualType;
12
13const ast = @import("ast.zig");
14const ZigNode = ast.Node;
15const ZigTag = ZigNode.Tag;
16const builtins = @import("builtins.zig");
17const helpers = @import("helpers.zig");
18const MacroTranslator = @import("MacroTranslator.zig");
19const PatternList = @import("PatternList.zig");
20const Scope = @import("Scope.zig");
21
22pub const Error = std.mem.Allocator.Error;
23pub const MacroProcessingError = Error || error{UnexpectedMacroToken};
24pub const TypeError = Error || error{UnsupportedType};
25pub const TransError = TypeError || error{UnsupportedTranslation};
26
27const Translator = @This();
28
29/// The C AST to be translated.
30tree: *const Tree,
31/// The compilation corresponding to the AST.
32comp: *aro.Compilation,
33/// The Preprocessor that produced the source for `tree`.
34pp: *const aro.Preprocessor,
35
36gpa: mem.Allocator,
37arena: mem.Allocator,
38
39alias_list: Scope.AliasList,
40global_scope: *Scope.Root,
41/// Running number used for creating new unique identifiers.
42mangle_count: u32 = 0,
43
44/// Table of declarations for enum, struct, union and typedef types.
45type_decls: std.AutoArrayHashMapUnmanaged(Node.Index, []const u8) = .empty,
46/// Table of record decls that have been demoted to opaques.
47opaque_demotes: std.AutoHashMapUnmanaged(QualType, void) = .empty,
48/// Table of unnamed enums and records that are child types of typedefs.
49unnamed_typedefs: std.AutoHashMapUnmanaged(QualType, []const u8) = .empty,
50/// Table of anonymous record to generated field names.
51anonymous_record_field_names: std.AutoHashMapUnmanaged(struct {
52 parent: QualType,
53 field: QualType,
54}, []const u8) = .empty,
55
56/// This one is different than the root scope's name table. This contains
57/// a list of names that we found by visiting all the top level decls without
58/// translating them. The other maps are updated as we translate; this one is updated
59/// up front in a pre-processing step.
60global_names: std.StringArrayHashMapUnmanaged(void) = .empty,
61
62/// This is similar to `global_names`, but contains names which we would
63/// *like* to use, but do not strictly *have* to if they are unavailable.
64/// These are relevant to types, which ideally we would name like
65/// 'struct_foo' with an alias 'foo', but if either of those names is taken,
66/// may be mangled.
67/// This is distinct from `global_names` so we can detect at a type
68/// declaration whether or not the name is available.
69weak_global_names: std.StringArrayHashMapUnmanaged(void) = .empty,
70
71/// Set of identifiers known to refer to typedef declarations.
72/// Used when parsing macros.
73typedefs: std.StringArrayHashMapUnmanaged(void) = .empty,
74
75/// The lhs lval of a compound assignment expression.
76compound_assign_dummy: ?ZigNode = null,
77
78pub fn getMangle(t: *Translator) u32 {
79 t.mangle_count += 1;
80 return t.mangle_count;
81}
82
83/// Convert an `aro.Source.Location` to a 'file:line:column' string.
84pub fn locStr(t: *Translator, loc: aro.Source.Location) ![]const u8 {
85 const source = t.comp.getSource(loc.id);
86 const line_col = source.lineCol(loc);
87 const filename = source.path;
88
89 const line = source.physicalLine(loc);
90 const col = line_col.col;
91
92 return std.fmt.allocPrint(t.arena, "{s}:{d}:{d}", .{ filename, line, col });
93}
94
95fn maybeSuppressResult(t: *Translator, used: ResultUsed, result: ZigNode) TransError!ZigNode {
96 if (used == .used) return result;
97 return ZigTag.discard.create(t.arena, .{ .should_skip = false, .value = result });
98}
99
100pub fn addTopLevelDecl(t: *Translator, name: []const u8, decl_node: ZigNode) !void {
101 const gop = try t.global_scope.sym_table.getOrPut(t.gpa, name);
102 if (!gop.found_existing) {
103 gop.value_ptr.* = decl_node;
104 try t.global_scope.nodes.append(t.gpa, decl_node);
105 }
106}
107
108fn fail(
109 t: *Translator,
110 err: anytype,
111 source_loc: TokenIndex,
112 comptime format: []const u8,
113 args: anytype,
114) (@TypeOf(err) || error{OutOfMemory}) {
115 try t.warn(&t.global_scope.base, source_loc, format, args);
116 return err;
117}
118
119pub fn failDecl(
120 t: *Translator,
121 scope: *Scope,
122 tok_idx: TokenIndex,
123 name: []const u8,
124 comptime format: []const u8,
125 args: anytype,
126) Error!void {
127 const loc = t.tree.tokens.items(.loc)[tok_idx];
128 return t.failDeclExtra(scope, loc, name, format, args);
129}
130
131pub fn failDeclExtra(
132 t: *Translator,
133 scope: *Scope,
134 loc: aro.Source.Location,
135 name: []const u8,
136 comptime format: []const u8,
137 args: anytype,
138) Error!void {
139 // location
140 // pub const name = @compileError(msg);
141 const fail_msg = try std.fmt.allocPrint(t.arena, format, args);
142 const fail_decl = try ZigTag.fail_decl.create(t.arena, .{ .actual = name, .mangled = fail_msg });
143
144 const str = try t.locStr(loc);
145 const location_comment = try std.fmt.allocPrint(t.arena, "// {s}", .{str});
146 const loc_node = try ZigTag.warning.create(t.arena, location_comment);
147
148 if (scope.id == .root) {
149 try t.addTopLevelDecl(name, fail_decl);
150 try scope.appendNode(loc_node);
151 } else {
152 try scope.appendNode(fail_decl);
153 try scope.appendNode(loc_node);
154
155 const bs = try scope.findBlockScope(t);
156 try bs.discardVariable(name);
157 }
158}
159
160fn warn(t: *Translator, scope: *Scope, tok_idx: TokenIndex, comptime format: []const u8, args: anytype) !void {
161 const loc = t.tree.tokens.items(.loc)[tok_idx];
162 const str = try t.locStr(loc);
163 const value = try std.fmt.allocPrint(t.arena, "// {s}: warning: " ++ format, .{str} ++ args);
164 try scope.appendNode(try ZigTag.warning.create(t.arena, value));
165}
166
167pub const Options = struct {
168 gpa: mem.Allocator,
169 comp: *aro.Compilation,
170 pp: *const aro.Preprocessor,
171 tree: *const aro.Tree,
172 module_libs: bool,
173};
174
175pub fn translate(options: Options) ![]u8 {
176 const gpa = options.gpa;
177 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
178 defer arena_allocator.deinit();
179 const arena = arena_allocator.allocator();
180
181 var translator: Translator = .{
182 .gpa = gpa,
183 .arena = arena,
184 .alias_list = .empty,
185 .global_scope = try arena.create(Scope.Root),
186 .comp = options.comp,
187 .pp = options.pp,
188 .tree = options.tree,
189 };
190 translator.global_scope.* = Scope.Root.init(&translator);
191 defer {
192 translator.type_decls.deinit(gpa);
193 translator.alias_list.deinit(gpa);
194 translator.global_names.deinit(gpa);
195 translator.weak_global_names.deinit(gpa);
196 translator.opaque_demotes.deinit(gpa);
197 translator.unnamed_typedefs.deinit(gpa);
198 translator.anonymous_record_field_names.deinit(gpa);
199 translator.typedefs.deinit(gpa);
200 translator.global_scope.deinit();
201 }
202
203 try translator.prepopulateGlobalNameTable();
204 try translator.transTopLevelDecls();
205
206 // Insert empty line before macros.
207 try translator.global_scope.nodes.append(gpa, try ZigTag.warning.create(arena, "\n"));
208
209 try translator.transMacros();
210
211 for (translator.alias_list.items) |alias| {
212 if (!translator.global_scope.sym_table.contains(alias.alias)) {
213 const node = try ZigTag.alias.create(arena, .{ .actual = alias.alias, .mangled = alias.name });
214 try translator.addTopLevelDecl(alias.alias, node);
215 }
216 }
217
218 try translator.global_scope.processContainerMemberFns();
219
220 var buf: std.ArrayList(u8) = .init(gpa);
221 defer buf.deinit();
222
223 if (options.module_libs) {
224 try buf.appendSlice(
225 \\pub const __builtin = @import("c_builtins");
226 \\pub const __helpers = @import("helpers");
227 \\
228 \\
229 );
230 } else {
231 try buf.appendSlice(
232 \\pub const __builtin = @import("c_builtins.zig");
233 \\pub const __helpers = @import("helpers.zig");
234 \\
235 \\
236 );
237 }
238
239 var zig_ast = try ast.render(gpa, translator.global_scope.nodes.items);
240 defer {
241 gpa.free(zig_ast.source);
242 zig_ast.deinit(gpa);
243 }
244 try zig_ast.renderToArrayList(&buf, .{});
245 return buf.toOwnedSlice();
246}
247
248fn prepopulateGlobalNameTable(t: *Translator) !void {
249 for (t.tree.root_decls.items) |decl| {
250 switch (decl.get(t.tree)) {
251 .typedef => |typedef_decl| {
252 const decl_name = t.tree.tokSlice(typedef_decl.name_tok);
253 try t.global_names.put(t.gpa, decl_name, {});
254
255 // Check for typedefs with unnamed enum/record child types.
256 const base = typedef_decl.qt.base(t.comp);
257 switch (base.type) {
258 .@"enum" => |enum_ty| {
259 if (enum_ty.name.lookup(t.comp)[0] != '(') continue;
260 },
261 .@"struct", .@"union" => |record_ty| {
262 if (record_ty.name.lookup(t.comp)[0] != '(') continue;
263 },
264 else => continue,
265 }
266
267 const gop = try t.unnamed_typedefs.getOrPut(t.gpa, base.qt);
268 if (gop.found_existing) {
269 // One typedef can declare multiple names.
270 // TODO Don't put this one in `decl_table` so it's processed later.
271 continue;
272 }
273 gop.value_ptr.* = decl_name;
274 },
275
276 .struct_decl,
277 .union_decl,
278 .struct_forward_decl,
279 .union_forward_decl,
280 .enum_decl,
281 .enum_forward_decl,
282 => {
283 const decl_qt = decl.qt(t.tree);
284 const prefix, const name = switch (decl_qt.base(t.comp).type) {
285 .@"struct" => |struct_ty| .{ "struct", struct_ty.name.lookup(t.comp) },
286 .@"union" => |union_ty| .{ "union", union_ty.name.lookup(t.comp) },
287 .@"enum" => |enum_ty| .{ "enum", enum_ty.name.lookup(t.comp) },
288 else => unreachable,
289 };
290 const prefixed_name = try std.fmt.allocPrint(t.arena, "{s}_{s}", .{ prefix, name });
291 // `name` and `prefixed_name` are the preferred names for this type.
292 // However, we can name it anything else if necessary, so these are "weak names".
293 try t.weak_global_names.ensureUnusedCapacity(t.gpa, 2);
294 t.weak_global_names.putAssumeCapacity(name, {});
295 t.weak_global_names.putAssumeCapacity(prefixed_name, {});
296 },
297
298 .function, .variable => {
299 const decl_name = t.tree.tokSlice(decl.tok(t.tree));
300 try t.global_names.put(t.gpa, decl_name, {});
301 },
302 .static_assert => {},
303 .empty_decl => {},
304 .global_asm => {},
305 else => unreachable,
306 }
307 }
308
309 for (t.pp.defines.keys(), t.pp.defines.values()) |name, macro| {
310 if (macro.is_builtin) continue;
311 if (!t.isSelfDefinedMacro(name, macro)) {
312 try t.global_names.put(t.gpa, name, {});
313 }
314 }
315}
316
317/// Determines whether macro is of the form: `#define FOO FOO` (Possibly with trailing tokens)
318/// Macros of this form will not be translated.
319fn isSelfDefinedMacro(t: *Translator, name: []const u8, macro: aro.Preprocessor.Macro) bool {
320 if (macro.is_func) return false;
321
322 if (macro.tokens.len < 1) return false;
323 const first_tok = macro.tokens[0];
324
325 const source = t.comp.getSource(macro.loc.id);
326 const slice = source.buf[first_tok.start..first_tok.end];
327
328 return std.mem.eql(u8, name, slice);
329}
330
331// =======================
332// Declaration translation
333// =======================
334
335fn transTopLevelDecls(t: *Translator) !void {
336 for (t.tree.root_decls.items) |decl| {
337 try t.transDecl(&t.global_scope.base, decl);
338 }
339}
340
341fn transDecl(t: *Translator, scope: *Scope, decl: Node.Index) !void {
342 switch (decl.get(t.tree)) {
343 .typedef => |typedef_decl| {
344 // Implicit typedefs are translated only if referenced.
345 if (typedef_decl.implicit) return;
346 try t.transTypeDef(scope, decl);
347 },
348
349 .struct_decl, .union_decl => |record_decl| {
350 try t.transRecordDecl(scope, record_decl.container_qt);
351 },
352
353 .enum_decl => |enum_decl| {
354 try t.transEnumDecl(scope, enum_decl.container_qt);
355 },
356
357 .enum_field,
358 .record_field,
359 .struct_forward_decl,
360 .union_forward_decl,
361 .enum_forward_decl,
362 => return,
363
364 .function => |function| {
365 if (function.definition) |definition| {
366 return t.transFnDecl(scope, definition.get(t.tree).function);
367 }
368 try t.transFnDecl(scope, function);
369 },
370
371 .variable => |variable| {
372 if (variable.definition != null) return;
373 try t.transVarDecl(scope, variable);
374 },
375 .static_assert => |static_assert| {
376 try t.transStaticAssert(&t.global_scope.base, static_assert);
377 },
378 .global_asm => |global_asm| {
379 try t.transGlobalAsm(&t.global_scope.base, global_asm);
380 },
381 .empty_decl => {},
382 else => unreachable,
383 }
384}
385
386pub const builtin_typedef_map = std.StaticStringMap([]const u8).initComptime(.{
387 .{ "uint8_t", "u8" },
388 .{ "int8_t", "i8" },
389 .{ "uint16_t", "u16" },
390 .{ "int16_t", "i16" },
391 .{ "uint32_t", "u32" },
392 .{ "int32_t", "i32" },
393 .{ "uint64_t", "u64" },
394 .{ "int64_t", "i64" },
395 .{ "intptr_t", "isize" },
396 .{ "uintptr_t", "usize" },
397 .{ "ssize_t", "isize" },
398 .{ "size_t", "usize" },
399});
400
401fn transTypeDef(t: *Translator, scope: *Scope, typedef_node: Node.Index) Error!void {
402 const typedef_decl = typedef_node.get(t.tree).typedef;
403 if (t.type_decls.get(typedef_node)) |_|
404 return; // Avoid processing this decl twice
405
406 const toplevel = scope.id == .root;
407 const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(t) else undefined;
408
409 var name: []const u8 = t.tree.tokSlice(typedef_decl.name_tok);
410 try t.typedefs.put(t.gpa, name, {});
411
412 if (builtin_typedef_map.get(name)) |builtin| {
413 return t.type_decls.putNoClobber(t.gpa, typedef_node, builtin);
414 }
415 if (!toplevel) name = try bs.makeMangledName(name);
416 try t.type_decls.putNoClobber(t.gpa, typedef_node, name);
417
418 const typedef_loc = typedef_decl.name_tok;
419 const init_node = t.transType(scope, typedef_decl.qt, typedef_loc) catch |err| switch (err) {
420 error.UnsupportedType => {
421 return t.failDecl(scope, typedef_loc, name, "unable to resolve typedef child type", .{});
422 },
423 error.OutOfMemory => |e| return e,
424 };
425
426 const payload = try t.arena.create(ast.Payload.SimpleVarDecl);
427 payload.* = .{
428 .base = .{ .tag = if (toplevel) .pub_var_simple else .var_simple },
429 .data = .{
430 .name = name,
431 .init = init_node,
432 },
433 };
434 const node = ZigNode.initPayload(&payload.base);
435
436 if (toplevel) {
437 try t.addTopLevelDecl(name, node);
438 } else {
439 try scope.appendNode(node);
440 try bs.discardVariable(name);
441 }
442}
443
444fn mangleWeakGlobalName(t: *Translator, want_name: []const u8) Error![]const u8 {
445 var cur_name = want_name;
446
447 if (!t.weak_global_names.contains(want_name)) {
448 // This type wasn't noticed by the name detection pass, so nothing has been treating this as
449 // a weak global name. We must mangle it to avoid conflicts with locals.
450 cur_name = try std.fmt.allocPrint(t.arena, "{s}_{d}", .{ want_name, t.getMangle() });
451 }
452
453 while (t.global_names.contains(cur_name)) {
454 cur_name = try std.fmt.allocPrint(t.arena, "{s}_{d}", .{ want_name, t.getMangle() });
455 }
456 return cur_name;
457}
458
459fn transRecordDecl(t: *Translator, scope: *Scope, record_qt: QualType) Error!void {
460 const base = record_qt.base(t.comp);
461 const record_ty = switch (base.type) {
462 .@"struct", .@"union" => |record_ty| record_ty,
463 else => unreachable,
464 };
465
466 if (t.type_decls.get(record_ty.decl_node)) |_|
467 return; // Avoid processing this decl twice
468
469 const toplevel = scope.id == .root;
470 const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(t) else undefined;
471
472 const container_kind: ZigTag = if (base.type == .@"union") .@"union" else .@"struct";
473 const container_kind_name = @tagName(container_kind);
474
475 var bare_name = record_ty.name.lookup(t.comp);
476 var is_unnamed = false;
477 var name = bare_name;
478
479 if (t.unnamed_typedefs.get(base.qt)) |typedef_name| {
480 bare_name = typedef_name;
481 name = typedef_name;
482 } else {
483 if (record_ty.isAnonymous(t.comp)) {
484 bare_name = try std.fmt.allocPrint(t.arena, "unnamed_{d}", .{t.getMangle()});
485 is_unnamed = true;
486 }
487 name = try std.fmt.allocPrint(t.arena, "{s}_{s}", .{ container_kind_name, bare_name });
488 if (toplevel and !is_unnamed) {
489 name = try t.mangleWeakGlobalName(name);
490 }
491 }
492 if (!toplevel) name = try bs.makeMangledName(name);
493 try t.type_decls.putNoClobber(t.gpa, record_ty.decl_node, name);
494
495 const is_pub = toplevel and !is_unnamed;
496 const init_node = init: {
497 if (record_ty.layout == null) {
498 try t.opaque_demotes.put(t.gpa, base.qt, {});
499 break :init ZigTag.opaque_literal.init();
500 }
501
502 var fields = try std.ArrayList(ast.Payload.Container.Field).initCapacity(t.gpa, record_ty.fields.len);
503 defer fields.deinit();
504
505 var functions = std.ArrayList(ZigNode).init(t.gpa);
506 defer functions.deinit();
507
508 var unnamed_field_count: u32 = 0;
509
510 // If a record doesn't have any attributes that would affect the alignment and
511 // layout, then we can just use a simple `extern` type. If it does have attributes,
512 // then we need to inspect the layout and assign an `align` value for each field.
513 const has_alignment_attributes = aligned: {
514 if (record_qt.hasAttribute(t.comp, .@"packed")) break :aligned true;
515 if (record_qt.hasAttribute(t.comp, .aligned)) break :aligned true;
516 for (record_ty.fields) |field| {
517 const field_attrs = field.attributes(t.comp);
518 for (field_attrs) |field_attr| {
519 switch (field_attr.tag) {
520 .@"packed", .aligned => break :aligned true,
521 else => {},
522 }
523 }
524 }
525 break :aligned false;
526 };
527 const head_field_alignment: ?c_uint = if (has_alignment_attributes) t.headFieldAlignment(record_ty) else null;
528
529 for (record_ty.fields, 0..) |field, field_index| {
530 const field_loc = field.name_tok;
531
532 // Demote record to opaque if it contains a bitfield
533 if (field.bit_width != .null) {
534 try t.opaque_demotes.put(t.gpa, base.qt, {});
535 try t.warn(scope, field_loc, "{s} demoted to opaque type - has bitfield", .{container_kind_name});
536 break :init ZigTag.opaque_literal.init();
537 }
538
539 var field_name = field.name.lookup(t.comp);
540 if (field.name_tok == 0) {
541 field_name = try std.fmt.allocPrint(t.arena, "unnamed_{d}", .{unnamed_field_count});
542 unnamed_field_count += 1;
543 try t.anonymous_record_field_names.put(t.gpa, .{
544 .parent = base.qt,
545 .field = field.qt,
546 }, field_name);
547 }
548
549 const field_alignment = if (has_alignment_attributes)
550 t.alignmentForField(record_ty, head_field_alignment, field_index)
551 else
552 null;
553
554 const field_type = field_type: {
555 // Check if this is a flexible array member.
556 flexible: {
557 if (field_index != record_ty.fields.len - 1 and container_kind != .@"union") break :flexible;
558 const array_ty = field.qt.get(t.comp, .array) orelse break :flexible;
559 if (array_ty.len != .incomplete and (array_ty.len != .fixed or array_ty.len.fixed != 0)) break :flexible;
560
561 const elem_type = t.transType(scope, array_ty.elem, field_loc) catch |err| switch (err) {
562 error.UnsupportedType => break :flexible,
563 else => |e| return e,
564 };
565 const zero_array = try ZigTag.array_type.create(t.arena, .{ .len = 0, .elem_type = elem_type });
566
567 const member_name = field_name;
568 field_name = try std.fmt.allocPrint(t.arena, "_{s}", .{field_name});
569
570 const member = try t.createFlexibleMemberFn(member_name, field_name);
571 try functions.append(member);
572
573 break :field_type zero_array;
574 }
575
576 break :field_type t.transType(scope, field.qt, field_loc) catch |err| switch (err) {
577 error.UnsupportedType => {
578 try t.opaque_demotes.put(t.gpa, base.qt, {});
579 try t.warn(scope, field.name_tok, "{s} demoted to opaque type - unable to translate type of field {s}", .{
580 container_kind_name,
581 field_name,
582 });
583 break :init ZigTag.opaque_literal.init();
584 },
585 else => |e| return e,
586 };
587 };
588
589 // C99 introduced designated initializers for structs. Omitted fields are implicitly
590 // initialized to zero. Some C APIs are designed with this in mind. Defaulting to zero
591 // values for translated struct fields permits Zig code to comfortably use such an API.
592 const default_value = if (container_kind == .@"struct")
593 try t.createZeroValueNode(field.qt, field_type, .no_as)
594 else
595 null;
596
597 fields.appendAssumeCapacity(.{
598 .name = field_name,
599 .type = field_type,
600 .alignment = field_alignment,
601 .default_value = default_value,
602 });
603 }
604
605 // A record is empty if it has no fields or only flexible array fields.
606 if (record_ty.fields.len == functions.items.len and
607 t.comp.target.os.tag == .windows and t.comp.target.abi == .msvc)
608 {
609 // In MSVC empty records have the same size as their alignment.
610 const padding_bits = record_ty.layout.?.size_bits;
611 const alignment_bits = record_ty.layout.?.field_alignment_bits;
612
613 try fields.append(.{
614 .name = "_padding",
615 .type = try ZigTag.type.create(t.arena, try std.fmt.allocPrint(t.arena, "u{d}", .{padding_bits})),
616 .alignment = @divExact(alignment_bits, 8),
617 .default_value = if (container_kind == .@"struct")
618 ZigTag.zero_literal.init()
619 else
620 null,
621 });
622 }
623
624 const container_payload = try t.arena.create(ast.Payload.Container);
625 container_payload.* = .{
626 .base = .{ .tag = container_kind },
627 .data = .{
628 .layout = .@"extern",
629 .fields = try t.arena.dupe(ast.Payload.Container.Field, fields.items),
630 .decls = try t.arena.dupe(ZigNode, functions.items),
631 },
632 };
633 break :init ZigNode.initPayload(&container_payload.base);
634 };
635
636 const payload = try t.arena.create(ast.Payload.SimpleVarDecl);
637 payload.* = .{
638 .base = .{ .tag = if (is_pub) .pub_var_simple else .var_simple },
639 .data = .{
640 .name = name,
641 .init = init_node,
642 },
643 };
644 const node = ZigNode.initPayload(&payload.base);
645 if (toplevel) {
646 try t.addTopLevelDecl(name, node);
647 // Only add the alias if the name is available *and* it was caught by
648 // name detection. Don't bother performing a weak mangle, since a
649 // mangled name is of no real use here.
650 if (!is_unnamed and !t.global_names.contains(bare_name) and t.weak_global_names.contains(bare_name))
651 try t.alias_list.append(t.gpa, .{ .alias = bare_name, .name = name });
652 try t.global_scope.container_member_fns_map.put(t.gpa, record_qt, .{
653 .container_decl_ptr = &payload.data.init,
654 });
655 } else {
656 try scope.appendNode(node);
657 try bs.discardVariable(name);
658 }
659}
660
661fn transFnDecl(t: *Translator, scope: *Scope, function: Node.Function) Error!void {
662 const func_ty = function.qt.get(t.comp, .func).?;
663
664 const is_pub = scope.id == .root;
665
666 const fn_name = t.tree.tokSlice(function.name_tok);
667 if (scope.getAlias(fn_name) != null or t.global_scope.containsNow(fn_name))
668 return; // Avoid processing this decl twice
669
670 const fn_decl_loc = function.name_tok;
671 const has_body = function.body != null and func_ty.kind != .variadic;
672 if (function.body != null and func_ty.kind == .variadic) {
673 try t.warn(scope, function.name_tok, "TODO unable to translate variadic function, demoted to extern", .{});
674 }
675
676 const is_always_inline = has_body and function.qt.getAttribute(t.comp, .always_inline) != null;
677 const proto_ctx: FnProtoContext = .{
678 .fn_name = fn_name,
679 .is_always_inline = is_always_inline,
680 .is_extern = !has_body,
681 .is_export = !function.static and has_body and !is_always_inline and !function.@"inline",
682 .is_pub = is_pub,
683 .has_body = has_body,
684 .cc = if (function.qt.getAttribute(t.comp, .calling_convention)) |some| switch (some.cc) {
685 .c => .c,
686 .stdcall => .x86_stdcall,
687 .thiscall => .x86_thiscall,
688 .fastcall => .x86_fastcall,
689 .regcall => .x86_regcall,
690 .riscv_vector => .riscv_vector,
691 .aarch64_sve_pcs => .aarch64_sve_pcs,
692 .aarch64_vector_pcs => .aarch64_vfabi,
693 .arm_aapcs => .arm_aapcs,
694 .arm_aapcs_vfp => .arm_aapcs_vfp,
695 .vectorcall => switch (t.comp.target.cpu.arch) {
696 .x86 => .x86_vectorcall,
697 .aarch64, .aarch64_be => .aarch64_vfabi,
698 else => .c,
699 },
700 .x86_64_sysv => .x86_64_sysv,
701 .x86_64_win => .x86_64_win,
702 } else .c,
703 };
704
705 const proto_node = t.transFnType(&t.global_scope.base, function.qt, func_ty, fn_decl_loc, proto_ctx) catch |err| switch (err) {
706 error.UnsupportedType => {
707 return t.failDecl(scope, fn_decl_loc, fn_name, "unable to resolve prototype of function", .{});
708 },
709 error.OutOfMemory => |e| return e,
710 };
711
712 const proto_payload = proto_node.castTag(.func).?;
713 if (!has_body) {
714 if (scope.id != .root) {
715 const bs: *Scope.Block = try scope.findBlockScope(t);
716 const mangled_name = try bs.createMangledName(fn_name, false, Scope.Block.extern_local_prefix);
717 const wrapped = try ZigTag.wrapped_local.create(t.arena, .{ .name = mangled_name, .init = proto_node });
718 try scope.appendNode(wrapped);
719 try bs.discardVariable(mangled_name);
720 return;
721 }
722 try t.global_scope.addMemberFunction(func_ty, proto_payload);
723 return t.addTopLevelDecl(fn_name, proto_node);
724 }
725
726 // actual function definition with body
727 const body_stmt = function.body.?.get(t.tree).compound_stmt;
728 var block_scope = try Scope.Block.init(t, &t.global_scope.base, false);
729 block_scope.return_type = func_ty.return_type;
730 defer block_scope.deinit();
731
732 var param_id: c_uint = 0;
733 for (proto_payload.data.params, func_ty.params) |*param, param_info| {
734 const param_name = param.name orelse {
735 proto_payload.data.is_extern = true;
736 proto_payload.data.is_export = false;
737 proto_payload.data.is_inline = false;
738 try t.warn(&t.global_scope.base, fn_decl_loc, "function {s} parameter has no name, demoted to extern", .{fn_name});
739 return t.addTopLevelDecl(fn_name, proto_node);
740 };
741
742 const is_const = param_info.qt.@"const";
743
744 const mangled_param_name = try block_scope.makeMangledName(param_name);
745 param.name = mangled_param_name;
746
747 if (!is_const) {
748 const bare_arg_name = try std.fmt.allocPrint(t.arena, "arg_{s}", .{mangled_param_name});
749 const arg_name = try block_scope.makeMangledName(bare_arg_name);
750 param.name = arg_name;
751
752 const redecl_node = try ZigTag.arg_redecl.create(t.arena, .{ .actual = mangled_param_name, .mangled = arg_name });
753 try block_scope.statements.append(t.gpa, redecl_node);
754 }
755 try block_scope.discardVariable(mangled_param_name);
756
757 param_id += 1;
758 }
759
760 t.transCompoundStmtInline(body_stmt, &block_scope) catch |err| switch (err) {
761 error.OutOfMemory => |e| return e,
762 error.UnsupportedTranslation,
763 error.UnsupportedType,
764 => {
765 proto_payload.data.is_extern = true;
766 proto_payload.data.is_export = false;
767 proto_payload.data.is_inline = false;
768 try t.warn(&t.global_scope.base, fn_decl_loc, "unable to translate function, demoted to extern", .{});
769 return t.addTopLevelDecl(fn_name, proto_node);
770 },
771 };
772
773 try t.global_scope.addMemberFunction(func_ty, proto_payload);
774 proto_payload.data.body = try block_scope.complete();
775 return t.addTopLevelDecl(fn_name, proto_node);
776}
777
778fn transVarDecl(t: *Translator, scope: *Scope, variable: Node.Variable) Error!void {
779 const base_name = t.tree.tokSlice(variable.name_tok);
780 const toplevel = scope.id == .root;
781 const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(t) else undefined;
782 const name, const use_base_name = blk: {
783 if (toplevel) break :blk .{ base_name, false };
784
785 // Local extern and static variables are wrapped in a struct.
786 const prefix: ?[]const u8 = switch (variable.storage_class) {
787 .@"extern" => Scope.Block.extern_local_prefix,
788 .static => Scope.Block.static_local_prefix,
789 else => null,
790 };
791 break :blk .{ try bs.createMangledName(base_name, false, prefix), prefix != null };
792 };
793
794 if (t.typeWasDemotedToOpaque(variable.qt)) {
795 if (variable.storage_class != .@"extern" and scope.id == .root) {
796 return t.failDecl(scope, variable.name_tok, name, "non-extern variable has opaque type", .{});
797 } else {
798 return t.failDecl(scope, variable.name_tok, name, "local variable has opaque type", .{});
799 }
800 }
801
802 const type_node = (if (variable.initializer) |init|
803 t.transTypeInit(scope, variable.qt, init, variable.name_tok)
804 else
805 t.transType(scope, variable.qt, variable.name_tok)) catch |err| switch (err) {
806 error.UnsupportedType => {
807 return t.failDecl(scope, variable.name_tok, name, "unable to translate variable declaration type", .{});
808 },
809 else => |e| return e,
810 };
811
812 const array_ty = variable.qt.get(t.comp, .array);
813 var is_const = variable.qt.@"const" or (array_ty != null and array_ty.?.elem.@"const");
814 var is_extern = variable.storage_class == .@"extern";
815
816 const init_node = init: {
817 if (variable.initializer) |init| {
818 const maybe_literal = init.get(t.tree);
819 const init_node = (if (maybe_literal == .string_literal_expr)
820 t.transStringLiteralInitializer(init, maybe_literal.string_literal_expr, type_node)
821 else
822 t.transExprCoercing(scope, init, .used)) catch |err| switch (err) {
823 error.UnsupportedTranslation, error.UnsupportedType => {
824 return t.failDecl(scope, variable.name_tok, name, "unable to resolve var init expr", .{});
825 },
826 else => |e| return e,
827 };
828
829 if (!variable.qt.is(t.comp, .bool) and init_node.isBoolRes()) {
830 break :init try ZigTag.int_from_bool.create(t.arena, init_node);
831 } else {
832 break :init init_node;
833 }
834 }
835 if (variable.storage_class == .@"extern") {
836 if (array_ty != null and array_ty.?.len == .incomplete) {
837 // Oh no, an extern array of unknown size! These are really fun because there's no
838 // direct equivalent in Zig. To translate correctly, we'll have to create a C-pointer
839 // to the data initialized via @extern.
840
841 // Since this is really a pointer to the underlying data, we tweak a few properties.
842 is_extern = false;
843 is_const = true;
844
845 const name_str = try std.fmt.allocPrint(t.arena, "\"{s}\"", .{base_name});
846 break :init try ZigTag.builtin_extern.create(t.arena, .{
847 .type = type_node,
848 .name = try ZigTag.string_literal.create(t.arena, name_str),
849 });
850 }
851 break :init null;
852 }
853 if (toplevel or variable.storage_class == .static or variable.thread_local) {
854 // The C language specification states that variables with static or threadlocal
855 // storage without an initializer are initialized to a zero value.
856 break :init try t.createZeroValueNode(variable.qt, type_node, .no_as);
857 }
858 break :init ZigTag.undefined_literal.init();
859 };
860
861 const linksection_string = blk: {
862 if (variable.qt.getAttribute(t.comp, .section)) |section| {
863 break :blk t.comp.interner.get(section.name.ref()).bytes;
864 }
865 break :blk null;
866 };
867
868 const alignment: ?c_uint = variable.qt.requestedAlignment(t.comp) orelse null;
869 var node = try ZigTag.var_decl.create(t.arena, .{
870 .is_pub = toplevel,
871 .is_const = is_const,
872 .is_extern = is_extern,
873 .is_export = toplevel and variable.storage_class == .auto,
874 .is_threadlocal = variable.thread_local,
875 .linksection_string = linksection_string,
876 .alignment = alignment,
877 .name = if (use_base_name) base_name else name,
878 .type = type_node,
879 .init = init_node,
880 });
881
882 if (toplevel) {
883 try t.addTopLevelDecl(name, node);
884 } else {
885 if (use_base_name) {
886 node = try ZigTag.wrapped_local.create(t.arena, .{ .name = name, .init = node });
887 }
888 try scope.appendNode(node);
889 try bs.discardVariable(name);
890
891 if (variable.qt.getAttribute(t.comp, .cleanup)) |cleanup_attr| {
892 const cleanup_fn_name = t.tree.tokSlice(cleanup_attr.function.tok);
893 const mangled_fn_name = scope.getAlias(cleanup_fn_name) orelse cleanup_fn_name;
894 const fn_id = try ZigTag.identifier.create(t.arena, mangled_fn_name);
895
896 const varname = try ZigTag.identifier.create(t.arena, name);
897 const args = try t.arena.alloc(ZigNode, 1);
898 args[0] = try ZigTag.address_of.create(t.arena, varname);
899
900 const cleanup_call = try ZigTag.call.create(t.arena, .{ .lhs = fn_id, .args = args });
901 const discard = try ZigTag.discard.create(t.arena, .{ .should_skip = false, .value = cleanup_call });
902 const deferred_cleanup = try ZigTag.@"defer".create(t.arena, discard);
903
904 try bs.statements.append(t.gpa, deferred_cleanup);
905 }
906 }
907}
908
909fn transEnumDecl(t: *Translator, scope: *Scope, enum_qt: QualType) Error!void {
910 const base = enum_qt.base(t.comp);
911 const enum_ty = base.type.@"enum";
912
913 if (t.type_decls.get(enum_ty.decl_node)) |_|
914 return; // Avoid processing this decl twice
915
916 const toplevel = scope.id == .root;
917 const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(t) else undefined;
918
919 var bare_name = enum_ty.name.lookup(t.comp);
920 var is_unnamed = false;
921 var name = bare_name;
922 if (t.unnamed_typedefs.get(base.qt)) |typedef_name| {
923 bare_name = typedef_name;
924 name = typedef_name;
925 } else {
926 if (enum_ty.isAnonymous(t.comp)) {
927 bare_name = try std.fmt.allocPrint(t.arena, "unnamed_{d}", .{t.getMangle()});
928 is_unnamed = true;
929 }
930 name = try std.fmt.allocPrint(t.arena, "enum_{s}", .{bare_name});
931 }
932 if (!toplevel) name = try bs.makeMangledName(name);
933 try t.type_decls.putNoClobber(t.gpa, enum_ty.decl_node, name);
934
935 const enum_type_node = if (!base.qt.hasIncompleteSize(t.comp)) blk: {
936 const enum_decl = enum_ty.decl_node.get(t.tree).enum_decl;
937 for (enum_ty.fields, enum_decl.fields) |field, field_node| {
938 var enum_val_name = field.name.lookup(t.comp);
939 if (!toplevel) {
940 enum_val_name = try bs.makeMangledName(enum_val_name);
941 }
942
943 const enum_const_type_node: ?ZigNode = t.transType(scope, field.qt, field.name_tok) catch |err| switch (err) {
944 error.UnsupportedType => null,
945 else => |e| return e,
946 };
947
948 const val = t.tree.value_map.get(field_node).?;
949 const enum_const_def = try ZigTag.enum_constant.create(t.arena, .{
950 .name = enum_val_name,
951 .is_public = toplevel,
952 .type = enum_const_type_node,
953 .value = try t.createIntNode(val),
954 });
955 if (toplevel)
956 try t.addTopLevelDecl(enum_val_name, enum_const_def)
957 else {
958 try scope.appendNode(enum_const_def);
959 try bs.discardVariable(enum_val_name);
960 }
961 }
962
963 break :blk t.transType(scope, enum_ty.tag.?, enum_decl.name_or_kind_tok) catch |err| switch (err) {
964 error.UnsupportedType => {
965 return t.failDecl(scope, enum_decl.name_or_kind_tok, name, "unable to translate enum integer type", .{});
966 },
967 else => |e| return e,
968 };
969 } else blk: {
970 try t.opaque_demotes.put(t.gpa, base.qt, {});
971 break :blk ZigTag.opaque_literal.init();
972 };
973
974 const is_pub = toplevel and !is_unnamed;
975 const payload = try t.arena.create(ast.Payload.SimpleVarDecl);
976 payload.* = .{
977 .base = .{ .tag = if (is_pub) .pub_var_simple else .var_simple },
978 .data = .{
979 .init = enum_type_node,
980 .name = name,
981 },
982 };
983 const node = ZigNode.initPayload(&payload.base);
984 if (toplevel) {
985 try t.addTopLevelDecl(name, node);
986 if (!is_unnamed)
987 try t.alias_list.append(t.gpa, .{ .alias = bare_name, .name = name });
988 } else {
989 try scope.appendNode(node);
990 try bs.discardVariable(name);
991 }
992}
993
994fn transStaticAssert(t: *Translator, scope: *Scope, static_assert: Node.StaticAssert) Error!void {
995 const condition = t.transExpr(scope, static_assert.cond, .used) catch |err| switch (err) {
996 error.UnsupportedTranslation, error.UnsupportedType => {
997 return try t.warn(&t.global_scope.base, static_assert.cond.tok(t.tree), "unable to translate _Static_assert condition", .{});
998 },
999 error.OutOfMemory => |e| return e,
1000 };
1001
1002 // generate @compileError message that matches C compiler output
1003 const diagnostic = if (static_assert.message) |message| str: {
1004 // Aro guarantees this to be a string literal.
1005 const str_val = t.tree.value_map.get(message).?;
1006 const str_qt = message.qt(t.tree);
1007
1008 const bytes = t.comp.interner.get(str_val.ref()).bytes;
1009 var allocating: std.Io.Writer.Allocating = .init(t.gpa);
1010 defer allocating.deinit();
1011
1012 allocating.writer.writeAll("\"static assertion failed \\") catch return error.OutOfMemory;
1013
1014 aro.Value.printString(bytes, str_qt, t.comp, &allocating.writer) catch return error.OutOfMemory;
1015 allocating.writer.end -= 1; // printString adds a terminating " so we need to remove it
1016 allocating.writer.writeAll("\\\"\"") catch return error.OutOfMemory;
1017
1018 break :str try ZigTag.string_literal.create(t.arena, try t.arena.dupe(u8, allocating.getWritten()));
1019 } else try ZigTag.string_literal.create(t.arena, "\"static assertion failed\"");
1020
1021 const assert_node = try ZigTag.static_assert.create(t.arena, .{ .lhs = condition, .rhs = diagnostic });
1022 try scope.appendNode(assert_node);
1023}
1024
1025fn transGlobalAsm(t: *Translator, scope: *Scope, global_asm: Node.SimpleAsm) Error!void {
1026 const asm_string = t.tree.value_map.get(global_asm.asm_str).?;
1027 const bytes = t.comp.interner.get(asm_string.ref()).bytes;
1028
1029 var allocating: std.Io.Writer.Allocating = try .initCapacity(t.gpa, bytes.len);
1030 defer allocating.deinit();
1031 aro.Value.printString(bytes, global_asm.asm_str.qt(t.tree), t.comp, &allocating.writer) catch return error.OutOfMemory;
1032
1033 const str_node = try ZigTag.string_literal.create(t.arena, try t.arena.dupe(u8, allocating.getWritten()));
1034
1035 const asm_node = try ZigTag.asm_simple.create(t.arena, str_node);
1036 const block = try ZigTag.block_single.create(t.arena, asm_node);
1037 const comptime_node = try ZigTag.@"comptime".create(t.arena, block);
1038
1039 try scope.appendNode(comptime_node);
1040}
1041
1042// ================
1043// Type translation
1044// ================
1045
1046fn getTypeStr(t: *Translator, qt: QualType) ![]const u8 {
1047 var allocating: std.Io.Writer.Allocating = .init(t.gpa);
1048 defer allocating.deinit();
1049 qt.print(t.comp, &allocating.writer) catch return error.OutOfMemory;
1050 return t.arena.dupe(u8, allocating.getWritten());
1051}
1052
1053fn transType(t: *Translator, scope: *Scope, qt: QualType, source_loc: TokenIndex) TypeError!ZigNode {
1054 loop: switch (qt.type(t.comp)) {
1055 .atomic => {
1056 const type_name = try t.getTypeStr(qt);
1057 return t.fail(error.UnsupportedType, source_loc, "TODO support atomic type: '{s}'", .{type_name});
1058 },
1059 .void => return ZigTag.type.create(t.arena, "anyopaque"),
1060 .bool => return ZigTag.type.create(t.arena, "bool"),
1061 .int => |int_ty| switch (int_ty) {
1062 //.char => return ZigTag.type.create(t.arena, "c_char"), // TODO: this is the preferred translation
1063 .char => return ZigTag.type.create(t.arena, "u8"),
1064 .schar => return ZigTag.type.create(t.arena, "i8"),
1065 .uchar => return ZigTag.type.create(t.arena, "u8"),
1066 .short => return ZigTag.type.create(t.arena, "c_short"),
1067 .ushort => return ZigTag.type.create(t.arena, "c_ushort"),
1068 .int => return ZigTag.type.create(t.arena, "c_int"),
1069 .uint => return ZigTag.type.create(t.arena, "c_uint"),
1070 .long => return ZigTag.type.create(t.arena, "c_long"),
1071 .ulong => return ZigTag.type.create(t.arena, "c_ulong"),
1072 .long_long => return ZigTag.type.create(t.arena, "c_longlong"),
1073 .ulong_long => return ZigTag.type.create(t.arena, "c_ulonglong"),
1074 .int128 => return ZigTag.type.create(t.arena, "i128"),
1075 .uint128 => return ZigTag.type.create(t.arena, "u128"),
1076 },
1077 .float => |float_ty| switch (float_ty) {
1078 .fp16, .float16 => return ZigTag.type.create(t.arena, "f16"),
1079 .float => return ZigTag.type.create(t.arena, "f32"),
1080 .double => return ZigTag.type.create(t.arena, "f64"),
1081 .long_double => return ZigTag.type.create(t.arena, "c_longdouble"),
1082 .float128 => return ZigTag.type.create(t.arena, "f128"),
1083 },
1084 .pointer => |pointer_ty| {
1085 const child_qt = pointer_ty.child;
1086
1087 const is_fn_proto = child_qt.is(t.comp, .func);
1088 const is_const = is_fn_proto or child_qt.@"const";
1089 const is_volatile = child_qt.@"volatile";
1090 const elem_type = try t.transType(scope, child_qt, source_loc);
1091 const ptr_info: @FieldType(ast.Payload.Pointer, "data") = .{
1092 .is_const = is_const,
1093 .is_volatile = is_volatile,
1094 .elem_type = elem_type,
1095 .is_allowzero = false,
1096 };
1097 if (is_fn_proto or
1098 t.typeIsOpaque(child_qt) or
1099 t.typeWasDemotedToOpaque(child_qt))
1100 {
1101 const ptr = try ZigTag.single_pointer.create(t.arena, ptr_info);
1102 return ZigTag.optional_type.create(t.arena, ptr);
1103 }
1104
1105 return ZigTag.c_pointer.create(t.arena, ptr_info);
1106 },
1107 .array => |array_ty| {
1108 const elem_qt = array_ty.elem;
1109 switch (array_ty.len) {
1110 .incomplete, .unspecified_variable => {
1111 const elem_type = try t.transType(scope, elem_qt, source_loc);
1112 return ZigTag.c_pointer.create(t.arena, .{
1113 .is_const = elem_qt.@"const",
1114 .is_volatile = elem_qt.@"volatile",
1115 .is_allowzero = false,
1116 .elem_type = elem_type,
1117 });
1118 },
1119 .fixed, .static => |len| {
1120 const elem_type = try t.transType(scope, elem_qt, source_loc);
1121 return ZigTag.array_type.create(t.arena, .{ .len = len, .elem_type = elem_type });
1122 },
1123 .variable => return t.fail(error.UnsupportedType, source_loc, "VLA unsupported '{s}'", .{try t.getTypeStr(qt)}),
1124 }
1125 },
1126 .func => |func_ty| return t.transFnType(scope, qt, func_ty, source_loc, .{}),
1127 .@"struct", .@"union" => |record_ty| {
1128 var trans_scope = scope;
1129 if (!record_ty.isAnonymous(t.comp)) {
1130 if (t.weak_global_names.contains(record_ty.name.lookup(t.comp))) trans_scope = &t.global_scope.base;
1131 }
1132 try t.transRecordDecl(trans_scope, qt);
1133 const name = t.type_decls.get(record_ty.decl_node).?;
1134 return ZigTag.identifier.create(t.arena, name);
1135 },
1136 .@"enum" => |enum_ty| {
1137 var trans_scope = scope;
1138 const is_anonymous = enum_ty.isAnonymous(t.comp);
1139 if (!is_anonymous) {
1140 if (t.weak_global_names.contains(enum_ty.name.lookup(t.comp))) trans_scope = &t.global_scope.base;
1141 }
1142 try t.transEnumDecl(trans_scope, qt);
1143 const name = t.type_decls.get(enum_ty.decl_node).?;
1144 return ZigTag.identifier.create(t.arena, name);
1145 },
1146 .typedef => |typedef_ty| {
1147 var trans_scope = scope;
1148 const typedef_name = typedef_ty.name.lookup(t.comp);
1149 if (builtin_typedef_map.get(typedef_name)) |builtin| return ZigTag.type.create(t.arena, builtin);
1150 if (t.global_names.contains(typedef_name)) trans_scope = &t.global_scope.base;
1151
1152 try t.transTypeDef(trans_scope, typedef_ty.decl_node);
1153 const name = t.type_decls.get(typedef_ty.decl_node).?;
1154 return ZigTag.identifier.create(t.arena, name);
1155 },
1156 .attributed => |attributed_ty| continue :loop attributed_ty.base.type(t.comp),
1157 .typeof => |typeof_ty| continue :loop typeof_ty.base.type(t.comp),
1158 .vector => |vector_ty| {
1159 const len = try t.createNumberNode(vector_ty.len, .int);
1160 const elem_type = try t.transType(scope, vector_ty.elem, source_loc);
1161 return ZigTag.vector.create(t.arena, .{ .lhs = len, .rhs = elem_type });
1162 },
1163 else => return t.fail(error.UnsupportedType, source_loc, "unsupported type: '{s}'", .{try t.getTypeStr(qt)}),
1164 }
1165}
1166
1167/// Look ahead through the fields of the record to determine what the alignment of the record
1168/// would be without any align/packed/etc. attributes. This helps us determine whether or not
1169/// the fields with 0 offset need an `align` qualifier. Strictly speaking, we could just
1170/// pedantically assign those fields the same alignment as the parent's pointer alignment,
1171/// but this helps the generated code to be a little less verbose.
1172fn headFieldAlignment(t: *Translator, record_decl: aro.Type.Record) ?c_uint {
1173 const bits_per_byte = 8;
1174 const parent_ptr_alignment_bits = record_decl.layout.?.pointer_alignment_bits;
1175 const parent_ptr_alignment = parent_ptr_alignment_bits / bits_per_byte;
1176 var max_field_alignment_bits: u64 = 0;
1177 for (record_decl.fields) |field| {
1178 if (field.qt.getRecord(t.comp)) |field_record_decl| {
1179 const child_record_alignment = field_record_decl.layout.?.field_alignment_bits;
1180 if (child_record_alignment > max_field_alignment_bits)
1181 max_field_alignment_bits = child_record_alignment;
1182 } else {
1183 const field_size = field.layout.size_bits;
1184 if (field_size > max_field_alignment_bits)
1185 max_field_alignment_bits = field_size;
1186 }
1187 }
1188 if (max_field_alignment_bits != parent_ptr_alignment_bits) {
1189 return parent_ptr_alignment;
1190 } else {
1191 return null;
1192 }
1193}
1194
1195/// This function inspects the generated layout of a record to determine the alignment for a
1196/// particular field. This approach is necessary because unlike Zig, a C compiler is not
1197/// required to fulfill the requested alignment, which means we'd risk generating different code
1198/// if we only look at the user-requested alignment.
1199///
1200/// Returns a ?c_uint to match Clang's behavior of using c_uint. The return type can be changed
1201/// after the Clang frontend for translate-c is removed. A null value indicates that a field is
1202/// 'naturally aligned'.
1203fn alignmentForField(
1204 t: *Translator,
1205 record_decl: aro.Type.Record,
1206 head_field_alignment: ?c_uint,
1207 field_index: usize,
1208) ?c_uint {
1209 const fields = record_decl.fields;
1210 assert(fields.len != 0);
1211 const field = fields[field_index];
1212
1213 const bits_per_byte = 8;
1214 const parent_ptr_alignment_bits = record_decl.layout.?.pointer_alignment_bits;
1215 const parent_ptr_alignment = parent_ptr_alignment_bits / bits_per_byte;
1216
1217 // bitfields aren't supported yet. Until support is added, records with bitfields
1218 // should be demoted to opaque, and this function shouldn't be called for them.
1219 if (field.bit_width != .null) {
1220 @panic("TODO: add bitfield support for records");
1221 }
1222
1223 const field_offset_bits: u64 = field.layout.offset_bits;
1224 const field_size_bits: u64 = field.layout.size_bits;
1225
1226 // Fields with zero width always have an alignment of 1
1227 if (field_size_bits == 0) {
1228 return 1;
1229 }
1230
1231 // Fields with 0 offset inherit the parent's pointer alignment.
1232 if (field_offset_bits == 0) {
1233 return head_field_alignment;
1234 }
1235
1236 // Records have a natural alignment when used as a field, and their size is
1237 // a multiple of this alignment value. For all other types, the natural alignment
1238 // is their size.
1239 const field_natural_alignment_bits: u64 = if (field.qt.getRecord(t.comp)) |record|
1240 record.layout.?.field_alignment_bits
1241 else
1242 field_size_bits;
1243 const rem_bits = field_offset_bits % field_natural_alignment_bits;
1244
1245 // If there's a remainder, then the alignment is smaller than the field's
1246 // natural alignment
1247 if (rem_bits > 0) {
1248 const rem_alignment = rem_bits / bits_per_byte;
1249 if (rem_alignment > 0 and std.math.isPowerOfTwo(rem_alignment)) {
1250 const actual_alignment = @min(rem_alignment, parent_ptr_alignment);
1251 return @as(c_uint, @truncate(actual_alignment));
1252 } else {
1253 return 1;
1254 }
1255 }
1256
1257 // A field may have an offset which positions it to be naturally aligned, but the
1258 // parent's pointer alignment determines if this is actually true, so we take the minimum
1259 // value.
1260 // For example, a float field (4 bytes wide) with a 4 byte offset is positioned to have natural
1261 // alignment, but if the parent pointer alignment is 2, then the actual alignment of the
1262 // float is 2.
1263 const field_natural_alignment: u64 = field_natural_alignment_bits / bits_per_byte;
1264 const offset_alignment = field_offset_bits / bits_per_byte;
1265 const possible_alignment = @min(parent_ptr_alignment, offset_alignment);
1266 if (possible_alignment == field_natural_alignment) {
1267 return null;
1268 } else if (possible_alignment < field_natural_alignment) {
1269 if (std.math.isPowerOfTwo(possible_alignment)) {
1270 return possible_alignment;
1271 } else {
1272 return 1;
1273 }
1274 } else { // possible_alignment > field_natural_alignment
1275 // Here, the field is positioned be at a higher alignment than it's natural alignment. This means we
1276 // need to determine whether it's a specified alignment. We can determine that from the padding preceding
1277 // the field.
1278 const padding_from_prev_field: u64 = blk: {
1279 if (field_offset_bits != 0) {
1280 const previous_field = fields[field_index - 1];
1281 break :blk (field_offset_bits - previous_field.layout.offset_bits) - previous_field.layout.size_bits;
1282 } else {
1283 break :blk 0;
1284 }
1285 };
1286 if (padding_from_prev_field < field_natural_alignment_bits) {
1287 return null;
1288 } else {
1289 return possible_alignment;
1290 }
1291 }
1292}
1293
1294const FnProtoContext = struct {
1295 is_pub: bool = false,
1296 is_export: bool = false,
1297 is_extern: bool = false,
1298 is_always_inline: bool = false,
1299 fn_name: ?[]const u8 = null,
1300 has_body: bool = false,
1301 cc: ast.Payload.Func.CallingConvention = .c,
1302};
1303
1304fn transFnType(
1305 t: *Translator,
1306 scope: *Scope,
1307 func_qt: QualType,
1308 func_ty: aro.Type.Func,
1309 source_loc: TokenIndex,
1310 ctx: FnProtoContext,
1311) !ZigNode {
1312 const param_count: usize = func_ty.params.len;
1313 const fn_params = try t.arena.alloc(ast.Payload.Param, param_count);
1314
1315 for (func_ty.params, fn_params) |param_info, *param_node| {
1316 const param_qt = param_info.qt;
1317 const is_noalias = param_qt.restrict;
1318
1319 const param_name: ?[]const u8 = if (param_info.name == .empty)
1320 null
1321 else
1322 param_info.name.lookup(t.comp);
1323
1324 const type_node = try t.transType(scope, param_qt, param_info.name_tok);
1325 param_node.* = .{
1326 .is_noalias = is_noalias,
1327 .name = param_name,
1328 .type = type_node,
1329 };
1330 }
1331
1332 const linksection_string = blk: {
1333 if (func_qt.getAttribute(t.comp, .section)) |section| {
1334 break :blk t.comp.interner.get(section.name.ref()).bytes;
1335 }
1336 break :blk null;
1337 };
1338
1339 const alignment: ?c_uint = func_qt.requestedAlignment(t.comp) orelse null;
1340
1341 const explicit_callconv = if ((ctx.is_always_inline or ctx.is_export or ctx.is_extern) and ctx.cc == .c) null else ctx.cc;
1342
1343 const return_type_node = blk: {
1344 if (func_qt.getAttribute(t.comp, .noreturn) != null) {
1345 break :blk ZigTag.noreturn_type.init();
1346 } else {
1347 const return_qt = func_ty.return_type;
1348 if (return_qt.is(t.comp, .void)) {
1349 // convert primitive anyopaque to actual void (only for return type)
1350 break :blk ZigTag.void_type.init();
1351 } else {
1352 break :blk t.transType(scope, return_qt, source_loc) catch |err| switch (err) {
1353 error.UnsupportedType => {
1354 try t.warn(scope, source_loc, "unsupported function proto return type", .{});
1355 return err;
1356 },
1357 error.OutOfMemory => |e| return e,
1358 };
1359 }
1360 }
1361 };
1362
1363 const payload = try t.arena.create(ast.Payload.Func);
1364 payload.* = .{
1365 .base = .{ .tag = .func },
1366 .data = .{
1367 .is_pub = ctx.is_pub,
1368 .is_extern = ctx.is_extern,
1369 .is_export = ctx.is_export,
1370 .is_inline = ctx.is_always_inline,
1371 .is_var_args = switch (func_ty.kind) {
1372 .normal => false,
1373 .variadic => true,
1374 .old_style => !ctx.is_export and !ctx.is_always_inline and !ctx.has_body,
1375 },
1376 .name = ctx.fn_name,
1377 .linksection_string = linksection_string,
1378 .explicit_callconv = explicit_callconv,
1379 .params = fn_params,
1380 .return_type = return_type_node,
1381 .body = null,
1382 .alignment = alignment,
1383 },
1384 };
1385 return ZigNode.initPayload(&payload.base);
1386}
1387
1388/// Produces a Zig AST node by translating a Type, respecting the width, but modifying the signed-ness.
1389/// Asserts the type is an integer.
1390fn transTypeIntWidthOf(t: *Translator, qt: QualType, is_signed: bool) TypeError!ZigNode {
1391 return ZigTag.type.create(t.arena, loop: switch (qt.base(t.comp).type) {
1392 .int => |int_ty| switch (int_ty) {
1393 .char, .schar, .uchar => if (is_signed) "i8" else "u8",
1394 .short, .ushort => if (is_signed) "c_short" else "c_ushort",
1395 .int, .uint => if (is_signed) "c_int" else "c_uint",
1396 .long, .ulong => if (is_signed) "c_long" else "c_ulong",
1397 .long_long, .ulong_long => if (is_signed) "c_longlong" else "c_ulonglong",
1398 .int128, .uint128 => if (is_signed) "i128" else "u128",
1399 },
1400 .bit_int => |bit_int_ty| try std.fmt.allocPrint(t.arena, "{s}{d}", .{
1401 if (is_signed) "i" else "u",
1402 bit_int_ty.bits,
1403 }),
1404 .@"enum" => |enum_ty| blk: {
1405 const tag_ty = enum_ty.tag orelse
1406 break :blk if (is_signed) "c_int" else "c_uint";
1407
1408 continue :loop tag_ty.base(t.comp).type;
1409 },
1410 else => unreachable, // only call this function when it has already been determined the type is int
1411 });
1412}
1413
1414fn transTypeInit(
1415 t: *Translator,
1416 scope: *Scope,
1417 qt: QualType,
1418 init: Node.Index,
1419 source_loc: TokenIndex,
1420) TypeError!ZigNode {
1421 switch (init.get(t.tree)) {
1422 .string_literal_expr => |literal| {
1423 const elem_ty = try t.transType(scope, qt.childType(t.comp), source_loc);
1424
1425 const string_lit_size = literal.qt.arrayLen(t.comp).?;
1426 const array_size = qt.arrayLen(t.comp).?;
1427
1428 if (array_size == string_lit_size) {
1429 return ZigTag.null_sentinel_array_type.create(t.arena, .{ .len = array_size - 1, .elem_type = elem_ty });
1430 } else {
1431 return ZigTag.array_type.create(t.arena, .{ .len = array_size, .elem_type = elem_ty });
1432 }
1433 },
1434 else => {},
1435 }
1436 return t.transType(scope, qt, source_loc);
1437}
1438
1439// ============
1440// Type helpers
1441// ============
1442
1443fn typeIsOpaque(t: *Translator, qt: QualType) bool {
1444 return switch (qt.base(t.comp).type) {
1445 .void => true,
1446 .@"struct", .@"union" => |record_ty| {
1447 if (record_ty.layout == null) return true;
1448 for (record_ty.fields) |field| {
1449 if (field.bit_width != .null) return true;
1450 }
1451 return false;
1452 },
1453 else => false,
1454 };
1455}
1456
1457fn typeWasDemotedToOpaque(t: *Translator, qt: QualType) bool {
1458 const base = qt.base(t.comp);
1459 switch (base.type) {
1460 .@"struct", .@"union" => |record_ty| {
1461 if (t.opaque_demotes.contains(base.qt)) return true;
1462 for (record_ty.fields) |field| {
1463 if (t.typeWasDemotedToOpaque(field.qt)) return true;
1464 }
1465 return false;
1466 },
1467 .@"enum" => return t.opaque_demotes.contains(base.qt),
1468 else => return false,
1469 }
1470}
1471
1472fn typeHasWrappingOverflow(t: *Translator, qt: QualType) bool {
1473 if (t.signedness(qt) == .unsigned) {
1474 // unsigned integer overflow wraps around.
1475 return true;
1476 } else {
1477 // float, signed integer, and pointer overflow is undefined behavior.
1478 return false;
1479 }
1480}
1481
1482/// Signedness of type when translated to Zig.
1483/// Different from `QualType.signedness()` for `char` and enums.
1484/// Returns null for non-int types.
1485fn signedness(t: *Translator, qt: QualType) ?std.builtin.Signedness {
1486 return loop: switch (qt.base(t.comp).type) {
1487 .bool => .unsigned,
1488 .bit_int => |bit_int| bit_int.signedness,
1489 .int => |int_ty| switch (int_ty) {
1490 .char => .unsigned, // Always translated as u8
1491 .schar, .short, .int, .long, .long_long, .int128 => .signed,
1492 .uchar, .ushort, .uint, .ulong, .ulong_long, .uint128 => .unsigned,
1493 },
1494 .@"enum" => |enum_ty| {
1495 const tag_qt = enum_ty.tag orelse return .signed;
1496 continue :loop tag_qt.base(t.comp).type;
1497 },
1498 else => return null,
1499 };
1500}
1501
1502// =====================
1503// Statement translation
1504// =====================
1505
1506fn transStmt(t: *Translator, scope: *Scope, stmt: Node.Index) TransError!ZigNode {
1507 switch (stmt.get(t.tree)) {
1508 .compound_stmt => |compound| {
1509 return t.transCompoundStmt(scope, compound);
1510 },
1511 .static_assert => |static_assert| {
1512 try t.transStaticAssert(scope, static_assert);
1513 return ZigTag.declaration.init();
1514 },
1515 .return_stmt => |return_stmt| return t.transReturnStmt(scope, return_stmt),
1516 .null_stmt => return ZigTag.empty_block.init(),
1517 .if_stmt => |if_stmt| return t.transIfStmt(scope, if_stmt),
1518 .while_stmt => |while_stmt| return t.transWhileStmt(scope, while_stmt),
1519 .do_while_stmt => |do_while_stmt| return t.transDoWhileStmt(scope, do_while_stmt),
1520 .for_stmt => |for_stmt| return t.transForStmt(scope, for_stmt),
1521 .continue_stmt => return ZigTag.@"continue".init(),
1522 .break_stmt => return ZigTag.@"break".init(),
1523 .typedef => |typedef_decl| {
1524 assert(!typedef_decl.implicit);
1525 try t.transTypeDef(scope, stmt);
1526 return ZigTag.declaration.init();
1527 },
1528 .struct_decl, .union_decl => |record_decl| {
1529 try t.transRecordDecl(scope, record_decl.container_qt);
1530 return ZigTag.declaration.init();
1531 },
1532 .enum_decl => |enum_decl| {
1533 try t.transEnumDecl(scope, enum_decl.container_qt);
1534 return ZigTag.declaration.init();
1535 },
1536 .function => |function| {
1537 try t.transFnDecl(scope, function);
1538 return ZigTag.declaration.init();
1539 },
1540 .variable => |variable| {
1541 try t.transVarDecl(scope, variable);
1542 return ZigTag.declaration.init();
1543 },
1544 .switch_stmt => |switch_stmt| return t.transSwitch(scope, switch_stmt),
1545 .case_stmt, .default_stmt => {
1546 return t.fail(error.UnsupportedTranslation, stmt.tok(t.tree), "TODO complex switch", .{});
1547 },
1548 .goto_stmt, .computed_goto_stmt, .labeled_stmt => {
1549 return t.fail(error.UnsupportedTranslation, stmt.tok(t.tree), "TODO goto", .{});
1550 },
1551 else => return t.transExprCoercing(scope, stmt, .unused),
1552 }
1553}
1554
1555fn transCompoundStmtInline(t: *Translator, compound: Node.CompoundStmt, block: *Scope.Block) TransError!void {
1556 for (compound.body) |stmt| {
1557 const result = try t.transStmt(&block.base, stmt);
1558 switch (result.tag()) {
1559 .declaration, .empty_block => {},
1560 else => try block.statements.append(t.gpa, result),
1561 }
1562 }
1563}
1564
1565fn transCompoundStmt(t: *Translator, scope: *Scope, compound: Node.CompoundStmt) TransError!ZigNode {
1566 var block_scope = try Scope.Block.init(t, scope, false);
1567 defer block_scope.deinit();
1568 try t.transCompoundStmtInline(compound, &block_scope);
1569 return try block_scope.complete();
1570}
1571
1572fn transReturnStmt(t: *Translator, scope: *Scope, return_stmt: Node.ReturnStmt) TransError!ZigNode {
1573 switch (return_stmt.operand) {
1574 .none => return ZigTag.return_void.init(),
1575 .expr => |operand| {
1576 var rhs = try t.transExprCoercing(scope, operand, .used);
1577 const return_qt = scope.findBlockReturnType();
1578 if (rhs.isBoolRes() and !return_qt.is(t.comp, .bool)) {
1579 rhs = try ZigTag.int_from_bool.create(t.arena, rhs);
1580 }
1581 return ZigTag.@"return".create(t.arena, rhs);
1582 },
1583 .implicit => |zero| {
1584 if (zero) return ZigTag.@"return".create(t.arena, ZigTag.zero_literal.init());
1585
1586 const return_qt = scope.findBlockReturnType();
1587 if (return_qt.is(t.comp, .void)) return ZigTag.empty_block.init();
1588
1589 return ZigTag.@"return".create(t.arena, ZigTag.undefined_literal.init());
1590 },
1591 }
1592}
1593
1594/// If a statement can possibly translate to a Zig assignment (either directly because it's
1595/// an assignment in C or indirectly via result assignment to `_`) AND it's the sole statement
1596/// in the body of an if statement or loop, then we need to put the statement into its own block.
1597/// The `else` case here corresponds to statements that could result in an assignment. If a statement
1598/// class never needs a block, add its enum to the top prong.
1599fn maybeBlockify(t: *Translator, scope: *Scope, stmt: Node.Index) TransError!ZigNode {
1600 switch (stmt.get(t.tree)) {
1601 .break_stmt,
1602 .continue_stmt,
1603 .compound_stmt,
1604 .decl_ref_expr,
1605 .enumeration_ref,
1606 .do_while_stmt,
1607 .for_stmt,
1608 .if_stmt,
1609 .return_stmt,
1610 .null_stmt,
1611 .while_stmt,
1612 => return t.transStmt(scope, stmt),
1613 else => return t.blockify(scope, stmt),
1614 }
1615}
1616
1617/// Translate statement and place it in its own block.
1618fn blockify(t: *Translator, scope: *Scope, stmt: Node.Index) TransError!ZigNode {
1619 var block_scope = try Scope.Block.init(t, scope, false);
1620 defer block_scope.deinit();
1621 const result = try t.transStmt(&block_scope.base, stmt);
1622 try block_scope.statements.append(t.gpa, result);
1623 return block_scope.complete();
1624}
1625
1626fn transIfStmt(t: *Translator, scope: *Scope, if_stmt: Node.IfStmt) TransError!ZigNode {
1627 var cond_scope: Scope.Condition = .{
1628 .base = .{
1629 .parent = scope,
1630 .id = .condition,
1631 },
1632 };
1633 defer cond_scope.deinit();
1634 const cond = try t.transBoolExpr(&cond_scope.base, if_stmt.cond);
1635
1636 // block needed to keep else statement from attaching to inner while
1637 const must_blockify = (if_stmt.else_body != null) and switch (if_stmt.then_body.get(t.tree)) {
1638 .while_stmt, .do_while_stmt, .for_stmt => true,
1639 else => false,
1640 };
1641
1642 const then_node = if (must_blockify)
1643 try t.blockify(scope, if_stmt.then_body)
1644 else
1645 try t.maybeBlockify(scope, if_stmt.then_body);
1646
1647 const else_node = if (if_stmt.else_body) |stmt|
1648 try t.maybeBlockify(scope, stmt)
1649 else
1650 null;
1651 return ZigTag.@"if".create(t.arena, .{ .cond = cond, .then = then_node, .@"else" = else_node });
1652}
1653
1654fn transWhileStmt(t: *Translator, scope: *Scope, while_stmt: Node.WhileStmt) TransError!ZigNode {
1655 var cond_scope: Scope.Condition = .{
1656 .base = .{
1657 .parent = scope,
1658 .id = .condition,
1659 },
1660 };
1661 defer cond_scope.deinit();
1662 const cond = try t.transBoolExpr(&cond_scope.base, while_stmt.cond);
1663
1664 var loop_scope: Scope = .{
1665 .parent = scope,
1666 .id = .loop,
1667 };
1668 const body = try t.maybeBlockify(&loop_scope, while_stmt.body);
1669 return ZigTag.@"while".create(t.arena, .{ .cond = cond, .body = body, .cont_expr = null });
1670}
1671
1672fn transDoWhileStmt(t: *Translator, scope: *Scope, do_stmt: Node.DoWhileStmt) TransError!ZigNode {
1673 var loop_scope: Scope = .{
1674 .parent = scope,
1675 .id = .do_loop,
1676 };
1677
1678 // if (!cond) break;
1679 var cond_scope: Scope.Condition = .{
1680 .base = .{
1681 .parent = scope,
1682 .id = .condition,
1683 },
1684 };
1685 defer cond_scope.deinit();
1686 const cond = try t.transBoolExpr(&cond_scope.base, do_stmt.cond);
1687 const if_not_break = switch (cond.tag()) {
1688 .true_literal => {
1689 const body_node = try t.maybeBlockify(scope, do_stmt.body);
1690 return ZigTag.while_true.create(t.arena, body_node);
1691 },
1692 else => try ZigTag.if_not_break.create(t.arena, cond),
1693 };
1694
1695 var body_node = try t.transStmt(&loop_scope, do_stmt.body);
1696 if (body_node.isNoreturn(true)) {
1697 // The body node ends in a noreturn statement. Simply put it in a while (true)
1698 // in case it contains breaks or continues.
1699 } else if (do_stmt.body.get(t.tree) == .compound_stmt) {
1700 // there's already a block in C, so we'll append our condition to it.
1701 // c: do {
1702 // c: a;
1703 // c: b;
1704 // c: } while(c);
1705 // zig: while (true) {
1706 // zig: a;
1707 // zig: b;
1708 // zig: if (!cond) break;
1709 // zig: }
1710 const block = body_node.castTag(.block).?;
1711 block.data.stmts.len += 1; // This is safe since we reserve one extra space in Scope.Block.complete.
1712 block.data.stmts[block.data.stmts.len - 1] = if_not_break;
1713 } else {
1714 // the C statement is without a block, so we need to create a block to contain it.
1715 // c: do
1716 // c: a;
1717 // c: while(c);
1718 // zig: while (true) {
1719 // zig: a;
1720 // zig: if (!cond) break;
1721 // zig: }
1722 const statements = try t.arena.alloc(ZigNode, 2);
1723 statements[0] = body_node;
1724 statements[1] = if_not_break;
1725 body_node = try ZigTag.block.create(t.arena, .{ .label = null, .stmts = statements });
1726 }
1727 return ZigTag.while_true.create(t.arena, body_node);
1728}
1729
1730fn transForStmt(t: *Translator, scope: *Scope, for_stmt: Node.ForStmt) TransError!ZigNode {
1731 var loop_scope: Scope = .{
1732 .parent = scope,
1733 .id = .loop,
1734 };
1735
1736 var block_scope: ?Scope.Block = null;
1737 defer if (block_scope) |*bs| bs.deinit();
1738
1739 switch (for_stmt.init) {
1740 .decls => |decls| {
1741 block_scope = try Scope.Block.init(t, scope, false);
1742 loop_scope.parent = &block_scope.?.base;
1743 for (decls) |decl| {
1744 try t.transDecl(&block_scope.?.base, decl);
1745 }
1746 },
1747 .expr => |maybe_init| if (maybe_init) |init| {
1748 block_scope = try Scope.Block.init(t, scope, false);
1749 loop_scope.parent = &block_scope.?.base;
1750 const init_node = try t.transStmt(&block_scope.?.base, init);
1751 try loop_scope.appendNode(init_node);
1752 },
1753 }
1754 var cond_scope: Scope.Condition = .{
1755 .base = .{
1756 .parent = &loop_scope,
1757 .id = .condition,
1758 },
1759 };
1760 defer cond_scope.deinit();
1761
1762 const cond = if (for_stmt.cond) |cond|
1763 try t.transBoolExpr(&cond_scope.base, cond)
1764 else
1765 ZigTag.true_literal.init();
1766
1767 const cont_expr = if (for_stmt.incr) |incr|
1768 try t.transExpr(&cond_scope.base, incr, .unused)
1769 else
1770 null;
1771
1772 const body = try t.maybeBlockify(&loop_scope, for_stmt.body);
1773 const while_node = try ZigTag.@"while".create(t.arena, .{ .cond = cond, .body = body, .cont_expr = cont_expr });
1774 if (block_scope) |*bs| {
1775 try bs.statements.append(t.gpa, while_node);
1776 return try bs.complete();
1777 } else {
1778 return while_node;
1779 }
1780}
1781
1782fn transSwitch(t: *Translator, scope: *Scope, switch_stmt: Node.SwitchStmt) TransError!ZigNode {
1783 var loop_scope: Scope = .{
1784 .parent = scope,
1785 .id = .loop,
1786 };
1787
1788 var block_scope = try Scope.Block.init(t, &loop_scope, false);
1789 defer block_scope.deinit();
1790
1791 const base_scope = &block_scope.base;
1792
1793 var cond_scope: Scope.Condition = .{
1794 .base = .{
1795 .parent = base_scope,
1796 .id = .condition,
1797 },
1798 };
1799 defer cond_scope.deinit();
1800 const switch_expr = try t.transExpr(&cond_scope.base, switch_stmt.cond, .used);
1801
1802 var cases = std.ArrayList(ZigNode).init(t.gpa);
1803 defer cases.deinit();
1804 var has_default = false;
1805
1806 const body_node = switch_stmt.body.get(t.tree);
1807 if (body_node != .compound_stmt) {
1808 return t.fail(error.UnsupportedTranslation, switch_stmt.switch_tok, "TODO complex switch", .{});
1809 }
1810 const body = body_node.compound_stmt.body;
1811 // Iterate over switch body and collect all cases.
1812 // Fallthrough is handled by duplicating statements.
1813 for (body, 0..) |stmt, i| {
1814 switch (stmt.get(t.tree)) {
1815 .case_stmt => {
1816 var items = std.ArrayList(ZigNode).init(t.gpa);
1817 defer items.deinit();
1818 const sub = try t.transCaseStmt(base_scope, stmt, &items);
1819 const res = try t.transSwitchProngStmt(base_scope, sub, body[i..]);
1820
1821 if (items.items.len == 0) {
1822 has_default = true;
1823 const switch_else = try ZigTag.switch_else.create(t.arena, res);
1824 try cases.append(switch_else);
1825 } else {
1826 const switch_prong = try ZigTag.switch_prong.create(t.arena, .{
1827 .cases = try t.arena.dupe(ZigNode, items.items),
1828 .cond = res,
1829 });
1830 try cases.append(switch_prong);
1831 }
1832 },
1833 .default_stmt => |default_stmt| {
1834 has_default = true;
1835
1836 var sub = default_stmt.body;
1837 while (true) switch (sub.get(t.tree)) {
1838 .case_stmt => |sub_case| sub = sub_case.body,
1839 .default_stmt => |sub_default| sub = sub_default.body,
1840 else => break,
1841 };
1842
1843 const res = try t.transSwitchProngStmt(base_scope, sub, body[i..]);
1844
1845 const switch_else = try ZigTag.switch_else.create(t.arena, res);
1846 try cases.append(switch_else);
1847 },
1848 else => {}, // collected in transSwitchProngStmt
1849 }
1850 }
1851
1852 if (!has_default) {
1853 const else_prong = try ZigTag.switch_else.create(t.arena, ZigTag.empty_block.init());
1854 try cases.append(else_prong);
1855 }
1856
1857 const switch_node = try ZigTag.@"switch".create(t.arena, .{
1858 .cond = switch_expr,
1859 .cases = try t.arena.dupe(ZigNode, cases.items),
1860 });
1861 try block_scope.statements.append(t.gpa, switch_node);
1862 try block_scope.statements.append(t.gpa, ZigTag.@"break".init());
1863 const while_body = try block_scope.complete();
1864
1865 return ZigTag.while_true.create(t.arena, while_body);
1866}
1867
1868/// Collects all items for this case, returns the first statement after the labels.
1869/// If items ends up empty, the prong should be translated as an else.
1870fn transCaseStmt(
1871 t: *Translator,
1872 scope: *Scope,
1873 stmt: Node.Index,
1874 items: *std.ArrayList(ZigNode),
1875) TransError!Node.Index {
1876 var sub = stmt;
1877 var seen_default = false;
1878 while (true) {
1879 switch (sub.get(t.tree)) {
1880 .default_stmt => |default_stmt| {
1881 seen_default = true;
1882 items.items.len = 0;
1883 sub = default_stmt.body;
1884 },
1885 .case_stmt => |case_stmt| {
1886 if (seen_default) {
1887 items.items.len = 0;
1888 sub = case_stmt.body;
1889 continue;
1890 }
1891
1892 const expr = if (case_stmt.end) |end| blk: {
1893 const start_node = try t.transExpr(scope, case_stmt.start, .used);
1894 const end_node = try t.transExpr(scope, end, .used);
1895
1896 break :blk try ZigTag.ellipsis3.create(t.arena, .{ .lhs = start_node, .rhs = end_node });
1897 } else try t.transExpr(scope, case_stmt.start, .used);
1898
1899 try items.append(expr);
1900 sub = case_stmt.body;
1901 },
1902 else => return sub,
1903 }
1904 }
1905}
1906
1907/// Collects all statements seen by this case into a block.
1908/// Avoids creating a block if the first statement is a break or return.
1909fn transSwitchProngStmt(
1910 t: *Translator,
1911 scope: *Scope,
1912 stmt: Node.Index,
1913 body: []const Node.Index,
1914) TransError!ZigNode {
1915 switch (stmt.get(t.tree)) {
1916 .break_stmt => return ZigTag.@"break".init(),
1917 .return_stmt => return t.transStmt(scope, stmt),
1918 .case_stmt, .default_stmt => unreachable,
1919 else => {
1920 var block_scope = try Scope.Block.init(t, scope, false);
1921 defer block_scope.deinit();
1922
1923 // we do not need to translate `stmt` since it is the first stmt of `body`
1924 try t.transSwitchProngStmtInline(&block_scope, body);
1925 return try block_scope.complete();
1926 },
1927 }
1928}
1929
1930/// Collects all statements seen by this case into a block.
1931fn transSwitchProngStmtInline(
1932 t: *Translator,
1933 block: *Scope.Block,
1934 body: []const Node.Index,
1935) TransError!void {
1936 for (body) |stmt| {
1937 switch (stmt.get(t.tree)) {
1938 .return_stmt => {
1939 const result = try t.transStmt(&block.base, stmt);
1940 try block.statements.append(t.gpa, result);
1941 return;
1942 },
1943 .break_stmt => {
1944 try block.statements.append(t.gpa, ZigTag.@"break".init());
1945 return;
1946 },
1947 .case_stmt => |case_stmt| {
1948 var sub = case_stmt.body;
1949 while (true) switch (sub.get(t.tree)) {
1950 .case_stmt => |sub_case| sub = sub_case.body,
1951 .default_stmt => |sub_default| sub = sub_default.body,
1952 else => break,
1953 };
1954 const result = try t.transStmt(&block.base, sub);
1955 assert(result.tag() != .declaration);
1956 try block.statements.append(t.gpa, result);
1957 if (result.isNoreturn(true)) return;
1958 },
1959 .default_stmt => |default_stmt| {
1960 var sub = default_stmt.body;
1961 while (true) switch (sub.get(t.tree)) {
1962 .case_stmt => |sub_case| sub = sub_case.body,
1963 .default_stmt => |sub_default| sub = sub_default.body,
1964 else => break,
1965 };
1966 const result = try t.transStmt(&block.base, sub);
1967 assert(result.tag() != .declaration);
1968 try block.statements.append(t.gpa, result);
1969 if (result.isNoreturn(true)) return;
1970 },
1971 .compound_stmt => |compound_stmt| {
1972 const result = try t.transCompoundStmt(&block.base, compound_stmt);
1973 try block.statements.append(t.gpa, result);
1974 if (result.isNoreturn(true)) return;
1975 },
1976 else => {
1977 const result = try t.transStmt(&block.base, stmt);
1978 switch (result.tag()) {
1979 .declaration, .empty_block => {},
1980 else => try block.statements.append(t.gpa, result),
1981 }
1982 },
1983 }
1984 }
1985}
1986
1987// ======================
1988// Expression translation
1989// ======================
1990
1991const ResultUsed = enum { used, unused };
1992
1993fn transExpr(t: *Translator, scope: *Scope, expr: Node.Index, used: ResultUsed) TransError!ZigNode {
1994 const qt = expr.qt(t.tree);
1995 return t.maybeSuppressResult(used, switch (expr.get(t.tree)) {
1996 .paren_expr => |paren_expr| {
1997 return t.transExpr(scope, paren_expr.operand, used);
1998 },
1999 .cast => |cast| return t.transCastExpr(scope, cast, cast.qt, used, .with_as),
2000 .decl_ref_expr => |decl_ref| try t.transDeclRefExpr(scope, decl_ref),
2001 .enumeration_ref => |enum_ref| try t.transDeclRefExpr(scope, enum_ref),
2002 .addr_of_expr => |addr_of_expr| try ZigTag.address_of.create(t.arena, try t.transExpr(scope, addr_of_expr.operand, .used)),
2003 .deref_expr => |deref_expr| res: {
2004 if (t.typeWasDemotedToOpaque(qt))
2005 return t.fail(error.UnsupportedTranslation, deref_expr.op_tok, "cannot dereference opaque type", .{});
2006
2007 // Dereferencing a function pointer is a no-op.
2008 if (qt.is(t.comp, .func)) return t.transExpr(scope, deref_expr.operand, used);
2009
2010 break :res try ZigTag.deref.create(t.arena, try t.transExpr(scope, deref_expr.operand, .used));
2011 },
2012 .bool_not_expr => |bool_not_expr| try ZigTag.not.create(t.arena, try t.transBoolExpr(scope, bool_not_expr.operand)),
2013 .bit_not_expr => |bit_not_expr| try ZigTag.bit_not.create(t.arena, try t.transExpr(scope, bit_not_expr.operand, .used)),
2014 .plus_expr => |plus_expr| return t.transExpr(scope, plus_expr.operand, used),
2015 .negate_expr => |negate_expr| res: {
2016 const operand_qt = negate_expr.operand.qt(t.tree);
2017 if (!t.typeHasWrappingOverflow(operand_qt)) {
2018 const sub_expr_node = try t.transExpr(scope, negate_expr.operand, .used);
2019 const to_negate = if (sub_expr_node.isBoolRes()) blk: {
2020 const ty_node = try ZigTag.type.create(t.arena, "c_int");
2021 const int_node = try ZigTag.int_from_bool.create(t.arena, sub_expr_node);
2022 break :blk try ZigTag.as.create(t.arena, .{ .lhs = ty_node, .rhs = int_node });
2023 } else sub_expr_node;
2024
2025 break :res try ZigTag.negate.create(t.arena, to_negate);
2026 } else if (t.signedness(operand_qt) == .unsigned) {
2027 // use -% x for unsigned integers
2028 break :res try ZigTag.negate_wrap.create(t.arena, try t.transExpr(scope, negate_expr.operand, .used));
2029 } else return t.fail(error.UnsupportedTranslation, negate_expr.op_tok, "C negation with non float non integer", .{});
2030 },
2031 .div_expr => |div_expr| res: {
2032 if (qt.isInt(t.comp) and t.signedness(qt) == .signed) {
2033 // signed integer division uses @divTrunc
2034 const lhs = try t.transExpr(scope, div_expr.lhs, .used);
2035 const rhs = try t.transExpr(scope, div_expr.rhs, .used);
2036 break :res try ZigTag.div_trunc.create(t.arena, .{ .lhs = lhs, .rhs = rhs });
2037 }
2038 // unsigned/float division uses the operator
2039 break :res try t.transBinExpr(scope, div_expr, .div);
2040 },
2041 .mod_expr => |mod_expr| res: {
2042 if (qt.isInt(t.comp) and t.signedness(qt) == .signed) {
2043 // signed integer remainder uses __helpers.signedRemainder
2044 const lhs = try t.transExpr(scope, mod_expr.lhs, .used);
2045 const rhs = try t.transExpr(scope, mod_expr.rhs, .used);
2046 break :res try t.createHelperCallNode(.signedRemainder, &.{ lhs, rhs });
2047 }
2048 // unsigned/float division uses the operator
2049 break :res try t.transBinExpr(scope, mod_expr, .mod);
2050 },
2051 .add_expr => |add_expr| res: {
2052 // `ptr + idx` and `idx + ptr` -> ptr + @as(usize, @bitCast(@as(isize, @intCast(idx))))
2053 const lhs_qt = add_expr.lhs.qt(t.tree);
2054 const rhs_qt = add_expr.rhs.qt(t.tree);
2055 if (qt.isPointer(t.comp) and (t.signedness(lhs_qt) == .signed or
2056 t.signedness(rhs_qt) == .signed))
2057 {
2058 break :res try t.transPointerArithmeticSignedOp(scope, add_expr, .add);
2059 }
2060
2061 if (t.signedness(qt) == .unsigned) {
2062 break :res try t.transBinExpr(scope, add_expr, .add_wrap);
2063 } else {
2064 break :res try t.transBinExpr(scope, add_expr, .add);
2065 }
2066 },
2067 .sub_expr => |sub_expr| res: {
2068 // `ptr - idx` -> ptr - @as(usize, @bitCast(@as(isize, @intCast(idx))))
2069 const lhs_qt = sub_expr.lhs.qt(t.tree);
2070 const rhs_qt = sub_expr.rhs.qt(t.tree);
2071 if (qt.isPointer(t.comp) and (t.signedness(lhs_qt) == .signed or
2072 t.signedness(rhs_qt) == .signed))
2073 {
2074 break :res try t.transPointerArithmeticSignedOp(scope, sub_expr, .sub);
2075 }
2076
2077 if (sub_expr.lhs.qt(t.tree).isPointer(t.comp) and sub_expr.rhs.qt(t.tree).isPointer(t.comp)) {
2078 break :res try t.transPtrDiffExpr(scope, sub_expr);
2079 } else if (t.signedness(qt) == .unsigned) {
2080 break :res try t.transBinExpr(scope, sub_expr, .sub_wrap);
2081 } else {
2082 break :res try t.transBinExpr(scope, sub_expr, .sub);
2083 }
2084 },
2085 .mul_expr => |mul_expr| if (t.signedness(qt) == .unsigned)
2086 try t.transBinExpr(scope, mul_expr, .mul_wrap)
2087 else
2088 try t.transBinExpr(scope, mul_expr, .mul),
2089
2090 .less_than_expr => |lt| try t.transBinExpr(scope, lt, .less_than),
2091 .greater_than_expr => |gt| try t.transBinExpr(scope, gt, .greater_than),
2092 .less_than_equal_expr => |lte| try t.transBinExpr(scope, lte, .less_than_equal),
2093 .greater_than_equal_expr => |gte| try t.transBinExpr(scope, gte, .greater_than_equal),
2094 .equal_expr => |equal_expr| try t.transBinExpr(scope, equal_expr, .equal),
2095 .not_equal_expr => |not_equal_expr| try t.transBinExpr(scope, not_equal_expr, .not_equal),
2096
2097 .bool_and_expr => |bool_and_expr| try t.transBoolBinExpr(scope, bool_and_expr, .@"and"),
2098 .bool_or_expr => |bool_or_expr| try t.transBoolBinExpr(scope, bool_or_expr, .@"or"),
2099
2100 .bit_and_expr => |bit_and_expr| try t.transBinExpr(scope, bit_and_expr, .bit_and),
2101 .bit_or_expr => |bit_or_expr| try t.transBinExpr(scope, bit_or_expr, .bit_or),
2102 .bit_xor_expr => |bit_xor_expr| try t.transBinExpr(scope, bit_xor_expr, .bit_xor),
2103
2104 .shl_expr => |shl_expr| try t.transShiftExpr(scope, shl_expr, .shl),
2105 .shr_expr => |shr_expr| try t.transShiftExpr(scope, shr_expr, .shr),
2106
2107 .member_access_expr => |member_access| try t.transMemberAccess(scope, .normal, member_access, null),
2108 .member_access_ptr_expr => |member_access| try t.transMemberAccess(scope, .ptr, member_access, null),
2109 .array_access_expr => |array_access| try t.transArrayAccess(scope, array_access, null),
2110
2111 .builtin_ref => unreachable,
2112 .builtin_call_expr => |call| return t.transBuiltinCall(scope, call, used),
2113 .call_expr => |call| return t.transCall(scope, call, used),
2114
2115 .builtin_types_compatible_p => |compatible| blk: {
2116 const lhs = try t.transType(scope, compatible.lhs, compatible.builtin_tok);
2117 const rhs = try t.transType(scope, compatible.rhs, compatible.builtin_tok);
2118
2119 break :blk try ZigTag.equal.create(t.arena, .{
2120 .lhs = lhs,
2121 .rhs = rhs,
2122 });
2123 },
2124 .builtin_choose_expr => |choose| return t.transCondExpr(scope, choose, used),
2125 .cond_expr => |cond_expr| return t.transCondExpr(scope, cond_expr, used),
2126 .binary_cond_expr => |conditional| return t.transBinaryCondExpr(scope, conditional, used),
2127 .cond_dummy_expr => unreachable,
2128
2129 .assign_expr => |assign| return t.transAssignExpr(scope, assign, used),
2130 .add_assign_expr => |assign| return t.transCompoundAssign(scope, assign, used),
2131 .sub_assign_expr => |assign| return t.transCompoundAssign(scope, assign, used),
2132 .mul_assign_expr => |assign| return t.transCompoundAssign(scope, assign, used),
2133 .div_assign_expr => |assign| return t.transCompoundAssign(scope, assign, used),
2134 .mod_assign_expr => |assign| return t.transCompoundAssign(scope, assign, used),
2135 .shl_assign_expr => |assign| return t.transCompoundAssign(scope, assign, used),
2136 .shr_assign_expr => |assign| return t.transCompoundAssign(scope, assign, used),
2137 .bit_and_assign_expr => |assign| return t.transCompoundAssign(scope, assign, used),
2138 .bit_xor_assign_expr => |assign| return t.transCompoundAssign(scope, assign, used),
2139 .bit_or_assign_expr => |assign| return t.transCompoundAssign(scope, assign, used),
2140 .compound_assign_dummy_expr => {
2141 assert(used == .used);
2142 return t.compound_assign_dummy.?;
2143 },
2144
2145 .comma_expr => |comma_expr| return t.transCommaExpr(scope, comma_expr, used),
2146 .pre_inc_expr => |un| return t.transIncDecExpr(scope, un, .pre, .inc, used),
2147 .pre_dec_expr => |un| return t.transIncDecExpr(scope, un, .pre, .dec, used),
2148 .post_inc_expr => |un| return t.transIncDecExpr(scope, un, .post, .inc, used),
2149 .post_dec_expr => |un| return t.transIncDecExpr(scope, un, .post, .dec, used),
2150
2151 .int_literal => return t.transIntLiteral(scope, expr, used, .with_as),
2152 .char_literal => return t.transCharLiteral(scope, expr, used, .with_as),
2153 .float_literal => return t.transFloatLiteral(scope, expr, used, .with_as),
2154 .string_literal_expr => |literal| try t.transStringLiteral(scope, expr, literal),
2155 .bool_literal => res: {
2156 const val = t.tree.value_map.get(expr).?;
2157 break :res if (val.toBool(t.comp))
2158 ZigTag.true_literal.init()
2159 else
2160 ZigTag.false_literal.init();
2161 },
2162 .nullptr_literal => ZigTag.null_literal.init(),
2163 .imaginary_literal => |literal| {
2164 return t.fail(error.UnsupportedTranslation, literal.op_tok, "TODO complex numbers", .{});
2165 },
2166 .compound_literal_expr => |literal| return t.transCompoundLiteral(scope, literal, used),
2167
2168 .default_init_expr => |default_init| return t.transDefaultInit(scope, default_init, used, .with_as),
2169 .array_init_expr => |array_init| return t.transArrayInit(scope, array_init, used),
2170 .union_init_expr => |union_init| return t.transUnionInit(scope, union_init, used),
2171 .struct_init_expr => |struct_init| return t.transStructInit(scope, struct_init, used),
2172 .array_filler_expr => unreachable,
2173
2174 .sizeof_expr => |sizeof| try t.transTypeInfo(scope, .sizeof, sizeof),
2175 .alignof_expr => |alignof| try t.transTypeInfo(scope, .alignof, alignof),
2176
2177 .imag_expr, .real_expr => |un| {
2178 return t.fail(error.UnsupportedTranslation, un.op_tok, "TODO complex numbers", .{});
2179 },
2180 .addr_of_label => |addr_of_label| {
2181 return t.fail(error.UnsupportedTranslation, addr_of_label.label_tok, "TODO computed goto", .{});
2182 },
2183
2184 .generic_expr => |generic| return t.transExpr(scope, generic.chosen, used),
2185 .generic_association_expr => |generic| return t.transExpr(scope, generic.expr, used),
2186 .generic_default_expr => |generic| return t.transExpr(scope, generic.expr, used),
2187
2188 .stmt_expr => |stmt_expr| return t.transStmtExpr(scope, stmt_expr, used),
2189
2190 .builtin_convertvector => |convertvector| try t.transConvertvectorExpr(scope, convertvector),
2191 .builtin_shufflevector => |shufflevector| try t.transShufflevectorExpr(scope, shufflevector),
2192
2193 .compound_stmt,
2194 .static_assert,
2195 .return_stmt,
2196 .null_stmt,
2197 .if_stmt,
2198 .while_stmt,
2199 .do_while_stmt,
2200 .for_stmt,
2201 .continue_stmt,
2202 .break_stmt,
2203 .labeled_stmt,
2204 .switch_stmt,
2205 .case_stmt,
2206 .default_stmt,
2207 .goto_stmt,
2208 .computed_goto_stmt,
2209 .gnu_asm_simple,
2210 .global_asm,
2211 .typedef,
2212 .struct_decl,
2213 .union_decl,
2214 .enum_decl,
2215 .function,
2216 .param,
2217 .variable,
2218 .enum_field,
2219 .record_field,
2220 .struct_forward_decl,
2221 .union_forward_decl,
2222 .enum_forward_decl,
2223 .empty_decl,
2224 => unreachable, // not an expression
2225 });
2226}
2227
2228/// Same as `transExpr` but with the knowledge that the operand will be type coerced, and therefore
2229/// an `@as` would be redundant. This is used to prevent redundant `@as` in integer literals.
2230fn transExprCoercing(t: *Translator, scope: *Scope, expr: Node.Index, used: ResultUsed) TransError!ZigNode {
2231 switch (expr.get(t.tree)) {
2232 .int_literal => return t.transIntLiteral(scope, expr, used, .no_as),
2233 .char_literal => return t.transCharLiteral(scope, expr, used, .no_as),
2234 .float_literal => return t.transFloatLiteral(scope, expr, used, .no_as),
2235 .cast => |cast| switch (cast.kind) {
2236 .no_op => {
2237 const operand = cast.operand.get(t.tree);
2238 if (operand == .cast) {
2239 return t.transCastExpr(scope, operand.cast, cast.qt, used, .no_as);
2240 }
2241 return t.transExprCoercing(scope, cast.operand, used);
2242 },
2243 .lval_to_rval => return t.transExprCoercing(scope, cast.operand, used),
2244 else => return t.transCastExpr(scope, cast, cast.qt, used, .no_as),
2245 },
2246 .default_init_expr => |default_init| return try t.transDefaultInit(scope, default_init, used, .no_as),
2247 .compound_literal_expr => |literal| {
2248 if (!literal.thread_local and literal.storage_class != .static) {
2249 return t.transExprCoercing(scope, literal.initializer, used);
2250 }
2251 },
2252 else => {},
2253 }
2254
2255 return t.transExpr(scope, expr, used);
2256}
2257
2258fn transBoolExpr(t: *Translator, scope: *Scope, expr: Node.Index) TransError!ZigNode {
2259 switch (expr.get(t.tree)) {
2260 .int_literal => {
2261 const int_val = t.tree.value_map.get(expr).?;
2262 return if (int_val.isZero(t.comp))
2263 ZigTag.false_literal.init()
2264 else
2265 ZigTag.true_literal.init();
2266 },
2267 .cast => |cast| switch (cast.kind) {
2268 .bool_to_int => return t.transExpr(scope, cast.operand, .used),
2269 .array_to_pointer => {
2270 const operand = cast.operand.get(t.tree);
2271 if (operand == .string_literal_expr) {
2272 // @intFromPtr("foo") != 0, always true
2273 const str = try t.transStringLiteral(scope, cast.operand, operand.string_literal_expr);
2274 const int_from_ptr = try ZigTag.int_from_ptr.create(t.arena, str);
2275 return ZigTag.not_equal.create(t.arena, .{ .lhs = int_from_ptr, .rhs = ZigTag.zero_literal.init() });
2276 }
2277 },
2278 else => {},
2279 },
2280 else => {},
2281 }
2282
2283 const maybe_bool_res = try t.transExpr(scope, expr, .used);
2284 if (maybe_bool_res.isBoolRes()) {
2285 return maybe_bool_res;
2286 }
2287
2288 return t.finishBoolExpr(expr.qt(t.tree), maybe_bool_res);
2289}
2290
2291fn finishBoolExpr(t: *Translator, qt: QualType, node: ZigNode) TransError!ZigNode {
2292 const sk = qt.scalarKind(t.comp);
2293 if (sk == .bool) return node;
2294 if (sk == .nullptr_t) {
2295 // node == null, always true
2296 return ZigTag.equal.create(t.arena, .{ .lhs = node, .rhs = ZigTag.null_literal.init() });
2297 }
2298 if (sk.isPointer()) {
2299 // node != null
2300 return ZigTag.not_equal.create(t.arena, .{ .lhs = node, .rhs = ZigTag.null_literal.init() });
2301 }
2302 if (sk != .none) {
2303 // node != 0
2304 return ZigTag.not_equal.create(t.arena, .{ .lhs = node, .rhs = ZigTag.zero_literal.init() });
2305 }
2306 unreachable; // Unexpected bool expression type
2307}
2308
2309fn transCastExpr(
2310 t: *Translator,
2311 scope: *Scope,
2312 cast: Node.Cast,
2313 dest_qt: QualType,
2314 used: ResultUsed,
2315 suppress_as: SuppressCast,
2316) TransError!ZigNode {
2317 const operand = switch (cast.kind) {
2318 .no_op => {
2319 const operand = cast.operand.get(t.tree);
2320 if (operand == .cast) {
2321 return t.transCastExpr(scope, operand.cast, cast.qt, used, suppress_as);
2322 }
2323 return t.transExpr(scope, cast.operand, used);
2324 },
2325 .lval_to_rval, .function_to_pointer => {
2326 return t.transExpr(scope, cast.operand, used);
2327 },
2328 .int_cast => int_cast: {
2329 const src_qt = cast.operand.qt(t.tree);
2330
2331 if (cast.implicit) {
2332 if (t.tree.value_map.get(cast.operand)) |val| {
2333 const max_int = try aro.Value.maxInt(dest_qt, t.comp);
2334 const min_int = try aro.Value.minInt(dest_qt, t.comp);
2335
2336 if (val.compare(.lte, max_int, t.comp) and val.compare(.gte, min_int, t.comp)) {
2337 break :int_cast try t.transExprCoercing(scope, cast.operand, .used);
2338 }
2339 }
2340 }
2341 const operand = try t.transExpr(scope, cast.operand, .used);
2342 break :int_cast try t.transIntCast(operand, src_qt, dest_qt);
2343 },
2344 .to_void => {
2345 assert(used == .unused);
2346 return try t.transExpr(scope, cast.operand, .unused);
2347 },
2348 .null_to_pointer => ZigTag.null_literal.init(),
2349 .array_to_pointer => array_to_pointer: {
2350 const child_qt = dest_qt.childType(t.comp);
2351
2352 loop: switch (cast.operand.get(t.tree)) {
2353 .string_literal_expr => |literal| {
2354 const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
2355
2356 const ref = if (literal.kind == .utf8 or literal.kind == .ascii)
2357 sub_expr_node
2358 else
2359 try ZigTag.address_of.create(t.arena, sub_expr_node);
2360
2361 const casted = if (child_qt.@"const")
2362 ref
2363 else
2364 try ZigTag.const_cast.create(t.arena, sub_expr_node);
2365
2366 return t.maybeSuppressResult(used, casted);
2367 },
2368 .paren_expr => |paren_expr| {
2369 continue :loop paren_expr.operand.get(t.tree);
2370 },
2371 .generic_expr => |generic| {
2372 continue :loop generic.chosen.get(t.tree);
2373 },
2374 .generic_association_expr => |generic| {
2375 continue :loop generic.expr.get(t.tree);
2376 },
2377 .generic_default_expr => |generic| {
2378 continue :loop generic.expr.get(t.tree);
2379 },
2380 else => {},
2381 }
2382
2383 if (cast.operand.qt(t.tree).arrayLen(t.comp) == null) {
2384 return try t.transExpr(scope, cast.operand, used);
2385 }
2386
2387 const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
2388 const ref = try ZigTag.address_of.create(t.arena, sub_expr_node);
2389 const align_cast = try ZigTag.align_cast.create(t.arena, ref);
2390 break :array_to_pointer try ZigTag.ptr_cast.create(t.arena, align_cast);
2391 },
2392 .int_to_pointer => int_to_pointer: {
2393 var sub_expr_node = try t.transExpr(scope, cast.operand, .used);
2394 const operand_qt = cast.operand.qt(t.tree);
2395 if (t.signedness(operand_qt) == .signed or operand_qt.bitSizeof(t.comp) > t.comp.target.ptrBitWidth()) {
2396 sub_expr_node = try ZigTag.as.create(t.arena, .{
2397 .lhs = try ZigTag.type.create(t.arena, "usize"),
2398 .rhs = try ZigTag.int_cast.create(t.arena, sub_expr_node),
2399 });
2400 }
2401 break :int_to_pointer try ZigTag.ptr_from_int.create(t.arena, sub_expr_node);
2402 },
2403 .int_to_bool => {
2404 const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
2405 if (sub_expr_node.isBoolRes()) return sub_expr_node;
2406 if (cast.operand.qt(t.tree).is(t.comp, .bool)) return sub_expr_node;
2407 const cmp_node = try ZigTag.not_equal.create(t.arena, .{ .lhs = sub_expr_node, .rhs = ZigTag.zero_literal.init() });
2408 return t.maybeSuppressResult(used, cmp_node);
2409 },
2410 .float_to_bool => {
2411 const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
2412 const cmp_node = try ZigTag.not_equal.create(t.arena, .{ .lhs = sub_expr_node, .rhs = ZigTag.zero_literal.init() });
2413 return t.maybeSuppressResult(used, cmp_node);
2414 },
2415 .pointer_to_bool => {
2416 const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
2417
2418 // Special case function pointers as @intFromPtr(expr) != 0
2419 if (cast.operand.qt(t.tree).get(t.comp, .pointer)) |ptr_ty| if (ptr_ty.child.is(t.comp, .func)) {
2420 const ptr_node = if (sub_expr_node.tag() == .identifier)
2421 try ZigTag.address_of.create(t.arena, sub_expr_node)
2422 else
2423 sub_expr_node;
2424 const int_from_ptr = try ZigTag.int_from_ptr.create(t.arena, ptr_node);
2425 const cmp_node = try ZigTag.not_equal.create(t.arena, .{ .lhs = int_from_ptr, .rhs = ZigTag.zero_literal.init() });
2426 return t.maybeSuppressResult(used, cmp_node);
2427 };
2428
2429 const cmp_node = try ZigTag.not_equal.create(t.arena, .{ .lhs = sub_expr_node, .rhs = ZigTag.null_literal.init() });
2430 return t.maybeSuppressResult(used, cmp_node);
2431 },
2432 .bool_to_int => bool_to_int: {
2433 const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
2434 break :bool_to_int try ZigTag.int_from_bool.create(t.arena, sub_expr_node);
2435 },
2436 .bool_to_float => bool_to_float: {
2437 const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
2438 const int_from_bool = try ZigTag.int_from_bool.create(t.arena, sub_expr_node);
2439 break :bool_to_float try ZigTag.float_from_int.create(t.arena, int_from_bool);
2440 },
2441 .bool_to_pointer => bool_to_pointer: {
2442 const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
2443 const int_from_bool = try ZigTag.int_from_bool.create(t.arena, sub_expr_node);
2444 break :bool_to_pointer try ZigTag.ptr_from_int.create(t.arena, int_from_bool);
2445 },
2446 .float_cast => float_cast: {
2447 const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
2448 break :float_cast try ZigTag.float_cast.create(t.arena, sub_expr_node);
2449 },
2450 .int_to_float => int_to_float: {
2451 const sub_expr_node = try t.transExpr(scope, cast.operand, used);
2452 const int_node = if (sub_expr_node.isBoolRes())
2453 try ZigTag.int_from_bool.create(t.arena, sub_expr_node)
2454 else
2455 sub_expr_node;
2456 break :int_to_float try ZigTag.float_from_int.create(t.arena, int_node);
2457 },
2458 .float_to_int => float_to_int: {
2459 const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
2460 break :float_to_int try ZigTag.int_from_float.create(t.arena, sub_expr_node);
2461 },
2462 .pointer_to_int => pointer_to_int: {
2463 const sub_expr_node = try t.transPointerCastExpr(scope, cast.operand);
2464 const ptr_node = try ZigTag.int_from_ptr.create(t.arena, sub_expr_node);
2465 break :pointer_to_int try ZigTag.int_cast.create(t.arena, ptr_node);
2466 },
2467 .bitcast => bitcast: {
2468 const sub_expr_node = try t.transPointerCastExpr(scope, cast.operand);
2469 const operand_qt = cast.operand.qt(t.tree);
2470 if (dest_qt.isPointer(t.comp) and operand_qt.isPointer(t.comp)) {
2471 var casted = try ZigTag.align_cast.create(t.arena, sub_expr_node);
2472 casted = try ZigTag.ptr_cast.create(t.arena, casted);
2473
2474 const src_elem = operand_qt.childType(t.comp);
2475 const dest_elem = dest_qt.childType(t.comp);
2476 if ((src_elem.@"const" or src_elem.is(t.comp, .func)) and !dest_elem.@"const") {
2477 casted = try ZigTag.const_cast.create(t.arena, casted);
2478 }
2479 if (src_elem.@"volatile" and !dest_elem.@"volatile") {
2480 casted = try ZigTag.volatile_cast.create(t.arena, casted);
2481 }
2482 break :bitcast casted;
2483 }
2484
2485 break :bitcast try ZigTag.bit_cast.create(t.arena, sub_expr_node);
2486 },
2487 .union_cast => union_cast: {
2488 const union_type = try t.transType(scope, dest_qt, cast.l_paren);
2489
2490 const operand_qt = cast.operand.qt(t.tree);
2491 const union_base = dest_qt.base(t.comp);
2492 const field = for (union_base.type.@"union".fields) |field| {
2493 if (field.qt.eql(operand_qt, t.comp)) break field;
2494 } else unreachable;
2495 const field_name = if (field.name_tok == 0) t.anonymous_record_field_names.get(.{
2496 .parent = union_base.qt,
2497 .field = field.qt,
2498 }).? else field.name.lookup(t.comp);
2499
2500 const field_init = try t.arena.create(ast.Payload.ContainerInit.Initializer);
2501 field_init.* = .{
2502 .name = field_name,
2503 .value = try t.transExpr(scope, cast.operand, .used),
2504 };
2505 break :union_cast try ZigTag.container_init.create(t.arena, .{
2506 .lhs = union_type,
2507 .inits = field_init[0..1],
2508 });
2509 },
2510 else => return t.fail(error.UnsupportedTranslation, cast.l_paren, "TODO translate {s} cast", .{@tagName(cast.kind)}),
2511 };
2512 if (suppress_as == .no_as) return t.maybeSuppressResult(used, operand);
2513 if (used == .unused) return t.maybeSuppressResult(used, operand);
2514 const as = try ZigTag.as.create(t.arena, .{
2515 .lhs = try t.transType(scope, dest_qt, cast.l_paren),
2516 .rhs = operand,
2517 });
2518 return as;
2519}
2520
2521fn transIntCast(t: *Translator, operand: ZigNode, src_qt: QualType, dest_qt: QualType) !ZigNode {
2522 const src_dest_order = src_qt.intRankOrder(dest_qt, t.comp);
2523 const different_sign = t.signedness(src_qt) != t.signedness(dest_qt);
2524 const needs_bitcast = different_sign and !(t.signedness(src_qt) == .unsigned and src_dest_order == .lt);
2525
2526 var casted = operand;
2527 if (casted.isBoolRes()) {
2528 casted = try ZigTag.int_from_bool.create(t.arena, casted);
2529 } else if (src_dest_order == .gt) {
2530 // No C type is smaller than the 1 bit from @intFromBool
2531 casted = try ZigTag.truncate.create(t.arena, casted);
2532 }
2533 if (needs_bitcast) {
2534 if (src_dest_order != .eq) {
2535 casted = try ZigTag.as.create(t.arena, .{
2536 .lhs = try t.transTypeIntWidthOf(dest_qt, t.signedness(src_qt) == .signed),
2537 .rhs = casted,
2538 });
2539 }
2540 return ZigTag.bit_cast.create(t.arena, casted);
2541 }
2542 return casted;
2543}
2544
2545/// Same as `transExpr` but adds a `&` if the expression is an identifier referencing a function type.
2546fn transPointerCastExpr(t: *Translator, scope: *Scope, expr: Node.Index) TransError!ZigNode {
2547 const sub_expr_node = try t.transExpr(scope, expr, .used);
2548 switch (expr.get(t.tree)) {
2549 .cast => |cast| if (cast.kind == .function_to_pointer and sub_expr_node.tag() == .identifier) {
2550 return ZigTag.address_of.create(t.arena, sub_expr_node);
2551 },
2552 else => {},
2553 }
2554 return sub_expr_node;
2555}
2556
2557fn transDeclRefExpr(t: *Translator, scope: *Scope, decl_ref: Node.DeclRef) TransError!ZigNode {
2558 const name = t.tree.tokSlice(decl_ref.name_tok);
2559 const maybe_alias = scope.getAlias(name);
2560 const mangled_name = maybe_alias orelse name;
2561
2562 switch (decl_ref.decl.get(t.tree)) {
2563 .function => |function| if (function.definition == null and function.body == null) {
2564 // Try translating the decl again in case of out of scope declaration.
2565 try t.transFnDecl(scope, function);
2566 },
2567 else => {},
2568 }
2569
2570 const decl = decl_ref.decl.get(t.tree);
2571 const ref_expr = blk: {
2572 const identifier = try ZigTag.identifier.create(t.arena, mangled_name);
2573 if (decl_ref.qt.is(t.comp, .func) and maybe_alias != null) {
2574 break :blk try ZigTag.field_access.create(t.arena, .{
2575 .lhs = identifier,
2576 .field_name = name,
2577 });
2578 }
2579 if (decl == .variable and maybe_alias != null) {
2580 switch (decl.variable.storage_class) {
2581 .@"extern", .static => {
2582 break :blk try ZigTag.field_access.create(t.arena, .{
2583 .lhs = identifier,
2584 .field_name = name,
2585 });
2586 },
2587 else => {},
2588 }
2589 }
2590 break :blk identifier;
2591 };
2592
2593 scope.skipVariableDiscard(mangled_name);
2594 return ref_expr;
2595}
2596
2597fn transBinExpr(t: *Translator, scope: *Scope, bin: Node.Binary, op_id: ZigTag) TransError!ZigNode {
2598 const lhs_uncasted = try t.transExpr(scope, bin.lhs, .used);
2599 const rhs_uncasted = try t.transExpr(scope, bin.rhs, .used);
2600
2601 const lhs = if (lhs_uncasted.isBoolRes())
2602 try ZigTag.int_from_bool.create(t.arena, lhs_uncasted)
2603 else
2604 lhs_uncasted;
2605
2606 const rhs = if (rhs_uncasted.isBoolRes())
2607 try ZigTag.int_from_bool.create(t.arena, rhs_uncasted)
2608 else
2609 rhs_uncasted;
2610
2611 return t.createBinOpNode(op_id, lhs, rhs);
2612}
2613
2614fn transBoolBinExpr(t: *Translator, scope: *Scope, bin: Node.Binary, op: ZigTag) !ZigNode {
2615 std.debug.assert(op == .@"and" or op == .@"or");
2616
2617 const lhs = try t.transBoolExpr(scope, bin.lhs);
2618 const rhs = try t.transBoolExpr(scope, bin.rhs);
2619
2620 return t.createBinOpNode(op, lhs, rhs);
2621}
2622
2623fn transShiftExpr(t: *Translator, scope: *Scope, bin: Node.Binary, op_id: ZigTag) !ZigNode {
2624 std.debug.assert(op_id == .shl or op_id == .shr);
2625
2626 // lhs >> @intCast(rh)
2627 const lhs = try t.transExpr(scope, bin.lhs, .used);
2628
2629 const rhs = try t.transExprCoercing(scope, bin.rhs, .used);
2630 const rhs_casted = try ZigTag.int_cast.create(t.arena, rhs);
2631
2632 return t.createBinOpNode(op_id, lhs, rhs_casted);
2633}
2634
2635fn transCondExpr(
2636 t: *Translator,
2637 scope: *Scope,
2638 conditional: Node.Conditional,
2639 used: ResultUsed,
2640) TransError!ZigNode {
2641 var cond_scope: Scope.Condition = .{
2642 .base = .{
2643 .parent = scope,
2644 .id = .condition,
2645 },
2646 };
2647 defer cond_scope.deinit();
2648
2649 const res_is_bool = conditional.qt.is(t.comp, .bool);
2650 const cond = try t.transBoolExpr(&cond_scope.base, conditional.cond);
2651
2652 var then_body = try t.transExpr(scope, conditional.then_expr, used);
2653 if (!res_is_bool and then_body.isBoolRes()) {
2654 then_body = try ZigTag.int_from_bool.create(t.arena, then_body);
2655 }
2656
2657 var else_body = try t.transExpr(scope, conditional.else_expr, used);
2658 if (!res_is_bool and else_body.isBoolRes()) {
2659 else_body = try ZigTag.int_from_bool.create(t.arena, else_body);
2660 }
2661
2662 // The `ResultUsed` is forwarded to both branches so no need to suppress the result here.
2663 return ZigTag.@"if".create(t.arena, .{ .cond = cond, .then = then_body, .@"else" = else_body });
2664}
2665
2666fn transBinaryCondExpr(
2667 t: *Translator,
2668 scope: *Scope,
2669 conditional: Node.Conditional,
2670 used: ResultUsed,
2671) TransError!ZigNode {
2672 // GNU extension of the ternary operator where the middle expression is
2673 // omitted, the condition itself is returned if it evaluates to true.
2674
2675 if (used == .unused) {
2676 // Result unused so this can be translated as
2677 // if (condition) else_expr;
2678 var cond_scope: Scope.Condition = .{
2679 .base = .{
2680 .parent = scope,
2681 .id = .condition,
2682 },
2683 };
2684 defer cond_scope.deinit();
2685
2686 return ZigTag.@"if".create(t.arena, .{
2687 .cond = try t.transBoolExpr(&cond_scope.base, conditional.cond),
2688 .then = try t.transExpr(scope, conditional.else_expr, .unused),
2689 .@"else" = null,
2690 });
2691 }
2692
2693 const res_is_bool = conditional.qt.is(t.comp, .bool);
2694 // c: (condition)?:(else_expr)
2695 // zig: (blk: {
2696 // const _cond_temp = (condition);
2697 // break :blk if (_cond_temp) _cond_temp else (else_expr);
2698 // })
2699 var block_scope = try Scope.Block.init(t, scope, true);
2700 defer block_scope.deinit();
2701
2702 const cond_temp = try block_scope.reserveMangledName("cond_temp");
2703 const init_node = try t.transExpr(&block_scope.base, conditional.cond, .used);
2704 const temp_decl = try ZigTag.var_simple.create(t.arena, .{ .name = cond_temp, .init = init_node });
2705 try block_scope.statements.append(t.gpa, temp_decl);
2706
2707 var cond_scope: Scope.Condition = .{
2708 .base = .{
2709 .parent = &block_scope.base,
2710 .id = .condition,
2711 },
2712 };
2713 defer cond_scope.deinit();
2714
2715 const cond_ident = try ZigTag.identifier.create(t.arena, cond_temp);
2716 const cond_node = try t.finishBoolExpr(conditional.cond.qt(t.tree), cond_ident);
2717 var then_body = cond_ident;
2718 if (!res_is_bool and init_node.isBoolRes()) {
2719 then_body = try ZigTag.int_from_bool.create(t.arena, then_body);
2720 }
2721
2722 var else_body = try t.transExpr(&block_scope.base, conditional.else_expr, .used);
2723 if (!res_is_bool and else_body.isBoolRes()) {
2724 else_body = try ZigTag.int_from_bool.create(t.arena, else_body);
2725 }
2726 const if_node = try ZigTag.@"if".create(t.arena, .{
2727 .cond = cond_node,
2728 .then = then_body,
2729 .@"else" = else_body,
2730 });
2731 const break_node = try ZigTag.break_val.create(t.arena, .{
2732 .label = block_scope.label,
2733 .val = if_node,
2734 });
2735 try block_scope.statements.append(t.gpa, break_node);
2736 return block_scope.complete();
2737}
2738
2739fn transCommaExpr(t: *Translator, scope: *Scope, bin: Node.Binary, used: ResultUsed) TransError!ZigNode {
2740 if (used == .unused) {
2741 const lhs = try t.transExprCoercing(scope, bin.lhs, .unused);
2742 try scope.appendNode(lhs);
2743 const rhs = try t.transExprCoercing(scope, bin.rhs, .unused);
2744 return rhs;
2745 }
2746
2747 var block_scope = try Scope.Block.init(t, scope, true);
2748 defer block_scope.deinit();
2749
2750 const lhs = try t.transExprCoercing(&block_scope.base, bin.lhs, .unused);
2751 try block_scope.statements.append(t.gpa, lhs);
2752
2753 const rhs = try t.transExprCoercing(&block_scope.base, bin.rhs, .used);
2754 const break_node = try ZigTag.break_val.create(t.arena, .{
2755 .label = block_scope.label,
2756 .val = rhs,
2757 });
2758 try block_scope.statements.append(t.gpa, break_node);
2759
2760 return try block_scope.complete();
2761}
2762
2763fn transAssignExpr(t: *Translator, scope: *Scope, bin: Node.Binary, used: ResultUsed) !ZigNode {
2764 if (used == .unused) {
2765 const lhs = try t.transExpr(scope, bin.lhs, .used);
2766 var rhs = try t.transExprCoercing(scope, bin.rhs, .used);
2767
2768 const lhs_qt = bin.lhs.qt(t.tree);
2769 if (rhs.isBoolRes() and !lhs_qt.is(t.comp, .bool)) {
2770 rhs = try ZigTag.int_from_bool.create(t.arena, rhs);
2771 }
2772
2773 return t.createBinOpNode(.assign, lhs, rhs);
2774 }
2775
2776 var block_scope = try Scope.Block.init(t, scope, true);
2777 defer block_scope.deinit();
2778
2779 const tmp = try block_scope.reserveMangledName("tmp");
2780
2781 var rhs = try t.transExpr(&block_scope.base, bin.rhs, .used);
2782 const lhs_qt = bin.lhs.qt(t.tree);
2783 if (rhs.isBoolRes() and !lhs_qt.is(t.comp, .bool)) {
2784 rhs = try ZigTag.int_from_bool.create(t.arena, rhs);
2785 }
2786
2787 const tmp_decl = try ZigTag.var_simple.create(t.arena, .{ .name = tmp, .init = rhs });
2788 try block_scope.statements.append(t.gpa, tmp_decl);
2789
2790 const lhs = try t.transExprCoercing(&block_scope.base, bin.lhs, .used);
2791 const tmp_ident = try ZigTag.identifier.create(t.arena, tmp);
2792
2793 const assign = try t.createBinOpNode(.assign, lhs, tmp_ident);
2794 try block_scope.statements.append(t.gpa, assign);
2795
2796 const break_node = try ZigTag.break_val.create(t.arena, .{
2797 .label = block_scope.label,
2798 .val = tmp_ident,
2799 });
2800 try block_scope.statements.append(t.gpa, break_node);
2801
2802 return try block_scope.complete();
2803}
2804
2805fn transCompoundAssign(
2806 t: *Translator,
2807 scope: *Scope,
2808 assign: Node.Binary,
2809 used: ResultUsed,
2810) !ZigNode {
2811 // If the result is unused we can try using the equivalent Zig operator
2812 // without a block
2813 if (used == .unused) {
2814 if (try t.transCompoundAssignSimple(scope, null, assign)) |some| {
2815 return some;
2816 }
2817 }
2818
2819 // Otherwise we need to wrap the the compound assignment in a block.
2820 var block_scope = try Scope.Block.init(t, scope, used == .used);
2821 defer block_scope.deinit();
2822 const ref = try block_scope.reserveMangledName("ref");
2823
2824 const lhs_expr = try t.transExpr(&block_scope.base, assign.lhs, .used);
2825 const addr_of = try ZigTag.address_of.create(t.arena, lhs_expr);
2826 const ref_decl = try ZigTag.var_simple.create(t.arena, .{ .name = ref, .init = addr_of });
2827 try block_scope.statements.append(t.gpa, ref_decl);
2828
2829 const lhs_node = try ZigTag.identifier.create(t.arena, ref);
2830 const ref_node = try ZigTag.deref.create(t.arena, lhs_node);
2831
2832 // Use the equivalent Zig operator if possible.
2833 if (try t.transCompoundAssignSimple(scope, ref_node, assign)) |some| {
2834 try block_scope.statements.append(t.gpa, some);
2835 } else {
2836 const old_dummy = t.compound_assign_dummy;
2837 defer t.compound_assign_dummy = old_dummy;
2838 t.compound_assign_dummy = ref_node;
2839
2840 // Otherwise do the operation and assignment separately.
2841 const rhs_node = try t.transExprCoercing(&block_scope.base, assign.rhs, .used);
2842 const assign_node = try t.createBinOpNode(.assign, ref_node, rhs_node);
2843 try block_scope.statements.append(t.gpa, assign_node);
2844 }
2845
2846 if (used == .used) {
2847 const break_node = try ZigTag.break_val.create(t.arena, .{
2848 .label = block_scope.label,
2849 .val = ref_node,
2850 });
2851 try block_scope.statements.append(t.gpa, break_node);
2852 }
2853 return block_scope.complete();
2854}
2855
2856/// Translates compound assignment using the equivalent Zig operator if possible.
2857fn transCompoundAssignSimple(t: *Translator, scope: *Scope, lhs_dummy_opt: ?ZigNode, assign: Node.Binary) TransError!?ZigNode {
2858 const assign_rhs = assign.rhs.get(t.tree);
2859 if (assign_rhs == .cast) return null;
2860
2861 const is_signed = t.signedness(assign.qt) == .signed;
2862 switch (assign_rhs) {
2863 .div_expr, .mod_expr => if (is_signed) return null,
2864 else => {},
2865 }
2866 const lhs_ptr = assign.qt.isPointer(t.comp);
2867
2868 const bin, const op: ZigTag, const cast: enum { none, shift, usize } = switch (assign_rhs) {
2869 .add_expr => |bin| .{
2870 bin,
2871 if (t.typeHasWrappingOverflow(bin.qt)) .add_wrap_assign else .add_assign,
2872 if (lhs_ptr and t.signedness(bin.rhs.qt(t.tree)) == .signed) .usize else .none,
2873 },
2874 .sub_expr => |bin| .{
2875 bin,
2876 if (t.typeHasWrappingOverflow(bin.qt)) .sub_wrap_assign else .sub_assign,
2877 if (lhs_ptr and t.signedness(bin.rhs.qt(t.tree)) == .signed) .usize else .none,
2878 },
2879 .mul_expr => |bin| .{
2880 bin,
2881 if (t.typeHasWrappingOverflow(bin.qt)) .mul_wrap_assign else .mul_assign,
2882 .none,
2883 },
2884 .mod_expr => |bin| .{ bin, .mod_assign, .none },
2885 .div_expr => |bin| .{ bin, .div_assign, .none },
2886 .shl_expr => |bin| .{ bin, .shl_assign, .shift },
2887 .shr_expr => |bin| .{ bin, .shr_assign, .shift },
2888 .bit_and_expr => |bin| .{ bin, .bit_and_assign, .none },
2889 .bit_xor_expr => |bin| .{ bin, .bit_xor_assign, .none },
2890 .bit_or_expr => |bin| .{ bin, .bit_or_assign, .none },
2891 else => unreachable,
2892 };
2893
2894 const lhs_node = blk: {
2895 const old_dummy = t.compound_assign_dummy;
2896 defer t.compound_assign_dummy = old_dummy;
2897 t.compound_assign_dummy = lhs_dummy_opt orelse try t.transExpr(scope, assign.lhs, .used);
2898
2899 break :blk try t.transExpr(scope, bin.lhs, .used);
2900 };
2901
2902 const rhs_node = try t.transExprCoercing(scope, bin.rhs, .used);
2903 const casted_rhs = switch (cast) {
2904 .none => rhs_node,
2905 .shift => try ZigTag.int_cast.create(t.arena, rhs_node),
2906 .usize => try t.usizeCastForWrappingPtrArithmetic(rhs_node),
2907 };
2908 return try t.createBinOpNode(op, lhs_node, casted_rhs);
2909}
2910
2911fn transIncDecExpr(
2912 t: *Translator,
2913 scope: *Scope,
2914 un: Node.Unary,
2915 position: enum { pre, post },
2916 kind: enum { inc, dec },
2917 used: ResultUsed,
2918) !ZigNode {
2919 const is_wrapping = t.typeHasWrappingOverflow(un.qt);
2920 const op: ZigTag = switch (kind) {
2921 .inc => if (is_wrapping) .add_wrap_assign else .add_assign,
2922 .dec => if (is_wrapping) .sub_wrap_assign else .sub_assign,
2923 };
2924
2925 const one_literal = ZigTag.one_literal.init();
2926 if (used == .unused) {
2927 const operand = try t.transExpr(scope, un.operand, .used);
2928 return try t.createBinOpNode(op, operand, one_literal);
2929 }
2930
2931 var block_scope = try Scope.Block.init(t, scope, true);
2932 defer block_scope.deinit();
2933
2934 const ref = try block_scope.reserveMangledName("ref");
2935 const operand = try t.transExprCoercing(&block_scope.base, un.operand, .used);
2936 const operand_ref = try ZigTag.address_of.create(t.arena, operand);
2937 const ref_decl = try ZigTag.var_simple.create(t.arena, .{ .name = ref, .init = operand_ref });
2938 try block_scope.statements.append(t.gpa, ref_decl);
2939
2940 const ref_ident = try ZigTag.identifier.create(t.arena, ref);
2941 const ref_deref = try ZigTag.deref.create(t.arena, ref_ident);
2942 const effect = try t.createBinOpNode(op, ref_deref, one_literal);
2943
2944 switch (position) {
2945 .pre => {
2946 try block_scope.statements.append(t.gpa, effect);
2947
2948 const break_node = try ZigTag.break_val.create(t.arena, .{
2949 .label = block_scope.label,
2950 .val = ref_deref,
2951 });
2952 try block_scope.statements.append(t.gpa, break_node);
2953 },
2954 .post => {
2955 const tmp = try block_scope.reserveMangledName("tmp");
2956 const tmp_decl = try ZigTag.var_simple.create(t.arena, .{ .name = tmp, .init = ref_deref });
2957 try block_scope.statements.append(t.gpa, tmp_decl);
2958
2959 try block_scope.statements.append(t.gpa, effect);
2960
2961 const tmp_ident = try ZigTag.identifier.create(t.arena, tmp);
2962 const break_node = try ZigTag.break_val.create(t.arena, .{
2963 .label = block_scope.label,
2964 .val = tmp_ident,
2965 });
2966 try block_scope.statements.append(t.gpa, break_node);
2967 },
2968 }
2969
2970 return try block_scope.complete();
2971}
2972
2973fn transPtrDiffExpr(t: *Translator, scope: *Scope, bin: Node.Binary) TransError!ZigNode {
2974 const lhs_uncasted = try t.transExpr(scope, bin.lhs, .used);
2975 const rhs_uncasted = try t.transExpr(scope, bin.rhs, .used);
2976
2977 const lhs = try ZigTag.int_from_ptr.create(t.arena, lhs_uncasted);
2978 const rhs = try ZigTag.int_from_ptr.create(t.arena, rhs_uncasted);
2979
2980 const sub_res = try t.createBinOpNode(.sub_wrap, lhs, rhs);
2981
2982 // @divExact(@as(<platform-ptrdiff_t>, @bitCast(@intFromPtr(lhs)) -% @intFromPtr(rhs)), @sizeOf(<lhs target type>))
2983 const ptrdiff_type = try t.transTypeIntWidthOf(bin.qt, true);
2984
2985 const bitcast = try ZigTag.as.create(t.arena, .{
2986 .lhs = ptrdiff_type,
2987 .rhs = try ZigTag.bit_cast.create(t.arena, sub_res),
2988 });
2989
2990 // C standard requires that pointer subtraction operands are of the same type,
2991 // otherwise it is undefined behavior. So we can assume the left and right
2992 // sides are the same Type and arbitrarily choose left.
2993 const lhs_ty = try t.transType(scope, bin.lhs.qt(t.tree), bin.lhs.tok(t.tree));
2994 const c_pointer = t.getContainer(lhs_ty).?;
2995
2996 if (c_pointer.castTag(.c_pointer)) |c_pointer_payload| {
2997 const sizeof = try ZigTag.sizeof.create(t.arena, c_pointer_payload.data.elem_type);
2998 return ZigTag.div_exact.create(t.arena, .{
2999 .lhs = bitcast,
3000 .rhs = sizeof,
3001 });
3002 } else {
3003 // This is an opaque/incomplete type. This subtraction exhibits Undefined Behavior by the C99 spec.
3004 // However, allowing subtraction on `void *` and function pointers is a commonly used extension.
3005 // So, just return the value in byte units, mirroring the behavior of this language extension as implemented by GCC and Clang.
3006 return bitcast;
3007 }
3008}
3009
3010/// Translate an arithmetic expression with a pointer operand and a signed-integer operand.
3011/// Zig requires a usize argument for pointer arithmetic, so we intCast to isize and then
3012/// bitcast to usize; pointer wraparound makes the math work.
3013/// Zig pointer addition is not commutative (unlike C); the pointer operand needs to be on the left.
3014/// The + operator in C is not a sequence point so it should be safe to switch the order if necessary.
3015fn transPointerArithmeticSignedOp(t: *Translator, scope: *Scope, bin: Node.Binary, op_id: ZigTag) TransError!ZigNode {
3016 std.debug.assert(op_id == .add or op_id == .sub);
3017
3018 const lhs_qt = bin.lhs.qt(t.tree);
3019 const swap_operands = op_id == .add and t.signedness(lhs_qt) == .signed;
3020
3021 const swizzled_lhs = if (swap_operands) bin.rhs else bin.lhs;
3022 const swizzled_rhs = if (swap_operands) bin.lhs else bin.rhs;
3023
3024 const lhs_node = try t.transExpr(scope, swizzled_lhs, .used);
3025 const rhs_node = try t.transExpr(scope, swizzled_rhs, .used);
3026
3027 const bitcast_node = try t.usizeCastForWrappingPtrArithmetic(rhs_node);
3028
3029 return t.createBinOpNode(op_id, lhs_node, bitcast_node);
3030}
3031
3032fn transMemberAccess(
3033 t: *Translator,
3034 scope: *Scope,
3035 kind: enum { normal, ptr },
3036 member_access: Node.MemberAccess,
3037 opt_base: ?ZigNode,
3038) TransError!ZigNode {
3039 const base_info = switch (kind) {
3040 .normal => member_access.base.qt(t.tree),
3041 .ptr => member_access.base.qt(t.tree).childType(t.comp),
3042 };
3043 const record = base_info.getRecord(t.comp).?;
3044 const field = record.fields[member_access.member_index];
3045 const field_name = if (field.name_tok == 0) t.anonymous_record_field_names.get(.{
3046 .parent = base_info.base(t.comp).qt,
3047 .field = field.qt,
3048 }).? else field.name.lookup(t.comp);
3049 const base_node = opt_base orelse try t.transExpr(scope, member_access.base, .used);
3050 const lhs = switch (kind) {
3051 .normal => base_node,
3052 .ptr => try ZigTag.deref.create(t.arena, base_node),
3053 };
3054 const field_access = try ZigTag.field_access.create(t.arena, .{
3055 .lhs = lhs,
3056 .field_name = field_name,
3057 });
3058
3059 // Flexible array members are translated as member functions.
3060 if (member_access.member_index == record.fields.len - 1 or base_info.base(t.comp).type == .@"union") {
3061 if (field.qt.get(t.comp, .array)) |array_ty| {
3062 if (array_ty.len == .incomplete or (array_ty.len == .fixed and array_ty.len.fixed == 0)) {
3063 return ZigTag.call.create(t.arena, .{ .lhs = field_access, .args = &.{} });
3064 }
3065 }
3066 }
3067
3068 return field_access;
3069}
3070
3071fn transArrayAccess(t: *Translator, scope: *Scope, array_access: Node.ArrayAccess, opt_base: ?ZigNode) TransError!ZigNode {
3072 // Unwrap the base statement if it's an array decayed to a bare pointer type
3073 // so that we index the array itself
3074 const base = base: {
3075 const base = array_access.base.get(t.tree);
3076 if (base != .cast) break :base array_access.base;
3077 if (base.cast.kind != .array_to_pointer) break :base array_access.base;
3078 break :base base.cast.operand;
3079 };
3080
3081 const base_node = opt_base orelse try t.transExpr(scope, base, .used);
3082 const index = index: {
3083 const index = try t.transExpr(scope, array_access.index, .used);
3084 const index_qt = array_access.index.qt(t.tree);
3085 const maybe_bigger_than_usize = switch (index_qt.base(t.comp).type) {
3086 .bool => {
3087 break :index try ZigTag.int_from_bool.create(t.arena, index);
3088 },
3089 .int => |int| switch (int) {
3090 .long_long, .ulong_long, .int128, .uint128 => true,
3091 else => false,
3092 },
3093 .bit_int => |bit_int| bit_int.bits > t.comp.target.ptrBitWidth(),
3094 else => unreachable,
3095 };
3096
3097 const is_nonnegative_int_literal = if (t.tree.value_map.get(array_access.index)) |val|
3098 val.compare(.gte, .zero, t.comp)
3099 else
3100 false;
3101 const is_signed = t.signedness(index_qt) == .signed;
3102
3103 if (is_signed and !is_nonnegative_int_literal) {
3104 // First cast to `isize` to get proper sign extension and
3105 // then @bitCast to `usize` to satisfy the compiler.
3106 const index_isize = try ZigTag.as.create(t.arena, .{
3107 .lhs = try ZigTag.type.create(t.arena, "isize"),
3108 .rhs = try ZigTag.int_cast.create(t.arena, index),
3109 });
3110 break :index try ZigTag.bit_cast.create(t.arena, index_isize);
3111 }
3112
3113 if (maybe_bigger_than_usize) {
3114 break :index try ZigTag.int_cast.create(t.arena, index);
3115 }
3116 break :index index;
3117 };
3118
3119 return ZigTag.array_access.create(t.arena, .{
3120 .lhs = base_node,
3121 .rhs = index,
3122 });
3123}
3124
3125fn transOffsetof(t: *Translator, scope: *Scope, arg: Node.Index) TransError!ZigNode {
3126 // Translate __builtin_offsetof(T, designator) as
3127 // @intFromPtr(&(@as(*allowzero T, @ptrFromInt(0)).designator))
3128 const member = try t.transMemberDesignator(scope, arg);
3129 const address = try ZigTag.address_of.create(t.arena, member);
3130 return ZigTag.int_from_ptr.create(t.arena, address);
3131}
3132
3133fn transMemberDesignator(t: *Translator, scope: *Scope, arg: Node.Index) TransError!ZigNode {
3134 switch (arg.get(t.tree)) {
3135 .default_init_expr => |default| {
3136 const elem_node = try t.transType(scope, default.qt, default.last_tok);
3137 const ptr_ty = try ZigTag.single_pointer.create(t.arena, .{
3138 .elem_type = elem_node,
3139 .is_allowzero = true,
3140 .is_const = false,
3141 .is_volatile = false,
3142 });
3143 const zero = try ZigTag.ptr_from_int.create(t.arena, ZigTag.zero_literal.init());
3144 return ZigTag.as.create(t.arena, .{ .lhs = ptr_ty, .rhs = zero });
3145 },
3146 .array_access_expr => |access| {
3147 const base = try t.transMemberDesignator(scope, access.base);
3148 return t.transArrayAccess(scope, access, base);
3149 },
3150 .member_access_expr => |access| {
3151 const base = try t.transMemberDesignator(scope, access.base);
3152 return t.transMemberAccess(scope, .normal, access, base);
3153 },
3154 .cast => |cast| {
3155 assert(cast.kind == .array_to_pointer);
3156 return t.transMemberDesignator(scope, cast.operand);
3157 },
3158 else => unreachable,
3159 }
3160}
3161
3162fn transBuiltinCall(
3163 t: *Translator,
3164 scope: *Scope,
3165 call: Node.BuiltinCall,
3166 used: ResultUsed,
3167) TransError!ZigNode {
3168 const builtin_name = t.tree.tokSlice(call.builtin_tok);
3169 if (std.mem.eql(u8, builtin_name, "__builtin_offsetof")) {
3170 const res = try t.transOffsetof(scope, call.args[0]);
3171 return t.maybeSuppressResult(used, res);
3172 }
3173
3174 const builtin = builtins.map.get(builtin_name) orelse
3175 return t.fail(error.UnsupportedTranslation, call.builtin_tok, "TODO implement function '{s}' in std.zig.c_builtins", .{builtin_name});
3176
3177 if (builtin.tag) |tag| switch (tag) {
3178 .byte_swap, .ceil, .cos, .sin, .exp, .exp2, .exp10, .abs, .log, .log2, .log10, .round, .sqrt, .trunc, .floor => {
3179 assert(call.args.len == 1);
3180 const arg = try t.transExprCoercing(scope, call.args[0], .used);
3181 const arg_ty = try t.transType(scope, call.args[0].qt(t.tree), call.args[0].tok(t.tree));
3182 const coerced = try ZigTag.as.create(t.arena, .{ .lhs = arg_ty, .rhs = arg });
3183
3184 const ptr = try t.arena.create(ast.Payload.UnOp);
3185 ptr.* = .{ .base = .{ .tag = tag }, .data = coerced };
3186 return t.maybeSuppressResult(used, ZigNode.initPayload(&ptr.base));
3187 },
3188 .@"unreachable" => return ZigTag.@"unreachable".init(),
3189 else => unreachable,
3190 };
3191
3192 const arg_nodes = try t.arena.alloc(ZigNode, call.args.len);
3193 for (call.args, arg_nodes) |c_arg, *zig_arg| {
3194 zig_arg.* = try t.transExprCoercing(scope, c_arg, .used);
3195 }
3196
3197 const builtin_identifier = try ZigTag.identifier.create(t.arena, "__builtin");
3198 const field_access = try ZigTag.field_access.create(t.arena, .{
3199 .lhs = builtin_identifier,
3200 .field_name = builtin.name,
3201 });
3202
3203 const res = try ZigTag.call.create(t.arena, .{
3204 .lhs = field_access,
3205 .args = arg_nodes,
3206 });
3207 if (call.qt.is(t.comp, .void)) return res;
3208 return t.maybeSuppressResult(used, res);
3209}
3210
3211fn transCall(
3212 t: *Translator,
3213 scope: *Scope,
3214 call: Node.Call,
3215 used: ResultUsed,
3216) TransError!ZigNode {
3217 const raw_fn_expr = try t.transExpr(scope, call.callee, .used);
3218 const fn_expr = blk: {
3219 loop: switch (call.callee.get(t.tree)) {
3220 .paren_expr => |paren_expr| {
3221 continue :loop paren_expr.operand.get(t.tree);
3222 },
3223 .decl_ref_expr => |decl_ref| {
3224 if (decl_ref.qt.is(t.comp, .func)) break :blk raw_fn_expr;
3225 },
3226 .cast => |cast| {
3227 if (cast.kind == .function_to_pointer) {
3228 continue :loop cast.operand.get(t.tree);
3229 }
3230 },
3231 .deref_expr, .addr_of_expr => |un| {
3232 continue :loop un.operand.get(t.tree);
3233 },
3234 .generic_expr => |generic| {
3235 continue :loop generic.chosen.get(t.tree);
3236 },
3237 .generic_association_expr => |generic| {
3238 continue :loop generic.expr.get(t.tree);
3239 },
3240 .generic_default_expr => |generic| {
3241 continue :loop generic.expr.get(t.tree);
3242 },
3243 else => {},
3244 }
3245 break :blk try ZigTag.unwrap.create(t.arena, raw_fn_expr);
3246 };
3247
3248 const callee_qt = call.callee.qt(t.tree);
3249 const maybe_ptr_ty = callee_qt.get(t.comp, .pointer);
3250 const func_qt = if (maybe_ptr_ty) |ptr| ptr.child else callee_qt;
3251 const func_ty = func_qt.get(t.comp, .func).?;
3252
3253 const arg_nodes = try t.arena.alloc(ZigNode, call.args.len);
3254 for (call.args, arg_nodes, 0..) |c_arg, *zig_arg, i| {
3255 if (i < func_ty.params.len) {
3256 zig_arg.* = try t.transExprCoercing(scope, c_arg, .used);
3257
3258 if (zig_arg.isBoolRes() and !func_ty.params[i].qt.is(t.comp, .bool)) {
3259 // In C the result type of a boolean expression is int. If this result is passed as
3260 // an argument to a function whose parameter is also int, there is no cast. Therefore
3261 // in Zig we'll need to cast it from bool to u1 (which will safely coerce to c_int).
3262 zig_arg.* = try ZigTag.int_from_bool.create(t.arena, zig_arg.*);
3263 }
3264 } else {
3265 zig_arg.* = try t.transExpr(scope, c_arg, .used);
3266
3267 if (zig_arg.isBoolRes()) {
3268 // Same as above but now we don't have a result type.
3269 const u1_node = try ZigTag.int_from_bool.create(t.arena, zig_arg.*);
3270 const c_int_node = try ZigTag.type.create(t.arena, "c_int");
3271 zig_arg.* = try ZigTag.as.create(t.arena, .{ .lhs = c_int_node, .rhs = u1_node });
3272 }
3273 }
3274 }
3275
3276 const res = try ZigTag.call.create(t.arena, .{
3277 .lhs = fn_expr,
3278 .args = arg_nodes,
3279 });
3280 if (call.qt.is(t.comp, .void)) return res;
3281 return t.maybeSuppressResult(used, res);
3282}
3283
3284const SuppressCast = enum { with_as, no_as };
3285
3286fn transIntLiteral(
3287 t: *Translator,
3288 scope: *Scope,
3289 literal_index: Node.Index,
3290 used: ResultUsed,
3291 suppress_as: SuppressCast,
3292) TransError!ZigNode {
3293 const val = t.tree.value_map.get(literal_index).?;
3294 const int_lit_node = try t.createIntNode(val);
3295 if (suppress_as == .no_as) {
3296 return t.maybeSuppressResult(used, int_lit_node);
3297 }
3298
3299 // Integer literals in C have types, and this can matter for several reasons.
3300 // For example, this is valid C:
3301 // unsigned char y = 256;
3302 // How this gets evaluated is the 256 is an integer, which gets truncated to signed char, then bit-casted
3303 // to unsigned char, resulting in 0. In order for this to work, we have to emit this zig code:
3304 // var y = @as(u8, @bitCast(@as(i8, @truncate(@as(c_int, 256)))));
3305
3306 // @as(T, x)
3307 const ty_node = try t.transType(scope, literal_index.qt(t.tree), literal_index.tok(t.tree));
3308 const as = try ZigTag.as.create(t.arena, .{ .lhs = ty_node, .rhs = int_lit_node });
3309 return t.maybeSuppressResult(used, as);
3310}
3311
3312fn transCharLiteral(
3313 t: *Translator,
3314 scope: *Scope,
3315 literal_index: Node.Index,
3316 used: ResultUsed,
3317 suppress_as: SuppressCast,
3318) TransError!ZigNode {
3319 const val = t.tree.value_map.get(literal_index).?;
3320 const char_literal = literal_index.get(t.tree).char_literal;
3321 const narrow = char_literal.kind == .ascii or char_literal.kind == .utf8;
3322
3323 // C has a somewhat obscure feature called multi-character character constant
3324 // e.g. 'abcd'
3325 const int_value = val.toInt(u32, t.comp).?;
3326 const int_lit_node = if (char_literal.kind == .ascii and int_value > 255)
3327 try t.createNumberNode(int_value, .int)
3328 else
3329 try t.createCharLiteralNode(narrow, int_value);
3330
3331 if (suppress_as == .no_as) {
3332 return t.maybeSuppressResult(used, int_lit_node);
3333 }
3334
3335 // See comment in `transIntLiteral` for why this code is here.
3336 // @as(T, x)
3337 const as_node = try ZigTag.as.create(t.arena, .{
3338 .lhs = try t.transType(scope, char_literal.qt, char_literal.literal_tok),
3339 .rhs = int_lit_node,
3340 });
3341 return t.maybeSuppressResult(used, as_node);
3342}
3343
3344fn transFloatLiteral(
3345 t: *Translator,
3346 scope: *Scope,
3347 literal_index: Node.Index,
3348 used: ResultUsed,
3349 suppress_as: SuppressCast,
3350) TransError!ZigNode {
3351 const val = t.tree.value_map.get(literal_index).?;
3352 const float_literal = literal_index.get(t.tree).float_literal;
3353
3354 var allocating: std.Io.Writer.Allocating = .init(t.gpa);
3355 defer allocating.deinit();
3356 _ = val.print(float_literal.qt, t.comp, &allocating.writer) catch return error.OutOfMemory;
3357
3358 const float_lit_node = try ZigTag.float_literal.create(t.arena, try t.arena.dupe(u8, allocating.getWritten()));
3359 if (suppress_as == .no_as) {
3360 return t.maybeSuppressResult(used, float_lit_node);
3361 }
3362
3363 const as_node = try ZigTag.as.create(t.arena, .{
3364 .lhs = try t.transType(scope, float_literal.qt, float_literal.literal_tok),
3365 .rhs = float_lit_node,
3366 });
3367 return t.maybeSuppressResult(used, as_node);
3368}
3369
3370fn transStringLiteral(
3371 t: *Translator,
3372 scope: *Scope,
3373 expr: Node.Index,
3374 literal: Node.CharLiteral,
3375) TransError!ZigNode {
3376 switch (literal.kind) {
3377 .ascii, .utf8 => return t.transNarrowStringLiteral(expr, literal),
3378 .utf16, .utf32, .wide => {
3379 const name = try std.fmt.allocPrint(t.arena, "{s}_string_{d}", .{ @tagName(literal.kind), t.getMangle() });
3380
3381 const array_type = try t.transTypeInit(scope, literal.qt, expr, literal.literal_tok);
3382 const lit_array = try t.transStringLiteralInitializer(expr, literal, array_type);
3383 const decl = try ZigTag.var_simple.create(t.arena, .{ .name = name, .init = lit_array });
3384 try scope.appendNode(decl);
3385 return ZigTag.identifier.create(t.arena, name);
3386 },
3387 }
3388}
3389
3390fn transNarrowStringLiteral(
3391 t: *Translator,
3392 expr: Node.Index,
3393 literal: Node.CharLiteral,
3394) TransError!ZigNode {
3395 const val = t.tree.value_map.get(expr).?;
3396
3397 const bytes = t.comp.interner.get(val.ref()).bytes;
3398 var allocating: std.Io.Writer.Allocating = try .initCapacity(t.gpa, bytes.len);
3399 defer allocating.deinit();
3400
3401 aro.Value.printString(bytes, literal.qt, t.comp, &allocating.writer) catch return error.OutOfMemory;
3402
3403 return ZigTag.string_literal.create(t.arena, try t.arena.dupe(u8, allocating.getWritten()));
3404}
3405
3406/// Translate a string literal that is initializing an array. In general narrow string
3407/// literals become `"<string>".*` or `"<string>"[0..<size>].*` if they need truncation.
3408/// Wide string literals become an array of integers. zero-fillers pad out the array to
3409/// the appropriate length, if necessary.
3410fn transStringLiteralInitializer(
3411 t: *Translator,
3412 expr: Node.Index,
3413 literal: Node.CharLiteral,
3414 array_type: ZigNode,
3415) TransError!ZigNode {
3416 assert(array_type.tag() == .array_type or array_type.tag() == .null_sentinel_array_type);
3417
3418 const is_narrow = literal.kind == .ascii or literal.kind == .utf8;
3419
3420 // The length of the string literal excluding the sentinel.
3421 const str_length = literal.qt.arrayLen(t.comp).? - 1;
3422
3423 const payload = (array_type.castTag(.array_type) orelse array_type.castTag(.null_sentinel_array_type).?).data;
3424 const array_size = payload.len;
3425 const elem_type = payload.elem_type;
3426
3427 if (array_size == 0) return ZigTag.empty_array.create(t.arena, array_type);
3428
3429 const num_inits = @min(str_length, array_size);
3430 if (num_inits == 0) {
3431 return ZigTag.array_filler.create(t.arena, .{
3432 .type = elem_type,
3433 .filler = ZigTag.zero_literal.init(),
3434 .count = array_size,
3435 });
3436 }
3437
3438 const init_node = if (is_narrow) blk: {
3439 // "string literal".* or string literal"[0..num_inits].*
3440 var str = try t.transNarrowStringLiteral(expr, literal);
3441 if (str_length != array_size) str = try ZigTag.string_slice.create(t.arena, .{ .string = str, .end = num_inits });
3442 break :blk try ZigTag.deref.create(t.arena, str);
3443 } else blk: {
3444 const size = literal.qt.childType(t.comp).sizeof(t.comp);
3445
3446 const val = t.tree.value_map.get(expr).?;
3447 const bytes = t.comp.interner.get(val.ref()).bytes;
3448
3449 const init_list = try t.arena.alloc(ZigNode, @intCast(num_inits));
3450 for (init_list, 0..) |*item, i| {
3451 const codepoint = switch (size) {
3452 2 => @as(*const u16, @alignCast(@ptrCast(bytes.ptr + i * 2))).*,
3453 4 => @as(*const u32, @alignCast(@ptrCast(bytes.ptr + i * 4))).*,
3454 else => unreachable,
3455 };
3456 item.* = try t.createCharLiteralNode(false, codepoint);
3457 }
3458 const init_args: ast.Payload.Array.ArrayTypeInfo = .{ .len = num_inits, .elem_type = elem_type };
3459 const init_array_type = if (array_type.tag() == .array_type)
3460 try ZigTag.array_type.create(t.arena, init_args)
3461 else
3462 try ZigTag.null_sentinel_array_type.create(t.arena, init_args);
3463 break :blk try ZigTag.array_init.create(t.arena, .{
3464 .cond = init_array_type,
3465 .cases = init_list,
3466 });
3467 };
3468
3469 if (num_inits == array_size) return init_node;
3470 assert(array_size > str_length); // If array_size <= str_length, `num_inits == array_size` and we've already returned.
3471
3472 const filler_node = try ZigTag.array_filler.create(t.arena, .{
3473 .type = elem_type,
3474 .filler = ZigTag.zero_literal.init(),
3475 .count = array_size - str_length,
3476 });
3477 return ZigTag.array_cat.create(t.arena, .{ .lhs = init_node, .rhs = filler_node });
3478}
3479
3480fn transCompoundLiteral(
3481 t: *Translator,
3482 scope: *Scope,
3483 literal: Node.CompoundLiteral,
3484 used: ResultUsed,
3485) TransError!ZigNode {
3486 if (used == .unused) {
3487 return t.transExpr(scope, literal.initializer, .unused);
3488 }
3489
3490 // TODO taking a reference to a compound literal should result in a mutable
3491 // pointer (unless the literal is const).
3492
3493 const initializer = try t.transExprCoercing(scope, literal.initializer, .used);
3494 const ty = try t.transType(scope, literal.qt, literal.l_paren_tok);
3495 if (!literal.thread_local and literal.storage_class != .static) {
3496 // In the simple case a compound literal can be translated
3497 // simply as `@as(type, initializer)`.
3498 return ZigTag.as.create(t.arena, .{ .lhs = ty, .rhs = initializer });
3499 }
3500
3501 // Otherwise static or thread local compound literals are translated as
3502 // a reference to a variable wrapped in a struct.
3503
3504 var block_scope = try Scope.Block.init(t, scope, true);
3505 defer block_scope.deinit();
3506
3507 const tmp = try block_scope.reserveMangledName("tmp");
3508 const wrapped_name = "compound_literal";
3509
3510 // const tmp = struct { var compound_literal = initializer };
3511 const temp_decl = try ZigTag.var_decl.create(t.arena, .{
3512 .is_pub = false,
3513 .is_const = literal.qt.@"const",
3514 .is_extern = false,
3515 .is_export = false,
3516 .is_threadlocal = literal.thread_local,
3517 .linksection_string = null,
3518 .alignment = null,
3519 .name = wrapped_name,
3520 .type = ty,
3521 .init = initializer,
3522 });
3523 const wrapped = try ZigTag.wrapped_local.create(t.arena, .{ .name = tmp, .init = temp_decl });
3524 try block_scope.statements.append(t.gpa, wrapped);
3525
3526 // break :blk tmp.compound_literal
3527 const static_tmp_ident = try ZigTag.identifier.create(t.arena, tmp);
3528 const field_access = try ZigTag.field_access.create(t.arena, .{
3529 .lhs = static_tmp_ident,
3530 .field_name = wrapped_name,
3531 });
3532 const break_node = try ZigTag.break_val.create(t.arena, .{
3533 .label = block_scope.label,
3534 .val = field_access,
3535 });
3536 try block_scope.statements.append(t.gpa, break_node);
3537
3538 return block_scope.complete();
3539}
3540
3541fn transDefaultInit(
3542 t: *Translator,
3543 scope: *Scope,
3544 default_init: Node.DefaultInit,
3545 used: ResultUsed,
3546 suppress_as: SuppressCast,
3547) TransError!ZigNode {
3548 assert(used == .used);
3549 const type_node = try t.transType(scope, default_init.qt, default_init.last_tok);
3550 return try t.createZeroValueNode(default_init.qt, type_node, suppress_as);
3551}
3552
3553fn transArrayInit(
3554 t: *Translator,
3555 scope: *Scope,
3556 array_init: Node.ContainerInit,
3557 used: ResultUsed,
3558) TransError!ZigNode {
3559 assert(used == .used);
3560 const array_item_qt = array_init.container_qt.childType(t.comp);
3561 const array_item_type = try t.transType(scope, array_item_qt, array_init.l_brace_tok);
3562 var maybe_lhs: ?ZigNode = null;
3563 var val_list: std.ArrayListUnmanaged(ZigNode) = .empty;
3564 defer val_list.deinit(t.gpa);
3565 var i: usize = 0;
3566 while (i < array_init.items.len) {
3567 const rhs = switch (array_init.items[i].get(t.tree)) {
3568 .array_filler_expr => |array_filler| blk: {
3569 const node = try ZigTag.array_filler.create(t.arena, .{
3570 .type = array_item_type,
3571 .filler = try t.createZeroValueNode(array_item_qt, array_item_type, .no_as),
3572 .count = @intCast(array_filler.count),
3573 });
3574 i += 1;
3575 break :blk node;
3576 },
3577 else => blk: {
3578 defer val_list.clearRetainingCapacity();
3579 while (i < array_init.items.len) : (i += 1) {
3580 if (array_init.items[i].get(t.tree) == .array_filler_expr) break;
3581 const expr = try t.transExprCoercing(scope, array_init.items[i], .used);
3582 try val_list.append(t.gpa, expr);
3583 }
3584 const array_type = try ZigTag.array_type.create(t.arena, .{
3585 .elem_type = array_item_type,
3586 .len = val_list.items.len,
3587 });
3588 const array_init_node = try ZigTag.array_init.create(t.arena, .{
3589 .cond = array_type,
3590 .cases = try t.arena.dupe(ZigNode, val_list.items),
3591 });
3592 break :blk array_init_node;
3593 },
3594 };
3595 maybe_lhs = if (maybe_lhs) |lhs| blk: {
3596 const cat = try ZigTag.array_cat.create(t.arena, .{
3597 .lhs = lhs,
3598 .rhs = rhs,
3599 });
3600 break :blk cat;
3601 } else rhs;
3602 }
3603 return maybe_lhs orelse try ZigTag.container_init_dot.create(t.arena, &.{});
3604}
3605
3606fn transUnionInit(
3607 t: *Translator,
3608 scope: *Scope,
3609 union_init: Node.UnionInit,
3610 used: ResultUsed,
3611) TransError!ZigNode {
3612 assert(used == .used);
3613 const init_expr = union_init.initializer orelse
3614 return ZigTag.undefined_literal.init();
3615
3616 if (init_expr.get(t.tree) == .default_init_expr) {
3617 return try t.transExpr(scope, init_expr, used);
3618 }
3619
3620 const union_type = try t.transType(scope, union_init.union_qt, union_init.l_brace_tok);
3621
3622 const union_base = union_init.union_qt.base(t.comp);
3623 const field = union_base.type.@"union".fields[union_init.field_index];
3624 const field_name = if (field.name_tok == 0) t.anonymous_record_field_names.get(.{
3625 .parent = union_base.qt,
3626 .field = field.qt,
3627 }).? else field.name.lookup(t.comp);
3628
3629 const field_init = try t.arena.create(ast.Payload.ContainerInit.Initializer);
3630 field_init.* = .{
3631 .name = field_name,
3632 .value = try t.transExprCoercing(scope, init_expr, .used),
3633 };
3634 const container_init = try ZigTag.container_init.create(t.arena, .{
3635 .lhs = union_type,
3636 .inits = field_init[0..1],
3637 });
3638 return container_init;
3639}
3640
3641fn transStructInit(
3642 t: *Translator,
3643 scope: *Scope,
3644 struct_init: Node.ContainerInit,
3645 used: ResultUsed,
3646) TransError!ZigNode {
3647 assert(used == .used);
3648 const struct_type = try t.transType(scope, struct_init.container_qt, struct_init.l_brace_tok);
3649 const field_inits = try t.arena.alloc(ast.Payload.ContainerInit.Initializer, struct_init.items.len);
3650
3651 const struct_base = struct_init.container_qt.base(t.comp);
3652 for (
3653 field_inits,
3654 struct_init.items,
3655 struct_base.type.@"struct".fields,
3656 ) |*init, field_expr, field| {
3657 const field_name = if (field.name_tok == 0) t.anonymous_record_field_names.get(.{
3658 .parent = struct_base.qt,
3659 .field = field.qt,
3660 }).? else field.name.lookup(t.comp);
3661 init.* = .{
3662 .name = field_name,
3663 .value = try t.transExprCoercing(scope, field_expr, .used),
3664 };
3665 }
3666
3667 const container_init = try ZigTag.container_init.create(t.arena, .{
3668 .lhs = struct_type,
3669 .inits = field_inits,
3670 });
3671 return container_init;
3672}
3673
3674fn transTypeInfo(
3675 t: *Translator,
3676 scope: *Scope,
3677 op: ZigTag,
3678 typeinfo: Node.TypeInfo,
3679) TransError!ZigNode {
3680 const operand = operand: {
3681 if (typeinfo.expr) |expr| {
3682 const operand = try t.transExpr(scope, expr, .used);
3683 break :operand try ZigTag.typeof.create(t.arena, operand);
3684 }
3685 break :operand try t.transType(scope, typeinfo.operand_qt, typeinfo.op_tok);
3686 };
3687
3688 const payload = try t.arena.create(ast.Payload.UnOp);
3689 payload.* = .{
3690 .base = .{ .tag = op },
3691 .data = operand,
3692 };
3693 return ZigNode.initPayload(&payload.base);
3694}
3695
3696fn transStmtExpr(
3697 t: *Translator,
3698 scope: *Scope,
3699 stmt_expr: Node.Unary,
3700 used: ResultUsed,
3701) TransError!ZigNode {
3702 const compound_stmt = stmt_expr.operand.get(t.tree).compound_stmt;
3703 if (used == .unused) {
3704 return t.transCompoundStmt(scope, compound_stmt);
3705 }
3706 var block_scope = try Scope.Block.init(t, scope, true);
3707 defer block_scope.deinit();
3708
3709 for (compound_stmt.body[0 .. compound_stmt.body.len - 1]) |stmt| {
3710 const result = try t.transStmt(&block_scope.base, stmt);
3711 switch (result.tag()) {
3712 .declaration, .empty_block => {},
3713 else => try block_scope.statements.append(t.gpa, result),
3714 }
3715 }
3716
3717 const last_result = try t.transExpr(&block_scope.base, compound_stmt.body[compound_stmt.body.len - 1], .used);
3718 switch (last_result.tag()) {
3719 .declaration, .empty_block => {},
3720 else => {
3721 const break_node = try ZigTag.break_val.create(t.arena, .{
3722 .label = block_scope.label,
3723 .val = last_result,
3724 });
3725 try block_scope.statements.append(t.gpa, break_node);
3726 },
3727 }
3728 return block_scope.complete();
3729}
3730
3731fn transConvertvectorExpr(
3732 t: *Translator,
3733 scope: *Scope,
3734 convertvector: Node.Convertvector,
3735) TransError!ZigNode {
3736 var block_scope = try Scope.Block.init(t, scope, true);
3737 defer block_scope.deinit();
3738
3739 const src_expr_node = try t.transExpr(&block_scope.base, convertvector.operand, .used);
3740 const tmp = try block_scope.reserveMangledName("tmp");
3741 const tmp_decl = try ZigTag.var_simple.create(t.arena, .{ .name = tmp, .init = src_expr_node });
3742 try block_scope.statements.append(t.gpa, tmp_decl);
3743 const tmp_ident = try ZigTag.identifier.create(t.arena, tmp);
3744
3745 const dest_type_node = try t.transType(&block_scope.base, convertvector.dest_qt, convertvector.builtin_tok);
3746 const dest_vec_ty = convertvector.dest_qt.get(t.comp, .vector).?;
3747 const src_vec_ty = convertvector.operand.qt(t.tree).get(t.comp, .vector).?;
3748
3749 const src_elem_sk = src_vec_ty.elem.scalarKind(t.comp);
3750 const dest_elem_sk = convertvector.dest_qt.childType(t.comp).scalarKind(t.comp);
3751
3752 const items = try t.arena.alloc(ZigNode, dest_vec_ty.len);
3753 for (items, 0..dest_vec_ty.len) |*item, i| {
3754 const value = try ZigTag.array_access.create(t.arena, .{
3755 .lhs = tmp_ident,
3756 .rhs = try t.createNumberNode(i, .int),
3757 });
3758
3759 if (src_elem_sk == .float and dest_elem_sk == .float) {
3760 item.* = try ZigTag.float_cast.create(t.arena, value);
3761 } else if (src_elem_sk == .float) {
3762 item.* = try ZigTag.int_from_float.create(t.arena, value);
3763 } else if (dest_elem_sk == .float) {
3764 item.* = try ZigTag.float_from_int.create(t.arena, value);
3765 } else {
3766 item.* = try t.transIntCast(value, src_vec_ty.elem, dest_vec_ty.elem);
3767 }
3768 }
3769
3770 const vec_init = try ZigTag.array_init.create(t.arena, .{
3771 .cond = dest_type_node,
3772 .cases = items,
3773 });
3774 const break_node = try ZigTag.break_val.create(t.arena, .{
3775 .label = block_scope.label,
3776 .val = vec_init,
3777 });
3778 try block_scope.statements.append(t.gpa, break_node);
3779
3780 return block_scope.complete();
3781}
3782
3783fn transShufflevectorExpr(
3784 t: *Translator,
3785 scope: *Scope,
3786 shufflevector: Node.Shufflevector,
3787) TransError!ZigNode {
3788 if (shufflevector.indexes.len == 0) {
3789 return t.fail(error.UnsupportedTranslation, shufflevector.builtin_tok, "@shuffle needs at least 1 index", .{});
3790 }
3791
3792 const a = try t.transExpr(scope, shufflevector.lhs, .used);
3793 const b = try t.transExpr(scope, shufflevector.rhs, .used);
3794
3795 // First two arguments to __builtin_shufflevector must be the same type
3796 const vector_child_type = try t.vectorTypeInfo(a, "child");
3797 const vector_len = try t.vectorTypeInfo(a, "len");
3798 const shuffle_mask = blk: {
3799 const mask_len = shufflevector.indexes.len;
3800
3801 const mask_type = try ZigTag.vector.create(t.arena, .{
3802 .lhs = try t.createNumberNode(mask_len, .int),
3803 .rhs = try ZigTag.type.create(t.arena, "i32"),
3804 });
3805
3806 const init_list = try t.arena.alloc(ZigNode, mask_len);
3807 for (init_list, shufflevector.indexes) |*init, index| {
3808 const index_expr = try t.transExprCoercing(scope, index, .used);
3809 const converted_index = try t.createHelperCallNode(.shuffleVectorIndex, &.{ index_expr, vector_len });
3810 init.* = converted_index;
3811 }
3812
3813 break :blk try ZigTag.array_init.create(t.arena, .{
3814 .cond = mask_type,
3815 .cases = init_list,
3816 });
3817 };
3818
3819 return ZigTag.shuffle.create(t.arena, .{
3820 .element_type = vector_child_type,
3821 .a = a,
3822 .b = b,
3823 .mask_vector = shuffle_mask,
3824 });
3825}
3826
3827// =====================
3828// Node creation helpers
3829// =====================
3830
3831fn createZeroValueNode(
3832 t: *Translator,
3833 qt: QualType,
3834 type_node: ZigNode,
3835 suppress_as: SuppressCast,
3836) !ZigNode {
3837 switch (qt.base(t.comp).type) {
3838 .bool => return ZigTag.false_literal.init(),
3839 .int, .bit_int, .float => {
3840 const zero_literal = ZigTag.zero_literal.init();
3841 return switch (suppress_as) {
3842 .with_as => try t.createBinOpNode(.as, type_node, zero_literal),
3843 .no_as => zero_literal,
3844 };
3845 },
3846 .pointer => {
3847 const null_literal = ZigTag.null_literal.init();
3848 return switch (suppress_as) {
3849 .with_as => try t.createBinOpNode(.as, type_node, null_literal),
3850 .no_as => null_literal,
3851 };
3852 },
3853 else => {},
3854 }
3855 return try ZigTag.std_mem_zeroes.create(t.arena, type_node);
3856}
3857
3858fn createIntNode(t: *Translator, int: aro.Value) !ZigNode {
3859 var space: aro.Interner.Tag.Int.BigIntSpace = undefined;
3860 var big = t.comp.interner.get(int.ref()).toBigInt(&space);
3861 const is_negative = !big.positive;
3862 big.positive = true;
3863
3864 const str = big.toStringAlloc(t.arena, 10, .lower) catch |err| switch (err) {
3865 error.OutOfMemory => return error.OutOfMemory,
3866 };
3867 const res = try ZigTag.integer_literal.create(t.arena, str);
3868 if (is_negative) return ZigTag.negate.create(t.arena, res);
3869 return res;
3870}
3871
3872fn createNumberNode(t: *Translator, num: anytype, num_kind: enum { int, float }) !ZigNode {
3873 const fmt_s = switch (@typeInfo(@TypeOf(num))) {
3874 .int, .comptime_int => "{d}",
3875 else => "{s}",
3876 };
3877 const str = try std.fmt.allocPrint(t.arena, fmt_s, .{num});
3878 if (num_kind == .float)
3879 return ZigTag.float_literal.create(t.arena, str)
3880 else
3881 return ZigTag.integer_literal.create(t.arena, str);
3882}
3883
3884fn createCharLiteralNode(t: *Translator, narrow: bool, val: u32) TransError!ZigNode {
3885 return ZigTag.char_literal.create(t.arena, if (narrow)
3886 try std.fmt.allocPrint(t.arena, "'{f}'", .{std.zig.fmtChar(&.{@as(u8, @intCast(val))})})
3887 else
3888 try std.fmt.allocPrint(t.arena, "'\\u{{{x}}}'", .{val}));
3889}
3890
3891fn createBinOpNode(
3892 t: *Translator,
3893 op: ZigTag,
3894 lhs: ZigNode,
3895 rhs: ZigNode,
3896) !ZigNode {
3897 const payload = try t.arena.create(ast.Payload.BinOp);
3898 payload.* = .{
3899 .base = .{ .tag = op },
3900 .data = .{
3901 .lhs = lhs,
3902 .rhs = rhs,
3903 },
3904 };
3905 return ZigNode.initPayload(&payload.base);
3906}
3907
3908pub fn createHelperCallNode(t: *Translator, name: std.meta.DeclEnum(@import("helpers")), args_opt: ?[]const ZigNode) !ZigNode {
3909 if (args_opt) |args| {
3910 return ZigTag.helper_call.create(t.arena, .{
3911 .name = @tagName(name),
3912 .args = try t.arena.dupe(ZigNode, args),
3913 });
3914 } else {
3915 return ZigTag.helper_ref.create(t.arena, @tagName(name));
3916 }
3917}
3918
3919/// Cast a signed integer node to a usize, for use in pointer arithmetic. Negative numbers
3920/// will become very large positive numbers but that is ok since we only use this in
3921/// pointer arithmetic expressions, where wraparound will ensure we get the correct value.
3922/// node -> @as(usize, @bitCast(@as(isize, @intCast(node))))
3923fn usizeCastForWrappingPtrArithmetic(t: *Translator, node: ZigNode) TransError!ZigNode {
3924 const intcast_node = try ZigTag.as.create(t.arena, .{
3925 .lhs = try ZigTag.type.create(t.arena, "isize"),
3926 .rhs = try ZigTag.int_cast.create(t.arena, node),
3927 });
3928
3929 return ZigTag.as.create(t.arena, .{
3930 .lhs = try ZigTag.type.create(t.arena, "usize"),
3931 .rhs = try ZigTag.bit_cast.create(t.arena, intcast_node),
3932 });
3933}
3934
3935/// @typeInfo(@TypeOf(vec_node)).vector.<field>
3936fn vectorTypeInfo(t: *Translator, vec_node: ZigNode, field: []const u8) TransError!ZigNode {
3937 const typeof_call = try ZigTag.typeof.create(t.arena, vec_node);
3938 const typeinfo_call = try ZigTag.typeinfo.create(t.arena, typeof_call);
3939 const vector_type_info = try ZigTag.field_access.create(t.arena, .{ .lhs = typeinfo_call, .field_name = "vector" });
3940 return ZigTag.field_access.create(t.arena, .{ .lhs = vector_type_info, .field_name = field });
3941}
3942
3943/// Build a getter function for a flexible array field in a C record
3944/// e.g. `T items[]` or `T items[0]`. The generated function returns a [*c] pointer
3945/// to the flexible array with the correct const and volatile qualifiers
3946fn createFlexibleMemberFn(
3947 t: *Translator,
3948 member_name: []const u8,
3949 field_name: []const u8,
3950) Error!ZigNode {
3951 const self_param_name = "self";
3952 const self_param = try ZigTag.identifier.create(t.arena, self_param_name);
3953 const self_type = try ZigTag.typeof.create(t.arena, self_param);
3954
3955 const fn_params = try t.arena.alloc(ast.Payload.Param, 1);
3956 fn_params[0] = .{
3957 .name = self_param_name,
3958 .type = ZigTag.@"anytype".init(),
3959 .is_noalias = false,
3960 };
3961
3962 // @typeInfo(@TypeOf(self.*.<field_name>)).pointer.child
3963 const dereffed = try ZigTag.deref.create(t.arena, self_param);
3964 const field_access = try ZigTag.field_access.create(t.arena, .{ .lhs = dereffed, .field_name = field_name });
3965 const type_of = try ZigTag.typeof.create(t.arena, field_access);
3966 const type_info = try ZigTag.typeinfo.create(t.arena, type_of);
3967 const array_info = try ZigTag.field_access.create(t.arena, .{ .lhs = type_info, .field_name = "array" });
3968 const child_info = try ZigTag.field_access.create(t.arena, .{ .lhs = array_info, .field_name = "child" });
3969
3970 const return_type = try t.createHelperCallNode(.FlexibleArrayType, &.{ self_type, child_info });
3971
3972 // return @ptrCast(&self.*.<field_name>);
3973 const address_of = try ZigTag.address_of.create(t.arena, field_access);
3974 const casted = try ZigTag.ptr_cast.create(t.arena, address_of);
3975 const return_stmt = try ZigTag.@"return".create(t.arena, casted);
3976 const body = try ZigTag.block_single.create(t.arena, return_stmt);
3977
3978 return ZigTag.func.create(t.arena, .{
3979 .is_pub = true,
3980 .is_extern = false,
3981 .is_export = false,
3982 .is_inline = false,
3983 .is_var_args = false,
3984 .name = member_name,
3985 .linksection_string = null,
3986 .explicit_callconv = null,
3987 .params = fn_params,
3988 .return_type = return_type,
3989 .body = body,
3990 .alignment = null,
3991 });
3992}
3993
3994// =================
3995// Macro translation
3996// =================
3997
3998fn transMacros(t: *Translator) !void {
3999 var tok_list = std.ArrayList(CToken).init(t.gpa);
4000 defer tok_list.deinit();
4001
4002 var pattern_list = try PatternList.init(t.gpa);
4003 defer pattern_list.deinit(t.gpa);
4004
4005 for (t.pp.defines.keys(), t.pp.defines.values()) |name, macro| {
4006 if (macro.is_builtin) continue;
4007 if (t.global_scope.containsNow(name)) {
4008 continue;
4009 }
4010
4011 tok_list.items.len = 0;
4012 try tok_list.ensureUnusedCapacity(macro.tokens.len);
4013 for (macro.tokens) |tok| {
4014 switch (tok.id) {
4015 .invalid => continue,
4016 .whitespace => continue,
4017 .comment => continue,
4018 .macro_ws => continue,
4019 else => {},
4020 }
4021 tok_list.appendAssumeCapacity(tok);
4022 }
4023
4024 if (macro.is_func) {
4025 const ms: PatternList.MacroSlicer = .{
4026 .tokens = tok_list.items,
4027 .source = t.comp.getSource(macro.loc.id).buf,
4028 .params = @intCast(macro.params.len),
4029 };
4030 if (try pattern_list.match(ms)) |impl| {
4031 const decl = try ZigTag.pub_var_simple.create(t.arena, .{
4032 .name = name,
4033 .init = try t.createHelperCallNode(impl, null),
4034 });
4035 try t.addTopLevelDecl(name, decl);
4036 continue;
4037 }
4038 }
4039
4040 if (t.checkTranslatableMacro(tok_list.items, macro.params)) |err| {
4041 switch (err) {
4042 .undefined_identifier => |ident| try t.failDeclExtra(&t.global_scope.base, macro.loc, name, "unable to translate macro: undefined identifier `{s}`", .{ident}),
4043 .invalid_arg_usage => |ident| try t.failDeclExtra(&t.global_scope.base, macro.loc, name, "unable to translate macro: untranslatable usage of arg `{s}`", .{ident}),
4044 }
4045 continue;
4046 }
4047
4048 var macro_translator: MacroTranslator = .{
4049 .t = t,
4050 .tokens = tok_list.items,
4051 .source = t.comp.getSource(macro.loc.id).buf,
4052 .name = name,
4053 .macro = macro,
4054 };
4055
4056 const res = if (macro.is_func)
4057 macro_translator.transFnMacro()
4058 else
4059 macro_translator.transMacro();
4060 res catch |err| switch (err) {
4061 error.ParseError => continue,
4062 error.OutOfMemory => |e| return e,
4063 };
4064 }
4065}
4066
4067const MacroTranslateError = union(enum) {
4068 undefined_identifier: []const u8,
4069 invalid_arg_usage: []const u8,
4070};
4071
4072fn checkTranslatableMacro(t: *Translator, tokens: []const CToken, params: []const []const u8) ?MacroTranslateError {
4073 var last_is_type_kw = false;
4074 var i: usize = 0;
4075 while (i < tokens.len) : (i += 1) {
4076 const token = tokens[i];
4077 switch (token.id) {
4078 .period, .arrow => i += 1, // skip next token since field identifiers can be unknown
4079 .keyword_struct, .keyword_union, .keyword_enum => if (!last_is_type_kw) {
4080 last_is_type_kw = true;
4081 continue;
4082 },
4083 .macro_param, .macro_param_no_expand => {
4084 if (last_is_type_kw) {
4085 return .{ .invalid_arg_usage = params[token.end] };
4086 }
4087 },
4088 .identifier, .extended_identifier => {
4089 const identifier = t.pp.tokSlice(token);
4090 if (!t.global_scope.contains(identifier) and !builtins.map.has(identifier)) {
4091 return .{ .undefined_identifier = identifier };
4092 }
4093 },
4094 else => {},
4095 }
4096 last_is_type_kw = false;
4097 }
4098 return null;
4099}
4100
4101fn getContainer(t: *Translator, node: ZigNode) ?ZigNode {
4102 switch (node.tag()) {
4103 .@"union",
4104 .@"struct",
4105 .address_of,
4106 .bit_not,
4107 .not,
4108 .optional_type,
4109 .negate,
4110 .negate_wrap,
4111 .array_type,
4112 .c_pointer,
4113 .single_pointer,
4114 => return node,
4115
4116 .identifier => {
4117 const ident = node.castTag(.identifier).?;
4118 if (t.global_scope.sym_table.get(ident.data)) |value| {
4119 if (value.castTag(.var_decl)) |var_decl|
4120 return t.getContainer(var_decl.data.init.?);
4121 if (value.castTag(.var_simple) orelse value.castTag(.pub_var_simple)) |var_decl|
4122 return t.getContainer(var_decl.data.init);
4123 }
4124 },
4125
4126 .field_access => {
4127 const field_access = node.castTag(.field_access).?;
4128
4129 if (t.getContainerTypeOf(field_access.data.lhs)) |ty_node| {
4130 if (ty_node.castTag(.@"struct") orelse ty_node.castTag(.@"union")) |container| {
4131 for (container.data.fields) |field| {
4132 if (mem.eql(u8, field.name, field_access.data.field_name)) {
4133 return t.getContainer(field.type);
4134 }
4135 }
4136 }
4137 }
4138 },
4139
4140 else => {},
4141 }
4142 return null;
4143}
4144
4145fn getContainerTypeOf(t: *Translator, ref: ZigNode) ?ZigNode {
4146 if (ref.castTag(.identifier)) |ident| {
4147 if (t.global_scope.sym_table.get(ident.data)) |value| {
4148 if (value.castTag(.var_decl)) |var_decl| {
4149 return t.getContainer(var_decl.data.type);
4150 }
4151 }
4152 } else if (ref.castTag(.field_access)) |field_access| {
4153 if (t.getContainerTypeOf(field_access.data.lhs)) |ty_node| {
4154 if (ty_node.castTag(.@"struct") orelse ty_node.castTag(.@"union")) |container| {
4155 for (container.data.fields) |field| {
4156 if (mem.eql(u8, field.name, field_access.data.field_name)) {
4157 return t.getContainer(field.type);
4158 }
4159 }
4160 } else return ty_node;
4161 }
4162 }
4163 return null;
4164}
4165
4166pub fn getFnProto(t: *Translator, ref: ZigNode) ?*ast.Payload.Func {
4167 const init = if (ref.castTag(.var_decl)) |v|
4168 v.data.init orelse return null
4169 else if (ref.castTag(.var_simple) orelse ref.castTag(.pub_var_simple)) |v|
4170 v.data.init
4171 else
4172 return null;
4173 if (t.getContainerTypeOf(init)) |ty_node| {
4174 if (ty_node.castTag(.optional_type)) |prefix| {
4175 if (prefix.data.castTag(.single_pointer)) |sp| {
4176 if (sp.data.elem_type.castTag(.func)) |fn_proto| {
4177 return fn_proto;
4178 }
4179 }
4180 }
4181 }
4182 return null;
4183}
lib/compiler/translate-c/src/ast.zig deleted-3063
...@@ -1,3063 +0,0 @@
1const std = @import("std");
2const Allocator = std.mem.Allocator;
3
4pub const Node = extern union {
5 /// If the tag value is less than Tag.no_payload_count, then no pointer
6 /// dereference is needed.
7 tag_if_small_enough: usize,
8 ptr_otherwise: *Payload,
9
10 pub const Tag = enum {
11 /// Declarations add themselves to the correct scopes and should not be emitted as this tag.
12 declaration,
13 null_literal,
14 undefined_literal,
15 /// opaque {}
16 opaque_literal,
17 true_literal,
18 false_literal,
19 empty_block,
20 return_void,
21 zero_literal,
22 one_literal,
23 @"unreachable",
24 void_type,
25 noreturn_type,
26 @"anytype",
27 @"continue",
28 @"break",
29 // After this, the tag requires a payload.
30
31 integer_literal,
32 float_literal,
33 string_literal,
34 char_literal,
35 enum_literal,
36 /// "string"[0..end]
37 string_slice,
38 identifier,
39 @"if",
40 /// if (!operand) break;
41 if_not_break,
42 @"while",
43 /// while (true) operand
44 while_true,
45 @"switch",
46 /// else => operand,
47 switch_else,
48 /// items => body,
49 switch_prong,
50 break_val,
51 @"return",
52 field_access,
53 array_access,
54 call,
55 var_decl,
56 /// const name = struct { init }
57 wrapped_local,
58 /// var name = init.*
59 mut_str,
60 func,
61 warning,
62 @"struct",
63 @"union",
64 @"opaque",
65 @"comptime",
66 @"defer",
67 array_init,
68 tuple,
69 container_init,
70 container_init_dot,
71 /// _ = operand;
72 discard,
73
74 // a + b
75 add,
76 // a = b
77 add_assign,
78 // c = (a = b)
79 add_wrap,
80 add_wrap_assign,
81 sub,
82 sub_assign,
83 sub_wrap,
84 sub_wrap_assign,
85 mul,
86 mul_assign,
87 mul_wrap,
88 mul_wrap_assign,
89 div,
90 div_assign,
91 shl,
92 shl_assign,
93 shr,
94 shr_assign,
95 mod,
96 mod_assign,
97 @"and",
98 @"or",
99 less_than,
100 less_than_equal,
101 greater_than,
102 greater_than_equal,
103 equal,
104 not_equal,
105 bit_and,
106 bit_and_assign,
107 bit_or,
108 bit_or_assign,
109 bit_xor,
110 bit_xor_assign,
111 array_cat,
112 ellipsis3,
113 assign,
114
115 /// @intCast(operand)
116 int_cast,
117 /// @constCast(operand)
118 const_cast,
119 /// @volatileCast(operand)
120 volatile_cast,
121 /// @divTrunc(lhs, rhs)
122 div_trunc,
123 /// @intFromBool(operand)
124 int_from_bool,
125 /// @as(lhs, rhs)
126 as,
127 /// @truncate(operand)
128 truncate,
129 /// @bitCast(operand)
130 bit_cast,
131 /// @floatCast(operand)
132 float_cast,
133 /// @intFromFloat(operand)
134 int_from_float,
135 /// @floatFromInt(operand)
136 float_from_int,
137 /// @ptrFromInt(operand)
138 ptr_from_int,
139 /// @intFromPtr(operand)
140 int_from_ptr,
141 /// @alignCast(operand)
142 align_cast,
143 /// @ptrCast(operand)
144 ptr_cast,
145 /// @divExact(lhs, rhs)
146 div_exact,
147 /// @offsetOf(lhs, rhs)
148 offset_of,
149 /// @splat(operand)
150 vector_zero_init,
151 /// @shuffle(type, a, b, mask)
152 shuffle,
153 /// @extern(ty, .{ .name = n })
154 builtin_extern,
155
156 /// @byteSwap(operand)
157 byte_swap,
158 /// @ceil(operand)
159 ceil,
160 /// @cos(operand)
161 cos,
162 /// @sin(operand)
163 sin,
164 /// @exp(operand)
165 exp,
166 /// @exp2(operand)
167 exp2,
168 /// @exp10(operand)
169 exp10,
170 /// @abs(operand)
171 abs,
172 /// @log(operand)
173 log,
174 /// @log2(operand)
175 log2,
176 /// @log10(operand)
177 log10,
178 /// @round(operand)
179 round,
180 /// @sqrt(operand)
181 sqrt,
182 /// @trunc(operand)
183 trunc,
184 /// @floor(operand)
185 floor,
186
187 /// __helpers.<name>(argshelper_call)
188 helper_call,
189 /// __helpers.<name>
190 helper_ref,
191
192 asm_simple,
193
194 negate,
195 negate_wrap,
196 bit_not,
197 not,
198 address_of,
199 /// .?
200 unwrap,
201 /// .*
202 deref,
203
204 block,
205 /// { operand }
206 block_single,
207
208 sizeof,
209 alignof,
210 typeof,
211 typeinfo,
212 type,
213
214 optional_type,
215 c_pointer,
216 single_pointer,
217 array_type,
218 null_sentinel_array_type,
219
220 /// @Vector(lhs, rhs)
221 vector,
222 /// @import("std").mem.zeroes(operand)
223 std_mem_zeroes,
224 /// @import("std").mem.zeroInit(lhs, rhs)
225 std_mem_zeroinit,
226 // pub const name = @compileError(msg);
227 fail_decl,
228 // var actual = mangled;
229 arg_redecl,
230 /// pub const alias = actual;
231 alias,
232 /// const name = init;
233 var_simple,
234 /// pub const name = init;
235 pub_var_simple,
236 /// pub? const name (: type)? = value
237 enum_constant,
238
239 /// pub inline fn name(params) return_type body
240 pub_inline_fn,
241
242 /// array_type{}
243 empty_array,
244 /// [1]type{val} ** count
245 array_filler,
246
247 /// comptime { if (!(lhs)) @compileError(rhs); }
248 static_assert,
249
250 pub const last_no_payload_tag = Tag.@"break";
251 pub const no_payload_count = @intFromEnum(last_no_payload_tag) + 1;
252
253 pub fn Type(comptime t: Tag) type {
254 return switch (t) {
255 .declaration,
256 .null_literal,
257 .undefined_literal,
258 .opaque_literal,
259 .true_literal,
260 .false_literal,
261 .empty_block,
262 .return_void,
263 .zero_literal,
264 .one_literal,
265 .void_type,
266 .noreturn_type,
267 .@"anytype",
268 .@"continue",
269 .@"break",
270 .@"unreachable",
271 => @compileError("Type Tag " ++ @tagName(t) ++ " has no payload"),
272
273 .std_mem_zeroes,
274 .@"return",
275 .@"comptime",
276 .@"defer",
277 .asm_simple,
278 .negate,
279 .negate_wrap,
280 .bit_not,
281 .not,
282 .optional_type,
283 .address_of,
284 .unwrap,
285 .deref,
286 .int_from_ptr,
287 .empty_array,
288 .while_true,
289 .if_not_break,
290 .switch_else,
291 .block_single,
292 .int_from_bool,
293 .sizeof,
294 .alignof,
295 .typeof,
296 .typeinfo,
297 .align_cast,
298 .truncate,
299 .bit_cast,
300 .float_cast,
301 .int_from_float,
302 .float_from_int,
303 .ptr_from_int,
304 .ptr_cast,
305 .int_cast,
306 .const_cast,
307 .volatile_cast,
308 .vector_zero_init,
309 .byte_swap,
310 .ceil,
311 .cos,
312 .sin,
313 .exp,
314 .exp2,
315 .exp10,
316 .abs,
317 .log,
318 .log2,
319 .log10,
320 .round,
321 .sqrt,
322 .trunc,
323 .floor,
324 => Payload.UnOp,
325
326 .add,
327 .add_assign,
328 .add_wrap,
329 .add_wrap_assign,
330 .sub,
331 .sub_assign,
332 .sub_wrap,
333 .sub_wrap_assign,
334 .mul,
335 .mul_assign,
336 .mul_wrap,
337 .mul_wrap_assign,
338 .div,
339 .div_assign,
340 .shl,
341 .shl_assign,
342 .shr,
343 .shr_assign,
344 .mod,
345 .mod_assign,
346 .@"and",
347 .@"or",
348 .less_than,
349 .less_than_equal,
350 .greater_than,
351 .greater_than_equal,
352 .equal,
353 .not_equal,
354 .bit_and,
355 .bit_and_assign,
356 .bit_or,
357 .bit_or_assign,
358 .bit_xor,
359 .bit_xor_assign,
360 .div_trunc,
361 .as,
362 .array_cat,
363 .ellipsis3,
364 .assign,
365 .array_access,
366 .std_mem_zeroinit,
367 .vector,
368 .div_exact,
369 .offset_of,
370 .static_assert,
371 => Payload.BinOp,
372
373 .integer_literal,
374 .float_literal,
375 .string_literal,
376 .char_literal,
377 .enum_literal,
378 .identifier,
379 .warning,
380 .type,
381 => Payload.Value,
382 .discard => Payload.Discard,
383 .@"if" => Payload.If,
384 .@"while" => Payload.While,
385 .@"switch", .array_init, .switch_prong => Payload.Switch,
386 .break_val => Payload.BreakVal,
387 .call => Payload.Call,
388 .var_decl => Payload.VarDecl,
389 .func => Payload.Func,
390 .@"struct", .@"union", .@"opaque" => Payload.Container,
391 .tuple => Payload.TupleInit,
392 .container_init => Payload.ContainerInit,
393 .container_init_dot => Payload.ContainerInitDot,
394 .block => Payload.Block,
395 .c_pointer, .single_pointer => Payload.Pointer,
396 .array_type, .null_sentinel_array_type => Payload.Array,
397 .arg_redecl, .alias, .fail_decl => Payload.ArgRedecl,
398 .var_simple, .pub_var_simple, .wrapped_local, .mut_str => Payload.SimpleVarDecl,
399 .enum_constant => Payload.EnumConstant,
400 .array_filler => Payload.ArrayFiller,
401 .pub_inline_fn => Payload.PubInlineFn,
402 .field_access => Payload.FieldAccess,
403 .string_slice => Payload.StringSlice,
404 .shuffle => Payload.Shuffle,
405 .builtin_extern => Payload.Extern,
406 .helper_call => Payload.HelperCall,
407 .helper_ref => Payload.HelperRef,
408 };
409 }
410
411 pub fn init(comptime t: Tag) Node {
412 comptime std.debug.assert(@intFromEnum(t) < Tag.no_payload_count);
413 return .{ .tag_if_small_enough = @intFromEnum(t) };
414 }
415
416 pub fn create(comptime t: Tag, ally: Allocator, data: Data(t)) error{OutOfMemory}!Node {
417 const ptr = try ally.create(t.Type());
418 ptr.* = .{
419 .base = .{ .tag = t },
420 .data = data,
421 };
422 return Node{ .ptr_otherwise = &ptr.base };
423 }
424
425 pub fn Data(comptime t: Tag) type {
426 return std.meta.fieldInfo(t.Type(), .data).type;
427 }
428 };
429
430 pub fn tag(self: Node) Tag {
431 if (self.tag_if_small_enough < Tag.no_payload_count) {
432 return @enumFromInt(@as(std.meta.Tag(Tag), @intCast(self.tag_if_small_enough)));
433 } else {
434 return self.ptr_otherwise.tag;
435 }
436 }
437
438 pub fn castTag(self: Node, comptime t: Tag) ?*t.Type() {
439 if (self.tag_if_small_enough < Tag.no_payload_count)
440 return null;
441
442 if (self.ptr_otherwise.tag == t)
443 return @alignCast(@fieldParentPtr("base", self.ptr_otherwise));
444
445 return null;
446 }
447
448 pub fn initPayload(payload: *Payload) Node {
449 std.debug.assert(@intFromEnum(payload.tag) >= Tag.no_payload_count);
450 return .{ .ptr_otherwise = payload };
451 }
452
453 pub fn isNoreturn(node: Node, break_counts: bool) bool {
454 switch (node.tag()) {
455 .block => {
456 const block_node = node.castTag(.block).?;
457 if (block_node.data.stmts.len == 0) return false;
458
459 const last = block_node.data.stmts[block_node.data.stmts.len - 1];
460 return last.isNoreturn(break_counts);
461 },
462 .@"switch" => {
463 const switch_node = node.castTag(.@"switch").?;
464
465 for (switch_node.data.cases) |case| {
466 const body = if (case.castTag(.switch_else)) |some|
467 some.data
468 else if (case.castTag(.switch_prong)) |some|
469 some.data.cond
470 else
471 unreachable;
472
473 if (!body.isNoreturn(break_counts)) return false;
474 }
475 return true;
476 },
477 .@"return", .return_void => return true,
478 .@"break" => if (break_counts) return true,
479 else => {},
480 }
481 return false;
482 }
483
484 pub fn isBoolRes(res: Node) bool {
485 switch (res.tag()) {
486 .@"or",
487 .@"and",
488 .equal,
489 .not_equal,
490 .less_than,
491 .less_than_equal,
492 .greater_than,
493 .greater_than_equal,
494 .not,
495 .false_literal,
496 .true_literal,
497 => return true,
498 else => return false,
499 }
500 }
501};
502
503pub const Payload = struct {
504 tag: Node.Tag,
505
506 pub const Value = struct {
507 base: Payload,
508 data: []const u8,
509 };
510
511 pub const UnOp = struct {
512 base: Payload,
513 data: Node,
514 };
515
516 pub const BinOp = struct {
517 base: Payload,
518 data: struct {
519 lhs: Node,
520 rhs: Node,
521 },
522 };
523
524 pub const Discard = struct {
525 base: Payload,
526 data: struct {
527 should_skip: bool,
528 value: Node,
529 },
530 };
531
532 pub const If = struct {
533 base: Payload,
534 data: struct {
535 cond: Node,
536 then: Node,
537 @"else": ?Node,
538 },
539 };
540
541 pub const While = struct {
542 base: Payload,
543 data: struct {
544 cond: Node,
545 body: Node,
546 cont_expr: ?Node,
547 },
548 };
549
550 pub const Switch = struct {
551 base: Payload,
552 data: struct {
553 cond: Node,
554 cases: []Node,
555 },
556 };
557
558 pub const BreakVal = struct {
559 base: Payload,
560 data: struct {
561 label: ?[]const u8,
562 val: Node,
563 },
564 };
565
566 pub const Call = struct {
567 base: Payload,
568 data: struct {
569 lhs: Node,
570 args: []Node,
571 },
572 };
573
574 pub const VarDecl = struct {
575 base: Payload,
576 data: struct {
577 is_pub: bool,
578 is_const: bool,
579 is_extern: bool,
580 is_export: bool,
581 is_threadlocal: bool,
582 alignment: ?c_uint,
583 linksection_string: ?[]const u8,
584 name: []const u8,
585 type: Node,
586 init: ?Node,
587 },
588 };
589
590 pub const Func = struct {
591 base: Payload,
592 data: struct {
593 is_pub: bool,
594 is_extern: bool,
595 is_export: bool,
596 is_inline: bool,
597 is_var_args: bool,
598 name: ?[]const u8,
599 linksection_string: ?[]const u8,
600 explicit_callconv: ?CallingConvention,
601 params: []Param,
602 return_type: Node,
603 body: ?Node,
604 alignment: ?c_uint,
605 },
606
607 pub const CallingConvention = enum {
608 c,
609 x86_64_sysv,
610 x86_64_win,
611 x86_stdcall,
612 x86_fastcall,
613 x86_thiscall,
614 x86_vectorcall,
615 x86_regcall,
616 aarch64_vfabi,
617 aarch64_sve_pcs,
618 arm_aapcs,
619 arm_aapcs_vfp,
620 m68k_rtd,
621 riscv_vector,
622 };
623 };
624
625 pub const Param = struct {
626 is_noalias: bool,
627 name: ?[]const u8,
628 type: Node,
629 };
630
631 pub const Container = struct {
632 base: Payload,
633 data: struct {
634 layout: enum { @"packed", @"extern", none },
635 fields: []Field,
636 decls: []Node,
637 },
638
639 pub const Field = struct {
640 name: []const u8,
641 type: Node,
642 alignment: ?c_uint,
643 default_value: ?Node,
644 };
645 };
646
647 pub const TupleInit = struct {
648 base: Payload,
649 data: []Node,
650 };
651
652 pub const ContainerInit = struct {
653 base: Payload,
654 data: struct {
655 lhs: Node,
656 inits: []Initializer,
657 },
658
659 pub const Initializer = struct {
660 name: []const u8,
661 value: Node,
662 };
663 };
664
665 pub const ContainerInitDot = struct {
666 base: Payload,
667 data: []Initializer,
668
669 pub const Initializer = struct {
670 name: []const u8,
671 value: Node,
672 };
673 };
674
675 pub const Block = struct {
676 base: Payload,
677 data: struct {
678 label: ?[]const u8,
679 stmts: []Node,
680 },
681 };
682
683 pub const Array = struct {
684 base: Payload,
685 data: ArrayTypeInfo,
686
687 pub const ArrayTypeInfo = struct {
688 elem_type: Node,
689 len: u64,
690 };
691 };
692
693 pub const Pointer = struct {
694 base: Payload,
695 data: struct {
696 elem_type: Node,
697 is_const: bool,
698 is_volatile: bool,
699 is_allowzero: bool,
700 },
701 };
702
703 pub const ArgRedecl = struct {
704 base: Payload,
705 data: struct {
706 actual: []const u8,
707 mangled: []const u8,
708 },
709 };
710
711 pub const SimpleVarDecl = struct {
712 base: Payload,
713 data: struct {
714 name: []const u8,
715 init: Node,
716 },
717 };
718
719 pub const EnumConstant = struct {
720 base: Payload,
721 data: struct {
722 name: []const u8,
723 is_public: bool,
724 type: ?Node,
725 value: Node,
726 },
727 };
728
729 pub const ArrayFiller = struct {
730 base: Payload,
731 data: struct {
732 type: Node,
733 filler: Node,
734 count: u64,
735 },
736 };
737
738 pub const PubInlineFn = struct {
739 base: Payload,
740 data: struct {
741 name: []const u8,
742 params: []Param,
743 return_type: Node,
744 body: Node,
745 },
746 };
747
748 pub const FieldAccess = struct {
749 base: Payload,
750 data: struct {
751 lhs: Node,
752 field_name: []const u8,
753 },
754 };
755
756 pub const StringSlice = struct {
757 base: Payload,
758 data: struct {
759 string: Node,
760 end: u64,
761 },
762 };
763
764 pub const Shuffle = struct {
765 base: Payload,
766 data: struct {
767 element_type: Node,
768 a: Node,
769 b: Node,
770 mask_vector: Node,
771 },
772 };
773
774 pub const Extern = struct {
775 base: Payload,
776 data: struct {
777 type: Node,
778 name: Node,
779 },
780 };
781
782 pub const HelperCall = struct {
783 base: Payload,
784 data: struct {
785 name: []const u8,
786 args: []const Node,
787 },
788 };
789
790 pub const HelperRef = struct {
791 base: Payload,
792 data: []const u8,
793 };
794};
795
796/// Converts the nodes into a Zig Ast.
797/// Caller must free the source slice.
798pub fn render(gpa: Allocator, nodes: []const Node) !std.zig.Ast {
799 var ctx: Context = .{
800 .gpa = gpa,
801 .buf = std.array_list.Managed(u8).init(gpa),
802 };
803 defer ctx.buf.deinit();
804 defer ctx.nodes.deinit(gpa);
805 defer ctx.extra_data.deinit(gpa);
806 defer ctx.tokens.deinit(gpa);
807
808 // Estimate that each top level node has 10 child nodes.
809 const estimated_node_count = nodes.len * 10 + 1; // +1 for the .root node
810 try ctx.nodes.ensureTotalCapacity(gpa, estimated_node_count);
811 // Estimate that each each node has 2 tokens.
812 const estimated_tokens_count = estimated_node_count * 2;
813 try ctx.tokens.ensureTotalCapacity(gpa, estimated_tokens_count);
814 // Estimate that each each token is 3 bytes long.
815 const estimated_buf_len = estimated_tokens_count * 3;
816 try ctx.buf.ensureTotalCapacity(estimated_buf_len);
817
818 ctx.nodes.appendAssumeCapacity(.{
819 .tag = .root,
820 .main_token = 0,
821 .data = undefined,
822 });
823
824 const root_members = blk: {
825 var result = std.array_list.Managed(NodeIndex).init(gpa);
826 defer result.deinit();
827
828 for (nodes) |node| {
829 const res = (try renderNodeOpt(&ctx, node)) orelse continue;
830 try result.append(res);
831 }
832 break :blk try ctx.listToSpan(result.items);
833 };
834
835 ctx.nodes.items(.data)[0] = .{ .extra_range = .{
836 .start = root_members.start,
837 .end = root_members.end,
838 } };
839
840 try ctx.tokens.append(gpa, .{
841 .tag = .eof,
842 .start = @as(u32, @intCast(ctx.buf.items.len)),
843 });
844
845 return .{
846 .source = try ctx.buf.toOwnedSliceSentinel(0),
847 .tokens = ctx.tokens.toOwnedSlice(),
848 .nodes = ctx.nodes.toOwnedSlice(),
849 .extra_data = try ctx.extra_data.toOwnedSlice(gpa),
850 .errors = &.{},
851 .mode = .zig,
852 };
853}
854
855const NodeIndex = std.zig.Ast.Node.Index;
856const NodeSubRange = std.zig.Ast.Node.SubRange;
857const TokenIndex = std.zig.Ast.TokenIndex;
858const TokenTag = std.zig.Token.Tag;
859
860const Context = struct {
861 gpa: Allocator,
862 buf: std.array_list.Managed(u8),
863 nodes: std.zig.Ast.NodeList = .{},
864 extra_data: std.ArrayListUnmanaged(u32) = .empty,
865 tokens: std.zig.Ast.TokenList = .{},
866
867 fn addTokenFmt(c: *Context, tag: TokenTag, comptime format: []const u8, args: anytype) Allocator.Error!TokenIndex {
868 const start_index = c.buf.items.len;
869 try c.buf.print(format ++ " ", args);
870
871 try c.tokens.append(c.gpa, .{
872 .tag = tag,
873 .start = @intCast(start_index),
874 });
875
876 return @intCast(c.tokens.len - 1);
877 }
878
879 fn addToken(c: *Context, tag: TokenTag, bytes: []const u8) Allocator.Error!TokenIndex {
880 return c.addTokenFmt(tag, "{s}", .{bytes});
881 }
882
883 fn addIdentifier(c: *Context, bytes: []const u8) Allocator.Error!TokenIndex {
884 if (std.zig.primitives.isPrimitive(bytes))
885 return c.addTokenFmt(.identifier, "@\"{s}\"", .{bytes});
886 return c.addTokenFmt(.identifier, "{f}", .{std.zig.fmtId(bytes)});
887 }
888
889 fn listToSpan(c: *Context, list: []const NodeIndex) Allocator.Error!NodeSubRange {
890 try c.extra_data.appendSlice(c.gpa, @ptrCast(list));
891 return .{
892 .start = @enumFromInt(c.extra_data.items.len - list.len),
893 .end = @enumFromInt(c.extra_data.items.len),
894 };
895 }
896
897 fn addNode(c: *Context, elem: std.zig.Ast.Node) Allocator.Error!NodeIndex {
898 const result: NodeIndex = @enumFromInt(c.nodes.len);
899 try c.nodes.append(c.gpa, elem);
900 return result;
901 }
902
903 fn addExtra(c: *Context, extra: anytype) Allocator.Error!std.zig.Ast.ExtraIndex {
904 const fields = std.meta.fields(@TypeOf(extra));
905 try c.extra_data.ensureUnusedCapacity(c.gpa, fields.len);
906 const result: std.zig.Ast.ExtraIndex = @enumFromInt(c.extra_data.items.len);
907 inline for (fields) |field| {
908 const data: u32 = switch (field.type) {
909 NodeIndex,
910 std.zig.Ast.Node.OptionalIndex,
911 std.zig.Ast.OptionalTokenIndex,
912 std.zig.Ast.ExtraIndex,
913 => @intFromEnum(@field(extra, field.name)),
914 TokenIndex,
915 => @field(extra, field.name),
916 else => @compileError("unexpected field type"),
917 };
918 c.extra_data.appendAssumeCapacity(data);
919 }
920 return result;
921 }
922};
923
924fn renderNodeOpt(c: *Context, node: Node) Allocator.Error!?NodeIndex {
925 switch (node.tag()) {
926 .warning => {
927 const payload = node.castTag(.warning).?.data;
928 try c.buf.appendSlice(payload);
929 try c.buf.append('\n');
930 return null;
931 },
932 .discard => {
933 const payload = node.castTag(.discard).?.data;
934 if (payload.should_skip) return null;
935
936 return try renderNode(c, node);
937 },
938 else => return try renderNode(c, node),
939 }
940}
941
942fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
943 switch (node.tag()) {
944 .declaration => unreachable,
945 .warning => unreachable,
946 .discard => {
947 const payload = node.castTag(.discard).?.data;
948 std.debug.assert(!payload.should_skip);
949
950 const lhs = try c.addNode(.{
951 .tag = .identifier,
952 .main_token = try c.addToken(.identifier, "_"),
953 .data = undefined,
954 });
955 const main_token = try c.addToken(.equal, "=");
956 if (payload.value.tag() == .identifier) {
957 // Render as `_ = &foo;` to avoid tripping "pointless discard" and "local variable never mutated" errors.
958 var addr_of_pl: Payload.UnOp = .{
959 .base = .{ .tag = .address_of },
960 .data = payload.value,
961 };
962 const addr_of: Node = .{ .ptr_otherwise = &addr_of_pl.base };
963 return try c.addNode(.{
964 .tag = .assign,
965 .main_token = main_token,
966 .data = .{ .node_and_node = .{
967 lhs, try renderNode(c, addr_of),
968 } },
969 });
970 } else {
971 return try c.addNode(.{
972 .tag = .assign,
973 .main_token = main_token,
974 .data = .{ .node_and_node = .{
975 lhs, try renderNode(c, payload.value),
976 } },
977 });
978 }
979 },
980 .std_mem_zeroes => {
981 const payload = node.castTag(.std_mem_zeroes).?.data;
982 const import_node = try renderStdImport(c, &.{ "mem", "zeroes" });
983 return renderCall(c, import_node, &.{payload});
984 },
985 .std_mem_zeroinit => {
986 const payload = node.castTag(.std_mem_zeroinit).?.data;
987 const import_node = try renderStdImport(c, &.{ "mem", "zeroInit" });
988 return renderCall(c, import_node, &.{ payload.lhs, payload.rhs });
989 },
990 .vector => {
991 const payload = node.castTag(.vector).?.data;
992 return renderBuiltinCall(c, "@Vector", &.{ payload.lhs, payload.rhs });
993 },
994 .call => {
995 const payload = node.castTag(.call).?.data;
996 const lhs = try renderNodeGrouped(c, payload.lhs);
997 return renderCall(c, lhs, payload.args);
998 },
999 .null_literal => return c.addNode(.{
1000 .tag = .identifier,
1001 .main_token = try c.addToken(.identifier, "null"),
1002 .data = undefined,
1003 }),
1004 .undefined_literal => return c.addNode(.{
1005 .tag = .identifier,
1006 .main_token = try c.addToken(.identifier, "undefined"),
1007 .data = undefined,
1008 }),
1009 .true_literal => return c.addNode(.{
1010 .tag = .identifier,
1011 .main_token = try c.addToken(.identifier, "true"),
1012 .data = undefined,
1013 }),
1014 .false_literal => return c.addNode(.{
1015 .tag = .identifier,
1016 .main_token = try c.addToken(.identifier, "false"),
1017 .data = undefined,
1018 }),
1019 .zero_literal => return c.addNode(.{
1020 .tag = .number_literal,
1021 .main_token = try c.addToken(.number_literal, "0"),
1022 .data = undefined,
1023 }),
1024 .one_literal => return c.addNode(.{
1025 .tag = .number_literal,
1026 .main_token = try c.addToken(.number_literal, "1"),
1027 .data = undefined,
1028 }),
1029 .@"unreachable" => return c.addNode(.{
1030 .tag = .unreachable_literal,
1031 .main_token = try c.addToken(.keyword_unreachable, "unreachable"),
1032 .data = undefined,
1033 }),
1034 .void_type => return c.addNode(.{
1035 .tag = .identifier,
1036 .main_token = try c.addToken(.identifier, "void"),
1037 .data = undefined,
1038 }),
1039 .noreturn_type => return c.addNode(.{
1040 .tag = .identifier,
1041 .main_token = try c.addToken(.identifier, "noreturn"),
1042 .data = undefined,
1043 }),
1044 .@"continue" => return c.addNode(.{
1045 .tag = .@"continue",
1046 .main_token = try c.addToken(.keyword_continue, "continue"),
1047 .data = .{ .opt_token_and_opt_node = .{
1048 .none, .none,
1049 } },
1050 }),
1051 .return_void => return c.addNode(.{
1052 .tag = .@"return",
1053 .main_token = try c.addToken(.keyword_return, "return"),
1054 .data = .{ .opt_node = .none },
1055 }),
1056 .@"break" => return c.addNode(.{
1057 .tag = .@"break",
1058 .main_token = try c.addToken(.keyword_break, "break"),
1059 .data = .{ .opt_token_and_opt_node = .{
1060 .none, .none,
1061 } },
1062 }),
1063 .break_val => {
1064 const payload = node.castTag(.break_val).?.data;
1065 const tok = try c.addToken(.keyword_break, "break");
1066 const break_label = if (payload.label) |some| blk: {
1067 _ = try c.addToken(.colon, ":");
1068 break :blk try c.addIdentifier(some);
1069 } else 0;
1070 return c.addNode(.{
1071 .tag = .@"break",
1072 .main_token = tok,
1073 .data = .{ .opt_token_and_opt_node = .{
1074 .fromToken(break_label), (try renderNode(c, payload.val)).toOptional(),
1075 } },
1076 });
1077 },
1078 .@"return" => {
1079 const payload = node.castTag(.@"return").?.data;
1080 return c.addNode(.{
1081 .tag = .@"return",
1082 .main_token = try c.addToken(.keyword_return, "return"),
1083 .data = .{ .opt_node = (try renderNode(c, payload)).toOptional() },
1084 });
1085 },
1086 .@"comptime" => {
1087 const payload = node.castTag(.@"comptime").?.data;
1088 return c.addNode(.{
1089 .tag = .@"comptime",
1090 .main_token = try c.addToken(.keyword_comptime, "comptime"),
1091 .data = .{
1092 .node = try renderNode(c, payload),
1093 },
1094 });
1095 },
1096 .@"defer" => {
1097 const payload = node.castTag(.@"defer").?.data;
1098 return c.addNode(.{
1099 .tag = .@"defer",
1100 .main_token = try c.addToken(.keyword_defer, "defer"),
1101 .data = .{
1102 .node = try renderNode(c, payload),
1103 },
1104 });
1105 },
1106 .asm_simple => {
1107 const payload = node.castTag(.asm_simple).?.data;
1108 const asm_token = try c.addToken(.keyword_asm, "asm");
1109 _ = try c.addToken(.l_paren, "(");
1110 return c.addNode(.{
1111 .tag = .asm_simple,
1112 .main_token = asm_token,
1113 .data = .{ .node_and_token = .{
1114 try renderNode(c, payload),
1115 try c.addToken(.r_paren, ")"),
1116 } },
1117 });
1118 },
1119 .type => {
1120 const payload = node.castTag(.type).?.data;
1121 return c.addNode(.{
1122 .tag = .identifier,
1123 .main_token = try c.addToken(.identifier, payload),
1124 .data = undefined,
1125 });
1126 },
1127 .identifier => {
1128 const payload = node.castTag(.identifier).?.data;
1129 return c.addNode(.{
1130 .tag = .identifier,
1131 .main_token = try c.addIdentifier(payload),
1132 .data = undefined,
1133 });
1134 },
1135 .float_literal => {
1136 const payload = node.castTag(.float_literal).?.data;
1137 return c.addNode(.{
1138 .tag = .number_literal,
1139 .main_token = try c.addToken(.number_literal, payload),
1140 .data = undefined,
1141 });
1142 },
1143 .integer_literal => {
1144 const payload = node.castTag(.integer_literal).?.data;
1145 return c.addNode(.{
1146 .tag = .number_literal,
1147 .main_token = try c.addToken(.number_literal, payload),
1148 .data = undefined,
1149 });
1150 },
1151 .string_literal => {
1152 const payload = node.castTag(.string_literal).?.data;
1153 return c.addNode(.{
1154 .tag = .string_literal,
1155 .main_token = try c.addToken(.string_literal, payload),
1156 .data = undefined,
1157 });
1158 },
1159 .char_literal => {
1160 const payload = node.castTag(.char_literal).?.data;
1161 return c.addNode(.{
1162 .tag = .char_literal,
1163 .main_token = try c.addToken(.char_literal, payload),
1164 .data = undefined,
1165 });
1166 },
1167 .enum_literal => {
1168 const payload = node.castTag(.enum_literal).?.data;
1169 _ = try c.addToken(.period, ".");
1170 return c.addNode(.{
1171 .tag = .enum_literal,
1172 .main_token = try c.addToken(.identifier, payload),
1173 .data = undefined,
1174 });
1175 },
1176 .string_slice => {
1177 const payload = node.castTag(.string_slice).?.data;
1178
1179 const string = try renderNode(c, payload.string);
1180 const l_bracket = try c.addToken(.l_bracket, "[");
1181 const start = try c.addNode(.{
1182 .tag = .number_literal,
1183 .main_token = try c.addToken(.number_literal, "0"),
1184 .data = undefined,
1185 });
1186 _ = try c.addToken(.ellipsis2, "..");
1187 const end = try c.addNode(.{
1188 .tag = .number_literal,
1189 .main_token = try c.addTokenFmt(.number_literal, "{d}", .{payload.end}),
1190 .data = undefined,
1191 });
1192 _ = try c.addToken(.r_bracket, "]");
1193
1194 return c.addNode(.{
1195 .tag = .slice,
1196 .main_token = l_bracket,
1197 .data = .{ .node_and_extra = .{
1198 string, try c.addExtra(std.zig.Ast.Node.Slice{
1199 .start = start,
1200 .end = end,
1201 }),
1202 } },
1203 });
1204 },
1205 .fail_decl => {
1206 const payload = node.castTag(.fail_decl).?.data;
1207 // pub const name = @compileError(msg);
1208 _ = try c.addToken(.keyword_pub, "pub");
1209 const const_tok = try c.addToken(.keyword_const, "const");
1210 _ = try c.addIdentifier(payload.actual);
1211 _ = try c.addToken(.equal, "=");
1212
1213 const compile_error_tok = try c.addToken(.builtin, "@compileError");
1214 _ = try c.addToken(.l_paren, "(");
1215 const err_msg_tok = try c.addTokenFmt(.string_literal, "\"{f}\"", .{std.zig.fmtString(payload.mangled)});
1216 const err_msg = try c.addNode(.{
1217 .tag = .string_literal,
1218 .main_token = err_msg_tok,
1219 .data = undefined,
1220 });
1221 _ = try c.addToken(.r_paren, ")");
1222 const compile_error = try c.addNode(.{
1223 .tag = .builtin_call_two,
1224 .main_token = compile_error_tok,
1225 .data = .{ .opt_node_and_opt_node = .{
1226 err_msg.toOptional(), .none,
1227 } },
1228 });
1229 _ = try c.addToken(.semicolon, ";");
1230
1231 return c.addNode(.{
1232 .tag = .simple_var_decl,
1233 .main_token = const_tok,
1234 .data = .{
1235 .opt_node_and_opt_node = .{
1236 .none, // Type expression
1237 compile_error.toOptional(), // Init expression
1238 },
1239 },
1240 });
1241 },
1242 .pub_var_simple, .var_simple => {
1243 const payload = @as(*Payload.SimpleVarDecl, @alignCast(@fieldParentPtr("base", node.ptr_otherwise))).data;
1244 if (node.tag() == .pub_var_simple) _ = try c.addToken(.keyword_pub, "pub");
1245 const const_tok = try c.addToken(.keyword_const, "const");
1246 _ = try c.addIdentifier(payload.name);
1247 _ = try c.addToken(.equal, "=");
1248
1249 const init = try renderNode(c, payload.init);
1250 _ = try c.addToken(.semicolon, ";");
1251
1252 return c.addNode(.{
1253 .tag = .simple_var_decl,
1254 .main_token = const_tok,
1255 .data = .{
1256 .opt_node_and_opt_node = .{
1257 .none, // Type expression
1258 init.toOptional(), // Init expression
1259 },
1260 },
1261 });
1262 },
1263 .wrapped_local => {
1264 const payload = node.castTag(.wrapped_local).?.data;
1265
1266 const const_tok = try c.addToken(.keyword_const, "const");
1267 _ = try c.addIdentifier(payload.name);
1268 _ = try c.addToken(.equal, "=");
1269
1270 const kind_tok = try c.addToken(.keyword_struct, "struct");
1271 _ = try c.addToken(.l_brace, "{");
1272
1273 const container_def = try c.addNode(.{
1274 .tag = .container_decl_two_trailing,
1275 .main_token = kind_tok,
1276 .data = .{ .opt_node_and_opt_node = .{
1277 (try renderNode(c, payload.init)).toOptional(), .none,
1278 } },
1279 });
1280 _ = try c.addToken(.r_brace, "}");
1281 _ = try c.addToken(.semicolon, ";");
1282
1283 return c.addNode(.{
1284 .tag = .simple_var_decl,
1285 .main_token = const_tok,
1286 .data = .{
1287 .opt_node_and_opt_node = .{
1288 .none, // Type expression
1289 container_def.toOptional(), // Init expression
1290 },
1291 },
1292 });
1293 },
1294 .mut_str => {
1295 const payload = node.castTag(.mut_str).?.data;
1296
1297 const var_tok = try c.addToken(.keyword_var, "var");
1298 _ = try c.addIdentifier(payload.name);
1299 _ = try c.addToken(.equal, "=");
1300
1301 const deref = try c.addNode(.{
1302 .tag = .deref,
1303 .data = .{
1304 .node = try renderNodeGrouped(c, payload.init),
1305 },
1306 .main_token = try c.addToken(.period_asterisk, ".*"),
1307 });
1308 _ = try c.addToken(.semicolon, ";");
1309
1310 return c.addNode(.{
1311 .tag = .simple_var_decl,
1312 .main_token = var_tok,
1313 .data = .{
1314 .opt_node_and_opt_node = .{
1315 .none, // Type expression
1316 deref.toOptional(), // Init expression
1317 },
1318 },
1319 });
1320 },
1321 .var_decl => return renderVar(c, node),
1322 .arg_redecl, .alias => {
1323 const payload = @as(*Payload.ArgRedecl, @alignCast(@fieldParentPtr("base", node.ptr_otherwise))).data;
1324 if (node.tag() == .alias) _ = try c.addToken(.keyword_pub, "pub");
1325 const mut_tok = if (node.tag() == .alias)
1326 try c.addToken(.keyword_const, "const")
1327 else
1328 try c.addToken(.keyword_var, "var");
1329 _ = try c.addIdentifier(payload.actual);
1330 _ = try c.addToken(.equal, "=");
1331
1332 const init = try c.addNode(.{
1333 .tag = .identifier,
1334 .main_token = try c.addIdentifier(payload.mangled),
1335 .data = undefined,
1336 });
1337 _ = try c.addToken(.semicolon, ";");
1338
1339 return c.addNode(.{
1340 .tag = .simple_var_decl,
1341 .main_token = mut_tok,
1342 .data = .{
1343 .opt_node_and_opt_node = .{
1344 .none, // Type expression
1345 init.toOptional(), // Init expression
1346 },
1347 },
1348 });
1349 },
1350 .int_cast => {
1351 const payload = node.castTag(.int_cast).?.data;
1352 return renderBuiltinCall(c, "@intCast", &.{payload});
1353 },
1354 .const_cast => {
1355 const payload = node.castTag(.const_cast).?.data;
1356 return renderBuiltinCall(c, "@constCast", &.{payload});
1357 },
1358 .volatile_cast => {
1359 const payload = node.castTag(.volatile_cast).?.data;
1360 return renderBuiltinCall(c, "@volatileCast", &.{payload});
1361 },
1362 .div_trunc => {
1363 const payload = node.castTag(.div_trunc).?.data;
1364 return renderBuiltinCall(c, "@divTrunc", &.{ payload.lhs, payload.rhs });
1365 },
1366 .int_from_bool => {
1367 const payload = node.castTag(.int_from_bool).?.data;
1368 return renderBuiltinCall(c, "@intFromBool", &.{payload});
1369 },
1370 .as => {
1371 const payload = node.castTag(.as).?.data;
1372 return renderBuiltinCall(c, "@as", &.{ payload.lhs, payload.rhs });
1373 },
1374 .truncate => {
1375 const payload = node.castTag(.truncate).?.data;
1376 return renderBuiltinCall(c, "@truncate", &.{payload});
1377 },
1378 .bit_cast => {
1379 const payload = node.castTag(.bit_cast).?.data;
1380 return renderBuiltinCall(c, "@bitCast", &.{payload});
1381 },
1382 .float_cast => {
1383 const payload = node.castTag(.float_cast).?.data;
1384 return renderBuiltinCall(c, "@floatCast", &.{payload});
1385 },
1386 .int_from_float => {
1387 const payload = node.castTag(.int_from_float).?.data;
1388 return renderBuiltinCall(c, "@intFromFloat", &.{payload});
1389 },
1390 .float_from_int => {
1391 const payload = node.castTag(.float_from_int).?.data;
1392 return renderBuiltinCall(c, "@floatFromInt", &.{payload});
1393 },
1394 .ptr_from_int => {
1395 const payload = node.castTag(.ptr_from_int).?.data;
1396 return renderBuiltinCall(c, "@ptrFromInt", &.{payload});
1397 },
1398 .int_from_ptr => {
1399 const payload = node.castTag(.int_from_ptr).?.data;
1400 return renderBuiltinCall(c, "@intFromPtr", &.{payload});
1401 },
1402 .align_cast => {
1403 const payload = node.castTag(.align_cast).?.data;
1404 return renderBuiltinCall(c, "@alignCast", &.{payload});
1405 },
1406 .ptr_cast => {
1407 const payload = node.castTag(.ptr_cast).?.data;
1408 return renderBuiltinCall(c, "@ptrCast", &.{payload});
1409 },
1410 .div_exact => {
1411 const payload = node.castTag(.div_exact).?.data;
1412 return renderBuiltinCall(c, "@divExact", &.{ payload.lhs, payload.rhs });
1413 },
1414 .offset_of => {
1415 const payload = node.castTag(.offset_of).?.data;
1416 return renderBuiltinCall(c, "@offsetOf", &.{ payload.lhs, payload.rhs });
1417 },
1418 .sizeof => {
1419 const payload = node.castTag(.sizeof).?.data;
1420 return renderBuiltinCall(c, "@sizeOf", &.{payload});
1421 },
1422 .shuffle => {
1423 const payload = node.castTag(.shuffle).?.data;
1424 return renderBuiltinCall(c, "@shuffle", &.{
1425 payload.element_type,
1426 payload.a,
1427 payload.b,
1428 payload.mask_vector,
1429 });
1430 },
1431 .builtin_extern => {
1432 const payload = node.castTag(.builtin_extern).?.data;
1433
1434 var info_inits: [1]Payload.ContainerInitDot.Initializer = .{
1435 .{ .name = "name", .value = payload.name },
1436 };
1437 var info_payload: Payload.ContainerInitDot = .{
1438 .base = .{ .tag = .container_init_dot },
1439 .data = &info_inits,
1440 };
1441
1442 return renderBuiltinCall(c, "@extern", &.{
1443 payload.type,
1444 .{ .ptr_otherwise = &info_payload.base },
1445 });
1446 },
1447 .helper_call => {
1448 const payload = node.castTag(.helper_call).?.data;
1449 const helpers_tok = try c.addNode(.{
1450 .tag = .identifier,
1451 .main_token = try c.addIdentifier("__helpers"),
1452 .data = undefined,
1453 });
1454 const func = try renderFieldAccess(c, helpers_tok, payload.name);
1455 return renderCall(c, func, payload.args);
1456 },
1457 .helper_ref => {
1458 const payload = node.castTag(.helper_ref).?.data;
1459 const helpers_tok = try c.addNode(.{
1460 .tag = .identifier,
1461 .main_token = try c.addIdentifier("__helpers"),
1462 .data = undefined,
1463 });
1464 return renderFieldAccess(c, helpers_tok, payload);
1465 },
1466 .alignof => {
1467 const payload = node.castTag(.alignof).?.data;
1468 return renderBuiltinCall(c, "@alignOf", &.{payload});
1469 },
1470 .typeof => {
1471 const payload = node.castTag(.typeof).?.data;
1472 return renderBuiltinCall(c, "@TypeOf", &.{payload});
1473 },
1474 .typeinfo => {
1475 const payload = node.castTag(.typeinfo).?.data;
1476 return renderBuiltinCall(c, "@typeInfo", &.{payload});
1477 },
1478 .byte_swap => {
1479 const payload = node.castTag(.byte_swap).?.data;
1480 return renderBuiltinCall(c, "@byteSwap", &.{payload});
1481 },
1482 .ceil => {
1483 const payload = node.castTag(.ceil).?.data;
1484 return renderBuiltinCall(c, "@ceil", &.{payload});
1485 },
1486 .cos => {
1487 const payload = node.castTag(.cos).?.data;
1488 return renderBuiltinCall(c, "@cos", &.{payload});
1489 },
1490 .sin => {
1491 const payload = node.castTag(.sin).?.data;
1492 return renderBuiltinCall(c, "@sin", &.{payload});
1493 },
1494 .exp => {
1495 const payload = node.castTag(.exp).?.data;
1496 return renderBuiltinCall(c, "@exp", &.{payload});
1497 },
1498 .exp2 => {
1499 const payload = node.castTag(.exp2).?.data;
1500 return renderBuiltinCall(c, "@exp2", &.{payload});
1501 },
1502 .exp10 => {
1503 const payload = node.castTag(.exp10).?.data;
1504 return renderBuiltinCall(c, "@exp10", &.{payload});
1505 },
1506 .abs => {
1507 const payload = node.castTag(.abs).?.data;
1508 return renderBuiltinCall(c, "@abs", &.{payload});
1509 },
1510 .log => {
1511 const payload = node.castTag(.log).?.data;
1512 return renderBuiltinCall(c, "@log", &.{payload});
1513 },
1514 .log2 => {
1515 const payload = node.castTag(.log2).?.data;
1516 return renderBuiltinCall(c, "@log2", &.{payload});
1517 },
1518 .log10 => {
1519 const payload = node.castTag(.log10).?.data;
1520 return renderBuiltinCall(c, "@log10", &.{payload});
1521 },
1522 .round => {
1523 const payload = node.castTag(.round).?.data;
1524 return renderBuiltinCall(c, "@round", &.{payload});
1525 },
1526 .sqrt => {
1527 const payload = node.castTag(.sqrt).?.data;
1528 return renderBuiltinCall(c, "@sqrt", &.{payload});
1529 },
1530 .trunc => {
1531 const payload = node.castTag(.trunc).?.data;
1532 return renderBuiltinCall(c, "@trunc", &.{payload});
1533 },
1534 .floor => {
1535 const payload = node.castTag(.floor).?.data;
1536 return renderBuiltinCall(c, "@floor", &.{payload});
1537 },
1538 .negate => return renderPrefixOp(c, node, .negation, .minus, "-"),
1539 .negate_wrap => return renderPrefixOp(c, node, .negation_wrap, .minus_percent, "-%"),
1540 .bit_not => return renderPrefixOp(c, node, .bit_not, .tilde, "~"),
1541 .not => return renderPrefixOp(c, node, .bool_not, .bang, "!"),
1542 .optional_type => return renderPrefixOp(c, node, .optional_type, .question_mark, "?"),
1543 .address_of => {
1544 const payload = node.castTag(.address_of).?.data;
1545
1546 const ampersand = try c.addToken(.ampersand, "&");
1547 const base = try renderNodeGrouped(c, payload);
1548 return c.addNode(.{
1549 .tag = .address_of,
1550 .main_token = ampersand,
1551 .data = .{
1552 .node = base,
1553 },
1554 });
1555 },
1556 .deref => {
1557 const payload = node.castTag(.deref).?.data;
1558 const operand = try renderNodeGrouped(c, payload);
1559 const deref_tok = try c.addToken(.period_asterisk, ".*");
1560 return c.addNode(.{
1561 .tag = .deref,
1562 .main_token = deref_tok,
1563 .data = .{
1564 .node = operand,
1565 },
1566 });
1567 },
1568 .unwrap => {
1569 const payload = node.castTag(.unwrap).?.data;
1570 const operand = try renderNodeGrouped(c, payload);
1571 const period = try c.addToken(.period, ".");
1572 const question_mark = try c.addToken(.question_mark, "?");
1573 return c.addNode(.{
1574 .tag = .unwrap_optional,
1575 .main_token = period,
1576 .data = .{ .node_and_token = .{
1577 operand, question_mark,
1578 } },
1579 });
1580 },
1581 .c_pointer, .single_pointer => {
1582 const payload = @as(*Payload.Pointer, @alignCast(@fieldParentPtr("base", node.ptr_otherwise))).data;
1583
1584 const main_token = if (node.tag() == .single_pointer)
1585 try c.addToken(.asterisk, "*")
1586 else blk: {
1587 const res = try c.addToken(.l_bracket, "[");
1588 _ = try c.addToken(.asterisk, "*");
1589 _ = try c.addIdentifier("c");
1590 _ = try c.addToken(.r_bracket, "]");
1591 break :blk res;
1592 };
1593 if (payload.is_const) _ = try c.addToken(.keyword_const, "const");
1594 if (payload.is_volatile) _ = try c.addToken(.keyword_volatile, "volatile");
1595 if (payload.is_allowzero) _ = try c.addToken(.keyword_allowzero, "allowzero");
1596 const elem_type = try renderNodeGrouped(c, payload.elem_type);
1597
1598 return c.addNode(.{
1599 .tag = .ptr_type_aligned,
1600 .main_token = main_token,
1601 .data = .{
1602 .opt_node_and_node = .{
1603 .none, // Align node
1604 elem_type,
1605 },
1606 },
1607 });
1608 },
1609 .add => return renderBinOpGrouped(c, node, .add, .plus, "+"),
1610 .add_assign => return renderBinOp(c, node, .assign_add, .plus_equal, "+="),
1611 .add_wrap => return renderBinOpGrouped(c, node, .add_wrap, .plus_percent, "+%"),
1612 .add_wrap_assign => return renderBinOp(c, node, .assign_add_wrap, .plus_percent_equal, "+%="),
1613 .sub => return renderBinOpGrouped(c, node, .sub, .minus, "-"),
1614 .sub_assign => return renderBinOp(c, node, .assign_sub, .minus_equal, "-="),
1615 .sub_wrap => return renderBinOpGrouped(c, node, .sub_wrap, .minus_percent, "-%"),
1616 .sub_wrap_assign => return renderBinOp(c, node, .assign_sub_wrap, .minus_percent_equal, "-%="),
1617 .mul => return renderBinOpGrouped(c, node, .mul, .asterisk, "*"),
1618 .mul_assign => return renderBinOp(c, node, .assign_mul, .asterisk_equal, "*="),
1619 .mul_wrap => return renderBinOpGrouped(c, node, .mul_wrap, .asterisk_percent, "*%"),
1620 .mul_wrap_assign => return renderBinOp(c, node, .assign_mul_wrap, .asterisk_percent_equal, "*%="),
1621 .div => return renderBinOpGrouped(c, node, .div, .slash, "/"),
1622 .div_assign => return renderBinOp(c, node, .assign_div, .slash_equal, "/="),
1623 .shl => return renderBinOpGrouped(c, node, .shl, .angle_bracket_angle_bracket_left, "<<"),
1624 .shl_assign => return renderBinOp(c, node, .assign_shl, .angle_bracket_angle_bracket_left_equal, "<<="),
1625 .shr => return renderBinOpGrouped(c, node, .shr, .angle_bracket_angle_bracket_right, ">>"),
1626 .shr_assign => return renderBinOp(c, node, .assign_shr, .angle_bracket_angle_bracket_right_equal, ">>="),
1627 .mod => return renderBinOpGrouped(c, node, .mod, .percent, "%"),
1628 .mod_assign => return renderBinOp(c, node, .assign_mod, .percent_equal, "%="),
1629 .@"and" => return renderBinOpGrouped(c, node, .bool_and, .keyword_and, "and"),
1630 .@"or" => return renderBinOpGrouped(c, node, .bool_or, .keyword_or, "or"),
1631 .less_than => return renderBinOpGrouped(c, node, .less_than, .angle_bracket_left, "<"),
1632 .less_than_equal => return renderBinOpGrouped(c, node, .less_or_equal, .angle_bracket_left_equal, "<="),
1633 .greater_than => return renderBinOpGrouped(c, node, .greater_than, .angle_bracket_right, ">="),
1634 .greater_than_equal => return renderBinOpGrouped(c, node, .greater_or_equal, .angle_bracket_right_equal, ">="),
1635 .equal => return renderBinOpGrouped(c, node, .equal_equal, .equal_equal, "=="),
1636 .not_equal => return renderBinOpGrouped(c, node, .bang_equal, .bang_equal, "!="),
1637 .bit_and => return renderBinOpGrouped(c, node, .bit_and, .ampersand, "&"),
1638 .bit_and_assign => return renderBinOp(c, node, .assign_bit_and, .ampersand_equal, "&="),
1639 .bit_or => return renderBinOpGrouped(c, node, .bit_or, .pipe, "|"),
1640 .bit_or_assign => return renderBinOp(c, node, .assign_bit_or, .pipe_equal, "|="),
1641 .bit_xor => return renderBinOpGrouped(c, node, .bit_xor, .caret, "^"),
1642 .bit_xor_assign => return renderBinOp(c, node, .assign_bit_xor, .caret_equal, "^="),
1643 .array_cat => return renderBinOp(c, node, .array_cat, .plus_plus, "++"),
1644 .ellipsis3 => return renderBinOpGrouped(c, node, .switch_range, .ellipsis3, "..."),
1645 .assign => return renderBinOp(c, node, .assign, .equal, "="),
1646 .empty_block => {
1647 const l_brace = try c.addToken(.l_brace, "{");
1648 _ = try c.addToken(.r_brace, "}");
1649 return c.addNode(.{
1650 .tag = .block_two,
1651 .main_token = l_brace,
1652 .data = .{ .opt_node_and_opt_node = .{
1653 .none, .none,
1654 } },
1655 });
1656 },
1657 .block_single => {
1658 const payload = node.castTag(.block_single).?.data;
1659 const l_brace = try c.addToken(.l_brace, "{");
1660
1661 const stmt = (try renderNodeOpt(c, payload)) orelse {
1662 _ = try c.addToken(.r_brace, "}");
1663 return c.addNode(.{
1664 .tag = .block_two,
1665 .main_token = l_brace,
1666 .data = .{ .opt_node_and_opt_node = .{
1667 .none, .none,
1668 } },
1669 });
1670 };
1671 try addSemicolonIfNeeded(c, payload);
1672
1673 _ = try c.addToken(.r_brace, "}");
1674 return c.addNode(.{
1675 .tag = .block_two_semicolon,
1676 .main_token = l_brace,
1677 .data = .{ .opt_node_and_opt_node = .{
1678 stmt.toOptional(), .none,
1679 } },
1680 });
1681 },
1682 .block => {
1683 const payload = node.castTag(.block).?.data;
1684 if (payload.label) |some| {
1685 _ = try c.addIdentifier(some);
1686 _ = try c.addToken(.colon, ":");
1687 }
1688 const l_brace = try c.addToken(.l_brace, "{");
1689
1690 var stmts = std.array_list.Managed(NodeIndex).init(c.gpa);
1691 defer stmts.deinit();
1692 for (payload.stmts) |stmt| {
1693 const res = (try renderNodeOpt(c, stmt)) orelse continue;
1694 try addSemicolonIfNeeded(c, stmt);
1695 try stmts.append(res);
1696 }
1697 const span = try c.listToSpan(stmts.items);
1698 _ = try c.addToken(.r_brace, "}");
1699
1700 const semicolon = c.tokens.items(.tag)[c.tokens.len - 2] == .semicolon;
1701 return c.addNode(.{
1702 .tag = if (semicolon) .block_semicolon else .block,
1703 .main_token = l_brace,
1704 .data = .{ .extra_range = span },
1705 });
1706 },
1707 .func => return renderFunc(c, node),
1708 .pub_inline_fn => return renderMacroFunc(c, node),
1709 .@"while" => {
1710 const payload = node.castTag(.@"while").?.data;
1711 const while_tok = try c.addToken(.keyword_while, "while");
1712 _ = try c.addToken(.l_paren, "(");
1713 const cond = try renderNode(c, payload.cond);
1714 _ = try c.addToken(.r_paren, ")");
1715
1716 const cont_expr_opt = if (payload.cont_expr) |some| blk: {
1717 _ = try c.addToken(.colon, ":");
1718 _ = try c.addToken(.l_paren, "(");
1719 const res = try renderNode(c, some);
1720 _ = try c.addToken(.r_paren, ")");
1721 break :blk res;
1722 } else null;
1723 const body = try renderNode(c, payload.body);
1724
1725 if (cont_expr_opt) |cont_expr| {
1726 return c.addNode(.{
1727 .tag = .while_cont,
1728 .main_token = while_tok,
1729 .data = .{ .node_and_extra = .{
1730 cond,
1731 try c.addExtra(std.zig.Ast.Node.WhileCont{
1732 .cont_expr = cont_expr,
1733 .then_expr = body,
1734 }),
1735 } },
1736 });
1737 } else {
1738 return c.addNode(.{
1739 .tag = .while_simple,
1740 .main_token = while_tok,
1741 .data = .{ .node_and_node = .{
1742 cond, body,
1743 } },
1744 });
1745 }
1746 },
1747 .while_true => {
1748 const payload = node.castTag(.while_true).?.data;
1749 const while_tok = try c.addToken(.keyword_while, "while");
1750 _ = try c.addToken(.l_paren, "(");
1751 const cond = try c.addNode(.{
1752 .tag = .identifier,
1753 .main_token = try c.addToken(.identifier, "true"),
1754 .data = undefined,
1755 });
1756 _ = try c.addToken(.r_paren, ")");
1757 const body = try renderNode(c, payload);
1758
1759 return c.addNode(.{
1760 .tag = .while_simple,
1761 .main_token = while_tok,
1762 .data = .{ .node_and_node = .{
1763 cond, body,
1764 } },
1765 });
1766 },
1767 .@"if" => {
1768 const payload = node.castTag(.@"if").?.data;
1769 const if_tok = try c.addToken(.keyword_if, "if");
1770 _ = try c.addToken(.l_paren, "(");
1771 const cond = try renderNode(c, payload.cond);
1772 _ = try c.addToken(.r_paren, ")");
1773
1774 const then_expr = try renderNode(c, payload.then);
1775 const else_node = payload.@"else" orelse return c.addNode(.{
1776 .tag = .if_simple,
1777 .main_token = if_tok,
1778 .data = .{ .node_and_node = .{
1779 cond, then_expr,
1780 } },
1781 });
1782 _ = try c.addToken(.keyword_else, "else");
1783 const else_expr = try renderNode(c, else_node);
1784
1785 return c.addNode(.{
1786 .tag = .@"if",
1787 .main_token = if_tok,
1788 .data = .{ .node_and_extra = .{
1789 cond,
1790 try c.addExtra(std.zig.Ast.Node.If{
1791 .then_expr = then_expr,
1792 .else_expr = else_expr,
1793 }),
1794 } },
1795 });
1796 },
1797 .if_not_break => {
1798 const payload = node.castTag(.if_not_break).?.data;
1799 const if_tok = try c.addToken(.keyword_if, "if");
1800 _ = try c.addToken(.l_paren, "(");
1801 const cond = try c.addNode(.{
1802 .tag = .bool_not,
1803 .main_token = try c.addToken(.bang, "!"),
1804 .data = .{
1805 .node = try renderNodeGrouped(c, payload),
1806 },
1807 });
1808 _ = try c.addToken(.r_paren, ")");
1809 const then_expr = try c.addNode(.{
1810 .tag = .@"break",
1811 .main_token = try c.addToken(.keyword_break, "break"),
1812 .data = .{ .opt_token_and_opt_node = .{
1813 .none, .none,
1814 } },
1815 });
1816
1817 return c.addNode(.{
1818 .tag = .if_simple,
1819 .main_token = if_tok,
1820 .data = .{ .node_and_node = .{
1821 cond, then_expr,
1822 } },
1823 });
1824 },
1825 .@"switch" => {
1826 const payload = node.castTag(.@"switch").?.data;
1827 const switch_tok = try c.addToken(.keyword_switch, "switch");
1828 _ = try c.addToken(.l_paren, "(");
1829 const cond = try renderNode(c, payload.cond);
1830 _ = try c.addToken(.r_paren, ")");
1831
1832 _ = try c.addToken(.l_brace, "{");
1833 var cases = try c.gpa.alloc(NodeIndex, payload.cases.len);
1834 defer c.gpa.free(cases);
1835 for (payload.cases, 0..) |case, i| {
1836 cases[i] = try renderNode(c, case);
1837 _ = try c.addToken(.comma, ",");
1838 }
1839 const span = try c.listToSpan(cases);
1840 _ = try c.addToken(.r_brace, "}");
1841 return c.addNode(.{
1842 .tag = .switch_comma,
1843 .main_token = switch_tok,
1844 .data = .{ .node_and_extra = .{
1845 cond,
1846 try c.addExtra(NodeSubRange{
1847 .start = span.start,
1848 .end = span.end,
1849 }),
1850 } },
1851 });
1852 },
1853 .switch_else => {
1854 const payload = node.castTag(.switch_else).?.data;
1855 _ = try c.addToken(.keyword_else, "else");
1856 return c.addNode(.{
1857 .tag = .switch_case_one,
1858 .main_token = try c.addToken(.equal_angle_bracket_right, "=>"),
1859 .data = .{ .opt_node_and_node = .{
1860 .none, try renderNode(c, payload),
1861 } },
1862 });
1863 },
1864 .switch_prong => {
1865 const payload = node.castTag(.switch_prong).?.data;
1866 var items = try c.gpa.alloc(NodeIndex, payload.cases.len);
1867 defer c.gpa.free(items);
1868
1869 for (payload.cases, 0..) |item, i| {
1870 if (i != 0) _ = try c.addToken(.comma, ",");
1871 items[i] = try renderNode(c, item);
1872 }
1873 _ = try c.addToken(.r_brace, "}");
1874 if (items.len < 2) {
1875 return c.addNode(.{
1876 .tag = .switch_case_one,
1877 .main_token = try c.addToken(.equal_angle_bracket_right, "=>"),
1878 .data = .{ .opt_node_and_node = .{
1879 if (payload.cases.len == 1) items[0].toOptional() else .none,
1880 try renderNode(c, payload.cond),
1881 } },
1882 });
1883 } else {
1884 return c.addNode(.{
1885 .tag = .switch_case,
1886 .main_token = try c.addToken(.equal_angle_bracket_right, "=>"),
1887 .data = .{ .extra_and_node = .{
1888 try c.addExtra(try c.listToSpan(items)),
1889 try renderNode(c, payload.cond),
1890 } },
1891 });
1892 }
1893 },
1894 .opaque_literal => {
1895 const opaque_tok = try c.addToken(.keyword_opaque, "opaque");
1896 _ = try c.addToken(.l_brace, "{");
1897 _ = try c.addToken(.r_brace, "}");
1898
1899 return c.addNode(.{
1900 .tag = .container_decl_two,
1901 .main_token = opaque_tok,
1902 .data = .{ .opt_node_and_opt_node = .{
1903 .none, .none,
1904 } },
1905 });
1906 },
1907 .array_access => {
1908 const payload = node.castTag(.array_access).?.data;
1909 const lhs = try renderNodeGrouped(c, payload.lhs);
1910 const l_bracket = try c.addToken(.l_bracket, "[");
1911 const index_expr = try renderNode(c, payload.rhs);
1912 _ = try c.addToken(.r_bracket, "]");
1913 return c.addNode(.{
1914 .tag = .array_access,
1915 .main_token = l_bracket,
1916 .data = .{ .node_and_node = .{
1917 lhs, index_expr,
1918 } },
1919 });
1920 },
1921 .array_type => {
1922 const payload = node.castTag(.array_type).?.data;
1923 return renderArrayType(c, payload.len, payload.elem_type);
1924 },
1925 .null_sentinel_array_type => {
1926 const payload = node.castTag(.null_sentinel_array_type).?.data;
1927 return renderNullSentinelArrayType(c, payload.len, payload.elem_type);
1928 },
1929 .array_filler => {
1930 const payload = node.castTag(.array_filler).?.data;
1931
1932 const type_expr = try renderArrayType(c, 1, payload.type);
1933 const l_brace = try c.addToken(.l_brace, "{");
1934 const val = try renderNode(c, payload.filler);
1935 _ = try c.addToken(.r_brace, "}");
1936
1937 const init = try c.addNode(.{
1938 .tag = .array_init_one,
1939 .main_token = l_brace,
1940 .data = .{ .node_and_node = .{
1941 type_expr, val,
1942 } },
1943 });
1944 return c.addNode(.{
1945 .tag = .array_cat,
1946 .main_token = try c.addToken(.asterisk_asterisk, "**"),
1947 .data = .{ .node_and_node = .{
1948 init,
1949 try c.addNode(.{
1950 .tag = .number_literal,
1951 .main_token = try c.addTokenFmt(.number_literal, "{d}", .{payload.count}),
1952 .data = undefined,
1953 }),
1954 } },
1955 });
1956 },
1957 .empty_array => {
1958 const payload = node.castTag(.empty_array).?.data;
1959
1960 const type_expr = try renderNode(c, payload);
1961 return renderArrayInit(c, type_expr, &.{});
1962 },
1963 .array_init => {
1964 const payload = node.castTag(.array_init).?.data;
1965 const type_expr = try renderNode(c, payload.cond);
1966 return renderArrayInit(c, type_expr, payload.cases);
1967 },
1968 .vector_zero_init => {
1969 const payload = node.castTag(.vector_zero_init).?.data;
1970 return renderBuiltinCall(c, "@splat", &.{payload});
1971 },
1972 .field_access => {
1973 const payload = node.castTag(.field_access).?.data;
1974 const lhs = try renderNodeGrouped(c, payload.lhs);
1975 return renderFieldAccess(c, lhs, payload.field_name);
1976 },
1977 .@"struct", .@"union", .@"opaque" => return renderContainer(c, node),
1978 .enum_constant => {
1979 const payload = node.castTag(.enum_constant).?.data;
1980
1981 if (payload.is_public) _ = try c.addToken(.keyword_pub, "pub");
1982 const const_tok = try c.addToken(.keyword_const, "const");
1983 _ = try c.addIdentifier(payload.name);
1984
1985 const type_node_opt = if (payload.type) |enum_const_type| blk: {
1986 _ = try c.addToken(.colon, ":");
1987 break :blk try renderNode(c, enum_const_type);
1988 } else null;
1989
1990 _ = try c.addToken(.equal, "=");
1991
1992 const init_node = try renderNode(c, payload.value);
1993 _ = try c.addToken(.semicolon, ";");
1994
1995 return c.addNode(.{
1996 .tag = .simple_var_decl,
1997 .main_token = const_tok,
1998 .data = .{ .opt_node_and_opt_node = .{
1999 .fromOptional(type_node_opt),
2000 init_node.toOptional(),
2001 } },
2002 });
2003 },
2004 .tuple => {
2005 const payload = node.castTag(.tuple).?.data;
2006 _ = try c.addToken(.period, ".");
2007 const l_brace = try c.addToken(.l_brace, "{");
2008 var inits = try c.gpa.alloc(NodeIndex, payload.len);
2009 defer c.gpa.free(inits);
2010
2011 for (payload, 0..) |init, i| {
2012 if (i != 0) _ = try c.addToken(.comma, ",");
2013 inits[i] = try renderNode(c, init);
2014 }
2015 _ = try c.addToken(.r_brace, "}");
2016 if (payload.len < 3) {
2017 return c.addNode(.{
2018 .tag = .array_init_dot_two,
2019 .main_token = l_brace,
2020 .data = .{ .opt_node_and_opt_node = .{
2021 if (inits.len >= 1) inits[0].toOptional() else .none,
2022 if (inits.len >= 2) inits[1].toOptional() else .none,
2023 } },
2024 });
2025 } else {
2026 return c.addNode(.{
2027 .tag = .array_init_dot,
2028 .main_token = l_brace,
2029 .data = .{ .extra_range = try c.listToSpan(inits) },
2030 });
2031 }
2032 },
2033 .container_init_dot => {
2034 const payload = node.castTag(.container_init_dot).?.data;
2035 _ = try c.addToken(.period, ".");
2036 const l_brace = try c.addToken(.l_brace, "{");
2037 var inits = try c.gpa.alloc(NodeIndex, payload.len);
2038 defer c.gpa.free(inits);
2039
2040 for (payload, 0..) |init, i| {
2041 _ = try c.addToken(.period, ".");
2042 _ = try c.addIdentifier(init.name);
2043 _ = try c.addToken(.equal, "=");
2044 inits[i] = try renderNode(c, init.value);
2045 _ = try c.addToken(.comma, ",");
2046 }
2047 _ = try c.addToken(.r_brace, "}");
2048
2049 if (payload.len < 3) {
2050 return c.addNode(.{
2051 .tag = .struct_init_dot_two_comma,
2052 .main_token = l_brace,
2053 .data = .{ .opt_node_and_opt_node = .{
2054 if (inits.len >= 1) inits[0].toOptional() else .none,
2055 if (inits.len >= 2) inits[1].toOptional() else .none,
2056 } },
2057 });
2058 } else {
2059 return c.addNode(.{
2060 .tag = .struct_init_dot_comma,
2061 .main_token = l_brace,
2062 .data = .{ .extra_range = try c.listToSpan(inits) },
2063 });
2064 }
2065 },
2066 .container_init => {
2067 const payload = node.castTag(.container_init).?.data;
2068 const lhs = try renderNode(c, payload.lhs);
2069
2070 const l_brace = try c.addToken(.l_brace, "{");
2071 var inits = try c.gpa.alloc(NodeIndex, payload.inits.len);
2072 defer c.gpa.free(inits);
2073
2074 for (payload.inits, 0..) |init, i| {
2075 _ = try c.addToken(.period, ".");
2076 _ = try c.addIdentifier(init.name);
2077 _ = try c.addToken(.equal, "=");
2078 inits[i] = try renderNode(c, init.value);
2079 _ = try c.addToken(.comma, ",");
2080 }
2081 _ = try c.addToken(.r_brace, "}");
2082
2083 switch (inits.len) {
2084 0 => return c.addNode(.{
2085 .tag = .struct_init_one,
2086 .main_token = l_brace,
2087 .data = .{ .node_and_opt_node = .{
2088 lhs, .none,
2089 } },
2090 }),
2091 1 => return c.addNode(.{
2092 .tag = .struct_init_one_comma,
2093 .main_token = l_brace,
2094 .data = .{ .node_and_opt_node = .{
2095 lhs, inits[0].toOptional(),
2096 } },
2097 }),
2098 else => return c.addNode(.{
2099 .tag = .struct_init_comma,
2100 .main_token = l_brace,
2101 .data = .{ .node_and_extra = .{
2102 lhs,
2103 try c.addExtra(try c.listToSpan(inits)),
2104 } },
2105 }),
2106 }
2107 },
2108 .static_assert => {
2109 const payload = node.castTag(.static_assert).?.data;
2110 const comptime_tok = try c.addToken(.keyword_comptime, "comptime");
2111 const l_brace = try c.addToken(.l_brace, "{");
2112
2113 const if_tok = try c.addToken(.keyword_if, "if");
2114 _ = try c.addToken(.l_paren, "(");
2115 const cond = try c.addNode(.{
2116 .tag = .bool_not,
2117 .main_token = try c.addToken(.bang, "!"),
2118 .data = .{
2119 .node = try renderNodeGrouped(c, payload.lhs),
2120 },
2121 });
2122 _ = try c.addToken(.r_paren, ")");
2123
2124 const compile_error_tok = try c.addToken(.builtin, "@compileError");
2125 _ = try c.addToken(.l_paren, "(");
2126 const err_msg = try renderNode(c, payload.rhs);
2127 _ = try c.addToken(.r_paren, ")");
2128 const compile_error = try c.addNode(.{
2129 .tag = .builtin_call_two,
2130 .main_token = compile_error_tok,
2131 .data = .{ .opt_node_and_opt_node = .{
2132 err_msg.toOptional(), .none,
2133 } },
2134 });
2135
2136 const if_node = try c.addNode(.{
2137 .tag = .if_simple,
2138 .main_token = if_tok,
2139 .data = .{ .node_and_node = .{
2140 cond, compile_error,
2141 } },
2142 });
2143 _ = try c.addToken(.semicolon, ";");
2144 _ = try c.addToken(.r_brace, "}");
2145 const block_node = try c.addNode(.{
2146 .tag = .block_two_semicolon,
2147 .main_token = l_brace,
2148 .data = .{ .opt_node_and_opt_node = .{
2149 if_node.toOptional(), .none,
2150 } },
2151 });
2152
2153 return c.addNode(.{
2154 .tag = .@"comptime",
2155 .main_token = comptime_tok,
2156 .data = .{
2157 .node = block_node,
2158 },
2159 });
2160 },
2161 .@"anytype" => unreachable, // Handled in renderParams
2162 }
2163}
2164
2165fn renderContainer(c: *Context, node: Node) !NodeIndex {
2166 const payload = @as(*Payload.Container, @alignCast(@fieldParentPtr("base", node.ptr_otherwise))).data;
2167 if (payload.layout == .@"packed")
2168 _ = try c.addToken(.keyword_packed, "packed")
2169 else if (payload.layout == .@"extern")
2170 _ = try c.addToken(.keyword_extern, "extern");
2171 const kind_tok = if (node.tag() == .@"struct")
2172 try c.addToken(.keyword_struct, "struct")
2173 else if (node.tag() == .@"union")
2174 try c.addToken(.keyword_union, "union")
2175 else if (node.tag() == .@"opaque")
2176 try c.addToken(.keyword_opaque, "opaque")
2177 else
2178 unreachable;
2179
2180 _ = try c.addToken(.l_brace, "{");
2181
2182 const num_decls = payload.decls.len;
2183 const total_members = payload.fields.len + num_decls;
2184 const members = try c.gpa.alloc(NodeIndex, total_members);
2185 defer c.gpa.free(members);
2186
2187 for (payload.fields, 0..) |field, i| {
2188 const name_tok = try c.addTokenFmt(.identifier, "{f}", .{std.zig.fmtIdFlags(field.name, .{ .allow_primitive = true })});
2189 _ = try c.addToken(.colon, ":");
2190 const type_expr = try renderNode(c, field.type);
2191
2192 const align_expr_opt = if (field.alignment) |alignment| blk: {
2193 _ = try c.addToken(.keyword_align, "align");
2194 _ = try c.addToken(.l_paren, "(");
2195 const align_expr = try c.addNode(.{
2196 .tag = .number_literal,
2197 .main_token = try c.addTokenFmt(.number_literal, "{d}", .{alignment}),
2198 .data = undefined,
2199 });
2200 _ = try c.addToken(.r_paren, ")");
2201 break :blk align_expr;
2202 } else null;
2203
2204 const value_expr_opt = if (field.default_value) |value| blk: {
2205 _ = try c.addToken(.equal, "=");
2206 break :blk try renderNode(c, value);
2207 } else null;
2208
2209 if (align_expr_opt) |align_expr| {
2210 if (value_expr_opt) |value_expr| {
2211 members[i] = try c.addNode(.{
2212 .tag = .container_field,
2213 .main_token = name_tok,
2214 .data = .{ .node_and_extra = .{
2215 type_expr,
2216 try c.addExtra(std.zig.Ast.Node.ContainerField{
2217 .align_expr = align_expr,
2218 .value_expr = value_expr,
2219 }),
2220 } },
2221 });
2222 } else {
2223 members[i] = try c.addNode(.{
2224 .tag = .container_field_align,
2225 .main_token = name_tok,
2226 .data = .{ .node_and_node = .{
2227 type_expr,
2228 align_expr,
2229 } },
2230 });
2231 }
2232 } else {
2233 members[i] = try c.addNode(.{
2234 .tag = .container_field_init,
2235 .main_token = name_tok,
2236 .data = .{ .node_and_opt_node = .{
2237 type_expr,
2238 .fromOptional(value_expr_opt),
2239 } },
2240 });
2241 }
2242 _ = try c.addToken(.comma, ",");
2243 }
2244 for (members[payload.fields.len..], payload.decls) |*member, decl| {
2245 member.* = try renderNode(c, decl);
2246 }
2247 const trailing = switch (c.tokens.items(.tag)[c.tokens.len - 1]) {
2248 .comma, .semicolon => true,
2249 else => false,
2250 };
2251 _ = try c.addToken(.r_brace, "}");
2252
2253 if (total_members == 0) {
2254 return c.addNode(.{
2255 .tag = .container_decl_two,
2256 .main_token = kind_tok,
2257 .data = .{ .opt_node_and_opt_node = .{
2258 .none, .none,
2259 } },
2260 });
2261 } else if (total_members <= 2) {
2262 return c.addNode(.{
2263 .tag = if (trailing) .container_decl_two_trailing else .container_decl_two,
2264 .main_token = kind_tok,
2265 .data = .{ .opt_node_and_opt_node = .{
2266 if (members.len >= 1) members[0].toOptional() else .none,
2267 if (members.len >= 2) members[1].toOptional() else .none,
2268 } },
2269 });
2270 } else {
2271 const span = try c.listToSpan(members);
2272 return c.addNode(.{
2273 .tag = if (trailing) .container_decl_trailing else .container_decl,
2274 .main_token = kind_tok,
2275 .data = .{ .extra_range = span },
2276 });
2277 }
2278}
2279
2280fn renderFieldAccess(c: *Context, lhs: NodeIndex, field_name: []const u8) !NodeIndex {
2281 return c.addNode(.{
2282 .tag = .field_access,
2283 .main_token = try c.addToken(.period, "."),
2284 .data = .{ .node_and_token = .{
2285 lhs, try c.addTokenFmt(.identifier, "{f}", .{std.zig.fmtIdFlags(field_name, .{ .allow_primitive = true })}),
2286 } },
2287 });
2288}
2289
2290fn renderArrayInit(c: *Context, lhs: NodeIndex, inits: []const Node) !NodeIndex {
2291 const l_brace = try c.addToken(.l_brace, "{");
2292 var rendered = try c.gpa.alloc(NodeIndex, inits.len);
2293 defer c.gpa.free(rendered);
2294
2295 for (inits, 0..) |init, i| {
2296 rendered[i] = try renderNode(c, init);
2297 _ = try c.addToken(.comma, ",");
2298 }
2299 _ = try c.addToken(.r_brace, "}");
2300 switch (inits.len) {
2301 0 => return c.addNode(.{
2302 .tag = .struct_init_one,
2303 .main_token = l_brace,
2304 .data = .{ .node_and_opt_node = .{
2305 lhs, .none,
2306 } },
2307 }),
2308 1 => return c.addNode(.{
2309 .tag = .array_init_one_comma,
2310 .main_token = l_brace,
2311 .data = .{ .node_and_node = .{
2312 lhs, rendered[0],
2313 } },
2314 }),
2315 else => return c.addNode(.{
2316 .tag = .array_init_comma,
2317 .main_token = l_brace,
2318 .data = .{ .node_and_extra = .{
2319 lhs,
2320 try c.addExtra(try c.listToSpan(rendered)),
2321 } },
2322 }),
2323 }
2324}
2325
2326fn renderArrayType(c: *Context, len: u64, elem_type: Node) !NodeIndex {
2327 const l_bracket = try c.addToken(.l_bracket, "[");
2328 const len_expr = try c.addNode(.{
2329 .tag = .number_literal,
2330 .main_token = try c.addTokenFmt(.number_literal, "{d}", .{len}),
2331 .data = undefined,
2332 });
2333 _ = try c.addToken(.r_bracket, "]");
2334 const elem_type_expr = try renderNode(c, elem_type);
2335 return c.addNode(.{
2336 .tag = .array_type,
2337 .main_token = l_bracket,
2338 .data = .{ .node_and_node = .{
2339 len_expr, elem_type_expr,
2340 } },
2341 });
2342}
2343
2344fn renderNullSentinelArrayType(c: *Context, len: u64, elem_type: Node) !NodeIndex {
2345 const l_bracket = try c.addToken(.l_bracket, "[");
2346 const len_expr = try c.addNode(.{
2347 .tag = .number_literal,
2348 .main_token = try c.addTokenFmt(.number_literal, "{d}", .{len}),
2349 .data = undefined,
2350 });
2351 _ = try c.addToken(.colon, ":");
2352
2353 const sentinel_expr = try c.addNode(.{
2354 .tag = .number_literal,
2355 .main_token = try c.addToken(.number_literal, "0"),
2356 .data = undefined,
2357 });
2358
2359 _ = try c.addToken(.r_bracket, "]");
2360 const elem_type_expr = try renderNode(c, elem_type);
2361 return c.addNode(.{
2362 .tag = .array_type_sentinel,
2363 .main_token = l_bracket,
2364 .data = .{ .node_and_extra = .{
2365 len_expr,
2366 try c.addExtra(std.zig.Ast.Node.ArrayTypeSentinel{
2367 .sentinel = sentinel_expr,
2368 .elem_type = elem_type_expr,
2369 }),
2370 } },
2371 });
2372}
2373
2374fn addSemicolonIfNeeded(c: *Context, node: Node) !void {
2375 switch (node.tag()) {
2376 .warning => unreachable,
2377 .var_decl, .var_simple, .arg_redecl, .alias, .block, .empty_block, .block_single, .@"switch", .wrapped_local, .mut_str => {},
2378 .while_true => {
2379 const payload = node.castTag(.while_true).?.data;
2380 return addSemicolonIfNotBlock(c, payload);
2381 },
2382 .@"while" => {
2383 const payload = node.castTag(.@"while").?.data;
2384 return addSemicolonIfNotBlock(c, payload.body);
2385 },
2386 .@"if" => {
2387 const payload = node.castTag(.@"if").?.data;
2388 if (payload.@"else") |some|
2389 return addSemicolonIfNeeded(c, some);
2390 return addSemicolonIfNotBlock(c, payload.then);
2391 },
2392 else => _ = try c.addToken(.semicolon, ";"),
2393 }
2394}
2395
2396fn addSemicolonIfNotBlock(c: *Context, node: Node) !void {
2397 switch (node.tag()) {
2398 .block, .empty_block, .block_single => {},
2399 else => _ = try c.addToken(.semicolon, ";"),
2400 }
2401}
2402
2403fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
2404 switch (node.tag()) {
2405 .declaration => unreachable,
2406 .null_literal,
2407 .undefined_literal,
2408 .true_literal,
2409 .false_literal,
2410 .return_void,
2411 .zero_literal,
2412 .one_literal,
2413 .void_type,
2414 .noreturn_type,
2415 .@"anytype",
2416 .div_trunc,
2417 .int_cast,
2418 .const_cast,
2419 .volatile_cast,
2420 .as,
2421 .truncate,
2422 .bit_cast,
2423 .float_cast,
2424 .int_from_float,
2425 .float_from_int,
2426 .ptr_from_int,
2427 .std_mem_zeroes,
2428 .int_from_ptr,
2429 .sizeof,
2430 .alignof,
2431 .typeof,
2432 .typeinfo,
2433 .vector,
2434 .std_mem_zeroinit,
2435 .integer_literal,
2436 .float_literal,
2437 .string_literal,
2438 .string_slice,
2439 .char_literal,
2440 .enum_literal,
2441 .identifier,
2442 .field_access,
2443 .ptr_cast,
2444 .type,
2445 .array_access,
2446 .align_cast,
2447 .optional_type,
2448 .c_pointer,
2449 .single_pointer,
2450 .unwrap,
2451 .deref,
2452 .not,
2453 .negate,
2454 .negate_wrap,
2455 .bit_not,
2456 .func,
2457 .call,
2458 .array_type,
2459 .null_sentinel_array_type,
2460 .int_from_bool,
2461 .div_exact,
2462 .offset_of,
2463 .shuffle,
2464 .builtin_extern,
2465 .wrapped_local,
2466 .mut_str,
2467 .helper_call,
2468 .helper_ref,
2469 .byte_swap,
2470 .ceil,
2471 .cos,
2472 .sin,
2473 .exp,
2474 .exp2,
2475 .exp10,
2476 .abs,
2477 .log,
2478 .log2,
2479 .log10,
2480 .round,
2481 .sqrt,
2482 .trunc,
2483 .floor,
2484 => {
2485 // no grouping needed
2486 return renderNode(c, node);
2487 },
2488
2489 .opaque_literal,
2490 .@"opaque",
2491 .empty_array,
2492 .block_single,
2493 .add,
2494 .add_wrap,
2495 .sub,
2496 .sub_wrap,
2497 .mul,
2498 .mul_wrap,
2499 .div,
2500 .shl,
2501 .shr,
2502 .mod,
2503 .@"and",
2504 .@"or",
2505 .less_than,
2506 .less_than_equal,
2507 .greater_than,
2508 .greater_than_equal,
2509 .equal,
2510 .not_equal,
2511 .bit_and,
2512 .bit_or,
2513 .bit_xor,
2514 .empty_block,
2515 .array_cat,
2516 .array_filler,
2517 .@"if",
2518 .@"struct",
2519 .@"union",
2520 .array_init,
2521 .vector_zero_init,
2522 .tuple,
2523 .container_init,
2524 .container_init_dot,
2525 .block,
2526 .address_of,
2527 => return c.addNode(.{
2528 .tag = .grouped_expression,
2529 .main_token = try c.addToken(.l_paren, "("),
2530 .data = .{ .node_and_token = .{
2531 try renderNode(c, node),
2532 try c.addToken(.r_paren, ")"),
2533 } },
2534 }),
2535 .ellipsis3,
2536 .switch_prong,
2537 .warning,
2538 .var_decl,
2539 .fail_decl,
2540 .arg_redecl,
2541 .alias,
2542 .var_simple,
2543 .pub_var_simple,
2544 .enum_constant,
2545 .@"while",
2546 .@"switch",
2547 .@"break",
2548 .break_val,
2549 .pub_inline_fn,
2550 .discard,
2551 .@"continue",
2552 .@"return",
2553 .@"comptime",
2554 .@"defer",
2555 .asm_simple,
2556 .while_true,
2557 .if_not_break,
2558 .switch_else,
2559 .add_assign,
2560 .add_wrap_assign,
2561 .sub_assign,
2562 .sub_wrap_assign,
2563 .mul_assign,
2564 .mul_wrap_assign,
2565 .div_assign,
2566 .shl_assign,
2567 .shr_assign,
2568 .mod_assign,
2569 .bit_and_assign,
2570 .bit_or_assign,
2571 .bit_xor_assign,
2572 .assign,
2573 .static_assert,
2574 .@"unreachable",
2575 => {
2576 // these should never appear in places where grouping might be needed.
2577 unreachable;
2578 },
2579 }
2580}
2581
2582fn renderPrefixOp(c: *Context, node: Node, tag: std.zig.Ast.Node.Tag, tok_tag: TokenTag, bytes: []const u8) !NodeIndex {
2583 const payload = @as(*Payload.UnOp, @alignCast(@fieldParentPtr("base", node.ptr_otherwise))).data;
2584 return c.addNode(.{
2585 .tag = tag,
2586 .main_token = try c.addToken(tok_tag, bytes),
2587 .data = .{
2588 .node = try renderNodeGrouped(c, payload),
2589 },
2590 });
2591}
2592
2593fn renderBinOpGrouped(c: *Context, node: Node, tag: std.zig.Ast.Node.Tag, tok_tag: TokenTag, bytes: []const u8) !NodeIndex {
2594 const payload = @as(*Payload.BinOp, @alignCast(@fieldParentPtr("base", node.ptr_otherwise))).data;
2595 const lhs = try renderNodeGrouped(c, payload.lhs);
2596 return c.addNode(.{
2597 .tag = tag,
2598 .main_token = try c.addToken(tok_tag, bytes),
2599 .data = .{ .node_and_node = .{
2600 lhs, try renderNodeGrouped(c, payload.rhs),
2601 } },
2602 });
2603}
2604
2605fn renderBinOp(c: *Context, node: Node, tag: std.zig.Ast.Node.Tag, tok_tag: TokenTag, bytes: []const u8) !NodeIndex {
2606 const payload = @as(*Payload.BinOp, @alignCast(@fieldParentPtr("base", node.ptr_otherwise))).data;
2607 const lhs = try renderNode(c, payload.lhs);
2608 return c.addNode(.{
2609 .tag = tag,
2610 .main_token = try c.addToken(tok_tag, bytes),
2611 .data = .{ .node_and_node = .{
2612 lhs, try renderNode(c, payload.rhs),
2613 } },
2614 });
2615}
2616
2617fn renderStdImport(c: *Context, parts: []const []const u8) !NodeIndex {
2618 const import_tok = try c.addToken(.builtin, "@import");
2619 _ = try c.addToken(.l_paren, "(");
2620 const std_tok = try c.addToken(.string_literal, "\"std\"");
2621 const std_node = try c.addNode(.{
2622 .tag = .string_literal,
2623 .main_token = std_tok,
2624 .data = undefined,
2625 });
2626 _ = try c.addToken(.r_paren, ")");
2627
2628 const import_node = try c.addNode(.{
2629 .tag = .builtin_call_two,
2630 .main_token = import_tok,
2631 .data = .{ .opt_node_and_opt_node = .{
2632 std_node.toOptional(), .none,
2633 } },
2634 });
2635
2636 var access_chain = import_node;
2637 for (parts) |part| {
2638 access_chain = try renderFieldAccess(c, access_chain, part);
2639 }
2640 return access_chain;
2641}
2642
2643fn renderCall(c: *Context, lhs: NodeIndex, args: []const Node) !NodeIndex {
2644 const lparen = try c.addToken(.l_paren, "(");
2645 const res = switch (args.len) {
2646 0 => try c.addNode(.{
2647 .tag = .call_one,
2648 .main_token = lparen,
2649 .data = .{ .node_and_opt_node = .{
2650 lhs, .none,
2651 } },
2652 }),
2653 1 => try c.addNode(.{
2654 .tag = .call_one,
2655 .main_token = lparen,
2656 .data = .{ .node_and_opt_node = .{
2657 lhs, (try renderNode(c, args[0])).toOptional(),
2658 } },
2659 }),
2660 else => blk: {
2661 var rendered = try c.gpa.alloc(NodeIndex, args.len);
2662 defer c.gpa.free(rendered);
2663
2664 for (args, 0..) |arg, i| {
2665 if (i != 0) _ = try c.addToken(.comma, ",");
2666 rendered[i] = try renderNode(c, arg);
2667 }
2668 const span = try c.listToSpan(rendered);
2669 break :blk try c.addNode(.{
2670 .tag = .call,
2671 .main_token = lparen,
2672 .data = .{ .node_and_extra = .{
2673 lhs, try c.addExtra(NodeSubRange{
2674 .start = span.start,
2675 .end = span.end,
2676 }),
2677 } },
2678 });
2679 },
2680 };
2681 _ = try c.addToken(.r_paren, ")");
2682 return res;
2683}
2684
2685fn renderBuiltinCall(c: *Context, builtin: []const u8, args: []const Node) !NodeIndex {
2686 const builtin_tok = try c.addToken(.builtin, builtin);
2687 _ = try c.addToken(.l_paren, "(");
2688 var arg_1: ?NodeIndex = null;
2689 var arg_2: ?NodeIndex = null;
2690 var arg_3: ?NodeIndex = null;
2691 var arg_4: ?NodeIndex = null;
2692 switch (args.len) {
2693 0 => {},
2694 1 => {
2695 arg_1 = try renderNode(c, args[0]);
2696 },
2697 2 => {
2698 arg_1 = try renderNode(c, args[0]);
2699 _ = try c.addToken(.comma, ",");
2700 arg_2 = try renderNode(c, args[1]);
2701 },
2702 4 => {
2703 arg_1 = try renderNode(c, args[0]);
2704 _ = try c.addToken(.comma, ",");
2705 arg_2 = try renderNode(c, args[1]);
2706 _ = try c.addToken(.comma, ",");
2707 arg_3 = try renderNode(c, args[2]);
2708 _ = try c.addToken(.comma, ",");
2709 arg_4 = try renderNode(c, args[3]);
2710 },
2711 else => unreachable, // expand this function as needed.
2712 }
2713
2714 _ = try c.addToken(.r_paren, ")");
2715 if (args.len <= 2) {
2716 return c.addNode(.{
2717 .tag = .builtin_call_two,
2718 .main_token = builtin_tok,
2719 .data = .{ .opt_node_and_opt_node = .{
2720 .fromOptional(arg_1), .fromOptional(arg_2),
2721 } },
2722 });
2723 } else {
2724 std.debug.assert(args.len == 4);
2725
2726 const params = try c.listToSpan(&.{ arg_1.?, arg_2.?, arg_3.?, arg_4.? });
2727 return c.addNode(.{
2728 .tag = .builtin_call,
2729 .main_token = builtin_tok,
2730 .data = .{ .extra_range = .{
2731 .start = params.start,
2732 .end = params.end,
2733 } },
2734 });
2735 }
2736}
2737
2738fn renderVar(c: *Context, node: Node) !NodeIndex {
2739 const payload = node.castTag(.var_decl).?.data;
2740 if (payload.is_pub) _ = try c.addToken(.keyword_pub, "pub");
2741 if (payload.is_extern) _ = try c.addToken(.keyword_extern, "extern");
2742 if (payload.is_export) _ = try c.addToken(.keyword_export, "export");
2743 if (payload.is_threadlocal) _ = try c.addToken(.keyword_threadlocal, "threadlocal");
2744 const mut_tok = if (payload.is_const)
2745 try c.addToken(.keyword_const, "const")
2746 else
2747 try c.addToken(.keyword_var, "var");
2748 _ = try c.addIdentifier(payload.name);
2749 _ = try c.addToken(.colon, ":");
2750 const type_node = try renderNode(c, payload.type);
2751
2752 const align_node_opt = if (payload.alignment) |some| blk: {
2753 _ = try c.addToken(.keyword_align, "align");
2754 _ = try c.addToken(.l_paren, "(");
2755 const res = try c.addNode(.{
2756 .tag = .number_literal,
2757 .main_token = try c.addTokenFmt(.number_literal, "{d}", .{some}),
2758 .data = undefined,
2759 });
2760 _ = try c.addToken(.r_paren, ")");
2761 break :blk res;
2762 } else null;
2763
2764 const section_node_opt = if (payload.linksection_string) |some| blk: {
2765 _ = try c.addToken(.keyword_linksection, "linksection");
2766 _ = try c.addToken(.l_paren, "(");
2767 const res = try c.addNode(.{
2768 .tag = .string_literal,
2769 .main_token = try c.addTokenFmt(.string_literal, "\"{f}\"", .{std.zig.fmtString(some)}),
2770 .data = undefined,
2771 });
2772 _ = try c.addToken(.r_paren, ")");
2773 break :blk res;
2774 } else null;
2775
2776 const init_node_opt = if (payload.init) |some| blk: {
2777 _ = try c.addToken(.equal, "=");
2778 break :blk try renderNode(c, some);
2779 } else null;
2780 _ = try c.addToken(.semicolon, ";");
2781
2782 if (section_node_opt) |section_node| {
2783 return c.addNode(.{
2784 .tag = .global_var_decl,
2785 .main_token = mut_tok,
2786 .data = .{ .extra_and_opt_node = .{
2787 try c.addExtra(std.zig.Ast.Node.GlobalVarDecl{
2788 .type_node = type_node.toOptional(),
2789 .align_node = .fromOptional(align_node_opt),
2790 .section_node = section_node.toOptional(),
2791 .addrspace_node = .none,
2792 }),
2793 .fromOptional(init_node_opt),
2794 } },
2795 });
2796 } else {
2797 if (align_node_opt) |align_node| {
2798 return c.addNode(.{
2799 .tag = .local_var_decl,
2800 .main_token = mut_tok,
2801 .data = .{ .extra_and_opt_node = .{
2802 try c.addExtra(std.zig.Ast.Node.LocalVarDecl{
2803 .type_node = type_node,
2804 .align_node = align_node,
2805 }),
2806 .fromOptional(init_node_opt),
2807 } },
2808 });
2809 } else {
2810 return c.addNode(.{
2811 .tag = .simple_var_decl,
2812 .main_token = mut_tok,
2813 .data = .{
2814 .opt_node_and_opt_node = .{
2815 type_node.toOptional(), // Type expression
2816 .fromOptional(init_node_opt), // Init expression
2817 },
2818 },
2819 });
2820 }
2821 }
2822}
2823
2824fn renderFunc(c: *Context, node: Node) !NodeIndex {
2825 const payload = node.castTag(.func).?.data;
2826 if (payload.is_pub) _ = try c.addToken(.keyword_pub, "pub");
2827 if (payload.is_extern) _ = try c.addToken(.keyword_extern, "extern");
2828 if (payload.is_export) _ = try c.addToken(.keyword_export, "export");
2829 if (payload.is_inline) _ = try c.addToken(.keyword_inline, "inline");
2830 const fn_token = try c.addToken(.keyword_fn, "fn");
2831 if (payload.name) |some| _ = try c.addIdentifier(some);
2832
2833 const params = try renderParams(c, payload.params, payload.is_var_args);
2834 defer params.deinit();
2835 var span: NodeSubRange = undefined;
2836 if (params.items.len > 1) span = try c.listToSpan(params.items);
2837
2838 const align_expr_opt = if (payload.alignment) |some| blk: {
2839 _ = try c.addToken(.keyword_align, "align");
2840 _ = try c.addToken(.l_paren, "(");
2841 const res = try c.addNode(.{
2842 .tag = .number_literal,
2843 .main_token = try c.addTokenFmt(.number_literal, "{d}", .{some}),
2844 .data = undefined,
2845 });
2846 _ = try c.addToken(.r_paren, ")");
2847 break :blk res;
2848 } else null;
2849
2850 const section_expr_opt = if (payload.linksection_string) |some| blk: {
2851 _ = try c.addToken(.keyword_linksection, "linksection");
2852 _ = try c.addToken(.l_paren, "(");
2853 const res = try c.addNode(.{
2854 .tag = .string_literal,
2855 .main_token = try c.addTokenFmt(.string_literal, "\"{f}\"", .{std.zig.fmtString(some)}),
2856 .data = undefined,
2857 });
2858 _ = try c.addToken(.r_paren, ")");
2859 break :blk res;
2860 } else null;
2861
2862 const callconv_expr_opt = if (payload.explicit_callconv) |some| blk: {
2863 _ = try c.addToken(.keyword_callconv, "callconv");
2864 _ = try c.addToken(.l_paren, "(");
2865 const cc_node = switch (some) {
2866 .c => cc_node: {
2867 _ = try c.addToken(.period, ".");
2868 break :cc_node try c.addNode(.{
2869 .tag = .enum_literal,
2870 .main_token = try c.addToken(.identifier, "c"),
2871 .data = undefined,
2872 });
2873 },
2874 .x86_64_sysv,
2875 .x86_64_win,
2876 .x86_stdcall,
2877 .x86_fastcall,
2878 .x86_thiscall,
2879 .x86_vectorcall,
2880 .x86_regcall,
2881 .aarch64_vfabi,
2882 .aarch64_sve_pcs,
2883 .arm_aapcs,
2884 .arm_aapcs_vfp,
2885 .m68k_rtd,
2886 .riscv_vector,
2887 => cc_node: {
2888 // .{ .foo = .{} }
2889 _ = try c.addToken(.period, ".");
2890 const outer_lbrace = try c.addToken(.l_brace, "{");
2891 _ = try c.addToken(.period, ".");
2892 _ = try c.addToken(.identifier, @tagName(some));
2893 _ = try c.addToken(.equal, "=");
2894 _ = try c.addToken(.period, ".");
2895 const inner_lbrace = try c.addToken(.l_brace, "{");
2896 _ = try c.addToken(.r_brace, "}");
2897 _ = try c.addToken(.r_brace, "}");
2898 break :cc_node try c.addNode(.{
2899 .tag = .struct_init_dot_two,
2900 .main_token = outer_lbrace,
2901 .data = .{ .opt_node_and_opt_node = .{
2902 (try c.addNode(.{
2903 .tag = .struct_init_dot_two,
2904 .main_token = inner_lbrace,
2905 .data = .{ .opt_node_and_opt_node = .{
2906 .none, .none,
2907 } },
2908 })).toOptional(),
2909 .none,
2910 } },
2911 });
2912 },
2913 };
2914 _ = try c.addToken(.r_paren, ")");
2915 break :blk cc_node;
2916 } else null;
2917
2918 const return_type_expr = try renderNode(c, payload.return_type);
2919
2920 const fn_proto = try blk: {
2921 if (align_expr_opt == null and section_expr_opt == null and callconv_expr_opt == null) {
2922 if (params.items.len < 2)
2923 break :blk c.addNode(.{
2924 .tag = .fn_proto_simple,
2925 .main_token = fn_token,
2926 .data = .{ .opt_node_and_opt_node = .{
2927 if (params.items.len == 1) params.items[0].toOptional() else .none,
2928 return_type_expr.toOptional(),
2929 } },
2930 })
2931 else
2932 break :blk c.addNode(.{
2933 .tag = .fn_proto_multi,
2934 .main_token = fn_token,
2935 .data = .{ .extra_and_opt_node = .{
2936 try c.addExtra(span),
2937 return_type_expr.toOptional(),
2938 } },
2939 });
2940 }
2941 if (params.items.len < 2)
2942 break :blk c.addNode(.{
2943 .tag = .fn_proto_one,
2944 .main_token = fn_token,
2945 .data = .{
2946 .extra_and_opt_node = .{
2947 try c.addExtra(std.zig.Ast.Node.FnProtoOne{
2948 .param = if (params.items.len == 1) params.items[0].toOptional() else .none,
2949 .align_expr = .fromOptional(align_expr_opt),
2950 .addrspace_expr = .none, // TODO
2951 .section_expr = .fromOptional(section_expr_opt),
2952 .callconv_expr = .fromOptional(callconv_expr_opt),
2953 }),
2954 return_type_expr.toOptional(),
2955 },
2956 },
2957 })
2958 else
2959 break :blk c.addNode(.{
2960 .tag = .fn_proto,
2961 .main_token = fn_token,
2962 .data = .{
2963 .extra_and_opt_node = .{
2964 try c.addExtra(std.zig.Ast.Node.FnProto{
2965 .params_start = span.start,
2966 .params_end = span.end,
2967 .align_expr = .fromOptional(align_expr_opt),
2968 .addrspace_expr = .none, // TODO
2969 .section_expr = .fromOptional(section_expr_opt),
2970 .callconv_expr = .fromOptional(callconv_expr_opt),
2971 }),
2972 return_type_expr.toOptional(),
2973 },
2974 },
2975 });
2976 };
2977
2978 const payload_body = payload.body orelse {
2979 if (payload.is_extern) {
2980 _ = try c.addToken(.semicolon, ";");
2981 }
2982 return fn_proto;
2983 };
2984 const body = try renderNode(c, payload_body);
2985 return c.addNode(.{
2986 .tag = .fn_decl,
2987 .main_token = fn_token,
2988 .data = .{ .node_and_node = .{
2989 fn_proto, body,
2990 } },
2991 });
2992}
2993
2994fn renderMacroFunc(c: *Context, node: Node) !NodeIndex {
2995 const payload = node.castTag(.pub_inline_fn).?.data;
2996 _ = try c.addToken(.keyword_pub, "pub");
2997 _ = try c.addToken(.keyword_inline, "inline");
2998 const fn_token = try c.addToken(.keyword_fn, "fn");
2999 _ = try c.addIdentifier(payload.name);
3000
3001 const params = try renderParams(c, payload.params, false);
3002 defer params.deinit();
3003 var span: NodeSubRange = undefined;
3004 if (params.items.len > 1) span = try c.listToSpan(params.items);
3005
3006 const return_type_expr = try renderNodeGrouped(c, payload.return_type);
3007
3008 const fn_proto = blk: {
3009 if (params.items.len < 2) {
3010 break :blk try c.addNode(.{
3011 .tag = .fn_proto_simple,
3012 .main_token = fn_token,
3013 .data = .{ .opt_node_and_opt_node = .{
3014 if (params.items.len == 1) params.items[0].toOptional() else .none,
3015 return_type_expr.toOptional(),
3016 } },
3017 });
3018 } else {
3019 break :blk try c.addNode(.{
3020 .tag = .fn_proto_multi,
3021 .main_token = fn_token,
3022 .data = .{ .extra_and_opt_node = .{
3023 try c.addExtra(span),
3024 return_type_expr.toOptional(),
3025 } },
3026 });
3027 }
3028 };
3029 return c.addNode(.{
3030 .tag = .fn_decl,
3031 .main_token = fn_token,
3032 .data = .{ .node_and_node = .{
3033 fn_proto, try renderNode(c, payload.body),
3034 } },
3035 });
3036}
3037
3038fn renderParams(c: *Context, params: []Payload.Param, is_var_args: bool) !std.array_list.Managed(NodeIndex) {
3039 _ = try c.addToken(.l_paren, "(");
3040 var rendered = try std.array_list.Managed(NodeIndex).initCapacity(c.gpa, @max(params.len, 1));
3041 errdefer rendered.deinit();
3042
3043 for (params, 0..) |param, i| {
3044 if (i != 0) _ = try c.addToken(.comma, ",");
3045 if (param.is_noalias) _ = try c.addToken(.keyword_noalias, "noalias");
3046 if (param.name) |some| {
3047 _ = try c.addIdentifier(some);
3048 _ = try c.addToken(.colon, ":");
3049 }
3050 if (param.type.tag() == .@"anytype") {
3051 _ = try c.addToken(.keyword_anytype, "anytype");
3052 continue;
3053 }
3054 rendered.appendAssumeCapacity(try renderNode(c, param.type));
3055 }
3056 if (is_var_args) {
3057 if (params.len != 0) _ = try c.addToken(.comma, ",");
3058 _ = try c.addToken(.ellipsis3, "...");
3059 }
3060 _ = try c.addToken(.r_paren, ")");
3061
3062 return rendered;
3063}
lib/compiler/translate-c/src/builtins.zig deleted-76
...@@ -1,76 +0,0 @@
1const std = @import("std");
2
3const ast = @import("ast.zig");
4
5/// All builtins need to have a source so that macros can reference them
6/// but for some it is possible to directly call an equivalent Zig builtin
7/// which is preferrable.
8pub const Builtin = struct {
9 /// The name of the builtin in `c_builtins.zig`.
10 name: []const u8,
11 tag: ?ast.Node.Tag = null,
12};
13
14pub const map = std.StaticStringMap(Builtin).initComptime([_]struct { []const u8, Builtin }{
15 .{ "__builtin_abs", .{ .name = "abs" } },
16 .{ "__builtin_assume", .{ .name = "assume" } },
17 .{ "__builtin_bswap16", .{ .name = "bswap16", .tag = .byte_swap } },
18 .{ "__builtin_bswap32", .{ .name = "bswap32", .tag = .byte_swap } },
19 .{ "__builtin_bswap64", .{ .name = "bswap64", .tag = .byte_swap } },
20 .{ "__builtin_ceilf", .{ .name = "ceilf", .tag = .ceil } },
21 .{ "__builtin_ceil", .{ .name = "ceil", .tag = .ceil } },
22 .{ "__builtin_clz", .{ .name = "clz" } },
23 .{ "__builtin_constant_p", .{ .name = "constant_p" } },
24 .{ "__builtin_cosf", .{ .name = "cosf", .tag = .cos } },
25 .{ "__builtin_cos", .{ .name = "cos", .tag = .cos } },
26 .{ "__builtin_ctz", .{ .name = "ctz" } },
27 .{ "__builtin_exp2f", .{ .name = "exp2f", .tag = .exp2 } },
28 .{ "__builtin_exp2", .{ .name = "exp2", .tag = .exp2 } },
29 .{ "__builtin_expf", .{ .name = "expf", .tag = .exp } },
30 .{ "__builtin_exp", .{ .name = "exp", .tag = .exp } },
31 .{ "__builtin_expect", .{ .name = "expect" } },
32 .{ "__builtin_fabsf", .{ .name = "fabsf", .tag = .abs } },
33 .{ "__builtin_fabs", .{ .name = "fabs", .tag = .abs } },
34 .{ "__builtin_floorf", .{ .name = "floorf", .tag = .floor } },
35 .{ "__builtin_floor", .{ .name = "floor", .tag = .floor } },
36 .{ "__builtin_huge_valf", .{ .name = "huge_valf" } },
37 .{ "__builtin_inff", .{ .name = "inff" } },
38 .{ "__builtin_isinf_sign", .{ .name = "isinf_sign" } },
39 .{ "__builtin_isinf", .{ .name = "isinf" } },
40 .{ "__builtin_isnan", .{ .name = "isnan" } },
41 .{ "__builtin_labs", .{ .name = "labs" } },
42 .{ "__builtin_llabs", .{ .name = "llabs" } },
43 .{ "__builtin_log10f", .{ .name = "log10f", .tag = .log10 } },
44 .{ "__builtin_log10", .{ .name = "log10", .tag = .log10 } },
45 .{ "__builtin_log2f", .{ .name = "log2f", .tag = .log2 } },
46 .{ "__builtin_log2", .{ .name = "log2", .tag = .log2 } },
47 .{ "__builtin_logf", .{ .name = "logf", .tag = .log } },
48 .{ "__builtin_log", .{ .name = "log", .tag = .log } },
49 .{ "__builtin___memcpy_chk", .{ .name = "memcpy_chk" } },
50 .{ "__builtin_memcpy", .{ .name = "memcpy" } },
51 .{ "__builtin___memset_chk", .{ .name = "memset_chk" } },
52 .{ "__builtin_memset", .{ .name = "memset" } },
53 .{ "__builtin_mul_overflow", .{ .name = "mul_overflow" } },
54 .{ "__builtin_nanf", .{ .name = "nanf" } },
55 .{ "__builtin_object_size", .{ .name = "object_size" } },
56 .{ "__builtin_popcount", .{ .name = "popcount" } },
57 .{ "__builtin_roundf", .{ .name = "roundf", .tag = .round } },
58 .{ "__builtin_round", .{ .name = "round", .tag = .round } },
59 .{ "__builtin_signbitf", .{ .name = "signbitf" } },
60 .{ "__builtin_signbit", .{ .name = "signbit" } },
61 .{ "__builtin_sinf", .{ .name = "sinf", .tag = .sin } },
62 .{ "__builtin_sin", .{ .name = "sin", .tag = .sin } },
63 .{ "__builtin_sqrtf", .{ .name = "sqrtf", .tag = .sqrt } },
64 .{ "__builtin_sqrt", .{ .name = "sqrt", .tag = .sqrt } },
65 .{ "__builtin_strcmp", .{ .name = "strcmp" } },
66 .{ "__builtin_strlen", .{ .name = "strlen" } },
67 .{ "__builtin_truncf", .{ .name = "truncf", .tag = .trunc } },
68 .{ "__builtin_trunc", .{ .name = "trunc", .tag = .trunc } },
69 .{ "__builtin_unreachable", .{ .name = "unreachable", .tag = .@"unreachable" } },
70 .{ "__has_builtin", .{ .name = "has_builtin" } },
71
72 // __builtin_alloca_with_align is not currently implemented.
73 // It is used in a run and a translate test to ensure that non-implemented
74 // builtins are correctly demoted. If you implement __builtin_alloca_with_align,
75 // please update the tests to use a different non-implemented builtin.
76});
lib/compiler/translate-c/src/helpers.zig deleted-327
...@@ -1,327 +0,0 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const testing = std.testing;
4const math = std.math;
5
6const helpers = @import("helpers");
7
8const cast = helpers.cast;
9
10test cast {
11 var i = @as(i64, 10);
12
13 try testing.expect(cast(*u8, 16) == @as(*u8, @ptrFromInt(16)));
14 try testing.expect(cast(*u64, &i).* == @as(u64, 10));
15 try testing.expect(cast(*i64, @as(?*align(1) i64, &i)) == &i);
16
17 try testing.expect(cast(?*u8, 2) == @as(*u8, @ptrFromInt(2)));
18 try testing.expect(cast(?*i64, @as(*align(1) i64, &i)) == &i);
19 try testing.expect(cast(?*i64, @as(?*align(1) i64, &i)) == &i);
20
21 try testing.expectEqual(@as(u32, 4), cast(u32, @as(*u32, @ptrFromInt(4))));
22 try testing.expectEqual(@as(u32, 4), cast(u32, @as(?*u32, @ptrFromInt(4))));
23 try testing.expectEqual(@as(u32, 10), cast(u32, @as(u64, 10)));
24
25 try testing.expectEqual(@as(i32, @bitCast(@as(u32, 0x8000_0000))), cast(i32, @as(u32, 0x8000_0000)));
26
27 try testing.expectEqual(@as(*u8, @ptrFromInt(2)), cast(*u8, @as(*const u8, @ptrFromInt(2))));
28 try testing.expectEqual(@as(*u8, @ptrFromInt(2)), cast(*u8, @as(*volatile u8, @ptrFromInt(2))));
29
30 try testing.expectEqual(@as(?*anyopaque, @ptrFromInt(2)), cast(?*anyopaque, @as(*u8, @ptrFromInt(2))));
31
32 var foo: c_int = -1;
33 _ = &foo;
34 try testing.expect(cast(*anyopaque, -1) == @as(*anyopaque, @ptrFromInt(@as(usize, @bitCast(@as(isize, -1))))));
35 try testing.expect(cast(*anyopaque, foo) == @as(*anyopaque, @ptrFromInt(@as(usize, @bitCast(@as(isize, -1))))));
36 try testing.expect(cast(?*anyopaque, -1) == @as(?*anyopaque, @ptrFromInt(@as(usize, @bitCast(@as(isize, -1))))));
37 try testing.expect(cast(?*anyopaque, foo) == @as(?*anyopaque, @ptrFromInt(@as(usize, @bitCast(@as(isize, -1))))));
38
39 const FnPtr = ?*align(1) const fn (*anyopaque) void;
40 try testing.expect(cast(FnPtr, 0) == @as(FnPtr, @ptrFromInt(@as(usize, 0))));
41 try testing.expect(cast(FnPtr, foo) == @as(FnPtr, @ptrFromInt(@as(usize, @bitCast(@as(isize, -1))))));
42
43 const complexFunction = struct {
44 fn f(_: ?*anyopaque, _: c_uint, _: ?*const fn (?*anyopaque) callconv(.c) c_uint, _: ?*anyopaque, _: c_uint, _: [*c]c_uint) callconv(.c) usize {
45 return 0;
46 }
47 }.f;
48
49 const SDL_FunctionPointer = ?*const fn () callconv(.c) void;
50 const fn_ptr = cast(SDL_FunctionPointer, complexFunction);
51 try testing.expect(fn_ptr != null);
52}
53
54const sizeof = helpers.sizeof;
55
56test sizeof {
57 const S = extern struct { a: u32 };
58
59 const ptr_size = @sizeOf(*anyopaque);
60
61 try testing.expect(sizeof(u32) == 4);
62 try testing.expect(sizeof(@as(u32, 2)) == 4);
63 try testing.expect(sizeof(2) == @sizeOf(c_int));
64
65 try testing.expect(sizeof(2.0) == @sizeOf(f64));
66
67 try testing.expect(sizeof(S) == 4);
68
69 try testing.expect(sizeof([_]u32{ 4, 5, 6 }) == 12);
70 try testing.expect(sizeof([3]u32) == 12);
71 try testing.expect(sizeof([3:0]u32) == 16);
72 try testing.expect(sizeof(&[_]u32{ 4, 5, 6 }) == ptr_size);
73
74 try testing.expect(sizeof(*u32) == ptr_size);
75 try testing.expect(sizeof([*]u32) == ptr_size);
76 try testing.expect(sizeof([*c]u32) == ptr_size);
77 try testing.expect(sizeof(?*u32) == ptr_size);
78 try testing.expect(sizeof(?[*]u32) == ptr_size);
79 try testing.expect(sizeof(*anyopaque) == ptr_size);
80 try testing.expect(sizeof(*void) == ptr_size);
81 try testing.expect(sizeof(null) == ptr_size);
82
83 try testing.expect(sizeof("foobar") == 7);
84 try testing.expect(sizeof(&[_:0]u16{ 'f', 'o', 'o', 'b', 'a', 'r' }) == 14);
85 try testing.expect(sizeof(*const [4:0]u8) == 5);
86 try testing.expect(sizeof(*[4:0]u8) == ptr_size);
87 try testing.expect(sizeof([*]const [4:0]u8) == ptr_size);
88 try testing.expect(sizeof(*const *const [4:0]u8) == ptr_size);
89 try testing.expect(sizeof(*const [4]u8) == ptr_size);
90
91 if (false) { // TODO
92 try testing.expect(sizeof(&sizeof) == @sizeOf(@TypeOf(&sizeof)));
93 try testing.expect(sizeof(sizeof) == 1);
94 }
95
96 try testing.expect(sizeof(void) == 1);
97 try testing.expect(sizeof(anyopaque) == 1);
98}
99
100const promoteIntLiteral = helpers.promoteIntLiteral;
101
102test promoteIntLiteral {
103 const signed_hex = promoteIntLiteral(c_int, math.maxInt(c_int) + 1, .hex);
104 try testing.expectEqual(c_uint, @TypeOf(signed_hex));
105
106 if (math.maxInt(c_longlong) == math.maxInt(c_int)) return;
107
108 const signed_decimal = promoteIntLiteral(c_int, math.maxInt(c_int) + 1, .decimal);
109 const unsigned = promoteIntLiteral(c_uint, math.maxInt(c_uint) + 1, .hex);
110
111 if (math.maxInt(c_long) > math.maxInt(c_int)) {
112 try testing.expectEqual(c_long, @TypeOf(signed_decimal));
113 try testing.expectEqual(c_ulong, @TypeOf(unsigned));
114 } else {
115 try testing.expectEqual(c_longlong, @TypeOf(signed_decimal));
116 try testing.expectEqual(c_ulonglong, @TypeOf(unsigned));
117 }
118}
119
120const shuffleVectorIndex = helpers.shuffleVectorIndex;
121
122test shuffleVectorIndex {
123 const vector_len: usize = 4;
124
125 _ = shuffleVectorIndex(-1, vector_len);
126
127 try testing.expect(shuffleVectorIndex(0, vector_len) == 0);
128 try testing.expect(shuffleVectorIndex(1, vector_len) == 1);
129 try testing.expect(shuffleVectorIndex(2, vector_len) == 2);
130 try testing.expect(shuffleVectorIndex(3, vector_len) == 3);
131
132 try testing.expect(shuffleVectorIndex(4, vector_len) == -1);
133 try testing.expect(shuffleVectorIndex(5, vector_len) == -2);
134 try testing.expect(shuffleVectorIndex(6, vector_len) == -3);
135 try testing.expect(shuffleVectorIndex(7, vector_len) == -4);
136}
137
138const FlexibleArrayType = helpers.FlexibleArrayType;
139
140test FlexibleArrayType {
141 const Container = extern struct {
142 size: usize,
143 };
144
145 try testing.expectEqual(FlexibleArrayType(*Container, c_int), [*c]c_int);
146 try testing.expectEqual(FlexibleArrayType(*const Container, c_int), [*c]const c_int);
147 try testing.expectEqual(FlexibleArrayType(*volatile Container, c_int), [*c]volatile c_int);
148 try testing.expectEqual(FlexibleArrayType(*const volatile Container, c_int), [*c]const volatile c_int);
149}
150
151const signedRemainder = helpers.signedRemainder;
152
153test signedRemainder {
154 // TODO add test
155 return error.SkipZigTest;
156}
157
158const ArithmeticConversion = helpers.ArithmeticConversion;
159
160test ArithmeticConversion {
161 // Promotions not necessarily the same for other platforms
162 if (builtin.target.cpu.arch != .x86_64 or builtin.target.os.tag != .linux) return error.SkipZigTest;
163
164 const Test = struct {
165 /// Order of operands should not matter for arithmetic conversions
166 fn checkPromotion(comptime A: type, comptime B: type, comptime Expected: type) !void {
167 try std.testing.expect(ArithmeticConversion(A, B) == Expected);
168 try std.testing.expect(ArithmeticConversion(B, A) == Expected);
169 }
170 };
171
172 try Test.checkPromotion(c_longdouble, c_int, c_longdouble);
173 try Test.checkPromotion(c_int, f64, f64);
174 try Test.checkPromotion(f32, bool, f32);
175
176 try Test.checkPromotion(bool, c_short, c_int);
177 try Test.checkPromotion(c_int, c_int, c_int);
178 try Test.checkPromotion(c_short, c_int, c_int);
179
180 try Test.checkPromotion(c_int, c_long, c_long);
181
182 try Test.checkPromotion(c_ulonglong, c_uint, c_ulonglong);
183
184 try Test.checkPromotion(c_uint, c_int, c_uint);
185
186 try Test.checkPromotion(c_uint, c_long, c_long);
187
188 try Test.checkPromotion(c_ulong, c_longlong, c_ulonglong);
189
190 // stdint.h
191 try Test.checkPromotion(u8, i8, c_int);
192 try Test.checkPromotion(u16, i16, c_int);
193 try Test.checkPromotion(i32, c_int, c_int);
194 try Test.checkPromotion(u32, c_int, c_uint);
195 try Test.checkPromotion(i64, c_int, c_long);
196 try Test.checkPromotion(u64, c_int, c_ulong);
197 try Test.checkPromotion(isize, c_int, c_long);
198 try Test.checkPromotion(usize, c_int, c_ulong);
199}
200
201const F_SUFFIX = helpers.F_SUFFIX;
202
203test F_SUFFIX {
204 try testing.expect(@TypeOf(F_SUFFIX(1)) == f32);
205}
206
207const U_SUFFIX = helpers.U_SUFFIX;
208
209test U_SUFFIX {
210 try testing.expect(@TypeOf(U_SUFFIX(1)) == c_uint);
211 if (math.maxInt(c_ulong) > math.maxInt(c_uint)) {
212 try testing.expect(@TypeOf(U_SUFFIX(math.maxInt(c_uint) + 1)) == c_ulong);
213 }
214 if (math.maxInt(c_ulonglong) > math.maxInt(c_ulong)) {
215 try testing.expect(@TypeOf(U_SUFFIX(math.maxInt(c_ulong) + 1)) == c_ulonglong);
216 }
217}
218
219const L_SUFFIX = helpers.L_SUFFIX;
220
221test L_SUFFIX {
222 try testing.expect(@TypeOf(L_SUFFIX(1)) == c_long);
223 if (math.maxInt(c_long) > math.maxInt(c_int)) {
224 try testing.expect(@TypeOf(L_SUFFIX(math.maxInt(c_int) + 1)) == c_long);
225 }
226 if (math.maxInt(c_longlong) > math.maxInt(c_long)) {
227 try testing.expect(@TypeOf(L_SUFFIX(math.maxInt(c_long) + 1)) == c_longlong);
228 }
229}
230const UL_SUFFIX = helpers.UL_SUFFIX;
231
232test UL_SUFFIX {
233 try testing.expect(@TypeOf(UL_SUFFIX(1)) == c_ulong);
234 if (math.maxInt(c_ulonglong) > math.maxInt(c_ulong)) {
235 try testing.expect(@TypeOf(UL_SUFFIX(math.maxInt(c_ulong) + 1)) == c_ulonglong);
236 }
237}
238const LL_SUFFIX = helpers.LL_SUFFIX;
239
240test LL_SUFFIX {
241 try testing.expect(@TypeOf(LL_SUFFIX(1)) == c_longlong);
242}
243const ULL_SUFFIX = helpers.ULL_SUFFIX;
244
245test ULL_SUFFIX {
246 try testing.expect(@TypeOf(ULL_SUFFIX(1)) == c_ulonglong);
247}
248
249test "Extended C ABI casting" {
250 if (math.maxInt(c_long) > math.maxInt(c_char)) {
251 try testing.expect(@TypeOf(L_SUFFIX(@as(c_char, math.maxInt(c_char) - 1))) == c_long); // c_char
252 }
253 if (math.maxInt(c_long) > math.maxInt(c_short)) {
254 try testing.expect(@TypeOf(L_SUFFIX(@as(c_short, math.maxInt(c_short) - 1))) == c_long); // c_short
255 }
256
257 if (math.maxInt(c_long) > math.maxInt(c_ushort)) {
258 try testing.expect(@TypeOf(L_SUFFIX(@as(c_ushort, math.maxInt(c_ushort) - 1))) == c_long); //c_ushort
259 }
260
261 if (math.maxInt(c_long) > math.maxInt(c_int)) {
262 try testing.expect(@TypeOf(L_SUFFIX(@as(c_int, math.maxInt(c_int) - 1))) == c_long); // c_int
263 }
264
265 if (math.maxInt(c_long) > math.maxInt(c_uint)) {
266 try testing.expect(@TypeOf(L_SUFFIX(@as(c_uint, math.maxInt(c_uint) - 1))) == c_long); // c_uint
267 try testing.expect(@TypeOf(L_SUFFIX(math.maxInt(c_uint) + 1)) == c_long); // comptime_int -> c_long
268 }
269
270 if (math.maxInt(c_longlong) > math.maxInt(c_long)) {
271 try testing.expect(@TypeOf(L_SUFFIX(@as(c_long, math.maxInt(c_long) - 1))) == c_long); // c_long
272 try testing.expect(@TypeOf(L_SUFFIX(math.maxInt(c_long) + 1)) == c_longlong); // comptime_int -> c_longlong
273 }
274}
275
276const WL_CONTAINER_OF = helpers.WL_CONTAINER_OF;
277
278test WL_CONTAINER_OF {
279 const S = struct {
280 a: u32 = 0,
281 b: u32 = 0,
282 };
283 const x = S{};
284 const y = S{};
285 const ptr = WL_CONTAINER_OF(&x.b, &y, "b");
286 try testing.expectEqual(&x, ptr);
287}
288
289const CAST_OR_CALL = helpers.CAST_OR_CALL;
290
291test "CAST_OR_CALL casting" {
292 const arg: c_int = 1000;
293 const casted = CAST_OR_CALL(u8, arg);
294 try testing.expectEqual(cast(u8, arg), casted);
295
296 const S = struct {
297 x: u32 = 0,
298 };
299 var s: S = .{};
300 const casted_ptr = CAST_OR_CALL(*u8, &s);
301 try testing.expectEqual(cast(*u8, &s), casted_ptr);
302}
303
304test "CAST_OR_CALL calling" {
305 const Helper = struct {
306 var last_val: bool = false;
307 fn returnsVoid(val: bool) void {
308 last_val = val;
309 }
310 fn returnsBool(f: f32) bool {
311 return f > 0;
312 }
313 fn identity(self: c_uint) c_uint {
314 return self;
315 }
316 };
317
318 CAST_OR_CALL(Helper.returnsVoid, true);
319 try testing.expectEqual(true, Helper.last_val);
320 CAST_OR_CALL(Helper.returnsVoid, false);
321 try testing.expectEqual(false, Helper.last_val);
322
323 try testing.expectEqual(Helper.returnsBool(1), CAST_OR_CALL(Helper.returnsBool, @as(f32, 1)));
324 try testing.expectEqual(Helper.returnsBool(-1), CAST_OR_CALL(Helper.returnsBool, @as(f32, -1)));
325
326 try testing.expectEqual(Helper.identity(@as(c_uint, 100)), CAST_OR_CALL(Helper.identity, @as(c_uint, 100)));
327}
lib/compiler/translate-c/src/main.zig deleted-251
...@@ -1,251 +0,0 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const mem = std.mem;
4const process = std.process;
5const aro = @import("aro");
6const Translator = @import("Translator.zig");
7
8const fast_exit = @import("builtin").mode != .Debug;
9
10var general_purpose_allocator: std.heap.GeneralPurposeAllocator(.{}) = .init;
11
12pub fn main() u8 {
13 const gpa = general_purpose_allocator.allocator();
14 defer _ = general_purpose_allocator.deinit();
15
16 var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);
17 defer arena_instance.deinit();
18 const arena = arena_instance.allocator();
19
20 const args = process.argsAlloc(arena) catch {
21 std.debug.print("ran out of memory allocating arguments\n", .{});
22 if (fast_exit) process.exit(1);
23 return 1;
24 };
25
26 var stderr_buf: [1024]u8 = undefined;
27 var stderr = std.fs.File.stderr().writer(&stderr_buf);
28 var diagnostics: aro.Diagnostics = .{
29 .output = .{ .to_writer = .{
30 .color = .detect(stderr.file),
31 .writer = &stderr.interface,
32 } },
33 };
34
35 var comp = aro.Compilation.initDefault(gpa, arena, &diagnostics, std.fs.cwd()) catch |err| switch (err) {
36 error.OutOfMemory => {
37 std.debug.print("ran out of memory initializing C compilation\n", .{});
38 if (fast_exit) process.exit(1);
39 return 1;
40 },
41 };
42 defer comp.deinit();
43
44 const exe_name = std.fs.selfExePathAlloc(gpa) catch {
45 std.debug.print("unable to find translate-c executable path\n", .{});
46 if (fast_exit) process.exit(1);
47 return 1;
48 };
49 defer gpa.free(exe_name);
50
51 var driver: aro.Driver = .{ .comp = &comp, .diagnostics = &diagnostics, .aro_name = exe_name };
52 defer driver.deinit();
53
54 var toolchain: aro.Toolchain = .{ .driver = &driver, .filesystem = .{ .real = comp.cwd } };
55 defer toolchain.deinit();
56
57 translate(&driver, &toolchain, args) catch |err| switch (err) {
58 error.OutOfMemory => {
59 std.debug.print("ran out of memory translating\n", .{});
60 if (fast_exit) process.exit(1);
61 return 1;
62 },
63 error.FatalError => {
64 if (fast_exit) process.exit(1);
65 return 1;
66 },
67 error.WriteFailed => {
68 std.debug.print("unable to write to stdout\n", .{});
69 if (fast_exit) process.exit(1);
70 return 1;
71 },
72 };
73 if (fast_exit) process.exit(@intFromBool(comp.diagnostics.errors != 0));
74 return @intFromBool(comp.diagnostics.errors != 0);
75}
76
77pub const usage =
78 \\Usage {s}: [options] file [CC options]
79 \\
80 \\Options:
81 \\ --help Print this message
82 \\ --version Print translate-c version
83 \\ -fmodule-libs Import libraries as modules
84 \\ -fno-module-libs (default) Install libraries next to output file
85 \\
86 \\
87;
88
89fn translate(d: *aro.Driver, tc: *aro.Toolchain, args: [][:0]u8) !void {
90 const gpa = d.comp.gpa;
91
92 var module_libs = false;
93
94 const aro_args = args: {
95 var i: usize = 0;
96 for (args) |arg| {
97 args[i] = arg;
98 if (mem.eql(u8, arg, "--help")) {
99 var stdout_buf: [512]u8 = undefined;
100 var stdout = std.fs.File.stdout().writer(&stdout_buf);
101 try stdout.interface.print(usage, .{args[0]});
102 try stdout.interface.flush();
103 return;
104 } else if (mem.eql(u8, arg, "--version")) {
105 var stdout_buf: [512]u8 = undefined;
106 var stdout = std.fs.File.stdout().writer(&stdout_buf);
107 // TODO add version
108 try stdout.interface.writeAll("0.0.0-dev\n");
109 try stdout.interface.flush();
110 return;
111 } else if (mem.eql(u8, arg, "-fmodule-libs")) {
112 module_libs = true;
113 } else if (mem.eql(u8, arg, "-fno-module-libs")) {
114 module_libs = false;
115 } else {
116 i += 1;
117 }
118 }
119 break :args args[0..i];
120 };
121 const user_macros = macros: {
122 var macro_buf: std.ArrayListUnmanaged(u8) = .empty;
123 defer macro_buf.deinit(gpa);
124
125 try macro_buf.appendSlice(gpa, "#define __TRANSLATE_C__ 1\n");
126
127 var discard_buf: [256]u8 = undefined;
128 var discarding: std.io.Writer.Discarding = .init(&discard_buf);
129 assert(!try d.parseArgs(&discarding.writer, &macro_buf, aro_args));
130 if (macro_buf.items.len > std.math.maxInt(u32)) {
131 return d.fatal("user provided macro source exceeded max size", .{});
132 }
133
134 const content = try macro_buf.toOwnedSlice(gpa);
135 errdefer gpa.free(content);
136
137 break :macros try d.comp.addSourceFromOwnedBuffer("<command line>", content, .user);
138 };
139
140 if (d.inputs.items.len != 1) {
141 return d.fatal("expected exactly one input file", .{});
142 }
143 const source = d.inputs.items[0];
144
145 tc.discover() catch |er| switch (er) {
146 error.OutOfMemory => return error.OutOfMemory,
147 error.TooManyMultilibs => return d.fatal("found more than one multilib with the same priority", .{}),
148 };
149 tc.defineSystemIncludes() catch |er| switch (er) {
150 error.OutOfMemory => return error.OutOfMemory,
151 error.AroIncludeNotFound => return d.fatal("unable to find Aro builtin headers", .{}),
152 };
153
154 const builtin_macros = d.comp.generateBuiltinMacros(.include_system_defines) catch |err| switch (err) {
155 error.FileTooBig => return d.fatal("builtin macro source exceeded max size", .{}),
156 else => |e| return e,
157 };
158
159 var pp = try aro.Preprocessor.initDefault(d.comp);
160 defer pp.deinit();
161
162 try pp.preprocessSources(&.{ source, builtin_macros, user_macros });
163
164 var c_tree = try pp.parse();
165 defer c_tree.deinit();
166
167 if (d.diagnostics.errors != 0) {
168 if (fast_exit) process.exit(1);
169 return error.FatalError;
170 }
171
172 const rendered_zig = try Translator.translate(.{
173 .gpa = gpa,
174 .comp = d.comp,
175 .pp = &pp,
176 .tree = &c_tree,
177 .module_libs = module_libs,
178 });
179 defer gpa.free(rendered_zig);
180
181 var close_out_file = false;
182 var out_file_path: []const u8 = "<stdout>";
183 var out_file: std.fs.File = .stdout();
184 defer if (close_out_file) out_file.close();
185
186 if (d.output_name) |path| blk: {
187 if (std.mem.eql(u8, path, "-")) break :blk;
188 if (std.fs.path.dirname(path)) |dirname| {
189 std.fs.cwd().makePath(dirname) catch |err|
190 return d.fatal("failed to create path to '{s}': {s}", .{ path, aro.Driver.errorDescription(err) });
191 }
192 out_file = std.fs.cwd().createFile(path, .{}) catch |err| {
193 return d.fatal("failed to create output file '{s}': {s}", .{ path, aro.Driver.errorDescription(err) });
194 };
195 close_out_file = true;
196 out_file_path = path;
197 }
198
199 var out_buf: [4096]u8 = undefined;
200 var out_writer = out_file.writer(&out_buf);
201 out_writer.interface.writeAll(rendered_zig) catch
202 return d.fatal("failed to write result to '{s}': {s}", .{ out_file_path, aro.Driver.errorDescription(out_writer.err.?) });
203
204 if (!module_libs) {
205 const dest_path = if (d.output_name) |path| std.fs.path.dirname(path) else null;
206 installLibs(d, dest_path) catch |err|
207 return d.fatal("failed to install library files: {s}", .{aro.Driver.errorDescription(err)});
208 }
209
210 if (fast_exit) process.exit(0);
211}
212
213fn installLibs(d: *aro.Driver, dest_path: ?[]const u8) !void {
214 const gpa = d.comp.gpa;
215 const cwd = std.fs.cwd();
216
217 const self_exe_path = try std.fs.selfExePathAlloc(gpa);
218 defer gpa.free(self_exe_path);
219
220 var cur_dir: []const u8 = self_exe_path;
221 while (std.fs.path.dirname(cur_dir)) |dirname| : (cur_dir = dirname) {
222 var base_dir = cwd.openDir(dirname, .{}) catch continue;
223 defer base_dir.close();
224
225 var lib_dir = base_dir.openDir("lib", .{}) catch continue;
226 defer lib_dir.close();
227
228 lib_dir.access("c_builtins.zig", .{}) catch continue;
229
230 {
231 const install_path = try std.fs.path.join(gpa, &.{ dest_path orelse "", "c_builtins.zig" });
232 defer gpa.free(install_path);
233 try lib_dir.copyFile("c_builtins.zig", cwd, install_path, .{});
234 }
235 {
236 const install_path = try std.fs.path.join(gpa, &.{ dest_path orelse "", "helpers.zig" });
237 defer gpa.free(install_path);
238 try lib_dir.copyFile("helpers.zig", cwd, install_path, .{});
239 }
240 return;
241 }
242 return error.FileNotFound;
243}
244
245comptime {
246 if (@import("builtin").is_test) {
247 _ = Translator;
248 _ = @import("helpers.zig");
249 _ = @import("PatternList.zig");
250 }
251}
lib/std/zig.zig+4-3
...@@ -36,9 +36,10 @@ pub const ParsedCharLiteral = string_literal.ParsedCharLiteral;...@@ -36,9 +36,10 @@ pub const ParsedCharLiteral = string_literal.ParsedCharLiteral;
36pub const parseCharLiteral = string_literal.parseCharLiteral;36pub const parseCharLiteral = string_literal.parseCharLiteral;
37pub const parseNumberLiteral = number_literal.parseNumberLiteral;37pub const parseNumberLiteral = number_literal.parseNumberLiteral;
3838
39// Files needed by translate-c.39pub const c_translation = struct {
40pub const c_builtins = @import("zig/c_builtins.zig");40 pub const builtins = @import("zig/c_translation/builtins.zig");
41pub const c_translation = @import("zig/c_translation.zig");41 pub const helpers = @import("zig/c_translation/helpers.zig");
42};
4243
43pub const SrcHasher = std.crypto.hash.Blake3;44pub const SrcHasher = std.crypto.hash.Blake3;
44pub const SrcHash = [16]u8;45pub const SrcHash = [16]u8;
lib/std/zig/c_builtins.zig deleted-268
...@@ -1,268 +0,0 @@
1const std = @import("std");
2
3pub inline fn __builtin_bswap16(val: u16) u16 {
4 return @byteSwap(val);
5}
6pub inline fn __builtin_bswap32(val: u32) u32 {
7 return @byteSwap(val);
8}
9pub inline fn __builtin_bswap64(val: u64) u64 {
10 return @byteSwap(val);
11}
12
13pub inline fn __builtin_signbit(val: f64) c_int {
14 return @intFromBool(std.math.signbit(val));
15}
16pub inline fn __builtin_signbitf(val: f32) c_int {
17 return @intFromBool(std.math.signbit(val));
18}
19
20pub inline fn __builtin_popcount(val: c_uint) c_int {
21 // popcount of a c_uint will never exceed the capacity of a c_int
22 @setRuntimeSafety(false);
23 return @as(c_int, @bitCast(@as(c_uint, @popCount(val))));
24}
25pub inline fn __builtin_ctz(val: c_uint) c_int {
26 // Returns the number of trailing 0-bits in val, starting at the least significant bit position.
27 // In C if `val` is 0, the result is undefined; in zig it's the number of bits in a c_uint
28 @setRuntimeSafety(false);
29 return @as(c_int, @bitCast(@as(c_uint, @ctz(val))));
30}
31pub inline fn __builtin_clz(val: c_uint) c_int {
32 // Returns the number of leading 0-bits in x, starting at the most significant bit position.
33 // In C if `val` is 0, the result is undefined; in zig it's the number of bits in a c_uint
34 @setRuntimeSafety(false);
35 return @as(c_int, @bitCast(@as(c_uint, @clz(val))));
36}
37
38pub inline fn __builtin_sqrt(val: f64) f64 {
39 return @sqrt(val);
40}
41pub inline fn __builtin_sqrtf(val: f32) f32 {
42 return @sqrt(val);
43}
44
45pub inline fn __builtin_sin(val: f64) f64 {
46 return @sin(val);
47}
48pub inline fn __builtin_sinf(val: f32) f32 {
49 return @sin(val);
50}
51pub inline fn __builtin_cos(val: f64) f64 {
52 return @cos(val);
53}
54pub inline fn __builtin_cosf(val: f32) f32 {
55 return @cos(val);
56}
57
58pub inline fn __builtin_exp(val: f64) f64 {
59 return @exp(val);
60}
61pub inline fn __builtin_expf(val: f32) f32 {
62 return @exp(val);
63}
64pub inline fn __builtin_exp2(val: f64) f64 {
65 return @exp2(val);
66}
67pub inline fn __builtin_exp2f(val: f32) f32 {
68 return @exp2(val);
69}
70pub inline fn __builtin_log(val: f64) f64 {
71 return @log(val);
72}
73pub inline fn __builtin_logf(val: f32) f32 {
74 return @log(val);
75}
76pub inline fn __builtin_log2(val: f64) f64 {
77 return @log2(val);
78}
79pub inline fn __builtin_log2f(val: f32) f32 {
80 return @log2(val);
81}
82pub inline fn __builtin_log10(val: f64) f64 {
83 return @log10(val);
84}
85pub inline fn __builtin_log10f(val: f32) f32 {
86 return @log10(val);
87}
88
89// Standard C Library bug: The absolute value of the most negative integer remains negative.
90pub inline fn __builtin_abs(val: c_int) c_int {
91 return if (val == std.math.minInt(c_int)) val else @intCast(@abs(val));
92}
93pub inline fn __builtin_labs(val: c_long) c_long {
94 return if (val == std.math.minInt(c_long)) val else @intCast(@abs(val));
95}
96pub inline fn __builtin_llabs(val: c_longlong) c_longlong {
97 return if (val == std.math.minInt(c_longlong)) val else @intCast(@abs(val));
98}
99pub inline fn __builtin_fabs(val: f64) f64 {
100 return @abs(val);
101}
102pub inline fn __builtin_fabsf(val: f32) f32 {
103 return @abs(val);
104}
105
106pub inline fn __builtin_floor(val: f64) f64 {
107 return @floor(val);
108}
109pub inline fn __builtin_floorf(val: f32) f32 {
110 return @floor(val);
111}
112pub inline fn __builtin_ceil(val: f64) f64 {
113 return @ceil(val);
114}
115pub inline fn __builtin_ceilf(val: f32) f32 {
116 return @ceil(val);
117}
118pub inline fn __builtin_trunc(val: f64) f64 {
119 return @trunc(val);
120}
121pub inline fn __builtin_truncf(val: f32) f32 {
122 return @trunc(val);
123}
124pub inline fn __builtin_round(val: f64) f64 {
125 return @round(val);
126}
127pub inline fn __builtin_roundf(val: f32) f32 {
128 return @round(val);
129}
130
131pub inline fn __builtin_strlen(s: [*c]const u8) usize {
132 return std.mem.sliceTo(s, 0).len;
133}
134pub inline fn __builtin_strcmp(s1: [*c]const u8, s2: [*c]const u8) c_int {
135 return switch (std.mem.orderZ(u8, s1, s2)) {
136 .lt => -1,
137 .eq => 0,
138 .gt => 1,
139 };
140}
141
142pub inline fn __builtin_object_size(ptr: ?*const anyopaque, ty: c_int) usize {
143 _ = ptr;
144 // clang semantics match gcc's: https://gcc.gnu.org/onlinedocs/gcc/Object-Size-Checking.html
145 // If it is not possible to determine which objects ptr points to at compile time,
146 // __builtin_object_size should return (size_t) -1 for type 0 or 1 and (size_t) 0
147 // for type 2 or 3.
148 if (ty == 0 or ty == 1) return @as(usize, @bitCast(-@as(isize, 1)));
149 if (ty == 2 or ty == 3) return 0;
150 unreachable;
151}
152
153pub inline fn __builtin___memset_chk(
154 dst: ?*anyopaque,
155 val: c_int,
156 len: usize,
157 remaining: usize,
158) ?*anyopaque {
159 if (len > remaining) @panic("std.c.builtins.memset_chk called with len > remaining");
160 return __builtin_memset(dst, val, len);
161}
162
163pub inline fn __builtin_memset(dst: ?*anyopaque, val: c_int, len: usize) ?*anyopaque {
164 const dst_cast = @as([*c]u8, @ptrCast(dst));
165 @memset(dst_cast[0..len], @as(u8, @bitCast(@as(i8, @truncate(val)))));
166 return dst;
167}
168
169pub inline fn __builtin___memcpy_chk(
170 noalias dst: ?*anyopaque,
171 noalias src: ?*const anyopaque,
172 len: usize,
173 remaining: usize,
174) ?*anyopaque {
175 if (len > remaining) @panic("std.c.builtins.memcpy_chk called with len > remaining");
176 return __builtin_memcpy(dst, src, len);
177}
178
179pub inline fn __builtin_memcpy(
180 noalias dst: ?*anyopaque,
181 noalias src: ?*const anyopaque,
182 len: usize,
183) ?*anyopaque {
184 if (len > 0) @memcpy(
185 @as([*]u8, @ptrCast(dst.?))[0..len],
186 @as([*]const u8, @ptrCast(src.?)),
187 );
188 return dst;
189}
190
191/// The return value of __builtin_expect is `expr`. `c` is the expected value
192/// of `expr` and is used as a hint to the compiler in C. Here it is unused.
193pub inline fn __builtin_expect(expr: c_long, c: c_long) c_long {
194 _ = c;
195 return expr;
196}
197
198/// returns a quiet NaN. Quiet NaNs have many representations; tagp is used to select one in an
199/// implementation-defined way.
200/// This implementation is based on the description for __builtin_nan provided in the GCC docs at
201/// https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html#index-_005f_005fbuiltin_005fnan
202/// Comment is reproduced below:
203/// Since ISO C99 defines this function in terms of strtod, which we do not implement, a description
204/// of the parsing is in order.
205/// The string is parsed as by strtol; that is, the base is recognized by leading ‘0’ or ‘0x’ prefixes.
206/// The number parsed is placed in the significand such that the least significant bit of the number is
207/// at the least significant bit of the significand.
208/// The number is truncated to fit the significand field provided.
209/// The significand is forced to be a quiet NaN.
210///
211/// If tagp contains any non-numeric characters, the function returns a NaN whose significand is zero.
212/// If tagp is empty, the function returns a NaN whose significand is zero.
213pub inline fn __builtin_nanf(tagp: []const u8) f32 {
214 const parsed = std.fmt.parseUnsigned(c_ulong, tagp, 0) catch 0;
215 const bits: u23 = @truncate(parsed); // single-precision float trailing significand is 23 bits
216 return @bitCast(@as(u32, bits) | @as(u32, @bitCast(std.math.nan(f32))));
217}
218
219pub inline fn __builtin_huge_valf() f32 {
220 return std.math.inf(f32);
221}
222
223pub inline fn __builtin_inff() f32 {
224 return std.math.inf(f32);
225}
226
227pub inline fn __builtin_isnan(x: anytype) c_int {
228 return @intFromBool(std.math.isNan(x));
229}
230
231pub inline fn __builtin_isinf(x: anytype) c_int {
232 return @intFromBool(std.math.isInf(x));
233}
234
235/// Similar to isinf, except the return value is -1 for an argument of -Inf and 1 for an argument of +Inf.
236pub inline fn __builtin_isinf_sign(x: anytype) c_int {
237 if (!std.math.isInf(x)) return 0;
238 return if (std.math.isPositiveInf(x)) 1 else -1;
239}
240
241pub inline fn __has_builtin(func: anytype) c_int {
242 _ = func;
243 return @intFromBool(true);
244}
245
246pub inline fn __builtin_assume(cond: bool) void {
247 if (!cond) unreachable;
248}
249
250pub inline fn __builtin_unreachable() noreturn {
251 unreachable;
252}
253
254pub inline fn __builtin_constant_p(expr: anytype) c_int {
255 _ = expr;
256 return @intFromBool(false);
257}
258pub fn __builtin_mul_overflow(a: anytype, b: anytype, result: *@TypeOf(a, b)) c_int {
259 const res = @mulWithOverflow(a, b);
260 result.* = res[0];
261 return res[1];
262}
263
264// __builtin_alloca_with_align is not currently implemented.
265// It is used in a run-translated-c test and a test-translate-c test to ensure that non-implemented
266// builtins are correctly demoted. If you implement __builtin_alloca_with_align, please update the
267// run-translated-c test and the test-translate-c test to use a different non-implemented builtin.
268// pub inline fn __builtin_alloca_with_align(size: usize, alignment: usize) *anyopaque {}
lib/std/zig/c_translation.zig deleted-699
...@@ -1,699 +0,0 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const testing = std.testing;
4const math = std.math;
5const mem = std.mem;
6
7/// Given a type and value, cast the value to the type as c would.
8pub fn cast(comptime DestType: type, target: anytype) DestType {
9 // this function should behave like transCCast in translate-c, except it's for macros
10 const SourceType = @TypeOf(target);
11 switch (@typeInfo(DestType)) {
12 .@"fn" => return castToPtr(*const DestType, SourceType, target),
13 .pointer => return castToPtr(DestType, SourceType, target),
14 .optional => |dest_opt| {
15 if (@typeInfo(dest_opt.child) == .pointer) {
16 return castToPtr(DestType, SourceType, target);
17 } else if (@typeInfo(dest_opt.child) == .@"fn") {
18 return castToPtr(?*const dest_opt.child, SourceType, target);
19 }
20 },
21 .int => {
22 switch (@typeInfo(SourceType)) {
23 .pointer => {
24 return castInt(DestType, @intFromPtr(target));
25 },
26 .optional => |opt| {
27 if (@typeInfo(opt.child) == .pointer) {
28 return castInt(DestType, @intFromPtr(target));
29 }
30 },
31 .int => {
32 return castInt(DestType, target);
33 },
34 .@"fn" => {
35 return castInt(DestType, @intFromPtr(&target));
36 },
37 .bool => {
38 return @intFromBool(target);
39 },
40 else => {},
41 }
42 },
43 .float => {
44 switch (@typeInfo(SourceType)) {
45 .int => return @as(DestType, @floatFromInt(target)),
46 .float => return @as(DestType, @floatCast(target)),
47 .bool => return @as(DestType, @floatFromInt(@intFromBool(target))),
48 else => {},
49 }
50 },
51 .@"union" => |info| {
52 inline for (info.fields) |field| {
53 if (field.type == SourceType) return @unionInit(DestType, field.name, target);
54 }
55 @compileError("cast to union type '" ++ @typeName(DestType) ++ "' from type '" ++ @typeName(SourceType) ++ "' which is not present in union");
56 },
57 .bool => return cast(usize, target) != 0,
58 else => {},
59 }
60 return @as(DestType, target);
61}
62
63fn castInt(comptime DestType: type, target: anytype) DestType {
64 const dest = @typeInfo(DestType).int;
65 const source = @typeInfo(@TypeOf(target)).int;
66
67 if (dest.bits < source.bits)
68 return @as(DestType, @bitCast(@as(std.meta.Int(source.signedness, dest.bits), @truncate(target))))
69 else
70 return @as(DestType, @bitCast(@as(std.meta.Int(source.signedness, dest.bits), target)));
71}
72
73fn castPtr(comptime DestType: type, target: anytype) DestType {
74 return @ptrCast(@alignCast(@constCast(@volatileCast(target))));
75}
76
77fn castToPtr(comptime DestType: type, comptime SourceType: type, target: anytype) DestType {
78 switch (@typeInfo(SourceType)) {
79 .int => {
80 return @as(DestType, @ptrFromInt(castInt(usize, target)));
81 },
82 .comptime_int => {
83 if (target < 0)
84 return @as(DestType, @ptrFromInt(@as(usize, @bitCast(@as(isize, @intCast(target))))))
85 else
86 return @as(DestType, @ptrFromInt(@as(usize, @intCast(target))));
87 },
88 .pointer => {
89 return castPtr(DestType, target);
90 },
91 .@"fn" => {
92 return castPtr(DestType, &target);
93 },
94 .optional => |target_opt| {
95 if (@typeInfo(target_opt.child) == .pointer) {
96 return castPtr(DestType, target);
97 }
98 },
99 else => {},
100 }
101 return @as(DestType, target);
102}
103
104fn ptrInfo(comptime PtrType: type) std.builtin.Type.Pointer {
105 return switch (@typeInfo(PtrType)) {
106 .optional => |opt_info| @typeInfo(opt_info.child).pointer,
107 .pointer => |ptr_info| ptr_info,
108 else => unreachable,
109 };
110}
111
112test "cast" {
113 var i = @as(i64, 10);
114
115 try testing.expect(cast(*u8, 16) == @as(*u8, @ptrFromInt(16)));
116 try testing.expect(cast(*u64, &i).* == @as(u64, 10));
117 try testing.expect(cast(*i64, @as(?*align(1) i64, &i)) == &i);
118
119 try testing.expect(cast(?*u8, 2) == @as(*u8, @ptrFromInt(2)));
120 try testing.expect(cast(?*i64, @as(*align(1) i64, &i)) == &i);
121 try testing.expect(cast(?*i64, @as(?*align(1) i64, &i)) == &i);
122
123 try testing.expectEqual(@as(u32, 4), cast(u32, @as(*u32, @ptrFromInt(4))));
124 try testing.expectEqual(@as(u32, 4), cast(u32, @as(?*u32, @ptrFromInt(4))));
125 try testing.expectEqual(@as(u32, 10), cast(u32, @as(u64, 10)));
126
127 try testing.expectEqual(@as(i32, @bitCast(@as(u32, 0x8000_0000))), cast(i32, @as(u32, 0x8000_0000)));
128
129 try testing.expectEqual(@as(*u8, @ptrFromInt(2)), cast(*u8, @as(*const u8, @ptrFromInt(2))));
130 try testing.expectEqual(@as(*u8, @ptrFromInt(2)), cast(*u8, @as(*volatile u8, @ptrFromInt(2))));
131
132 try testing.expectEqual(@as(?*anyopaque, @ptrFromInt(2)), cast(?*anyopaque, @as(*u8, @ptrFromInt(2))));
133
134 var foo: c_int = -1;
135 _ = &foo;
136 try testing.expect(cast(*anyopaque, -1) == @as(*anyopaque, @ptrFromInt(@as(usize, @bitCast(@as(isize, -1))))));
137 try testing.expect(cast(*anyopaque, foo) == @as(*anyopaque, @ptrFromInt(@as(usize, @bitCast(@as(isize, -1))))));
138 try testing.expect(cast(?*anyopaque, -1) == @as(?*anyopaque, @ptrFromInt(@as(usize, @bitCast(@as(isize, -1))))));
139 try testing.expect(cast(?*anyopaque, foo) == @as(?*anyopaque, @ptrFromInt(@as(usize, @bitCast(@as(isize, -1))))));
140
141 const FnPtr = ?*align(1) const fn (*anyopaque) void;
142 try testing.expect(cast(FnPtr, 0) == @as(FnPtr, @ptrFromInt(@as(usize, 0))));
143 try testing.expect(cast(FnPtr, foo) == @as(FnPtr, @ptrFromInt(@as(usize, @bitCast(@as(isize, -1))))));
144}
145
146/// Given a value returns its size as C's sizeof operator would.
147pub fn sizeof(target: anytype) usize {
148 const T: type = if (@TypeOf(target) == type) target else @TypeOf(target);
149 switch (@typeInfo(T)) {
150 .float, .int, .@"struct", .@"union", .array, .bool, .vector => return @sizeOf(T),
151 .@"fn" => {
152 // sizeof(main) in C returns 1
153 return 1;
154 },
155 .null => return @sizeOf(*anyopaque),
156 .void => {
157 // Note: sizeof(void) is 1 on clang/gcc and 0 on MSVC.
158 return 1;
159 },
160 .@"opaque" => {
161 if (T == anyopaque) {
162 // Note: sizeof(void) is 1 on clang/gcc and 0 on MSVC.
163 return 1;
164 } else {
165 @compileError("Cannot use C sizeof on opaque type " ++ @typeName(T));
166 }
167 },
168 .optional => |opt| {
169 if (@typeInfo(opt.child) == .pointer) {
170 return sizeof(opt.child);
171 } else {
172 @compileError("Cannot use C sizeof on non-pointer optional " ++ @typeName(T));
173 }
174 },
175 .pointer => |ptr| {
176 if (ptr.size == .slice) {
177 @compileError("Cannot use C sizeof on slice type " ++ @typeName(T));
178 }
179 // for strings, sizeof("a") returns 2.
180 // normal pointer decay scenarios from C are handled
181 // in the .array case above, but strings remain literals
182 // and are therefore always pointers, so they need to be
183 // specially handled here.
184 if (ptr.size == .one and ptr.is_const and @typeInfo(ptr.child) == .array) {
185 const array_info = @typeInfo(ptr.child).array;
186 if ((array_info.child == u8 or array_info.child == u16) and array_info.sentinel() == 0) {
187 // length of the string plus one for the null terminator.
188 return (array_info.len + 1) * @sizeOf(array_info.child);
189 }
190 }
191 // When zero sized pointers are removed, this case will no
192 // longer be reachable and can be deleted.
193 if (@sizeOf(T) == 0) {
194 return @sizeOf(*anyopaque);
195 }
196 return @sizeOf(T);
197 },
198 .comptime_float => return @sizeOf(f64), // TODO c_double #3999
199 .comptime_int => {
200 // TODO to get the correct result we have to translate
201 // `1073741824 * 4` as `int(1073741824) *% int(4)` since
202 // sizeof(1073741824 * 4) != sizeof(4294967296).
203
204 // TODO test if target fits in int, long or long long
205 return @sizeOf(c_int);
206 },
207 else => @compileError("std.meta.sizeof does not support type " ++ @typeName(T)),
208 }
209}
210
211test "sizeof" {
212 const S = extern struct { a: u32 };
213
214 const ptr_size = @sizeOf(*anyopaque);
215
216 try testing.expect(sizeof(u32) == 4);
217 try testing.expect(sizeof(@as(u32, 2)) == 4);
218 try testing.expect(sizeof(2) == @sizeOf(c_int));
219
220 try testing.expect(sizeof(2.0) == @sizeOf(f64));
221
222 try testing.expect(sizeof(S) == 4);
223
224 try testing.expect(sizeof([_]u32{ 4, 5, 6 }) == 12);
225 try testing.expect(sizeof([3]u32) == 12);
226 try testing.expect(sizeof([3:0]u32) == 16);
227 try testing.expect(sizeof(&[_]u32{ 4, 5, 6 }) == ptr_size);
228
229 try testing.expect(sizeof(*u32) == ptr_size);
230 try testing.expect(sizeof([*]u32) == ptr_size);
231 try testing.expect(sizeof([*c]u32) == ptr_size);
232 try testing.expect(sizeof(?*u32) == ptr_size);
233 try testing.expect(sizeof(?[*]u32) == ptr_size);
234 try testing.expect(sizeof(*anyopaque) == ptr_size);
235 try testing.expect(sizeof(*void) == ptr_size);
236 try testing.expect(sizeof(null) == ptr_size);
237
238 try testing.expect(sizeof("foobar") == 7);
239 try testing.expect(sizeof(&[_:0]u16{ 'f', 'o', 'o', 'b', 'a', 'r' }) == 14);
240 try testing.expect(sizeof(*const [4:0]u8) == 5);
241 try testing.expect(sizeof(*[4:0]u8) == ptr_size);
242 try testing.expect(sizeof([*]const [4:0]u8) == ptr_size);
243 try testing.expect(sizeof(*const *const [4:0]u8) == ptr_size);
244 try testing.expect(sizeof(*const [4]u8) == ptr_size);
245
246 if (false) { // TODO
247 try testing.expect(sizeof(&sizeof) == @sizeOf(@TypeOf(&sizeof)));
248 try testing.expect(sizeof(sizeof) == 1);
249 }
250
251 try testing.expect(sizeof(void) == 1);
252 try testing.expect(sizeof(anyopaque) == 1);
253}
254
255pub const CIntLiteralBase = enum { decimal, octal, hex };
256
257fn PromoteIntLiteralReturnType(comptime SuffixType: type, comptime number: comptime_int, comptime base: CIntLiteralBase) type {
258 const signed_decimal = [_]type{ c_int, c_long, c_longlong, c_ulonglong };
259 const signed_oct_hex = [_]type{ c_int, c_uint, c_long, c_ulong, c_longlong, c_ulonglong };
260 const unsigned = [_]type{ c_uint, c_ulong, c_ulonglong };
261
262 const list: []const type = if (@typeInfo(SuffixType).int.signedness == .unsigned)
263 &unsigned
264 else if (base == .decimal)
265 &signed_decimal
266 else
267 &signed_oct_hex;
268
269 var pos = mem.indexOfScalar(type, list, SuffixType).?;
270
271 while (pos < list.len) : (pos += 1) {
272 if (number >= math.minInt(list[pos]) and number <= math.maxInt(list[pos])) {
273 return list[pos];
274 }
275 }
276 @compileError("Integer literal is too large");
277}
278
279/// Promote the type of an integer literal until it fits as C would.
280pub fn promoteIntLiteral(
281 comptime SuffixType: type,
282 comptime number: comptime_int,
283 comptime base: CIntLiteralBase,
284) PromoteIntLiteralReturnType(SuffixType, number, base) {
285 return number;
286}
287
288test "promoteIntLiteral" {
289 const signed_hex = promoteIntLiteral(c_int, math.maxInt(c_int) + 1, .hex);
290 try testing.expectEqual(c_uint, @TypeOf(signed_hex));
291
292 if (math.maxInt(c_longlong) == math.maxInt(c_int)) return;
293
294 const signed_decimal = promoteIntLiteral(c_int, math.maxInt(c_int) + 1, .decimal);
295 const unsigned = promoteIntLiteral(c_uint, math.maxInt(c_uint) + 1, .hex);
296
297 if (math.maxInt(c_long) > math.maxInt(c_int)) {
298 try testing.expectEqual(c_long, @TypeOf(signed_decimal));
299 try testing.expectEqual(c_ulong, @TypeOf(unsigned));
300 } else {
301 try testing.expectEqual(c_longlong, @TypeOf(signed_decimal));
302 try testing.expectEqual(c_ulonglong, @TypeOf(unsigned));
303 }
304}
305
306/// Convert from clang __builtin_shufflevector index to Zig @shuffle index
307/// clang requires __builtin_shufflevector index arguments to be integer constants.
308/// negative values for `this_index` indicate "don't care".
309/// clang enforces that `this_index` is less than the total number of vector elements
310/// See https://ziglang.org/documentation/master/#shuffle
311/// See https://clang.llvm.org/docs/LanguageExtensions.html#langext-builtin-shufflevector
312pub fn shuffleVectorIndex(comptime this_index: c_int, comptime source_vector_len: usize) i32 {
313 const positive_index = std.math.cast(usize, this_index) orelse return undefined;
314 if (positive_index < source_vector_len) return @as(i32, @intCast(this_index));
315 const b_index = positive_index - source_vector_len;
316 return ~@as(i32, @intCast(b_index));
317}
318
319test "shuffleVectorIndex" {
320 const vector_len: usize = 4;
321
322 _ = shuffleVectorIndex(-1, vector_len);
323
324 try testing.expect(shuffleVectorIndex(0, vector_len) == 0);
325 try testing.expect(shuffleVectorIndex(1, vector_len) == 1);
326 try testing.expect(shuffleVectorIndex(2, vector_len) == 2);
327 try testing.expect(shuffleVectorIndex(3, vector_len) == 3);
328
329 try testing.expect(shuffleVectorIndex(4, vector_len) == -1);
330 try testing.expect(shuffleVectorIndex(5, vector_len) == -2);
331 try testing.expect(shuffleVectorIndex(6, vector_len) == -3);
332 try testing.expect(shuffleVectorIndex(7, vector_len) == -4);
333}
334
335/// Constructs a [*c] pointer with the const and volatile annotations
336/// from SelfType for pointing to a C flexible array of ElementType.
337pub fn FlexibleArrayType(comptime SelfType: type, comptime ElementType: type) type {
338 switch (@typeInfo(SelfType)) {
339 .pointer => |ptr| {
340 return @Type(.{ .pointer = .{
341 .size = .c,
342 .is_const = ptr.is_const,
343 .is_volatile = ptr.is_volatile,
344 .alignment = @alignOf(ElementType),
345 .address_space = .generic,
346 .child = ElementType,
347 .is_allowzero = true,
348 .sentinel_ptr = null,
349 } });
350 },
351 else => |info| @compileError("Invalid self type \"" ++ @tagName(info) ++ "\" for flexible array getter: " ++ @typeName(SelfType)),
352 }
353}
354
355test "Flexible Array Type" {
356 const Container = extern struct {
357 size: usize,
358 };
359
360 try testing.expectEqual(FlexibleArrayType(*Container, c_int), [*c]c_int);
361 try testing.expectEqual(FlexibleArrayType(*const Container, c_int), [*c]const c_int);
362 try testing.expectEqual(FlexibleArrayType(*volatile Container, c_int), [*c]volatile c_int);
363 try testing.expectEqual(FlexibleArrayType(*const volatile Container, c_int), [*c]const volatile c_int);
364}
365
366/// C `%` operator for signed integers
367/// C standard states: "If the quotient a/b is representable, the expression (a/b)*b + a%b shall equal a"
368/// The quotient is not representable if denominator is zero, or if numerator is the minimum integer for
369/// the type and denominator is -1. C has undefined behavior for those two cases; this function has safety
370/// checked undefined behavior
371pub fn signedRemainder(numerator: anytype, denominator: anytype) @TypeOf(numerator, denominator) {
372 std.debug.assert(@typeInfo(@TypeOf(numerator, denominator)).int.signedness == .signed);
373 if (denominator > 0) return @rem(numerator, denominator);
374 return numerator - @divTrunc(numerator, denominator) * denominator;
375}
376
377pub const Macros = struct {
378 pub fn U_SUFFIX(comptime n: comptime_int) @TypeOf(promoteIntLiteral(c_uint, n, .decimal)) {
379 return promoteIntLiteral(c_uint, n, .decimal);
380 }
381
382 fn L_SUFFIX_ReturnType(comptime number: anytype) type {
383 switch (@typeInfo(@TypeOf(number))) {
384 .int, .comptime_int => return @TypeOf(promoteIntLiteral(c_long, number, .decimal)),
385 .float, .comptime_float => return c_longdouble,
386 else => @compileError("Invalid value for L suffix"),
387 }
388 }
389 pub fn L_SUFFIX(comptime number: anytype) L_SUFFIX_ReturnType(number) {
390 switch (@typeInfo(@TypeOf(number))) {
391 .int, .comptime_int => return promoteIntLiteral(c_long, number, .decimal),
392 .float, .comptime_float => @compileError("TODO: c_longdouble initialization from comptime_float not supported"),
393 else => @compileError("Invalid value for L suffix"),
394 }
395 }
396
397 pub fn UL_SUFFIX(comptime n: comptime_int) @TypeOf(promoteIntLiteral(c_ulong, n, .decimal)) {
398 return promoteIntLiteral(c_ulong, n, .decimal);
399 }
400
401 pub fn LL_SUFFIX(comptime n: comptime_int) @TypeOf(promoteIntLiteral(c_longlong, n, .decimal)) {
402 return promoteIntLiteral(c_longlong, n, .decimal);
403 }
404
405 pub fn ULL_SUFFIX(comptime n: comptime_int) @TypeOf(promoteIntLiteral(c_ulonglong, n, .decimal)) {
406 return promoteIntLiteral(c_ulonglong, n, .decimal);
407 }
408
409 pub fn F_SUFFIX(comptime f: comptime_float) f32 {
410 return @as(f32, f);
411 }
412
413 pub fn WL_CONTAINER_OF(ptr: anytype, sample: anytype, comptime member: []const u8) @TypeOf(sample) {
414 return @fieldParentPtr(member, ptr);
415 }
416
417 /// A 2-argument function-like macro defined as #define FOO(A, B) (A)(B)
418 /// could be either: cast B to A, or call A with the value B.
419 pub fn CAST_OR_CALL(a: anytype, b: anytype) switch (@typeInfo(@TypeOf(a))) {
420 .type => a,
421 .@"fn" => |fn_info| fn_info.return_type orelse void,
422 else => |info| @compileError("Unexpected argument type: " ++ @tagName(info)),
423 } {
424 switch (@typeInfo(@TypeOf(a))) {
425 .type => return cast(a, b),
426 .@"fn" => return a(b),
427 else => unreachable, // return type will be a compile error otherwise
428 }
429 }
430
431 pub inline fn DISCARD(x: anytype) void {
432 _ = x;
433 }
434};
435
436/// Integer promotion described in C11 6.3.1.1.2
437fn PromotedIntType(comptime T: type) type {
438 return switch (T) {
439 bool, c_short => c_int,
440 c_ushort => if (@sizeOf(c_ushort) == @sizeOf(c_int)) c_uint else c_int,
441 c_int, c_uint, c_long, c_ulong, c_longlong, c_ulonglong => T,
442 else => switch (@typeInfo(T)) {
443 .comptime_int => @compileError("Cannot promote `" ++ @typeName(T) ++ "`; a fixed-size number type is required"),
444 // promote to c_int if it can represent all values of T
445 .int => |int_info| if (int_info.bits < @bitSizeOf(c_int))
446 c_int
447 // otherwise, restore the original C type
448 else if (int_info.bits == @bitSizeOf(c_int))
449 if (int_info.signedness == .unsigned) c_uint else c_int
450 else if (int_info.bits <= @bitSizeOf(c_long))
451 if (int_info.signedness == .unsigned) c_ulong else c_long
452 else if (int_info.bits <= @bitSizeOf(c_longlong))
453 if (int_info.signedness == .unsigned) c_ulonglong else c_longlong
454 else
455 @compileError("Cannot promote `" ++ @typeName(T) ++ "`; a C ABI type is required"),
456 else => @compileError("Attempted to promote invalid type `" ++ @typeName(T) ++ "`"),
457 },
458 };
459}
460
461/// C11 6.3.1.1.1
462fn integerRank(comptime T: type) u8 {
463 return switch (T) {
464 bool => 0,
465 u8, i8 => 1,
466 c_short, c_ushort => 2,
467 c_int, c_uint => 3,
468 c_long, c_ulong => 4,
469 c_longlong, c_ulonglong => 5,
470 else => @compileError("integer rank not supported for `" ++ @typeName(T) ++ "`"),
471 };
472}
473
474fn ToUnsigned(comptime T: type) type {
475 return switch (T) {
476 c_int => c_uint,
477 c_long => c_ulong,
478 c_longlong => c_ulonglong,
479 else => @compileError("Cannot convert `" ++ @typeName(T) ++ "` to unsigned"),
480 };
481}
482
483/// "Usual arithmetic conversions" from C11 standard 6.3.1.8
484fn ArithmeticConversion(comptime A: type, comptime B: type) type {
485 if (A == c_longdouble or B == c_longdouble) return c_longdouble;
486 if (A == f80 or B == f80) return f80;
487 if (A == f64 or B == f64) return f64;
488 if (A == f32 or B == f32) return f32;
489
490 const A_Promoted = PromotedIntType(A);
491 const B_Promoted = PromotedIntType(B);
492 comptime {
493 std.debug.assert(integerRank(A_Promoted) >= integerRank(c_int));
494 std.debug.assert(integerRank(B_Promoted) >= integerRank(c_int));
495 }
496
497 if (A_Promoted == B_Promoted) return A_Promoted;
498
499 const a_signed = @typeInfo(A_Promoted).int.signedness == .signed;
500 const b_signed = @typeInfo(B_Promoted).int.signedness == .signed;
501
502 if (a_signed == b_signed) {
503 return if (integerRank(A_Promoted) > integerRank(B_Promoted)) A_Promoted else B_Promoted;
504 }
505
506 const SignedType = if (a_signed) A_Promoted else B_Promoted;
507 const UnsignedType = if (!a_signed) A_Promoted else B_Promoted;
508
509 if (integerRank(UnsignedType) >= integerRank(SignedType)) return UnsignedType;
510
511 if (std.math.maxInt(SignedType) >= std.math.maxInt(UnsignedType)) return SignedType;
512
513 return ToUnsigned(SignedType);
514}
515
516test "ArithmeticConversion" {
517 // Promotions not necessarily the same for other platforms
518 if (builtin.target.cpu.arch != .x86_64 or builtin.target.os.tag != .linux) return error.SkipZigTest;
519
520 const Test = struct {
521 /// Order of operands should not matter for arithmetic conversions
522 fn checkPromotion(comptime A: type, comptime B: type, comptime Expected: type) !void {
523 try std.testing.expect(ArithmeticConversion(A, B) == Expected);
524 try std.testing.expect(ArithmeticConversion(B, A) == Expected);
525 }
526 };
527
528 try Test.checkPromotion(c_longdouble, c_int, c_longdouble);
529 try Test.checkPromotion(c_int, f64, f64);
530 try Test.checkPromotion(f32, bool, f32);
531
532 try Test.checkPromotion(bool, c_short, c_int);
533 try Test.checkPromotion(c_int, c_int, c_int);
534 try Test.checkPromotion(c_short, c_int, c_int);
535
536 try Test.checkPromotion(c_int, c_long, c_long);
537
538 try Test.checkPromotion(c_ulonglong, c_uint, c_ulonglong);
539
540 try Test.checkPromotion(c_uint, c_int, c_uint);
541
542 try Test.checkPromotion(c_uint, c_long, c_long);
543
544 try Test.checkPromotion(c_ulong, c_longlong, c_ulonglong);
545
546 // stdint.h
547 try Test.checkPromotion(u8, i8, c_int);
548 try Test.checkPromotion(u16, i16, c_int);
549 try Test.checkPromotion(i32, c_int, c_int);
550 try Test.checkPromotion(u32, c_int, c_uint);
551 try Test.checkPromotion(i64, c_int, c_long);
552 try Test.checkPromotion(u64, c_int, c_ulong);
553 try Test.checkPromotion(isize, c_int, c_long);
554 try Test.checkPromotion(usize, c_int, c_ulong);
555}
556
557pub const MacroArithmetic = struct {
558 pub fn div(a: anytype, b: anytype) ArithmeticConversion(@TypeOf(a), @TypeOf(b)) {
559 const ResType = ArithmeticConversion(@TypeOf(a), @TypeOf(b));
560 const a_casted = cast(ResType, a);
561 const b_casted = cast(ResType, b);
562 switch (@typeInfo(ResType)) {
563 .float => return a_casted / b_casted,
564 .int => return @divTrunc(a_casted, b_casted),
565 else => unreachable,
566 }
567 }
568
569 pub fn rem(a: anytype, b: anytype) ArithmeticConversion(@TypeOf(a), @TypeOf(b)) {
570 const ResType = ArithmeticConversion(@TypeOf(a), @TypeOf(b));
571 const a_casted = cast(ResType, a);
572 const b_casted = cast(ResType, b);
573 switch (@typeInfo(ResType)) {
574 .int => {
575 if (@typeInfo(ResType).int.signedness == .signed) {
576 return signedRemainder(a_casted, b_casted);
577 } else {
578 return a_casted % b_casted;
579 }
580 },
581 else => unreachable,
582 }
583 }
584};
585
586test "Macro suffix functions" {
587 try testing.expect(@TypeOf(Macros.F_SUFFIX(1)) == f32);
588
589 try testing.expect(@TypeOf(Macros.U_SUFFIX(1)) == c_uint);
590 if (math.maxInt(c_ulong) > math.maxInt(c_uint)) {
591 try testing.expect(@TypeOf(Macros.U_SUFFIX(math.maxInt(c_uint) + 1)) == c_ulong);
592 }
593 if (math.maxInt(c_ulonglong) > math.maxInt(c_ulong)) {
594 try testing.expect(@TypeOf(Macros.U_SUFFIX(math.maxInt(c_ulong) + 1)) == c_ulonglong);
595 }
596
597 try testing.expect(@TypeOf(Macros.L_SUFFIX(1)) == c_long);
598 if (math.maxInt(c_long) > math.maxInt(c_int)) {
599 try testing.expect(@TypeOf(Macros.L_SUFFIX(math.maxInt(c_int) + 1)) == c_long);
600 }
601 if (math.maxInt(c_longlong) > math.maxInt(c_long)) {
602 try testing.expect(@TypeOf(Macros.L_SUFFIX(math.maxInt(c_long) + 1)) == c_longlong);
603 }
604
605 try testing.expect(@TypeOf(Macros.UL_SUFFIX(1)) == c_ulong);
606 if (math.maxInt(c_ulonglong) > math.maxInt(c_ulong)) {
607 try testing.expect(@TypeOf(Macros.UL_SUFFIX(math.maxInt(c_ulong) + 1)) == c_ulonglong);
608 }
609
610 try testing.expect(@TypeOf(Macros.LL_SUFFIX(1)) == c_longlong);
611 try testing.expect(@TypeOf(Macros.ULL_SUFFIX(1)) == c_ulonglong);
612}
613
614test "WL_CONTAINER_OF" {
615 const S = struct {
616 a: u32 = 0,
617 b: u32 = 0,
618 };
619 const x = S{};
620 const y = S{};
621 const ptr = Macros.WL_CONTAINER_OF(&x.b, &y, "b");
622 try testing.expectEqual(&x, ptr);
623}
624
625test "CAST_OR_CALL casting" {
626 const arg: c_int = 1000;
627 const casted = Macros.CAST_OR_CALL(u8, arg);
628 try testing.expectEqual(cast(u8, arg), casted);
629
630 const S = struct {
631 x: u32 = 0,
632 };
633 var s: S = .{};
634 const casted_ptr = Macros.CAST_OR_CALL(*u8, &s);
635 try testing.expectEqual(cast(*u8, &s), casted_ptr);
636}
637
638test "CAST_OR_CALL calling" {
639 const Helper = struct {
640 var last_val: bool = false;
641 fn returnsVoid(val: bool) void {
642 last_val = val;
643 }
644 fn returnsBool(f: f32) bool {
645 return f > 0;
646 }
647 fn identity(self: c_uint) c_uint {
648 return self;
649 }
650 };
651
652 Macros.CAST_OR_CALL(Helper.returnsVoid, true);
653 try testing.expectEqual(true, Helper.last_val);
654 Macros.CAST_OR_CALL(Helper.returnsVoid, false);
655 try testing.expectEqual(false, Helper.last_val);
656
657 try testing.expectEqual(Helper.returnsBool(1), Macros.CAST_OR_CALL(Helper.returnsBool, @as(f32, 1)));
658 try testing.expectEqual(Helper.returnsBool(-1), Macros.CAST_OR_CALL(Helper.returnsBool, @as(f32, -1)));
659
660 try testing.expectEqual(Helper.identity(@as(c_uint, 100)), Macros.CAST_OR_CALL(Helper.identity, @as(c_uint, 100)));
661}
662
663test "Extended C ABI casting" {
664 if (math.maxInt(c_long) > math.maxInt(c_char)) {
665 try testing.expect(@TypeOf(Macros.L_SUFFIX(@as(c_char, math.maxInt(c_char) - 1))) == c_long); // c_char
666 }
667 if (math.maxInt(c_long) > math.maxInt(c_short)) {
668 try testing.expect(@TypeOf(Macros.L_SUFFIX(@as(c_short, math.maxInt(c_short) - 1))) == c_long); // c_short
669 }
670
671 if (math.maxInt(c_long) > math.maxInt(c_ushort)) {
672 try testing.expect(@TypeOf(Macros.L_SUFFIX(@as(c_ushort, math.maxInt(c_ushort) - 1))) == c_long); //c_ushort
673 }
674
675 if (math.maxInt(c_long) > math.maxInt(c_int)) {
676 try testing.expect(@TypeOf(Macros.L_SUFFIX(@as(c_int, math.maxInt(c_int) - 1))) == c_long); // c_int
677 }
678
679 if (math.maxInt(c_long) > math.maxInt(c_uint)) {
680 try testing.expect(@TypeOf(Macros.L_SUFFIX(@as(c_uint, math.maxInt(c_uint) - 1))) == c_long); // c_uint
681 try testing.expect(@TypeOf(Macros.L_SUFFIX(math.maxInt(c_uint) + 1)) == c_long); // comptime_int -> c_long
682 }
683
684 if (math.maxInt(c_longlong) > math.maxInt(c_long)) {
685 try testing.expect(@TypeOf(Macros.L_SUFFIX(@as(c_long, math.maxInt(c_long) - 1))) == c_long); // c_long
686 try testing.expect(@TypeOf(Macros.L_SUFFIX(math.maxInt(c_long) + 1)) == c_longlong); // comptime_int -> c_longlong
687 }
688}
689
690// Function with complex signature for testing the SDL case
691fn complexFunction(_: ?*anyopaque, _: c_uint, _: ?*const fn (?*anyopaque) callconv(.c) c_uint, _: ?*anyopaque, _: c_uint, _: [*c]c_uint) callconv(.c) usize {
692 return 0;
693}
694
695test "function pointer casting" {
696 const SDL_FunctionPointer = ?*const fn () callconv(.c) void;
697 const fn_ptr = cast(SDL_FunctionPointer, complexFunction);
698 try testing.expect(fn_ptr != null);
699}
lib/std/zig/c_translation/builtins.zig created+301
...@@ -0,0 +1,301 @@
1const std = @import("std");
2
3/// Standard C Library bug: The absolute value of the most negative integer remains negative.
4pub inline fn abs(val: c_int) c_int {
5 return if (val == std.math.minInt(c_int)) val else @intCast(@abs(val));
6}
7
8pub inline fn assume(cond: bool) void {
9 if (!cond) unreachable;
10}
11
12pub inline fn bswap16(val: u16) u16 {
13 return @byteSwap(val);
14}
15
16pub inline fn bswap32(val: u32) u32 {
17 return @byteSwap(val);
18}
19
20pub inline fn bswap64(val: u64) u64 {
21 return @byteSwap(val);
22}
23
24pub inline fn ceilf(val: f32) f32 {
25 return @ceil(val);
26}
27
28pub inline fn ceil(val: f64) f64 {
29 return @ceil(val);
30}
31
32/// Returns the number of leading 0-bits in x, starting at the most significant bit position.
33/// In C if `val` is 0, the result is undefined; in zig it's the number of bits in a c_uint
34pub inline fn clz(val: c_uint) c_int {
35 @setRuntimeSafety(false);
36 return @as(c_int, @bitCast(@as(c_uint, @clz(val))));
37}
38
39pub inline fn constant_p(expr: anytype) c_int {
40 _ = expr;
41 return @intFromBool(false);
42}
43
44pub inline fn cosf(val: f32) f32 {
45 return @cos(val);
46}
47
48pub inline fn cos(val: f64) f64 {
49 return @cos(val);
50}
51
52/// Returns the number of trailing 0-bits in val, starting at the least significant bit position.
53/// In C if `val` is 0, the result is undefined; in zig it's the number of bits in a c_uint
54pub inline fn ctz(val: c_uint) c_int {
55 @setRuntimeSafety(false);
56 return @as(c_int, @bitCast(@as(c_uint, @ctz(val))));
57}
58
59pub inline fn exp2f(val: f32) f32 {
60 return @exp2(val);
61}
62
63pub inline fn exp2(val: f64) f64 {
64 return @exp2(val);
65}
66
67pub inline fn expf(val: f32) f32 {
68 return @exp(val);
69}
70
71pub inline fn exp(val: f64) f64 {
72 return @exp(val);
73}
74
75/// The return value of __builtin_expect is `expr`. `c` is the expected value
76/// of `expr` and is used as a hint to the compiler in C. Here it is unused.
77pub inline fn expect(expr: c_long, c: c_long) c_long {
78 _ = c;
79 return expr;
80}
81
82pub inline fn fabsf(val: f32) f32 {
83 return @abs(val);
84}
85
86pub inline fn fabs(val: f64) f64 {
87 return @abs(val);
88}
89
90pub inline fn floorf(val: f32) f32 {
91 return @floor(val);
92}
93
94pub inline fn floor(val: f64) f64 {
95 return @floor(val);
96}
97
98pub inline fn has_builtin(func: anytype) c_int {
99 _ = func;
100 return @intFromBool(true);
101}
102
103pub inline fn huge_valf() f32 {
104 return std.math.inf(f32);
105}
106
107pub inline fn inff() f32 {
108 return std.math.inf(f32);
109}
110
111/// Similar to isinf, except the return value is -1 for an argument of -Inf and 1 for an argument of +Inf.
112pub inline fn isinf_sign(x: anytype) c_int {
113 if (!std.math.isInf(x)) return 0;
114 return if (std.math.isPositiveInf(x)) 1 else -1;
115}
116
117pub inline fn isinf(x: anytype) c_int {
118 return @intFromBool(std.math.isInf(x));
119}
120
121pub inline fn isnan(x: anytype) c_int {
122 return @intFromBool(std.math.isNan(x));
123}
124
125/// Standard C Library bug: The absolute value of the most negative integer remains negative.
126pub inline fn labs(val: c_long) c_long {
127 return if (val == std.math.minInt(c_long)) val else @intCast(@abs(val));
128}
129
130/// Standard C Library bug: The absolute value of the most negative integer remains negative.
131pub inline fn llabs(val: c_longlong) c_longlong {
132 return if (val == std.math.minInt(c_longlong)) val else @intCast(@abs(val));
133}
134
135pub inline fn log10f(val: f32) f32 {
136 return @log10(val);
137}
138
139pub inline fn log10(val: f64) f64 {
140 return @log10(val);
141}
142
143pub inline fn log2f(val: f32) f32 {
144 return @log2(val);
145}
146
147pub inline fn log2(val: f64) f64 {
148 return @log2(val);
149}
150
151pub inline fn logf(val: f32) f32 {
152 return @log(val);
153}
154
155pub inline fn log(val: f64) f64 {
156 return @log(val);
157}
158
159pub inline fn memcpy_chk(
160 noalias dst: ?*anyopaque,
161 noalias src: ?*const anyopaque,
162 len: usize,
163 remaining: usize,
164) ?*anyopaque {
165 if (len > remaining) @panic("__builtin___memcpy_chk called with len > remaining");
166 if (len > 0) @memcpy(
167 @as([*]u8, @ptrCast(dst.?))[0..len],
168 @as([*]const u8, @ptrCast(src.?)),
169 );
170 return dst;
171}
172
173pub inline fn memcpy(
174 noalias dst: ?*anyopaque,
175 noalias src: ?*const anyopaque,
176 len: usize,
177) ?*anyopaque {
178 if (len > 0) @memcpy(
179 @as([*]u8, @ptrCast(dst.?))[0..len],
180 @as([*]const u8, @ptrCast(src.?)),
181 );
182 return dst;
183}
184
185pub inline fn memset_chk(
186 dst: ?*anyopaque,
187 val: c_int,
188 len: usize,
189 remaining: usize,
190) ?*anyopaque {
191 if (len > remaining) @panic("__builtin___memset_chk called with len > remaining");
192 const dst_cast = @as([*c]u8, @ptrCast(dst));
193 @memset(dst_cast[0..len], @as(u8, @bitCast(@as(i8, @truncate(val)))));
194 return dst;
195}
196
197pub inline fn memset(dst: ?*anyopaque, val: c_int, len: usize) ?*anyopaque {
198 const dst_cast = @as([*c]u8, @ptrCast(dst));
199 @memset(dst_cast[0..len], @as(u8, @bitCast(@as(i8, @truncate(val)))));
200 return dst;
201}
202
203pub fn mul_overflow(a: anytype, b: anytype, result: *@TypeOf(a, b)) c_int {
204 const res = @mulWithOverflow(a, b);
205 result.* = res[0];
206 return res[1];
207}
208
209/// returns a quiet NaN. Quiet NaNs have many representations; tagp is used to select one in an
210/// implementation-defined way.
211/// This implementation is based on the description for nan provided in the GCC docs at
212/// https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html#index-_005f_005fbuiltin_005fnan
213/// Comment is reproduced below:
214/// Since ISO C99 defines this function in terms of strtod, which we do not implement, a description
215/// of the parsing is in order.
216/// The string is parsed as by strtol; that is, the base is recognized by leading ‘0’ or ‘0x’ prefixes.
217/// The number parsed is placed in the significand such that the least significant bit of the number is
218/// at the least significant bit of the significand.
219/// The number is truncated to fit the significand field provided.
220/// The significand is forced to be a quiet NaN.
221///
222/// If tagp contains any non-numeric characters, the function returns a NaN whose significand is zero.
223/// If tagp is empty, the function returns a NaN whose significand is zero.
224pub inline fn nanf(tagp: []const u8) f32 {
225 const parsed = std.fmt.parseUnsigned(c_ulong, tagp, 0) catch 0;
226 const bits: u23 = @truncate(parsed); // single-precision float trailing significand is 23 bits
227 return @bitCast(@as(u32, bits) | @as(u32, @bitCast(std.math.nan(f32))));
228}
229
230pub inline fn object_size(ptr: ?*const anyopaque, ty: c_int) usize {
231 _ = ptr;
232 // clang semantics match gcc's: https://gcc.gnu.org/onlinedocs/gcc/Object-Size-Checking.html
233 // If it is not possible to determine which objects ptr points to at compile time,
234 // object_size should return (size_t) -1 for type 0 or 1 and (size_t) 0
235 // for type 2 or 3.
236 if (ty == 0 or ty == 1) return @as(usize, @bitCast(-@as(isize, 1)));
237 if (ty == 2 or ty == 3) return 0;
238 unreachable;
239}
240
241/// popcount of a c_uint will never exceed the capacity of a c_int
242pub inline fn popcount(val: c_uint) c_int {
243 @setRuntimeSafety(false);
244 return @as(c_int, @bitCast(@as(c_uint, @popCount(val))));
245}
246
247pub inline fn roundf(val: f32) f32 {
248 return @round(val);
249}
250
251pub inline fn round(val: f64) f64 {
252 return @round(val);
253}
254
255pub inline fn signbitf(val: f32) c_int {
256 return @intFromBool(std.math.signbit(val));
257}
258
259pub inline fn signbit(val: f64) c_int {
260 return @intFromBool(std.math.signbit(val));
261}
262
263pub inline fn sinf(val: f32) f32 {
264 return @sin(val);
265}
266
267pub inline fn sin(val: f64) f64 {
268 return @sin(val);
269}
270
271pub inline fn sqrtf(val: f32) f32 {
272 return @sqrt(val);
273}
274
275pub inline fn sqrt(val: f64) f64 {
276 return @sqrt(val);
277}
278
279pub inline fn strcmp(s1: [*c]const u8, s2: [*c]const u8) c_int {
280 return switch (std.mem.orderZ(u8, s1, s2)) {
281 .lt => -1,
282 .eq => 0,
283 .gt => 1,
284 };
285}
286
287pub inline fn strlen(s: [*c]const u8) usize {
288 return std.mem.sliceTo(s, 0).len;
289}
290
291pub inline fn truncf(val: f32) f32 {
292 return @trunc(val);
293}
294
295pub inline fn trunc(val: f64) f64 {
296 return @trunc(val);
297}
298
299pub inline fn @"unreachable"() noreturn {
300 unreachable;
301}
lib/std/zig/c_translation/helpers.zig created+413
...@@ -0,0 +1,413 @@
1const std = @import("std");
2
3/// "Usual arithmetic conversions" from C11 standard 6.3.1.8
4pub fn ArithmeticConversion(comptime A: type, comptime B: type) type {
5 if (A == c_longdouble or B == c_longdouble) return c_longdouble;
6 if (A == f80 or B == f80) return f80;
7 if (A == f64 or B == f64) return f64;
8 if (A == f32 or B == f32) return f32;
9
10 const A_Promoted = PromotedIntType(A);
11 const B_Promoted = PromotedIntType(B);
12 comptime {
13 std.debug.assert(integerRank(A_Promoted) >= integerRank(c_int));
14 std.debug.assert(integerRank(B_Promoted) >= integerRank(c_int));
15 }
16
17 if (A_Promoted == B_Promoted) return A_Promoted;
18
19 const a_signed = @typeInfo(A_Promoted).int.signedness == .signed;
20 const b_signed = @typeInfo(B_Promoted).int.signedness == .signed;
21
22 if (a_signed == b_signed) {
23 return if (integerRank(A_Promoted) > integerRank(B_Promoted)) A_Promoted else B_Promoted;
24 }
25
26 const SignedType = if (a_signed) A_Promoted else B_Promoted;
27 const UnsignedType = if (!a_signed) A_Promoted else B_Promoted;
28
29 if (integerRank(UnsignedType) >= integerRank(SignedType)) return UnsignedType;
30
31 if (std.math.maxInt(SignedType) >= std.math.maxInt(UnsignedType)) return SignedType;
32
33 return ToUnsigned(SignedType);
34}
35
36/// Integer promotion described in C11 6.3.1.1.2
37fn PromotedIntType(comptime T: type) type {
38 return switch (T) {
39 bool, c_short => c_int,
40 c_ushort => if (@sizeOf(c_ushort) == @sizeOf(c_int)) c_uint else c_int,
41 c_int, c_uint, c_long, c_ulong, c_longlong, c_ulonglong => T,
42 else => switch (@typeInfo(T)) {
43 .comptime_int => @compileError("Cannot promote `" ++ @typeName(T) ++ "`; a fixed-size number type is required"),
44 // promote to c_int if it can represent all values of T
45 .int => |int_info| if (int_info.bits < @bitSizeOf(c_int))
46 c_int
47 // otherwise, restore the original C type
48 else if (int_info.bits == @bitSizeOf(c_int))
49 if (int_info.signedness == .unsigned) c_uint else c_int
50 else if (int_info.bits <= @bitSizeOf(c_long))
51 if (int_info.signedness == .unsigned) c_ulong else c_long
52 else if (int_info.bits <= @bitSizeOf(c_longlong))
53 if (int_info.signedness == .unsigned) c_ulonglong else c_longlong
54 else
55 @compileError("Cannot promote `" ++ @typeName(T) ++ "`; a C ABI type is required"),
56 else => @compileError("Attempted to promote invalid type `" ++ @typeName(T) ++ "`"),
57 },
58 };
59}
60
61/// C11 6.3.1.1.1
62fn integerRank(comptime T: type) u8 {
63 return switch (T) {
64 bool => 0,
65 u8, i8 => 1,
66 c_short, c_ushort => 2,
67 c_int, c_uint => 3,
68 c_long, c_ulong => 4,
69 c_longlong, c_ulonglong => 5,
70 else => @compileError("integer rank not supported for `" ++ @typeName(T) ++ "`"),
71 };
72}
73
74fn ToUnsigned(comptime T: type) type {
75 return switch (T) {
76 c_int => c_uint,
77 c_long => c_ulong,
78 c_longlong => c_ulonglong,
79 else => @compileError("Cannot convert `" ++ @typeName(T) ++ "` to unsigned"),
80 };
81}
82
83/// Constructs a [*c] pointer with the const and volatile annotations
84/// from SelfType for pointing to a C flexible array of ElementType.
85pub fn FlexibleArrayType(comptime SelfType: type, comptime ElementType: type) type {
86 switch (@typeInfo(SelfType)) {
87 .pointer => |ptr| {
88 return @Type(.{ .pointer = .{
89 .size = .c,
90 .is_const = ptr.is_const,
91 .is_volatile = ptr.is_volatile,
92 .alignment = @alignOf(ElementType),
93 .address_space = .generic,
94 .child = ElementType,
95 .is_allowzero = true,
96 .sentinel_ptr = null,
97 } });
98 },
99 else => |info| @compileError("Invalid self type \"" ++ @tagName(info) ++ "\" for flexible array getter: " ++ @typeName(SelfType)),
100 }
101}
102
103/// Promote the type of an integer literal until it fits as C would.
104pub fn promoteIntLiteral(
105 comptime SuffixType: type,
106 comptime number: comptime_int,
107 comptime base: CIntLiteralBase,
108) PromoteIntLiteralReturnType(SuffixType, number, base) {
109 return number;
110}
111
112const CIntLiteralBase = enum { decimal, octal, hex };
113
114fn PromoteIntLiteralReturnType(comptime SuffixType: type, comptime number: comptime_int, comptime base: CIntLiteralBase) type {
115 const signed_decimal = [_]type{ c_int, c_long, c_longlong, c_ulonglong };
116 const signed_oct_hex = [_]type{ c_int, c_uint, c_long, c_ulong, c_longlong, c_ulonglong };
117 const unsigned = [_]type{ c_uint, c_ulong, c_ulonglong };
118
119 const list: []const type = if (@typeInfo(SuffixType).int.signedness == .unsigned)
120 &unsigned
121 else if (base == .decimal)
122 &signed_decimal
123 else
124 &signed_oct_hex;
125
126 var pos = std.mem.indexOfScalar(type, list, SuffixType).?;
127 while (pos < list.len) : (pos += 1) {
128 if (number >= std.math.minInt(list[pos]) and number <= std.math.maxInt(list[pos])) {
129 return list[pos];
130 }
131 }
132
133 @compileError("Integer literal is too large");
134}
135
136/// Convert from clang __builtin_shufflevector index to Zig @shuffle index
137/// clang requires __builtin_shufflevector index arguments to be integer constants.
138/// negative values for `this_index` indicate "don't care".
139/// clang enforces that `this_index` is less than the total number of vector elements
140/// See https://ziglang.org/documentation/master/#shuffle
141/// See https://clang.llvm.org/docs/LanguageExtensions.html#langext-builtin-shufflevector
142pub fn shuffleVectorIndex(comptime this_index: c_int, comptime source_vector_len: usize) i32 {
143 const positive_index = std.math.cast(usize, this_index) orelse return undefined;
144 if (positive_index < source_vector_len) return @as(i32, @intCast(this_index));
145 const b_index = positive_index - source_vector_len;
146 return ~@as(i32, @intCast(b_index));
147}
148
149/// C `%` operator for signed integers
150/// C standard states: "If the quotient a/b is representable, the expression (a/b)*b + a%b shall equal a"
151/// The quotient is not representable if denominator is zero, or if numerator is the minimum integer for
152/// the type and denominator is -1. C has undefined behavior for those two cases; this function has safety
153/// checked undefined behavior
154pub fn signedRemainder(numerator: anytype, denominator: anytype) @TypeOf(numerator, denominator) {
155 std.debug.assert(@typeInfo(@TypeOf(numerator, denominator)).int.signedness == .signed);
156 if (denominator > 0) return @rem(numerator, denominator);
157 return numerator - @divTrunc(numerator, denominator) * denominator;
158}
159
160/// Given a type and value, cast the value to the type as c would.
161pub fn cast(comptime DestType: type, target: anytype) DestType {
162 // this function should behave like transCCast in translate-c, except it's for macros
163 const SourceType = @TypeOf(target);
164 switch (@typeInfo(DestType)) {
165 .@"fn" => return castToPtr(*const DestType, SourceType, target),
166 .pointer => return castToPtr(DestType, SourceType, target),
167 .optional => |dest_opt| {
168 if (@typeInfo(dest_opt.child) == .pointer) {
169 return castToPtr(DestType, SourceType, target);
170 } else if (@typeInfo(dest_opt.child) == .@"fn") {
171 return castToPtr(?*const dest_opt.child, SourceType, target);
172 }
173 },
174 .int => {
175 switch (@typeInfo(SourceType)) {
176 .pointer => {
177 return castInt(DestType, @intFromPtr(target));
178 },
179 .optional => |opt| {
180 if (@typeInfo(opt.child) == .pointer) {
181 return castInt(DestType, @intFromPtr(target));
182 }
183 },
184 .int => {
185 return castInt(DestType, target);
186 },
187 .@"fn" => {
188 return castInt(DestType, @intFromPtr(&target));
189 },
190 .bool => {
191 return @intFromBool(target);
192 },
193 else => {},
194 }
195 },
196 .float => {
197 switch (@typeInfo(SourceType)) {
198 .int => return @as(DestType, @floatFromInt(target)),
199 .float => return @as(DestType, @floatCast(target)),
200 .bool => return @as(DestType, @floatFromInt(@intFromBool(target))),
201 else => {},
202 }
203 },
204 .@"union" => |info| {
205 inline for (info.fields) |field| {
206 if (field.type == SourceType) return @unionInit(DestType, field.name, target);
207 }
208
209 @compileError("cast to union type '" ++ @typeName(DestType) ++ "' from type '" ++ @typeName(SourceType) ++ "' which is not present in union");
210 },
211 .bool => return cast(usize, target) != 0,
212 else => {},
213 }
214
215 return @as(DestType, target);
216}
217
218fn castInt(comptime DestType: type, target: anytype) DestType {
219 const dest = @typeInfo(DestType).int;
220 const source = @typeInfo(@TypeOf(target)).int;
221
222 const Int = @Type(.{ .int = .{ .bits = dest.bits, .signedness = source.signedness } });
223
224 if (dest.bits < source.bits)
225 return @as(DestType, @bitCast(@as(Int, @truncate(target))))
226 else
227 return @as(DestType, @bitCast(@as(Int, target)));
228}
229
230fn castPtr(comptime DestType: type, target: anytype) DestType {
231 return @constCast(@volatileCast(@alignCast(@ptrCast(target))));
232}
233
234fn castToPtr(comptime DestType: type, comptime SourceType: type, target: anytype) DestType {
235 switch (@typeInfo(SourceType)) {
236 .int => {
237 return @as(DestType, @ptrFromInt(castInt(usize, target)));
238 },
239 .comptime_int => {
240 if (target < 0)
241 return @as(DestType, @ptrFromInt(@as(usize, @bitCast(@as(isize, @intCast(target))))))
242 else
243 return @as(DestType, @ptrFromInt(@as(usize, @intCast(target))));
244 },
245 .pointer => {
246 return castPtr(DestType, target);
247 },
248 .@"fn" => {
249 return castPtr(DestType, &target);
250 },
251 .optional => |target_opt| {
252 if (@typeInfo(target_opt.child) == .pointer) {
253 return castPtr(DestType, target);
254 }
255 },
256 else => {},
257 }
258
259 return @as(DestType, target);
260}
261
262/// Given a value returns its size as C's sizeof operator would.
263pub fn sizeof(target: anytype) usize {
264 const T: type = if (@TypeOf(target) == type) target else @TypeOf(target);
265 switch (@typeInfo(T)) {
266 .float, .int, .@"struct", .@"union", .array, .bool, .vector => return @sizeOf(T),
267 .@"fn" => {
268 // sizeof(main) in C returns 1
269 return 1;
270 },
271 .null => return @sizeOf(*anyopaque),
272 .void => {
273 // Note: sizeof(void) is 1 on clang/gcc and 0 on MSVC.
274 return 1;
275 },
276 .@"opaque" => {
277 if (T == anyopaque) {
278 // Note: sizeof(void) is 1 on clang/gcc and 0 on MSVC.
279 return 1;
280 } else {
281 @compileError("Cannot use C sizeof on opaque type " ++ @typeName(T));
282 }
283 },
284 .optional => |opt| {
285 if (@typeInfo(opt.child) == .pointer) {
286 return sizeof(opt.child);
287 } else {
288 @compileError("Cannot use C sizeof on non-pointer optional " ++ @typeName(T));
289 }
290 },
291 .pointer => |ptr| {
292 if (ptr.size == .slice) {
293 @compileError("Cannot use C sizeof on slice type " ++ @typeName(T));
294 }
295
296 // for strings, sizeof("a") returns 2.
297 // normal pointer decay scenarios from C are handled
298 // in the .array case above, but strings remain literals
299 // and are therefore always pointers, so they need to be
300 // specially handled here.
301 if (ptr.size == .one and ptr.is_const and @typeInfo(ptr.child) == .array) {
302 const array_info = @typeInfo(ptr.child).array;
303 if ((array_info.child == u8 or array_info.child == u16) and array_info.sentinel() == 0) {
304 // length of the string plus one for the null terminator.
305 return (array_info.len + 1) * @sizeOf(array_info.child);
306 }
307 }
308
309 // When zero sized pointers are removed, this case will no
310 // longer be reachable and can be deleted.
311 if (@sizeOf(T) == 0) {
312 return @sizeOf(*anyopaque);
313 }
314
315 return @sizeOf(T);
316 },
317 .comptime_float => return @sizeOf(f64), // TODO c_double #3999
318 .comptime_int => {
319 // TODO to get the correct result we have to translate
320 // `1073741824 * 4` as `int(1073741824) *% int(4)` since
321 // sizeof(1073741824 * 4) != sizeof(4294967296).
322
323 // TODO test if target fits in int, long or long long
324 return @sizeOf(c_int);
325 },
326 else => @compileError("__helpers.sizeof does not support type " ++ @typeName(T)),
327 }
328}
329
330pub fn div(a: anytype, b: anytype) ArithmeticConversion(@TypeOf(a), @TypeOf(b)) {
331 const ResType = ArithmeticConversion(@TypeOf(a), @TypeOf(b));
332 const a_casted = cast(ResType, a);
333 const b_casted = cast(ResType, b);
334 switch (@typeInfo(ResType)) {
335 .float => return a_casted / b_casted,
336 .int => return @divTrunc(a_casted, b_casted),
337 else => unreachable,
338 }
339}
340
341pub fn rem(a: anytype, b: anytype) ArithmeticConversion(@TypeOf(a), @TypeOf(b)) {
342 const ResType = ArithmeticConversion(@TypeOf(a), @TypeOf(b));
343 const a_casted = cast(ResType, a);
344 const b_casted = cast(ResType, b);
345 switch (@typeInfo(ResType)) {
346 .int => {
347 if (@typeInfo(ResType).int.signedness == .signed) {
348 return signedRemainder(a_casted, b_casted);
349 } else {
350 return a_casted % b_casted;
351 }
352 },
353 else => unreachable,
354 }
355}
356
357/// A 2-argument function-like macro defined as #define FOO(A, B) (A)(B)
358/// could be either: cast B to A, or call A with the value B.
359pub fn CAST_OR_CALL(a: anytype, b: anytype) switch (@typeInfo(@TypeOf(a))) {
360 .type => a,
361 .@"fn" => |fn_info| fn_info.return_type orelse void,
362 else => |info| @compileError("Unexpected argument type: " ++ @tagName(info)),
363} {
364 switch (@typeInfo(@TypeOf(a))) {
365 .type => return cast(a, b),
366 .@"fn" => return a(b),
367 else => unreachable, // return type will be a compile error otherwise
368 }
369}
370
371pub inline fn DISCARD(x: anytype) void {
372 _ = x;
373}
374
375pub fn F_SUFFIX(comptime f: comptime_float) f32 {
376 return @as(f32, f);
377}
378
379fn L_SUFFIX_ReturnType(comptime number: anytype) type {
380 switch (@typeInfo(@TypeOf(number))) {
381 .int, .comptime_int => return @TypeOf(promoteIntLiteral(c_long, number, .decimal)),
382 .float, .comptime_float => return c_longdouble,
383 else => @compileError("Invalid value for L suffix"),
384 }
385}
386
387pub fn L_SUFFIX(comptime number: anytype) L_SUFFIX_ReturnType(number) {
388 switch (@typeInfo(@TypeOf(number))) {
389 .int, .comptime_int => return promoteIntLiteral(c_long, number, .decimal),
390 .float, .comptime_float => @compileError("TODO: c_longdouble initialization from comptime_float not supported"),
391 else => @compileError("Invalid value for L suffix"),
392 }
393}
394
395pub fn LL_SUFFIX(comptime n: comptime_int) @TypeOf(promoteIntLiteral(c_longlong, n, .decimal)) {
396 return promoteIntLiteral(c_longlong, n, .decimal);
397}
398
399pub fn U_SUFFIX(comptime n: comptime_int) @TypeOf(promoteIntLiteral(c_uint, n, .decimal)) {
400 return promoteIntLiteral(c_uint, n, .decimal);
401}
402
403pub fn UL_SUFFIX(comptime n: comptime_int) @TypeOf(promoteIntLiteral(c_ulong, n, .decimal)) {
404 return promoteIntLiteral(c_ulong, n, .decimal);
405}
406
407pub fn ULL_SUFFIX(comptime n: comptime_int) @TypeOf(promoteIntLiteral(c_ulonglong, n, .decimal)) {
408 return promoteIntLiteral(c_ulonglong, n, .decimal);
409}
410
411pub fn WL_CONTAINER_OF(ptr: anytype, sample: anytype, comptime member: []const u8) @TypeOf(sample) {
412 return @fieldParentPtr(member, ptr);
413}