1const std = @import("std");
2const Zcu = @import("../Zcu.zig");
3const Sema = @import("../Sema.zig");
4const Air = @import("../Air.zig");
5const InternPool = @import("../InternPool.zig");
6const Type = @import("../Type.zig");
7const Value = @import("../Value.zig");
8const Zir = std.zig.Zir;
9const AstGen = std.zig.AstGen;
10const CompileError = Zcu.CompileError;
11const Ast = std.zig.Ast;
12const Allocator = std.mem.Allocator;
13const assert = std.debug.assert;
14const File = Zcu.File;
15const LazySrcLoc = Zcu.LazySrcLoc;
16const Ref = std.zig.Zir.Inst.Ref;
17const NullTerminatedString = InternPool.NullTerminatedString;
18const NumberLiteralError = std.zig.number_literal.Error;
19const NodeIndex = std.zig.Ast.Node.Index;
20const Zoir = std.zig.Zoir;
21
22const LowerZon = @This();
23
24sema: *Sema,
25file: *File,
26file_index: Zcu.File.Index,
27import_loc: LazySrcLoc,
28block: *Sema.Block,
29base_node_inst: InternPool.TrackedInst.Index,
30
31/// Lowers the given file as ZON.
32pub fn run(
33 sema: *Sema,
34 file: *File,
35 file_index: Zcu.File.Index,
36 res_ty_interned: InternPool.Index,
37 import_loc: LazySrcLoc,
38 block: *Sema.Block,
39) CompileError!InternPool.Index {
40 const pt = sema.pt;
41 const comp = pt.zcu.comp;
42 const gpa = comp.gpa;
43 const io = comp.io;
44
45 const tracked_inst = try pt.zcu.intern_pool.trackZir(gpa, io, pt.tid, .{
46 .file = file_index,
47 .inst = .main_struct_inst, // this is the only trackable instruction in a ZON file
48 });
49
50 var lower_zon: LowerZon = .{
51 .sema = sema,
52 .file = file,
53 .file_index = file_index,
54 .import_loc = import_loc,
55 .block = block,
56 .base_node_inst = tracked_inst,
57 };
58
59 if (res_ty_interned == .none) {
60 return lower_zon.lowerExprAnonResTy(.root);
61 } else {
62 const res_ty: Type = .fromInterned(res_ty_interned);
63 try lower_zon.checkType(res_ty);
64 return lower_zon.lowerExprKnownResTy(.root, res_ty);
65 }
66}
67
68fn lowerExprAnonResTy(self: *LowerZon, node: Zoir.Node.Index) CompileError!InternPool.Index {
69 const pt = self.sema.pt;
70 const comp = pt.zcu.comp;
71 const gpa = comp.gpa;
72 const io = comp.io;
73 const ip = &pt.zcu.intern_pool;
74 switch (node.get(self.file.zoir.?)) {
75 .true => return .bool_true,
76 .false => return .bool_false,
77 .null => return .null_value,
78 .pos_inf => return self.fail(node, "infinity requires a known result type", .{}),
79 .neg_inf => return self.fail(node, "negative infinity requires a known result type", .{}),
80 .nan => return self.fail(node, "NaN requires a known result type", .{}),
81 .int_literal => |int| switch (int) {
82 .small => |val| return pt.intern(.{ .int = .{
83 .ty = .comptime_int_type,
84 .storage = .{ .i64 = val },
85 } }),
86 .big => |val| return pt.intern(.{ .int = .{
87 .ty = .comptime_int_type,
88 .storage = .{ .big_int = val },
89 } }),
90 },
91 .float_literal => |val| {
92 const result = try pt.floatValue(.comptime_float, val);
93 return result.toIntern();
94 },
95 .char_literal => |val| return pt.intern(.{ .int = .{
96 .ty = .comptime_int_type,
97 .storage = .{ .i64 = val },
98 } }),
99 .enum_literal => |val| return pt.intern(.{
100 .enum_literal = try ip.getOrPutString(
101 gpa,
102 io,
103 pt.tid,
104 val.get(self.file.zoir.?),
105 .no_embedded_nulls,
106 ),
107 }),
108 .string_literal => |val| {
109 const ip_str = try ip.getOrPutString(gpa, io, pt.tid, val, .maybe_embedded_nulls);
110 const result = try self.sema.addStrLit(ip_str, val.len);
111 return result.toInterned().?;
112 },
113 .empty_literal => return .empty_tuple,
114 .array_literal => |nodes| {
115 const types = try self.sema.arena.alloc(InternPool.Index, nodes.len);
116 const values = try self.sema.arena.alloc(InternPool.Index, nodes.len);
117 for (0..nodes.len) |i| {
118 values[i] = try self.lowerExprAnonResTy(nodes.at(@intCast(i)));
119 types[i] = Value.fromInterned(values[i]).typeOf(pt.zcu).toIntern();
120 }
121 const ty = try ip.getTupleType(gpa, io, pt.tid, .{
122 .types = types,
123 .values = values,
124 });
125 return (try pt.aggregateValue(.fromInterned(ty), values)).toIntern();
126 },
127 .struct_literal => |init| {
128 const elems = try self.sema.arena.alloc(InternPool.Index, init.names.len);
129 for (0..init.names.len) |i| {
130 elems[i] = try self.lowerExprAnonResTy(init.vals.at(@intCast(i)));
131 }
132 const struct_ty: Type = switch (try ip.getReifiedStructType(gpa, io, pt.tid, .{
133 .zir_index = self.base_node_inst,
134 .type_hash = hash: {
135 var hasher: std.hash.Wyhash = .init(0);
136 hasher.update(std.mem.asBytes(&node));
137 hasher.update(std.mem.sliceAsBytes(elems));
138 hasher.update(std.mem.sliceAsBytes(init.names));
139 break :hash hasher.final();
140 },
141 .fields_len = @intCast(init.names.len),
142 .layout = .auto,
143 .any_comptime_fields = true,
144 .any_field_defaults = true,
145 .any_field_aligns = false,
146 .packed_backing_int_type = .none,
147 })) {
148 .existing => |ty| .fromInterned(ty),
149 .wip => |wip| ty: {
150 errdefer wip.cancel(ip, pt.tid);
151 const block = self.block;
152 const zcu = pt.zcu;
153 try self.sema.setTypeName(block, &wip, .anon, "struct", self.base_node_inst.resolve(ip).?);
154
155 // Reified structs have field information populated immediately.
156 @memcpy(wip.field_values.get(ip), elems);
157 if (init.names.len > 0) {
158 // All fields are comptime, but unused bits remain zeroed.
159 const unused_bits = switch (init.names.len % 32) {
160 0 => 0,
161 else => |n| 32 - n,
162 };
163 const comptime_bits = wip.field_is_comptime_bits.getAll(ip);
164 @memset(comptime_bits[0 .. comptime_bits.len - 1], std.math.maxInt(u32));
165 comptime_bits[comptime_bits.len - 1] = @as(u32, std.math.maxInt(u32)) >> @intCast(unused_bits);
166 }
167 for (
168 init.names,
169 wip.field_names.get(ip),
170 wip.field_types.get(ip),
171 wip.field_values.get(ip),
172 ) |zoir_name, *field_name, *field_ty, field_val| {
173 field_name.* = try ip.getOrPutString(
174 gpa,
175 io,
176 pt.tid,
177 zoir_name.get(self.file.zoir.?),
178 .no_embedded_nulls,
179 );
180 field_ty.* = ip.typeOf(field_val);
181 }
182
183 const new_namespace_index = try pt.createNamespace(.{
184 .parent = block.namespace.toOptional(),
185 .owner_type = wip.index,
186 .file_scope = block.getFileScopeIndex(zcu),
187 .generation = zcu.generation,
188 });
189 errdefer pt.destroyNamespace(new_namespace_index);
190 if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index);
191 break :ty .fromInterned(wip.finish(ip, new_namespace_index));
192 },
193 };
194 try self.sema.addTypeReferenceEntry(self.nodeSrc(node), struct_ty);
195 // No need for `ensureNamespaceUpToDate` because this type's namespace is always empty.
196 try self.sema.ensureLayoutResolved(struct_ty, self.nodeSrc(node), .init);
197
198 return (try pt.aggregateValue(struct_ty, elems)).toIntern();
199 },
200 }
201}
202
203/// Validate that `ty` is a valid ZON type, or emit a compile error.
204///
205/// Rules out nested optionals, error sets, etc.
206fn checkType(self: *LowerZon, ty: Type) !void {
207 var visited: std.AutoHashMapUnmanaged(InternPool.Index, void) = .empty;
208 try self.checkTypeInner(ty, null, &visited);
209}
210
211fn checkTypeInner(
212 self: *LowerZon,
213 ty: Type,
214 parent_opt_ty: ?Type,
215 /// Visited structs and unions (not tuples). These are tracked because they are the only way in
216 /// which a type can be self-referential, so must be tracked to avoid loops. Tracking more types
217 /// consumes memory unnecessarily, and would be complicated by optionals.
218 /// Allocated into `self.sema.arena`.
219 visited: *std.AutoHashMapUnmanaged(InternPool.Index, void),
220) !void {
221 const sema = self.sema;
222 const pt = sema.pt;
223 const zcu = pt.zcu;
224 const ip = &zcu.intern_pool;
225
226 switch (ty.zigTypeTag(zcu)) {
227 .bool,
228 .int,
229 .float,
230 .null,
231 .@"enum",
232 .comptime_float,
233 .comptime_int,
234 .enum_literal,
235 => {},
236
237 .noreturn,
238 .void,
239 .type,
240 .undefined,
241 .error_union,
242 .error_set,
243 .@"fn",
244 .frame,
245 .@"anyframe",
246 .@"opaque",
247 .spirv,
248 => return self.failUnsupportedResultType(ty, null),
249
250 .pointer => {
251 const ptr_info = ty.ptrInfo(zcu);
252 if (!ptr_info.flags.is_const) {
253 return self.failUnsupportedResultType(
254 ty,
255 "ZON does not allow mutable pointers",
256 );
257 }
258 switch (ptr_info.flags.size) {
259 .one => try self.checkTypeInner(
260 .fromInterned(ptr_info.child),
261 parent_opt_ty, // preserved
262 visited,
263 ),
264 .slice => try self.checkTypeInner(
265 .fromInterned(ptr_info.child),
266 null,
267 visited,
268 ),
269 .many => return self.failUnsupportedResultType(ty, "ZON does not allow many-pointers"),
270 .c => return self.failUnsupportedResultType(ty, "ZON does not allow C pointers"),
271 }
272 },
273 .optional => if (parent_opt_ty) |p| {
274 return self.failUnsupportedResultType(p, "ZON does not allow nested optionals");
275 } else try self.checkTypeInner(
276 ty.optionalChild(zcu),
277 ty,
278 visited,
279 ),
280 .array, .vector => {
281 try self.checkTypeInner(ty.childType(zcu), null, visited);
282 },
283 .@"struct" => if (ty.isTuple(zcu)) {
284 const tuple_info = ip.indexToKey(ty.toIntern()).tuple_type;
285 const field_types = tuple_info.types.get(ip);
286 for (field_types) |field_type| {
287 try self.checkTypeInner(.fromInterned(field_type), null, visited);
288 }
289 } else {
290 const gop = try visited.getOrPut(sema.arena, ty.toIntern());
291 if (gop.found_existing) return;
292 try sema.ensureLayoutResolved(ty, self.import_loc, .init);
293 const struct_info = zcu.typeToStruct(ty).?;
294 for (struct_info.field_types.get(ip)) |field_type| {
295 try self.checkTypeInner(.fromInterned(field_type), null, visited);
296 }
297 },
298 .@"union" => {
299 const gop = try visited.getOrPut(sema.arena, ty.toIntern());
300 if (gop.found_existing) return;
301 try sema.ensureLayoutResolved(ty, self.import_loc, .init);
302 const union_info = zcu.typeToUnion(ty).?;
303 for (union_info.field_types.get(ip)) |field_type| {
304 if (field_type != .void_type) {
305 try self.checkTypeInner(.fromInterned(field_type), null, visited);
306 }
307 }
308 },
309 }
310}
311
312fn nodeSrc(self: *LowerZon, node: Zoir.Node.Index) LazySrcLoc {
313 return .{
314 .base_node_inst = self.base_node_inst,
315 .offset = .{ .node_abs = node.getAstNode(self.file.zoir.?) },
316 };
317}
318
319fn failUnsupportedResultType(
320 self: *LowerZon,
321 ty: Type,
322 opt_note: ?[]const u8,
323) Zcu.SemaError {
324 @branchHint(.cold);
325 const sema = self.sema;
326 const gpa = sema.gpa;
327 const pt = sema.pt;
328 return sema.failWithOwnedErrorMsg(self.block, msg: {
329 const msg = try sema.errMsg(self.import_loc, "type '{f}' is not available in ZON", .{ty.fmt(pt)});
330 errdefer msg.destroy(gpa);
331 if (opt_note) |n| try sema.errNote(self.import_loc, msg, "{s}", .{n});
332 break :msg msg;
333 });
334}
335
336fn fail(
337 self: *LowerZon,
338 node: Zoir.Node.Index,
339 comptime format: []const u8,
340 args: anytype,
341) Zcu.SemaError {
342 @branchHint(.cold);
343 const err_msg = try Zcu.ErrorMsg.create(self.sema.pt.zcu.gpa, self.nodeSrc(node), format, args);
344 try self.sema.pt.zcu.errNote(self.import_loc, err_msg, "imported here", .{});
345 return self.sema.failWithOwnedErrorMsg(self.block, err_msg);
346}
347
348fn lowerExprKnownResTy(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) CompileError!InternPool.Index {
349 const pt = self.sema.pt;
350 return self.lowerExprKnownResTyInner(node, res_ty) catch |err| switch (err) {
351 error.WrongType => return self.fail(node, "expected type '{f}'", .{res_ty.fmt(pt)}),
352 else => |e| return e,
353 };
354}
355
356fn lowerExprKnownResTyInner(
357 self: *LowerZon,
358 node: Zoir.Node.Index,
359 res_ty: Type,
360) (CompileError || error{WrongType})!InternPool.Index {
361 const pt = self.sema.pt;
362 switch (res_ty.zigTypeTag(pt.zcu)) {
363 .optional => return pt.intern(.{
364 .opt = .{
365 .ty = res_ty.toIntern(),
366 .val = if (node.get(self.file.zoir.?) == .null) b: {
367 break :b .none;
368 } else b: {
369 const child_type = res_ty.optionalChild(pt.zcu);
370 break :b try self.lowerExprKnownResTyInner(node, child_type);
371 },
372 },
373 }),
374 .pointer => {
375 const ptr_info = res_ty.ptrInfo(pt.zcu);
376 switch (ptr_info.flags.size) {
377 .one => return pt.intern(.{ .ptr = .{
378 .ty = res_ty.toIntern(),
379 .base_addr = .{
380 .uav = .{
381 .orig_ty = res_ty.toIntern(),
382 .val = try self.lowerExprKnownResTyInner(node, .fromInterned(ptr_info.child)),
383 },
384 },
385 .byte_offset = 0,
386 } }),
387 .slice => return self.lowerSlice(node, res_ty),
388 else => {
389 // Unsupported pointer type, checked in `lower`
390 unreachable;
391 },
392 }
393 },
394 .bool => return self.lowerBool(node),
395 .int, .comptime_int => return self.lowerInt(node, res_ty),
396 .float, .comptime_float => return self.lowerFloat(node, res_ty),
397 .null => return self.lowerNull(node),
398 .@"enum" => return self.lowerEnum(node, res_ty),
399 .enum_literal => return self.lowerEnumLiteral(node),
400 .array => return self.lowerArray(node, res_ty),
401 .@"struct" => return self.lowerStructOrTuple(node, res_ty),
402 .@"union" => return self.lowerUnion(node, res_ty),
403 .vector => return self.lowerVector(node, res_ty),
404
405 .type,
406 .noreturn,
407 .undefined,
408 .error_union,
409 .error_set,
410 .@"fn",
411 .@"opaque",
412 .spirv,
413 .frame,
414 .@"anyframe",
415 .void,
416 => return self.fail(node, "type '{f}' not available in ZON", .{res_ty.fmt(pt)}),
417 }
418}
419
420fn lowerBool(self: *LowerZon, node: Zoir.Node.Index) !InternPool.Index {
421 return switch (node.get(self.file.zoir.?)) {
422 .true => .bool_true,
423 .false => .bool_false,
424 else => return error.WrongType,
425 };
426}
427
428fn lowerInt(
429 self: *LowerZon,
430 node: Zoir.Node.Index,
431 res_ty: Type,
432) !InternPool.Index {
433 @setFloatMode(.strict);
434 return switch (node.get(self.file.zoir.?)) {
435 .int_literal => |int| switch (int) {
436 .small => |val| {
437 const rhs: i32 = val;
438
439 // If our result is a fixed size integer, check that our value is not out of bounds
440 if (res_ty.zigTypeTag(self.sema.pt.zcu) == .int) {
441 const lhs_info = res_ty.intInfo(self.sema.pt.zcu);
442
443 // If lhs is unsigned and rhs is less than 0, we're out of bounds
444 if (lhs_info.signedness == .unsigned and rhs < 0) return self.fail(
445 node,
446 "type '{f}' cannot represent integer value '{d}'",
447 .{ res_ty.fmt(self.sema.pt), rhs },
448 );
449
450 // If lhs has less than the 32 bits rhs can hold, we need to check the max and
451 // min values
452 if (std.math.cast(u5, lhs_info.bits)) |bits| {
453 const unsigned_bits = bits - @intFromBool(lhs_info.signedness == .signed);
454 const min_int: i32 = switch (lhs_info.signedness) {
455 .unsigned => 0,
456 .signed => -(@as(i32, 1) << unsigned_bits),
457 };
458 const max_int: i32 = (@as(i32, 1) << unsigned_bits) - 1;
459 if (rhs < min_int or rhs > max_int) {
460 return self.fail(
461 node,
462 "type '{f}' cannot represent integer value '{d}'",
463 .{ res_ty.fmt(self.sema.pt), rhs },
464 );
465 }
466 }
467 }
468
469 return self.sema.pt.intern(.{ .int = .{
470 .ty = res_ty.toIntern(),
471 .storage = .{ .i64 = rhs },
472 } });
473 },
474 .big => |val| {
475 if (res_ty.zigTypeTag(self.sema.pt.zcu) == .int) {
476 const int_info = res_ty.intInfo(self.sema.pt.zcu);
477 if (!val.fitsInTwosComp(int_info.signedness, int_info.bits)) {
478 return self.fail(
479 node,
480 "type '{f}' cannot represent integer value '{d}'",
481 .{ res_ty.fmt(self.sema.pt), val },
482 );
483 }
484 }
485
486 return self.sema.pt.intern(.{ .int = .{
487 .ty = res_ty.toIntern(),
488 .storage = .{ .big_int = val },
489 } });
490 },
491 },
492 .float_literal => |val| {
493 var big_int: std.math.big.int.Mutable = .{
494 .limbs = try self.sema.arena.alloc(std.math.big.Limb, std.math.big.int.calcLimbLen(val)),
495 .len = undefined,
496 .positive = undefined,
497 };
498 switch (big_int.setFloat(val, .trunc)) {
499 .inexact => return self.fail(
500 node,
501 "fractional component prevents float value '{d}' from coercion to type '{f}'",
502 .{ val, res_ty.fmt(self.sema.pt) },
503 ),
504 .exact => {},
505 }
506
507 // Check that the result is in range of the result type
508 const int_info = res_ty.intInfo(self.sema.pt.zcu);
509 if (!big_int.toConst().fitsInTwosComp(int_info.signedness, int_info.bits)) {
510 return self.fail(
511 node,
512 "type '{f}' cannot represent integer value '{d}'",
513 .{ res_ty.fmt(self.sema.pt), val },
514 );
515 }
516
517 return self.sema.pt.intern(.{
518 .int = .{
519 .ty = res_ty.toIntern(),
520 .storage = .{ .big_int = big_int.toConst() },
521 },
522 });
523 },
524 .char_literal => |val| {
525 // If our result is a fixed size integer, check that our value is not out of bounds
526 if (res_ty.zigTypeTag(self.sema.pt.zcu) == .int) {
527 const dest_info = res_ty.intInfo(self.sema.pt.zcu);
528 const unsigned_bits = dest_info.bits - @intFromBool(dest_info.signedness == .signed);
529 if (unsigned_bits < 21) {
530 const out_of_range: u21 = @as(u21, 1) << @intCast(unsigned_bits);
531 if (val >= out_of_range) {
532 return self.fail(
533 node,
534 "type '{f}' cannot represent integer value '{d}'",
535 .{ res_ty.fmt(self.sema.pt), val },
536 );
537 }
538 }
539 }
540 return self.sema.pt.intern(.{
541 .int = .{
542 .ty = res_ty.toIntern(),
543 .storage = .{ .i64 = val },
544 },
545 });
546 },
547
548 else => return error.WrongType,
549 };
550}
551
552fn lowerFloat(
553 self: *LowerZon,
554 node: Zoir.Node.Index,
555 res_ty: Type,
556) !InternPool.Index {
557 @setFloatMode(.strict);
558 const value = switch (node.get(self.file.zoir.?)) {
559 .int_literal => |int| switch (int) {
560 .small => |val| try self.sema.pt.floatValue(res_ty, @as(f128, @floatFromInt(val))),
561 .big => |val| try self.sema.pt.floatValue(res_ty, val.toFloat(f128, .nearest_even)[0]),
562 },
563 .float_literal => |val| try self.sema.pt.floatValue(res_ty, val),
564 .char_literal => |val| try self.sema.pt.floatValue(res_ty, @as(f128, @floatFromInt(val))),
565 .pos_inf => b: {
566 if (res_ty.toIntern() == .comptime_float_type) return self.fail(
567 node,
568 "expected type '{f}'",
569 .{res_ty.fmt(self.sema.pt)},
570 );
571 break :b try self.sema.pt.floatValue(res_ty, std.math.inf(f128));
572 },
573 .neg_inf => b: {
574 if (res_ty.toIntern() == .comptime_float_type) return self.fail(
575 node,
576 "expected type '{f}'",
577 .{res_ty.fmt(self.sema.pt)},
578 );
579 break :b try self.sema.pt.floatValue(res_ty, -std.math.inf(f128));
580 },
581 .nan => b: {
582 if (res_ty.toIntern() == .comptime_float_type) return self.fail(
583 node,
584 "expected type '{f}'",
585 .{res_ty.fmt(self.sema.pt)},
586 );
587 break :b try self.sema.pt.floatValue(res_ty, std.math.nan(f128));
588 },
589 else => return error.WrongType,
590 };
591 return value.toIntern();
592}
593
594fn lowerNull(self: *LowerZon, node: Zoir.Node.Index) !InternPool.Index {
595 switch (node.get(self.file.zoir.?)) {
596 .null => return .null_value,
597 else => return error.WrongType,
598 }
599}
600
601fn lowerArray(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index {
602 const array_info = res_ty.arrayInfo(self.sema.pt.zcu);
603 const nodes: Zoir.Node.Index.Range = switch (node.get(self.file.zoir.?)) {
604 .array_literal => |nodes| nodes,
605 .empty_literal => .{ .start = node, .len = 0 },
606 else => return error.WrongType,
607 };
608
609 if (nodes.len != array_info.len) {
610 return error.WrongType;
611 }
612
613 const elems = try self.sema.arena.alloc(
614 InternPool.Index,
615 nodes.len + @intFromBool(array_info.sentinel != null),
616 );
617
618 for (0..nodes.len) |i| {
619 elems[i] = try self.lowerExprKnownResTy(nodes.at(@intCast(i)), array_info.elem_type);
620 }
621
622 if (array_info.sentinel) |sentinel| {
623 elems[elems.len - 1] = sentinel.toIntern();
624 }
625
626 return (try self.sema.pt.aggregateValue(res_ty, elems)).toIntern();
627}
628
629fn lowerEnum(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index {
630 const pt = self.sema.pt;
631 const comp = pt.zcu.comp;
632 const gpa = comp.gpa;
633 const io = comp.io;
634 const ip = &pt.zcu.intern_pool;
635 try self.sema.ensureLayoutResolved(res_ty, self.import_loc, .init);
636 switch (node.get(self.file.zoir.?)) {
637 .enum_literal => |field_name| {
638 const field_name_interned = try ip.getOrPutString(
639 gpa,
640 io,
641 self.sema.pt.tid,
642 field_name.get(self.file.zoir.?),
643 .no_embedded_nulls,
644 );
645 const field_index = res_ty.enumFieldIndex(field_name_interned, self.sema.pt.zcu) orelse {
646 return self.fail(
647 node,
648 "enum {f} has no member named '{f}'",
649 .{
650 res_ty.fmt(self.sema.pt),
651 std.zig.fmtId(field_name.get(self.file.zoir.?)),
652 },
653 );
654 };
655
656 const value = try self.sema.pt.enumValueFieldIndex(res_ty, field_index);
657
658 return value.toIntern();
659 },
660 else => return error.WrongType,
661 }
662}
663
664fn lowerEnumLiteral(self: *LowerZon, node: Zoir.Node.Index) !InternPool.Index {
665 const pt = self.sema.pt;
666 const comp = pt.zcu.comp;
667 const gpa = comp.gpa;
668 const io = comp.io;
669 const ip = &pt.zcu.intern_pool;
670 switch (node.get(self.file.zoir.?)) {
671 .enum_literal => |field_name| {
672 const field_name_interned = try ip.getOrPutString(
673 gpa,
674 io,
675 self.sema.pt.tid,
676 field_name.get(self.file.zoir.?),
677 .no_embedded_nulls,
678 );
679 return self.sema.pt.intern(.{ .enum_literal = field_name_interned });
680 },
681 else => return error.WrongType,
682 }
683}
684
685fn lowerStructOrTuple(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index {
686 const ip = &self.sema.pt.zcu.intern_pool;
687 return switch (ip.indexToKey(res_ty.toIntern())) {
688 .tuple_type => self.lowerTuple(node, res_ty),
689 .struct_type => self.lowerStruct(node, res_ty),
690 else => unreachable,
691 };
692}
693
694fn lowerTuple(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index {
695 const ip = &self.sema.pt.zcu.intern_pool;
696
697 const tuple_info = ip.indexToKey(res_ty.toIntern()).tuple_type;
698
699 const elem_nodes: Zoir.Node.Index.Range = switch (node.get(self.file.zoir.?)) {
700 .array_literal => |nodes| nodes,
701 .empty_literal => .{ .start = node, .len = 0 },
702 else => return error.WrongType,
703 };
704
705 const field_types = tuple_info.types.get(ip);
706 const elems = try self.sema.arena.alloc(InternPool.Index, field_types.len);
707
708 const field_comptime_vals = tuple_info.values.get(ip);
709 if (field_comptime_vals.len > 0) {
710 @memcpy(elems, field_comptime_vals);
711 } else {
712 @memset(elems, .none);
713 }
714
715 for (0..elem_nodes.len) |i| {
716 if (i >= elems.len) {
717 const elem_node = elem_nodes.at(@intCast(i));
718 return self.fail(
719 elem_node,
720 "index {} outside tuple of length {}",
721 .{
722 elems.len,
723 elem_nodes.at(@intCast(i)).getAstNode(self.file.zoir.?),
724 },
725 );
726 }
727
728 const val = try self.lowerExprKnownResTy(elem_nodes.at(@intCast(i)), .fromInterned(field_types[i]));
729
730 if (elems[i] != .none and val != elems[i]) {
731 const elem_node = elem_nodes.at(@intCast(i));
732 return self.fail(
733 elem_node,
734 "value stored in comptime field does not match the default value of the field",
735 .{},
736 );
737 }
738
739 elems[i] = val;
740 }
741
742 for (elems, 0..) |val, i| {
743 if (val == .none) {
744 return self.fail(node, "missing tuple field with index {}", .{i});
745 }
746 }
747
748 return (try self.sema.pt.aggregateValue(res_ty, elems)).toIntern();
749}
750
751fn lowerStruct(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index {
752 const pt = self.sema.pt;
753 const zcu = pt.zcu;
754 const comp = zcu.comp;
755 const gpa = comp.gpa;
756 const io = comp.io;
757 const ip = &pt.zcu.intern_pool;
758
759 try self.sema.ensureLayoutResolved(res_ty, self.import_loc, .init);
760 try self.sema.ensureStructDefaultsResolved(res_ty, self.import_loc);
761 const struct_info = self.sema.pt.zcu.typeToStruct(res_ty).?;
762
763 const fields: @FieldType(Zoir.Node, "struct_literal") = switch (node.get(self.file.zoir.?)) {
764 .struct_literal => |fields| fields,
765 .empty_literal => .{ .names = &.{}, .vals = .{ .start = node, .len = 0 } },
766 else => return error.WrongType,
767 };
768
769 const field_values = try self.sema.arena.alloc(InternPool.Index, struct_info.field_names.len);
770
771 const field_defaults = struct_info.field_defaults.get(ip);
772 if (field_defaults.len > 0) {
773 @memcpy(field_values, field_defaults);
774 } else {
775 @memset(field_values, .none);
776 }
777
778 for (0..fields.names.len) |i| {
779 const field_name = try ip.getOrPutString(
780 gpa,
781 io,
782 self.sema.pt.tid,
783 fields.names[i].get(self.file.zoir.?),
784 .no_embedded_nulls,
785 );
786 const field_node = fields.vals.at(@intCast(i));
787
788 const name_index = struct_info.nameIndex(ip, field_name) orelse {
789 return self.fail(field_node, "unexpected field '{f}'", .{field_name.fmt(ip)});
790 };
791
792 const field_type: Type = .fromInterned(struct_info.field_types.get(ip)[name_index]);
793 field_values[name_index] = try self.lowerExprKnownResTy(field_node, field_type);
794
795 if (struct_info.field_is_comptime_bits.get(ip, name_index)) {
796 const val = ip.indexToKey(field_values[name_index]);
797 const default = ip.indexToKey(field_defaults[name_index]);
798 if (!val.eql(default, ip)) {
799 return self.fail(
800 field_node,
801 "value stored in comptime field does not match the default value of the field",
802 .{},
803 );
804 }
805 }
806 }
807
808 const field_names = struct_info.field_names.get(ip);
809 for (field_values, field_names) |*value, name| {
810 if (value.* == .none) return self.fail(node, "missing field '{f}'", .{name.fmt(ip)});
811 }
812
813 const result: Value = switch (struct_info.layout) {
814 .auto, .@"extern" => try pt.aggregateValue(res_ty, field_values),
815 .@"packed" => result: {
816 const arena = self.sema.arena;
817 const buf = try arena.alloc(u8, @intCast((res_ty.bitSize(zcu) + 7) / 8));
818 @memset(buf, 0);
819 var bit_offset: u16 = 0;
820 for (field_values) |field_ip| {
821 const field_val: Value = .fromInterned(field_ip);
822 field_val.writeToPackedMemory(zcu, buf, bit_offset);
823 bit_offset += @intCast(field_val.typeOf(zcu).bitSize(zcu));
824 }
825 assert(bit_offset == res_ty.bitSize(zcu));
826 break :result try .readFromPackedMemory(res_ty, pt, buf, 0);
827 },
828 };
829 return result.toIntern();
830}
831
832fn lowerSlice(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index {
833 const pt = self.sema.pt;
834 const comp = pt.zcu.comp;
835 const gpa = comp.gpa;
836 const io = comp.io;
837 const ip = &pt.zcu.intern_pool;
838
839 const ptr_info = res_ty.ptrInfo(self.sema.pt.zcu);
840
841 assert(ptr_info.flags.size == .slice);
842
843 // String literals
844 const string_alignment = ptr_info.flags.alignment == .none or ptr_info.flags.alignment == .@"1";
845 const string_sentinel = ptr_info.sentinel == .none or ptr_info.sentinel == .zero_u8;
846 if (string_alignment and ptr_info.child == .u8_type and string_sentinel) {
847 switch (node.get(self.file.zoir.?)) {
848 .string_literal => |val| {
849 const ip_str = try ip.getOrPutString(gpa, io, self.sema.pt.tid, val, .maybe_embedded_nulls);
850 const str_ref = try self.sema.addStrLit(ip_str, val.len);
851 return (try self.sema.coerce(
852 self.block,
853 res_ty,
854 str_ref,
855 self.nodeSrc(node),
856 )).toInterned().?;
857 },
858 else => {},
859 }
860 }
861
862 // Slice literals
863 const elem_nodes: Zoir.Node.Index.Range = switch (node.get(self.file.zoir.?)) {
864 .array_literal => |nodes| nodes,
865 .empty_literal => .{ .start = node, .len = 0 },
866 else => return error.WrongType,
867 };
868
869 const elems = try self.sema.arena.alloc(InternPool.Index, elem_nodes.len + @intFromBool(ptr_info.sentinel != .none));
870
871 for (elems, 0..) |*elem, i| {
872 elem.* = try self.lowerExprKnownResTy(elem_nodes.at(@intCast(i)), .fromInterned(ptr_info.child));
873 }
874
875 if (ptr_info.sentinel != .none) {
876 elems[elems.len - 1] = ptr_info.sentinel;
877 }
878
879 const array_ty = try self.sema.pt.arrayType(.{
880 .len = elems.len,
881 .sentinel = ptr_info.sentinel,
882 .child = ptr_info.child,
883 });
884
885 const array_val = try self.sema.pt.aggregateValue(array_ty, elems);
886
887 const many_item_ptr_type = try self.sema.pt.intern(.{ .ptr_type = .{
888 .child = ptr_info.child,
889 .sentinel = ptr_info.sentinel,
890 .flags = b: {
891 var flags = ptr_info.flags;
892 flags.size = .many;
893 break :b flags;
894 },
895 .packed_offset = ptr_info.packed_offset,
896 } });
897
898 const many_item_ptr = try self.sema.pt.intern(.{
899 .ptr = .{
900 .ty = many_item_ptr_type,
901 .base_addr = .{
902 .uav = .{
903 .orig_ty = (try self.sema.pt.singleConstPtrType(array_ty)).toIntern(),
904 .val = array_val.toIntern(),
905 },
906 },
907 .byte_offset = 0,
908 },
909 });
910
911 const len = (try self.sema.pt.intValue(.usize, elems.len)).toIntern();
912
913 return self.sema.pt.intern(.{ .slice = .{
914 .ty = res_ty.toIntern(),
915 .ptr = many_item_ptr,
916 .len = len,
917 } });
918}
919
920fn lowerUnion(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index {
921 const pt = self.sema.pt;
922 const comp = pt.zcu.comp;
923 const gpa = comp.gpa;
924 const io = comp.io;
925 const ip = &pt.zcu.intern_pool;
926 try self.sema.ensureLayoutResolved(res_ty, self.import_loc, .init);
927 const union_info = pt.zcu.typeToUnion(res_ty).?;
928 const enum_tag_info = ip.loadEnumType(union_info.enum_tag_type);
929
930 const field_name, const maybe_field_node = switch (node.get(self.file.zoir.?)) {
931 .enum_literal => |name| b: {
932 const field_name = try ip.getOrPutString(
933 gpa,
934 io,
935 self.sema.pt.tid,
936 name.get(self.file.zoir.?),
937 .no_embedded_nulls,
938 );
939 break :b .{ field_name, null };
940 },
941 .struct_literal => b: {
942 const fields: @FieldType(Zoir.Node, "struct_literal") = switch (node.get(self.file.zoir.?)) {
943 .struct_literal => |fields| fields,
944 else => return self.fail(node, "expected type '{f}'", .{res_ty.fmt(self.sema.pt)}),
945 };
946 if (fields.names.len != 1) {
947 return error.WrongType;
948 }
949 const field_name = try ip.getOrPutString(
950 gpa,
951 io,
952 self.sema.pt.tid,
953 fields.names[0].get(self.file.zoir.?),
954 .no_embedded_nulls,
955 );
956 break :b .{ field_name, fields.vals.at(0) };
957 },
958 else => return error.WrongType,
959 };
960
961 const name_index = enum_tag_info.nameIndex(ip, field_name) orelse {
962 return error.WrongType;
963 };
964 const tag = try self.sema.pt.enumValueFieldIndex(.fromInterned(union_info.enum_tag_type), name_index);
965 const field_type: Type = .fromInterned(union_info.field_types.get(ip)[name_index]);
966 const val: Value = if (maybe_field_node) |field_node| b: {
967 if (field_type.toIntern() == .void_type) {
968 return self.fail(field_node, "expected type 'void'", .{});
969 }
970 break :b .fromInterned(try self.lowerExprKnownResTy(field_node, field_type));
971 } else b: {
972 if (field_type.toIntern() != .void_type) {
973 return error.WrongType;
974 }
975 break :b .void;
976 };
977 const result: Value = switch (union_info.layout) {
978 .auto, .@"extern" => try pt.unionValue(res_ty, tag, val),
979 .@"packed" => try self.sema.bitCastVal(val, res_ty),
980 };
981 return result.toIntern();
982}
983
984fn lowerVector(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index {
985 const ip = &self.sema.pt.zcu.intern_pool;
986
987 const vector_info = ip.indexToKey(res_ty.toIntern()).vector_type;
988
989 const elem_nodes: Zoir.Node.Index.Range = switch (node.get(self.file.zoir.?)) {
990 .array_literal => |nodes| nodes,
991 .empty_literal => .{ .start = node, .len = 0 },
992 else => return error.WrongType,
993 };
994
995 const elems = try self.sema.arena.alloc(InternPool.Index, vector_info.len);
996
997 if (elem_nodes.len != vector_info.len) {
998 return self.fail(
999 node,
1000 "expected {} vector elements; found {}",
1001 .{ vector_info.len, elem_nodes.len },
1002 );
1003 }
1004
1005 for (elems, 0..) |*elem, i| {
1006 elem.* = try self.lowerExprKnownResTy(elem_nodes.at(@intCast(i)), .fromInterned(vector_info.child));
1007 }
1008
1009 return (try self.sema.pt.aggregateValue(res_ty, elems)).toIntern();
1010}