| author | |
| committer | |
| log | d024b032f7ff08f0876ca53d5e4b55b294f419c4 |
| tree | 53497b56a37cfcc47cc25b6492d99989a8adcee6 |
| parent | deeaeceb25e046541a6e42d49c4e51226dca9660 |
| signature | Commit is signed but in an unrecognized format. |
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.2 | 8 | ## 0.1.2 |
| 2 | 9 | ||
| 3 | - feat: semantic analysis can return formatted errors to the user | 10 | - 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; |
| 315 | const Type = InternPool.Type; | 315 | const Type = InternPool.Type; |
| 316 | 316 | ||
| 317 | const Teg = @import("Teg.zig"); | 317 | const Teg = @import("Teg.zig"); |
| 318 | const dod = @import("dod.zig"); | 318 | const dod = @import("array.zig"); |
| 319 | 319 | ||
| 320 | const Unused = enum(u32) { zero = 0 }; | 320 | const 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 { |
| 381 | pub const Error = error{ OutOfMemory, ParseFail }; | 381 | pub const Error = error{ OutOfMemory, ParseFail }; |
| 382 | 382 | ||
| 383 | pub const Location = struct { | 383 | pub 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 | }; |
| 390 | 391 | ||
| 391 | pub const Span = struct { | 392 | pub const Span = struct { |
| ... | @@ -672,3 +673,6 @@ const TokenList = Tokenizer.Token.List; | ... | @@ -672,3 +673,6 @@ const TokenList = Tokenizer.Token.List; |
| 672 | const ByteOffset = Tokenizer.ByteOffset; | 673 | const ByteOffset = Tokenizer.ByteOffset; |
| 673 | 674 | ||
| 674 | const Report = @import("Report.zig"); | 675 | const Report = @import("Report.zig"); |
| 676 | |||
| 677 | const util = @import("util.zig"); | ||
| 678 | const 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, | ||
| 174 | 175 | ||
| 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"); |
| 1170 | const Teg = @import("Teg.zig"); | 1171 | const Teg = @import("Teg.zig"); |
| 1171 | const Air = @import("Air.zig"); | 1172 | const Air = @import("Air.zig"); |
| 1172 | 1173 | ||
| 1173 | const dod = @import("dod.zig"); | 1174 | const dod = @import("array.zig"); |
| 1174 | const Float = dod.Float; | 1175 | const Float = dod.Float; |
| 1175 | 1176 | ||
| 1176 | const tracer = @import("tracer"); | 1177 | const 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; |
| 696 | 696 | ||
| 697 | const Air = @import("Air.zig"); | 697 | const Air = @import("Air.zig"); |
| 698 | 698 | ||
| 699 | const dod = @import("dod.zig"); | 699 | const dod = @import("array.zig"); |
| 700 | const tracer = @import("tracer"); | 700 | const 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; |
| 252 | const Comp = @import("Comp.zig"); | 252 | const Comp = @import("Comp.zig"); |
| 253 | const Decl = Comp.Decl; | 253 | const Decl = Comp.Decl; |
| 254 | 254 | ||
| 255 | const dod = @import("dod.zig"); | 255 | const dod = @import("array.zig"); |
| 256 | 256 | ||
| 257 | const Unused = @import("Ast.zig").Unused; | 257 | const 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 | }; |
| 173 | 215 | ||
| 174 | pub const Warn = union(enum) { | 216 | pub 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, | ||
| 181 | 222 | ||
| 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 | }); |
| 194 | 235 | ||
| 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, |
| 206 | 255 | ||
| ... | @@ -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, |
| 213 | 276 | ||
| ... | @@ -219,9 +282,8 @@ pub const Warn = union(enum) { | ... | @@ -219,9 +282,8 @@ pub const Warn = union(enum) { |
| 219 | node: LazyNode, | 282 | node: LazyNode, |
| 220 | 283 | ||
| 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( |
| 346 | 408 | ||
| 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; | ||
| 350 | 411 | ||
| 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"); |
| 394 | 455 | ||
| 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; |
| 402 | 457 | ||
| 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 | }; |
| 466 | 467 | ||
| 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()}); |
| 471 | 472 | ||
| 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 | }; |
| 595 | 596 | ||
| 597 | pub const SliceLoc = struct { | ||
| 598 | loc: Location, | ||
| 599 | byte: ByteOffset, | ||
| 600 | slice: []const u8, | ||
| 601 | }; | ||
| 602 | |||
| 603 | const Component = struct { | ||
| 604 | message_utf8: []const u8, | ||
| 605 | location: ?SliceLoc, | ||
| 606 | }; | ||
| 607 | |||
| 596 | pub const Highlight = union(enum) { | 608 | pub const Highlight = union(enum) { |
| 597 | // No source view | 609 | // 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 | }; | ||
| 612 | 623 | ||
| 613 | const 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 | }; |
| 617 | 682 | ||
| 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 | } |
| 642 | 707 | ||
| ... | @@ -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 | } |
| 724 | 804 |
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 instruction | 500 | assert(s.code.instTag(data.memory) == .memory); // corrupt store instruction |
| 501 | 501 | ||
| 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); |
| 504 | 505 | 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; |
| 985 | const Air = @import("Air.zig"); | 990 | const Air = @import("Air.zig"); |
| 986 | const Report = @import("Report.zig"); | 991 | const Report = @import("Report.zig"); |
| 987 | const Ast = @import("Ast.zig"); | 992 | const Ast = @import("Ast.zig"); |
| 988 | const dod = @import("dod.zig"); | 993 | const dod = @import("array.zig"); |
| 989 | const tracer = @import("tracer"); | 994 | const tracer = @import("tracer"); |
| 990 | 995 | ||
| 991 | const InternPool = @import("InternPool.zig"); | 996 | const 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, | ||
| 227 | 228 | ||
| 228 | /// This Ref does not correspond to any instruction or constant | 229 | /// 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; |
| 472 | const optionalNodeIndex = OptionalNodeIndex.init; | 473 | const optionalNodeIndex = OptionalNodeIndex.init; |
| 473 | const optionalExtraIndex = OptionalExtraIndex.init; | 474 | const optionalExtraIndex = OptionalExtraIndex.init; |
| 474 | 475 | ||
| 475 | const dod = @import("dod.zig"); | 476 | const dod = @import("array.zig"); |
| 476 | const Float = dod.Float; | 477 | const Float = dod.Float; |
| 477 | 478 | ||
| 478 | const InternPool = @import("InternPool.zig"); | 479 | const 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 | }; |
| 957 | 957 | ||
| 958 | // Scan to by line until we go past the token start | 958 | // 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 past | 961 | 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 | } |
| 966 | 966 | ||
| 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 | } |
| 984 | 984 |
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, | ||
| 84 | 86 | ||
| 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 | }; | ||
| 92 | 93 | ||
| 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 | } |
| 111 | 126 | ||
| 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 | }); |
| 217 | 232 | ||
| 218 | fn transformModule(t: *Transform, module_node: NodeIndex) Allocator.Error!Inst.Index { | 233 | fn 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 | }); |
| 305 | 320 | ||
| 306 | try t.scope().addUnusedWarnings(t); | 321 | try t.scope().addWarnings(t); |
| 307 | 322 | ||
| 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 | }); |
| 402 | 417 | ||
| 403 | try t.scope().addUnusedWarnings(t); | 418 | try t.scope().addWarnings(t); |
| 404 | 419 | ||
| 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 param | 633 | .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 |
| 650 | 664 | ||
| 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; |
| 714 | 735 | ||
| 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 | } |
| 1044 | 1065 | ||
| 1066 | fn 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 | |||
| 1045 | fn transformBlockStatement( | 1135 | fn 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 }); |
| 1065 | 1155 | ||
| 1066 | const ref = try t.transformExpression(decl, var_decl.init) orelse | 1156 | 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 | }; | ||
| 1068 | 1180 | ||
| 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( |
| 1115 | 1227 | ||
| 1116 | // if re-declaring, apply unused warnings now | 1228 | // 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 | } |
| 1135 | 1232 | ||
| 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 | }; |
| 1143 | 1240 | ||
| ... | @@ -1352,7 +1449,7 @@ const IndexList = Teg.IndexList; | ... | @@ -1352,7 +1449,7 @@ const IndexList = Teg.IndexList; |
| 1352 | const EncodedData = Teg.EncodedData; | 1449 | const EncodedData = Teg.EncodedData; |
| 1353 | const ExtraIndex = Teg.ExtraIndex; | 1450 | const ExtraIndex = Teg.ExtraIndex; |
| 1354 | 1451 | ||
| 1355 | const dod = @import("dod.zig"); | 1452 | const dod = @import("array.zig"); |
| 1356 | const ExtraStore = dod.ExtraStore(.teg, .@"var"); | 1453 | const ExtraStore = dod.ExtraStore(.teg, .@"var"); |
| 1357 | const Float = dod.Float; | 1454 | const Float = dod.Float; |
| 1358 | 1455 |
src/compiler/array.zig created+390| ... | @@ -0,0 +1,390 @@ | ||
| 1 | const extra = @This(); | ||
| 2 | |||
| 3 | pub const Mutability = enum { @"const", @"var" }; | ||
| 4 | |||
| 5 | fn 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 | |||
| 42 | fn 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 | |||
| 76 | pub 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 | |||
| 111 | fn 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 | |||
| 139 | pub 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`. | ||
| 176 | pub 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 | |||
| 207 | pub 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 | |||
| 344 | pub 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 | |||
| 388 | const std = @import("std"); | ||
| 389 | const assert = std.debug.assert; | ||
| 390 | const Allocator = std.mem.Allocator; | ||
src/compiler/dod.zig deleted-390| ... | @@ -1,390 +0,0 @@ | ||
| 1 | const extra = @This(); | ||
| 2 | |||
| 3 | pub const Mutability = enum { @"const", @"var" }; | ||
| 4 | |||
| 5 | fn 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 | |||
| 42 | fn 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 | |||
| 76 | pub 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 | |||
| 111 | fn 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 | |||
| 139 | pub 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`. | ||
| 176 | pub 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 | |||
| 207 | pub 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 | |||
| 344 | pub 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 | |||
| 388 | const std = @import("std"); | ||
| 389 | const assert = std.debug.assert; | ||
| 390 | const 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 { |
| 19 | pub fn Ordinal(Int: type) type { | 19 | pub 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 | } |
| 39 | 54 |