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}