master
1const Air = @import("../Air.zig");
2const Zcu = @import("../Zcu.zig");
3const Type = @import("../Type.zig");
4const Value = @import("../Value.zig");
5const InternPool = @import("../InternPool.zig");
6
7/// Given a body of AIR instructions, returns whether all type resolution necessary for codegen is complete.
8/// If `false`, then type resolution must have failed, so codegen cannot proceed.
9pub fn typesFullyResolved(air: Air, zcu: *Zcu) bool {
10 return checkBody(air, air.getMainBody(), zcu);
11}
12
13fn checkBody(air: Air, body: []const Air.Inst.Index, zcu: *Zcu) bool {
14 const tags = air.instructions.items(.tag);
15 const datas = air.instructions.items(.data);
16
17 for (body) |inst| {
18 const data = datas[@intFromEnum(inst)];
19 switch (tags[@intFromEnum(inst)]) {
20 .inferred_alloc, .inferred_alloc_comptime => unreachable,
21
22 .arg => {
23 if (!checkType(data.arg.ty.toType(), zcu)) return false;
24 },
25
26 .add,
27 .add_safe,
28 .add_optimized,
29 .add_wrap,
30 .add_sat,
31 .sub,
32 .sub_safe,
33 .sub_optimized,
34 .sub_wrap,
35 .sub_sat,
36 .mul,
37 .mul_safe,
38 .mul_optimized,
39 .mul_wrap,
40 .mul_sat,
41 .div_float,
42 .div_float_optimized,
43 .div_trunc,
44 .div_trunc_optimized,
45 .div_floor,
46 .div_floor_optimized,
47 .div_exact,
48 .div_exact_optimized,
49 .rem,
50 .rem_optimized,
51 .mod,
52 .mod_optimized,
53 .max,
54 .min,
55 .bit_and,
56 .bit_or,
57 .shr,
58 .shr_exact,
59 .shl,
60 .shl_exact,
61 .shl_sat,
62 .xor,
63 .cmp_lt,
64 .cmp_lt_optimized,
65 .cmp_lte,
66 .cmp_lte_optimized,
67 .cmp_eq,
68 .cmp_eq_optimized,
69 .cmp_gte,
70 .cmp_gte_optimized,
71 .cmp_gt,
72 .cmp_gt_optimized,
73 .cmp_neq,
74 .cmp_neq_optimized,
75 .bool_and,
76 .bool_or,
77 .store,
78 .store_safe,
79 .set_union_tag,
80 .array_elem_val,
81 .slice_elem_val,
82 .ptr_elem_val,
83 .memset,
84 .memset_safe,
85 .memcpy,
86 .memmove,
87 .atomic_store_unordered,
88 .atomic_store_monotonic,
89 .atomic_store_release,
90 .atomic_store_seq_cst,
91 .legalize_vec_elem_val,
92 => {
93 if (!checkRef(data.bin_op.lhs, zcu)) return false;
94 if (!checkRef(data.bin_op.rhs, zcu)) return false;
95 },
96
97 .not,
98 .bitcast,
99 .clz,
100 .ctz,
101 .popcount,
102 .byte_swap,
103 .bit_reverse,
104 .abs,
105 .load,
106 .fptrunc,
107 .fpext,
108 .intcast,
109 .intcast_safe,
110 .trunc,
111 .optional_payload,
112 .optional_payload_ptr,
113 .optional_payload_ptr_set,
114 .wrap_optional,
115 .unwrap_errunion_payload,
116 .unwrap_errunion_err,
117 .unwrap_errunion_payload_ptr,
118 .unwrap_errunion_err_ptr,
119 .errunion_payload_ptr_set,
120 .wrap_errunion_payload,
121 .wrap_errunion_err,
122 .struct_field_ptr_index_0,
123 .struct_field_ptr_index_1,
124 .struct_field_ptr_index_2,
125 .struct_field_ptr_index_3,
126 .get_union_tag,
127 .slice_len,
128 .slice_ptr,
129 .ptr_slice_len_ptr,
130 .ptr_slice_ptr_ptr,
131 .array_to_slice,
132 .int_from_float,
133 .int_from_float_optimized,
134 .int_from_float_safe,
135 .int_from_float_optimized_safe,
136 .float_from_int,
137 .splat,
138 .error_set_has_value,
139 .addrspace_cast,
140 .c_va_arg,
141 .c_va_copy,
142 => {
143 if (!checkType(data.ty_op.ty.toType(), zcu)) return false;
144 if (!checkRef(data.ty_op.operand, zcu)) return false;
145 },
146
147 .alloc,
148 .ret_ptr,
149 .c_va_start,
150 => {
151 if (!checkType(data.ty, zcu)) return false;
152 },
153
154 .ptr_add,
155 .ptr_sub,
156 .add_with_overflow,
157 .sub_with_overflow,
158 .mul_with_overflow,
159 .shl_with_overflow,
160 .slice,
161 .slice_elem_ptr,
162 .ptr_elem_ptr,
163 => {
164 const bin = air.extraData(Air.Bin, data.ty_pl.payload).data;
165 if (!checkType(data.ty_pl.ty.toType(), zcu)) return false;
166 if (!checkRef(bin.lhs, zcu)) return false;
167 if (!checkRef(bin.rhs, zcu)) return false;
168 },
169
170 .block,
171 .loop,
172 => {
173 const extra = air.extraData(Air.Block, data.ty_pl.payload);
174 if (!checkType(data.ty_pl.ty.toType(), zcu)) return false;
175 if (!checkBody(
176 air,
177 @ptrCast(air.extra.items[extra.end..][0..extra.data.body_len]),
178 zcu,
179 )) return false;
180 },
181
182 .dbg_inline_block => {
183 const extra = air.extraData(Air.DbgInlineBlock, data.ty_pl.payload);
184 if (!checkType(data.ty_pl.ty.toType(), zcu)) return false;
185 if (!checkBody(
186 air,
187 @ptrCast(air.extra.items[extra.end..][0..extra.data.body_len]),
188 zcu,
189 )) return false;
190 },
191
192 .sqrt,
193 .sin,
194 .cos,
195 .tan,
196 .exp,
197 .exp2,
198 .log,
199 .log2,
200 .log10,
201 .floor,
202 .ceil,
203 .round,
204 .trunc_float,
205 .neg,
206 .neg_optimized,
207 .is_null,
208 .is_non_null,
209 .is_null_ptr,
210 .is_non_null_ptr,
211 .is_err,
212 .is_non_err,
213 .is_err_ptr,
214 .is_non_err_ptr,
215 .ret,
216 .ret_safe,
217 .ret_load,
218 .is_named_enum_value,
219 .tag_name,
220 .error_name,
221 .cmp_lt_errors_len,
222 .c_va_end,
223 .set_err_return_trace,
224 => {
225 if (!checkRef(data.un_op, zcu)) return false;
226 },
227
228 .br, .switch_dispatch => {
229 if (!checkRef(data.br.operand, zcu)) return false;
230 },
231
232 .cmp_vector,
233 .cmp_vector_optimized,
234 => {
235 const extra = air.extraData(Air.VectorCmp, data.ty_pl.payload).data;
236 if (!checkType(data.ty_pl.ty.toType(), zcu)) return false;
237 if (!checkRef(extra.lhs, zcu)) return false;
238 if (!checkRef(extra.rhs, zcu)) return false;
239 },
240
241 .reduce,
242 .reduce_optimized,
243 => {
244 if (!checkRef(data.reduce.operand, zcu)) return false;
245 },
246
247 .struct_field_ptr,
248 .struct_field_val,
249 => {
250 const extra = air.extraData(Air.StructField, data.ty_pl.payload).data;
251 if (!checkType(data.ty_pl.ty.toType(), zcu)) return false;
252 if (!checkRef(extra.struct_operand, zcu)) return false;
253 },
254
255 .shuffle_one => {
256 const unwrapped = air.unwrapShuffleOne(zcu, inst);
257 if (!checkType(unwrapped.result_ty, zcu)) return false;
258 if (!checkRef(unwrapped.operand, zcu)) return false;
259 for (unwrapped.mask) |m| switch (m.unwrap()) {
260 .elem => {},
261 .value => |val| if (!checkVal(.fromInterned(val), zcu)) return false,
262 };
263 },
264
265 .shuffle_two => {
266 const unwrapped = air.unwrapShuffleTwo(zcu, inst);
267 if (!checkType(unwrapped.result_ty, zcu)) return false;
268 if (!checkRef(unwrapped.operand_a, zcu)) return false;
269 if (!checkRef(unwrapped.operand_b, zcu)) return false;
270 // No values to check because there are no comptime-known values other than undef
271 },
272
273 .cmpxchg_weak,
274 .cmpxchg_strong,
275 => {
276 const extra = air.extraData(Air.Cmpxchg, data.ty_pl.payload).data;
277 if (!checkType(data.ty_pl.ty.toType(), zcu)) return false;
278 if (!checkRef(extra.ptr, zcu)) return false;
279 if (!checkRef(extra.expected_value, zcu)) return false;
280 if (!checkRef(extra.new_value, zcu)) return false;
281 },
282
283 .aggregate_init => {
284 const ty = data.ty_pl.ty.toType();
285 const elems_len: usize = @intCast(ty.arrayLen(zcu));
286 const elems: []const Air.Inst.Ref = @ptrCast(air.extra.items[data.ty_pl.payload..][0..elems_len]);
287 if (!checkType(ty, zcu)) return false;
288 if (ty.zigTypeTag(zcu) == .@"struct") {
289 for (elems, 0..) |elem, elem_idx| {
290 if (ty.structFieldIsComptime(elem_idx, zcu)) continue;
291 if (!checkRef(elem, zcu)) return false;
292 }
293 } else {
294 for (elems) |elem| {
295 if (!checkRef(elem, zcu)) return false;
296 }
297 }
298 },
299
300 .union_init => {
301 const extra = air.extraData(Air.UnionInit, data.ty_pl.payload).data;
302 if (!checkType(data.ty_pl.ty.toType(), zcu)) return false;
303 if (!checkRef(extra.init, zcu)) return false;
304 },
305
306 .field_parent_ptr => {
307 const extra = air.extraData(Air.FieldParentPtr, data.ty_pl.payload).data;
308 if (!checkType(data.ty_pl.ty.toType(), zcu)) return false;
309 if (!checkRef(extra.field_ptr, zcu)) return false;
310 },
311
312 .atomic_load => {
313 if (!checkRef(data.atomic_load.ptr, zcu)) return false;
314 },
315
316 .prefetch => {
317 if (!checkRef(data.prefetch.ptr, zcu)) return false;
318 },
319
320 .runtime_nav_ptr => {
321 if (!checkType(.fromInterned(data.ty_nav.ty), zcu)) return false;
322 },
323
324 .select,
325 .mul_add,
326 .legalize_vec_store_elem,
327 => {
328 const bin = air.extraData(Air.Bin, data.pl_op.payload).data;
329 if (!checkRef(data.pl_op.operand, zcu)) return false;
330 if (!checkRef(bin.lhs, zcu)) return false;
331 if (!checkRef(bin.rhs, zcu)) return false;
332 },
333
334 .atomic_rmw => {
335 const extra = air.extraData(Air.AtomicRmw, data.pl_op.payload).data;
336 if (!checkRef(data.pl_op.operand, zcu)) return false;
337 if (!checkRef(extra.operand, zcu)) return false;
338 },
339
340 .call,
341 .call_always_tail,
342 .call_never_tail,
343 .call_never_inline,
344 => {
345 const extra = air.extraData(Air.Call, data.pl_op.payload);
346 const args: []const Air.Inst.Ref = @ptrCast(air.extra.items[extra.end..][0..extra.data.args_len]);
347 if (!checkRef(data.pl_op.operand, zcu)) return false;
348 for (args) |arg| if (!checkRef(arg, zcu)) return false;
349 },
350
351 .dbg_var_ptr,
352 .dbg_var_val,
353 .dbg_arg_inline,
354 => {
355 if (!checkRef(data.pl_op.operand, zcu)) return false;
356 },
357
358 .@"try", .try_cold => {
359 const extra = air.extraData(Air.Try, data.pl_op.payload);
360 if (!checkRef(data.pl_op.operand, zcu)) return false;
361 if (!checkBody(
362 air,
363 @ptrCast(air.extra.items[extra.end..][0..extra.data.body_len]),
364 zcu,
365 )) return false;
366 },
367
368 .try_ptr, .try_ptr_cold => {
369 const extra = air.extraData(Air.TryPtr, data.ty_pl.payload);
370 if (!checkType(data.ty_pl.ty.toType(), zcu)) return false;
371 if (!checkRef(extra.data.ptr, zcu)) return false;
372 if (!checkBody(
373 air,
374 @ptrCast(air.extra.items[extra.end..][0..extra.data.body_len]),
375 zcu,
376 )) return false;
377 },
378
379 .cond_br => {
380 const extra = air.extraData(Air.CondBr, data.pl_op.payload);
381 if (!checkRef(data.pl_op.operand, zcu)) return false;
382 if (!checkBody(
383 air,
384 @ptrCast(air.extra.items[extra.end..][0..extra.data.then_body_len]),
385 zcu,
386 )) return false;
387 if (!checkBody(
388 air,
389 @ptrCast(air.extra.items[extra.end + extra.data.then_body_len ..][0..extra.data.else_body_len]),
390 zcu,
391 )) return false;
392 },
393
394 .switch_br, .loop_switch_br => {
395 const switch_br = air.unwrapSwitch(inst);
396 if (!checkRef(switch_br.operand, zcu)) return false;
397 var it = switch_br.iterateCases();
398 while (it.next()) |case| {
399 for (case.items) |item| if (!checkRef(item, zcu)) return false;
400 for (case.ranges) |range| {
401 if (!checkRef(range[0], zcu)) return false;
402 if (!checkRef(range[1], zcu)) return false;
403 }
404 if (!checkBody(air, case.body, zcu)) return false;
405 }
406 if (!checkBody(air, it.elseBody(), zcu)) return false;
407 },
408
409 .assembly => {
410 const extra = air.extraData(Air.Asm, data.ty_pl.payload);
411 if (!checkType(data.ty_pl.ty.toType(), zcu)) return false;
412 // Luckily, we only care about the inputs and outputs, so we don't have to do
413 // the whole null-terminated string dance.
414 const outputs_len = extra.data.flags.outputs_len;
415 const outputs: []const Air.Inst.Ref = @ptrCast(air.extra.items[extra.end..][0..outputs_len]);
416 const inputs: []const Air.Inst.Ref = @ptrCast(air.extra.items[extra.end + outputs_len ..][0..extra.data.inputs_len]);
417 for (outputs) |output| if (output != .none and !checkRef(output, zcu)) return false;
418 for (inputs) |input| if (input != .none and !checkRef(input, zcu)) return false;
419 },
420
421 .legalize_compiler_rt_call => {
422 const extra = air.extraData(Air.Call, data.legalize_compiler_rt_call.payload);
423 const args: []const Air.Inst.Ref = @ptrCast(air.extra.items[extra.end..][0..extra.data.args_len]);
424 for (args) |arg| if (!checkRef(arg, zcu)) return false;
425 },
426
427 .trap,
428 .breakpoint,
429 .ret_addr,
430 .frame_addr,
431 .unreach,
432 .wasm_memory_size,
433 .wasm_memory_grow,
434 .work_item_id,
435 .work_group_size,
436 .work_group_id,
437 .dbg_stmt,
438 .dbg_empty_stmt,
439 .err_return_trace,
440 .save_err_return_trace_index,
441 .repeat,
442 => {},
443 }
444 }
445 return true;
446}
447
448fn checkRef(ref: Air.Inst.Ref, zcu: *Zcu) bool {
449 const ip_index = ref.toInterned() orelse {
450 // This operand refers back to a previous instruction.
451 // We have already checked that instruction's type.
452 // So, there's no need to check this operand's type.
453 return true;
454 };
455 return checkVal(Value.fromInterned(ip_index), zcu);
456}
457
458pub fn checkVal(val: Value, zcu: *Zcu) bool {
459 const ty = val.typeOf(zcu);
460 if (!checkType(ty, zcu)) return false;
461 if (val.isUndef(zcu)) return true;
462 if (ty.toIntern() == .type_type and !checkType(val.toType(), zcu)) return false;
463 // Check for lazy values
464 switch (zcu.intern_pool.indexToKey(val.toIntern())) {
465 .int => |int| switch (int.storage) {
466 .u64, .i64, .big_int => return true,
467 .lazy_align, .lazy_size => |ty_index| {
468 return checkType(Type.fromInterned(ty_index), zcu);
469 },
470 },
471 else => return true,
472 }
473}
474
475pub fn checkType(ty: Type, zcu: *Zcu) bool {
476 const ip = &zcu.intern_pool;
477 if (ty.isGenericPoison()) return true;
478 return switch (ty.zigTypeTag(zcu)) {
479 .type,
480 .void,
481 .bool,
482 .noreturn,
483 .int,
484 .float,
485 .error_set,
486 .@"enum",
487 .@"opaque",
488 .vector,
489 // These types can appear due to some dummy instructions Sema introduces and expects to be omitted by Liveness.
490 // It's a little silly -- but fine, we'll return `true`.
491 .comptime_float,
492 .comptime_int,
493 .undefined,
494 .null,
495 .enum_literal,
496 => true,
497
498 .frame,
499 .@"anyframe",
500 => @panic("TODO Air.types_resolved.checkType async frames"),
501
502 .optional => checkType(ty.childType(zcu), zcu),
503 .error_union => checkType(ty.errorUnionPayload(zcu), zcu),
504 .pointer => checkType(ty.childType(zcu), zcu),
505 .array => checkType(ty.childType(zcu), zcu),
506
507 .@"fn" => {
508 const info = zcu.typeToFunc(ty).?;
509 for (0..info.param_types.len) |i| {
510 const param_ty = info.param_types.get(ip)[i];
511 if (!checkType(Type.fromInterned(param_ty), zcu)) return false;
512 }
513 return checkType(Type.fromInterned(info.return_type), zcu);
514 },
515 .@"struct" => switch (ip.indexToKey(ty.toIntern())) {
516 .struct_type => {
517 const struct_obj = zcu.typeToStruct(ty).?;
518 return switch (struct_obj.layout) {
519 .@"packed" => struct_obj.backingIntTypeUnordered(ip) != .none,
520 .auto, .@"extern" => struct_obj.flagsUnordered(ip).fully_resolved,
521 };
522 },
523 .tuple_type => |tuple| {
524 for (0..tuple.types.len) |i| {
525 const field_is_comptime = tuple.values.get(ip)[i] != .none;
526 if (field_is_comptime) continue;
527 const field_ty = tuple.types.get(ip)[i];
528 if (!checkType(Type.fromInterned(field_ty), zcu)) return false;
529 }
530 return true;
531 },
532 else => unreachable,
533 },
534 .@"union" => return zcu.typeToUnion(ty).?.flagsUnordered(ip).status == .fully_resolved,
535 };
536}