1pub const CType = union(enum) {
2 pub const render_defs = @import("type/render_defs.zig");
3
4 // The first nodes are primitive types (or standard typedefs).
5
6 void,
7 bool,
8 int: Int,
9 float: Float,
10
11 // These next nodes are all typedefs, structs, or unions.
12
13 @"fn": Type,
14 @"enum": Type,
15 bitpack: Type,
16 @"struct": Type,
17 union_auto: Type,
18 union_extern: Type,
19 slice: Type,
20 opt: Type,
21 arr: Type,
22 vec: Type,
23 errunion: struct { payload_ty: Type },
24 aligned: struct {
25 ty: Type,
26 alignment: InternPool.Alignment,
27 },
28 bigint: BigInt,
29
30 // The remaining nodes have children.
31
32 pointer: struct {
33 @"const": bool,
34 @"volatile": bool,
35 elem_ty: *const CType,
36 nonstring: bool,
37 },
38 array: struct {
39 len: u64,
40 elem_ty: *const CType,
41 nonstring: bool,
42 },
43 function: struct {
44 param_tys: []const CType,
45 ret_ty: *const CType,
46 varargs: bool,
47 cc: CallingConvention,
48 },
49
50 pub const CallingConvention = enum {
51 c,
52
53 cdecl,
54 regparmcall,
55 sysv_abi,
56 ms_abi,
57 stdcall,
58 fastcall,
59 thiscall,
60
61 vectorcall,
62
63 regcall,
64
65 preserve_none,
66
67 aarch64_vector_pcs,
68 aarch64_sve_pcs,
69
70 @"pcs(\"aapcs\")",
71 @"pcs(\"aapcs-vfp\")",
72
73 @"interrupt(\"ilink1\")",
74 @"interrupt(\"ilink2\")",
75 @"interrupt(\"ilink\")",
76 @"interrupt(\"firq\")",
77
78 interrupt,
79 @"interrupt(\"IRQ\")",
80 @"interrupt(\"FIQ\")",
81 @"interrupt(\"SWI\")",
82 @"interrupt(\"ABORT\")",
83 @"interrupt(\"UNDEF\")",
84
85 signal,
86
87 save_volatiles,
88 interrupt_handler,
89 fast_interrupt,
90 break_handler,
91
92 @"interrupt(\"eic\")",
93 @"interrupt(\"sw0\")",
94 @"interrupt(\"sw1\")",
95 @"interrupt(\"hw0\")",
96 @"interrupt(\"hw1\")",
97 @"interrupt(\"hw2\")",
98 @"interrupt(\"hw3\")",
99 @"interrupt(\"hw4\")",
100 @"interrupt(\"hw5\")",
101
102 riscv_vector_cc,
103 @"interrupt(\"supervisor\")",
104 @"interrupt(\"machine\")",
105
106 renesas,
107 /// Implies `interrupt_handler`.
108 trapa_handler,
109 @"interrupt_handler, nosave_low_regs",
110 @"interrupt_handler, resbank",
111
112 m68k_rtd,
113
114 tiflags,
115
116 pub fn fromLang(cc: std.lang.CallingConvention, target: *const std.Target) CallingConvention {
117 if (target.cCallingConvention()) |ccc| {
118 if (cc.eql(ccc)) {
119 return .c;
120 }
121 }
122 return switch (cc) {
123 .auto, .naked => .c,
124
125 .x86_16_cdecl => .cdecl,
126 .x86_16_regparmcall => .regparmcall,
127 .x86_64_sysv, .x86_sysv => .sysv_abi,
128 .x86_64_win, .x86_win, .x86_mingw => .ms_abi,
129 .x86_16_stdcall, .x86_stdcall => .stdcall,
130 .x86_fastcall => .fastcall,
131 .x86_thiscall => .thiscall,
132
133 .x86_vectorcall,
134 .x86_64_vectorcall,
135 => .vectorcall,
136
137 .x86_64_regcall_v3_sysv,
138 .x86_64_regcall_v4_win,
139 .x86_regcall_v3,
140 .x86_regcall_v4_win,
141 => .regcall,
142
143 .x86_64_preserve_none,
144 .aarch64_preserve_none,
145 => .preserve_none,
146
147 .aarch64_vfabi => .aarch64_vector_pcs,
148 .aarch64_vfabi_sve => .aarch64_sve_pcs,
149
150 .arm_aapcs => .@"pcs(\"aapcs\")",
151 .arm_aapcs_vfp => .@"pcs(\"aapcs-vfp\")",
152
153 .arc_interrupt => |opts| switch (opts.type) {
154 .ilink1 => .@"interrupt(\"ilink1\")",
155 .ilink2 => .@"interrupt(\"ilink2\")",
156 .ilink => .@"interrupt(\"ilink\")",
157 .firq => .@"interrupt(\"firq\")",
158 },
159
160 .arm_interrupt => |opts| switch (opts.type) {
161 .generic => .interrupt,
162 .irq => .@"interrupt(\"IRQ\")",
163 .fiq => .@"interrupt(\"FIQ\")",
164 .swi => .@"interrupt(\"SWI\")",
165 .abort => .@"interrupt(\"ABORT\")",
166 .undef => .@"interrupt(\"UNDEF\")",
167 },
168
169 .avr_signal => .signal,
170
171 .microblaze_interrupt => |opts| switch (opts.type) {
172 .user => .save_volatiles,
173 .regular => .interrupt_handler,
174 .fast => .fast_interrupt,
175 .breakpoint => .break_handler,
176 },
177
178 .mips_interrupt, .mips64_interrupt => |opts| switch (opts.mode) {
179 .eic => .@"interrupt(\"eic\")",
180 .sw0 => .@"interrupt(\"sw0\")",
181 .sw1 => .@"interrupt(\"sw1\")",
182 .hw0 => .@"interrupt(\"hw0\")",
183 .hw1 => .@"interrupt(\"hw1\")",
184 .hw2 => .@"interrupt(\"hw2\")",
185 .hw3 => .@"interrupt(\"hw3\")",
186 .hw4 => .@"interrupt(\"hw4\")",
187 .hw5 => .@"interrupt(\"hw5\")",
188 },
189
190 .riscv64_lp64_v, .riscv32_ilp32_v => .riscv_vector_cc,
191 .riscv32_interrupt, .riscv64_interrupt => |opts| switch (opts.mode) {
192 .supervisor => .@"interrupt(\"supervisor\")",
193 .machine => .@"interrupt(\"machine\")",
194 },
195
196 .sh_renesas => .renesas,
197 .sh_interrupt => |opts| switch (opts.save) {
198 .fpscr => .trapa_handler,
199 .high => .@"interrupt_handler, nosave_low_regs",
200 .full => .interrupt_handler,
201 .bank => .@"interrupt_handler, resbank",
202 },
203
204 .m68k_rtd => .m68k_rtd,
205
206 .avr_interrupt,
207 .csky_interrupt,
208 .m68k_interrupt,
209 .msp430_interrupt,
210 .x86_16_interrupt,
211 .x86_interrupt,
212 .x86_64_interrupt,
213 => .interrupt,
214
215 .ez80_tiflags => .tiflags,
216
217 else => unreachable, // `Zcu.callconvSupported`
218 };
219 }
220 };
221
222 /// Returns `true` if this node has a postfix operator, meaning an `[...]` or `(...)` appears
223 /// after the identifier in a declarator with this type. In this case, if this node is wrapped
224 /// in a pointer type, we will need to add parentheses due to operator precedence.
225 ///
226 /// For instance, when lowering a Zig declaration `foo: *const fn (c_int) void`, it would be a
227 /// bug to write the C declarator as `void *foo(int)`, because the `(int)` suffix declaring the
228 /// function type has higher precedence than the `*` prefix declaring the pointer type. Instead,
229 /// this type must be lowered as `void (*foo)(int)`.
230 fn kind(cty: *const CType) enum {
231 /// `cty` is just a C type specifier, i.e. a typedef or a named struct/union type.
232 specifier,
233 /// `cty` is a C function or array type. It will have a postfix "operator" in its suffix to
234 /// declare the type, either `(...)` (for a function type) or `[...]` (for an array type).
235 postfix_op,
236 /// `cty` is a C pointer type. Its prefix will end with "*".
237 pointer,
238 } {
239 return switch (cty.*) {
240 .void,
241 .bool,
242 .int,
243 .float,
244 .@"fn",
245 .@"enum",
246 .bitpack,
247 .@"struct",
248 .union_auto,
249 .union_extern,
250 .slice,
251 .opt,
252 .arr,
253 .vec,
254 .errunion,
255 .aligned,
256 .bigint,
257 => .specifier,
258
259 .array,
260 .function,
261 => .postfix_op,
262
263 .pointer => .pointer,
264 };
265 }
266
267 pub const Int = enum {
268 char,
269
270 @"unsigned short",
271 @"unsigned int",
272 @"unsigned long",
273 @"unsigned long long",
274
275 @"signed short",
276 @"signed int",
277 @"signed long",
278 @"signed long long",
279
280 uint8_t,
281 uint16_t,
282 /// eZ80-specific
283 uint24_t,
284 uint32_t,
285 /// eZ80-specific
286 uint48_t,
287 uint64_t,
288 zig_u128,
289
290 int8_t,
291 int16_t,
292 /// eZ80-specific
293 int24_t,
294 int32_t,
295 /// eZ80-specific
296 int48_t,
297 int64_t,
298 zig_i128,
299
300 uintptr_t,
301 intptr_t,
302
303 pub fn bits(int: Int, target: *const std.Target) u16 {
304 return switch (int) {
305 // zig fmt: off
306 .char => target.cTypeBitSize(.char).?,
307
308 .@"unsigned short" => target.cTypeBitSize(.ushort).?,
309 .@"unsigned int" => target.cTypeBitSize(.uint).?,
310 .@"unsigned long" => target.cTypeBitSize(.ulong).?,
311 .@"unsigned long long" => target.cTypeBitSize(.ulonglong).?,
312
313 .@"signed short" => target.cTypeBitSize(.short).?,
314 .@"signed int" => target.cTypeBitSize(.int).?,
315 .@"signed long" => target.cTypeBitSize(.long).?,
316 .@"signed long long" => target.cTypeBitSize(.longlong).?,
317
318 .uintptr_t, .intptr_t => target.ptrBitWidth(),
319
320 .uint8_t, .int8_t => 8,
321 .uint16_t, .int16_t => 16,
322 .uint24_t, .int24_t => 24,
323 .uint32_t, .int32_t => 32,
324 .uint48_t, .int48_t => 48,
325 .uint64_t, .int64_t => 64,
326 .zig_u128, .zig_i128 => 128,
327 // zig fmt: on
328 };
329 }
330 };
331
332 pub const BigInt = struct {
333 limb_size: LimbSize,
334 /// Always greater than 1.
335 limbs_len: u16,
336
337 pub const LimbSize = enum {
338 @"8",
339 @"16",
340 @"24",
341 @"32",
342 @"64",
343 @"128",
344 pub fn bits(s: LimbSize) u8 {
345 return switch (s) {
346 .@"8" => 8,
347 .@"16" => 16,
348 .@"24" => 24,
349 .@"32" => 32,
350 .@"64" => 64,
351 .@"128" => 128,
352 };
353 }
354 pub fn unsigned(s: LimbSize) Int {
355 return switch (s) {
356 .@"8" => .uint8_t,
357 .@"16" => .uint16_t,
358 .@"24" => .uint24_t,
359 .@"32" => .uint32_t,
360 .@"64" => .uint64_t,
361 .@"128" => .zig_u128,
362 };
363 }
364 pub fn signed(s: LimbSize) Int {
365 return switch (s) {
366 .@"8" => .int8_t,
367 .@"16" => .int16_t,
368 .@"24" => .int24_t,
369 .@"32" => .int32_t,
370 .@"64" => .int64_t,
371 .@"128" => .zig_i128,
372 };
373 }
374 };
375 };
376
377 pub const Float = enum {
378 @"long double",
379 zig_f16,
380 zig_f32,
381 zig_f64,
382 zig_f80,
383 zig_f128,
384 zig_u128,
385 zig_i128,
386 };
387
388 pub fn isStringElem(cty: CType) bool {
389 return switch (cty) {
390 .int => |int| switch (int) {
391 .char, .int8_t, .uint8_t => true,
392 else => false,
393 },
394 else => false,
395 };
396 }
397
398 pub fn lower(
399 ty: Type,
400 deps: *Dependencies,
401 arena: Allocator,
402 zcu: *const Zcu,
403 ) Allocator.Error!CType {
404 return lowerInner(ty, false, deps, arena, zcu);
405 }
406 fn lowerInner(
407 start_ty: Type,
408 allow_incomplete: bool,
409 deps: *Dependencies,
410 arena: Allocator,
411 zcu: *const Zcu,
412 ) Allocator.Error!CType {
413 const gpa = zcu.comp.gpa;
414 const ip = &zcu.intern_pool;
415 var cur_ty = start_ty;
416 while (true) {
417 switch (cur_ty.zigTypeTag(zcu)) {
418 .type,
419 .comptime_int,
420 .comptime_float,
421 .undefined,
422 .null,
423 .enum_literal,
424 .@"opaque",
425 .spirv,
426 .noreturn,
427 .void,
428 => return .void,
429
430 .bool => return .bool,
431
432 .int, .error_set => switch (classifyInt(cur_ty, zcu)) {
433 .void => return .void,
434 .small => |s| return .{ .int = s },
435 .big => |big| {
436 try deps.bigint.put(gpa, big, {});
437 return .{ .bigint = big };
438 },
439 },
440
441 .float => return .{ .float = switch (cur_ty.toIntern()) {
442 .c_longdouble_type => .@"long double",
443 .f16_type => .zig_f16,
444 .f32_type => .zig_f32,
445 .f64_type => .zig_f64,
446 .f80_type => .zig_f80,
447 .f128_type => .zig_f128,
448 else => unreachable,
449 } },
450 .vector => {
451 try deps.addType(gpa, cur_ty, allow_incomplete);
452 return .{ .vec = cur_ty };
453 },
454 .array => {
455 try deps.addType(gpa, cur_ty, allow_incomplete);
456 return .{ .arr = cur_ty };
457 },
458
459 .pointer => {
460 const ptr = cur_ty.ptrInfo(zcu);
461 switch (ptr.flags.size) {
462 .slice => {
463 try deps.addType(gpa, cur_ty, allow_incomplete);
464 return .{ .slice = cur_ty };
465 },
466 .one, .many, .c => {
467 const elem_ty: Type = .fromInterned(ptr.child);
468 const is_fn_ptr = elem_ty.zigTypeTag(zcu) == .@"fn";
469 const elem_cty: CType = elem_cty: {
470 if (ptr.packed_offset.host_size > 0 and ptr.flags.vector_index == .none) {
471 switch (classifyBitInt(.unsigned, ptr.packed_offset.host_size * 8, zcu)) {
472 .void => break :elem_cty .void,
473 .small => |s| break :elem_cty .{ .int = s },
474 .big => |big| {
475 try deps.bigint.put(gpa, big, {});
476 break :elem_cty .{ .bigint = big };
477 },
478 }
479 }
480 if (ptr.flags.alignment != .none and !is_fn_ptr) {
481 // The pointer has an explicit alignment---if it's an underalignment
482 // then we need to use an "aligned" typedef.
483 const ptr_align = ptr.flags.alignment;
484 if (!alwaysHasLayout(elem_ty, ip) or
485 ptr_align.compareStrict(.lt, elem_ty.abiAlignment(zcu)))
486 {
487 const gop = try deps.aligned_type_fwd.getOrPut(gpa, elem_ty.toIntern());
488 if (!gop.found_existing) gop.value_ptr.* = 0;
489 gop.value_ptr.* |= @as(u64, 1) << ptr_align.toLog2Units();
490 break :elem_cty .{ .aligned = .{
491 .ty = elem_ty,
492 .alignment = ptr_align,
493 } };
494 }
495 }
496 break :elem_cty try .lowerInner(elem_ty, true, deps, arena, zcu);
497 };
498 const elem_cty_buf = try arena.create(CType);
499 elem_cty_buf.* = elem_cty;
500 return .{ .pointer = .{
501 .@"const" = ptr.flags.is_const and !is_fn_ptr,
502 .@"volatile" = ptr.flags.is_volatile and !is_fn_ptr,
503 .elem_ty = elem_cty_buf,
504 .nonstring = nonstring: {
505 if (!elem_cty.isStringElem()) break :nonstring false;
506 if (ptr.sentinel == .none) break :nonstring true;
507 break :nonstring Value.compareHetero(
508 .fromInterned(ptr.sentinel),
509 .neq,
510 .zero_comptime_int,
511 zcu,
512 );
513 },
514 } };
515 },
516 }
517 },
518
519 .@"fn" => {
520 const func_type = ip.indexToKey(cur_ty.toIntern()).func_type;
521 direct: {
522 const ret_ty: Type = .fromInterned(func_type.return_type);
523 if (!alwaysHasLayout(ret_ty, ip)) break :direct;
524 var params_len: usize = 0; // only counts parameter types with runtime bits
525 for (func_type.param_types.get(ip)) |param_ty_ip| {
526 const param_ty: Type = .fromInterned(param_ty_ip);
527 if (!alwaysHasLayout(param_ty, ip)) break :direct;
528 if (param_ty.hasRuntimeBits(zcu)) params_len += 1;
529 }
530 // We can actually write this function type directly!
531 if (!cur_ty.fnHasRuntimeBits(zcu)) return .void;
532 const ret_cty_buf = try arena.create(CType);
533 if (!ret_ty.hasRuntimeBits(zcu)) {
534 // Incomplete function return types must always be `void`.
535 ret_cty_buf.* = .void;
536 } else {
537 ret_cty_buf.* = try .lowerInner(ret_ty, allow_incomplete, deps, arena, zcu);
538 }
539 const param_cty_buf = try arena.alloc(CType, params_len);
540 var param_index: usize = 0;
541 for (func_type.param_types.get(ip)) |param_ty_ip| {
542 const param_ty: Type = .fromInterned(param_ty_ip);
543 if (!param_ty.hasRuntimeBits(zcu)) continue;
544 param_cty_buf[param_index] = try .lowerInner(param_ty, allow_incomplete, deps, arena, zcu);
545 param_index += 1;
546 }
547 assert(param_index == params_len);
548 return .{ .function = .{
549 .ret_ty = ret_cty_buf,
550 .param_tys = param_cty_buf,
551 .varargs = func_type.is_var_args,
552 .cc = .fromLang(func_type.cc, zcu.getTarget()),
553 } };
554 }
555 try deps.addType(gpa, cur_ty, allow_incomplete);
556 return .{ .@"fn" = cur_ty };
557 },
558
559 .@"struct" => {
560 try deps.addType(gpa, cur_ty, allow_incomplete);
561 switch (cur_ty.containerLayout(zcu)) {
562 .auto, .@"extern" => return .{ .@"struct" = cur_ty },
563 .@"packed" => return .{ .bitpack = cur_ty },
564 }
565 },
566 .@"union" => {
567 try deps.addType(gpa, cur_ty, allow_incomplete);
568 switch (cur_ty.containerLayout(zcu)) {
569 .auto => return .{ .union_auto = cur_ty },
570 .@"extern" => return .{ .union_extern = cur_ty },
571 .@"packed" => return .{ .bitpack = cur_ty },
572 }
573 },
574 .@"enum" => {
575 try deps.addType(gpa, cur_ty, allow_incomplete);
576 return .{ .@"enum" = cur_ty };
577 },
578
579 .optional => {
580 // This query does not require any type resolution.
581 if (cur_ty.optionalReprIsPayload(zcu)) {
582 // Either a pointer-like optional, or an optional error set. Just lower the payload.
583 cur_ty = cur_ty.optionalChild(zcu);
584 continue;
585 }
586 if (alwaysHasLayout(cur_ty, ip)) switch (classifyOptional(cur_ty, zcu)) {
587 .error_set, .ptr_like, .slice_like => unreachable, // handled above
588 .npv_payload => return .void,
589 .opv_payload, .@"struct" => {},
590 };
591 try deps.addType(gpa, cur_ty, allow_incomplete);
592 return .{ .opt = cur_ty };
593 },
594
595 .error_union => {
596 const payload_ty = cur_ty.errorUnionPayload(zcu);
597 if (allow_incomplete) {
598 try deps.errunion_type_fwd.put(gpa, payload_ty.toIntern(), {});
599 } else {
600 try deps.errunion_type.put(gpa, payload_ty.toIntern(), {});
601 }
602 return .{ .errunion = .{
603 .payload_ty = payload_ty,
604 } };
605 },
606
607 .frame,
608 .@"anyframe",
609 => unreachable,
610 }
611 comptime unreachable;
612 }
613 }
614
615 pub fn classifyOptional(opt_ty: Type, zcu: *const Zcu) enum {
616 /// The optional is something like `?noreturn`; it lowers to `void`.
617 npv_payload,
618 /// The payload type is an error set; the representation matches that of the error set, with
619 /// the value 0 representing `null`.
620 error_set,
621 /// The payload type is a non-optional pointer; the NULL pointer is used for `null`.
622 ptr_like,
623 /// The payload type is a non-optional slice; a NULL pointer field is used for `null`.
624 slice_like,
625 /// The optional is something like `?void`; it lowers to a struct, but one containing only
626 /// one field `is_null` (the payload is omitted).
627 opv_payload,
628 /// The optional uses the "default" lowering of a struct with two fields, like this:
629 /// struct optional_1234 { payload_ty payload; bool is_null; }
630 @"struct",
631 } {
632 const payload_ty = opt_ty.optionalChild(zcu);
633 if (opt_ty.optionalReprIsPayload(zcu)) {
634 return switch (payload_ty.zigTypeTag(zcu)) {
635 .error_set => .error_set,
636 .pointer => if (payload_ty.isSlice(zcu)) .slice_like else .ptr_like,
637 else => unreachable,
638 };
639 } else {
640 return switch (payload_ty.classify(zcu)) {
641 .no_possible_value => .npv_payload,
642 .one_possible_value => .opv_payload,
643 else => .@"struct",
644 };
645 }
646 }
647
648 pub const IntClass = union(enum) {
649 /// The integer type is zero-bit, so lowers to `void`.
650 void,
651 /// The integer is under 128 bits long, so lowers to this C integer type.
652 small: Int,
653 /// The integer is over 128 bits long, so lowers to an array of limbs.
654 big: BigInt,
655 };
656
657 /// Asserts that `ty` is an integer, enum, bitpack, or error set.
658 pub fn classifyInt(ty: Type, zcu: *const Zcu) IntClass {
659 const int_ty: Type = switch (ty.zigTypeTag(zcu)) {
660 .error_set => return classifyBitInt(.unsigned, zcu.errorSetBits(), zcu),
661 .@"enum", .@"struct", .@"union" => ty.backingIntType(zcu),
662 .int => ty,
663 else => unreachable,
664 };
665 switch (int_ty.toIntern()) {
666 // zig fmt: off
667 .usize_type => return .{ .small = .uintptr_t },
668 .isize_type => return .{ .small = .intptr_t },
669
670 .c_char_type => return .{ .small = .char },
671
672 .c_short_type => return .{ .small = .@"signed short" },
673 .c_int_type => return .{ .small = .@"signed int" },
674 .c_long_type => return .{ .small = .@"signed long" },
675 .c_longlong_type => return .{ .small = .@"signed long long" },
676
677 .c_ushort_type => return .{ .small = .@"unsigned short" },
678 .c_uint_type => return .{ .small = .@"unsigned int" },
679 .c_ulong_type => return .{ .small = .@"unsigned long" },
680 .c_ulonglong_type => return .{ .small = .@"unsigned long long" },
681 // zig fmt: on
682
683 else => {
684 const int = ty.intInfo(zcu);
685 return classifyBitInt(int.signedness, int.bits, zcu);
686 },
687 }
688 }
689 fn classifyBitInt(signedness: std.lang.Signedness, bits: u16, zcu: *const Zcu) IntClass {
690 const target = zcu.getTarget();
691 return switch (std.zig.target.intByteSize(target, bits)) {
692 0 => .void,
693 1 => switch (signedness) {
694 .unsigned => .{ .small = .uint8_t },
695 .signed => .{ .small = .int8_t },
696 },
697 2 => switch (signedness) {
698 .unsigned => .{ .small = .uint16_t },
699 .signed => .{ .small = .int16_t },
700 },
701 3 => switch (signedness) {
702 .unsigned => .{ .small = .uint24_t },
703 .signed => .{ .small = .int24_t },
704 },
705 4 => switch (signedness) {
706 .unsigned => .{ .small = .uint32_t },
707 .signed => .{ .small = .int32_t },
708 },
709 6 => switch (signedness) {
710 .unsigned => .{ .small = .uint48_t },
711 .signed => .{ .small = .int48_t },
712 },
713 8 => switch (signedness) {
714 .unsigned => .{ .small = .uint64_t },
715 .signed => .{ .small = .int64_t },
716 },
717 16 => switch (signedness) {
718 .unsigned => .{ .small = .zig_u128 },
719 .signed => .{ .small = .zig_i128 },
720 },
721 else => |n| {
722 @branchHint(.unlikely);
723 const limb_bytes = std.zig.target.intAlignment(target, bits);
724 return .{ .big = .{
725 .limb_size = switch (limb_bytes) {
726 1 => .@"8",
727 2 => .@"16",
728 4 => .@"32",
729 8 => .@"64",
730 16 => .@"128",
731 else => unreachable,
732 },
733 .limbs_len = @divExact(n, limb_bytes),
734 } };
735 },
736 };
737 }
738
739 /// Describes a set of types which must be declared or completed in the C source file before
740 /// some string of rendered C code (such as a function), due to said C code using these types.
741 pub const Dependencies = struct {
742 /// Key is any Zig type which corresponds to a C `struct`, `union`, or `typedef`. That C
743 /// type must be declared and complete.
744 type: std.array_hash_map.Auto(InternPool.Index, void),
745
746 /// Key is a Zig type which is the *payload* of an error union. The C `struct` type
747 /// corresponding to such an error union must be declared and complete.
748 ///
749 /// These are separate from `type` to avoid redundant types for every different error set
750 /// used with the same payload type---for instance a different C type for every `E!void`.
751 errunion_type: std.array_hash_map.Auto(InternPool.Index, void),
752
753 /// Like `type`, but the type does not necessarily need to be completed yet: a forward
754 /// declaration is sufficient.
755 type_fwd: std.array_hash_map.Auto(InternPool.Index, void),
756
757 /// Like `errunion_type`, but the type does not necessarily need to be completed yet: a
758 /// forward declaration is sufficient.
759 errunion_type_fwd: std.array_hash_map.Auto(InternPool.Index, void),
760
761 /// Key is a Zig type; value is a bitmask of alignments. For every bit which is set, an
762 /// aligned typedef is required. For instance, if bit 3 is set, the C type 'aligned__8_foo'
763 /// must be declared through `typedef` (but not necessarily completed yet).
764 aligned_type_fwd: std.array_hash_map.Auto(InternPool.Index, u64),
765
766 /// Key specifies a big-int type whose C `struct` must be declared and complete.
767 bigint: std.array_hash_map.Auto(BigInt, void),
768
769 pub const empty: Dependencies = .{
770 .type = .empty,
771 .errunion_type = .empty,
772 .type_fwd = .empty,
773 .errunion_type_fwd = .empty,
774 .aligned_type_fwd = .empty,
775 .bigint = .empty,
776 };
777
778 pub fn deinit(deps: *Dependencies, gpa: Allocator) void {
779 deps.type.deinit(gpa);
780 deps.errunion_type.deinit(gpa);
781 deps.type_fwd.deinit(gpa);
782 deps.errunion_type_fwd.deinit(gpa);
783 deps.aligned_type_fwd.deinit(gpa);
784 deps.bigint.deinit(gpa);
785 }
786
787 pub fn clearRetainingCapacity(deps: *Dependencies) void {
788 deps.type.clearRetainingCapacity();
789 deps.errunion_type.clearRetainingCapacity();
790 deps.type_fwd.clearRetainingCapacity();
791 deps.errunion_type_fwd.clearRetainingCapacity();
792 deps.aligned_type_fwd.clearRetainingCapacity();
793 deps.bigint.clearRetainingCapacity();
794 }
795
796 pub fn move(deps: *Dependencies) Dependencies {
797 const moved = deps.*;
798 deps.* = .empty;
799 return moved;
800 }
801
802 fn addType(deps: *Dependencies, gpa: Allocator, ty: Type, allow_incomplete: bool) Allocator.Error!void {
803 if (allow_incomplete) {
804 try deps.type_fwd.put(gpa, ty.toIntern(), {});
805 } else {
806 try deps.type.put(gpa, ty.toIntern(), {});
807 }
808 }
809 };
810
811 /// Formats the bytes which appear *before* the identifier in a declarator. This includes the
812 /// type specifier and all "prefix type operators" in the declarator. e.g:
813 /// * for the declarator "int foo", writes "int "
814 /// * for the declarator "struct thing *foo", writes "struct thing *"
815 /// * for the declarator "void *(*foo)(int)", writes "void *(*"
816 pub fn fmtDeclaratorPrefix(cty: CType, zcu: *const Zcu) Formatter {
817 return .{
818 .cty = cty,
819 .zcu = zcu,
820 .kind = .declarator_prefix,
821 };
822 }
823 /// Formats the bytes which appear *before* the identifier in a declarator. This includes the
824 /// type specifier and all "prefix type operators" in the declarator. e.g:
825 /// * for the declarator "int foo", writes ""
826 /// * for the declarator "struct thing *foo", writes ""
827 /// * for the declarator "void *(*foo)(int)", writes ")(int)"
828 pub fn fmtDeclaratorSuffix(cty: CType, zcu: *const Zcu) Formatter {
829 return .{
830 .cty = cty,
831 .zcu = zcu,
832 .kind = .declarator_suffix,
833 };
834 }
835 /// Like `fmtDeclaratorSuffix`, except never emits a `zig_nonstring` annotation.
836 pub fn fmtDeclaratorSuffixIgnoreNonstring(cty: CType, zcu: *const Zcu) Formatter {
837 return .{
838 .cty = cty,
839 .zcu = zcu,
840 .kind = .declarator_suffix_ignore_nonstring,
841 };
842 }
843 /// Formats a type's full name, e.g. "int", "struct foo *", "void *(uint32_t)".
844 ///
845 /// This is almost identical to `fmtDeclaratorPrefix` followed by `fmtDeclaratorSuffix`, but
846 /// that sequence of calls may emit trailing whitespace where this one does not---for instance,
847 /// those calls would write the type "void" as "void ".
848 pub fn fmtTypeName(cty: CType, zcu: *const Zcu) Formatter {
849 return .{
850 .cty = cty,
851 .zcu = zcu,
852 .kind = .type_name,
853 };
854 }
855
856 const Formatter = struct {
857 cty: CType,
858 zcu: *const Zcu,
859 kind: enum { type_name, declarator_prefix, declarator_suffix, declarator_suffix_ignore_nonstring },
860
861 pub fn format(ctx: Formatter, w: *Writer) Writer.Error!void {
862 switch (ctx.kind) {
863 .type_name => {
864 try ctx.cty.writeTypePrefix(w, ctx.zcu);
865 try ctx.cty.writeTypeSuffix(w, ctx.zcu);
866 },
867 .declarator_prefix => {
868 try ctx.cty.writeTypePrefix(w, ctx.zcu);
869 switch (ctx.cty.kind()) {
870 .specifier => try w.writeByte(' '), // write "int " rather than "int"
871 .pointer => {}, // we already have something like "foo *"
872 .postfix_op => {}, // we already have something like "ret_ty "
873 }
874 },
875 .declarator_suffix => {
876 try ctx.cty.writeTypeSuffix(w, ctx.zcu);
877 const nonstring = switch (ctx.cty) {
878 .array => |arr| arr.nonstring,
879 .pointer => |ptr| ptr.nonstring,
880 else => false,
881 };
882 if (nonstring) try w.writeAll(" zig_nonstring");
883 },
884 .declarator_suffix_ignore_nonstring => {
885 try ctx.cty.writeTypeSuffix(w, ctx.zcu);
886 },
887 }
888 }
889 };
890
891 fn writeTypePrefix(cty: CType, w: *Writer, zcu: *const Zcu) Writer.Error!void {
892 switch (cty) {
893 .void => try w.writeAll("void"),
894 .bool => try w.writeAll("bool"),
895 .int => |int| try w.writeAll(@tagName(int)),
896 .float => |float| try w.writeAll(@tagName(float)),
897 .@"fn" => |ty| try w.print("{f}_{d}", .{ fmtZigType(ty, zcu), ty.toIntern() }),
898 .@"enum" => |ty| try w.print("enum__{f}_{d}", .{ fmtZigType(ty, zcu), ty.toIntern() }),
899 .bitpack => |ty| try w.print("bitpack__{f}_{d}", .{ fmtZigType(ty, zcu), ty.toIntern() }),
900 .@"struct" => |ty| try w.print("struct {f}_{d}", .{ fmtZigType(ty, zcu), ty.toIntern() }),
901 .union_auto => |ty| try w.print("struct {f}_{d}", .{ fmtZigType(ty, zcu), ty.toIntern() }),
902 .union_extern => |ty| try w.print("union {f}_{d}", .{ fmtZigType(ty, zcu), ty.toIntern() }),
903 .slice => |ty| try w.print("struct {f}_{d}", .{ fmtZigType(ty, zcu), ty.toIntern() }),
904 .opt => |ty| try w.print("struct {f}_{d}", .{ fmtZigType(ty, zcu), ty.toIntern() }),
905 .arr => |ty| try w.print("struct {f}_{d}", .{ fmtZigType(ty, zcu), ty.toIntern() }),
906 .vec => |ty| try w.print("struct {f}_{d}", .{ fmtZigType(ty, zcu), ty.toIntern() }),
907 .errunion => |eu| try w.print("struct errunion_{f}_{d}", .{
908 fmtZigType(eu.payload_ty, zcu),
909 eu.payload_ty.toIntern(),
910 }),
911 .aligned => |aligned| try w.print("aligned__{d}_{f}_{d}", .{
912 aligned.alignment.toByteUnits().?,
913 fmtZigType(aligned.ty, zcu),
914 aligned.ty.toIntern(),
915 }),
916 .bigint => |bigint| try w.print("struct int_{d}x{d}", .{
917 bigint.limb_size.bits(),
918 bigint.limbs_len,
919 }),
920
921 .pointer => |ptr| {
922 try ptr.elem_ty.writeTypePrefix(w, zcu);
923 switch (ptr.elem_ty.kind()) {
924 .pointer, .postfix_op => {},
925 .specifier => {
926 // We want "foo *" or "foo const *" rather than "foo*" or "fooconst *".
927 try w.writeByte(' ');
928 },
929 }
930 if (ptr.@"const") try w.writeAll("const ");
931 if (ptr.@"volatile") try w.writeAll("volatile ");
932 switch (ptr.elem_ty.kind()) {
933 .specifier, .pointer => {},
934 .postfix_op => {
935 // Prefix "*" is lower precedence than postfix "(x)" or "[x]" so use parens
936 // to disambiguate; e.g. "void (*foo)(int)" instead of "void *foo(int)".
937 try w.writeByte('(');
938 },
939 }
940 switch (ptr.elem_ty.*) {
941 else => {},
942 .function => |function| switch (function.cc) {
943 .c => {},
944 else => |cc| try w.print("zig_callconv({t}) ", .{cc}),
945 },
946 }
947 try w.writeByte('*');
948 },
949
950 .array => |array| {
951 try array.elem_ty.writeTypePrefix(w, zcu);
952 switch (array.elem_ty.kind()) {
953 .pointer, .postfix_op => {},
954 .specifier => {
955 // We want e.g. "struct foo [5]" rather than "struct foo[5]".
956 try w.writeByte(' ');
957 },
958 }
959 },
960
961 .function => |function| {
962 try function.ret_ty.writeTypePrefix(w, zcu);
963 switch (function.ret_ty.kind()) {
964 .pointer, .postfix_op => {},
965 .specifier => {
966 // We want e.g. "struct foo (void)" rather than "struct foo(void)".
967 try w.writeByte(' ');
968 },
969 }
970 },
971 }
972 }
973 fn writeTypeSuffix(cty: CType, w: *Writer, zcu: *const Zcu) Writer.Error!void {
974 switch (cty) {
975 // simple type specifiers
976 .void,
977 .bool,
978 .int,
979 .float,
980 .@"fn",
981 .@"enum",
982 .bitpack,
983 .@"struct",
984 .union_auto,
985 .union_extern,
986 .slice,
987 .opt,
988 .arr,
989 .vec,
990 .errunion,
991 .aligned,
992 .bigint,
993 => {},
994
995 .pointer => |ptr| {
996 // Match opening paren "(" in `writeTypePrefix`.
997 switch (ptr.elem_ty.kind()) {
998 .specifier, .pointer => {},
999 .postfix_op => try w.writeByte(')'),
1000 }
1001 try ptr.elem_ty.writeTypeSuffix(w, zcu);
1002 },
1003
1004 .array => |array| {
1005 try w.print("[{d}]", .{array.len});
1006 try array.elem_ty.writeTypeSuffix(w, zcu);
1007 },
1008
1009 .function => |function| {
1010 if (function.param_tys.len == 0 and !function.varargs) {
1011 try w.writeAll("(void)");
1012 } else {
1013 try w.writeByte('(');
1014 for (function.param_tys, 0..) |param_ty, param_index| {
1015 if (param_index > 0) try w.writeAll(", ");
1016 try param_ty.writeTypePrefix(w, zcu);
1017 try param_ty.writeTypeSuffix(w, zcu);
1018 }
1019 if (function.varargs) {
1020 if (function.param_tys.len > 0) try w.writeAll(", ");
1021 try w.writeAll("...");
1022 }
1023 try w.writeByte(')');
1024 }
1025 try function.ret_ty.writeTypeSuffix(w, zcu);
1026 },
1027 }
1028 }
1029
1030 /// Renders Zig types using only bytes allowed in C identifiers in a somewhat-understandable
1031 /// way. The output is *not* guaranteed to be unique.
1032 fn fmtZigType(ty: Type, zcu: *const Zcu) FormatZigType {
1033 return .{ .ty = ty, .zcu = zcu };
1034 }
1035 const FormatZigType = struct {
1036 ty: Type,
1037 zcu: *const Zcu,
1038 pub fn format(ctx: FormatZigType, w: *Writer) Writer.Error!void {
1039 const ty = ctx.ty;
1040 const zcu = ctx.zcu;
1041 const ip = &zcu.intern_pool;
1042 switch (ty.zigTypeTag(zcu)) {
1043 .frame => unreachable,
1044 .@"anyframe" => unreachable,
1045 .spirv => unreachable,
1046
1047 .type => try w.writeAll("type"),
1048 .void => try w.writeAll("void"),
1049 .bool => try w.writeAll("bool"),
1050 .noreturn => try w.writeAll("noreturn"),
1051 .comptime_int => try w.writeAll("comptime_int"),
1052 .comptime_float => try w.writeAll("comptime_float"),
1053 .enum_literal => try w.writeAll("enum_literal"),
1054 .undefined => try w.writeAll("undefined"),
1055 .null => try w.writeAll("null"),
1056
1057 .int => switch (ty.toIntern()) {
1058 .usize_type => try w.writeAll("usize"),
1059 .isize_type => try w.writeAll("isize"),
1060 .c_char_type => try w.writeAll("c_char"),
1061 .c_short_type => try w.writeAll("c_short"),
1062 .c_ushort_type => try w.writeAll("c_ushort"),
1063 .c_int_type => try w.writeAll("c_int"),
1064 .c_uint_type => try w.writeAll("c_uint"),
1065 .c_long_type => try w.writeAll("c_long"),
1066 .c_ulong_type => try w.writeAll("c_ulong"),
1067 .c_longlong_type => try w.writeAll("c_longlong"),
1068 .c_ulonglong_type => try w.writeAll("c_ulonglong"),
1069 else => {
1070 const info = ty.intInfo(zcu);
1071 switch (info.signedness) {
1072 .unsigned => try w.print("u{d}", .{info.bits}),
1073 .signed => try w.print("i{d}", .{info.bits}),
1074 }
1075 },
1076 },
1077 .float => switch (ty.toIntern()) {
1078 .c_longdouble_type => try w.writeAll("c_longdouble"),
1079 .f16_type => try w.writeAll("f16"),
1080 .f32_type => try w.writeAll("f32"),
1081 .f64_type => try w.writeAll("f64"),
1082 .f80_type => try w.writeAll("f80"),
1083 .f128_type => try w.writeAll("f128"),
1084 else => unreachable,
1085 },
1086 .error_set => switch (ty.toIntern()) {
1087 .anyerror_type => try w.writeAll("anyerror"),
1088 else => try w.print("error_{d}", .{@backingInt(ty.toIntern())}),
1089 },
1090 .optional => try w.print("opt_{f}", .{fmtZigType(ty.optionalChild(zcu), zcu)}),
1091 .error_union => try w.print("errunion_{f}", .{fmtZigType(ty.errorUnionPayload(zcu), zcu)}),
1092
1093 .pointer => switch (ty.ptrSize(zcu)) {
1094 .one, .many, .c => try w.print("ptr_{f}", .{fmtZigType(ty.childType(zcu), zcu)}),
1095 .slice => try w.print("slice_{f}", .{fmtZigType(ty.childType(zcu), zcu)}),
1096 },
1097 .@"fn" => {
1098 const func_type = ip.indexToKey(ty.toIntern()).func_type;
1099 try w.writeAll("fn_"); // intentional double underscore to start
1100 for (func_type.param_types.get(ip)) |param_ty_ip| {
1101 const param_ty: Type = .fromInterned(param_ty_ip);
1102 if (param_ty.isGenericPoison()) {
1103 try w.writeAll("_Pgeneric");
1104 } else {
1105 try w.print("_P{f}", .{fmtZigType(param_ty, zcu)});
1106 }
1107 }
1108 if (func_type.is_var_args) {
1109 try w.writeAll("_VA");
1110 }
1111 const ret_ty: Type = .fromInterned(func_type.return_type);
1112 if (ret_ty.isGenericPoison()) {
1113 try w.writeAll("_Rgeneric");
1114 } else if (ret_ty.zigTypeTag(zcu) == .error_union and ret_ty.errorUnionPayload(zcu).isGenericPoison()) {
1115 try w.writeAll("_Rgeneric_ies");
1116 } else {
1117 try w.print("_R{f}", .{fmtZigType(ret_ty, zcu)});
1118 }
1119 },
1120
1121 .vector => try w.print("vec_{d}_{f}", .{
1122 ty.arrayLen(zcu),
1123 fmtZigType(ty.childType(zcu), zcu),
1124 }),
1125
1126 .array => if (ty.sentinel(zcu)) |s| try w.print("arr_{d}s{d}_{f}", .{
1127 ty.arrayLen(zcu),
1128 @backingInt(s.toIntern()),
1129 fmtZigType(ty.childType(zcu), zcu),
1130 }) else try w.print("arr_{d}_{f}", .{
1131 ty.arrayLen(zcu),
1132 fmtZigType(ty.childType(zcu), zcu),
1133 }),
1134
1135 .@"struct" => if (ty.isTuple(zcu)) {
1136 const len = ty.structFieldCount(zcu);
1137 try w.print("tuple_{d}", .{len});
1138 for (0..len) |field_index| {
1139 const field_ty = ty.fieldType(field_index, zcu);
1140 try w.print("_{f}", .{fmtZigType(field_ty, zcu)});
1141 }
1142 } else {
1143 const name = ty.containerTypeName(ip).fqn.toSlice(ip);
1144 try w.print("{f}", .{@import("../c.zig").fmtIdentUnsolo(name)});
1145 },
1146 .@"opaque" => if (ty.toIntern() == .anyopaque_type) {
1147 try w.writeAll("anyopaque");
1148 } else {
1149 const name = ty.containerTypeName(ip).fqn.toSlice(ip);
1150 try w.print("{f}", .{@import("../c.zig").fmtIdentUnsolo(name)});
1151 },
1152 .@"union", .@"enum" => {
1153 const name = ty.containerTypeName(ip).fqn.toSlice(ip);
1154 try w.print("{f}", .{@import("../c.zig").fmtIdentUnsolo(name)});
1155 },
1156 }
1157 }
1158 };
1159
1160 /// Returns `true` if the layout of `ty` is known without any type resolution required. This
1161 /// allows some types to be lowered directly where 'typedef' would otherwise be necessary.
1162 fn alwaysHasLayout(ty: Type, ip: *const InternPool) bool {
1163 return switch (ip.indexToKey(ty.toIntern())) {
1164 .int_type,
1165 .ptr_type,
1166 .anyframe_type,
1167 .simple_type,
1168 .opaque_type,
1169 .spirv_type,
1170 .error_set_type,
1171 .inferred_error_set_type,
1172 => true,
1173
1174 .struct_type,
1175 .union_type,
1176 .enum_type,
1177 => false,
1178
1179 .array_type => |arr| alwaysHasLayout(.fromInterned(arr.child), ip),
1180 .vector_type => |vec| alwaysHasLayout(.fromInterned(vec.child), ip),
1181 .opt_type => |child| alwaysHasLayout(.fromInterned(child), ip),
1182 .error_union_type => |eu| alwaysHasLayout(.fromInterned(eu.payload_type), ip),
1183
1184 .tuple_type => |tuple| for (tuple.types.get(ip)) |field_ty| {
1185 if (!alwaysHasLayout(.fromInterned(field_ty), ip)) break false;
1186 } else true,
1187
1188 .func_type => |f| for (f.param_types.get(ip)) |param_ty| {
1189 if (!alwaysHasLayout(.fromInterned(param_ty), ip)) break false;
1190 } else alwaysHasLayout(.fromInterned(f.return_type), ip),
1191
1192 // values, not types
1193 .undef,
1194 .simple_value,
1195 .@"extern",
1196 .func,
1197 .int,
1198 .err,
1199 .error_union,
1200 .enum_literal,
1201 .enum_tag,
1202 .float,
1203 .ptr,
1204 .slice,
1205 .opt,
1206 .aggregate,
1207 .un,
1208 .bitpack,
1209 // memoization, not types
1210 .memoized_call,
1211 => unreachable,
1212 };
1213 }
1214};
1215
1216const Zcu = @import("../../Zcu.zig");
1217const Type = @import("../../Type.zig");
1218const Value = @import("../../Value.zig");
1219const InternPool = @import("../../InternPool.zig");
1220
1221const std = @import("std");
1222const assert = std.debug.assert;
1223const Allocator = std.mem.Allocator;
1224const Writer = std.Io.Writer;