authorgravatar for me@paperdave.netdave caruso <me@paperdave.net> 2024-05-27 17:19:49-07:00
committergravatar for me@paperdave.netdave caruso <me@paperdave.net> 2024-05-27 17:19:49-07:00
logd024b032f7ff08f0876ca53d5e4b55b294f419c4
tree53497b56a37cfcc47cc25b6492d99989a8adcee6
parentdeeaeceb25e046541a6e42d49c4e51226dca9660
signature Commit is signed but in an unrecognized format.

introduce assignment targets and validate them


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

CHANGELOG.md+7
...@@ -1,3 +1,10 @@...@@ -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
1## 0.1.28## 0.1.2
29
3- feat: semantic analysis can return formatted errors to the user10- feat: semantic analysis can return formatted errors to the user
src/compiler/Air.zig+1-1
...@@ -315,6 +315,6 @@ const Value = InternPool.Value;...@@ -315,6 +315,6 @@ const Value = InternPool.Value;
315const Type = InternPool.Type;315const Type = InternPool.Type;
316316
317const Teg = @import("Teg.zig");317const Teg = @import("Teg.zig");
318const dod = @import("dod.zig");318const dod = @import("array.zig");
319319
320const Unused = enum(u32) { zero = 0 };320const Unused = enum(u32) { zero = 0 };
src/compiler/Ast.zig+9-5
...@@ -381,11 +381,12 @@ pub const Node = struct {...@@ -381,11 +381,12 @@ pub const Node = struct {
381pub const Error = error{ OutOfMemory, ParseFail };381pub const Error = error{ OutOfMemory, ParseFail };
382382
383pub const Location = struct {383pub const Location = struct {
384 // TODO: add an ordinal number type, replace with Ordinal(usize)384 line: Ordinal(u32),
385 line: usize,385 column: Ordinal(u32),
386 column: usize,386 // Byte offset of the start of the line
387 line_start: usize,387 line_start: ByteOffset,
388 line_end: usize,388 // Byte offset of the end of the line
389 line_end: ByteOffset,
389};390};
390391
391pub const Span = struct {392pub const Span = struct {
...@@ -672,3 +673,6 @@ const TokenList = Tokenizer.Token.List;...@@ -672,3 +673,6 @@ const TokenList = Tokenizer.Token.List;
672const ByteOffset = Tokenizer.ByteOffset;673const ByteOffset = Tokenizer.ByteOffset;
673674
674const Report = @import("Report.zig");675const 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) {...@@ -171,6 +171,7 @@ pub const Value = enum(u32) {
171 float_zero,171 float_zero,
172 nan,172 nan,
173 empty_string,173 empty_string,
174 standard_library,
174175
175 none = std.math.maxInt(u32),176 none = std.math.maxInt(u32),
176 _,177 _,
...@@ -1170,7 +1171,7 @@ const Cache = @import("Cache.zig");...@@ -1170,7 +1171,7 @@ const Cache = @import("Cache.zig");
1170const Teg = @import("Teg.zig");1171const Teg = @import("Teg.zig");
1171const Air = @import("Air.zig");1172const Air = @import("Air.zig");
11721173
1173const dod = @import("dod.zig");1174const dod = @import("array.zig");
1174const Float = dod.Float;1175const Float = dod.Float;
11751176
1176const tracer = @import("tracer");1177const tracer = @import("tracer");
src/compiler/JsNodeGen.zig+1-1
...@@ -696,5 +696,5 @@ const LocalIndex = JsTree.LocalIndex;...@@ -696,5 +696,5 @@ const LocalIndex = JsTree.LocalIndex;
696696
697const Air = @import("Air.zig");697const Air = @import("Air.zig");
698698
699const dod = @import("dod.zig");699const dod = @import("array.zig");
700const tracer = @import("tracer");700const tracer = @import("tracer");
src/compiler/JsTree.zig+1-1
...@@ -252,6 +252,6 @@ const String = InternPool.String;...@@ -252,6 +252,6 @@ const String = InternPool.String;
252const Comp = @import("Comp.zig");252const Comp = @import("Comp.zig");
253const Decl = Comp.Decl;253const Decl = Comp.Decl;
254254
255const dod = @import("dod.zig");255const dod = @import("array.zig");
256256
257const Unused = @import("Ast.zig").Unused;257const Unused = @import("Ast.zig").Unused;
src/compiler/Report.zig+168-88
...@@ -169,6 +169,48 @@ pub const Err = union(enum) {...@@ -169,6 +169,48 @@ pub const Err = union(enum) {
169 });169 });
170 }170 }
171 },171 },
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 },
172};214};
173215
174pub const Warn = union(enum) {216pub const Warn = union(enum) {
...@@ -177,7 +219,6 @@ pub const Warn = union(enum) {...@@ -177,7 +219,6 @@ pub const Warn = union(enum) {
177 unused: struct {219 unused: struct {
178 node: NodeIndex,220 node: NodeIndex,
179 kind: UnusedKind,221 kind: UnusedKind,
180 // shadowed: ?NodeIndex = null,
181222
182 fn build(data: @This(), ctx: *MsgContext) !void {223 fn build(data: @This(), ctx: *MsgContext) !void {
183 const id = switch (ctx.ast.nodeTag(data.node)) {224 const id = switch (ctx.ast.nodeTag(data.node)) {
...@@ -192,15 +233,23 @@ pub const Warn = union(enum) {...@@ -192,15 +233,23 @@ pub const Warn = union(enum) {
192 id,233 id,
193 });234 });
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
201 try ctx.msgAlone("if this was intentional, discard it with \"_ = {s};\"", .{id});236 try ctx.msgAlone("if this was intentional, discard it with \"_ = {s};\"", .{id});
202 }237 }
203 },238 },
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 },
204 invalid_discard: struct {253 invalid_discard: struct {
205 node: NodeIndex,254 node: NodeIndex,
206255
...@@ -208,6 +257,20 @@ pub const Warn = union(enum) {...@@ -208,6 +257,20 @@ pub const Warn = union(enum) {
208 try ctx.msgToken(ctx.ast.nodeToken(data.node), "\"let\" is not required for a discard", .{});257 try ctx.msgToken(ctx.ast.nodeToken(data.node), "\"let\" is not required for a discard", .{});
209 }258 }
210 },259 },
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 },
211 dangling_identifier: struct {274 dangling_identifier: struct {
212 node: NodeIndex,275 node: NodeIndex,
213276
...@@ -219,9 +282,8 @@ pub const Warn = union(enum) {...@@ -219,9 +282,8 @@ pub const Warn = union(enum) {
219 node: LazyNode,282 node: LazyNode,
220283
221 fn build(data: @This(), ctx: *MsgContext) !void {284 fn build(data: @This(), ctx: *MsgContext) !void {
222 const var_decl_node = try data.node.get(ctx);285 const node = try data.node.get(ctx);
223 const init_node = ctx.ast.nodeData(var_decl_node).var_decl.init;286 try ctx.msgNode(node, "constant integer in a \"var\" has an unknown type, assuming \"float\"", .{});
224 try ctx.msgNode(init_node, "constant integer in a \"var\" has an unknown type, assuming \"float\"", .{});
225 try ctx.msgAlone("add \": float\" or \": int\" after the variable name to set the type", .{});287 try ctx.msgAlone("add \": float\" or \": int\" after the variable name to set the type", .{});
226 }288 }
227 },289 },
...@@ -346,7 +408,6 @@ pub fn formatItem(...@@ -346,7 +408,6 @@ pub fn formatItem(
346408
347 const need_load_ast = !(item_type == .@"error" and item.data.@"error" == .ast);409 const need_load_ast = !(item_type == .@"error" and item.data.@"error" == .ast);
348 const ast_data = if (need_load_ast) try file.getAst(comp) else undefined;410 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
351 var ctx: MsgContext = .{412 var ctx: MsgContext = .{
352 .arena = alloc,413 .arena = alloc,
...@@ -392,92 +453,32 @@ pub fn formatItem(...@@ -392,92 +453,32 @@ pub fn formatItem(
392 try writer.writeAll(component.message_utf8);453 try writer.writeAll(component.message_utf8);
393 try writer.writeAll("\n");454 try writer.writeAll("\n");
394455
395 const SliceLoc = struct {
396 loc: Location,
397 byte: ByteOffset,
398 slice: []const u8,
399 };
400
401 const utf8_source = ctx.utf8_source;456 const utf8_source = ctx.utf8_source;
402457
403 const at: SliceLoc = switch (component.highlight) {458 const at = component.location orelse {
404 .alone => {459 if (is_last) {
405 if (is_last) {460 try tty.setColor(writer, .dim);
406 try tty.setColor(writer, .dim);461 try writer.print(" {s}", .{"┴"});
407 try writer.print(" {s}", .{"┴"});462 try writer.writeAll("\n");
408 try writer.writeAll("\n");463 try tty.setColor(writer, .reset);
409 try tty.setColor(writer, .reset);464 }
410 }465 continue;
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 },
465 };466 };
466467
467 const line_before = utf8_source[at.loc.line_start..at.byte];468 const line_before = utf8_source[at.loc.line_start..at.byte];
468 const trimmed_before = std.mem.trimLeft(u8, line_before, " \t\r");469 const trimmed_before = std.mem.trimLeft(u8, line_before, " \t\r");
469 const line_after = utf8_source[at.byte + at.slice.len .. at.loc.line_end + 1];470 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
472 try tty.setColor(writer, .dim);473 try tty.setColor(writer, .dim);
473 try writer.writeByteNTimes(' ', width);474 try writer.writeByteNTimes(' ', width);
474 try writer.print("{s}{s}:{d}:{d}\n", .{475 try writer.print("{s}{s}:{d}:{d}\n", .{
475 if (i == 1) "┬ " else "├ ",476 if (i == 1) "┬ " else "├ ",
476 file.file_path,477 file.file_path,
477 at.loc.line,478 at.loc.line.oneBased(),
478 at.loc.column,479 at.loc.column.oneBased(),
479 });480 });
480 try writer.print("{d}", .{at.loc.line});481 try writer.print("{d}", .{at.loc.line.oneBased()});
481 try writer.print("{s} ", .{"│"});482 try writer.print("{s} ", .{"│"});
482 try tty.setColor(writer, .reset);483 try tty.setColor(writer, .reset);
483 try writer.writeAll(trimmed_before);484 try writer.writeAll(trimmed_before);
...@@ -593,6 +594,17 @@ pub const Builder = struct {...@@ -593,6 +594,17 @@ pub const Builder = struct {
593 }594 }
594};595};
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
596pub const Highlight = union(enum) {608pub const Highlight = union(enum) {
597 // No source view609 // No source view
598 alone,610 alone,
...@@ -608,11 +620,64 @@ pub const Highlight = union(enum) {...@@ -608,11 +620,64 @@ pub const Highlight = union(enum) {
608 codepoint: ByteOffset,620 codepoint: ByteOffset,
609 // Highlight from the start of a codepoint to '\n'621 // Highlight from the start of a codepoint to '\n'
610 codepoint_eol: ByteOffset,622 codepoint_eol: ByteOffset,
611};
612623
613const Component = struct {624 pub fn loc(highlight: Highlight, ast: *const Ast) ?SliceLoc {
614 message_utf8: []const u8,625 return switch (highlight) {
615 highlight: 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 }
616};681};
617682
618/// This serves as a small API to make creating messages easier.683/// This serves as a small API to make creating messages easier.
...@@ -636,7 +701,7 @@ pub const MsgContext = struct {...@@ -636,7 +701,7 @@ pub const MsgContext = struct {
636 const text = try std.fmt.allocPrint(m.arena, fmt, args);701 const text = try std.fmt.allocPrint(m.arena, fmt, args);
637 try m.components.append(m.arena, .{702 try m.components.append(m.arena, .{
638 .message_utf8 = text,703 .message_utf8 = text,
639 .highlight = highlight,704 .location = highlight.loc(m.ast),
640 });705 });
641 }706 }
642707
...@@ -719,6 +784,21 @@ pub const LazyNode = struct {...@@ -719,6 +784,21 @@ pub const LazyNode = struct {
719 },784 },
720 else => return base_node,785 else => return base_node,
721 },786 },
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 },
722 else => {},802 else => {},
723 }803 }
724804
src/compiler/Sema.zig+15-10
...@@ -500,16 +500,16 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {...@@ -500,16 +500,16 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {
500 assert(s.code.instTag(data.memory) == .memory); // corrupt store instruction500 assert(s.code.instTag(data.memory) == .memory); // corrupt store instruction
501501
502 const location = s.mapTeg(data.memory.toRef()).decode().index;502 const location = s.mapTeg(data.memory.toRef()).decode().index;
503 s.markInst(location);
503 const operand = try s.resolveRefRuntime(data.operand);504 const operand = try s.resolveRefRuntime(data.operand);
504505 const operand_coerced = try s.coerceRef(
505 // try s.validateType(506 in.op(.rhs),
506 // s.typeOfRef(operand).toValue(),507 operand,
507 // s.tempAir().instData(.memory, location).ty.ip_index,508 s.typeOfRef(location.toRef()),
508 // );509 );
509
510 return try s.addInstRef(.store, .{510 return try s.addInstRef(.store, .{
511 .memory = location,511 .memory = location,
512 .operand = operand,512 .operand = operand_coerced,
513 });513 });
514 },514 },
515 .as => {515 .as => {
...@@ -690,8 +690,13 @@ fn coerceValue(s: *Sema, src: InstOp, value: Value, to: Type) !Value {...@@ -690,8 +690,13 @@ fn coerceValue(s: *Sema, src: InstOp, value: Value, to: Type) !Value {
690 },690 },
691 else => return s.coersionError(src, from, to),691 else => return s.coersionError(src, from, to),
692 },692 },
693 .float => {693 .float => switch (from) {
694 //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 => {},
695 },700 },
696 .string => {701 .string => {
697 //702 //
...@@ -985,7 +990,7 @@ const InstOp = Teg.InstOp;...@@ -985,7 +990,7 @@ const InstOp = Teg.InstOp;
985const Air = @import("Air.zig");990const Air = @import("Air.zig");
986const Report = @import("Report.zig");991const Report = @import("Report.zig");
987const Ast = @import("Ast.zig");992const Ast = @import("Ast.zig");
988const dod = @import("dod.zig");993const dod = @import("array.zig");
989const tracer = @import("tracer");994const tracer = @import("tracer");
990995
991const InternPool = @import("InternPool.zig");996const InternPool = @import("InternPool.zig");
src/compiler/Teg.zig+2-1
...@@ -224,6 +224,7 @@ pub const Inst = struct {...@@ -224,6 +224,7 @@ pub const Inst = struct {
224 float_zero,224 float_zero,
225 nan,225 nan,
226 empty_string,226 empty_string,
227 standard_library,
227228
228 /// This Ref does not correspond to any instruction or constant229 /// This Ref does not correspond to any instruction or constant
229 /// value and may instead be used as a sentinel to indicate null.230 /// value and may instead be used as a sentinel to indicate null.
...@@ -472,7 +473,7 @@ const optionalTokenIndex = OptionalTokenIndex.init;...@@ -472,7 +473,7 @@ const optionalTokenIndex = OptionalTokenIndex.init;
472const optionalNodeIndex = OptionalNodeIndex.init;473const optionalNodeIndex = OptionalNodeIndex.init;
473const optionalExtraIndex = OptionalExtraIndex.init;474const optionalExtraIndex = OptionalExtraIndex.init;
474475
475const dod = @import("dod.zig");476const dod = @import("array.zig");
476const Float = dod.Float;477const Float = dod.Float;
477478
478const InternPool = @import("InternPool.zig");479const InternPool = @import("InternPool.zig");
src/compiler/Tokenizer.zig+10-10
...@@ -949,10 +949,10 @@ pub fn tokenLocation(...@@ -949,10 +949,10 @@ pub fn tokenLocation(
949 token_start: ByteOffset,949 token_start: ByteOffset,
950) Location {950) Location {
951 var loc: Location = .{951 var loc: Location = .{
952 .line = 1,952 .line = .start,
953 .column = 1,953 .column = .start,
954 .line_start = start_offset,954 .line_start = start_offset,
955 .line_end = utf8_source.len,955 .line_end = @intCast(utf8_source.len),
956 };956 };
957957
958 // Scan to by line until we go past the token start958 // Scan to by line until we go past the token start
...@@ -960,25 +960,25 @@ pub fn tokenLocation(...@@ -960,25 +960,25 @@ pub fn tokenLocation(
960 if (i >= token_start) {960 if (i >= token_start) {
961 break; // Went past961 break; // Went past
962 }962 }
963 loc.line += 1;963 loc.line = loc.line.add(1);
964 loc.line_start = i + 1;964 loc.line_start = @intCast(i + 1);
965 }965 }
966966
967 const offset = loc.line_start;967 const offset = loc.line_start;
968 for (utf8_source[offset..], 0..) |c, i| {968 for (utf8_source[offset..], 0..) |c, i| {
969 if (i + offset == token_start) {969 if (i + offset == token_start) {
970 loc.line_end = i + offset;970 loc.line_end = @intCast(i + offset);
971 while (loc.line_end < utf8_source.len and utf8_source[loc.line_end] != '\n') {971 while (loc.line_end < utf8_source.len and utf8_source[loc.line_end] != '\n') {
972 loc.line_end += 1;972 loc.line_end += 1;
973 }973 }
974 return loc;974 return loc;
975 }975 }
976 if (c == '\n') {976 if (c == '\n') {
977 loc.line += 1;977 loc.line = loc.line.add(1);
978 loc.column = 1;978 loc.column = .start;
979 loc.line_start = i + 1;979 loc.line_start = @intCast(i + 1);
980 } else {980 } else {
981 loc.column += 1;981 loc.column = loc.column.add(1);
982 }982 }
983 }983 }
984984
src/compiler/Transform.zig+137-40
...@@ -79,34 +79,49 @@ const Scope = struct {...@@ -79,34 +79,49 @@ const Scope = struct {
79 const Symbol = struct {79 const Symbol = struct {
80 node: NodeIndex,80 node: NodeIndex,
81 data: Symbol.Data,81 data: Symbol.Data,
82 // TODO: optimize this structure
82 used: bool = false,83 used: bool = false,
83 mutated: bool = false,84 mutated: bool = false,
85 discarded: ?NodeIndex = null,
8486
85 const Data = union(enum) {87 const Data = union(enum) {
86 declaration,88 declaration,
87 param,89 param,
88 mutable: Inst.Ref,90 mutable: Inst.Index,
89 immutable: Inst.Ref,91 immutable: Inst.Ref,
90 };92 };
91 };
9293
93 fn addUnusedWarnings(s: Scope, t: *Transform) !void {94 pub fn addWarnings(symbol: Symbol, t: *Transform) !void {
94 for (s.symbols.values()) |value| {95 if (!symbol.mutated and symbol.data == .mutable) {
95 if (!value.used) {96 try t.report.warn(.not_mutated_var, .{
97 .node = symbol.node,
98 });
99 }
100 if (!symbol.used) {
96 try t.report.warn(.unused, .{101 try t.report.warn(.unused, .{
97 .node = value.node,102 .node = symbol.node,
98 .kind = switch (value.data) {103 .kind = switch (symbol.data) {
99 .declaration => .function,104 .declaration => .function,
100 .param => .param,105 .param => .param,
101 .mutable => .variable,106 .mutable => .variable,
102 .immutable => switch (t.ast.nodeTag(value.node)) {107 .immutable => switch (t.ast.nodeTag(symbol.node)) {
103 .param_decl => .param,108 .param_decl => .param,
104 else => .constant,109 else => .constant,
105 },110 },
106 },111 },
107 });112 });
113 } else if (symbol.discarded) |discard| {
114 try t.report.warn(.unnecessary_discard, .{
115 .node = discard,
116 });
108 }117 }
109 }118 }
119 };
120
121 fn addWarnings(s: Scope, t: *Transform) !void {
122 for (s.symbols.values()) |value| {
123 try value.addWarnings(t);
124 }
110 }125 }
111126
112 fn deinit(s: *Scope, gpa: Allocator) void {127 fn deinit(s: *Scope, gpa: Allocator) void {
...@@ -212,7 +227,7 @@ const BuiltInIds = std.ComptimeStringMap(Inst.Ref, .{...@@ -212,7 +227,7 @@ const BuiltInIds = std.ComptimeStringMap(Inst.Ref, .{
212 .{ "bool", .bool_type },227 .{ "bool", .bool_type },
213 .{ "noreturn", .noreturn_type },228 .{ "noreturn", .noreturn_type },
214 .{ "type", .type_type },229 .{ "type", .type_type },
215 // todo: nan/infinity?230 .{ "std", .standard_library },
216});231});
217232
218fn transformModule(t: *Transform, module_node: NodeIndex) Allocator.Error!Inst.Index {233fn 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...@@ -303,7 +318,7 @@ fn transformModule(t: *Transform, module_node: NodeIndex) Allocator.Error!Inst.I
303 .extra_index = try t.extra.append(t.gpa, IndexList, declarations),318 .extra_index = try t.extra.append(t.gpa, IndexList, declarations),
304 });319 });
305320
306 try t.scope().addUnusedWarnings(t);321 try t.scope().addWarnings(t);
307322
308 return module;323 return module;
309}324}
...@@ -400,7 +415,7 @@ fn transformFunction(t: *Transform, outer_decl: BlockContext, node: NodeIndex) A...@@ -400,7 +415,7 @@ fn transformFunction(t: *Transform, outer_decl: BlockContext, node: NodeIndex) A
400 .extra_index = decl_extra,415 .extra_index = decl_extra,
401 });416 });
402417
403 try t.scope().addUnusedWarnings(t);418 try t.scope().addWarnings(t);
404419
405 return func;420 return func;
406}421}
...@@ -613,9 +628,8 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc...@@ -613,9 +628,8 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc
613 .string = try t.addNullTerminatedString(id),628 .string = try t.addNullTerminatedString(id),
614 }),629 }),
615 }),630 }),
616 .mutable,631 .mutable => |idx| idx.toRef(),
617 .immutable,632 .immutable => |ref| ref,
618 => |ref| ref,
619 .param => unreachable, //unresolved param633 .param => unreachable, //unresolved param
620 };634 };
621 },635 },
...@@ -650,14 +664,16 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc...@@ -650,14 +664,16 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc
650664
651 .assign => {665 .assign => {
652 const ast_bin = t.ast.nodeData(node).bin;666 const ast_bin = t.ast.nodeData(node).bin;
653 const lhs = try t.transformExpression(decl, ast_bin.lhs) orelse return null;667 const lhs = try t.transformAssignmentTarget(decl, ast_bin.lhs) orelse
654 const rhs = try t.transformExpression(decl, ast_bin.rhs) orelse return null;668 return .void;
669 const rhs = try t.transformExpression(decl, ast_bin.rhs) orelse
670 return null;
655 const assign_inst = try t.addInstRef(.{671 const assign_inst = try t.addInstRef(.{
656 .tag = .store,672 .tag = .store,
657 .data = EncodedData.encode(.store, .{673 .data = EncodedData.encode(.store, .{
658 .src_node = decl.nodeOffset(node),674 .src_node = decl.nodeOffset(node),
659 .extra_index = try t.extra.append(t.gpa, Inst.Store, .{675 .extra_index = try t.extra.append(t.gpa, Inst.Store, .{
660 .memory = lhs.toIndex() orelse @panic("TODO: convert this into Ref"),676 .memory = lhs,
661 .operand = rhs,677 .operand = rhs,
662 }),678 }),
663 }),679 }),
...@@ -709,7 +725,12 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc...@@ -709,7 +725,12 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc
709 @tagName(tag),725 @tagName(tag),
710 );726 );
711 const ast_bin = t.ast.nodeData(node).bin;727 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;
713 const rhs = try t.transformExpression(decl, ast_bin.rhs) orelse return null;734 const rhs = try t.transformExpression(decl, ast_bin.rhs) orelse return null;
714735
715 const compute_inst = try t.addInstRef(.{736 const compute_inst = try t.addInstRef(.{
...@@ -717,7 +738,7 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc...@@ -717,7 +738,7 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc
717 .data = EncodedData.encode(teg_tag, .{738 .data = EncodedData.encode(teg_tag, .{
718 .src_node = decl.nodeOffset(node),739 .src_node = decl.nodeOffset(node),
719 .extra_index = try t.extra.append(t.gpa, Inst.Bin, .{740 .extra_index = try t.extra.append(t.gpa, Inst.Bin, .{
720 .lhs = lhs,741 .lhs = if (is_assign) lhs.toRef() else lhs,
721 .rhs = rhs,742 .rhs = rhs,
722 }),743 }),
723 }),744 }),
...@@ -729,7 +750,7 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc...@@ -729,7 +750,7 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc
729 .data = EncodedData.encode(.store, .{750 .data = EncodedData.encode(.store, .{
730 .src_node = decl.nodeOffset(node),751 .src_node = decl.nodeOffset(node),
731 .extra_index = try t.extra.append(t.gpa, Inst.Store, .{752 .extra_index = try t.extra.append(t.gpa, Inst.Store, .{
732 .memory = lhs.toIndex() orelse @panic("TODO: convert this into Ref"),753 .memory = lhs,
733 .operand = compute_inst,754 .operand = compute_inst,
734 }),755 }),
735 }),756 }),
...@@ -1042,6 +1063,75 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc...@@ -1042,6 +1063,75 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc
1042 }1063 }
1043}1064}
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
1045fn transformBlockStatement(1135fn transformBlockStatement(
1046 t: *Transform,1136 t: *Transform,
1047 decl: BlockContext,1137 decl: BlockContext,
...@@ -1063,8 +1153,30 @@ fn transformBlockStatement(...@@ -1063,8 +1153,30 @@ fn transformBlockStatement(
1063 if (mem.eql(u8, var_decl.id, "_")) {1153 if (mem.eql(u8, var_decl.id, "_")) {
1064 try t.report.warn(.invalid_discard, .{ .node = node });1154 try t.report.warn(.invalid_discard, .{ .node = node });
10651155
1066 const ref = try t.transformExpression(decl, var_decl.init) orelse1156 const ref = switch (t.ast.nodeTag(var_decl.init)) {
1067 return null;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
1069 return try t.addInstRef(.{1181 return try t.addInstRef(.{
1070 .tag = .discard,1182 .tag = .discard,
...@@ -1115,29 +1227,14 @@ fn transformBlockStatement(...@@ -1115,29 +1227,14 @@ fn transformBlockStatement(
11151227
1116 // if re-declaring, apply unused warnings now1228 // if re-declaring, apply unused warnings now
1117 if (gop.found_existing) {1229 if (gop.found_existing) {
1118 const value = gop.value_ptr.*;1230 try gop.value_ptr.addWarnings(t);
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 }
1134 }1231 }
11351232
1136 gop.value_ptr.* = .{1233 gop.value_ptr.* = .{
1137 .node = node,1234 .node = node,
1138 .data = switch (var_decl.is_const) {1235 .data = switch (var_decl.is_const) {
1139 true => .{ .immutable = mem_ref },1236 true => .{ .immutable = mem_ref },
1140 false => .{ .mutable = mem_ref },1237 false => .{ .mutable = mem_ref.toIndex().? },
1141 },1238 },
1142 };1239 };
11431240
...@@ -1352,7 +1449,7 @@ const IndexList = Teg.IndexList;...@@ -1352,7 +1449,7 @@ const IndexList = Teg.IndexList;
1352const EncodedData = Teg.EncodedData;1449const EncodedData = Teg.EncodedData;
1353const ExtraIndex = Teg.ExtraIndex;1450const ExtraIndex = Teg.ExtraIndex;
13541451
1355const dod = @import("dod.zig");1452const dod = @import("array.zig");
1356const ExtraStore = dod.ExtraStore(.teg, .@"var");1453const ExtraStore = dod.ExtraStore(.teg, .@"var");
1357const Float = dod.Float;1454const 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 {...@@ -19,7 +19,9 @@ pub fn detectTTYFromWriter(writer: anytype) std.io.tty.Config {
19pub fn Ordinal(Int: type) type {19pub fn Ordinal(Int: type) type {
20 comptime assert(@typeInfo(Int).Int.signedness == .unsigned);20 comptime assert(@typeInfo(Int).Int.signedness == .unsigned);
21 return enum(Int) {21 return enum(Int) {
22 start = 0,
22 _,23 _,
24
23 pub fn fromZeroBased(int: Int) @This() {25 pub fn fromZeroBased(int: Int) @This() {
24 assert(int != std.math.maxInt(Int));26 assert(int != std.math.maxInt(Int));
25 return @enumFromInt(int);27 return @enumFromInt(int);
...@@ -34,6 +36,19 @@ pub fn Ordinal(Int: type) type {...@@ -34,6 +36,19 @@ pub fn Ordinal(Int: type) type {
34 pub fn oneBased(ord: @This()) Int {36 pub fn oneBased(ord: @This()) Int {
35 return @intFromEnum(ord) + 1;37 return @intFromEnum(ord) + 1;
36 }38 }
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 }
37 };52 };
38}53}
3954