authorchloe caruso <git@paperclover.net> 2024-05-27 17:19:49-07:00
committerchloe caruso <git@paperclover.net> 2024-05-27 17:19:49-07:00
loga78c131c45a7f6e79080e8a7965e19f35f656fd8
tree53497b56a37cfcc47cc25b6492d99989a8adcee6
parent5f95edf690b26c6b9172456261643c7eb2612987

introduce assignment targets and validate them


14 files changed, 758 insertions(+), 548 deletions(-)

CHANGELOG.md+7
......@@ -1,3 +1,10 @@
1## 0.1.3
2
3- feat(sema): coersion: numeric_literal -> float
4- feat(transform): lhs of assignments now validated
5- feat(transform): mark useless discard
6- fix(sema): properly coerce assignments
7
18## 0.1.2
29
310- feat: semantic analysis can return formatted errors to the user
src/compiler/Air.zig+1-1
......@@ -315,6 +315,6 @@ const Value = InternPool.Value;
315315const Type = InternPool.Type;
316316
317317const Teg = @import("Teg.zig");
318const dod = @import("dod.zig");
318const dod = @import("array.zig");
319319
320320const Unused = enum(u32) { zero = 0 };
src/compiler/Ast.zig+9-5
......@@ -381,11 +381,12 @@ pub const Node = struct {
381381pub const Error = error{ OutOfMemory, ParseFail };
382382
383383pub const Location = struct {
384 // TODO: add an ordinal number type, replace with Ordinal(usize)
385 line: usize,
386 column: usize,
387 line_start: usize,
388 line_end: usize,
384 line: Ordinal(u32),
385 column: Ordinal(u32),
386 // Byte offset of the start of the line
387 line_start: ByteOffset,
388 // Byte offset of the end of the line
389 line_end: ByteOffset,
389390};
390391
391392pub const Span = struct {
......@@ -672,3 +673,6 @@ const TokenList = Tokenizer.Token.List;
672673const ByteOffset = Tokenizer.ByteOffset;
673674
674675const Report = @import("Report.zig");
676
677const util = @import("util.zig");
678const Ordinal = util.Ordinal;
src/compiler/InternPool.zig+2-1
......@@ -171,6 +171,7 @@ pub const Value = enum(u32) {
171171 float_zero,
172172 nan,
173173 empty_string,
174 standard_library,
174175
175176 none = std.math.maxInt(u32),
176177 _,
......@@ -1170,7 +1171,7 @@ const Cache = @import("Cache.zig");
11701171const Teg = @import("Teg.zig");
11711172const Air = @import("Air.zig");
11721173
1173const dod = @import("dod.zig");
1174const dod = @import("array.zig");
11741175const Float = dod.Float;
11751176
11761177const tracer = @import("tracer");
src/compiler/JsNodeGen.zig+1-1
......@@ -696,5 +696,5 @@ const LocalIndex = JsTree.LocalIndex;
696696
697697const Air = @import("Air.zig");
698698
699const dod = @import("dod.zig");
699const dod = @import("array.zig");
700700const tracer = @import("tracer");
src/compiler/JsTree.zig+1-1
......@@ -252,6 +252,6 @@ const String = InternPool.String;
252252const Comp = @import("Comp.zig");
253253const Decl = Comp.Decl;
254254
255const dod = @import("dod.zig");
255const dod = @import("array.zig");
256256
257257const Unused = @import("Ast.zig").Unused;
src/compiler/Report.zig+168-88
......@@ -169,6 +169,48 @@ pub const Err = union(enum) {
169169 });
170170 }
171171 },
172 assign_builtin: struct {
173 node: NodeIndex,
174
175 fn build(data: @This(), ctx: *MsgContext) !void {
176 try ctx.msgNode(
177 data.node,
178 "cannot re-assign built-in \"{s}\"",
179 .{ctx.ast.basicNodeSlice(data.node)},
180 );
181 }
182 },
183 invalid_assignment_symbol: struct {
184 node: NodeIndex,
185 decl: NodeIndex,
186 kind: UnusedKind,
187
188 fn build(data: @This(), ctx: *MsgContext) !void {
189 const id = switch (ctx.ast.nodeTag(data.decl)) {
190 .param_decl => ctx.ast.fullParamDecl(data.decl).id,
191 .fn_decl => ctx.ast.fullFnDecl(data.decl).id,
192 .var_decl => ctx.ast.fullVarDecl(data.decl).id,
193 else => unreachable, // not a valid symbol node
194 };
195
196 try ctx.msgToken(ctx.ast.idToken(data.node), "cannot assign to {s} \"{s}\"", .{
197 @tagName(data.kind),
198 id,
199 });
200 try ctx.msgToken(ctx.ast.idToken(data.decl), "declared here", .{});
201 }
202 },
203 invalid_assignment_target: struct {
204 node: NodeIndex,
205
206 fn build(data: @This(), ctx: *MsgContext) !void {
207 try ctx.msgNode(
208 data.node,
209 "invalid assignment target",
210 .{},
211 );
212 }
213 },
172214};
173215
174216pub const Warn = union(enum) {
......@@ -177,7 +219,6 @@ pub const Warn = union(enum) {
177219 unused: struct {
178220 node: NodeIndex,
179221 kind: UnusedKind,
180 // shadowed: ?NodeIndex = null,
181222
182223 fn build(data: @This(), ctx: *MsgContext) !void {
183224 const id = switch (ctx.ast.nodeTag(data.node)) {
......@@ -192,15 +233,23 @@ pub const Warn = union(enum) {
192233 id,
193234 });
194235
195 // if (data.shadowed) |node| {
196 // try ctx.msgToken(ctx.ast.idToken(node), "{s} was re-declared here", .{
197 // @tagName(data.kind),
198 // });
199 // }
200
201236 try ctx.msgAlone("if this was intentional, discard it with \"_ = {s};\"", .{id});
202237 }
203238 },
239 not_mutated_var: struct {
240 node: NodeIndex,
241
242 fn build(data: @This(), ctx: *MsgContext) !void {
243 const id = switch (ctx.ast.nodeTag(data.node)) {
244 .var_decl => ctx.ast.fullVarDecl(data.node).id,
245 else => unreachable, // not a valid variable symbol node
246 };
247
248 try ctx.msgToken(ctx.ast.idToken(data.node), "variable \"{s}\" not mutated", .{
249 id,
250 });
251 }
252 },
204253 invalid_discard: struct {
205254 node: NodeIndex,
206255
......@@ -208,6 +257,20 @@ pub const Warn = union(enum) {
208257 try ctx.msgToken(ctx.ast.nodeToken(data.node), "\"let\" is not required for a discard", .{});
209258 }
210259 },
260 discard_builtin: struct {
261 node: NodeIndex,
262
263 fn build(data: @This(), ctx: *MsgContext) !void {
264 try ctx.msgToken(ctx.ast.nodeToken(data.node), "discarding a built-in symbol is meaningless", .{});
265 }
266 },
267 unnecessary_discard: struct {
268 node: NodeIndex,
269
270 fn build(data: @This(), ctx: *MsgContext) !void {
271 try ctx.msgToken(ctx.ast.nodeToken(data.node), "useless discard", .{});
272 }
273 },
211274 dangling_identifier: struct {
212275 node: NodeIndex,
213276
......@@ -219,9 +282,8 @@ pub const Warn = union(enum) {
219282 node: LazyNode,
220283
221284 fn build(data: @This(), ctx: *MsgContext) !void {
222 const var_decl_node = try data.node.get(ctx);
223 const init_node = ctx.ast.nodeData(var_decl_node).var_decl.init;
224 try ctx.msgNode(init_node, "constant integer in a \"var\" has an unknown type, assuming \"float\"", .{});
285 const node = try data.node.get(ctx);
286 try ctx.msgNode(node, "constant integer in a \"var\" has an unknown type, assuming \"float\"", .{});
225287 try ctx.msgAlone("add \": float\" or \": int\" after the variable name to set the type", .{});
226288 }
227289 },
......@@ -346,7 +408,6 @@ pub fn formatItem(
346408
347409 const need_load_ast = !(item_type == .@"error" and item.data.@"error" == .ast);
348410 const ast_data = if (need_load_ast) try file.getAst(comp) else undefined;
349 const ast = if (need_load_ast) &ast_data else null;
350411
351412 var ctx: MsgContext = .{
352413 .arena = alloc,
......@@ -392,92 +453,32 @@ pub fn formatItem(
392453 try writer.writeAll(component.message_utf8);
393454 try writer.writeAll("\n");
394455
395 const SliceLoc = struct {
396 loc: Location,
397 byte: ByteOffset,
398 slice: []const u8,
399 };
400
401456 const utf8_source = ctx.utf8_source;
402457
403 const at: SliceLoc = switch (component.highlight) {
404 .alone => {
405 if (is_last) {
406 try tty.setColor(writer, .dim);
407 try writer.print(" {s}", .{"┴"});
408 try writer.writeAll("\n");
409 try tty.setColor(writer, .reset);
410 }
411 continue;
412 },
413 inline .node,
414 .token,
415 .two_tokens,
416 => |payload, tag| loc: {
417 const token_index: TokenIndex = switch (tag) {
418 .node => ast.?.nodeToken(payload),
419 .token, .two_tokens => payload,
420 else => comptime unreachable,
421 };
422
423 const location = ast.?.tokenLocation(0, token_index);
424 const token = ast.?.tokens.get(token_index);
425
426 break :loc .{
427 .loc = location,
428 .slice = token.slice(ast.?.utf8_source),
429 .byte = token.offset,
430 };
431 },
432
433 .token_stored => |token| loc: {
434 const location = Tokenizer.tokenLocation(ctx.utf8_source, 0, token.offset);
435
436 break :loc .{
437 .loc = location,
438 .slice = token.slice(utf8_source),
439 .byte = token.offset,
440 };
441 },
442
443 inline .codepoint,
444 .codepoint_eol,
445 => |b, tag| loc: {
446 const len = switch (tag) {
447 .codepoint => std.unicode.utf8ByteSequenceLength(utf8_source[b]) catch 1,
448 .codepoint_eol => if (std.mem.indexOfScalar(u8, utf8_source[b..], '\n')) |n|
449 n
450 else
451 (utf8_source.len - b),
452 else => comptime unreachable,
453 };
454 break :loc .{
455 .byte = b,
456 .slice = utf8_source[b..][0..len],
457 .loc = .{
458 .column = 0,
459 .line = 0,
460 .line_start = 0,
461 .line_end = utf8_source.len,
462 },
463 };
464 },
458 const at = component.location orelse {
459 if (is_last) {
460 try tty.setColor(writer, .dim);
461 try writer.print(" {s}", .{"┴"});
462 try writer.writeAll("\n");
463 try tty.setColor(writer, .reset);
464 }
465 continue;
465466 };
466467
467468 const line_before = utf8_source[at.loc.line_start..at.byte];
468469 const trimmed_before = std.mem.trimLeft(u8, line_before, " \t\r");
469470 const line_after = utf8_source[at.byte + at.slice.len .. at.loc.line_end + 1];
470 const width = std.fmt.count("{d}", .{at.loc.line});
471 const width = std.fmt.count("{d}", .{at.loc.line.oneBased()});
471472
472473 try tty.setColor(writer, .dim);
473474 try writer.writeByteNTimes(' ', width);
474475 try writer.print("{s}{s}:{d}:{d}\n", .{
475476 if (i == 1) "┬ " else "├ ",
476477 file.file_path,
477 at.loc.line,
478 at.loc.column,
478 at.loc.line.oneBased(),
479 at.loc.column.oneBased(),
479480 });
480 try writer.print("{d}", .{at.loc.line});
481 try writer.print("{d}", .{at.loc.line.oneBased()});
481482 try writer.print("{s} ", .{"│"});
482483 try tty.setColor(writer, .reset);
483484 try writer.writeAll(trimmed_before);
......@@ -593,6 +594,17 @@ pub const Builder = struct {
593594 }
594595};
595596
597pub const SliceLoc = struct {
598 loc: Location,
599 byte: ByteOffset,
600 slice: []const u8,
601};
602
603const Component = struct {
604 message_utf8: []const u8,
605 location: ?SliceLoc,
606};
607
596608pub const Highlight = union(enum) {
597609 // No source view
598610 alone,
......@@ -608,11 +620,64 @@ pub const Highlight = union(enum) {
608620 codepoint: ByteOffset,
609621 // Highlight from the start of a codepoint to '\n'
610622 codepoint_eol: ByteOffset,
611};
612623
613const Component = struct {
614 message_utf8: []const u8,
615 highlight: Highlight,
624 pub fn loc(highlight: Highlight, ast: *const Ast) ?SliceLoc {
625 return switch (highlight) {
626 .alone => null,
627 inline .node,
628 .token,
629 .two_tokens,
630 => |payload, tag| loc: {
631 const token_index: TokenIndex = switch (tag) {
632 .node => ast.nodeToken(payload),
633 .token, .two_tokens => payload,
634 else => comptime unreachable,
635 };
636
637 const location = ast.tokenLocation(0, token_index);
638 const token = ast.tokens.get(token_index);
639
640 break :loc .{
641 .loc = location,
642 .slice = token.slice(ast.utf8_source),
643 .byte = token.offset,
644 };
645 },
646
647 .token_stored => |token| loc: {
648 const location = Tokenizer.tokenLocation(ast.utf8_source, 0, token.offset);
649
650 break :loc .{
651 .loc = location,
652 .slice = token.slice(ast.utf8_source),
653 .byte = token.offset,
654 };
655 },
656 inline .codepoint,
657 .codepoint_eol,
658 => |b, tag| loc: {
659 const len = switch (tag) {
660 .codepoint => std.unicode.utf8ByteSequenceLength(ast.utf8_source[b]) catch 1,
661 .codepoint_eol => if (std.mem.indexOfScalar(u8, ast.utf8_source[b..], '\n')) |n|
662 n
663 else
664 (ast.utf8_source.len - b),
665 else => comptime unreachable,
666 };
667 break :loc .{
668 .byte = b,
669 .slice = ast.utf8_source[b..][0..len],
670 .loc = .{
671 // TODO:
672 .column = .start,
673 .line = .start,
674 .line_start = 0,
675 .line_end = @intCast(ast.utf8_source.len),
676 },
677 };
678 },
679 };
680 }
616681};
617682
618683/// This serves as a small API to make creating messages easier.
......@@ -636,7 +701,7 @@ pub const MsgContext = struct {
636701 const text = try std.fmt.allocPrint(m.arena, fmt, args);
637702 try m.components.append(m.arena, .{
638703 .message_utf8 = text,
639 .highlight = highlight,
704 .location = highlight.loc(m.ast),
640705 });
641706 }
642707
......@@ -719,6 +784,21 @@ pub const LazyNode = struct {
719784 },
720785 else => return base_node,
721786 },
787 .memory => switch (node_tag) {
788 .var_decl => switch (lazy.operand) {
789 .operand => return data.var_decl.init,
790 else => {},
791 },
792 else => {},
793 },
794 .store => {
795 assert(node_tag.isAssignBin());
796 switch (lazy.operand) {
797 .lhs => return data.bin.lhs,
798 .rhs => return data.bin.rhs,
799 else => unreachable,
800 }
801 },
722802 else => {},
723803 }
724804
src/compiler/Sema.zig+15-10
......@@ -500,16 +500,16 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {
500500 assert(s.code.instTag(data.memory) == .memory); // corrupt store instruction
501501
502502 const location = s.mapTeg(data.memory.toRef()).decode().index;
503 s.markInst(location);
503504 const operand = try s.resolveRefRuntime(data.operand);
504
505 // try s.validateType(
506 // s.typeOfRef(operand).toValue(),
507 // s.tempAir().instData(.memory, location).ty.ip_index,
508 // );
509
505 const operand_coerced = try s.coerceRef(
506 in.op(.rhs),
507 operand,
508 s.typeOfRef(location.toRef()),
509 );
510510 return try s.addInstRef(.store, .{
511511 .memory = location,
512 .operand = operand,
512 .operand = operand_coerced,
513513 });
514514 },
515515 .as => {
......@@ -690,8 +690,13 @@ fn coerceValue(s: *Sema, src: InstOp, value: Value, to: Type) !Value {
690690 },
691691 else => return s.coersionError(src, from, to),
692692 },
693 .float => {
694 //
693 .float => switch (from) {
694 .float => unreachable, // same type handled
695 .numeric_literal => if (value.toFloat(s.ip)) |val|
696 return try s.makeValue(.{
697 .float = val,
698 }),
699 else => {},
695700 },
696701 .string => {
697702 //
......@@ -985,7 +990,7 @@ const InstOp = Teg.InstOp;
985990const Air = @import("Air.zig");
986991const Report = @import("Report.zig");
987992const Ast = @import("Ast.zig");
988const dod = @import("dod.zig");
993const dod = @import("array.zig");
989994const tracer = @import("tracer");
990995
991996const InternPool = @import("InternPool.zig");
src/compiler/Teg.zig+2-1
......@@ -224,6 +224,7 @@ pub const Inst = struct {
224224 float_zero,
225225 nan,
226226 empty_string,
227 standard_library,
227228
228229 /// This Ref does not correspond to any instruction or constant
229230 /// value and may instead be used as a sentinel to indicate null.
......@@ -472,7 +473,7 @@ const optionalTokenIndex = OptionalTokenIndex.init;
472473const optionalNodeIndex = OptionalNodeIndex.init;
473474const optionalExtraIndex = OptionalExtraIndex.init;
474475
475const dod = @import("dod.zig");
476const dod = @import("array.zig");
476477const Float = dod.Float;
477478
478479const InternPool = @import("InternPool.zig");
src/compiler/Tokenizer.zig+10-10
......@@ -949,10 +949,10 @@ pub fn tokenLocation(
949949 token_start: ByteOffset,
950950) Location {
951951 var loc: Location = .{
952 .line = 1,
953 .column = 1,
952 .line = .start,
953 .column = .start,
954954 .line_start = start_offset,
955 .line_end = utf8_source.len,
955 .line_end = @intCast(utf8_source.len),
956956 };
957957
958958 // Scan to by line until we go past the token start
......@@ -960,25 +960,25 @@ pub fn tokenLocation(
960960 if (i >= token_start) {
961961 break; // Went past
962962 }
963 loc.line += 1;
964 loc.line_start = i + 1;
963 loc.line = loc.line.add(1);
964 loc.line_start = @intCast(i + 1);
965965 }
966966
967967 const offset = loc.line_start;
968968 for (utf8_source[offset..], 0..) |c, i| {
969969 if (i + offset == token_start) {
970 loc.line_end = i + offset;
970 loc.line_end = @intCast(i + offset);
971971 while (loc.line_end < utf8_source.len and utf8_source[loc.line_end] != '\n') {
972972 loc.line_end += 1;
973973 }
974974 return loc;
975975 }
976976 if (c == '\n') {
977 loc.line += 1;
978 loc.column = 1;
979 loc.line_start = i + 1;
977 loc.line = loc.line.add(1);
978 loc.column = .start;
979 loc.line_start = @intCast(i + 1);
980980 } else {
981 loc.column += 1;
981 loc.column = loc.column.add(1);
982982 }
983983 }
984984
src/compiler/Transform.zig+137-40
......@@ -79,34 +79,49 @@ const Scope = struct {
7979 const Symbol = struct {
8080 node: NodeIndex,
8181 data: Symbol.Data,
82 // TODO: optimize this structure
8283 used: bool = false,
8384 mutated: bool = false,
85 discarded: ?NodeIndex = null,
8486
8587 const Data = union(enum) {
8688 declaration,
8789 param,
88 mutable: Inst.Ref,
90 mutable: Inst.Index,
8991 immutable: Inst.Ref,
9092 };
91 };
9293
93 fn addUnusedWarnings(s: Scope, t: *Transform) !void {
94 for (s.symbols.values()) |value| {
95 if (!value.used) {
94 pub fn addWarnings(symbol: Symbol, t: *Transform) !void {
95 if (!symbol.mutated and symbol.data == .mutable) {
96 try t.report.warn(.not_mutated_var, .{
97 .node = symbol.node,
98 });
99 }
100 if (!symbol.used) {
96101 try t.report.warn(.unused, .{
97 .node = value.node,
98 .kind = switch (value.data) {
102 .node = symbol.node,
103 .kind = switch (symbol.data) {
99104 .declaration => .function,
100105 .param => .param,
101106 .mutable => .variable,
102 .immutable => switch (t.ast.nodeTag(value.node)) {
107 .immutable => switch (t.ast.nodeTag(symbol.node)) {
103108 .param_decl => .param,
104109 else => .constant,
105110 },
106111 },
107112 });
113 } else if (symbol.discarded) |discard| {
114 try t.report.warn(.unnecessary_discard, .{
115 .node = discard,
116 });
108117 }
109118 }
119 };
120
121 fn addWarnings(s: Scope, t: *Transform) !void {
122 for (s.symbols.values()) |value| {
123 try value.addWarnings(t);
124 }
110125 }
111126
112127 fn deinit(s: *Scope, gpa: Allocator) void {
......@@ -212,7 +227,7 @@ const BuiltInIds = std.ComptimeStringMap(Inst.Ref, .{
212227 .{ "bool", .bool_type },
213228 .{ "noreturn", .noreturn_type },
214229 .{ "type", .type_type },
215 // todo: nan/infinity?
230 .{ "std", .standard_library },
216231});
217232
218233fn transformModule(t: *Transform, module_node: NodeIndex) Allocator.Error!Inst.Index {
......@@ -303,7 +318,7 @@ fn transformModule(t: *Transform, module_node: NodeIndex) Allocator.Error!Inst.I
303318 .extra_index = try t.extra.append(t.gpa, IndexList, declarations),
304319 });
305320
306 try t.scope().addUnusedWarnings(t);
321 try t.scope().addWarnings(t);
307322
308323 return module;
309324}
......@@ -400,7 +415,7 @@ fn transformFunction(t: *Transform, outer_decl: BlockContext, node: NodeIndex) A
400415 .extra_index = decl_extra,
401416 });
402417
403 try t.scope().addUnusedWarnings(t);
418 try t.scope().addWarnings(t);
404419
405420 return func;
406421}
......@@ -613,9 +628,8 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc
613628 .string = try t.addNullTerminatedString(id),
614629 }),
615630 }),
616 .mutable,
617 .immutable,
618 => |ref| ref,
631 .mutable => |idx| idx.toRef(),
632 .immutable => |ref| ref,
619633 .param => unreachable, //unresolved param
620634 };
621635 },
......@@ -650,14 +664,16 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc
650664
651665 .assign => {
652666 const ast_bin = t.ast.nodeData(node).bin;
653 const lhs = try t.transformExpression(decl, ast_bin.lhs) orelse return null;
654 const rhs = try t.transformExpression(decl, ast_bin.rhs) orelse return null;
667 const lhs = try t.transformAssignmentTarget(decl, ast_bin.lhs) orelse
668 return .void;
669 const rhs = try t.transformExpression(decl, ast_bin.rhs) orelse
670 return null;
655671 const assign_inst = try t.addInstRef(.{
656672 .tag = .store,
657673 .data = EncodedData.encode(.store, .{
658674 .src_node = decl.nodeOffset(node),
659675 .extra_index = try t.extra.append(t.gpa, Inst.Store, .{
660 .memory = lhs.toIndex() orelse @panic("TODO: convert this into Ref"),
676 .memory = lhs,
661677 .operand = rhs,
662678 }),
663679 }),
......@@ -709,7 +725,12 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc
709725 @tagName(tag),
710726 );
711727 const ast_bin = t.ast.nodeData(node).bin;
712 const lhs = try t.transformExpression(decl, ast_bin.lhs) orelse return null;
728 const lhs = if (is_assign)
729 try t.transformAssignmentTarget(decl, ast_bin.lhs) orelse
730 return .void
731 else
732 try t.transformExpression(decl, ast_bin.lhs) orelse
733 return null;
713734 const rhs = try t.transformExpression(decl, ast_bin.rhs) orelse return null;
714735
715736 const compute_inst = try t.addInstRef(.{
......@@ -717,7 +738,7 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc
717738 .data = EncodedData.encode(teg_tag, .{
718739 .src_node = decl.nodeOffset(node),
719740 .extra_index = try t.extra.append(t.gpa, Inst.Bin, .{
720 .lhs = lhs,
741 .lhs = if (is_assign) lhs.toRef() else lhs,
721742 .rhs = rhs,
722743 }),
723744 }),
......@@ -729,7 +750,7 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc
729750 .data = EncodedData.encode(.store, .{
730751 .src_node = decl.nodeOffset(node),
731752 .extra_index = try t.extra.append(t.gpa, Inst.Store, .{
732 .memory = lhs.toIndex() orelse @panic("TODO: convert this into Ref"),
753 .memory = lhs,
733754 .operand = compute_inst,
734755 }),
735756 }),
......@@ -1042,6 +1063,75 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc
10421063 }
10431064}
10441065
1066fn transformAssignmentTarget(t: *Transform, decl: BlockContext, node: NodeIndex) Allocator.Error!?Inst.Index {
1067 const node_tag = t.ast.nodes.items(.tag)[node];
1068
1069 const trace = tracer.traceFmt(@src(), "{s}", .{@tagName(node_tag)});
1070 defer trace.end();
1071
1072 switch (node_tag) {
1073 .root,
1074 .param_decl,
1075 .var_decl,
1076 .fn_decl,
1077 => unreachable, // not an expression
1078
1079 .id => {
1080 const id = t.ast.basicNodeSlice(node);
1081 assert(!mem.eql(u8, id, "_")); // discards are not assignment targets
1082
1083 if (BuiltInIds.has(id)) {
1084 try t.report.err(.assign_builtin, .{ .node = node });
1085 return null;
1086 }
1087
1088 if (mem.startsWith(u8, id, "@")) {
1089 @panic("TODO: string identifiers");
1090 }
1091
1092 const lookup = t.lookupId(id) orelse {
1093 try t.report.err(.unknown_identifier, .{ .node = node });
1094 return null;
1095 };
1096
1097 return switch (lookup.data) {
1098 .mutable => |inst| {
1099 lookup.mutated = true;
1100 return inst;
1101 },
1102 else => {
1103 lookup.used = true;
1104
1105 try t.report.err(.invalid_assignment_symbol, .{
1106 .node = node,
1107 .decl = lookup.node,
1108 .kind = switch (lookup.data) {
1109 .mutable => unreachable,
1110 .declaration => .function,
1111 .param => .param,
1112 .immutable => switch (t.ast.nodeTag(lookup.node)) {
1113 .param_decl => .param,
1114 else => .constant,
1115 },
1116 },
1117 });
1118 return null;
1119 },
1120 };
1121 },
1122
1123 // TODO: allow field expressions, but these need to emit a different store node
1124
1125 else => {
1126 _ = try t.transformExpression(decl, node);
1127 try t.report.err(.invalid_assignment_target, .{
1128 .node = node,
1129 });
1130 return null;
1131 },
1132 }
1133}
1134
10451135fn transformBlockStatement(
10461136 t: *Transform,
10471137 decl: BlockContext,
......@@ -1063,8 +1153,30 @@ fn transformBlockStatement(
10631153 if (mem.eql(u8, var_decl.id, "_")) {
10641154 try t.report.warn(.invalid_discard, .{ .node = node });
10651155
1066 const ref = try t.transformExpression(decl, var_decl.init) orelse
1067 return null;
1156 const ref = switch (t.ast.nodeTag(var_decl.init)) {
1157 .id => {
1158 const id = t.ast.basicNodeSlice(node);
1159 if (BuiltInIds.has(id)) {
1160 try t.report.warn(.discard_builtin, .{ .node = node });
1161 return .void;
1162 }
1163
1164 if (mem.startsWith(u8, id, "@")) {
1165 @panic("TODO: string identifiers");
1166 }
1167
1168 const lookup = t.lookupId(id) orelse {
1169 try t.report.err(.unknown_identifier, .{ .node = node });
1170 return .void;
1171 };
1172
1173 lookup.discarded = node;
1174
1175 return .void;
1176 },
1177 else => try t.transformExpression(decl, var_decl.init) orelse
1178 return null,
1179 };
10681180
10691181 return try t.addInstRef(.{
10701182 .tag = .discard,
......@@ -1115,29 +1227,14 @@ fn transformBlockStatement(
11151227
11161228 // if re-declaring, apply unused warnings now
11171229 if (gop.found_existing) {
1118 const value = gop.value_ptr.*;
1119 if (!value.used) {
1120 try t.report.warn(.unused, .{
1121 .node = value.node,
1122 .kind = switch (value.data) {
1123 .declaration => .function,
1124 .param => .param,
1125 .mutable => .variable,
1126 .immutable => switch (t.ast.nodeTag(value.node)) {
1127 .param_decl => .param,
1128 else => .constant,
1129 },
1130 },
1131 // .shadowed = node,
1132 });
1133 }
1230 try gop.value_ptr.addWarnings(t);
11341231 }
11351232
11361233 gop.value_ptr.* = .{
11371234 .node = node,
11381235 .data = switch (var_decl.is_const) {
11391236 true => .{ .immutable = mem_ref },
1140 false => .{ .mutable = mem_ref },
1237 false => .{ .mutable = mem_ref.toIndex().? },
11411238 },
11421239 };
11431240
......@@ -1352,7 +1449,7 @@ const IndexList = Teg.IndexList;
13521449const EncodedData = Teg.EncodedData;
13531450const ExtraIndex = Teg.ExtraIndex;
13541451
1355const dod = @import("dod.zig");
1452const dod = @import("array.zig");
13561453const ExtraStore = dod.ExtraStore(.teg, .@"var");
13571454const Float = dod.Float;
13581455
src/compiler/array.zig created+390
......@@ -0,0 +1,390 @@
1const extra = @This();
2
3pub const Mutability = enum { @"const", @"var" };
4
5fn setAsU32(items: []u32, comptime T: type, index: usize, payload: T) void {
6 const fields = std.meta.fields(@TypeOf(payload));
7 var i = index;
8 inline for (fields) |field| {
9 const value = @field(payload, field.name);
10 switch (field.type) {
11 u32 => items[i] = value,
12 i32 => items[i] = @bitCast(value),
13
14 [16]u8 => {
15 const u32s: [4]u32 = @bitCast(value);
16 items[i] = u32s[0];
17 items[i + 1] = u32s[1];
18 items[i + 2] = u32s[2];
19 items[i + 3] = u32s[3];
20 },
21
22 else => brk: {
23 const info = @typeInfo(field.type);
24 if (info == .Enum) {
25 items[i] = @intFromEnum(value);
26 break :brk;
27 }
28
29 const struct_data = info.Struct;
30 if (struct_data.layout == .@"packed") {
31 items[i] = @bitCast(value);
32 break :brk;
33 }
34
35 @call(.always_inline, setAsU32, .{ items, field.type, i, value });
36 },
37 }
38 i += extraSizeOf(field.type);
39 }
40}
41
42fn getAsU32(items: []const u32, comptime T: type, index: usize) T {
43 const fields = std.meta.fields(T);
44 var i = index;
45 var result: T = undefined;
46 inline for (fields) |field| {
47 @field(result, field.name) = switch (field.type) {
48 u32 => items[i],
49 i32 => @bitCast(items[i]),
50
51 [16]u8 => @bitCast(items[i..][0..4].*),
52
53 else => brk: {
54 const info = @typeInfo(field.type);
55 if (info == .Enum) {
56 break :brk @enumFromInt(items[i]);
57 }
58
59 const struct_data = info.Struct;
60 if (struct_data.layout == .@"packed") {
61 break :brk @bitCast(items[i]);
62 }
63
64 break :brk @call(
65 .always_inline,
66 getAsU32,
67 .{ items, field.type, i },
68 );
69 },
70 };
71 i += extraSizeOf(field.type);
72 }
73 return result;
74}
75
76pub const StoreKind = enum {
77 intern_pool,
78 teg,
79 air,
80 tfr,
81
82 pub fn resolveStoreConst(
83 comptime kind: StoreKind,
84 owner: anytype,
85 ) *const kind.ResolvedStore(@TypeOf(owner)) {
86 return &owner.extra;
87 }
88
89 pub fn resolveStoreMut(
90 comptime kind: StoreKind,
91 owner: anytype,
92 ) *kind.ResolvedStore(@TypeOf(owner)) {
93 return &owner.extra;
94 }
95
96 pub fn ResolvedStore(
97 comptime kind: StoreKind,
98 comptime Owner: type,
99 ) type {
100 const ptr = @typeInfo(Owner).Pointer;
101 assert(ptr.size == .One);
102 const mutability = brk: for (@typeInfo(ptr.child).Struct.fields) |field| {
103 if (std.mem.eql(u8, field.name, "extra")) {
104 break :brk field.type.mutability;
105 }
106 } else unreachable;
107 return ExtraStore(kind, mutability);
108 }
109};
110
111fn ExtraWrap(comptime kind: StoreKind) type {
112 return struct {
113 pub fn Index(comptime T: type) type {
114 return enum(u32) {
115 _,
116
117 // Reads the data from `store.extra` and returns it.
118 pub fn get(index: @This(), owner: anytype) T {
119 return kind.resolveStoreConst(owner).get(T, @intFromEnum(index));
120 }
121
122 /// Returns data with a trailing reader to read data stored immediately after
123 /// the main data structure. What is stored should be documented on `T`
124 pub fn getTrail(index: @This(), owner: anytype) struct {
125 T,
126 kind.ResolvedStore(@TypeOf(owner)).TrailReader,
127 } {
128 const advance = comptime extraSizeOf(T);
129 return .{
130 index.get(owner),
131 .{ .index = @intFromEnum(index) + advance },
132 };
133 }
134 };
135 }
136 };
137}
138
139pub fn EncodedData(comptime Tag: type, backing_type: type) type {
140 return enum(backing_type) {
141 _,
142
143 pub fn decode(data: @This(), comptime tag: Tag, proof: Tag) tag.Data() {
144 assert(proof == tag); // incorrect memory access
145 const Data = tag.Data();
146 const info = @typeInfo(Data);
147 if (info == .Enum) {
148 return @enumFromInt(@intFromEnum(data));
149 }
150 if (info == .Struct) {
151 assert(backing_type == u64);
152 var fields: [2]u32 = @bitCast(@intFromEnum(data));
153 return getAsU32(&fields, Data, 0);
154 }
155 return @bitCast(@intFromEnum(data));
156 }
157
158 pub fn encode(comptime tag: Tag, data: tag.Data()) @This() {
159 const Data = tag.Data();
160 const info = @typeInfo(Data);
161 if (info == .Enum) {
162 return @enumFromInt(@intFromEnum(data));
163 }
164 if (info == .Struct) {
165 assert(backing_type == u64);
166 var fields: [2]u32 = undefined;
167 setAsU32(&fields, Data, 0, data);
168 return @enumFromInt(@as(backing_type, @bitCast(fields)));
169 }
170 return @enumFromInt(@as(backing_type, @bitCast(data)));
171 }
172 };
173}
174
175/// The number of 'u32's this type consumes in `ip.extra`.
176pub fn extraSizeOf(comptime T: type) usize {
177 return switch (T) {
178 u32,
179 i32,
180 => return 1,
181
182 [16]u8 => return 4,
183
184 else => {
185 const info = @typeInfo(T);
186 if (info == .Enum) {
187 comptime assert(@sizeOf(info.Enum.tag_type) <= 32);
188 return 1;
189 }
190
191 const struct_data = info.Struct;
192 if (struct_data.layout == .@"packed") {
193 comptime assert(struct_data.backing_integer == u32);
194 return 1;
195 }
196
197 const fields = std.meta.fields(T);
198 var size: usize = 0;
199 inline for (fields) |field| {
200 size += extraSizeOf(field.type);
201 }
202 return size;
203 },
204 };
205}
206
207pub fn ExtraStore(comptime set_kind: StoreKind, comptime set_mutability: Mutability) type {
208 return struct {
209 list: List = .{},
210
211 pub const kind = set_kind;
212 pub const mutability = set_mutability;
213
214 const Store = @This();
215
216 const index_holder = ExtraWrap(kind);
217 pub const Index = index_holder.Index;
218
219 pub const List = switch (mutability) {
220 .@"const" => struct {
221 items: []const u32 = &.{},
222
223 pub fn deinit(slice: *@This(), gpa: Allocator) void {
224 gpa.free(slice.items);
225 }
226 },
227 .@"var" => std.ArrayListUnmanaged(u32),
228 };
229
230 pub fn deinit(store: *Store, gpa: Allocator) void {
231 store.list.deinit(gpa);
232 store.* = undefined;
233 }
234
235 pub const TrailReader = struct {
236 index: usize,
237
238 pub fn read(trail: *TrailReader, ip: *const Store, comptime T: type) T {
239 defer trail.index += comptime extraSizeOf(T);
240 return ip.get(T, trail.index);
241 }
242
243 pub fn readValueSlice(trail: *TrailReader, len: u32) @import("InternPool.zig").Value.Slice {
244 defer trail.index += len;
245 return .{
246 .start = @intCast(trail.index),
247 .len = len,
248 };
249 }
250
251 pub fn readTypeSlice(trail: *TrailReader, len: u32) @import("InternPool.zig").Type.Slice {
252 defer trail.index += len;
253 return .{
254 .start = @intCast(trail.index),
255 .len = len,
256 };
257 }
258 };
259
260 pub const TrailWriter = struct {
261 store: *Store,
262 leftover: usize,
263
264 pub fn init(store: *Store, len: usize) TrailWriter {
265 return .{ .store = store, .leftover = len };
266 }
267
268 pub fn end(w: *TrailWriter) void {
269 assert(w.leftover == 0);
270 }
271
272 pub fn writeU32(w: *TrailWriter, value: u32) void {
273 assert(w.leftover > 0);
274 w.ip.extra.appendAssumeCapacity(value);
275 w.leftover -= 1;
276 }
277
278 pub fn writeSlice(w: *TrailWriter, slice: anytype) void {
279 const Child = std.meta.Child(@TypeOf(slice));
280 const child = @typeInfo(Child);
281 comptime assert(Child == u32 or (child == .Enum and child.Enum.tag_type == u32));
282 assert(w.leftover >= slice.len);
283 w.store.list.appendSliceAssumeCapacity(@ptrCast(slice));
284 w.leftover -= slice.len;
285 }
286
287 pub fn writeStruct(w: *TrailWriter, comptime T: type, data: T) void {
288 _ = appendAssumeCapacity(w.store, T, data);
289 }
290 };
291
292 pub fn append(store: *Store, gpa: Allocator, comptime T: type, payload: T) Allocator.Error!Index(T) {
293 comptime assert(mutability == .@"var");
294 try store.list.ensureUnusedCapacity(gpa, extraSizeOf(@TypeOf(payload)));
295 return store.appendAssumeCapacity(T, payload);
296 }
297
298 pub fn appendAssumeCapacity(ip: *Store, comptime T: type, payload: T) Index(T) {
299 comptime assert(mutability == .@"var");
300 const extra_index: u32 = @intCast(ip.list.items.len);
301 ip.list.items.len += extraSizeOf(@TypeOf(payload));
302 ip.set(T, extra_index, payload);
303 return @enumFromInt(extra_index);
304 }
305
306 pub fn appendTrailing(
307 store: *Store,
308 gpa: Allocator,
309 comptime T: type,
310 payload: T,
311 trailing_len: usize,
312 ) Allocator.Error!struct { Index(T), TrailWriter } {
313 comptime assert(mutability == .@"var");
314 try store.list.ensureUnusedCapacity(gpa, extraSizeOf(@TypeOf(payload)) + trailing_len);
315 const extra_index: u32 = @intCast(store.list.items.len);
316 store.list.items.len += extraSizeOf(@TypeOf(payload));
317 store.set(T, extra_index, payload);
318 return .{
319 @enumFromInt(extra_index),
320 TrailWriter.init(store, trailing_len),
321 };
322 }
323
324 fn set(store: *Store, comptime T: type, index: usize, payload: T) void {
325 comptime assert(mutability == .@"var");
326 setAsU32(store.list.items, T, index, payload);
327 }
328
329 fn get(store: *const Store, comptime T: type, index: usize) T {
330 return getAsU32(store.list.items, T, index);
331 }
332
333 pub fn toOwnedStore(store: *Store, gpa: Allocator) !ExtraStore(set_kind, .@"const") {
334 comptime assert(mutability == .@"var");
335 return .{ .list = .{ .items = try store.list.toOwnedSlice(gpa) } };
336 }
337
338 pub fn constant(store: *const Store) ExtraStore(set_kind, .@"const") {
339 return .{ .list = .{ .items = store.list.items } };
340 }
341 };
342}
343
344pub const Float = struct {
345 low: u32,
346 high: u32,
347
348 pub const pure_nan = Float{ .low = 0x7f800000, .high = 0 };
349
350 pub fn purifyNaN(f: f64) f64 {
351 if (std.math.isNan(f)) {
352 return comptime Float.pure_nan.decode();
353 }
354 return f;
355 }
356
357 pub fn assertPure(f: f64) f64 {
358 if (std.math.isNan(f)) {
359 assert(Float.pure_nan.eql(encode(f)));
360 }
361 return f;
362 }
363
364 pub fn encode(decoded: f64) Float {
365 if (std.math.isNan(decoded)) {
366 return pure_nan;
367 }
368 const as_bytes: [2]u32 = @bitCast(decoded);
369 return .{
370 .low = as_bytes[0],
371 .high = as_bytes[1],
372 };
373 }
374
375 pub fn decode(encoded: Float) f64 {
376 const as_bytes: [2]u32 = .{
377 encoded.low,
378 encoded.high,
379 };
380 return @bitCast(as_bytes);
381 }
382
383 pub fn eql(a: Float, b: Float) bool {
384 return a.low == b.low and a.high == b.high;
385 }
386};
387
388const std = @import("std");
389const assert = std.debug.assert;
390const Allocator = std.mem.Allocator;
src/compiler/dod.zig deleted-390
......@@ -1,390 +0,0 @@
1const extra = @This();
2
3pub const Mutability = enum { @"const", @"var" };
4
5fn setAsU32(items: []u32, comptime T: type, index: usize, payload: T) void {
6 const fields = std.meta.fields(@TypeOf(payload));
7 var i = index;
8 inline for (fields) |field| {
9 const value = @field(payload, field.name);
10 switch (field.type) {
11 u32 => items[i] = value,
12 i32 => items[i] = @bitCast(value),
13
14 [16]u8 => {
15 const u32s: [4]u32 = @bitCast(value);
16 items[i] = u32s[0];
17 items[i + 1] = u32s[1];
18 items[i + 2] = u32s[2];
19 items[i + 3] = u32s[3];
20 },
21
22 else => brk: {
23 const info = @typeInfo(field.type);
24 if (info == .Enum) {
25 items[i] = @intFromEnum(value);
26 break :brk;
27 }
28
29 const struct_data = info.Struct;
30 if (struct_data.layout == .@"packed") {
31 items[i] = @bitCast(value);
32 break :brk;
33 }
34
35 @call(.always_inline, setAsU32, .{ items, field.type, i, value });
36 },
37 }
38 i += extraSizeOf(field.type);
39 }
40}
41
42fn getAsU32(items: []const u32, comptime T: type, index: usize) T {
43 const fields = std.meta.fields(T);
44 var i = index;
45 var result: T = undefined;
46 inline for (fields) |field| {
47 @field(result, field.name) = switch (field.type) {
48 u32 => items[i],
49 i32 => @bitCast(items[i]),
50
51 [16]u8 => @bitCast(items[i..][0..4].*),
52
53 else => brk: {
54 const info = @typeInfo(field.type);
55 if (info == .Enum) {
56 break :brk @enumFromInt(items[i]);
57 }
58
59 const struct_data = info.Struct;
60 if (struct_data.layout == .@"packed") {
61 break :brk @bitCast(items[i]);
62 }
63
64 break :brk @call(
65 .always_inline,
66 getAsU32,
67 .{ items, field.type, i },
68 );
69 },
70 };
71 i += extraSizeOf(field.type);
72 }
73 return result;
74}
75
76pub const StoreKind = enum {
77 intern_pool,
78 teg,
79 air,
80 tfr,
81
82 pub fn resolveStoreConst(
83 comptime kind: StoreKind,
84 owner: anytype,
85 ) *const kind.ResolvedStore(@TypeOf(owner)) {
86 return &owner.extra;
87 }
88
89 pub fn resolveStoreMut(
90 comptime kind: StoreKind,
91 owner: anytype,
92 ) *kind.ResolvedStore(@TypeOf(owner)) {
93 return &owner.extra;
94 }
95
96 pub fn ResolvedStore(
97 comptime kind: StoreKind,
98 comptime Owner: type,
99 ) type {
100 const ptr = @typeInfo(Owner).Pointer;
101 assert(ptr.size == .One);
102 const mutability = brk: for (@typeInfo(ptr.child).Struct.fields) |field| {
103 if (std.mem.eql(u8, field.name, "extra")) {
104 break :brk field.type.mutability;
105 }
106 } else unreachable;
107 return ExtraStore(kind, mutability);
108 }
109};
110
111fn ExtraWrap(comptime kind: StoreKind) type {
112 return struct {
113 pub fn Index(comptime T: type) type {
114 return enum(u32) {
115 _,
116
117 // Reads the data from `store.extra` and returns it.
118 pub fn get(index: @This(), owner: anytype) T {
119 return kind.resolveStoreConst(owner).get(T, @intFromEnum(index));
120 }
121
122 /// Returns data with a trailing reader to read data stored immediately after
123 /// the main data structure. What is stored should be documented on `T`
124 pub fn getTrail(index: @This(), owner: anytype) struct {
125 T,
126 kind.ResolvedStore(@TypeOf(owner)).TrailReader,
127 } {
128 const advance = comptime extraSizeOf(T);
129 return .{
130 index.get(owner),
131 .{ .index = @intFromEnum(index) + advance },
132 };
133 }
134 };
135 }
136 };
137}
138
139pub fn EncodedData(comptime Tag: type, backing_type: type) type {
140 return enum(backing_type) {
141 _,
142
143 pub fn decode(data: @This(), comptime tag: Tag, proof: Tag) tag.Data() {
144 assert(proof == tag); // incorrect memory access
145 const Data = tag.Data();
146 const info = @typeInfo(Data);
147 if (info == .Enum) {
148 return @enumFromInt(@intFromEnum(data));
149 }
150 if (info == .Struct) {
151 assert(backing_type == u64);
152 var fields: [2]u32 = @bitCast(@intFromEnum(data));
153 return getAsU32(&fields, Data, 0);
154 }
155 return @bitCast(@intFromEnum(data));
156 }
157
158 pub fn encode(comptime tag: Tag, data: tag.Data()) @This() {
159 const Data = tag.Data();
160 const info = @typeInfo(Data);
161 if (info == .Enum) {
162 return @enumFromInt(@intFromEnum(data));
163 }
164 if (info == .Struct) {
165 assert(backing_type == u64);
166 var fields: [2]u32 = undefined;
167 setAsU32(&fields, Data, 0, data);
168 return @enumFromInt(@as(backing_type, @bitCast(fields)));
169 }
170 return @enumFromInt(@as(backing_type, @bitCast(data)));
171 }
172 };
173}
174
175/// The number of 'u32's this type consumes in `ip.extra`.
176pub fn extraSizeOf(comptime T: type) usize {
177 return switch (T) {
178 u32,
179 i32,
180 => return 1,
181
182 [16]u8 => return 4,
183
184 else => {
185 const info = @typeInfo(T);
186 if (info == .Enum) {
187 comptime assert(@sizeOf(info.Enum.tag_type) <= 32);
188 return 1;
189 }
190
191 const struct_data = info.Struct;
192 if (struct_data.layout == .@"packed") {
193 comptime assert(struct_data.backing_integer == u32);
194 return 1;
195 }
196
197 const fields = std.meta.fields(T);
198 var size: usize = 0;
199 inline for (fields) |field| {
200 size += extraSizeOf(field.type);
201 }
202 return size;
203 },
204 };
205}
206
207pub fn ExtraStore(comptime set_kind: StoreKind, comptime set_mutability: Mutability) type {
208 return struct {
209 list: List = .{},
210
211 pub const kind = set_kind;
212 pub const mutability = set_mutability;
213
214 const Store = @This();
215
216 const index_holder = ExtraWrap(kind);
217 pub const Index = index_holder.Index;
218
219 pub const List = switch (mutability) {
220 .@"const" => struct {
221 items: []const u32 = &.{},
222
223 pub fn deinit(slice: *@This(), gpa: Allocator) void {
224 gpa.free(slice.items);
225 }
226 },
227 .@"var" => std.ArrayListUnmanaged(u32),
228 };
229
230 pub fn deinit(store: *Store, gpa: Allocator) void {
231 store.list.deinit(gpa);
232 store.* = undefined;
233 }
234
235 pub const TrailReader = struct {
236 index: usize,
237
238 pub fn read(trail: *TrailReader, ip: *const Store, comptime T: type) T {
239 defer trail.index += comptime extraSizeOf(T);
240 return ip.get(T, trail.index);
241 }
242
243 pub fn readValueSlice(trail: *TrailReader, len: u32) @import("InternPool.zig").Value.Slice {
244 defer trail.index += len;
245 return .{
246 .start = @intCast(trail.index),
247 .len = len,
248 };
249 }
250
251 pub fn readTypeSlice(trail: *TrailReader, len: u32) @import("InternPool.zig").Type.Slice {
252 defer trail.index += len;
253 return .{
254 .start = @intCast(trail.index),
255 .len = len,
256 };
257 }
258 };
259
260 pub const TrailWriter = struct {
261 store: *Store,
262 leftover: usize,
263
264 pub fn init(store: *Store, len: usize) TrailWriter {
265 return .{ .store = store, .leftover = len };
266 }
267
268 pub fn end(w: *TrailWriter) void {
269 assert(w.leftover == 0);
270 }
271
272 pub fn writeU32(w: *TrailWriter, value: u32) void {
273 assert(w.leftover > 0);
274 w.ip.extra.appendAssumeCapacity(value);
275 w.leftover -= 1;
276 }
277
278 pub fn writeSlice(w: *TrailWriter, slice: anytype) void {
279 const Child = std.meta.Child(@TypeOf(slice));
280 const child = @typeInfo(Child);
281 comptime assert(Child == u32 or (child == .Enum and child.Enum.tag_type == u32));
282 assert(w.leftover >= slice.len);
283 w.store.list.appendSliceAssumeCapacity(@ptrCast(slice));
284 w.leftover -= slice.len;
285 }
286
287 pub fn writeStruct(w: *TrailWriter, comptime T: type, data: T) void {
288 _ = appendAssumeCapacity(w.store, T, data);
289 }
290 };
291
292 pub fn append(store: *Store, gpa: Allocator, comptime T: type, payload: T) Allocator.Error!Index(T) {
293 comptime assert(mutability == .@"var");
294 try store.list.ensureUnusedCapacity(gpa, extraSizeOf(@TypeOf(payload)));
295 return store.appendAssumeCapacity(T, payload);
296 }
297
298 pub fn appendAssumeCapacity(ip: *Store, comptime T: type, payload: T) Index(T) {
299 comptime assert(mutability == .@"var");
300 const extra_index: u32 = @intCast(ip.list.items.len);
301 ip.list.items.len += extraSizeOf(@TypeOf(payload));
302 ip.set(T, extra_index, payload);
303 return @enumFromInt(extra_index);
304 }
305
306 pub fn appendTrailing(
307 store: *Store,
308 gpa: Allocator,
309 comptime T: type,
310 payload: T,
311 trailing_len: usize,
312 ) Allocator.Error!struct { Index(T), TrailWriter } {
313 comptime assert(mutability == .@"var");
314 try store.list.ensureUnusedCapacity(gpa, extraSizeOf(@TypeOf(payload)) + trailing_len);
315 const extra_index: u32 = @intCast(store.list.items.len);
316 store.list.items.len += extraSizeOf(@TypeOf(payload));
317 store.set(T, extra_index, payload);
318 return .{
319 @enumFromInt(extra_index),
320 TrailWriter.init(store, trailing_len),
321 };
322 }
323
324 fn set(store: *Store, comptime T: type, index: usize, payload: T) void {
325 comptime assert(mutability == .@"var");
326 setAsU32(store.list.items, T, index, payload);
327 }
328
329 fn get(store: *const Store, comptime T: type, index: usize) T {
330 return getAsU32(store.list.items, T, index);
331 }
332
333 pub fn toOwnedStore(store: *Store, gpa: Allocator) !ExtraStore(set_kind, .@"const") {
334 comptime assert(mutability == .@"var");
335 return .{ .list = .{ .items = try store.list.toOwnedSlice(gpa) } };
336 }
337
338 pub fn constant(store: *const Store) ExtraStore(set_kind, .@"const") {
339 return .{ .list = .{ .items = store.list.items } };
340 }
341 };
342}
343
344pub const Float = struct {
345 low: u32,
346 high: u32,
347
348 pub const pure_nan = Float{ .low = 0x7f800000, .high = 0 };
349
350 pub fn purifyNaN(f: f64) f64 {
351 if (std.math.isNan(f)) {
352 return comptime Float.pure_nan.decode();
353 }
354 return f;
355 }
356
357 pub fn assertPure(f: f64) f64 {
358 if (std.math.isNan(f)) {
359 assert(Float.pure_nan.eql(encode(f)));
360 }
361 return f;
362 }
363
364 pub fn encode(decoded: f64) Float {
365 if (std.math.isNan(decoded)) {
366 return pure_nan;
367 }
368 const as_bytes: [2]u32 = @bitCast(decoded);
369 return .{
370 .low = as_bytes[0],
371 .high = as_bytes[1],
372 };
373 }
374
375 pub fn decode(encoded: Float) f64 {
376 const as_bytes: [2]u32 = .{
377 encoded.low,
378 encoded.high,
379 };
380 return @bitCast(as_bytes);
381 }
382
383 pub fn eql(a: Float, b: Float) bool {
384 return a.low == b.low and a.high == b.high;
385 }
386};
387
388const std = @import("std");
389const assert = std.debug.assert;
390const Allocator = std.mem.Allocator;
src/compiler/util.zig+15
......@@ -19,7 +19,9 @@ pub fn detectTTYFromWriter(writer: anytype) std.io.tty.Config {
1919pub fn Ordinal(Int: type) type {
2020 comptime assert(@typeInfo(Int).Int.signedness == .unsigned);
2121 return enum(Int) {
22 start = 0,
2223 _,
24
2325 pub fn fromZeroBased(int: Int) @This() {
2426 assert(int != std.math.maxInt(Int));
2527 return @enumFromInt(int);
......@@ -34,6 +36,19 @@ pub fn Ordinal(Int: type) type {
3436 pub fn oneBased(ord: @This()) Int {
3537 return @intFromEnum(ord) + 1;
3638 }
39
40 pub fn add(ord: @This(), inc: Int) @This() {
41 return fromZeroBased(ord.zeroBased() + inc);
42 }
43
44 pub fn format(
45 _: @This(),
46 comptime _: []const u8,
47 _: std.fmt.FormatOptions,
48 _: anytype,
49 ) !void {
50 @compileError("Do not format `Ordinal` directly. Call oneBased or zeroBased to specify.");
51 }
3752 };
3853}
3954