| author | |
| committer | |
| log | aac6e8c4182b5d70de27b1f325151ac4233ed912 |
| tree | c745097fb669f9b7f8c21f0bdc264a7d20656d7a |
| parent | b4d383a47876b794b3f0950151c7cdcd5eddfc7a |
* AST: flatten ControlFlowExpression into Continue, Break, and Return.
* AST: unify identifiers and literals into the same AST type: OneToken
* AST: ControlFlowExpression uses TrailerFlags to optimize storage
space.
* astgen: support `var` as well as `const` locals, and support
explicitly typed locals. Corresponding Module and codegen code is not
implemented yet.
* astgen: support result locations.
* ZIR: add the following instructions (see the corresponding doc
comments for explanations of semantics):
- alloc
- alloc_inferred
- bitcast_result_ptr
- coerce_result_block_ptr
- coerce_result_ptr
- coerce_to_ptr_elem
- ensure_result_used
- ensure_result_non_error
- ret_ptr
- ret_type
- store
- param_type
* the skeleton structure for result locations is set up. It's looking
pretty clean so far.
* add compile error for unused result and compile error for discarding
errors.
* astgen: split builtin calls up to implemented manually, and implement
`@as`, `@bitCast` (and others) with respect to result locations.
* add CLI support for hex and raw object formats. They are not
supported by the self-hosted compiler yet, and emit errors.
* rename `--c` CLI to `-ofmt=[objectformat]` which can be any of the
object formats. Only ELF and C are supported so far. Also added missing
help to the help text.
* Remove hard tabs from C backend test cases. Shame on you Noam, you
are grounded, you should know better, etc. Bad boy.
* Delete C backend code and test case that relied on comptime_int
incorrectly making it all the way to codegen.12 files changed, 1177 insertions(+), 765 deletions(-)
lib/std/target.zig+2| ... | ... | @@ -436,6 +436,8 @@ pub const Target = struct { |
| 436 | 436 | macho, |
| 437 | 437 | wasm, |
| 438 | 438 | c, |
| 439 | hex, | |
| 440 | raw, | |
| 439 | 441 | }; |
| 440 | 442 | |
| 441 | 443 | pub const SubSystem = enum { |
lib/std/zig/ast.zig+85-217| ... | ... | @@ -495,8 +495,10 @@ pub const Node = struct { |
| 495 | 495 | While, |
| 496 | 496 | For, |
| 497 | 497 | If, |
| 498 | ControlFlowExpression, | |
| 499 | 498 | Suspend, |
| 499 | Continue, | |
| 500 | Break, | |
| 501 | Return, | |
| 500 | 502 | |
| 501 | 503 | // Type expressions |
| 502 | 504 | AnyType, |
| ... | ... | @@ -601,6 +603,24 @@ pub const Node = struct { |
| 601 | 603 | .Try, |
| 602 | 604 | => SimplePrefixOp, |
| 603 | 605 | |
| 606 | .Identifier, | |
| 607 | .BoolLiteral, | |
| 608 | .NullLiteral, | |
| 609 | .UndefinedLiteral, | |
| 610 | .Unreachable, | |
| 611 | .AnyType, | |
| 612 | .ErrorType, | |
| 613 | .IntegerLiteral, | |
| 614 | .FloatLiteral, | |
| 615 | .StringLiteral, | |
| 616 | .CharLiteral, | |
| 617 | => OneToken, | |
| 618 | ||
| 619 | .Continue, | |
| 620 | .Break, | |
| 621 | .Return, | |
| 622 | => ControlFlowExpression, | |
| 623 | ||
| 604 | 624 | .ArrayType => ArrayType, |
| 605 | 625 | .ArrayTypeSentinel => ArrayTypeSentinel, |
| 606 | 626 | |
| ... | ... | @@ -621,23 +641,11 @@ pub const Node = struct { |
| 621 | 641 | .While => While, |
| 622 | 642 | .For => For, |
| 623 | 643 | .If => If, |
| 624 | .ControlFlowExpression => ControlFlowExpression, | |
| 625 | 644 | .Suspend => Suspend, |
| 626 | .AnyType => AnyType, | |
| 627 | .ErrorType => ErrorType, | |
| 628 | 645 | .FnProto => FnProto, |
| 629 | 646 | .AnyFrameType => AnyFrameType, |
| 630 | .IntegerLiteral => IntegerLiteral, | |
| 631 | .FloatLiteral => FloatLiteral, | |
| 632 | 647 | .EnumLiteral => EnumLiteral, |
| 633 | .StringLiteral => StringLiteral, | |
| 634 | 648 | .MultilineStringLiteral => MultilineStringLiteral, |
| 635 | .CharLiteral => CharLiteral, | |
| 636 | .BoolLiteral => BoolLiteral, | |
| 637 | .NullLiteral => NullLiteral, | |
| 638 | .UndefinedLiteral => UndefinedLiteral, | |
| 639 | .Unreachable => Unreachable, | |
| 640 | .Identifier => Identifier, | |
| 641 | 649 | .GroupedExpression => GroupedExpression, |
| 642 | 650 | .BuiltinCall => BuiltinCall, |
| 643 | 651 | .ErrorSetDecl => ErrorSetDecl, |
| ... | ... | @@ -1182,19 +1190,19 @@ pub const Node = struct { |
| 1182 | 1190 | } |
| 1183 | 1191 | }; |
| 1184 | 1192 | |
| 1185 | pub const Identifier = struct { | |
| 1186 | base: Node = Node{ .tag = .Identifier }, | |
| 1193 | pub const OneToken = struct { | |
| 1194 | base: Node, | |
| 1187 | 1195 | token: TokenIndex, |
| 1188 | 1196 | |
| 1189 | pub fn iterate(self: *const Identifier, index: usize) ?*Node { | |
| 1197 | pub fn iterate(self: *const OneToken, index: usize) ?*Node { | |
| 1190 | 1198 | return null; |
| 1191 | 1199 | } |
| 1192 | 1200 | |
| 1193 | pub fn firstToken(self: *const Identifier) TokenIndex { | |
| 1201 | pub fn firstToken(self: *const OneToken) TokenIndex { | |
| 1194 | 1202 | return self.token; |
| 1195 | 1203 | } |
| 1196 | 1204 | |
| 1197 | pub fn lastToken(self: *const Identifier) TokenIndex { | |
| 1205 | pub fn lastToken(self: *const OneToken) TokenIndex { | |
| 1198 | 1206 | return self.token; |
| 1199 | 1207 | } |
| 1200 | 1208 | }; |
| ... | ... | @@ -2569,34 +2577,65 @@ pub const Node = struct { |
| 2569 | 2577 | } |
| 2570 | 2578 | }; |
| 2571 | 2579 | |
| 2572 | /// TODO break this into separate Break, Continue, Return AST Nodes to save memory. | |
| 2573 | /// Could be further broken into LabeledBreak, LabeledContinue, and ReturnVoid to save even more. | |
| 2580 | /// Trailed in memory by possibly many things, with each optional thing | |
| 2581 | /// determined by a bit in `trailer_flags`. | |
| 2582 | /// Can be: return, break, continue | |
| 2574 | 2583 | pub const ControlFlowExpression = struct { |
| 2575 | base: Node = Node{ .tag = .ControlFlowExpression }, | |
| 2584 | base: Node, | |
| 2585 | trailer_flags: TrailerFlags, | |
| 2576 | 2586 | ltoken: TokenIndex, |
| 2577 | kind: Kind, | |
| 2578 | rhs: ?*Node, | |
| 2579 | 2587 | |
| 2580 | pub const Kind = union(enum) { | |
| 2581 | Break: ?*Node, | |
| 2582 | Continue: ?*Node, | |
| 2583 | Return, | |
| 2588 | pub const TrailerFlags = std.meta.TrailerFlags(struct { | |
| 2589 | rhs: *Node, | |
| 2590 | label: TokenIndex, | |
| 2591 | }); | |
| 2592 | ||
| 2593 | pub const RequiredFields = struct { | |
| 2594 | tag: Tag, | |
| 2595 | ltoken: TokenIndex, | |
| 2584 | 2596 | }; |
| 2585 | 2597 | |
| 2598 | pub fn getRHS(self: *const ControlFlowExpression) ?*Node { | |
| 2599 | return self.getTrailer("rhs"); | |
| 2600 | } | |
| 2601 | ||
| 2602 | pub fn getLabel(self: *const ControlFlowExpression) ?TokenIndex { | |
| 2603 | return self.getTrailer("label"); | |
| 2604 | } | |
| 2605 | ||
| 2606 | pub fn getTrailer(self: *const ControlFlowExpression, comptime name: []const u8) ?TrailerFlags.Field(name) { | |
| 2607 | const trailers_start = @ptrCast([*]const u8, self) + @sizeOf(ControlFlowExpression); | |
| 2608 | return self.trailer_flags.get(trailers_start, name); | |
| 2609 | } | |
| 2610 | ||
| 2611 | pub fn setTrailer(self: *ControlFlowExpression, comptime name: []const u8, value: TrailerFlags.Field(name)) void { | |
| 2612 | const trailers_start = @ptrCast([*]u8, self) + @sizeOf(ControlFlowExpression); | |
| 2613 | self.trailer_flags.set(trailers_start, name, value); | |
| 2614 | } | |
| 2615 | ||
| 2616 | pub fn create(allocator: *mem.Allocator, required: RequiredFields, trailers: anytype) !*ControlFlowExpression { | |
| 2617 | const trailer_flags = TrailerFlags.init(trailers); | |
| 2618 | const bytes = try allocator.alignedAlloc(u8, @alignOf(ControlFlowExpression), sizeInBytes(trailer_flags)); | |
| 2619 | const ctrl_flow_expr = @ptrCast(*ControlFlowExpression, bytes.ptr); | |
| 2620 | ctrl_flow_expr.* = .{ | |
| 2621 | .base = .{ .tag = required.tag }, | |
| 2622 | .trailer_flags = trailer_flags, | |
| 2623 | .ltoken = required.ltoken, | |
| 2624 | }; | |
| 2625 | const trailers_start = bytes.ptr + @sizeOf(ControlFlowExpression); | |
| 2626 | trailer_flags.setMany(trailers_start, trailers); | |
| 2627 | return ctrl_flow_expr; | |
| 2628 | } | |
| 2629 | ||
| 2630 | pub fn destroy(self: *ControlFlowExpression, allocator: *mem.Allocator) void { | |
| 2631 | const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.trailer_flags)]; | |
| 2632 | allocator.free(bytes); | |
| 2633 | } | |
| 2634 | ||
| 2586 | 2635 | pub fn iterate(self: *const ControlFlowExpression, index: usize) ?*Node { |
| 2587 | 2636 | var i = index; |
| 2588 | 2637 | |
| 2589 | switch (self.kind) { | |
| 2590 | .Break, .Continue => |maybe_label| { | |
| 2591 | if (maybe_label) |label| { | |
| 2592 | if (i < 1) return label; | |
| 2593 | i -= 1; | |
| 2594 | } | |
| 2595 | }, | |
| 2596 | .Return => {}, | |
| 2597 | } | |
| 2598 | ||
| 2599 | if (self.rhs) |rhs| { | |
| 2638 | if (self.getRHS()) |rhs| { | |
| 2600 | 2639 | if (i < 1) return rhs; |
| 2601 | 2640 | i -= 1; |
| 2602 | 2641 | } |
| ... | ... | @@ -2609,21 +2648,20 @@ pub const Node = struct { |
| 2609 | 2648 | } |
| 2610 | 2649 | |
| 2611 | 2650 | pub fn lastToken(self: *const ControlFlowExpression) TokenIndex { |
| 2612 | if (self.rhs) |rhs| { | |
| 2651 | if (self.getRHS()) |rhs| { | |
| 2613 | 2652 | return rhs.lastToken(); |
| 2614 | 2653 | } |
| 2615 | 2654 | |
| 2616 | switch (self.kind) { | |
| 2617 | .Break, .Continue => |maybe_label| { | |
| 2618 | if (maybe_label) |label| { | |
| 2619 | return label.lastToken(); | |
| 2620 | } | |
| 2621 | }, | |
| 2622 | .Return => return self.ltoken, | |
| 2655 | if (self.getLabel()) |label| { | |
| 2656 | return label; | |
| 2623 | 2657 | } |
| 2624 | 2658 | |
| 2625 | 2659 | return self.ltoken; |
| 2626 | 2660 | } |
| 2661 | ||
| 2662 | fn sizeInBytes(trailer_flags: TrailerFlags) usize { | |
| 2663 | return @sizeOf(ControlFlowExpression) + trailer_flags.sizeInBytes(); | |
| 2664 | } | |
| 2627 | 2665 | }; |
| 2628 | 2666 | |
| 2629 | 2667 | pub const Suspend = struct { |
| ... | ... | @@ -2655,23 +2693,6 @@ pub const Node = struct { |
| 2655 | 2693 | } |
| 2656 | 2694 | }; |
| 2657 | 2695 | |
| 2658 | pub const IntegerLiteral = struct { | |
| 2659 | base: Node = Node{ .tag = .IntegerLiteral }, | |
| 2660 | token: TokenIndex, | |
| 2661 | ||
| 2662 | pub fn iterate(self: *const IntegerLiteral, index: usize) ?*Node { | |
| 2663 | return null; | |
| 2664 | } | |
| 2665 | ||
| 2666 | pub fn firstToken(self: *const IntegerLiteral) TokenIndex { | |
| 2667 | return self.token; | |
| 2668 | } | |
| 2669 | ||
| 2670 | pub fn lastToken(self: *const IntegerLiteral) TokenIndex { | |
| 2671 | return self.token; | |
| 2672 | } | |
| 2673 | }; | |
| 2674 | ||
| 2675 | 2696 | pub const EnumLiteral = struct { |
| 2676 | 2697 | base: Node = Node{ .tag = .EnumLiteral }, |
| 2677 | 2698 | dot: TokenIndex, |
| ... | ... | @@ -2690,23 +2711,6 @@ pub const Node = struct { |
| 2690 | 2711 | } |
| 2691 | 2712 | }; |
| 2692 | 2713 | |
| 2693 | pub const FloatLiteral = struct { | |
| 2694 | base: Node = Node{ .tag = .FloatLiteral }, | |
| 2695 | token: TokenIndex, | |
| 2696 | ||
| 2697 | pub fn iterate(self: *const FloatLiteral, index: usize) ?*Node { | |
| 2698 | return null; | |
| 2699 | } | |
| 2700 | ||
| 2701 | pub fn firstToken(self: *const FloatLiteral) TokenIndex { | |
| 2702 | return self.token; | |
| 2703 | } | |
| 2704 | ||
| 2705 | pub fn lastToken(self: *const FloatLiteral) TokenIndex { | |
| 2706 | return self.token; | |
| 2707 | } | |
| 2708 | }; | |
| 2709 | ||
| 2710 | 2714 | /// Parameters are in memory following BuiltinCall. |
| 2711 | 2715 | pub const BuiltinCall = struct { |
| 2712 | 2716 | base: Node = Node{ .tag = .BuiltinCall }, |
| ... | ... | @@ -2757,23 +2761,6 @@ pub const Node = struct { |
| 2757 | 2761 | } |
| 2758 | 2762 | }; |
| 2759 | 2763 | |
| 2760 | pub const StringLiteral = struct { | |
| 2761 | base: Node = Node{ .tag = .StringLiteral }, | |
| 2762 | token: TokenIndex, | |
| 2763 | ||
| 2764 | pub fn iterate(self: *const StringLiteral, index: usize) ?*Node { | |
| 2765 | return null; | |
| 2766 | } | |
| 2767 | ||
| 2768 | pub fn firstToken(self: *const StringLiteral) TokenIndex { | |
| 2769 | return self.token; | |
| 2770 | } | |
| 2771 | ||
| 2772 | pub fn lastToken(self: *const StringLiteral) TokenIndex { | |
| 2773 | return self.token; | |
| 2774 | } | |
| 2775 | }; | |
| 2776 | ||
| 2777 | 2764 | /// The string literal tokens appear directly in memory after MultilineStringLiteral. |
| 2778 | 2765 | pub const MultilineStringLiteral = struct { |
| 2779 | 2766 | base: Node = Node{ .tag = .MultilineStringLiteral }, |
| ... | ... | @@ -2817,74 +2804,6 @@ pub const Node = struct { |
| 2817 | 2804 | } |
| 2818 | 2805 | }; |
| 2819 | 2806 | |
| 2820 | pub const CharLiteral = struct { | |
| 2821 | base: Node = Node{ .tag = .CharLiteral }, | |
| 2822 | token: TokenIndex, | |
| 2823 | ||
| 2824 | pub fn iterate(self: *const CharLiteral, index: usize) ?*Node { | |
| 2825 | return null; | |
| 2826 | } | |
| 2827 | ||
| 2828 | pub fn firstToken(self: *const CharLiteral) TokenIndex { | |
| 2829 | return self.token; | |
| 2830 | } | |
| 2831 | ||
| 2832 | pub fn lastToken(self: *const CharLiteral) TokenIndex { | |
| 2833 | return self.token; | |
| 2834 | } | |
| 2835 | }; | |
| 2836 | ||
| 2837 | pub const BoolLiteral = struct { | |
| 2838 | base: Node = Node{ .tag = .BoolLiteral }, | |
| 2839 | token: TokenIndex, | |
| 2840 | ||
| 2841 | pub fn iterate(self: *const BoolLiteral, index: usize) ?*Node { | |
| 2842 | return null; | |
| 2843 | } | |
| 2844 | ||
| 2845 | pub fn firstToken(self: *const BoolLiteral) TokenIndex { | |
| 2846 | return self.token; | |
| 2847 | } | |
| 2848 | ||
| 2849 | pub fn lastToken(self: *const BoolLiteral) TokenIndex { | |
| 2850 | return self.token; | |
| 2851 | } | |
| 2852 | }; | |
| 2853 | ||
| 2854 | pub const NullLiteral = struct { | |
| 2855 | base: Node = Node{ .tag = .NullLiteral }, | |
| 2856 | token: TokenIndex, | |
| 2857 | ||
| 2858 | pub fn iterate(self: *const NullLiteral, index: usize) ?*Node { | |
| 2859 | return null; | |
| 2860 | } | |
| 2861 | ||
| 2862 | pub fn firstToken(self: *const NullLiteral) TokenIndex { | |
| 2863 | return self.token; | |
| 2864 | } | |
| 2865 | ||
| 2866 | pub fn lastToken(self: *const NullLiteral) TokenIndex { | |
| 2867 | return self.token; | |
| 2868 | } | |
| 2869 | }; | |
| 2870 | ||
| 2871 | pub const UndefinedLiteral = struct { | |
| 2872 | base: Node = Node{ .tag = .UndefinedLiteral }, | |
| 2873 | token: TokenIndex, | |
| 2874 | ||
| 2875 | pub fn iterate(self: *const UndefinedLiteral, index: usize) ?*Node { | |
| 2876 | return null; | |
| 2877 | } | |
| 2878 | ||
| 2879 | pub fn firstToken(self: *const UndefinedLiteral) TokenIndex { | |
| 2880 | return self.token; | |
| 2881 | } | |
| 2882 | ||
| 2883 | pub fn lastToken(self: *const UndefinedLiteral) TokenIndex { | |
| 2884 | return self.token; | |
| 2885 | } | |
| 2886 | }; | |
| 2887 | ||
| 2888 | 2807 | pub const Asm = struct { |
| 2889 | 2808 | base: Node = Node{ .tag = .Asm }, |
| 2890 | 2809 | asm_token: TokenIndex, |
| ... | ... | @@ -2904,7 +2823,7 @@ pub const Node = struct { |
| 2904 | 2823 | rparen: TokenIndex, |
| 2905 | 2824 | |
| 2906 | 2825 | pub const Kind = union(enum) { |
| 2907 | Variable: *Identifier, | |
| 2826 | Variable: *OneToken, | |
| 2908 | 2827 | Return: *Node, |
| 2909 | 2828 | }; |
| 2910 | 2829 | |
| ... | ... | @@ -3005,57 +2924,6 @@ pub const Node = struct { |
| 3005 | 2924 | } |
| 3006 | 2925 | }; |
| 3007 | 2926 | |
| 3008 | pub const Unreachable = struct { | |
| 3009 | base: Node = Node{ .tag = .Unreachable }, | |
| 3010 | token: TokenIndex, | |
| 3011 | ||
| 3012 | pub fn iterate(self: *const Unreachable, index: usize) ?*Node { | |
| 3013 | return null; | |
| 3014 | } | |
| 3015 | ||
| 3016 | pub fn firstToken(self: *const Unreachable) TokenIndex { | |
| 3017 | return self.token; | |
| 3018 | } | |
| 3019 | ||
| 3020 | pub fn lastToken(self: *const Unreachable) TokenIndex { | |
| 3021 | return self.token; | |
| 3022 | } | |
| 3023 | }; | |
| 3024 | ||
| 3025 | pub const ErrorType = struct { | |
| 3026 | base: Node = Node{ .tag = .ErrorType }, | |
| 3027 | token: TokenIndex, | |
| 3028 | ||
| 3029 | pub fn iterate(self: *const ErrorType, index: usize) ?*Node { | |
| 3030 | return null; | |
| 3031 | } | |
| 3032 | ||
| 3033 | pub fn firstToken(self: *const ErrorType) TokenIndex { | |
| 3034 | return self.token; | |
| 3035 | } | |
| 3036 | ||
| 3037 | pub fn lastToken(self: *const ErrorType) TokenIndex { | |
| 3038 | return self.token; | |
| 3039 | } | |
| 3040 | }; | |
| 3041 | ||
| 3042 | pub const AnyType = struct { | |
| 3043 | base: Node = Node{ .tag = .AnyType }, | |
| 3044 | token: TokenIndex, | |
| 3045 | ||
| 3046 | pub fn iterate(self: *const AnyType, index: usize) ?*Node { | |
| 3047 | return null; | |
| 3048 | } | |
| 3049 | ||
| 3050 | pub fn firstToken(self: *const AnyType) TokenIndex { | |
| 3051 | return self.token; | |
| 3052 | } | |
| 3053 | ||
| 3054 | pub fn lastToken(self: *const AnyType) TokenIndex { | |
| 3055 | return self.token; | |
| 3056 | } | |
| 3057 | }; | |
| 3058 | ||
| 3059 | 2927 | /// TODO remove from the Node base struct |
| 3060 | 2928 | /// TODO actually maybe remove entirely in favor of iterating backward from Node.firstToken() |
| 3061 | 2929 | /// and forwards to find same-line doc comments. |
lib/std/zig/parse.zig+47-35| ... | ... | @@ -628,8 +628,11 @@ const Parser = struct { |
| 628 | 628 | var type_expr: ?*Node = null; |
| 629 | 629 | if (p.eatToken(.Colon)) |_| { |
| 630 | 630 | if (p.eatToken(.Keyword_anytype) orelse p.eatToken(.Keyword_var)) |anytype_tok| { |
| 631 | const node = try p.arena.allocator.create(Node.AnyType); | |
| 632 | node.* = .{ .token = anytype_tok }; | |
| 631 | const node = try p.arena.allocator.create(Node.OneToken); | |
| 632 | node.* = .{ | |
| 633 | .base = .{ .tag = .AnyType }, | |
| 634 | .token = anytype_tok, | |
| 635 | }; | |
| 633 | 636 | type_expr = &node.base; |
| 634 | 637 | } else { |
| 635 | 638 | type_expr = try p.expectNode(parseTypeExpr, .{ |
| ... | ... | @@ -1079,12 +1082,13 @@ const Parser = struct { |
| 1079 | 1082 | if (p.eatToken(.Keyword_break)) |token| { |
| 1080 | 1083 | const label = try p.parseBreakLabel(); |
| 1081 | 1084 | const expr_node = try p.parseExpr(); |
| 1082 | const node = try p.arena.allocator.create(Node.ControlFlowExpression); | |
| 1083 | node.* = .{ | |
| 1085 | const node = try Node.ControlFlowExpression.create(&p.arena.allocator, .{ | |
| 1086 | .tag = .Break, | |
| 1084 | 1087 | .ltoken = token, |
| 1085 | .kind = .{ .Break = label }, | |
| 1088 | }, .{ | |
| 1089 | .label = label, | |
| 1086 | 1090 | .rhs = expr_node, |
| 1087 | }; | |
| 1091 | }); | |
| 1088 | 1092 | return &node.base; |
| 1089 | 1093 | } |
| 1090 | 1094 | |
| ... | ... | @@ -1115,12 +1119,13 @@ const Parser = struct { |
| 1115 | 1119 | |
| 1116 | 1120 | if (p.eatToken(.Keyword_continue)) |token| { |
| 1117 | 1121 | const label = try p.parseBreakLabel(); |
| 1118 | const node = try p.arena.allocator.create(Node.ControlFlowExpression); | |
| 1119 | node.* = .{ | |
| 1122 | const node = try Node.ControlFlowExpression.create(&p.arena.allocator, .{ | |
| 1123 | .tag = .Continue, | |
| 1120 | 1124 | .ltoken = token, |
| 1121 | .kind = .{ .Continue = label }, | |
| 1125 | }, .{ | |
| 1126 | .label = label, | |
| 1122 | 1127 | .rhs = null, |
| 1123 | }; | |
| 1128 | }); | |
| 1124 | 1129 | return &node.base; |
| 1125 | 1130 | } |
| 1126 | 1131 | |
| ... | ... | @@ -1139,12 +1144,12 @@ const Parser = struct { |
| 1139 | 1144 | |
| 1140 | 1145 | if (p.eatToken(.Keyword_return)) |token| { |
| 1141 | 1146 | const expr_node = try p.parseExpr(); |
| 1142 | const node = try p.arena.allocator.create(Node.ControlFlowExpression); | |
| 1143 | node.* = .{ | |
| 1147 | const node = try Node.ControlFlowExpression.create(&p.arena.allocator, .{ | |
| 1148 | .tag = .Return, | |
| 1144 | 1149 | .ltoken = token, |
| 1145 | .kind = .Return, | |
| 1150 | }, .{ | |
| 1146 | 1151 | .rhs = expr_node, |
| 1147 | }; | |
| 1152 | }); | |
| 1148 | 1153 | return &node.base; |
| 1149 | 1154 | } |
| 1150 | 1155 | |
| ... | ... | @@ -1516,8 +1521,9 @@ const Parser = struct { |
| 1516 | 1521 | fn parsePrimaryTypeExpr(p: *Parser) !?*Node { |
| 1517 | 1522 | if (try p.parseBuiltinCall()) |node| return node; |
| 1518 | 1523 | if (p.eatToken(.CharLiteral)) |token| { |
| 1519 | const node = try p.arena.allocator.create(Node.CharLiteral); | |
| 1524 | const node = try p.arena.allocator.create(Node.OneToken); | |
| 1520 | 1525 | node.* = .{ |
| 1526 | .base = .{ .tag = .CharLiteral }, | |
| 1521 | 1527 | .token = token, |
| 1522 | 1528 | }; |
| 1523 | 1529 | return &node.base; |
| ... | ... | @@ -1547,7 +1553,7 @@ const Parser = struct { |
| 1547 | 1553 | const identifier = try p.expectNodeRecoverable(parseIdentifier, .{ |
| 1548 | 1554 | .ExpectedIdentifier = .{ .token = p.tok_i }, |
| 1549 | 1555 | }); |
| 1550 | const global_error_set = try p.createLiteral(Node.ErrorType, token); | |
| 1556 | const global_error_set = try p.createLiteral(.ErrorType, token); | |
| 1551 | 1557 | if (period == null or identifier == null) return global_error_set; |
| 1552 | 1558 | |
| 1553 | 1559 | const node = try p.arena.allocator.create(Node.SimpleInfixOp); |
| ... | ... | @@ -1559,8 +1565,8 @@ const Parser = struct { |
| 1559 | 1565 | }; |
| 1560 | 1566 | return &node.base; |
| 1561 | 1567 | } |
| 1562 | if (p.eatToken(.Keyword_false)) |token| return p.createLiteral(Node.BoolLiteral, token); | |
| 1563 | if (p.eatToken(.Keyword_null)) |token| return p.createLiteral(Node.NullLiteral, token); | |
| 1568 | if (p.eatToken(.Keyword_false)) |token| return p.createLiteral(.BoolLiteral, token); | |
| 1569 | if (p.eatToken(.Keyword_null)) |token| return p.createLiteral(.NullLiteral, token); | |
| 1564 | 1570 | if (p.eatToken(.Keyword_anyframe)) |token| { |
| 1565 | 1571 | const node = try p.arena.allocator.create(Node.AnyFrameType); |
| 1566 | 1572 | node.* = .{ |
| ... | ... | @@ -1569,9 +1575,9 @@ const Parser = struct { |
| 1569 | 1575 | }; |
| 1570 | 1576 | return &node.base; |
| 1571 | 1577 | } |
| 1572 | if (p.eatToken(.Keyword_true)) |token| return p.createLiteral(Node.BoolLiteral, token); | |
| 1573 | if (p.eatToken(.Keyword_undefined)) |token| return p.createLiteral(Node.UndefinedLiteral, token); | |
| 1574 | if (p.eatToken(.Keyword_unreachable)) |token| return p.createLiteral(Node.Unreachable, token); | |
| 1578 | if (p.eatToken(.Keyword_true)) |token| return p.createLiteral(.BoolLiteral, token); | |
| 1579 | if (p.eatToken(.Keyword_undefined)) |token| return p.createLiteral(.UndefinedLiteral, token); | |
| 1580 | if (p.eatToken(.Keyword_unreachable)) |token| return p.createLiteral(.Unreachable, token); | |
| 1575 | 1581 | if (try p.parseStringLiteral()) |node| return node; |
| 1576 | 1582 | if (try p.parseSwitchExpr()) |node| return node; |
| 1577 | 1583 | |
| ... | ... | @@ -1865,7 +1871,7 @@ const Parser = struct { |
| 1865 | 1871 | const variable = try p.expectNode(parseIdentifier, .{ |
| 1866 | 1872 | .ExpectedIdentifier = .{ .token = p.tok_i }, |
| 1867 | 1873 | }); |
| 1868 | break :blk .{ .Variable = variable.cast(Node.Identifier).? }; | |
| 1874 | break :blk .{ .Variable = variable.castTag(.Identifier).? }; | |
| 1869 | 1875 | }; |
| 1870 | 1876 | const rparen = try p.expectToken(.RParen); |
| 1871 | 1877 | |
| ... | ... | @@ -1906,11 +1912,10 @@ const Parser = struct { |
| 1906 | 1912 | } |
| 1907 | 1913 | |
| 1908 | 1914 | /// BreakLabel <- COLON IDENTIFIER |
| 1909 | fn parseBreakLabel(p: *Parser) !?*Node { | |
| 1915 | fn parseBreakLabel(p: *Parser) !?TokenIndex { | |
| 1910 | 1916 | _ = p.eatToken(.Colon) orelse return null; |
| 1911 | return try p.expectNode(parseIdentifier, .{ | |
| 1912 | .ExpectedIdentifier = .{ .token = p.tok_i }, | |
| 1913 | }); | |
| 1917 | const ident = try p.expectToken(.Identifier); | |
| 1918 | return ident; | |
| 1914 | 1919 | } |
| 1915 | 1920 | |
| 1916 | 1921 | /// BlockLabel <- IDENTIFIER COLON |
| ... | ... | @@ -3022,8 +3027,9 @@ const Parser = struct { |
| 3022 | 3027 | }); |
| 3023 | 3028 | |
| 3024 | 3029 | // lets pretend this was an identifier so we can continue parsing |
| 3025 | const node = try p.arena.allocator.create(Node.Identifier); | |
| 3030 | const node = try p.arena.allocator.create(Node.OneToken); | |
| 3026 | 3031 | node.* = .{ |
| 3032 | .base = .{ .tag = .Identifier }, | |
| 3027 | 3033 | .token = token, |
| 3028 | 3034 | }; |
| 3029 | 3035 | return &node.base; |
| ... | ... | @@ -3054,8 +3060,9 @@ const Parser = struct { |
| 3054 | 3060 | |
| 3055 | 3061 | fn parseIdentifier(p: *Parser) !?*Node { |
| 3056 | 3062 | const token = p.eatToken(.Identifier) orelse return null; |
| 3057 | const node = try p.arena.allocator.create(Node.Identifier); | |
| 3063 | const node = try p.arena.allocator.create(Node.OneToken); | |
| 3058 | 3064 | node.* = .{ |
| 3065 | .base = .{ .tag = .Identifier }, | |
| 3059 | 3066 | .token = token, |
| 3060 | 3067 | }; |
| 3061 | 3068 | return &node.base; |
| ... | ... | @@ -3064,16 +3071,18 @@ const Parser = struct { |
| 3064 | 3071 | fn parseAnyType(p: *Parser) !?*Node { |
| 3065 | 3072 | const token = p.eatToken(.Keyword_anytype) orelse |
| 3066 | 3073 | p.eatToken(.Keyword_var) orelse return null; // TODO remove in next release cycle |
| 3067 | const node = try p.arena.allocator.create(Node.AnyType); | |
| 3074 | const node = try p.arena.allocator.create(Node.OneToken); | |
| 3068 | 3075 | node.* = .{ |
| 3076 | .base = .{ .tag = .AnyType }, | |
| 3069 | 3077 | .token = token, |
| 3070 | 3078 | }; |
| 3071 | 3079 | return &node.base; |
| 3072 | 3080 | } |
| 3073 | 3081 | |
| 3074 | fn createLiteral(p: *Parser, comptime T: type, token: TokenIndex) !*Node { | |
| 3075 | const result = try p.arena.allocator.create(T); | |
| 3076 | result.* = T{ | |
| 3082 | fn createLiteral(p: *Parser, tag: ast.Node.Tag, token: TokenIndex) !*Node { | |
| 3083 | const result = try p.arena.allocator.create(Node.OneToken); | |
| 3084 | result.* = .{ | |
| 3085 | .base = .{ .tag = tag }, | |
| 3077 | 3086 | .token = token, |
| 3078 | 3087 | }; |
| 3079 | 3088 | return &result.base; |
| ... | ... | @@ -3081,8 +3090,9 @@ const Parser = struct { |
| 3081 | 3090 | |
| 3082 | 3091 | fn parseStringLiteralSingle(p: *Parser) !?*Node { |
| 3083 | 3092 | if (p.eatToken(.StringLiteral)) |token| { |
| 3084 | const node = try p.arena.allocator.create(Node.StringLiteral); | |
| 3093 | const node = try p.arena.allocator.create(Node.OneToken); | |
| 3085 | 3094 | node.* = .{ |
| 3095 | .base = .{ .tag = .StringLiteral }, | |
| 3086 | 3096 | .token = token, |
| 3087 | 3097 | }; |
| 3088 | 3098 | return &node.base; |
| ... | ... | @@ -3131,8 +3141,9 @@ const Parser = struct { |
| 3131 | 3141 | |
| 3132 | 3142 | fn parseIntegerLiteral(p: *Parser) !?*Node { |
| 3133 | 3143 | const token = p.eatToken(.IntegerLiteral) orelse return null; |
| 3134 | const node = try p.arena.allocator.create(Node.IntegerLiteral); | |
| 3144 | const node = try p.arena.allocator.create(Node.OneToken); | |
| 3135 | 3145 | node.* = .{ |
| 3146 | .base = .{ .tag = .IntegerLiteral }, | |
| 3136 | 3147 | .token = token, |
| 3137 | 3148 | }; |
| 3138 | 3149 | return &node.base; |
| ... | ... | @@ -3140,8 +3151,9 @@ const Parser = struct { |
| 3140 | 3151 | |
| 3141 | 3152 | fn parseFloatLiteral(p: *Parser) !?*Node { |
| 3142 | 3153 | const token = p.eatToken(.FloatLiteral) orelse return null; |
| 3143 | const node = try p.arena.allocator.create(Node.FloatLiteral); | |
| 3154 | const node = try p.arena.allocator.create(Node.OneToken); | |
| 3144 | 3155 | node.* = .{ |
| 3156 | .base = .{ .tag = .FloatLiteral }, | |
| 3145 | 3157 | .token = token, |
| 3146 | 3158 | }; |
| 3147 | 3159 | return &node.base; |
lib/std/zig/render.zig+62-87| ... | ... | @@ -366,10 +366,32 @@ fn renderExpression( |
| 366 | 366 | space: Space, |
| 367 | 367 | ) (@TypeOf(stream).Error || Error)!void { |
| 368 | 368 | switch (base.tag) { |
| 369 | .Identifier => { | |
| 370 | const identifier = @fieldParentPtr(ast.Node.Identifier, "base", base); | |
| 371 | return renderToken(tree, stream, identifier.token, indent, start_col, space); | |
| 369 | .Identifier, | |
| 370 | .IntegerLiteral, | |
| 371 | .FloatLiteral, | |
| 372 | .StringLiteral, | |
| 373 | .CharLiteral, | |
| 374 | .BoolLiteral, | |
| 375 | .NullLiteral, | |
| 376 | .Unreachable, | |
| 377 | .ErrorType, | |
| 378 | .UndefinedLiteral, | |
| 379 | => { | |
| 380 | const casted_node = base.cast(ast.Node.OneToken).?; | |
| 381 | return renderToken(tree, stream, casted_node.token, indent, start_col, space); | |
| 372 | 382 | }, |
| 383 | ||
| 384 | .AnyType => { | |
| 385 | const any_type = base.castTag(.AnyType).?; | |
| 386 | if (mem.eql(u8, tree.tokenSlice(any_type.token), "var")) { | |
| 387 | // TODO remove in next release cycle | |
| 388 | try stream.writeAll("anytype"); | |
| 389 | if (space == .Comma) try stream.writeAll(",\n"); | |
| 390 | return; | |
| 391 | } | |
| 392 | return renderToken(tree, stream, any_type.token, indent, start_col, space); | |
| 393 | }, | |
| 394 | ||
| 373 | 395 | .Block => { |
| 374 | 396 | const block = @fieldParentPtr(ast.Node.Block, "base", base); |
| 375 | 397 | |
| ... | ... | @@ -399,6 +421,7 @@ fn renderExpression( |
| 399 | 421 | return renderToken(tree, stream, block.rbrace, indent, start_col, space); |
| 400 | 422 | } |
| 401 | 423 | }, |
| 424 | ||
| 402 | 425 | .Defer => { |
| 403 | 426 | const defer_node = @fieldParentPtr(ast.Node.Defer, "base", base); |
| 404 | 427 | |
| ... | ... | @@ -1107,50 +1130,48 @@ fn renderExpression( |
| 1107 | 1130 | return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); // ? |
| 1108 | 1131 | }, |
| 1109 | 1132 | |
| 1110 | .ControlFlowExpression => { | |
| 1111 | const flow_expr = @fieldParentPtr(ast.Node.ControlFlowExpression, "base", base); | |
| 1133 | .Break => { | |
| 1134 | const flow_expr = base.castTag(.Break).?; | |
| 1135 | const maybe_rhs = flow_expr.getRHS(); | |
| 1136 | const maybe_label = flow_expr.getLabel(); | |
| 1112 | 1137 | |
| 1113 | switch (flow_expr.kind) { | |
| 1114 | .Break => |maybe_label| { | |
| 1115 | if (maybe_label == null and flow_expr.rhs == null) { | |
| 1116 | return renderToken(tree, stream, flow_expr.ltoken, indent, start_col, space); // break | |
| 1117 | } | |
| 1118 | ||
| 1119 | try renderToken(tree, stream, flow_expr.ltoken, indent, start_col, Space.Space); // break | |
| 1120 | if (maybe_label) |label| { | |
| 1121 | const colon = tree.nextToken(flow_expr.ltoken); | |
| 1122 | try renderToken(tree, stream, colon, indent, start_col, Space.None); // : | |
| 1123 | ||
| 1124 | if (flow_expr.rhs == null) { | |
| 1125 | return renderExpression(allocator, stream, tree, indent, start_col, label, space); // label | |
| 1126 | } | |
| 1127 | try renderExpression(allocator, stream, tree, indent, start_col, label, Space.Space); // label | |
| 1128 | } | |
| 1129 | }, | |
| 1130 | .Continue => |maybe_label| { | |
| 1131 | assert(flow_expr.rhs == null); | |
| 1138 | if (maybe_label == null and maybe_rhs == null) { | |
| 1139 | return renderToken(tree, stream, flow_expr.ltoken, indent, start_col, space); // break | |
| 1140 | } | |
| 1132 | 1141 | |
| 1133 | if (maybe_label == null and flow_expr.rhs == null) { | |
| 1134 | return renderToken(tree, stream, flow_expr.ltoken, indent, start_col, space); // continue | |
| 1135 | } | |
| 1142 | try renderToken(tree, stream, flow_expr.ltoken, indent, start_col, Space.Space); // break | |
| 1143 | if (maybe_label) |label| { | |
| 1144 | const colon = tree.nextToken(flow_expr.ltoken); | |
| 1145 | try renderToken(tree, stream, colon, indent, start_col, Space.None); // : | |
| 1136 | 1146 | |
| 1137 | try renderToken(tree, stream, flow_expr.ltoken, indent, start_col, Space.Space); // continue | |
| 1138 | if (maybe_label) |label| { | |
| 1139 | const colon = tree.nextToken(flow_expr.ltoken); | |
| 1140 | try renderToken(tree, stream, colon, indent, start_col, Space.None); // : | |
| 1147 | if (maybe_rhs == null) { | |
| 1148 | return renderToken(tree, stream, label, indent, start_col, space); // label | |
| 1149 | } | |
| 1150 | try renderToken(tree, stream, label, indent, start_col, Space.Space); // label | |
| 1151 | } | |
| 1152 | return renderExpression(allocator, stream, tree, indent, start_col, maybe_rhs.?, space); | |
| 1153 | }, | |
| 1141 | 1154 | |
| 1142 | return renderExpression(allocator, stream, tree, indent, start_col, label, space); | |
| 1143 | } | |
| 1144 | }, | |
| 1145 | .Return => { | |
| 1146 | if (flow_expr.rhs == null) { | |
| 1147 | return renderToken(tree, stream, flow_expr.ltoken, indent, start_col, space); | |
| 1148 | } | |
| 1149 | try renderToken(tree, stream, flow_expr.ltoken, indent, start_col, Space.Space); | |
| 1150 | }, | |
| 1155 | .Continue => { | |
| 1156 | const flow_expr = base.castTag(.Continue).?; | |
| 1157 | if (flow_expr.getLabel()) |label| { | |
| 1158 | try renderToken(tree, stream, flow_expr.ltoken, indent, start_col, Space.Space); // continue | |
| 1159 | const colon = tree.nextToken(flow_expr.ltoken); | |
| 1160 | try renderToken(tree, stream, colon, indent, start_col, Space.None); // : | |
| 1161 | return renderToken(tree, stream, label, indent, start_col, space); // label | |
| 1162 | } else { | |
| 1163 | return renderToken(tree, stream, flow_expr.ltoken, indent, start_col, space); // continue | |
| 1151 | 1164 | } |
| 1165 | }, | |
| 1152 | 1166 | |
| 1153 | return renderExpression(allocator, stream, tree, indent, start_col, flow_expr.rhs.?, space); | |
| 1167 | .Return => { | |
| 1168 | const flow_expr = base.castTag(.Return).?; | |
| 1169 | if (flow_expr.getRHS()) |rhs| { | |
| 1170 | try renderToken(tree, stream, flow_expr.ltoken, indent, start_col, Space.Space); | |
| 1171 | return renderExpression(allocator, stream, tree, indent, start_col, rhs, space); | |
| 1172 | } else { | |
| 1173 | return renderToken(tree, stream, flow_expr.ltoken, indent, start_col, space); | |
| 1174 | } | |
| 1154 | 1175 | }, |
| 1155 | 1176 | |
| 1156 | 1177 | .Payload => { |
| ... | ... | @@ -1208,48 +1229,6 @@ fn renderExpression( |
| 1208 | 1229 | return renderExpression(allocator, stream, tree, indent, start_col, field_init.expr, space); |
| 1209 | 1230 | }, |
| 1210 | 1231 | |
| 1211 | .IntegerLiteral => { | |
| 1212 | const integer_literal = @fieldParentPtr(ast.Node.IntegerLiteral, "base", base); | |
| 1213 | return renderToken(tree, stream, integer_literal.token, indent, start_col, space); | |
| 1214 | }, | |
| 1215 | .FloatLiteral => { | |
| 1216 | const float_literal = @fieldParentPtr(ast.Node.FloatLiteral, "base", base); | |
| 1217 | return renderToken(tree, stream, float_literal.token, indent, start_col, space); | |
| 1218 | }, | |
| 1219 | .StringLiteral => { | |
| 1220 | const string_literal = @fieldParentPtr(ast.Node.StringLiteral, "base", base); | |
| 1221 | return renderToken(tree, stream, string_literal.token, indent, start_col, space); | |
| 1222 | }, | |
| 1223 | .CharLiteral => { | |
| 1224 | const char_literal = @fieldParentPtr(ast.Node.CharLiteral, "base", base); | |
| 1225 | return renderToken(tree, stream, char_literal.token, indent, start_col, space); | |
| 1226 | }, | |
| 1227 | .BoolLiteral => { | |
| 1228 | const bool_literal = @fieldParentPtr(ast.Node.CharLiteral, "base", base); | |
| 1229 | return renderToken(tree, stream, bool_literal.token, indent, start_col, space); | |
| 1230 | }, | |
| 1231 | .NullLiteral => { | |
| 1232 | const null_literal = @fieldParentPtr(ast.Node.NullLiteral, "base", base); | |
| 1233 | return renderToken(tree, stream, null_literal.token, indent, start_col, space); | |
| 1234 | }, | |
| 1235 | .Unreachable => { | |
| 1236 | const unreachable_node = @fieldParentPtr(ast.Node.Unreachable, "base", base); | |
| 1237 | return renderToken(tree, stream, unreachable_node.token, indent, start_col, space); | |
| 1238 | }, | |
| 1239 | .ErrorType => { | |
| 1240 | const error_type = @fieldParentPtr(ast.Node.ErrorType, "base", base); | |
| 1241 | return renderToken(tree, stream, error_type.token, indent, start_col, space); | |
| 1242 | }, | |
| 1243 | .AnyType => { | |
| 1244 | const any_type = @fieldParentPtr(ast.Node.AnyType, "base", base); | |
| 1245 | if (mem.eql(u8, tree.tokenSlice(any_type.token), "var")) { | |
| 1246 | // TODO remove in next release cycle | |
| 1247 | try stream.writeAll("anytype"); | |
| 1248 | if (space == .Comma) try stream.writeAll(",\n"); | |
| 1249 | return; | |
| 1250 | } | |
| 1251 | return renderToken(tree, stream, any_type.token, indent, start_col, space); | |
| 1252 | }, | |
| 1253 | 1232 | .ContainerDecl => { |
| 1254 | 1233 | const container_decl = @fieldParentPtr(ast.Node.ContainerDecl, "base", base); |
| 1255 | 1234 | |
| ... | ... | @@ -1468,10 +1447,6 @@ fn renderExpression( |
| 1468 | 1447 | } |
| 1469 | 1448 | try stream.writeByteNTimes(' ', indent); |
| 1470 | 1449 | }, |
| 1471 | .UndefinedLiteral => { | |
| 1472 | const undefined_literal = @fieldParentPtr(ast.Node.UndefinedLiteral, "base", base); | |
| 1473 | return renderToken(tree, stream, undefined_literal.token, indent, start_col, space); | |
| 1474 | }, | |
| 1475 | 1450 | |
| 1476 | 1451 | .BuiltinCall => { |
| 1477 | 1452 | const builtin_call = @fieldParentPtr(ast.Node.BuiltinCall, "base", base); |
src-self-hosted/Module.zig+185-45| ... | ... | @@ -212,7 +212,8 @@ pub const Decl = struct { |
| 212 | 212 | }, |
| 213 | 213 | .block => unreachable, |
| 214 | 214 | .gen_zir => unreachable, |
| 215 | .local_var => unreachable, | |
| 215 | .local_val => unreachable, | |
| 216 | .local_ptr => unreachable, | |
| 216 | 217 | .decl => unreachable, |
| 217 | 218 | } |
| 218 | 219 | } |
| ... | ... | @@ -308,7 +309,8 @@ pub const Scope = struct { |
| 308 | 309 | .block => return self.cast(Block).?.arena, |
| 309 | 310 | .decl => return &self.cast(DeclAnalysis).?.arena.allocator, |
| 310 | 311 | .gen_zir => return self.cast(GenZIR).?.arena, |
| 311 | .local_var => return self.cast(LocalVar).?.gen_zir.arena, | |
| 312 | .local_val => return self.cast(LocalVal).?.gen_zir.arena, | |
| 313 | .local_ptr => return self.cast(LocalPtr).?.gen_zir.arena, | |
| 312 | 314 | .zir_module => return &self.cast(ZIRModule).?.contents.module.arena.allocator, |
| 313 | 315 | .file => unreachable, |
| 314 | 316 | } |
| ... | ... | @@ -320,7 +322,8 @@ pub const Scope = struct { |
| 320 | 322 | return switch (self.tag) { |
| 321 | 323 | .block => self.cast(Block).?.decl, |
| 322 | 324 | .gen_zir => self.cast(GenZIR).?.decl, |
| 323 | .local_var => return self.cast(LocalVar).?.gen_zir.decl, | |
| 325 | .local_val => return self.cast(LocalVal).?.gen_zir.decl, | |
| 326 | .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl, | |
| 324 | 327 | .decl => self.cast(DeclAnalysis).?.decl, |
| 325 | 328 | .zir_module => null, |
| 326 | 329 | .file => null, |
| ... | ... | @@ -333,7 +336,8 @@ pub const Scope = struct { |
| 333 | 336 | switch (self.tag) { |
| 334 | 337 | .block => return self.cast(Block).?.decl.scope, |
| 335 | 338 | .gen_zir => return self.cast(GenZIR).?.decl.scope, |
| 336 | .local_var => return self.cast(LocalVar).?.gen_zir.decl.scope, | |
| 339 | .local_val => return self.cast(LocalVal).?.gen_zir.decl.scope, | |
| 340 | .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.scope, | |
| 337 | 341 | .decl => return self.cast(DeclAnalysis).?.decl.scope, |
| 338 | 342 | .zir_module, .file => return self, |
| 339 | 343 | } |
| ... | ... | @@ -346,7 +350,8 @@ pub const Scope = struct { |
| 346 | 350 | switch (self.tag) { |
| 347 | 351 | .block => unreachable, |
| 348 | 352 | .gen_zir => unreachable, |
| 349 | .local_var => unreachable, | |
| 353 | .local_val => unreachable, | |
| 354 | .local_ptr => unreachable, | |
| 350 | 355 | .decl => unreachable, |
| 351 | 356 | .zir_module => return self.cast(ZIRModule).?.fullyQualifiedNameHash(name), |
| 352 | 357 | .file => return self.cast(File).?.fullyQualifiedNameHash(name), |
| ... | ... | @@ -361,7 +366,8 @@ pub const Scope = struct { |
| 361 | 366 | .decl => return self.cast(DeclAnalysis).?.decl.scope.cast(File).?.contents.tree, |
| 362 | 367 | .block => return self.cast(Block).?.decl.scope.cast(File).?.contents.tree, |
| 363 | 368 | .gen_zir => return self.cast(GenZIR).?.decl.scope.cast(File).?.contents.tree, |
| 364 | .local_var => return self.cast(LocalVar).?.gen_zir.decl.scope.cast(File).?.contents.tree, | |
| 369 | .local_val => return self.cast(LocalVal).?.gen_zir.decl.scope.cast(File).?.contents.tree, | |
| 370 | .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.scope.cast(File).?.contents.tree, | |
| 365 | 371 | } |
| 366 | 372 | } |
| 367 | 373 | |
| ... | ... | @@ -370,7 +376,8 @@ pub const Scope = struct { |
| 370 | 376 | return switch (self.tag) { |
| 371 | 377 | .block => unreachable, |
| 372 | 378 | .gen_zir => self.cast(GenZIR).?, |
| 373 | .local_var => return self.cast(LocalVar).?.gen_zir, | |
| 379 | .local_val => return self.cast(LocalVal).?.gen_zir, | |
| 380 | .local_ptr => return self.cast(LocalPtr).?.gen_zir, | |
| 374 | 381 | .decl => unreachable, |
| 375 | 382 | .zir_module => unreachable, |
| 376 | 383 | .file => unreachable, |
| ... | ... | @@ -397,7 +404,8 @@ pub const Scope = struct { |
| 397 | 404 | .zir_module => return @fieldParentPtr(ZIRModule, "base", base).sub_file_path, |
| 398 | 405 | .block => unreachable, |
| 399 | 406 | .gen_zir => unreachable, |
| 400 | .local_var => unreachable, | |
| 407 | .local_val => unreachable, | |
| 408 | .local_ptr => unreachable, | |
| 401 | 409 | .decl => unreachable, |
| 402 | 410 | } |
| 403 | 411 | } |
| ... | ... | @@ -408,7 +416,8 @@ pub const Scope = struct { |
| 408 | 416 | .zir_module => return @fieldParentPtr(ZIRModule, "base", base).unload(gpa), |
| 409 | 417 | .block => unreachable, |
| 410 | 418 | .gen_zir => unreachable, |
| 411 | .local_var => unreachable, | |
| 419 | .local_val => unreachable, | |
| 420 | .local_ptr => unreachable, | |
| 412 | 421 | .decl => unreachable, |
| 413 | 422 | } |
| 414 | 423 | } |
| ... | ... | @@ -418,7 +427,8 @@ pub const Scope = struct { |
| 418 | 427 | .file => return @fieldParentPtr(File, "base", base).getSource(module), |
| 419 | 428 | .zir_module => return @fieldParentPtr(ZIRModule, "base", base).getSource(module), |
| 420 | 429 | .gen_zir => unreachable, |
| 421 | .local_var => unreachable, | |
| 430 | .local_val => unreachable, | |
| 431 | .local_ptr => unreachable, | |
| 422 | 432 | .block => unreachable, |
| 423 | 433 | .decl => unreachable, |
| 424 | 434 | } |
| ... | ... | @@ -431,7 +441,8 @@ pub const Scope = struct { |
| 431 | 441 | .zir_module => return @fieldParentPtr(ZIRModule, "base", base).removeDecl(child), |
| 432 | 442 | .block => unreachable, |
| 433 | 443 | .gen_zir => unreachable, |
| 434 | .local_var => unreachable, | |
| 444 | .local_val => unreachable, | |
| 445 | .local_ptr => unreachable, | |
| 435 | 446 | .decl => unreachable, |
| 436 | 447 | } |
| 437 | 448 | } |
| ... | ... | @@ -451,7 +462,8 @@ pub const Scope = struct { |
| 451 | 462 | }, |
| 452 | 463 | .block => unreachable, |
| 453 | 464 | .gen_zir => unreachable, |
| 454 | .local_var => unreachable, | |
| 465 | .local_val => unreachable, | |
| 466 | .local_ptr => unreachable, | |
| 455 | 467 | .decl => unreachable, |
| 456 | 468 | } |
| 457 | 469 | } |
| ... | ... | @@ -472,7 +484,8 @@ pub const Scope = struct { |
| 472 | 484 | block, |
| 473 | 485 | decl, |
| 474 | 486 | gen_zir, |
| 475 | local_var, | |
| 487 | local_val, | |
| 488 | local_ptr, | |
| 476 | 489 | }; |
| 477 | 490 | |
| 478 | 491 | pub const File = struct { |
| ... | ... | @@ -708,17 +721,31 @@ pub const Scope = struct { |
| 708 | 721 | instructions: std.ArrayListUnmanaged(*zir.Inst) = .{}, |
| 709 | 722 | }; |
| 710 | 723 | |
| 724 | /// This is always a `const` local and importantly the `inst` is a value type, not a pointer. | |
| 711 | 725 | /// This structure lives as long as the AST generation of the Block |
| 712 | 726 | /// node that contains the variable. |
| 713 | pub const LocalVar = struct { | |
| 714 | pub const base_tag: Tag = .local_var; | |
| 727 | pub const LocalVal = struct { | |
| 728 | pub const base_tag: Tag = .local_val; | |
| 715 | 729 | base: Scope = Scope{ .tag = base_tag }, |
| 716 | /// Parents can be: `LocalVar`, `GenZIR`. | |
| 730 | /// Parents can be: `LocalVal`, `LocalPtr`, `GenZIR`. | |
| 717 | 731 | parent: *Scope, |
| 718 | 732 | gen_zir: *GenZIR, |
| 719 | 733 | name: []const u8, |
| 720 | 734 | inst: *zir.Inst, |
| 721 | 735 | }; |
| 736 | ||
| 737 | /// This could be a `const` or `var` local. It has a pointer instead of a value. | |
| 738 | /// This structure lives as long as the AST generation of the Block | |
| 739 | /// node that contains the variable. | |
| 740 | pub const LocalPtr = struct { | |
| 741 | pub const base_tag: Tag = .local_ptr; | |
| 742 | base: Scope = Scope{ .tag = base_tag }, | |
| 743 | /// Parents can be: `LocalVal`, `LocalPtr`, `GenZIR`. | |
| 744 | parent: *Scope, | |
| 745 | gen_zir: *GenZIR, | |
| 746 | name: []const u8, | |
| 747 | ptr: *zir.Inst, | |
| 748 | }; | |
| 722 | 749 | }; |
| 723 | 750 | |
| 724 | 751 | pub const AllErrors = struct { |
| ... | ... | @@ -1176,12 +1203,19 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { |
| 1176 | 1203 | |
| 1177 | 1204 | const param_decls = fn_proto.params(); |
| 1178 | 1205 | const param_types = try fn_type_scope.arena.alloc(*zir.Inst, param_decls.len); |
| 1206 | ||
| 1207 | const fn_src = tree.token_locs[fn_proto.fn_token].start; | |
| 1208 | const type_type = try self.addZIRInstConst(&fn_type_scope.base, fn_src, .{ | |
| 1209 | .ty = Type.initTag(.type), | |
| 1210 | .val = Value.initTag(.type_type), | |
| 1211 | }); | |
| 1212 | const type_type_rl: astgen.ResultLoc = .{ .ty = type_type }; | |
| 1179 | 1213 | for (param_decls) |param_decl, i| { |
| 1180 | 1214 | const param_type_node = switch (param_decl.param_type) { |
| 1181 | 1215 | .any_type => |node| return self.failNode(&fn_type_scope.base, node, "TODO implement anytype parameter", .{}), |
| 1182 | 1216 | .type_expr => |node| node, |
| 1183 | 1217 | }; |
| 1184 | param_types[i] = try astgen.expr(self, &fn_type_scope.base, param_type_node); | |
| 1218 | param_types[i] = try astgen.expr(self, &fn_type_scope.base, type_type_rl, param_type_node); | |
| 1185 | 1219 | } |
| 1186 | 1220 | if (fn_proto.getTrailer("var_args_token")) |var_args_token| { |
| 1187 | 1221 | return self.failTok(&fn_type_scope.base, var_args_token, "TODO implement var args", .{}); |
| ... | ... | @@ -1209,8 +1243,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { |
| 1209 | 1243 | .Invalid => |tok| return self.failTok(&fn_type_scope.base, tok, "unable to parse return type", .{}), |
| 1210 | 1244 | }; |
| 1211 | 1245 | |
| 1212 | const return_type_inst = try astgen.expr(self, &fn_type_scope.base, return_type_expr); | |
| 1213 | const fn_src = tree.token_locs[fn_proto.fn_token].start; | |
| 1246 | const return_type_inst = try astgen.expr(self, &fn_type_scope.base, type_type_rl, return_type_expr); | |
| 1214 | 1247 | const fn_type_inst = try self.addZIRInst(&fn_type_scope.base, fn_src, zir.Inst.FnType, .{ |
| 1215 | 1248 | .return_type = return_type_inst, |
| 1216 | 1249 | .param_types = param_types, |
| ... | ... | @@ -1266,7 +1299,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { |
| 1266 | 1299 | .kw_args = .{}, |
| 1267 | 1300 | }; |
| 1268 | 1301 | gen_scope.instructions.items[i] = &arg.base; |
| 1269 | const sub_scope = try gen_scope_arena.allocator.create(Scope.LocalVar); | |
| 1302 | const sub_scope = try gen_scope_arena.allocator.create(Scope.LocalVal); | |
| 1270 | 1303 | sub_scope.* = .{ |
| 1271 | 1304 | .parent = params_scope, |
| 1272 | 1305 | .gen_zir = &gen_scope, |
| ... | ... | @@ -1829,6 +1862,7 @@ fn resolveInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*In |
| 1829 | 1862 | return self.analyzeDeref(scope, old_inst.src, decl_ref, old_inst.src); |
| 1830 | 1863 | } |
| 1831 | 1864 | |
| 1865 | /// TODO split this into `requireRuntimeBlock` and `requireFunctionBlock` and audit callsites. | |
| 1832 | 1866 | fn requireRuntimeBlock(self: *Module, scope: *Scope, src: usize) !*Scope.Block { |
| 1833 | 1867 | return scope.cast(Scope.Block) orelse |
| 1834 | 1868 | return self.fail(scope, src, "instruction illegal outside function body", .{}); |
| ... | ... | @@ -2098,12 +2132,7 @@ pub fn addZIRInstSpecial( |
| 2098 | 2132 | return inst; |
| 2099 | 2133 | } |
| 2100 | 2134 | |
| 2101 | pub fn addZIRNoOp( | |
| 2102 | self: *Module, | |
| 2103 | scope: *Scope, | |
| 2104 | src: usize, | |
| 2105 | tag: zir.Inst.Tag, | |
| 2106 | ) !*zir.Inst { | |
| 2135 | pub fn addZIRNoOpT(self: *Module, scope: *Scope, src: usize, tag: zir.Inst.Tag) !*zir.Inst.NoOp { | |
| 2107 | 2136 | const gen_zir = scope.getGenZIR(); |
| 2108 | 2137 | try gen_zir.instructions.ensureCapacity(self.gpa, gen_zir.instructions.items.len + 1); |
| 2109 | 2138 | const inst = try gen_zir.arena.create(zir.Inst.NoOp); |
| ... | ... | @@ -2116,6 +2145,11 @@ pub fn addZIRNoOp( |
| 2116 | 2145 | .kw_args = .{}, |
| 2117 | 2146 | }; |
| 2118 | 2147 | gen_zir.instructions.appendAssumeCapacity(&inst.base); |
| 2148 | return inst; | |
| 2149 | } | |
| 2150 | ||
| 2151 | pub fn addZIRNoOp(self: *Module, scope: *Scope, src: usize, tag: zir.Inst.Tag) !*zir.Inst { | |
| 2152 | const inst = try self.addZIRNoOpT(scope, src, tag); | |
| 2119 | 2153 | return &inst.base; |
| 2120 | 2154 | } |
| 2121 | 2155 | |
| ... | ... | @@ -2320,24 +2354,36 @@ fn analyzeInstConst(self: *Module, scope: *Scope, const_inst: *zir.Inst.Const) I |
| 2320 | 2354 | |
| 2321 | 2355 | fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Inst { |
| 2322 | 2356 | switch (old_inst.tag) { |
| 2357 | .alloc => return self.analyzeInstAlloc(scope, old_inst.castTag(.alloc).?), | |
| 2358 | .alloc_inferred => return self.analyzeInstAllocInferred(scope, old_inst.castTag(.alloc_inferred).?), | |
| 2323 | 2359 | .arg => return self.analyzeInstArg(scope, old_inst.castTag(.arg).?), |
| 2360 | .bitcast_result_ptr => return self.analyzeInstBitCastResultPtr(scope, old_inst.castTag(.bitcast_result_ptr).?), | |
| 2324 | 2361 | .block => return self.analyzeInstBlock(scope, old_inst.castTag(.block).?), |
| 2325 | 2362 | .@"break" => return self.analyzeInstBreak(scope, old_inst.castTag(.@"break").?), |
| 2326 | 2363 | .breakpoint => return self.analyzeInstBreakpoint(scope, old_inst.castTag(.breakpoint).?), |
| 2327 | 2364 | .breakvoid => return self.analyzeInstBreakVoid(scope, old_inst.castTag(.breakvoid).?), |
| 2328 | 2365 | .call => return self.analyzeInstCall(scope, old_inst.castTag(.call).?), |
| 2366 | .coerce_result_block_ptr => return self.analyzeInstCoerceResultBlockPtr(scope, old_inst.castTag(.coerce_result_block_ptr).?), | |
| 2367 | .coerce_result_ptr => return self.analyzeInstCoerceResultPtr(scope, old_inst.castTag(.coerce_result_ptr).?), | |
| 2368 | .coerce_to_ptr_elem => return self.analyzeInstCoerceToPtrElem(scope, old_inst.castTag(.coerce_to_ptr_elem).?), | |
| 2329 | 2369 | .compileerror => return self.analyzeInstCompileError(scope, old_inst.castTag(.compileerror).?), |
| 2330 | 2370 | .@"const" => return self.analyzeInstConst(scope, old_inst.castTag(.@"const").?), |
| 2331 | 2371 | .declref => return self.analyzeInstDeclRef(scope, old_inst.castTag(.declref).?), |
| 2332 | 2372 | .declref_str => return self.analyzeInstDeclRefStr(scope, old_inst.castTag(.declref_str).?), |
| 2333 | 2373 | .declval => return self.analyzeInstDeclVal(scope, old_inst.castTag(.declval).?), |
| 2334 | 2374 | .declval_in_module => return self.analyzeInstDeclValInModule(scope, old_inst.castTag(.declval_in_module).?), |
| 2375 | .ensure_result_used => return self.analyzeInstEnsureResultUsed(scope, old_inst.castTag(.ensure_result_used).?), | |
| 2376 | .ensure_result_non_error => return self.analyzeInstEnsureResultNonError(scope, old_inst.castTag(.ensure_result_non_error).?), | |
| 2377 | .ret_ptr => return self.analyzeInstRetPtr(scope, old_inst.castTag(.ret_ptr).?), | |
| 2378 | .ret_type => return self.analyzeInstRetType(scope, old_inst.castTag(.ret_type).?), | |
| 2379 | .store => return self.analyzeInstStore(scope, old_inst.castTag(.store).?), | |
| 2335 | 2380 | .str => return self.analyzeInstStr(scope, old_inst.castTag(.str).?), |
| 2336 | 2381 | .int => { |
| 2337 | 2382 | const big_int = old_inst.castTag(.int).?.positionals.int; |
| 2338 | 2383 | return self.constIntBig(scope, old_inst.src, Type.initTag(.comptime_int), big_int); |
| 2339 | 2384 | }, |
| 2340 | 2385 | .inttype => return self.analyzeInstIntType(scope, old_inst.castTag(.inttype).?), |
| 2386 | .param_type => return self.analyzeInstParamType(scope, old_inst.castTag(.param_type).?), | |
| 2341 | 2387 | .ptrtoint => return self.analyzeInstPtrToInt(scope, old_inst.castTag(.ptrtoint).?), |
| 2342 | 2388 | .fieldptr => return self.analyzeInstFieldPtr(scope, old_inst.castTag(.fieldptr).?), |
| 2343 | 2389 | .deref => return self.analyzeInstDeref(scope, old_inst.castTag(.deref).?), |
| ... | ... | @@ -2369,6 +2415,94 @@ fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*In |
| 2369 | 2415 | } |
| 2370 | 2416 | } |
| 2371 | 2417 | |
| 2418 | fn analyzeInstCoerceResultBlockPtr( | |
| 2419 | self: *Module, | |
| 2420 | scope: *Scope, | |
| 2421 | inst: *zir.Inst.CoerceResultBlockPtr, | |
| 2422 | ) InnerError!*Inst { | |
| 2423 | return self.fail(scope, inst.base.src, "TODO implement analyzeInstCoerceResultBlockPtr", .{}); | |
| 2424 | } | |
| 2425 | ||
| 2426 | fn analyzeInstBitCastResultPtr(self: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { | |
| 2427 | return self.fail(scope, inst.base.src, "TODO implement analyzeInstBitCastResultPtr", .{}); | |
| 2428 | } | |
| 2429 | ||
| 2430 | fn analyzeInstCoerceResultPtr(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { | |
| 2431 | return self.fail(scope, inst.base.src, "TODO implement analyzeInstCoerceResultPtr", .{}); | |
| 2432 | } | |
| 2433 | ||
| 2434 | fn analyzeInstCoerceToPtrElem(self: *Module, scope: *Scope, inst: *zir.Inst.CoerceToPtrElem) InnerError!*Inst { | |
| 2435 | return self.fail(scope, inst.base.src, "TODO implement analyzeInstCoerceToPtrElem", .{}); | |
| 2436 | } | |
| 2437 | ||
| 2438 | fn analyzeInstRetPtr(self: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst { | |
| 2439 | return self.fail(scope, inst.base.src, "TODO implement analyzeInstRetPtr", .{}); | |
| 2440 | } | |
| 2441 | ||
| 2442 | fn analyzeInstRetType(self: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst { | |
| 2443 | const b = try self.requireRuntimeBlock(scope, inst.base.src); | |
| 2444 | const fn_ty = b.func.?.owner_decl.typed_value.most_recent.typed_value.ty; | |
| 2445 | const ret_type = fn_ty.fnReturnType(); | |
| 2446 | return self.constType(scope, inst.base.src, ret_type); | |
| 2447 | } | |
| 2448 | ||
| 2449 | fn analyzeInstEnsureResultUsed(self: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { | |
| 2450 | const operand = try self.resolveInst(scope, inst.positionals.operand); | |
| 2451 | switch (operand.ty.zigTypeTag()) { | |
| 2452 | .Void, .NoReturn => return self.constVoid(scope, operand.src), | |
| 2453 | else => return self.fail(scope, operand.src, "expression value is ignored", .{}), | |
| 2454 | } | |
| 2455 | } | |
| 2456 | ||
| 2457 | fn analyzeInstEnsureResultNonError(self: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { | |
| 2458 | const operand = try self.resolveInst(scope, inst.positionals.operand); | |
| 2459 | switch (operand.ty.zigTypeTag()) { | |
| 2460 | .ErrorSet, .ErrorUnion => return self.fail(scope, operand.src, "error is discarded", .{}), | |
| 2461 | else => return self.constVoid(scope, operand.src), | |
| 2462 | } | |
| 2463 | } | |
| 2464 | ||
| 2465 | fn analyzeInstAlloc(self: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { | |
| 2466 | return self.fail(scope, inst.base.src, "TODO implement analyzeInstAlloc", .{}); | |
| 2467 | } | |
| 2468 | ||
| 2469 | fn analyzeInstAllocInferred(self: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst { | |
| 2470 | return self.fail(scope, inst.base.src, "TODO implement analyzeInstAllocInferred", .{}); | |
| 2471 | } | |
| 2472 | ||
| 2473 | fn analyzeInstStore(self: *Module, scope: *Scope, inst: *zir.Inst.Store) InnerError!*Inst { | |
| 2474 | return self.fail(scope, inst.base.src, "TODO implement analyzeInstStore", .{}); | |
| 2475 | } | |
| 2476 | ||
| 2477 | fn analyzeInstParamType(self: *Module, scope: *Scope, inst: *zir.Inst.ParamType) InnerError!*Inst { | |
| 2478 | const fn_inst = try self.resolveInst(scope, inst.positionals.func); | |
| 2479 | const arg_index = inst.positionals.arg_index; | |
| 2480 | ||
| 2481 | const fn_ty: Type = switch (fn_inst.ty.zigTypeTag()) { | |
| 2482 | .Fn => fn_inst.ty, | |
| 2483 | .BoundFn => { | |
| 2484 | return self.fail(scope, fn_inst.src, "TODO implement analyzeInstParamType for method call syntax", .{}); | |
| 2485 | }, | |
| 2486 | else => { | |
| 2487 | return self.fail(scope, fn_inst.src, "expected function, found '{}'", .{fn_inst.ty}); | |
| 2488 | }, | |
| 2489 | }; | |
| 2490 | ||
| 2491 | // TODO support C-style var args | |
| 2492 | const param_count = fn_ty.fnParamLen(); | |
| 2493 | if (arg_index >= param_count) { | |
| 2494 | return self.fail(scope, inst.base.src, "arg index {} out of bounds; '{}' has {} arguments", .{ | |
| 2495 | arg_index, | |
| 2496 | fn_ty, | |
| 2497 | param_count, | |
| 2498 | }); | |
| 2499 | } | |
| 2500 | ||
| 2501 | // TODO support generic functions | |
| 2502 | const param_type = fn_ty.fnParamType(arg_index); | |
| 2503 | return self.constType(scope, inst.base.src, param_type); | |
| 2504 | } | |
| 2505 | ||
| 2372 | 2506 | fn analyzeInstStr(self: *Module, scope: *Scope, str_inst: *zir.Inst.Str) InnerError!*Inst { |
| 2373 | 2507 | // The bytes references memory inside the ZIR module, which can get deallocated |
| 2374 | 2508 | // after semantic analysis is complete. We need the memory to be in the new anonymous Decl's arena. |
| ... | ... | @@ -2746,13 +2880,13 @@ fn analyzeInstPrimitive(self: *Module, scope: *Scope, primitive: *zir.Inst.Primi |
| 2746 | 2880 | return self.constInst(scope, primitive.base.src, primitive.positionals.tag.toTypedValue()); |
| 2747 | 2881 | } |
| 2748 | 2882 | |
| 2749 | fn analyzeInstAs(self: *Module, scope: *Scope, as: *zir.Inst.As) InnerError!*Inst { | |
| 2750 | const dest_type = try self.resolveType(scope, as.positionals.dest_type); | |
| 2751 | const new_inst = try self.resolveInst(scope, as.positionals.value); | |
| 2883 | fn analyzeInstAs(self: *Module, scope: *Scope, as: *zir.Inst.BinOp) InnerError!*Inst { | |
| 2884 | const dest_type = try self.resolveType(scope, as.positionals.lhs); | |
| 2885 | const new_inst = try self.resolveInst(scope, as.positionals.rhs); | |
| 2752 | 2886 | return self.coerce(scope, dest_type, new_inst); |
| 2753 | 2887 | } |
| 2754 | 2888 | |
| 2755 | fn analyzeInstPtrToInt(self: *Module, scope: *Scope, ptrtoint: *zir.Inst.PtrToInt) InnerError!*Inst { | |
| 2889 | fn analyzeInstPtrToInt(self: *Module, scope: *Scope, ptrtoint: *zir.Inst.UnOp) InnerError!*Inst { | |
| 2756 | 2890 | const ptr = try self.resolveInst(scope, ptrtoint.positionals.operand); |
| 2757 | 2891 | if (ptr.ty.zigTypeTag() != .Pointer) { |
| 2758 | 2892 | return self.fail(scope, ptrtoint.positionals.operand.src, "expected pointer, found '{}'", .{ptr.ty}); |
| ... | ... | @@ -2797,16 +2931,16 @@ fn analyzeInstFieldPtr(self: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPt |
| 2797 | 2931 | } |
| 2798 | 2932 | } |
| 2799 | 2933 | |
| 2800 | fn analyzeInstIntCast(self: *Module, scope: *Scope, inst: *zir.Inst.IntCast) InnerError!*Inst { | |
| 2801 | const dest_type = try self.resolveType(scope, inst.positionals.dest_type); | |
| 2802 | const operand = try self.resolveInst(scope, inst.positionals.operand); | |
| 2934 | fn analyzeInstIntCast(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { | |
| 2935 | const dest_type = try self.resolveType(scope, inst.positionals.lhs); | |
| 2936 | const operand = try self.resolveInst(scope, inst.positionals.rhs); | |
| 2803 | 2937 | |
| 2804 | 2938 | const dest_is_comptime_int = switch (dest_type.zigTypeTag()) { |
| 2805 | 2939 | .ComptimeInt => true, |
| 2806 | 2940 | .Int => false, |
| 2807 | 2941 | else => return self.fail( |
| 2808 | 2942 | scope, |
| 2809 | inst.positionals.dest_type.src, | |
| 2943 | inst.positionals.lhs.src, | |
| 2810 | 2944 | "expected integer type, found '{}'", |
| 2811 | 2945 | .{ |
| 2812 | 2946 | dest_type, |
| ... | ... | @@ -2818,7 +2952,7 @@ fn analyzeInstIntCast(self: *Module, scope: *Scope, inst: *zir.Inst.IntCast) Inn |
| 2818 | 2952 | .ComptimeInt, .Int => {}, |
| 2819 | 2953 | else => return self.fail( |
| 2820 | 2954 | scope, |
| 2821 | inst.positionals.operand.src, | |
| 2955 | inst.positionals.rhs.src, | |
| 2822 | 2956 | "expected integer type, found '{}'", |
| 2823 | 2957 | .{operand.ty}, |
| 2824 | 2958 | ), |
| ... | ... | @@ -2833,22 +2967,22 @@ fn analyzeInstIntCast(self: *Module, scope: *Scope, inst: *zir.Inst.IntCast) Inn |
| 2833 | 2967 | return self.fail(scope, inst.base.src, "TODO implement analyze widen or shorten int", .{}); |
| 2834 | 2968 | } |
| 2835 | 2969 | |
| 2836 | fn analyzeInstBitCast(self: *Module, scope: *Scope, inst: *zir.Inst.BitCast) InnerError!*Inst { | |
| 2837 | const dest_type = try self.resolveType(scope, inst.positionals.dest_type); | |
| 2838 | const operand = try self.resolveInst(scope, inst.positionals.operand); | |
| 2970 | fn analyzeInstBitCast(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { | |
| 2971 | const dest_type = try self.resolveType(scope, inst.positionals.lhs); | |
| 2972 | const operand = try self.resolveInst(scope, inst.positionals.rhs); | |
| 2839 | 2973 | return self.bitcast(scope, dest_type, operand); |
| 2840 | 2974 | } |
| 2841 | 2975 | |
| 2842 | fn analyzeInstFloatCast(self: *Module, scope: *Scope, inst: *zir.Inst.FloatCast) InnerError!*Inst { | |
| 2843 | const dest_type = try self.resolveType(scope, inst.positionals.dest_type); | |
| 2844 | const operand = try self.resolveInst(scope, inst.positionals.operand); | |
| 2976 | fn analyzeInstFloatCast(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { | |
| 2977 | const dest_type = try self.resolveType(scope, inst.positionals.lhs); | |
| 2978 | const operand = try self.resolveInst(scope, inst.positionals.rhs); | |
| 2845 | 2979 | |
| 2846 | 2980 | const dest_is_comptime_float = switch (dest_type.zigTypeTag()) { |
| 2847 | 2981 | .ComptimeFloat => true, |
| 2848 | 2982 | .Float => false, |
| 2849 | 2983 | else => return self.fail( |
| 2850 | 2984 | scope, |
| 2851 | inst.positionals.dest_type.src, | |
| 2985 | inst.positionals.lhs.src, | |
| 2852 | 2986 | "expected float type, found '{}'", |
| 2853 | 2987 | .{ |
| 2854 | 2988 | dest_type, |
| ... | ... | @@ -2860,7 +2994,7 @@ fn analyzeInstFloatCast(self: *Module, scope: *Scope, inst: *zir.Inst.FloatCast) |
| 2860 | 2994 | .ComptimeFloat, .Float, .ComptimeInt => {}, |
| 2861 | 2995 | else => return self.fail( |
| 2862 | 2996 | scope, |
| 2863 | inst.positionals.operand.src, | |
| 2997 | inst.positionals.rhs.src, | |
| 2864 | 2998 | "expected float type, found '{}'", |
| 2865 | 2999 | .{operand.ty}, |
| 2866 | 3000 | ), |
| ... | ... | @@ -3560,8 +3694,14 @@ fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *Err |
| 3560 | 3694 | gen_zir.decl.generation = self.generation; |
| 3561 | 3695 | self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg); |
| 3562 | 3696 | }, |
| 3563 | .local_var => { | |
| 3564 | const gen_zir = scope.cast(Scope.LocalVar).?.gen_zir; | |
| 3697 | .local_val => { | |
| 3698 | const gen_zir = scope.cast(Scope.LocalVal).?.gen_zir; | |
| 3699 | gen_zir.decl.analysis = .sema_failure; | |
| 3700 | gen_zir.decl.generation = self.generation; | |
| 3701 | self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg); | |
| 3702 | }, | |
| 3703 | .local_ptr => { | |
| 3704 | const gen_zir = scope.cast(Scope.LocalPtr).?.gen_zir; | |
| 3565 | 3705 | gen_zir.decl.analysis = .sema_failure; |
| 3566 | 3706 | gen_zir.decl.generation = self.generation; |
| 3567 | 3707 | self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg); |
src-self-hosted/astgen.zig+457-153| ... | ... | @@ -1,5 +1,6 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | const mem = std.mem; |
| 3 | const Allocator = std.mem.Allocator; | |
| 3 | 4 | const Value = @import("value.zig").Value; |
| 4 | 5 | const Type = @import("type.zig").Type; |
| 5 | 6 | const TypedValue = @import("TypedValue.zig"); |
| ... | ... | @@ -11,35 +12,67 @@ const trace = @import("tracy.zig").trace; |
| 11 | 12 | const Scope = Module.Scope; |
| 12 | 13 | const InnerError = Module.InnerError; |
| 13 | 14 | |
| 15 | pub const ResultLoc = union(enum) { | |
| 16 | /// The expression is the right-hand side of assignment to `_`. | |
| 17 | discard, | |
| 18 | /// The expression has an inferred type, and it will be evaluated as an rvalue. | |
| 19 | none, | |
| 20 | /// The expression will be type coerced into this type, but it will be evaluated as an rvalue. | |
| 21 | ty: *zir.Inst, | |
| 22 | /// The expression must store its result into this typed pointer. | |
| 23 | ptr: *zir.Inst, | |
| 24 | /// The expression must store its result into this allocation, which has an inferred type. | |
| 25 | inferred_ptr: *zir.Inst.Tag.alloc_inferred.Type(), | |
| 26 | /// The expression must store its result into this pointer, which is a typed pointer that | |
| 27 | /// has been bitcasted to whatever the expression's type is. | |
| 28 | bitcasted_ptr: *zir.Inst.UnOp, | |
| 29 | /// There is a pointer for the expression to store its result into, however, its type | |
| 30 | /// is inferred based on peer type resolution for a `zir.Inst.Block`. | |
| 31 | block_ptr: *zir.Inst.Block, | |
| 32 | }; | |
| 33 | ||
| 34 | pub fn typeExpr(mod: *Module, scope: *Scope, type_node: *ast.Node) InnerError!*zir.Inst { | |
| 35 | const type_src = scope.tree().token_locs[type_node.firstToken()].start; | |
| 36 | const type_type = try mod.addZIRInstConst(scope, type_src, .{ | |
| 37 | .ty = Type.initTag(.type), | |
| 38 | .val = Value.initTag(.type_type), | |
| 39 | }); | |
| 40 | const type_rl: ResultLoc = .{ .ty = type_type }; | |
| 41 | return expr(mod, scope, type_rl, type_node); | |
| 42 | } | |
| 43 | ||
| 14 | 44 | /// Turn Zig AST into untyped ZIR istructions. |
| 15 | pub fn expr(mod: *Module, scope: *Scope, node: *ast.Node) InnerError!*zir.Inst { | |
| 45 | pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerError!*zir.Inst { | |
| 16 | 46 | switch (node.tag) { |
| 17 | 47 | .VarDecl => unreachable, // Handled in `blockExpr`. |
| 18 | ||
| 19 | .Add => return simpleInfixOp(mod, scope, node.castTag(.Add).?, .add), | |
| 20 | .Sub => return simpleInfixOp(mod, scope, node.castTag(.Sub).?, .sub), | |
| 21 | .BangEqual => return simpleInfixOp(mod, scope, node.castTag(.BangEqual).?, .cmp_neq), | |
| 22 | .EqualEqual => return simpleInfixOp(mod, scope, node.castTag(.EqualEqual).?, .cmp_eq), | |
| 23 | .GreaterThan => return simpleInfixOp(mod, scope, node.castTag(.GreaterThan).?, .cmp_gt), | |
| 24 | .GreaterOrEqual => return simpleInfixOp(mod, scope, node.castTag(.GreaterOrEqual).?, .cmp_gte), | |
| 25 | .LessThan => return simpleInfixOp(mod, scope, node.castTag(.LessThan).?, .cmp_lt), | |
| 26 | .LessOrEqual => return simpleInfixOp(mod, scope, node.castTag(.LessOrEqual).?, .cmp_lte), | |
| 27 | ||
| 28 | .Identifier => return identifier(mod, scope, node.castTag(.Identifier).?), | |
| 29 | .Asm => return assembly(mod, scope, node.castTag(.Asm).?), | |
| 30 | .StringLiteral => return stringLiteral(mod, scope, node.castTag(.StringLiteral).?), | |
| 31 | .IntegerLiteral => return integerLiteral(mod, scope, node.castTag(.IntegerLiteral).?), | |
| 32 | .BuiltinCall => return builtinCall(mod, scope, node.castTag(.BuiltinCall).?), | |
| 33 | .Call => return callExpr(mod, scope, node.castTag(.Call).?), | |
| 48 | .Assign => unreachable, // Handled in `blockExpr`. | |
| 49 | ||
| 50 | .Add => return arithmetic(mod, scope, rl, node.castTag(.Add).?, .add), | |
| 51 | .Sub => return arithmetic(mod, scope, rl, node.castTag(.Sub).?, .sub), | |
| 52 | ||
| 53 | .BangEqual => return cmp(mod, scope, rl, node.castTag(.BangEqual).?, .cmp_neq), | |
| 54 | .EqualEqual => return cmp(mod, scope, rl, node.castTag(.EqualEqual).?, .cmp_eq), | |
| 55 | .GreaterThan => return cmp(mod, scope, rl, node.castTag(.GreaterThan).?, .cmp_gt), | |
| 56 | .GreaterOrEqual => return cmp(mod, scope, rl, node.castTag(.GreaterOrEqual).?, .cmp_gte), | |
| 57 | .LessThan => return cmp(mod, scope, rl, node.castTag(.LessThan).?, .cmp_lt), | |
| 58 | .LessOrEqual => return cmp(mod, scope, rl, node.castTag(.LessOrEqual).?, .cmp_lte), | |
| 59 | ||
| 60 | .Identifier => return rlWrap(mod, scope, rl, try identifier(mod, scope, node.castTag(.Identifier).?)), | |
| 61 | .Asm => return rlWrap(mod, scope, rl, try assembly(mod, scope, node.castTag(.Asm).?)), | |
| 62 | .StringLiteral => return rlWrap(mod, scope, rl, try stringLiteral(mod, scope, node.castTag(.StringLiteral).?)), | |
| 63 | .IntegerLiteral => return rlWrap(mod, scope, rl, try integerLiteral(mod, scope, node.castTag(.IntegerLiteral).?)), | |
| 64 | .BuiltinCall => return builtinCall(mod, scope, rl, node.castTag(.BuiltinCall).?), | |
| 65 | .Call => return callExpr(mod, scope, rl, node.castTag(.Call).?), | |
| 34 | 66 | .Unreachable => return unreach(mod, scope, node.castTag(.Unreachable).?), |
| 35 | .ControlFlowExpression => return controlFlowExpr(mod, scope, node.castTag(.ControlFlowExpression).?), | |
| 36 | .If => return ifExpr(mod, scope, node.castTag(.If).?), | |
| 37 | .Assign => return assign(mod, scope, node.castTag(.Assign).?), | |
| 38 | .Period => return field(mod, scope, node.castTag(.Period).?), | |
| 39 | .Deref => return deref(mod, scope, node.castTag(.Deref).?), | |
| 40 | .BoolNot => return boolNot(mod, scope, node.castTag(.BoolNot).?), | |
| 41 | .FloatLiteral => return floatLiteral(mod, scope, node.castTag(.FloatLiteral).?), | |
| 42 | .UndefinedLiteral, .BoolLiteral, .NullLiteral => return primitiveLiteral(mod, scope, node), | |
| 67 | .Return => return ret(mod, scope, node.castTag(.Return).?), | |
| 68 | .If => return ifExpr(mod, scope, rl, node.castTag(.If).?), | |
| 69 | .Period => return rlWrap(mod, scope, rl, try field(mod, scope, node.castTag(.Period).?)), | |
| 70 | .Deref => return rlWrap(mod, scope, rl, try deref(mod, scope, node.castTag(.Deref).?)), | |
| 71 | .BoolNot => return rlWrap(mod, scope, rl, try boolNot(mod, scope, node.castTag(.BoolNot).?)), | |
| 72 | .FloatLiteral => return rlWrap(mod, scope, rl, try floatLiteral(mod, scope, node.castTag(.FloatLiteral).?)), | |
| 73 | .UndefinedLiteral => return rlWrap(mod, scope, rl, try undefLiteral(mod, scope, node.castTag(.UndefinedLiteral).?)), | |
| 74 | .BoolLiteral => return rlWrap(mod, scope, rl, try boolLiteral(mod, scope, node.castTag(.BoolLiteral).?)), | |
| 75 | .NullLiteral => return rlWrap(mod, scope, rl, try nullLiteral(mod, scope, node.castTag(.NullLiteral).?)), | |
| 43 | 76 | else => return mod.failNode(scope, node, "TODO implement astgen.Expr for {}", .{@tagName(node.tag)}), |
| 44 | 77 | } |
| 45 | 78 | } |
| ... | ... | @@ -59,17 +92,28 @@ pub fn blockExpr(mod: *Module, parent_scope: *Scope, block_node: *ast.Node.Block |
| 59 | 92 | for (block_node.statements()) |statement| { |
| 60 | 93 | switch (statement.tag) { |
| 61 | 94 | .VarDecl => { |
| 62 | const sub_scope = try block_arena.allocator.create(Scope.LocalVar); | |
| 63 | const var_decl_node = @fieldParentPtr(ast.Node.VarDecl, "base", statement); | |
| 64 | sub_scope.* = try varDecl(mod, scope, var_decl_node); | |
| 65 | scope = &sub_scope.base; | |
| 95 | const var_decl_node = statement.castTag(.VarDecl).?; | |
| 96 | scope = try varDecl(mod, scope, var_decl_node, &block_arena.allocator); | |
| 97 | }, | |
| 98 | .Assign => { | |
| 99 | const ass = statement.castTag(.Assign).?; | |
| 100 | try assign(mod, scope, ass); | |
| 101 | }, | |
| 102 | else => { | |
| 103 | const possibly_unused_result = try expr(mod, scope, .none, statement); | |
| 104 | const src = scope.tree().token_locs[statement.firstToken()].start; | |
| 105 | _ = try mod.addZIRUnOp(scope, src, .ensure_result_used, possibly_unused_result); | |
| 66 | 106 | }, |
| 67 | else => _ = try expr(mod, scope, statement), | |
| 68 | 107 | } |
| 69 | 108 | } |
| 70 | 109 | } |
| 71 | 110 | |
| 72 | fn varDecl(mod: *Module, scope: *Scope, node: *ast.Node.VarDecl) InnerError!Scope.LocalVar { | |
| 111 | fn varDecl( | |
| 112 | mod: *Module, | |
| 113 | scope: *Scope, | |
| 114 | node: *ast.Node.VarDecl, | |
| 115 | block_arena: *Allocator, | |
| 116 | ) InnerError!*Scope { | |
| 73 | 117 | // TODO implement detection of shadowing |
| 74 | 118 | if (node.getTrailer("comptime_token")) |comptime_token| { |
| 75 | 119 | return mod.failTok(scope, comptime_token, "TODO implement comptime locals", .{}); |
| ... | ... | @@ -78,48 +122,96 @@ fn varDecl(mod: *Module, scope: *Scope, node: *ast.Node.VarDecl) InnerError!Scop |
| 78 | 122 | return mod.failNode(scope, align_node, "TODO implement alignment on locals", .{}); |
| 79 | 123 | } |
| 80 | 124 | const tree = scope.tree(); |
| 125 | const name_src = tree.token_locs[node.name_token].start; | |
| 126 | const ident_name = try identifierTokenString(mod, scope, node.name_token); | |
| 127 | const init_node = node.getTrailer("init_node").?; | |
| 81 | 128 | switch (tree.token_ids[node.mut_token]) { |
| 82 | 129 | .Keyword_const => { |
| 83 | if (node.getTrailer("type_node")) |type_node| { | |
| 84 | return mod.failNode(scope, type_node, "TODO implement typed const locals", .{}); | |
| 85 | } | |
| 86 | 130 | // Depending on the type of AST the initialization expression is, we may need an lvalue |
| 87 | 131 | // or an rvalue as a result location. If it is an rvalue, we can use the instruction as |
| 88 | 132 | // the variable, no memory location needed. |
| 89 | const init_node = node.getTrailer("init_node").?; | |
| 90 | 133 | if (nodeMayNeedMemoryLocation(init_node)) { |
| 91 | return mod.failNode(scope, init_node, "TODO implement result locations", .{}); | |
| 134 | if (node.getTrailer("type_node")) |type_node| { | |
| 135 | const type_inst = try typeExpr(mod, scope, type_node); | |
| 136 | const alloc = try mod.addZIRUnOp(scope, name_src, .alloc, type_inst); | |
| 137 | const result_loc: ResultLoc = .{ .ptr = alloc }; | |
| 138 | const init_inst = try expr(mod, scope, result_loc, init_node); | |
| 139 | const sub_scope = try block_arena.create(Scope.LocalVal); | |
| 140 | sub_scope.* = .{ | |
| 141 | .parent = scope, | |
| 142 | .gen_zir = scope.getGenZIR(), | |
| 143 | .name = ident_name, | |
| 144 | .inst = init_inst, | |
| 145 | }; | |
| 146 | return &sub_scope.base; | |
| 147 | } else { | |
| 148 | const alloc = try mod.addZIRNoOpT(scope, name_src, .alloc_inferred); | |
| 149 | const result_loc: ResultLoc = .{ .inferred_ptr = alloc }; | |
| 150 | const init_inst = try expr(mod, scope, result_loc, init_node); | |
| 151 | const sub_scope = try block_arena.create(Scope.LocalVal); | |
| 152 | sub_scope.* = .{ | |
| 153 | .parent = scope, | |
| 154 | .gen_zir = scope.getGenZIR(), | |
| 155 | .name = ident_name, | |
| 156 | .inst = init_inst, | |
| 157 | }; | |
| 158 | return &sub_scope.base; | |
| 159 | } | |
| 160 | } else { | |
| 161 | const result_loc: ResultLoc = if (node.getTrailer("type_node")) |type_node| | |
| 162 | .{ .ty = try typeExpr(mod, scope, type_node) } | |
| 163 | else | |
| 164 | .none; | |
| 165 | const init_inst = try expr(mod, scope, result_loc, init_node); | |
| 166 | const sub_scope = try block_arena.create(Scope.LocalVal); | |
| 167 | sub_scope.* = .{ | |
| 168 | .parent = scope, | |
| 169 | .gen_zir = scope.getGenZIR(), | |
| 170 | .name = ident_name, | |
| 171 | .inst = init_inst, | |
| 172 | }; | |
| 173 | return &sub_scope.base; | |
| 92 | 174 | } |
| 93 | const init_inst = try expr(mod, scope, init_node); | |
| 94 | const ident_name = try identifierTokenString(mod, scope, node.name_token); | |
| 95 | return Scope.LocalVar{ | |
| 96 | .parent = scope, | |
| 97 | .gen_zir = scope.getGenZIR(), | |
| 98 | .name = ident_name, | |
| 99 | .inst = init_inst, | |
| 100 | }; | |
| 101 | 175 | }, |
| 102 | 176 | .Keyword_var => { |
| 103 | return mod.failNode(scope, &node.base, "TODO implement local vars", .{}); | |
| 177 | if (node.getTrailer("type_node")) |type_node| { | |
| 178 | const type_inst = try typeExpr(mod, scope, type_node); | |
| 179 | const alloc = try mod.addZIRUnOp(scope, name_src, .alloc, type_inst); | |
| 180 | const result_loc: ResultLoc = .{ .ptr = alloc }; | |
| 181 | const init_inst = try expr(mod, scope, result_loc, init_node); | |
| 182 | const sub_scope = try block_arena.create(Scope.LocalPtr); | |
| 183 | sub_scope.* = .{ | |
| 184 | .parent = scope, | |
| 185 | .gen_zir = scope.getGenZIR(), | |
| 186 | .name = ident_name, | |
| 187 | .ptr = alloc, | |
| 188 | }; | |
| 189 | return &sub_scope.base; | |
| 190 | } else { | |
| 191 | const alloc = try mod.addZIRNoOp(scope, name_src, .alloc_inferred); | |
| 192 | const result_loc = .{ .inferred_ptr = alloc.castTag(.alloc_inferred).? }; | |
| 193 | const init_inst = try expr(mod, scope, result_loc, init_node); | |
| 194 | const sub_scope = try block_arena.create(Scope.LocalPtr); | |
| 195 | sub_scope.* = .{ | |
| 196 | .parent = scope, | |
| 197 | .gen_zir = scope.getGenZIR(), | |
| 198 | .name = ident_name, | |
| 199 | .ptr = alloc, | |
| 200 | }; | |
| 201 | return &sub_scope.base; | |
| 202 | } | |
| 104 | 203 | }, |
| 105 | 204 | else => unreachable, |
| 106 | 205 | } |
| 107 | 206 | } |
| 108 | 207 | |
| 109 | fn boolNot(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst { | |
| 110 | const operand = try expr(mod, scope, node.rhs); | |
| 111 | const tree = scope.tree(); | |
| 112 | const src = tree.token_locs[node.op_token].start; | |
| 113 | return mod.addZIRUnOp(scope, src, .boolnot, operand); | |
| 114 | } | |
| 115 | ||
| 116 | fn assign(mod: *Module, scope: *Scope, infix_node: *ast.Node.SimpleInfixOp) InnerError!*zir.Inst { | |
| 117 | if (infix_node.lhs.tag == .Identifier) { | |
| 118 | const ident = @fieldParentPtr(ast.Node.Identifier, "base", infix_node.lhs); | |
| 208 | fn assign(mod: *Module, scope: *Scope, infix_node: *ast.Node.SimpleInfixOp) InnerError!void { | |
| 209 | if (infix_node.lhs.castTag(.Identifier)) |ident| { | |
| 119 | 210 | const tree = scope.tree(); |
| 120 | 211 | const ident_name = try identifierTokenString(mod, scope, ident.token); |
| 121 | 212 | if (std.mem.eql(u8, ident_name, "_")) { |
| 122 | return expr(mod, scope, infix_node.rhs); | |
| 213 | _ = try expr(mod, scope, .discard, infix_node.rhs); | |
| 214 | return; | |
| 123 | 215 | } else { |
| 124 | 216 | return mod.failNode(scope, &infix_node.base, "TODO implement infix operator assign", .{}); |
| 125 | 217 | } |
| ... | ... | @@ -128,6 +220,17 @@ fn assign(mod: *Module, scope: *Scope, infix_node: *ast.Node.SimpleInfixOp) Inne |
| 128 | 220 | } |
| 129 | 221 | } |
| 130 | 222 | |
| 223 | fn boolNot(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst { | |
| 224 | const tree = scope.tree(); | |
| 225 | const src = tree.token_locs[node.op_token].start; | |
| 226 | const bool_type = try mod.addZIRInstConst(scope, src, .{ | |
| 227 | .ty = Type.initTag(.type), | |
| 228 | .val = Value.initTag(.bool_type), | |
| 229 | }); | |
| 230 | const operand = try expr(mod, scope, .{ .ty = bool_type }, node.rhs); | |
| 231 | return mod.addZIRUnOp(scope, src, .boolnot, operand); | |
| 232 | } | |
| 233 | ||
| 131 | 234 | /// Identifier token -> String (allocated in scope.arena()) |
| 132 | 235 | pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) InnerError![]const u8 { |
| 133 | 236 | const tree = scope.tree(); |
| ... | ... | @@ -148,7 +251,7 @@ pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) |
| 148 | 251 | return ident_name; |
| 149 | 252 | } |
| 150 | 253 | |
| 151 | pub fn identifierStringInst(mod: *Module, scope: *Scope, node: *ast.Node.Identifier) InnerError!*zir.Inst { | |
| 254 | pub fn identifierStringInst(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst { | |
| 152 | 255 | const tree = scope.tree(); |
| 153 | 256 | const src = tree.token_locs[node.token].start; |
| 154 | 257 | |
| ... | ... | @@ -158,10 +261,11 @@ pub fn identifierStringInst(mod: *Module, scope: *Scope, node: *ast.Node.Identif |
| 158 | 261 | } |
| 159 | 262 | |
| 160 | 263 | fn field(mod: *Module, scope: *Scope, node: *ast.Node.SimpleInfixOp) InnerError!*zir.Inst { |
| 264 | // TODO introduce lvalues | |
| 161 | 265 | const tree = scope.tree(); |
| 162 | 266 | const src = tree.token_locs[node.op_token].start; |
| 163 | 267 | |
| 164 | const lhs = try expr(mod, scope, node.lhs); | |
| 268 | const lhs = try expr(mod, scope, .none, node.lhs); | |
| 165 | 269 | const field_name = try identifierStringInst(mod, scope, node.rhs.castTag(.Identifier).?); |
| 166 | 270 | |
| 167 | 271 | const pointer = try mod.addZIRInst(scope, src, zir.Inst.FieldPtr, .{ .object_ptr = lhs, .field_name = field_name }, .{}); |
| ... | ... | @@ -171,26 +275,44 @@ fn field(mod: *Module, scope: *Scope, node: *ast.Node.SimpleInfixOp) InnerError! |
| 171 | 275 | fn deref(mod: *Module, scope: *Scope, node: *ast.Node.SimpleSuffixOp) InnerError!*zir.Inst { |
| 172 | 276 | const tree = scope.tree(); |
| 173 | 277 | const src = tree.token_locs[node.rtoken].start; |
| 174 | const lhs = try expr(mod, scope, node.lhs); | |
| 278 | const lhs = try expr(mod, scope, .none, node.lhs); | |
| 175 | 279 | return mod.addZIRUnOp(scope, src, .deref, lhs); |
| 176 | 280 | } |
| 177 | 281 | |
| 178 | fn simpleInfixOp( | |
| 282 | fn cmp( | |
| 283 | mod: *Module, | |
| 284 | scope: *Scope, | |
| 285 | rl: ResultLoc, | |
| 286 | infix_node: *ast.Node.SimpleInfixOp, | |
| 287 | cmp_inst_tag: zir.Inst.Tag, | |
| 288 | ) InnerError!*zir.Inst { | |
| 289 | const tree = scope.tree(); | |
| 290 | const src = tree.token_locs[infix_node.op_token].start; | |
| 291 | ||
| 292 | const lhs = try expr(mod, scope, .none, infix_node.lhs); | |
| 293 | const rhs = try expr(mod, scope, .none, infix_node.rhs); | |
| 294 | const result = try mod.addZIRBinOp(scope, src, cmp_inst_tag, lhs, rhs); | |
| 295 | return rlWrap(mod, scope, rl, result); | |
| 296 | } | |
| 297 | ||
| 298 | fn arithmetic( | |
| 179 | 299 | mod: *Module, |
| 180 | 300 | scope: *Scope, |
| 301 | rl: ResultLoc, | |
| 181 | 302 | infix_node: *ast.Node.SimpleInfixOp, |
| 182 | 303 | op_inst_tag: zir.Inst.Tag, |
| 183 | 304 | ) InnerError!*zir.Inst { |
| 184 | const lhs = try expr(mod, scope, infix_node.lhs); | |
| 185 | const rhs = try expr(mod, scope, infix_node.rhs); | |
| 305 | const lhs = try expr(mod, scope, .none, infix_node.lhs); | |
| 306 | const rhs = try expr(mod, scope, .none, infix_node.rhs); | |
| 186 | 307 | |
| 187 | 308 | const tree = scope.tree(); |
| 188 | 309 | const src = tree.token_locs[infix_node.op_token].start; |
| 189 | 310 | |
| 190 | return mod.addZIRBinOp(scope, src, op_inst_tag, lhs, rhs); | |
| 311 | const result = try mod.addZIRBinOp(scope, src, op_inst_tag, lhs, rhs); | |
| 312 | return rlWrap(mod, scope, rl, result); | |
| 191 | 313 | } |
| 192 | 314 | |
| 193 | fn ifExpr(mod: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir.Inst { | |
| 315 | fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) InnerError!*zir.Inst { | |
| 194 | 316 | if (if_node.payload) |payload| { |
| 195 | 317 | return mod.failNode(scope, payload, "TODO implement astgen.IfExpr for optionals", .{}); |
| 196 | 318 | } |
| ... | ... | @@ -207,10 +329,14 @@ fn ifExpr(mod: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir.In |
| 207 | 329 | }; |
| 208 | 330 | defer block_scope.instructions.deinit(mod.gpa); |
| 209 | 331 | |
| 210 | const cond = try expr(mod, &block_scope.base, if_node.condition); | |
| 211 | ||
| 212 | 332 | const tree = scope.tree(); |
| 213 | 333 | const if_src = tree.token_locs[if_node.if_token].start; |
| 334 | const bool_type = try mod.addZIRInstConst(scope, if_src, .{ | |
| 335 | .ty = Type.initTag(.type), | |
| 336 | .val = Value.initTag(.bool_type), | |
| 337 | }); | |
| 338 | const cond = try expr(mod, &block_scope.base, .{ .ty = bool_type }, if_node.condition); | |
| 339 | ||
| 214 | 340 | const condbr = try mod.addZIRInstSpecial(&block_scope.base, if_src, zir.Inst.CondBr, .{ |
| 215 | 341 | .condition = cond, |
| 216 | 342 | .then_body = undefined, // populated below |
| ... | ... | @@ -228,7 +354,16 @@ fn ifExpr(mod: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir.In |
| 228 | 354 | }; |
| 229 | 355 | defer then_scope.instructions.deinit(mod.gpa); |
| 230 | 356 | |
| 231 | const then_result = try expr(mod, &then_scope.base, if_node.body); | |
| 357 | // Most result location types can be forwarded directly; however | |
| 358 | // if we need to write to a pointer which has an inferred type, | |
| 359 | // proper type inference requires peer type resolution on the if's | |
| 360 | // branches. | |
| 361 | const branch_rl: ResultLoc = switch (rl) { | |
| 362 | .discard, .none, .ty, .ptr => rl, | |
| 363 | .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block }, | |
| 364 | }; | |
| 365 | ||
| 366 | const then_result = try expr(mod, &then_scope.base, branch_rl, if_node.body); | |
| 232 | 367 | if (!then_result.tag.isNoReturn()) { |
| 233 | 368 | const then_src = tree.token_locs[if_node.body.lastToken()].start; |
| 234 | 369 | _ = try mod.addZIRInst(&then_scope.base, then_src, zir.Inst.Break, .{ |
| ... | ... | @@ -249,7 +384,7 @@ fn ifExpr(mod: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir.In |
| 249 | 384 | defer else_scope.instructions.deinit(mod.gpa); |
| 250 | 385 | |
| 251 | 386 | if (if_node.@"else") |else_node| { |
| 252 | const else_result = try expr(mod, &else_scope.base, else_node.body); | |
| 387 | const else_result = try expr(mod, &else_scope.base, branch_rl, else_node.body); | |
| 253 | 388 | if (!else_result.tag.isNoReturn()) { |
| 254 | 389 | const else_src = tree.token_locs[else_node.body.lastToken()].start; |
| 255 | 390 | _ = try mod.addZIRInst(&else_scope.base, else_src, zir.Inst.Break, .{ |
| ... | ... | @@ -272,27 +407,25 @@ fn ifExpr(mod: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir.In |
| 272 | 407 | return &block.base; |
| 273 | 408 | } |
| 274 | 409 | |
| 275 | fn controlFlowExpr( | |
| 276 | mod: *Module, | |
| 277 | scope: *Scope, | |
| 278 | cfe: *ast.Node.ControlFlowExpression, | |
| 279 | ) InnerError!*zir.Inst { | |
| 280 | switch (cfe.kind) { | |
| 281 | .Break => return mod.failNode(scope, &cfe.base, "TODO implement astgen.Expr for Break", .{}), | |
| 282 | .Continue => return mod.failNode(scope, &cfe.base, "TODO implement astgen.Expr for Continue", .{}), | |
| 283 | .Return => {}, | |
| 284 | } | |
| 410 | fn ret(mod: *Module, scope: *Scope, cfe: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst { | |
| 285 | 411 | const tree = scope.tree(); |
| 286 | 412 | const src = tree.token_locs[cfe.ltoken].start; |
| 287 | if (cfe.rhs) |rhs_node| { | |
| 288 | const operand = try expr(mod, scope, rhs_node); | |
| 289 | return mod.addZIRUnOp(scope, src, .@"return", operand); | |
| 413 | if (cfe.getRHS()) |rhs_node| { | |
| 414 | if (nodeMayNeedMemoryLocation(rhs_node)) { | |
| 415 | const ret_ptr = try mod.addZIRNoOp(scope, src, .ret_ptr); | |
| 416 | const operand = try expr(mod, scope, .{ .ptr = ret_ptr }, rhs_node); | |
| 417 | return mod.addZIRUnOp(scope, src, .@"return", operand); | |
| 418 | } else { | |
| 419 | const fn_ret_ty = try mod.addZIRNoOp(scope, src, .ret_type); | |
| 420 | const operand = try expr(mod, scope, .{ .ty = fn_ret_ty }, rhs_node); | |
| 421 | return mod.addZIRUnOp(scope, src, .@"return", operand); | |
| 422 | } | |
| 290 | 423 | } else { |
| 291 | 424 | return mod.addZIRNoOp(scope, src, .returnvoid); |
| 292 | 425 | } |
| 293 | 426 | } |
| 294 | 427 | |
| 295 | fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.Identifier) InnerError!*zir.Inst { | |
| 428 | fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError!*zir.Inst { | |
| 296 | 429 | const tracy = trace(@src()); |
| 297 | 430 | defer tracy.end(); |
| 298 | 431 | |
| ... | ... | @@ -345,12 +478,19 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.Identifier) InnerErr |
| 345 | 478 | { |
| 346 | 479 | var s = scope; |
| 347 | 480 | while (true) switch (s.tag) { |
| 348 | .local_var => { | |
| 349 | const local_var = s.cast(Scope.LocalVar).?; | |
| 350 | if (mem.eql(u8, local_var.name, ident_name)) { | |
| 351 | return local_var.inst; | |
| 481 | .local_val => { | |
| 482 | const local_val = s.cast(Scope.LocalVal).?; | |
| 483 | if (mem.eql(u8, local_val.name, ident_name)) { | |
| 484 | return local_val.inst; | |
| 485 | } | |
| 486 | s = local_val.parent; | |
| 487 | }, | |
| 488 | .local_ptr => { | |
| 489 | const local_ptr = s.cast(Scope.LocalPtr).?; | |
| 490 | if (mem.eql(u8, local_ptr.name, ident_name)) { | |
| 491 | return try mod.addZIRUnOp(scope, src, .deref, local_ptr.ptr); | |
| 352 | 492 | } |
| 353 | s = local_var.parent; | |
| 493 | s = local_ptr.parent; | |
| 354 | 494 | }, |
| 355 | 495 | .gen_zir => s = s.cast(Scope.GenZIR).?.parent, |
| 356 | 496 | else => break, |
| ... | ... | @@ -364,7 +504,7 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.Identifier) InnerErr |
| 364 | 504 | return mod.failNode(scope, &ident.base, "use of undeclared identifier '{}'", .{ident_name}); |
| 365 | 505 | } |
| 366 | 506 | |
| 367 | fn stringLiteral(mod: *Module, scope: *Scope, str_lit: *ast.Node.StringLiteral) InnerError!*zir.Inst { | |
| 507 | fn stringLiteral(mod: *Module, scope: *Scope, str_lit: *ast.Node.OneToken) InnerError!*zir.Inst { | |
| 368 | 508 | const tree = scope.tree(); |
| 369 | 509 | const unparsed_bytes = tree.tokenSlice(str_lit.token); |
| 370 | 510 | const arena = scope.arena(); |
| ... | ... | @@ -383,7 +523,7 @@ fn stringLiteral(mod: *Module, scope: *Scope, str_lit: *ast.Node.StringLiteral) |
| 383 | 523 | return mod.addZIRInst(scope, src, zir.Inst.Str, .{ .bytes = bytes }, .{}); |
| 384 | 524 | } |
| 385 | 525 | |
| 386 | fn integerLiteral(mod: *Module, scope: *Scope, int_lit: *ast.Node.IntegerLiteral) InnerError!*zir.Inst { | |
| 526 | fn integerLiteral(mod: *Module, scope: *Scope, int_lit: *ast.Node.OneToken) InnerError!*zir.Inst { | |
| 387 | 527 | const arena = scope.arena(); |
| 388 | 528 | const tree = scope.tree(); |
| 389 | 529 | const prefixed_bytes = tree.tokenSlice(int_lit.token); |
| ... | ... | @@ -414,7 +554,7 @@ fn integerLiteral(mod: *Module, scope: *Scope, int_lit: *ast.Node.IntegerLiteral |
| 414 | 554 | } |
| 415 | 555 | } |
| 416 | 556 | |
| 417 | fn floatLiteral(mod: *Module, scope: *Scope, float_lit: *ast.Node.FloatLiteral) InnerError!*zir.Inst { | |
| 557 | fn floatLiteral(mod: *Module, scope: *Scope, float_lit: *ast.Node.OneToken) InnerError!*zir.Inst { | |
| 418 | 558 | const arena = scope.arena(); |
| 419 | 559 | const tree = scope.tree(); |
| 420 | 560 | const bytes = tree.tokenSlice(float_lit.token); |
| ... | ... | @@ -434,30 +574,38 @@ fn floatLiteral(mod: *Module, scope: *Scope, float_lit: *ast.Node.FloatLiteral) |
| 434 | 574 | }); |
| 435 | 575 | } |
| 436 | 576 | |
| 437 | fn primitiveLiteral(mod: *Module, scope: *Scope, node: *ast.Node) InnerError!*zir.Inst { | |
| 577 | fn undefLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst { | |
| 438 | 578 | const arena = scope.arena(); |
| 439 | 579 | const tree = scope.tree(); |
| 440 | const src = tree.token_locs[node.firstToken()].start; | |
| 580 | const src = tree.token_locs[node.token].start; | |
| 581 | return mod.addZIRInstConst(scope, src, .{ | |
| 582 | .ty = Type.initTag(.@"undefined"), | |
| 583 | .val = Value.initTag(.undef), | |
| 584 | }); | |
| 585 | } | |
| 441 | 586 | |
| 442 | if (node.cast(ast.Node.BoolLiteral)) |bool_node| { | |
| 443 | return mod.addZIRInstConst(scope, src, .{ | |
| 444 | .ty = Type.initTag(.bool), | |
| 445 | .val = if (tree.token_ids[bool_node.token] == .Keyword_true) | |
| 446 | Value.initTag(.bool_true) | |
| 447 | else | |
| 448 | Value.initTag(.bool_false), | |
| 449 | }); | |
| 450 | } else if (node.tag == .UndefinedLiteral) { | |
| 451 | return mod.addZIRInstConst(scope, src, .{ | |
| 452 | .ty = Type.initTag(.@"undefined"), | |
| 453 | .val = Value.initTag(.undef), | |
| 454 | }); | |
| 455 | } else if (node.tag == .NullLiteral) { | |
| 456 | return mod.addZIRInstConst(scope, src, .{ | |
| 457 | .ty = Type.initTag(.@"null"), | |
| 458 | .val = Value.initTag(.null_value), | |
| 459 | }); | |
| 460 | } else unreachable; | |
| 587 | fn boolLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst { | |
| 588 | const arena = scope.arena(); | |
| 589 | const tree = scope.tree(); | |
| 590 | const src = tree.token_locs[node.token].start; | |
| 591 | return mod.addZIRInstConst(scope, src, .{ | |
| 592 | .ty = Type.initTag(.bool), | |
| 593 | .val = switch (tree.token_ids[node.token]) { | |
| 594 | .Keyword_true => Value.initTag(.bool_true), | |
| 595 | .Keyword_false => Value.initTag(.bool_false), | |
| 596 | else => unreachable, | |
| 597 | }, | |
| 598 | }); | |
| 599 | } | |
| 600 | ||
| 601 | fn nullLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst { | |
| 602 | const arena = scope.arena(); | |
| 603 | const tree = scope.tree(); | |
| 604 | const src = tree.token_locs[node.token].start; | |
| 605 | return mod.addZIRInstConst(scope, src, .{ | |
| 606 | .ty = Type.initTag(.@"null"), | |
| 607 | .val = Value.initTag(.null_value), | |
| 608 | }); | |
| 461 | 609 | } |
| 462 | 610 | |
| 463 | 611 | fn assembly(mod: *Module, scope: *Scope, asm_node: *ast.Node.Asm) InnerError!*zir.Inst { |
| ... | ... | @@ -470,19 +618,26 @@ fn assembly(mod: *Module, scope: *Scope, asm_node: *ast.Node.Asm) InnerError!*zi |
| 470 | 618 | const inputs = try arena.alloc(*zir.Inst, asm_node.inputs.len); |
| 471 | 619 | const args = try arena.alloc(*zir.Inst, asm_node.inputs.len); |
| 472 | 620 | |
| 621 | const src = tree.token_locs[asm_node.asm_token].start; | |
| 622 | ||
| 623 | const str_type = try mod.addZIRInstConst(scope, src, .{ | |
| 624 | .ty = Type.initTag(.type), | |
| 625 | .val = Value.initTag(.const_slice_u8_type), | |
| 626 | }); | |
| 627 | const str_type_rl: ResultLoc = .{ .ty = str_type }; | |
| 628 | ||
| 473 | 629 | for (asm_node.inputs) |input, i| { |
| 474 | 630 | // TODO semantically analyze constraints |
| 475 | inputs[i] = try expr(mod, scope, input.constraint); | |
| 476 | args[i] = try expr(mod, scope, input.expr); | |
| 631 | inputs[i] = try expr(mod, scope, str_type_rl, input.constraint); | |
| 632 | args[i] = try expr(mod, scope, .none, input.expr); | |
| 477 | 633 | } |
| 478 | 634 | |
| 479 | const src = tree.token_locs[asm_node.asm_token].start; | |
| 480 | 635 | const return_type = try mod.addZIRInstConst(scope, src, .{ |
| 481 | 636 | .ty = Type.initTag(.type), |
| 482 | 637 | .val = Value.initTag(.void_type), |
| 483 | 638 | }); |
| 484 | 639 | const asm_inst = try mod.addZIRInst(scope, src, zir.Inst.Asm, .{ |
| 485 | .asm_source = try expr(mod, scope, asm_node.template), | |
| 640 | .asm_source = try expr(mod, scope, str_type_rl, asm_node.template), | |
| 486 | 641 | .return_type = return_type, |
| 487 | 642 | }, .{ |
| 488 | 643 | .@"volatile" = asm_node.volatile_token != null, |
| ... | ... | @@ -493,63 +648,174 @@ fn assembly(mod: *Module, scope: *Scope, asm_node: *ast.Node.Asm) InnerError!*zi |
| 493 | 648 | return asm_inst; |
| 494 | 649 | } |
| 495 | 650 | |
| 496 | fn builtinCall(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst { | |
| 651 | fn ensureBuiltinParamCount(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall, count: u32) !void { | |
| 652 | if (call.params_len == count) | |
| 653 | return; | |
| 654 | ||
| 655 | const s = if (count == 1) "" else "s"; | |
| 656 | return mod.failTok(scope, call.builtin_token, "expected {} parameter{}, found {}", .{ count, s, call.params_len }); | |
| 657 | } | |
| 658 | ||
| 659 | fn simpleCast( | |
| 660 | mod: *Module, | |
| 661 | scope: *Scope, | |
| 662 | rl: ResultLoc, | |
| 663 | call: *ast.Node.BuiltinCall, | |
| 664 | inst_tag: zir.Inst.Tag, | |
| 665 | ) InnerError!*zir.Inst { | |
| 666 | try ensureBuiltinParamCount(mod, scope, call, 2); | |
| 497 | 667 | const tree = scope.tree(); |
| 498 | const builtin_name = tree.tokenSlice(call.builtin_token); | |
| 499 | 668 | const src = tree.token_locs[call.builtin_token].start; |
| 669 | const type_type = try mod.addZIRInstConst(scope, src, .{ | |
| 670 | .ty = Type.initTag(.type), | |
| 671 | .val = Value.initTag(.type_type), | |
| 672 | }); | |
| 673 | const params = call.params(); | |
| 674 | const dest_type = try expr(mod, scope, .{ .ty = type_type }, params[0]); | |
| 675 | const rhs = try expr(mod, scope, .none, params[1]); | |
| 676 | const result = try mod.addZIRBinOp(scope, src, inst_tag, dest_type, rhs); | |
| 677 | return rlWrap(mod, scope, rl, result); | |
| 678 | } | |
| 500 | 679 | |
| 501 | inline for (std.meta.declarations(zir.Inst)) |inst| { | |
| 502 | if (inst.data != .Type) continue; | |
| 503 | const T = inst.data.Type; | |
| 504 | if (!@hasDecl(T, "builtin_name")) continue; | |
| 505 | if (std.mem.eql(u8, builtin_name, T.builtin_name)) { | |
| 506 | var value: T = undefined; | |
| 507 | const positionals = @typeInfo(std.meta.fieldInfo(T, "positionals").field_type).Struct; | |
| 508 | if (positionals.fields.len == 0) { | |
| 509 | return mod.addZIRInst(scope, src, T, value.positionals, value.kw_args); | |
| 510 | } | |
| 511 | const arg_count: ?usize = if (positionals.fields[0].field_type == []*zir.Inst) null else positionals.fields.len; | |
| 512 | if (arg_count) |some| { | |
| 513 | if (call.params_len != some) { | |
| 514 | return mod.failTok( | |
| 515 | scope, | |
| 516 | call.builtin_token, | |
| 517 | "expected {} parameter{}, found {}", | |
| 518 | .{ some, if (some == 1) "" else "s", call.params_len }, | |
| 519 | ); | |
| 520 | } | |
| 521 | const params = call.params(); | |
| 522 | inline for (positionals.fields) |p, i| { | |
| 523 | @field(value.positionals, p.name) = try expr(mod, scope, params[i]); | |
| 524 | } | |
| 525 | } else { | |
| 526 | return mod.failTok(scope, call.builtin_token, "TODO var args builtin '{}'", .{builtin_name}); | |
| 527 | } | |
| 680 | fn ptrToInt(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst { | |
| 681 | try ensureBuiltinParamCount(mod, scope, call, 1); | |
| 682 | const operand = try expr(mod, scope, .none, call.params()[0]); | |
| 683 | const tree = scope.tree(); | |
| 684 | const src = tree.token_locs[call.builtin_token].start; | |
| 685 | return mod.addZIRUnOp(scope, src, .ptrtoint, operand); | |
| 686 | } | |
| 528 | 687 | |
| 529 | return mod.addZIRInst(scope, src, T, value.positionals, .{}); | |
| 530 | } | |
| 688 | fn as(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst { | |
| 689 | try ensureBuiltinParamCount(mod, scope, call, 2); | |
| 690 | const tree = scope.tree(); | |
| 691 | const src = tree.token_locs[call.builtin_token].start; | |
| 692 | const params = call.params(); | |
| 693 | const dest_type = try typeExpr(mod, scope, params[0]); | |
| 694 | switch (rl) { | |
| 695 | .none => return try expr(mod, scope, .{ .ty = dest_type }, params[1]), | |
| 696 | .discard => { | |
| 697 | const result = try expr(mod, scope, .{ .ty = dest_type }, params[1]); | |
| 698 | _ = try mod.addZIRUnOp(scope, result.src, .ensure_result_non_error, result); | |
| 699 | return result; | |
| 700 | }, | |
| 701 | .ty => |result_ty| { | |
| 702 | const result = try expr(mod, scope, .{ .ty = dest_type }, params[1]); | |
| 703 | return mod.addZIRBinOp(scope, src, .as, result_ty, result); | |
| 704 | }, | |
| 705 | .ptr => |result_ptr| { | |
| 706 | const casted_result_ptr = try mod.addZIRBinOp(scope, src, .coerce_result_ptr, dest_type, result_ptr); | |
| 707 | return expr(mod, scope, .{ .ptr = casted_result_ptr }, params[1]); | |
| 708 | }, | |
| 709 | .bitcasted_ptr => |bitcasted_ptr| { | |
| 710 | // TODO here we should be able to resolve the inference; we now have a type for the result. | |
| 711 | return mod.failTok(scope, call.builtin_token, "TODO implement @as with result location @bitCast", .{}); | |
| 712 | }, | |
| 713 | .inferred_ptr => |result_alloc| { | |
| 714 | // TODO here we should be able to resolve the inference; we now have a type for the result. | |
| 715 | return mod.failTok(scope, call.builtin_token, "TODO implement @as with inferred-type result location pointer", .{}); | |
| 716 | }, | |
| 717 | .block_ptr => |block_ptr| { | |
| 718 | const casted_block_ptr = try mod.addZIRInst(scope, src, zir.Inst.CoerceResultBlockPtr, .{ | |
| 719 | .dest_type = dest_type, | |
| 720 | .block = block_ptr, | |
| 721 | }, .{}); | |
| 722 | return expr(mod, scope, .{ .ptr = casted_block_ptr }, params[1]); | |
| 723 | }, | |
| 724 | } | |
| 725 | } | |
| 726 | ||
| 727 | fn bitCast(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst { | |
| 728 | try ensureBuiltinParamCount(mod, scope, call, 2); | |
| 729 | const tree = scope.tree(); | |
| 730 | const src = tree.token_locs[call.builtin_token].start; | |
| 731 | const type_type = try mod.addZIRInstConst(scope, src, .{ | |
| 732 | .ty = Type.initTag(.type), | |
| 733 | .val = Value.initTag(.type_type), | |
| 734 | }); | |
| 735 | const params = call.params(); | |
| 736 | const dest_type = try expr(mod, scope, .{ .ty = type_type }, params[0]); | |
| 737 | switch (rl) { | |
| 738 | .none => { | |
| 739 | const operand = try expr(mod, scope, .none, params[1]); | |
| 740 | return mod.addZIRBinOp(scope, src, .bitcast, dest_type, operand); | |
| 741 | }, | |
| 742 | .discard => { | |
| 743 | const operand = try expr(mod, scope, .none, params[1]); | |
| 744 | const result = try mod.addZIRBinOp(scope, src, .bitcast, dest_type, operand); | |
| 745 | _ = try mod.addZIRUnOp(scope, result.src, .ensure_result_non_error, result); | |
| 746 | return result; | |
| 747 | }, | |
| 748 | .ty => |result_ty| { | |
| 749 | const result = try expr(mod, scope, .none, params[1]); | |
| 750 | const bitcasted = try mod.addZIRBinOp(scope, src, .bitcast, dest_type, result); | |
| 751 | return mod.addZIRBinOp(scope, src, .as, result_ty, bitcasted); | |
| 752 | }, | |
| 753 | .ptr => |result_ptr| { | |
| 754 | const casted_result_ptr = try mod.addZIRUnOp(scope, src, .bitcast_result_ptr, result_ptr); | |
| 755 | return expr(mod, scope, .{ .bitcasted_ptr = casted_result_ptr.castTag(.bitcast_result_ptr).? }, params[1]); | |
| 756 | }, | |
| 757 | .bitcasted_ptr => |bitcasted_ptr| { | |
| 758 | return mod.failTok(scope, call.builtin_token, "TODO implement @bitCast with result location another @bitCast", .{}); | |
| 759 | }, | |
| 760 | .block_ptr => |block_ptr| { | |
| 761 | return mod.failTok(scope, call.builtin_token, "TODO implement @bitCast with result location inferred peer types", .{}); | |
| 762 | }, | |
| 763 | .inferred_ptr => |result_alloc| { | |
| 764 | // TODO here we should be able to resolve the inference; we now have a type for the result. | |
| 765 | return mod.failTok(scope, call.builtin_token, "TODO implement @bitCast with inferred-type result location pointer", .{}); | |
| 766 | }, | |
| 531 | 767 | } |
| 532 | return mod.failTok(scope, call.builtin_token, "TODO implement builtin call for '{}'", .{builtin_name}); | |
| 533 | 768 | } |
| 534 | 769 | |
| 535 | fn callExpr(mod: *Module, scope: *Scope, node: *ast.Node.Call) InnerError!*zir.Inst { | |
| 770 | fn builtinCall(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst { | |
| 536 | 771 | const tree = scope.tree(); |
| 537 | const lhs = try expr(mod, scope, node.lhs); | |
| 772 | const builtin_name = tree.tokenSlice(call.builtin_token); | |
| 773 | ||
| 774 | // We handle the different builtins manually because they have different semantics depending | |
| 775 | // on the function. For example, `@as` and others participate in result location semantics, | |
| 776 | // and `@cImport` creates a special scope that collects a .c source code text buffer. | |
| 777 | // Also, some builtins have a variable number of parameters. | |
| 778 | ||
| 779 | if (mem.eql(u8, builtin_name, "@ptrToInt")) { | |
| 780 | return rlWrap(mod, scope, rl, try ptrToInt(mod, scope, call)); | |
| 781 | } else if (mem.eql(u8, builtin_name, "@as")) { | |
| 782 | return as(mod, scope, rl, call); | |
| 783 | } else if (mem.eql(u8, builtin_name, "@floatCast")) { | |
| 784 | return simpleCast(mod, scope, rl, call, .floatcast); | |
| 785 | } else if (mem.eql(u8, builtin_name, "@intCast")) { | |
| 786 | return simpleCast(mod, scope, rl, call, .intcast); | |
| 787 | } else if (mem.eql(u8, builtin_name, "@bitCast")) { | |
| 788 | return bitCast(mod, scope, rl, call); | |
| 789 | } else { | |
| 790 | return mod.failTok(scope, call.builtin_token, "invalid builtin function: '{}'", .{builtin_name}); | |
| 791 | } | |
| 792 | } | |
| 793 | ||
| 794 | fn callExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Call) InnerError!*zir.Inst { | |
| 795 | const tree = scope.tree(); | |
| 796 | const lhs = try expr(mod, scope, .none, node.lhs); | |
| 538 | 797 | |
| 539 | 798 | const param_nodes = node.params(); |
| 540 | 799 | const args = try scope.getGenZIR().arena.alloc(*zir.Inst, param_nodes.len); |
| 541 | 800 | for (param_nodes) |param_node, i| { |
| 542 | args[i] = try expr(mod, scope, param_node); | |
| 801 | const param_src = tree.token_locs[param_node.firstToken()].start; | |
| 802 | const param_type = try mod.addZIRInst(scope, param_src, zir.Inst.ParamType, .{ | |
| 803 | .func = lhs, | |
| 804 | .arg_index = i, | |
| 805 | }, .{}); | |
| 806 | args[i] = try expr(mod, scope, .{ .ty = param_type }, param_node); | |
| 543 | 807 | } |
| 544 | 808 | |
| 545 | 809 | const src = tree.token_locs[node.lhs.firstToken()].start; |
| 546 | return mod.addZIRInst(scope, src, zir.Inst.Call, .{ | |
| 810 | const result = try mod.addZIRInst(scope, src, zir.Inst.Call, .{ | |
| 547 | 811 | .func = lhs, |
| 548 | 812 | .args = args, |
| 549 | 813 | }, .{}); |
| 814 | // TODO function call with result location | |
| 815 | return rlWrap(mod, scope, rl, result); | |
| 550 | 816 | } |
| 551 | 817 | |
| 552 | fn unreach(mod: *Module, scope: *Scope, unreach_node: *ast.Node.Unreachable) InnerError!*zir.Inst { | |
| 818 | fn unreach(mod: *Module, scope: *Scope, unreach_node: *ast.Node.OneToken) InnerError!*zir.Inst { | |
| 553 | 819 | const tree = scope.tree(); |
| 554 | 820 | const src = tree.token_locs[unreach_node.token].start; |
| 555 | 821 | return mod.addZIRNoOp(scope, src, .@"unreachable"); |
| ... | ... | @@ -611,7 +877,9 @@ fn nodeMayNeedMemoryLocation(start_node: *ast.Node) bool { |
| 611 | 877 | .FieldInitializer, |
| 612 | 878 | => unreachable, |
| 613 | 879 | |
| 614 | .ControlFlowExpression, | |
| 880 | .Return, | |
| 881 | .Break, | |
| 882 | .Continue, | |
| 615 | 883 | .BitNot, |
| 616 | 884 | .BoolNot, |
| 617 | 885 | .VarDecl, |
| ... | ... | @@ -722,3 +990,39 @@ fn nodeMayNeedMemoryLocation(start_node: *ast.Node) bool { |
| 722 | 990 | } |
| 723 | 991 | } |
| 724 | 992 | } |
| 993 | ||
| 994 | /// Applies `rl` semantics to `inst`. Expressions which do not do their own handling of | |
| 995 | /// result locations must call this function on their result. | |
| 996 | /// As an example, if the `ResultLoc` is `ptr`, it will write the result to the pointer. | |
| 997 | /// If the `ResultLoc` is `ty`, it will coerce the result to the type. | |
| 998 | fn rlWrap(mod: *Module, scope: *Scope, rl: ResultLoc, result: *zir.Inst) InnerError!*zir.Inst { | |
| 999 | switch (rl) { | |
| 1000 | .none => return result, | |
| 1001 | .discard => { | |
| 1002 | // Emit a compile error for discarding error values. | |
| 1003 | _ = try mod.addZIRUnOp(scope, result.src, .ensure_result_non_error, result); | |
| 1004 | return result; | |
| 1005 | }, | |
| 1006 | .ty => |ty_inst| return mod.addZIRBinOp(scope, result.src, .as, ty_inst, result), | |
| 1007 | .ptr => |ptr_inst| { | |
| 1008 | const casted_result = try mod.addZIRInst(scope, result.src, zir.Inst.CoerceToPtrElem, .{ | |
| 1009 | .ptr = ptr_inst, | |
| 1010 | .value = result, | |
| 1011 | }, .{}); | |
| 1012 | _ = try mod.addZIRInst(scope, result.src, zir.Inst.Store, .{ | |
| 1013 | .ptr = ptr_inst, | |
| 1014 | .value = casted_result, | |
| 1015 | }, .{}); | |
| 1016 | return casted_result; | |
| 1017 | }, | |
| 1018 | .bitcasted_ptr => |bitcasted_ptr| { | |
| 1019 | return mod.fail(scope, result.src, "TODO implement rlWrap .bitcasted_ptr", .{}); | |
| 1020 | }, | |
| 1021 | .inferred_ptr => |alloc| { | |
| 1022 | return mod.fail(scope, result.src, "TODO implement rlWrap .inferred_ptr", .{}); | |
| 1023 | }, | |
| 1024 | .block_ptr => |block_ptr| { | |
| 1025 | return mod.fail(scope, result.src, "TODO implement rlWrap .block_ptr", .{}); | |
| 1026 | }, | |
| 1027 | } | |
| 1028 | } |
src-self-hosted/codegen/c.zig+3-17| ... | ... | @@ -90,7 +90,7 @@ fn genFn(file: *C, decl: *Decl) !void { |
| 90 | 90 | const instructions = func.analysis.success.instructions; |
| 91 | 91 | if (instructions.len > 0) { |
| 92 | 92 | for (instructions) |inst| { |
| 93 | try writer.writeAll("\n\t"); | |
| 93 | try writer.writeAll("\n "); | |
| 94 | 94 | switch (inst.tag) { |
| 95 | 95 | .assembly => try genAsm(file, inst.castTag(.assembly).?, decl), |
| 96 | 96 | .call => try genCall(file, inst.castTag(.call).?, decl), |
| ... | ... | @@ -106,21 +106,7 @@ fn genFn(file: *C, decl: *Decl) !void { |
| 106 | 106 | } |
| 107 | 107 | |
| 108 | 108 | fn genRet(file: *C, inst: *Inst.UnOp, decl: *Decl, expected_return_type: Type) !void { |
| 109 | const writer = file.main.writer(); | |
| 110 | const ret_value = inst.operand; | |
| 111 | const value = ret_value.value().?; | |
| 112 | if (expected_return_type.eql(ret_value.ty)) | |
| 113 | return file.fail(decl.src(), "TODO return {}", .{expected_return_type}) | |
| 114 | else if (expected_return_type.isInt() and ret_value.ty.tag() == .comptime_int) | |
| 115 | if (value.intFitsInType(expected_return_type, file.options.target)) | |
| 116 | if (expected_return_type.intInfo(file.options.target).bits <= 64) | |
| 117 | try writer.print("return {};", .{value.toUnsignedInt()}) | |
| 118 | else | |
| 119 | return file.fail(decl.src(), "TODO return ints > 64 bits", .{}) | |
| 120 | else | |
| 121 | return file.fail(decl.src(), "comptime int {} does not fit in {}", .{ value.toUnsignedInt(), expected_return_type }) | |
| 122 | else | |
| 123 | return file.fail(decl.src(), "return type mismatch: expected {}, found {}", .{ expected_return_type, ret_value.ty }); | |
| 109 | return file.fail(decl.src(), "TODO return {}", .{expected_return_type}); | |
| 124 | 110 | } |
| 125 | 111 | |
| 126 | 112 | fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void { |
| ... | ... | @@ -162,7 +148,7 @@ fn genAsm(file: *C, as: *Inst.Assembly, decl: *Decl) !void { |
| 162 | 148 | if (c.val.tag() == .int_u64) { |
| 163 | 149 | try writer.writeAll("register "); |
| 164 | 150 | try renderType(file, writer, arg.ty, decl.src()); |
| 165 | try writer.print(" {}_constant __asm__(\"{}\") = {};\n\t", .{ reg, reg, c.val.toUnsignedInt() }); | |
| 151 | try writer.print(" {}_constant __asm__(\"{}\") = {};\n ", .{ reg, reg, c.val.toUnsignedInt() }); | |
| 166 | 152 | } else { |
| 167 | 153 | return file.fail(decl.src(), "TODO inline asm {} args", .{c.val.tag()}); |
| 168 | 154 | } |
src-self-hosted/link.zig+4| ... | ... | @@ -1579,6 +1579,8 @@ pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !Fi |
| 1579 | 1579 | .elf => {}, |
| 1580 | 1580 | .macho => return error.TODOImplementWritingMachO, |
| 1581 | 1581 | .wasm => return error.TODOImplementWritingWasmObjects, |
| 1582 | .hex => return error.TODOImplementWritingHex, | |
| 1583 | .raw => return error.TODOImplementWritingRaw, | |
| 1582 | 1584 | } |
| 1583 | 1585 | |
| 1584 | 1586 | var self: File.Elf = .{ |
| ... | ... | @@ -1638,6 +1640,8 @@ fn openBinFileInner(allocator: *Allocator, file: fs.File, options: Options) !Fil |
| 1638 | 1640 | .elf => {}, |
| 1639 | 1641 | .macho => return error.IncrFailed, |
| 1640 | 1642 | .wasm => return error.IncrFailed, |
| 1643 | .hex => return error.IncrFailed, | |
| 1644 | .raw => return error.IncrFailed, | |
| 1641 | 1645 | } |
| 1642 | 1646 | var self: File.Elf = .{ |
| 1643 | 1647 | .allocator = allocator, |
src-self-hosted/main.zig+38-10| ... | ... | @@ -141,11 +141,19 @@ const usage_build_generic = |
| 141 | 141 | \\ --name [name] Override output name |
| 142 | 142 | \\ --mode [mode] Set the build mode |
| 143 | 143 | \\ Debug (default) optimizations off, safety on |
| 144 | \\ ReleaseFast optimizations on, safety off | |
| 145 | \\ ReleaseSafe optimizations on, safety on | |
| 146 | \\ ReleaseSmall optimize for small binary, safety off | |
| 144 | \\ ReleaseFast Optimizations on, safety off | |
| 145 | \\ ReleaseSafe Optimizations on, safety on | |
| 146 | \\ ReleaseSmall Optimize for small binary, safety off | |
| 147 | 147 | \\ --dynamic Force output to be dynamically linked |
| 148 | 148 | \\ --strip Exclude debug symbols |
| 149 | \\ -ofmt=[mode] Override target object format | |
| 150 | \\ elf Executable and Linking Format | |
| 151 | \\ c Compile to C source code | |
| 152 | \\ coff (planned) Common Object File Format (Windows) | |
| 153 | \\ pe (planned) Portable Executable (Windows) | |
| 154 | \\ macho (planned) macOS relocatables | |
| 155 | \\ hex (planned) Intel IHEX | |
| 156 | \\ raw (planned) Dump machine code directly | |
| 149 | 157 | \\ |
| 150 | 158 | \\Link Options: |
| 151 | 159 | \\ -l[lib], --library [lib] Link against system library |
| ... | ... | @@ -195,7 +203,7 @@ fn buildOutputType( |
| 195 | 203 | var target_arch_os_abi: []const u8 = "native"; |
| 196 | 204 | var target_mcpu: ?[]const u8 = null; |
| 197 | 205 | var target_dynamic_linker: ?[]const u8 = null; |
| 198 | var object_format: ?std.builtin.ObjectFormat = null; | |
| 206 | var target_ofmt: ?[]const u8 = null; | |
| 199 | 207 | |
| 200 | 208 | var system_libs = std.ArrayList([]const u8).init(gpa); |
| 201 | 209 | defer system_libs.deinit(); |
| ... | ... | @@ -282,12 +290,8 @@ fn buildOutputType( |
| 282 | 290 | } |
| 283 | 291 | i += 1; |
| 284 | 292 | target_mcpu = args[i]; |
| 285 | } else if (mem.eql(u8, arg, "--c")) { | |
| 286 | if (object_format) |old| { | |
| 287 | std.debug.print("attempted to override object format {} with C\n", .{old}); | |
| 288 | process.exit(1); | |
| 289 | } | |
| 290 | object_format = .c; | |
| 293 | } else if (mem.startsWith(u8, arg, "-ofmt=")) { | |
| 294 | target_ofmt = arg["-ofmt=".len..]; | |
| 291 | 295 | } else if (mem.startsWith(u8, arg, "-mcpu=")) { |
| 292 | 296 | target_mcpu = arg["-mcpu=".len..]; |
| 293 | 297 | } else if (mem.eql(u8, arg, "--dynamic-linker")) { |
| ... | ... | @@ -434,6 +438,30 @@ fn buildOutputType( |
| 434 | 438 | process.exit(1); |
| 435 | 439 | }; |
| 436 | 440 | |
| 441 | const object_format: ?std.Target.ObjectFormat = blk: { | |
| 442 | const ofmt = target_ofmt orelse break :blk null; | |
| 443 | if (mem.eql(u8, ofmt, "elf")) { | |
| 444 | break :blk .elf; | |
| 445 | } else if (mem.eql(u8, ofmt, "c")) { | |
| 446 | break :blk .c; | |
| 447 | } else if (mem.eql(u8, ofmt, "coff")) { | |
| 448 | break :blk .coff; | |
| 449 | } else if (mem.eql(u8, ofmt, "pe")) { | |
| 450 | break :blk .coff; | |
| 451 | } else if (mem.eql(u8, ofmt, "macho")) { | |
| 452 | break :blk .macho; | |
| 453 | } else if (mem.eql(u8, ofmt, "wasm")) { | |
| 454 | break :blk .wasm; | |
| 455 | } else if (mem.eql(u8, ofmt, "hex")) { | |
| 456 | break :blk .hex; | |
| 457 | } else if (mem.eql(u8, ofmt, "raw")) { | |
| 458 | break :blk .raw; | |
| 459 | } else { | |
| 460 | std.debug.print("unsupported object format: {}", .{ofmt}); | |
| 461 | process.exit(1); | |
| 462 | } | |
| 463 | }; | |
| 464 | ||
| 437 | 465 | const bin_path = switch (emit_bin) { |
| 438 | 466 | .no => { |
| 439 | 467 | std.debug.print("-fno-emit-bin not supported yet", .{}); |
src-self-hosted/translate_c.zig+165-91| ... | ... | @@ -1308,8 +1308,7 @@ fn transBinaryOperator( |
| 1308 | 1308 | const rhs = try transExpr(rp, &block_scope.base, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value); |
| 1309 | 1309 | if (expr) { |
| 1310 | 1310 | _ = try appendToken(rp.c, .Semicolon, ";"); |
| 1311 | const break_node = try transCreateNodeBreakToken(rp.c, block_scope.label); | |
| 1312 | break_node.rhs = rhs; | |
| 1311 | const break_node = try transCreateNodeBreakToken(rp.c, block_scope.label, rhs); | |
| 1313 | 1312 | try block_scope.statements.append(&break_node.base); |
| 1314 | 1313 | const block_node = try block_scope.complete(rp.c); |
| 1315 | 1314 | const rparen = try appendToken(rp.c, .RParen, ")"); |
| ... | ... | @@ -1881,12 +1880,19 @@ fn transReturnStmt( |
| 1881 | 1880 | scope: *Scope, |
| 1882 | 1881 | expr: *const ZigClangReturnStmt, |
| 1883 | 1882 | ) TransError!*ast.Node { |
| 1884 | const node = try transCreateNodeReturnExpr(rp.c); | |
| 1885 | if (ZigClangReturnStmt_getRetValue(expr)) |val_expr| { | |
| 1886 | node.rhs = try transExprCoercing(rp, scope, val_expr, .used, .r_value); | |
| 1887 | } | |
| 1883 | const return_kw = try appendToken(rp.c, .Keyword_return, "return"); | |
| 1884 | const rhs: ?*ast.Node = if (ZigClangReturnStmt_getRetValue(expr)) |val_expr| | |
| 1885 | try transExprCoercing(rp, scope, val_expr, .used, .r_value) | |
| 1886 | else | |
| 1887 | null; | |
| 1888 | const return_expr = try ast.Node.ControlFlowExpression.create(rp.c.arena, .{ | |
| 1889 | .ltoken = return_kw, | |
| 1890 | .tag = .Return, | |
| 1891 | }, .{ | |
| 1892 | .rhs = rhs, | |
| 1893 | }); | |
| 1888 | 1894 | _ = try appendToken(rp.c, .Semicolon, ";"); |
| 1889 | return &node.base; | |
| 1895 | return &return_expr.base; | |
| 1890 | 1896 | } |
| 1891 | 1897 | |
| 1892 | 1898 | fn transStringLiteral( |
| ... | ... | @@ -1912,8 +1918,9 @@ fn transStringLiteral( |
| 1912 | 1918 | buf[buf.len - 1] = '"'; |
| 1913 | 1919 | |
| 1914 | 1920 | const token = try appendToken(rp.c, .StringLiteral, buf); |
| 1915 | const node = try rp.c.arena.create(ast.Node.StringLiteral); | |
| 1921 | const node = try rp.c.arena.create(ast.Node.OneToken); | |
| 1916 | 1922 | node.* = .{ |
| 1923 | .base = .{ .tag = .StringLiteral }, | |
| 1917 | 1924 | .token = token, |
| 1918 | 1925 | }; |
| 1919 | 1926 | return maybeSuppressResult(rp, scope, result_used, &node.base); |
| ... | ... | @@ -2518,7 +2525,7 @@ fn transDoWhileLoop( |
| 2518 | 2525 | prefix_op.rhs = try transBoolExpr(rp, &cond_scope.base, @ptrCast(*const ZigClangExpr, ZigClangDoStmt_getCond(stmt)), .used, .r_value, true); |
| 2519 | 2526 | _ = try appendToken(rp.c, .RParen, ")"); |
| 2520 | 2527 | if_node.condition = &prefix_op.base; |
| 2521 | if_node.body = &(try transCreateNodeBreak(rp.c, null)).base; | |
| 2528 | if_node.body = &(try transCreateNodeBreak(rp.c, null, null)).base; | |
| 2522 | 2529 | _ = try appendToken(rp.c, .Semicolon, ";"); |
| 2523 | 2530 | |
| 2524 | 2531 | const body_node = if (ZigClangStmt_getStmtClass(ZigClangDoStmt_getBody(stmt)) == .CompoundStmtClass) blk: { |
| ... | ... | @@ -2688,7 +2695,7 @@ fn transSwitch( |
| 2688 | 2695 | _ = try appendToken(rp.c, .Colon, ":"); |
| 2689 | 2696 | if (!switch_scope.has_default) { |
| 2690 | 2697 | const else_prong = try transCreateNodeSwitchCase(rp.c, try transCreateNodeSwitchElse(rp.c)); |
| 2691 | else_prong.expr = &(try transCreateNodeBreak(rp.c, "__switch")).base; | |
| 2698 | else_prong.expr = &(try transCreateNodeBreak(rp.c, "__switch", null)).base; | |
| 2692 | 2699 | _ = try appendToken(rp.c, .Comma, ","); |
| 2693 | 2700 | |
| 2694 | 2701 | if (switch_scope.case_index >= switch_scope.cases.len) |
| ... | ... | @@ -2732,7 +2739,7 @@ fn transCase( |
| 2732 | 2739 | try transExpr(rp, scope, ZigClangCaseStmt_getLHS(stmt), .used, .r_value); |
| 2733 | 2740 | |
| 2734 | 2741 | const switch_prong = try transCreateNodeSwitchCase(rp.c, expr); |
| 2735 | switch_prong.expr = &(try transCreateNodeBreak(rp.c, label)).base; | |
| 2742 | switch_prong.expr = &(try transCreateNodeBreak(rp.c, label, null)).base; | |
| 2736 | 2743 | _ = try appendToken(rp.c, .Comma, ","); |
| 2737 | 2744 | |
| 2738 | 2745 | if (switch_scope.case_index >= switch_scope.cases.len) |
| ... | ... | @@ -2768,7 +2775,7 @@ fn transDefault( |
| 2768 | 2775 | _ = try appendToken(rp.c, .Semicolon, ";"); |
| 2769 | 2776 | |
| 2770 | 2777 | const else_prong = try transCreateNodeSwitchCase(rp.c, try transCreateNodeSwitchElse(rp.c)); |
| 2771 | else_prong.expr = &(try transCreateNodeBreak(rp.c, label)).base; | |
| 2778 | else_prong.expr = &(try transCreateNodeBreak(rp.c, label, null)).base; | |
| 2772 | 2779 | _ = try appendToken(rp.c, .Comma, ","); |
| 2773 | 2780 | |
| 2774 | 2781 | if (switch_scope.case_index >= switch_scope.cases.len) |
| ... | ... | @@ -2843,8 +2850,9 @@ fn transCharLiteral( |
| 2843 | 2850 | } |
| 2844 | 2851 | var char_buf: [4]u8 = undefined; |
| 2845 | 2852 | const token = try appendTokenFmt(rp.c, .CharLiteral, "'{}'", .{escapeChar(@intCast(u8, val), &char_buf)}); |
| 2846 | const node = try rp.c.arena.create(ast.Node.CharLiteral); | |
| 2853 | const node = try rp.c.arena.create(ast.Node.OneToken); | |
| 2847 | 2854 | node.* = .{ |
| 2855 | .base = .{ .tag = .CharLiteral }, | |
| 2848 | 2856 | .token = token, |
| 2849 | 2857 | }; |
| 2850 | 2858 | break :blk &node.base; |
| ... | ... | @@ -2889,8 +2897,11 @@ fn transStmtExpr(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangStmtExpr, |
| 2889 | 2897 | const result = try transStmt(rp, &block_scope.base, it[0], .unused, .r_value); |
| 2890 | 2898 | try block_scope.statements.append(result); |
| 2891 | 2899 | } |
| 2892 | const break_node = try transCreateNodeBreak(rp.c, "blk"); | |
| 2893 | break_node.rhs = try transStmt(rp, &block_scope.base, it[0], .used, .r_value); | |
| 2900 | const break_node = blk: { | |
| 2901 | var tmp = try CtrlFlow.init(rp.c, .Break, "blk"); | |
| 2902 | const rhs = try transStmt(rp, &block_scope.base, it[0], .used, .r_value); | |
| 2903 | break :blk try tmp.finish(rhs); | |
| 2904 | }; | |
| 2894 | 2905 | _ = try appendToken(rp.c, .Semicolon, ";"); |
| 2895 | 2906 | try block_scope.statements.append(&break_node.base); |
| 2896 | 2907 | const block_node = try block_scope.complete(rp.c); |
| ... | ... | @@ -3205,8 +3216,7 @@ fn transCreatePreCrement( |
| 3205 | 3216 | const assign = try transCreateNodeInfixOp(rp, scope, ref_node, op, token, one, .used, false); |
| 3206 | 3217 | try block_scope.statements.append(assign); |
| 3207 | 3218 | |
| 3208 | const break_node = try transCreateNodeBreakToken(rp.c, block_scope.label); | |
| 3209 | break_node.rhs = ref_node; | |
| 3219 | const break_node = try transCreateNodeBreakToken(rp.c, block_scope.label, ref_node); | |
| 3210 | 3220 | try block_scope.statements.append(&break_node.base); |
| 3211 | 3221 | const block_node = try block_scope.complete(rp.c); |
| 3212 | 3222 | // semicolon must immediately follow rbrace because it is the last token in a block |
| ... | ... | @@ -3297,8 +3307,11 @@ fn transCreatePostCrement( |
| 3297 | 3307 | const assign = try transCreateNodeInfixOp(rp, scope, ref_node, op, token, one, .used, false); |
| 3298 | 3308 | try block_scope.statements.append(assign); |
| 3299 | 3309 | |
| 3300 | const break_node = try transCreateNodeBreakToken(rp.c, block_scope.label); | |
| 3301 | break_node.rhs = try transCreateNodeIdentifier(rp.c, tmp); | |
| 3310 | const break_node = blk: { | |
| 3311 | var tmp_ctrl_flow = try CtrlFlow.initToken(rp.c, .Break, block_scope.label); | |
| 3312 | const rhs = try transCreateNodeIdentifier(rp.c, tmp); | |
| 3313 | break :blk try tmp_ctrl_flow.finish(rhs); | |
| 3314 | }; | |
| 3302 | 3315 | try block_scope.statements.append(&break_node.base); |
| 3303 | 3316 | _ = try appendToken(rp.c, .Semicolon, ";"); |
| 3304 | 3317 | const block_node = try block_scope.complete(rp.c); |
| ... | ... | @@ -3490,8 +3503,7 @@ fn transCreateCompoundAssign( |
| 3490 | 3503 | try block_scope.statements.append(assign); |
| 3491 | 3504 | } |
| 3492 | 3505 | |
| 3493 | const break_node = try transCreateNodeBreakToken(rp.c, block_scope.label); | |
| 3494 | break_node.rhs = ref_node; | |
| 3506 | const break_node = try transCreateNodeBreakToken(rp.c, block_scope.label, ref_node); | |
| 3495 | 3507 | try block_scope.statements.append(&break_node.base); |
| 3496 | 3508 | const block_node = try block_scope.complete(rp.c); |
| 3497 | 3509 | const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression); |
| ... | ... | @@ -3567,10 +3579,8 @@ fn transCPtrCast( |
| 3567 | 3579 | |
| 3568 | 3580 | fn transBreak(rp: RestorePoint, scope: *Scope) TransError!*ast.Node { |
| 3569 | 3581 | const break_scope = scope.getBreakableScope(); |
| 3570 | const br = try transCreateNodeBreak(rp.c, if (break_scope.id == .Switch) | |
| 3571 | "__switch" | |
| 3572 | else | |
| 3573 | null); | |
| 3582 | const label_text: ?[]const u8 = if (break_scope.id == .Switch) "__switch" else null; | |
| 3583 | const br = try transCreateNodeBreak(rp.c, label_text, null); | |
| 3574 | 3584 | _ = try appendToken(rp.c, .Semicolon, ";"); |
| 3575 | 3585 | return &br.base; |
| 3576 | 3586 | } |
| ... | ... | @@ -3578,8 +3588,9 @@ fn transBreak(rp: RestorePoint, scope: *Scope) TransError!*ast.Node { |
| 3578 | 3588 | fn transFloatingLiteral(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangFloatingLiteral, used: ResultUsed) TransError!*ast.Node { |
| 3579 | 3589 | // TODO use something more accurate |
| 3580 | 3590 | const dbl = ZigClangAPFloat_getValueAsApproximateDouble(stmt); |
| 3581 | const node = try rp.c.arena.create(ast.Node.FloatLiteral); | |
| 3591 | const node = try rp.c.arena.create(ast.Node.OneToken); | |
| 3582 | 3592 | node.* = .{ |
| 3593 | .base = .{ .tag = .FloatLiteral }, | |
| 3583 | 3594 | .token = try appendTokenFmt(rp.c, .FloatLiteral, "{d}", .{dbl}), |
| 3584 | 3595 | }; |
| 3585 | 3596 | return maybeSuppressResult(rp, scope, used, &node.base); |
| ... | ... | @@ -3619,7 +3630,7 @@ fn transBinaryConditionalOperator(rp: RestorePoint, scope: *Scope, stmt: *const |
| 3619 | 3630 | }); |
| 3620 | 3631 | try block_scope.statements.append(&tmp_var.base); |
| 3621 | 3632 | |
| 3622 | const break_node = try transCreateNodeBreakToken(rp.c, block_scope.label); | |
| 3633 | var break_node_tmp = try CtrlFlow.initToken(rp.c, .Break, block_scope.label); | |
| 3623 | 3634 | |
| 3624 | 3635 | const if_node = try transCreateNodeIf(rp.c); |
| 3625 | 3636 | var cond_scope = Scope.Condition{ |
| ... | ... | @@ -3641,7 +3652,7 @@ fn transBinaryConditionalOperator(rp: RestorePoint, scope: *Scope, stmt: *const |
| 3641 | 3652 | if_node.@"else".?.body = try transExpr(rp, &block_scope.base, false_expr, .used, .r_value); |
| 3642 | 3653 | _ = try appendToken(rp.c, .Semicolon, ";"); |
| 3643 | 3654 | |
| 3644 | break_node.rhs = &if_node.base; | |
| 3655 | const break_node = try break_node_tmp.finish(&if_node.base); | |
| 3645 | 3656 | _ = try appendToken(rp.c, .Semicolon, ";"); |
| 3646 | 3657 | try block_scope.statements.append(&break_node.base); |
| 3647 | 3658 | const block_node = try block_scope.complete(rp.c); |
| ... | ... | @@ -3822,8 +3833,9 @@ fn qualTypeToLog2IntRef(rp: RestorePoint, qt: ZigClangQualType, source_loc: ZigC |
| 3822 | 3833 | if (int_bit_width != 0) { |
| 3823 | 3834 | // we can perform the log2 now. |
| 3824 | 3835 | const cast_bit_width = math.log2_int(u64, int_bit_width); |
| 3825 | const node = try rp.c.arena.create(ast.Node.IntegerLiteral); | |
| 3836 | const node = try rp.c.arena.create(ast.Node.OneToken); | |
| 3826 | 3837 | node.* = .{ |
| 3838 | .base = .{ .tag = .IntegerLiteral }, | |
| 3827 | 3839 | .token = try appendTokenFmt(rp.c, .Identifier, "u{}", .{cast_bit_width}), |
| 3828 | 3840 | }; |
| 3829 | 3841 | return &node.base; |
| ... | ... | @@ -3845,8 +3857,9 @@ fn qualTypeToLog2IntRef(rp: RestorePoint, qt: ZigClangQualType, source_loc: ZigC |
| 3845 | 3857 | |
| 3846 | 3858 | const import_fn_call = try rp.c.createBuiltinCall("@import", 1); |
| 3847 | 3859 | const std_token = try appendToken(rp.c, .StringLiteral, "\"std\""); |
| 3848 | const std_node = try rp.c.arena.create(ast.Node.StringLiteral); | |
| 3860 | const std_node = try rp.c.arena.create(ast.Node.OneToken); | |
| 3849 | 3861 | std_node.* = .{ |
| 3862 | .base = .{ .tag = .StringLiteral }, | |
| 3850 | 3863 | .token = std_token, |
| 3851 | 3864 | }; |
| 3852 | 3865 | import_fn_call.params()[0] = &std_node.base; |
| ... | ... | @@ -4081,8 +4094,11 @@ fn transCreateNodeAssign( |
| 4081 | 4094 | const assign = try transCreateNodeInfixOp(rp, &block_scope.base, lhs_node, .Assign, lhs_eq_token, ident, .used, false); |
| 4082 | 4095 | try block_scope.statements.append(assign); |
| 4083 | 4096 | |
| 4084 | const break_node = try transCreateNodeBreak(rp.c, label_name); | |
| 4085 | break_node.rhs = try transCreateNodeIdentifier(rp.c, tmp); | |
| 4097 | const break_node = blk: { | |
| 4098 | var tmp_ctrl_flow = try CtrlFlow.init(rp.c, .Break, label_name); | |
| 4099 | const rhs_expr = try transCreateNodeIdentifier(rp.c, tmp); | |
| 4100 | break :blk try tmp_ctrl_flow.finish(rhs_expr); | |
| 4101 | }; | |
| 4086 | 4102 | _ = try appendToken(rp.c, .Semicolon, ";"); |
| 4087 | 4103 | try block_scope.statements.append(&break_node.base); |
| 4088 | 4104 | const block_node = try block_scope.complete(rp.c); |
| ... | ... | @@ -4255,28 +4271,19 @@ fn transCreateNodeAPInt(c: *Context, int: *const ZigClangAPSInt) !*ast.Node { |
| 4255 | 4271 | }; |
| 4256 | 4272 | defer c.arena.free(str); |
| 4257 | 4273 | const token = try appendToken(c, .IntegerLiteral, str); |
| 4258 | const node = try c.arena.create(ast.Node.IntegerLiteral); | |
| 4274 | const node = try c.arena.create(ast.Node.OneToken); | |
| 4259 | 4275 | node.* = .{ |
| 4276 | .base = .{ .tag = .IntegerLiteral }, | |
| 4260 | 4277 | .token = token, |
| 4261 | 4278 | }; |
| 4262 | 4279 | return &node.base; |
| 4263 | 4280 | } |
| 4264 | 4281 | |
| 4265 | fn transCreateNodeReturnExpr(c: *Context) !*ast.Node.ControlFlowExpression { | |
| 4266 | const ltoken = try appendToken(c, .Keyword_return, "return"); | |
| 4267 | const node = try c.arena.create(ast.Node.ControlFlowExpression); | |
| 4268 | node.* = .{ | |
| 4269 | .ltoken = ltoken, | |
| 4270 | .kind = .Return, | |
| 4271 | .rhs = null, | |
| 4272 | }; | |
| 4273 | return node; | |
| 4274 | } | |
| 4275 | ||
| 4276 | 4282 | fn transCreateNodeUndefinedLiteral(c: *Context) !*ast.Node { |
| 4277 | 4283 | const token = try appendToken(c, .Keyword_undefined, "undefined"); |
| 4278 | const node = try c.arena.create(ast.Node.UndefinedLiteral); | |
| 4284 | const node = try c.arena.create(ast.Node.OneToken); | |
| 4279 | 4285 | node.* = .{ |
| 4286 | .base = .{ .tag = .UndefinedLiteral }, | |
| 4280 | 4287 | .token = token, |
| 4281 | 4288 | }; |
| 4282 | 4289 | return &node.base; |
| ... | ... | @@ -4284,8 +4291,9 @@ fn transCreateNodeUndefinedLiteral(c: *Context) !*ast.Node { |
| 4284 | 4291 | |
| 4285 | 4292 | fn transCreateNodeNullLiteral(c: *Context) !*ast.Node { |
| 4286 | 4293 | const token = try appendToken(c, .Keyword_null, "null"); |
| 4287 | const node = try c.arena.create(ast.Node.NullLiteral); | |
| 4294 | const node = try c.arena.create(ast.Node.OneToken); | |
| 4288 | 4295 | node.* = .{ |
| 4296 | .base = .{ .tag = .NullLiteral }, | |
| 4289 | 4297 | .token = token, |
| 4290 | 4298 | }; |
| 4291 | 4299 | return &node.base; |
| ... | ... | @@ -4296,8 +4304,9 @@ fn transCreateNodeBoolLiteral(c: *Context, value: bool) !*ast.Node { |
| 4296 | 4304 | try appendToken(c, .Keyword_true, "true") |
| 4297 | 4305 | else |
| 4298 | 4306 | try appendToken(c, .Keyword_false, "false"); |
| 4299 | const node = try c.arena.create(ast.Node.BoolLiteral); | |
| 4307 | const node = try c.arena.create(ast.Node.OneToken); | |
| 4300 | 4308 | node.* = .{ |
| 4309 | .base = .{ .tag = .BoolLiteral }, | |
| 4301 | 4310 | .token = token, |
| 4302 | 4311 | }; |
| 4303 | 4312 | return &node.base; |
| ... | ... | @@ -4305,8 +4314,9 @@ fn transCreateNodeBoolLiteral(c: *Context, value: bool) !*ast.Node { |
| 4305 | 4314 | |
| 4306 | 4315 | fn transCreateNodeInt(c: *Context, int: anytype) !*ast.Node { |
| 4307 | 4316 | const token = try appendTokenFmt(c, .IntegerLiteral, "{}", .{int}); |
| 4308 | const node = try c.arena.create(ast.Node.IntegerLiteral); | |
| 4317 | const node = try c.arena.create(ast.Node.OneToken); | |
| 4309 | 4318 | node.* = .{ |
| 4319 | .base = .{ .tag = .IntegerLiteral }, | |
| 4310 | 4320 | .token = token, |
| 4311 | 4321 | }; |
| 4312 | 4322 | return &node.base; |
| ... | ... | @@ -4314,8 +4324,9 @@ fn transCreateNodeInt(c: *Context, int: anytype) !*ast.Node { |
| 4314 | 4324 | |
| 4315 | 4325 | fn transCreateNodeFloat(c: *Context, int: anytype) !*ast.Node { |
| 4316 | 4326 | const token = try appendTokenFmt(c, .FloatLiteral, "{}", .{int}); |
| 4317 | const node = try c.arena.create(ast.Node.FloatLiteral); | |
| 4327 | const node = try c.arena.create(ast.Node.OneToken); | |
| 4318 | 4328 | node.* = .{ |
| 4329 | .base = .{ .tag = .FloatLiteral }, | |
| 4319 | 4330 | .token = token, |
| 4320 | 4331 | }; |
| 4321 | 4332 | return &node.base; |
| ... | ... | @@ -4362,7 +4373,7 @@ fn transCreateNodeMacroFn(c: *Context, name: []const u8, ref: *ast.Node, proto_a |
| 4362 | 4373 | |
| 4363 | 4374 | const block_lbrace = try appendToken(c, .LBrace, "{"); |
| 4364 | 4375 | |
| 4365 | const return_expr = try transCreateNodeReturnExpr(c); | |
| 4376 | const return_kw = try appendToken(c, .Keyword_return, "return"); | |
| 4366 | 4377 | const unwrap_expr = try transCreateNodeUnwrapNull(c, ref.cast(ast.Node.VarDecl).?.getTrailer("init_node").?); |
| 4367 | 4378 | |
| 4368 | 4379 | const call_expr = try c.createCall(unwrap_expr, fn_params.items.len); |
| ... | ... | @@ -4376,7 +4387,12 @@ fn transCreateNodeMacroFn(c: *Context, name: []const u8, ref: *ast.Node, proto_a |
| 4376 | 4387 | } |
| 4377 | 4388 | call_expr.rtoken = try appendToken(c, .RParen, ")"); |
| 4378 | 4389 | |
| 4379 | return_expr.rhs = &call_expr.base; | |
| 4390 | const return_expr = try ast.Node.ControlFlowExpression.create(c.arena, .{ | |
| 4391 | .ltoken = return_kw, | |
| 4392 | .tag = .Return, | |
| 4393 | }, .{ | |
| 4394 | .rhs = &call_expr.base, | |
| 4395 | }); | |
| 4380 | 4396 | _ = try appendToken(c, .Semicolon, ";"); |
| 4381 | 4397 | |
| 4382 | 4398 | const block = try ast.Node.Block.alloc(c.arena, 1); |
| ... | ... | @@ -4424,8 +4440,9 @@ fn transCreateNodeEnumLiteral(c: *Context, name: []const u8) !*ast.Node { |
| 4424 | 4440 | } |
| 4425 | 4441 | |
| 4426 | 4442 | fn transCreateNodeStringLiteral(c: *Context, str: []const u8) !*ast.Node { |
| 4427 | const node = try c.arena.create(ast.Node.StringLiteral); | |
| 4443 | const node = try c.arena.create(ast.Node.OneToken); | |
| 4428 | 4444 | node.* = .{ |
| 4445 | .base = .{ .tag = .StringLiteral }, | |
| 4429 | 4446 | .token = try appendToken(c, .StringLiteral, str), |
| 4430 | 4447 | }; |
| 4431 | 4448 | return &node.base; |
| ... | ... | @@ -4455,28 +4472,77 @@ fn transCreateNodeElse(c: *Context) !*ast.Node.Else { |
| 4455 | 4472 | return node; |
| 4456 | 4473 | } |
| 4457 | 4474 | |
| 4458 | fn transCreateNodeBreakToken(c: *Context, label: ?ast.TokenIndex) !*ast.Node.ControlFlowExpression { | |
| 4459 | const other_token = label orelse return transCreateNodeBreak(c, null); | |
| 4475 | fn transCreateNodeBreakToken( | |
| 4476 | c: *Context, | |
| 4477 | label: ?ast.TokenIndex, | |
| 4478 | rhs: ?*ast.Node, | |
| 4479 | ) !*ast.Node.ControlFlowExpression { | |
| 4480 | const other_token = label orelse return transCreateNodeBreak(c, null, rhs); | |
| 4460 | 4481 | const loc = c.token_locs.items[other_token]; |
| 4461 | 4482 | const label_name = c.source_buffer.items[loc.start..loc.end]; |
| 4462 | return transCreateNodeBreak(c, label_name); | |
| 4483 | return transCreateNodeBreak(c, label_name, rhs); | |
| 4463 | 4484 | } |
| 4464 | 4485 | |
| 4465 | fn transCreateNodeBreak(c: *Context, label: ?[]const u8) !*ast.Node.ControlFlowExpression { | |
| 4466 | const ltoken = try appendToken(c, .Keyword_break, "break"); | |
| 4467 | const label_node = if (label) |l| blk: { | |
| 4468 | _ = try appendToken(c, .Colon, ":"); | |
| 4469 | break :blk try transCreateNodeIdentifier(c, l); | |
| 4470 | } else null; | |
| 4471 | const node = try c.arena.create(ast.Node.ControlFlowExpression); | |
| 4472 | node.* = .{ | |
| 4473 | .ltoken = ltoken, | |
| 4474 | .kind = .{ .Break = label_node }, | |
| 4475 | .rhs = null, | |
| 4476 | }; | |
| 4477 | return node; | |
| 4486 | fn transCreateNodeBreak( | |
| 4487 | c: *Context, | |
| 4488 | label: ?[]const u8, | |
| 4489 | rhs: ?*ast.Node, | |
| 4490 | ) !*ast.Node.ControlFlowExpression { | |
| 4491 | var ctrl_flow = try CtrlFlow.init(c, .Break, label); | |
| 4492 | return ctrl_flow.finish(rhs); | |
| 4478 | 4493 | } |
| 4479 | 4494 | |
| 4495 | const CtrlFlow = struct { | |
| 4496 | c: *Context, | |
| 4497 | ltoken: ast.TokenIndex, | |
| 4498 | label_token: ?ast.TokenIndex, | |
| 4499 | tag: ast.Node.Tag, | |
| 4500 | ||
| 4501 | /// Does everything except the RHS. | |
| 4502 | fn init(c: *Context, tag: ast.Node.Tag, label: ?[]const u8) !CtrlFlow { | |
| 4503 | const kw: Token.Id = switch (tag) { | |
| 4504 | .Break => .Keyword_break, | |
| 4505 | .Continue => .Keyword_continue, | |
| 4506 | .Return => .Keyword_return, | |
| 4507 | else => unreachable, | |
| 4508 | }; | |
| 4509 | const kw_text = switch (tag) { | |
| 4510 | .Break => "break", | |
| 4511 | .Continue => "continue", | |
| 4512 | .Return => "return", | |
| 4513 | else => unreachable, | |
| 4514 | }; | |
| 4515 | const ltoken = try appendToken(c, kw, kw_text); | |
| 4516 | const label_token = if (label) |l| blk: { | |
| 4517 | _ = try appendToken(c, .Colon, ":"); | |
| 4518 | break :blk try appendToken(c, .Identifier, l); | |
| 4519 | } else null; | |
| 4520 | return CtrlFlow{ | |
| 4521 | .c = c, | |
| 4522 | .ltoken = ltoken, | |
| 4523 | .label_token = label_token, | |
| 4524 | .tag = tag, | |
| 4525 | }; | |
| 4526 | } | |
| 4527 | ||
| 4528 | fn initToken(c: *Context, tag: ast.Node.Tag, label: ?ast.TokenIndex) !CtrlFlow { | |
| 4529 | const other_token = label orelse return init(c, tag, null); | |
| 4530 | const loc = c.token_locs.items[other_token]; | |
| 4531 | const label_name = c.source_buffer.items[loc.start..loc.end]; | |
| 4532 | return init(c, tag, label_name); | |
| 4533 | } | |
| 4534 | ||
| 4535 | fn finish(self: *CtrlFlow, rhs: ?*ast.Node) !*ast.Node.ControlFlowExpression { | |
| 4536 | return ast.Node.ControlFlowExpression.create(self.c.arena, .{ | |
| 4537 | .ltoken = self.ltoken, | |
| 4538 | .tag = self.tag, | |
| 4539 | }, .{ | |
| 4540 | .label = self.label_token, | |
| 4541 | .rhs = rhs, | |
| 4542 | }); | |
| 4543 | } | |
| 4544 | }; | |
| 4545 | ||
| 4480 | 4546 | fn transCreateNodeWhile(c: *Context) !*ast.Node.While { |
| 4481 | 4547 | const while_tok = try appendToken(c, .Keyword_while, "while"); |
| 4482 | 4548 | _ = try appendToken(c, .LParen, "("); |
| ... | ... | @@ -4497,12 +4563,10 @@ fn transCreateNodeWhile(c: *Context) !*ast.Node.While { |
| 4497 | 4563 | |
| 4498 | 4564 | fn transCreateNodeContinue(c: *Context) !*ast.Node { |
| 4499 | 4565 | const ltoken = try appendToken(c, .Keyword_continue, "continue"); |
| 4500 | const node = try c.arena.create(ast.Node.ControlFlowExpression); | |
| 4501 | node.* = .{ | |
| 4566 | const node = try ast.Node.ControlFlowExpression.create(c.arena, .{ | |
| 4502 | 4567 | .ltoken = ltoken, |
| 4503 | .kind = .{ .Continue = null }, | |
| 4504 | .rhs = null, | |
| 4505 | }; | |
| 4568 | .tag = .Continue, | |
| 4569 | }, .{}); | |
| 4506 | 4570 | _ = try appendToken(c, .Semicolon, ";"); |
| 4507 | 4571 | return &node.base; |
| 4508 | 4572 | } |
| ... | ... | @@ -5006,8 +5070,9 @@ pub fn failDecl(c: *Context, loc: ZigClangSourceLocation, name: []const u8, comp |
| 5006 | 5070 | const semi_tok = try appendToken(c, .Semicolon, ";"); |
| 5007 | 5071 | _ = try appendTokenFmt(c, .LineComment, "// {}", .{c.locStr(loc)}); |
| 5008 | 5072 | |
| 5009 | const msg_node = try c.arena.create(ast.Node.StringLiteral); | |
| 5073 | const msg_node = try c.arena.create(ast.Node.OneToken); | |
| 5010 | 5074 | msg_node.* = .{ |
| 5075 | .base = .{ .tag = .StringLiteral }, | |
| 5011 | 5076 | .token = msg_tok, |
| 5012 | 5077 | }; |
| 5013 | 5078 | |
| ... | ... | @@ -5110,8 +5175,9 @@ fn appendIdentifier(c: *Context, name: []const u8) !ast.TokenIndex { |
| 5110 | 5175 | |
| 5111 | 5176 | fn transCreateNodeIdentifier(c: *Context, name: []const u8) !*ast.Node { |
| 5112 | 5177 | const token_index = try appendIdentifier(c, name); |
| 5113 | const identifier = try c.arena.create(ast.Node.Identifier); | |
| 5178 | const identifier = try c.arena.create(ast.Node.OneToken); | |
| 5114 | 5179 | identifier.* = .{ |
| 5180 | .base = .{ .tag = .Identifier }, | |
| 5115 | 5181 | .token = token_index, |
| 5116 | 5182 | }; |
| 5117 | 5183 | return &identifier.base; |
| ... | ... | @@ -5119,8 +5185,9 @@ fn transCreateNodeIdentifier(c: *Context, name: []const u8) !*ast.Node { |
| 5119 | 5185 | |
| 5120 | 5186 | fn transCreateNodeIdentifierUnchecked(c: *Context, name: []const u8) !*ast.Node { |
| 5121 | 5187 | const token_index = try appendTokenFmt(c, .Identifier, "{}", .{name}); |
| 5122 | const identifier = try c.arena.create(ast.Node.Identifier); | |
| 5188 | const identifier = try c.arena.create(ast.Node.OneToken); | |
| 5123 | 5189 | identifier.* = .{ |
| 5190 | .base = .{ .tag = .Identifier }, | |
| 5124 | 5191 | .token = token_index, |
| 5125 | 5192 | }; |
| 5126 | 5193 | return &identifier.base; |
| ... | ... | @@ -5289,8 +5356,9 @@ fn transMacroFnDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8, |
| 5289 | 5356 | const param_name_tok = try appendIdentifier(c, mangled_name); |
| 5290 | 5357 | _ = try appendToken(c, .Colon, ":"); |
| 5291 | 5358 | |
| 5292 | const any_type = try c.arena.create(ast.Node.AnyType); | |
| 5359 | const any_type = try c.arena.create(ast.Node.OneToken); | |
| 5293 | 5360 | any_type.* = .{ |
| 5361 | .base = .{ .tag = .AnyType }, | |
| 5294 | 5362 | .token = try appendToken(c, .Keyword_anytype, "anytype"), |
| 5295 | 5363 | }; |
| 5296 | 5364 | |
| ... | ... | @@ -5322,7 +5390,7 @@ fn transMacroFnDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8, |
| 5322 | 5390 | |
| 5323 | 5391 | const type_of = try c.createBuiltinCall("@TypeOf", 1); |
| 5324 | 5392 | |
| 5325 | const return_expr = try transCreateNodeReturnExpr(c); | |
| 5393 | const return_kw = try appendToken(c, .Keyword_return, "return"); | |
| 5326 | 5394 | const expr = try parseCExpr(c, it, source, source_loc, scope); |
| 5327 | 5395 | const last = it.next().?; |
| 5328 | 5396 | if (last.id != .Eof and last.id != .Nl) |
| ... | ... | @@ -5337,13 +5405,17 @@ fn transMacroFnDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8, |
| 5337 | 5405 | const type_of_arg = if (expr.tag != .Block) expr else blk: { |
| 5338 | 5406 | const blk = @fieldParentPtr(ast.Node.Block, "base", expr); |
| 5339 | 5407 | const blk_last = blk.statements()[blk.statements_len - 1]; |
| 5340 | std.debug.assert(blk_last.tag == .ControlFlowExpression); | |
| 5341 | const br = @fieldParentPtr(ast.Node.ControlFlowExpression, "base", blk_last); | |
| 5342 | break :blk br.rhs.?; | |
| 5408 | const br = blk_last.cast(ast.Node.ControlFlowExpression).?; | |
| 5409 | break :blk br.getRHS().?; | |
| 5343 | 5410 | }; |
| 5344 | 5411 | type_of.params()[0] = type_of_arg; |
| 5345 | 5412 | type_of.rparen_token = try appendToken(c, .RParen, ")"); |
| 5346 | return_expr.rhs = expr; | |
| 5413 | const return_expr = try ast.Node.ControlFlowExpression.create(c.arena, .{ | |
| 5414 | .ltoken = return_kw, | |
| 5415 | .tag = .Return, | |
| 5416 | }, .{ | |
| 5417 | .rhs = expr, | |
| 5418 | }); | |
| 5347 | 5419 | |
| 5348 | 5420 | try block_scope.statements.append(&return_expr.base); |
| 5349 | 5421 | const block_node = try block_scope.complete(c); |
| ... | ... | @@ -5416,8 +5488,7 @@ fn parseCExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8, source_ |
| 5416 | 5488 | } |
| 5417 | 5489 | } |
| 5418 | 5490 | |
| 5419 | const break_node = try transCreateNodeBreak(c, label_name); | |
| 5420 | break_node.rhs = last; | |
| 5491 | const break_node = try transCreateNodeBreak(c, label_name, last); | |
| 5421 | 5492 | try block_scope.statements.append(&break_node.base); |
| 5422 | 5493 | const block_node = try block_scope.complete(c); |
| 5423 | 5494 | return &block_node.base; |
| ... | ... | @@ -5656,15 +5727,17 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8, |
| 5656 | 5727 | const first_tok = it.list.at(0); |
| 5657 | 5728 | if (source[tok.start] != '\'' or source[tok.start + 1] == '\\' or tok.end - tok.start == 3) { |
| 5658 | 5729 | const token = try appendToken(c, .CharLiteral, try zigifyEscapeSequences(c, source[tok.start..tok.end], source[first_tok.start..first_tok.end], source_loc)); |
| 5659 | const node = try c.arena.create(ast.Node.CharLiteral); | |
| 5730 | const node = try c.arena.create(ast.Node.OneToken); | |
| 5660 | 5731 | node.* = .{ |
| 5732 | .base = .{ .tag = .CharLiteral }, | |
| 5661 | 5733 | .token = token, |
| 5662 | 5734 | }; |
| 5663 | 5735 | return &node.base; |
| 5664 | 5736 | } else { |
| 5665 | 5737 | const token = try appendTokenFmt(c, .IntegerLiteral, "0x{x}", .{source[tok.start + 1 .. tok.end - 1]}); |
| 5666 | const node = try c.arena.create(ast.Node.IntegerLiteral); | |
| 5738 | const node = try c.arena.create(ast.Node.OneToken); | |
| 5667 | 5739 | node.* = .{ |
| 5740 | .base = .{ .tag = .IntegerLiteral }, | |
| 5668 | 5741 | .token = token, |
| 5669 | 5742 | }; |
| 5670 | 5743 | return &node.base; |
| ... | ... | @@ -5673,8 +5746,9 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8, |
| 5673 | 5746 | .StringLiteral => { |
| 5674 | 5747 | const first_tok = it.list.at(0); |
| 5675 | 5748 | const token = try appendToken(c, .StringLiteral, try zigifyEscapeSequences(c, source[tok.start..tok.end], source[first_tok.start..first_tok.end], source_loc)); |
| 5676 | const node = try c.arena.create(ast.Node.StringLiteral); | |
| 5749 | const node = try c.arena.create(ast.Node.OneToken); | |
| 5677 | 5750 | node.* = .{ |
| 5751 | .base = .{ .tag = .StringLiteral }, | |
| 5678 | 5752 | .token = token, |
| 5679 | 5753 | }; |
| 5680 | 5754 | return &node.base; |
| ... | ... | @@ -6224,7 +6298,7 @@ fn getContainer(c: *Context, node: *ast.Node) ?*ast.Node { |
| 6224 | 6298 | => return node, |
| 6225 | 6299 | |
| 6226 | 6300 | .Identifier => { |
| 6227 | const ident = node.cast(ast.Node.Identifier).?; | |
| 6301 | const ident = node.castTag(.Identifier).?; | |
| 6228 | 6302 | if (c.global_scope.sym_table.get(tokenSlice(c, ident.token))) |value| { |
| 6229 | 6303 | if (value.cast(ast.Node.VarDecl)) |var_decl| |
| 6230 | 6304 | return getContainer(c, var_decl.getTrailer("init_node").?); |
| ... | ... | @@ -6238,7 +6312,7 @@ fn getContainer(c: *Context, node: *ast.Node) ?*ast.Node { |
| 6238 | 6312 | if (ty_node.cast(ast.Node.ContainerDecl)) |container| { |
| 6239 | 6313 | for (container.fieldsAndDecls()) |field_ref| { |
| 6240 | 6314 | const field = field_ref.cast(ast.Node.ContainerField).?; |
| 6241 | const ident = infix.rhs.cast(ast.Node.Identifier).?; | |
| 6315 | const ident = infix.rhs.castTag(.Identifier).?; | |
| 6242 | 6316 | if (mem.eql(u8, tokenSlice(c, field.name_token), tokenSlice(c, ident.token))) { |
| 6243 | 6317 | return getContainer(c, field.type_expr.?); |
| 6244 | 6318 | } |
| ... | ... | @@ -6253,7 +6327,7 @@ fn getContainer(c: *Context, node: *ast.Node) ?*ast.Node { |
| 6253 | 6327 | } |
| 6254 | 6328 | |
| 6255 | 6329 | fn getContainerTypeOf(c: *Context, ref: *ast.Node) ?*ast.Node { |
| 6256 | if (ref.cast(ast.Node.Identifier)) |ident| { | |
| 6330 | if (ref.castTag(.Identifier)) |ident| { | |
| 6257 | 6331 | if (c.global_scope.sym_table.get(tokenSlice(c, ident.token))) |value| { |
| 6258 | 6332 | if (value.cast(ast.Node.VarDecl)) |var_decl| { |
| 6259 | 6333 | if (var_decl.getTrailer("type_node")) |ty| |
| ... | ... | @@ -6265,7 +6339,7 @@ fn getContainerTypeOf(c: *Context, ref: *ast.Node) ?*ast.Node { |
| 6265 | 6339 | if (ty_node.cast(ast.Node.ContainerDecl)) |container| { |
| 6266 | 6340 | for (container.fieldsAndDecls()) |field_ref| { |
| 6267 | 6341 | const field = field_ref.cast(ast.Node.ContainerField).?; |
| 6268 | const ident = infix.rhs.cast(ast.Node.Identifier).?; | |
| 6342 | const ident = infix.rhs.castTag(.Identifier).?; | |
| 6269 | 6343 | if (mem.eql(u8, tokenSlice(c, field.name_token), tokenSlice(c, ident.token))) { |
| 6270 | 6344 | return getContainer(c, field.type_expr.?); |
| 6271 | 6345 | } |
src-self-hosted/zir.zig+116-75| ... | ... | @@ -34,9 +34,17 @@ pub const Inst = struct { |
| 34 | 34 | |
| 35 | 35 | /// These names are used directly as the instruction names in the text format. |
| 36 | 36 | pub const Tag = enum { |
| 37 | /// Allocates stack local memory. Its lifetime ends when the block ends that contains | |
| 38 | /// this instruction. | |
| 39 | alloc, | |
| 40 | /// Same as `alloc` except the type is inferred. | |
| 41 | alloc_inferred, | |
| 37 | 42 | /// Function parameter value. These must be first in a function's main block, |
| 38 | 43 | /// in respective order with the parameters. |
| 39 | 44 | arg, |
| 45 | /// A typed result location pointer is bitcasted to a new result location pointer. | |
| 46 | /// The new result location pointer has an inferred type. | |
| 47 | bitcast_result_ptr, | |
| 40 | 48 | /// A labeled block of code, which can return a value. |
| 41 | 49 | block, |
| 42 | 50 | /// Return a value from a `Block`. |
| ... | ... | @@ -45,6 +53,17 @@ pub const Inst = struct { |
| 45 | 53 | /// Same as `break` but without an operand; the operand is assumed to be the void value. |
| 46 | 54 | breakvoid, |
| 47 | 55 | call, |
| 56 | /// Coerces a result location pointer to a new element type. It is evaluated "backwards"- | |
| 57 | /// as type coercion from the new element type to the old element type. | |
| 58 | /// LHS is destination element type, RHS is result pointer. | |
| 59 | coerce_result_ptr, | |
| 60 | /// This instruction does a `coerce_result_ptr` operation on a `Block`'s | |
| 61 | /// result location pointer, whose type is inferred by peer type resolution on the | |
| 62 | /// `Block`'s corresponding `break` instructions. | |
| 63 | coerce_result_block_ptr, | |
| 64 | /// Equivalent to `as(ptr_child_type(typeof(ptr)), value)`. | |
| 65 | coerce_to_ptr_elem, | |
| 66 | /// Emit an error message and fail compilation. | |
| 48 | 67 | compileerror, |
| 49 | 68 | /// Special case, has no textual representation. |
| 50 | 69 | @"const", |
| ... | ... | @@ -57,7 +76,17 @@ pub const Inst = struct { |
| 57 | 76 | declval, |
| 58 | 77 | /// Same as declval but the parameter is a `*Module.Decl` rather than a name. |
| 59 | 78 | declval_in_module, |
| 79 | /// Emits a compile error if the operand is not `void`. | |
| 80 | ensure_result_used, | |
| 81 | /// Emits a compile error if an error is ignored. | |
| 82 | ensure_result_non_error, | |
| 60 | 83 | boolnot, |
| 84 | /// Obtains a pointer to the return value. | |
| 85 | ret_ptr, | |
| 86 | /// Obtains the return type of the in-scope function. | |
| 87 | ret_type, | |
| 88 | /// Write a value to a pointer. | |
| 89 | store, | |
| 61 | 90 | /// String Literal. Makes an anonymous Decl and then takes a pointer to it. |
| 62 | 91 | str, |
| 63 | 92 | int, |
| ... | ... | @@ -73,6 +102,9 @@ pub const Inst = struct { |
| 73 | 102 | @"fn", |
| 74 | 103 | fntype, |
| 75 | 104 | @"export", |
| 105 | /// Given a reference to a function and a parameter index, returns the | |
| 106 | /// type of the parameter. TODO what happens when the parameter is `anytype`? | |
| 107 | param_type, | |
| 76 | 108 | primitive, |
| 77 | 109 | intcast, |
| 78 | 110 | bitcast, |
| ... | ... | @@ -96,6 +128,9 @@ pub const Inst = struct { |
| 96 | 128 | .breakpoint, |
| 97 | 129 | .@"unreachable", |
| 98 | 130 | .returnvoid, |
| 131 | .alloc_inferred, | |
| 132 | .ret_ptr, | |
| 133 | .ret_type, | |
| 99 | 134 | => NoOp, |
| 100 | 135 | |
| 101 | 136 | .boolnot, |
| ... | ... | @@ -103,6 +138,11 @@ pub const Inst = struct { |
| 103 | 138 | .@"return", |
| 104 | 139 | .isnull, |
| 105 | 140 | .isnonnull, |
| 141 | .ptrtoint, | |
| 142 | .alloc, | |
| 143 | .ensure_result_used, | |
| 144 | .ensure_result_non_error, | |
| 145 | .bitcast_result_ptr, | |
| 106 | 146 | => UnOp, |
| 107 | 147 | |
| 108 | 148 | .add, |
| ... | ... | @@ -113,32 +153,36 @@ pub const Inst = struct { |
| 113 | 153 | .cmp_gte, |
| 114 | 154 | .cmp_gt, |
| 115 | 155 | .cmp_neq, |
| 156 | .as, | |
| 157 | .floatcast, | |
| 158 | .intcast, | |
| 159 | .bitcast, | |
| 160 | .coerce_result_ptr, | |
| 116 | 161 | => BinOp, |
| 117 | 162 | |
| 118 | 163 | .block => Block, |
| 119 | 164 | .@"break" => Break, |
| 120 | 165 | .breakvoid => BreakVoid, |
| 121 | 166 | .call => Call, |
| 167 | .coerce_to_ptr_elem => CoerceToPtrElem, | |
| 122 | 168 | .declref => DeclRef, |
| 123 | 169 | .declref_str => DeclRefStr, |
| 124 | 170 | .declval => DeclVal, |
| 125 | 171 | .declval_in_module => DeclValInModule, |
| 172 | .coerce_result_block_ptr => CoerceResultBlockPtr, | |
| 126 | 173 | .compileerror => CompileError, |
| 127 | 174 | .@"const" => Const, |
| 175 | .store => Store, | |
| 128 | 176 | .str => Str, |
| 129 | 177 | .int => Int, |
| 130 | 178 | .inttype => IntType, |
| 131 | .ptrtoint => PtrToInt, | |
| 132 | 179 | .fieldptr => FieldPtr, |
| 133 | .as => As, | |
| 134 | 180 | .@"asm" => Asm, |
| 135 | 181 | .@"fn" => Fn, |
| 136 | 182 | .@"export" => Export, |
| 183 | .param_type => ParamType, | |
| 137 | 184 | .primitive => Primitive, |
| 138 | 185 | .fntype => FnType, |
| 139 | .intcast => IntCast, | |
| 140 | .bitcast => BitCast, | |
| 141 | .floatcast => FloatCast, | |
| 142 | 186 | .elemptr => ElemPtr, |
| 143 | 187 | .condbr => CondBr, |
| 144 | 188 | }; |
| ... | ... | @@ -148,15 +192,26 @@ pub const Inst = struct { |
| 148 | 192 | /// Function calls do not count. |
| 149 | 193 | pub fn isNoReturn(tag: Tag) bool { |
| 150 | 194 | return switch (tag) { |
| 195 | .alloc, | |
| 196 | .alloc_inferred, | |
| 151 | 197 | .arg, |
| 198 | .bitcast_result_ptr, | |
| 152 | 199 | .block, |
| 153 | 200 | .breakpoint, |
| 154 | 201 | .call, |
| 202 | .coerce_result_ptr, | |
| 203 | .coerce_result_block_ptr, | |
| 204 | .coerce_to_ptr_elem, | |
| 155 | 205 | .@"const", |
| 156 | 206 | .declref, |
| 157 | 207 | .declref_str, |
| 158 | 208 | .declval, |
| 159 | 209 | .declval_in_module, |
| 210 | .ensure_result_used, | |
| 211 | .ensure_result_non_error, | |
| 212 | .ret_ptr, | |
| 213 | .ret_type, | |
| 214 | .store, | |
| 160 | 215 | .str, |
| 161 | 216 | .int, |
| 162 | 217 | .inttype, |
| ... | ... | @@ -168,6 +223,7 @@ pub const Inst = struct { |
| 168 | 223 | .@"fn", |
| 169 | 224 | .fntype, |
| 170 | 225 | .@"export", |
| 226 | .param_type, | |
| 171 | 227 | .primitive, |
| 172 | 228 | .intcast, |
| 173 | 229 | .bitcast, |
| ... | ... | @@ -292,6 +348,17 @@ pub const Inst = struct { |
| 292 | 348 | }, |
| 293 | 349 | }; |
| 294 | 350 | |
| 351 | pub const CoerceToPtrElem = struct { | |
| 352 | pub const base_tag = Tag.coerce_to_ptr_elem; | |
| 353 | base: Inst, | |
| 354 | ||
| 355 | positionals: struct { | |
| 356 | ptr: *Inst, | |
| 357 | value: *Inst, | |
| 358 | }, | |
| 359 | kw_args: struct {}, | |
| 360 | }; | |
| 361 | ||
| 295 | 362 | pub const DeclRef = struct { |
| 296 | 363 | pub const base_tag = Tag.declref; |
| 297 | 364 | base: Inst, |
| ... | ... | @@ -332,6 +399,17 @@ pub const Inst = struct { |
| 332 | 399 | kw_args: struct {}, |
| 333 | 400 | }; |
| 334 | 401 | |
| 402 | pub const CoerceResultBlockPtr = struct { | |
| 403 | pub const base_tag = Tag.coerce_result_block_ptr; | |
| 404 | base: Inst, | |
| 405 | ||
| 406 | positionals: struct { | |
| 407 | dest_type: *Inst, | |
| 408 | block: *Block, | |
| 409 | }, | |
| 410 | kw_args: struct {}, | |
| 411 | }; | |
| 412 | ||
| 335 | 413 | pub const CompileError = struct { |
| 336 | 414 | pub const base_tag = Tag.compileerror; |
| 337 | 415 | base: Inst, |
| ... | ... | @@ -352,33 +430,33 @@ pub const Inst = struct { |
| 352 | 430 | kw_args: struct {}, |
| 353 | 431 | }; |
| 354 | 432 | |
| 355 | pub const Str = struct { | |
| 356 | pub const base_tag = Tag.str; | |
| 433 | pub const Store = struct { | |
| 434 | pub const base_tag = Tag.store; | |
| 357 | 435 | base: Inst, |
| 358 | 436 | |
| 359 | 437 | positionals: struct { |
| 360 | bytes: []const u8, | |
| 438 | ptr: *Inst, | |
| 439 | value: *Inst, | |
| 361 | 440 | }, |
| 362 | 441 | kw_args: struct {}, |
| 363 | 442 | }; |
| 364 | 443 | |
| 365 | pub const Int = struct { | |
| 366 | pub const base_tag = Tag.int; | |
| 444 | pub const Str = struct { | |
| 445 | pub const base_tag = Tag.str; | |
| 367 | 446 | base: Inst, |
| 368 | 447 | |
| 369 | 448 | positionals: struct { |
| 370 | int: BigIntConst, | |
| 449 | bytes: []const u8, | |
| 371 | 450 | }, |
| 372 | 451 | kw_args: struct {}, |
| 373 | 452 | }; |
| 374 | 453 | |
| 375 | pub const PtrToInt = struct { | |
| 376 | pub const builtin_name = "@ptrToInt"; | |
| 377 | pub const base_tag = Tag.ptrtoint; | |
| 454 | pub const Int = struct { | |
| 455 | pub const base_tag = Tag.int; | |
| 378 | 456 | base: Inst, |
| 379 | 457 | |
| 380 | 458 | positionals: struct { |
| 381 | operand: *Inst, | |
| 459 | int: BigIntConst, | |
| 382 | 460 | }, |
| 383 | 461 | kw_args: struct {}, |
| 384 | 462 | }; |
| ... | ... | @@ -394,18 +472,6 @@ pub const Inst = struct { |
| 394 | 472 | kw_args: struct {}, |
| 395 | 473 | }; |
| 396 | 474 | |
| 397 | pub const As = struct { | |
| 398 | pub const base_tag = Tag.as; | |
| 399 | pub const builtin_name = "@as"; | |
| 400 | base: Inst, | |
| 401 | ||
| 402 | positionals: struct { | |
| 403 | dest_type: *Inst, | |
| 404 | value: *Inst, | |
| 405 | }, | |
| 406 | kw_args: struct {}, | |
| 407 | }; | |
| 408 | ||
| 409 | 475 | pub const Asm = struct { |
| 410 | 476 | pub const base_tag = Tag.@"asm"; |
| 411 | 477 | base: Inst, |
| ... | ... | @@ -469,6 +535,17 @@ pub const Inst = struct { |
| 469 | 535 | kw_args: struct {}, |
| 470 | 536 | }; |
| 471 | 537 | |
| 538 | pub const ParamType = struct { | |
| 539 | pub const base_tag = Tag.param_type; | |
| 540 | base: Inst, | |
| 541 | ||
| 542 | positionals: struct { | |
| 543 | func: *Inst, | |
| 544 | arg_index: usize, | |
| 545 | }, | |
| 546 | kw_args: struct {}, | |
| 547 | }; | |
| 548 | ||
| 472 | 549 | pub const Primitive = struct { |
| 473 | 550 | pub const base_tag = Tag.primitive; |
| 474 | 551 | base: Inst, |
| ... | ... | @@ -559,42 +636,6 @@ pub const Inst = struct { |
| 559 | 636 | }; |
| 560 | 637 | }; |
| 561 | 638 | |
| 562 | pub const FloatCast = struct { | |
| 563 | pub const base_tag = Tag.floatcast; | |
| 564 | pub const builtin_name = "@floatCast"; | |
| 565 | base: Inst, | |
| 566 | ||
| 567 | positionals: struct { | |
| 568 | dest_type: *Inst, | |
| 569 | operand: *Inst, | |
| 570 | }, | |
| 571 | kw_args: struct {}, | |
| 572 | }; | |
| 573 | ||
| 574 | pub const IntCast = struct { | |
| 575 | pub const base_tag = Tag.intcast; | |
| 576 | pub const builtin_name = "@intCast"; | |
| 577 | base: Inst, | |
| 578 | ||
| 579 | positionals: struct { | |
| 580 | dest_type: *Inst, | |
| 581 | operand: *Inst, | |
| 582 | }, | |
| 583 | kw_args: struct {}, | |
| 584 | }; | |
| 585 | ||
| 586 | pub const BitCast = struct { | |
| 587 | pub const base_tag = Tag.bitcast; | |
| 588 | pub const builtin_name = "@bitCast"; | |
| 589 | base: Inst, | |
| 590 | ||
| 591 | positionals: struct { | |
| 592 | dest_type: *Inst, | |
| 593 | operand: *Inst, | |
| 594 | }, | |
| 595 | kw_args: struct {}, | |
| 596 | }; | |
| 597 | ||
| 598 | 639 | pub const ElemPtr = struct { |
| 599 | 640 | pub const base_tag = Tag.elemptr; |
| 600 | 641 | base: Inst, |
| ... | ... | @@ -1467,15 +1508,15 @@ const EmitZIR = struct { |
| 1467 | 1508 | }, |
| 1468 | 1509 | .ComptimeInt => return self.emitComptimeIntVal(src, typed_value.val), |
| 1469 | 1510 | .Int => { |
| 1470 | const as_inst = try self.arena.allocator.create(Inst.As); | |
| 1511 | const as_inst = try self.arena.allocator.create(Inst.BinOp); | |
| 1471 | 1512 | as_inst.* = .{ |
| 1472 | 1513 | .base = .{ |
| 1514 | .tag = .as, | |
| 1473 | 1515 | .src = src, |
| 1474 | .tag = Inst.As.base_tag, | |
| 1475 | 1516 | }, |
| 1476 | 1517 | .positionals = .{ |
| 1477 | .dest_type = (try self.emitType(src, typed_value.ty)).inst, | |
| 1478 | .value = (try self.emitComptimeIntVal(src, typed_value.val)).inst, | |
| 1518 | .lhs = (try self.emitType(src, typed_value.ty)).inst, | |
| 1519 | .rhs = (try self.emitComptimeIntVal(src, typed_value.val)).inst, | |
| 1479 | 1520 | }, |
| 1480 | 1521 | .kw_args = .{}, |
| 1481 | 1522 | }; |
| ... | ... | @@ -1640,17 +1681,17 @@ const EmitZIR = struct { |
| 1640 | 1681 | src: usize, |
| 1641 | 1682 | new_body: ZirBody, |
| 1642 | 1683 | old_inst: *ir.Inst.UnOp, |
| 1643 | comptime I: type, | |
| 1684 | tag: Inst.Tag, | |
| 1644 | 1685 | ) Allocator.Error!*Inst { |
| 1645 | const new_inst = try self.arena.allocator.create(I); | |
| 1686 | const new_inst = try self.arena.allocator.create(Inst.BinOp); | |
| 1646 | 1687 | new_inst.* = .{ |
| 1647 | 1688 | .base = .{ |
| 1648 | 1689 | .src = src, |
| 1649 | .tag = I.base_tag, | |
| 1690 | .tag = tag, | |
| 1650 | 1691 | }, |
| 1651 | 1692 | .positionals = .{ |
| 1652 | .dest_type = (try self.emitType(src, old_inst.base.ty)).inst, | |
| 1653 | .operand = try self.resolveInst(new_body, old_inst.operand), | |
| 1693 | .lhs = (try self.emitType(old_inst.base.src, old_inst.base.ty)).inst, | |
| 1694 | .rhs = try self.resolveInst(new_body, old_inst.operand), | |
| 1654 | 1695 | }, |
| 1655 | 1696 | .kw_args = .{}, |
| 1656 | 1697 | }; |
| ... | ... | @@ -1691,9 +1732,9 @@ const EmitZIR = struct { |
| 1691 | 1732 | .cmp_gt => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_gt).?, .cmp_gt), |
| 1692 | 1733 | .cmp_neq => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_neq).?, .cmp_neq), |
| 1693 | 1734 | |
| 1694 | .bitcast => try self.emitCast(inst.src, new_body, inst.castTag(.bitcast).?, Inst.BitCast), | |
| 1695 | .intcast => try self.emitCast(inst.src, new_body, inst.castTag(.intcast).?, Inst.IntCast), | |
| 1696 | .floatcast => try self.emitCast(inst.src, new_body, inst.castTag(.floatcast).?, Inst.FloatCast), | |
| 1735 | .bitcast => try self.emitCast(inst.src, new_body, inst.castTag(.bitcast).?, .bitcast), | |
| 1736 | .intcast => try self.emitCast(inst.src, new_body, inst.castTag(.intcast).?, .intcast), | |
| 1737 | .floatcast => try self.emitCast(inst.src, new_body, inst.castTag(.floatcast).?, .floatcast), | |
| 1697 | 1738 | |
| 1698 | 1739 | .block => blk: { |
| 1699 | 1740 | const old_inst = inst.castTag(.block).?; |
test/stage2/cbe.zig+13-35| ... | ... | @@ -19,13 +19,13 @@ pub fn addCases(ctx: *TestContext) !void { |
| 19 | 19 | \\fn main() noreturn {} |
| 20 | 20 | \\ |
| 21 | 21 | \\export fn _start() noreturn { |
| 22 | \\	main(); | |
| 22 | \\ main(); | |
| 23 | 23 | \\} |
| 24 | 24 | , |
| 25 | 25 | \\noreturn void main(void); |
| 26 | 26 | \\ |
| 27 | 27 | \\noreturn void _start(void) { |
| 28 | \\	main(); | |
| 28 | \\ main(); | |
| 29 | 29 | \\} |
| 30 | 30 | \\ |
| 31 | 31 | \\noreturn void main(void) {} |
| ... | ... | @@ -35,15 +35,15 @@ pub fn addCases(ctx: *TestContext) !void { |
| 35 | 35 | // TODO: figure out a way to prevent asm constants from being generated |
| 36 | 36 | ctx.c("inline asm", linux_x64, |
| 37 | 37 | \\fn exitGood() void { |
| 38 | \\	asm volatile ("syscall" | |
| 39 | \\ 		: | |
| 40 | \\		: [number] "{rax}" (231), | |
| 41 | \\		 [arg1] "{rdi}" (0) | |
| 42 | \\	); | |
| 38 | \\ asm volatile ("syscall" | |
| 39 | \\ : | |
| 40 | \\ : [number] "{rax}" (231), | |
| 41 | \\ [arg1] "{rdi}" (0) | |
| 42 | \\ ); | |
| 43 | 43 | \\} |
| 44 | 44 | \\ |
| 45 | 45 | \\export fn _start() noreturn { |
| 46 | \\	exitGood(); | |
| 46 | \\ exitGood(); | |
| 47 | 47 | \\} |
| 48 | 48 | , |
| 49 | 49 | \\#include <stddef.h> |
| ... | ... | @@ -55,36 +55,14 @@ pub fn addCases(ctx: *TestContext) !void { |
| 55 | 55 | \\const char *const exitGood__anon_2 = "syscall"; |
| 56 | 56 | \\ |
| 57 | 57 | \\noreturn void _start(void) { |
| 58 | \\	exitGood(); | |
| 58 | \\ exitGood(); | |
| 59 | 59 | \\} |
| 60 | 60 | \\ |
| 61 | 61 | \\void exitGood(void) { |
| 62 | \\	register size_t rax_constant __asm__("rax") = 231; | |
| 63 | \\	register size_t rdi_constant __asm__("rdi") = 0; | |
| 64 | \\	__asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant)); | |
| 65 | \\	return; | |
| 66 | \\} | |
| 67 | \\ | |
| 68 | ); | |
| 69 | ctx.c("basic return", linux_x64, | |
| 70 | \\fn main() u8 { | |
| 71 | \\	return 103; | |
| 72 | \\} | |
| 73 | \\ | |
| 74 | \\export fn _start() noreturn { | |
| 75 | \\	_ = main(); | |
| 76 | \\} | |
| 77 | , | |
| 78 | \\#include <stdint.h> | |
| 79 | \\ | |
| 80 | \\uint8_t main(void); | |
| 81 | \\ | |
| 82 | \\noreturn void _start(void) { | |
| 83 | \\	(void)main(); | |
| 84 | \\} | |
| 85 | \\ | |
| 86 | \\uint8_t main(void) { | |
| 87 | \\	return 103; | |
| 62 | \\ register size_t rax_constant __asm__("rax") = 231; | |
| 63 | \\ register size_t rdi_constant __asm__("rdi") = 0; | |
| 64 | \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant)); | |
| 65 | \\ return; | |
| 88 | 66 | \\} |
| 89 | 67 | \\ |
| 90 | 68 | ); |