1//! Core module for the Ix compiler
2
3// sort-lines: start
4
5pub const Air = @import("Air.zig");
6pub const array = @import("array.zig");
7pub const Ast = @import("Ast.zig");
8pub const Cache = @import("Cache.zig");
9pub const Comp = @import("Comp.zig");
10pub const InternPool = @import("InternPool.zig");
11pub const Js = @import("js/Js.zig");
12pub const Parse = @import("Parse.zig");
13pub const Report = @import("Report.zig");
14pub const Sema = @import("Sema.zig");
15pub const Stack = @import("stack.zig").Stack;
16pub const U32Stack = Stack(u32);
17pub const StoredTrace = @import("StoredTrace.zig");
18pub const DebugStoredTrace = StoredTrace.DebugStoredTrace;
19pub const Queue = @import("queue.zig").Queue;
20pub const Hir = @import("Hir.zig");
21pub const Tokenizer = @import("Tokenizer.zig");
22pub const Transform = @import("Transform.zig");
23pub const unicode_props = @import("unicode_props.zig");
24
25// sort-lines: end
26
27// The rest of this file is small utility functions, which are not specific to
28// any of the above namespaces. Remember Zig discourages namespaces like `utils`.
29
30pub const runtime_safety = switch (@import("builtin").mode) {
31 .Debug, .ReleaseSafe => true,
32 .ReleaseFast, .ReleaseSmall => false,
33};
34
35/// This is an alternative to `std.debug.assert`, where you want to print some
36/// debug info instead of "reached unreachable code". Usage is as follows:
37///
38/// if (assertFailed(variantIsTrue())) {
39/// std.debug.panic("...", .{ ... });
40/// }
41///
42/// This allows you to perform any number of side effects in the failure case
43/// with certainty that they are not analyzed in unsafe modes.
44pub inline fn assertFailed(cond: bool) bool {
45 if (runtime_safety) {
46 return !cond;
47 } else if (!cond) {
48 unreachable; // assertion failure
49 }
50}
51
52/// If `value` can be cast to `i32`
53pub fn inInt32Range(value: anytype) bool {
54 return switch (@typeInfo(@TypeOf(value))) {
55 .int,
56 .comptime_int,
57 => value >= std.math.minInt(i32) and
58 value <= std.math.maxInt(i32),
59
60 .float,
61 .comptime_float,
62 => std.math.isFinite(value) and
63 value >= std.math.minInt(i32) and
64 value <= std.math.maxInt(i32) and
65 (@rem(value, 1) == 0),
66
67 else => @compileError(unreachable),
68 };
69}
70
71pub fn detectTTYFromWriter(writer: std.io.AnyWriter) std.io.tty.Config {
72 const fileWriteFn = comptime (std.fs.File{ .handle = undefined })
73 .writer().any().writeFn;
74
75 if (writer.writeFn == fileWriteFn) {
76 const file: *const std.fs.File = @alignCast(@ptrCast(writer.context));
77 return std.io.tty.detectConfig(file.*);
78 }
79
80 return .no_color;
81}
82
83/// This function is entirely because ZLS cannot infer `anytype` -> `std.json.WriteStream`
84/// It should be removed once ZLS or the Zig language fixes this.
85pub fn jsonWriterCast(json_writer: anytype) *std.json.WriteStream(
86 @TypeOf(json_writer.*).Stream,
87 switch (@TypeOf(json_writer.nesting_stack)) {
88 void => .assumed_correct,
89 std.BitStack => .checked_to_arbitrary_depth,
90 else => .{ .checked_to_fixed_depth = 256 },
91 },
92) {
93 return json_writer;
94}
95
96/// A writer with TTY information. This abstraction is used so that test runners and wasm
97/// builds can specify an output writer that supports TTY, even if the underlying writer
98/// is an ArrayList writer (detectTTYFromWriter would report .no_color)
99pub const OutStream = struct {
100 writer: std.io.AnyWriter,
101 tty: std.io.tty.Config,
102
103 /// To make it possible to have reasonable error sets, all logging
104 /// IO-related errors will be categorized as error.OutStream. We preserve
105 /// OutOfMemory so that it is still detectable.
106 pub const Error = error{ OutStream, OutOfMemory };
107
108 pub fn init(writer: std.io.AnyWriter) OutStream {
109 return .{
110 .writer = writer,
111 .tty = detectTTYFromWriter(writer),
112 };
113 }
114
115 var stderr_writer: std.fs.File.Writer = undefined;
116 pub fn getStderr() OutStream {
117 const stderr = std.io.getStdErr();
118 stderr_writer = stderr.writer();
119 return .{
120 .writer = stderr_writer.any(),
121 .tty = std.io.tty.detectConfig(stderr),
122 };
123 }
124
125 pub fn print(out: OutStream, comptime fmt: []const u8, args: anytype) Error!void {
126 out.writer.print(fmt, args) catch |e| switch (e) {
127 error.OutOfMemory => return Error.OutOfMemory,
128 else => return Error.OutStream,
129 };
130 }
131
132 pub fn setColor(out: OutStream, color: std.io.tty.Color) !void {
133 out.tty.setColor(out.writer, color) catch |e| switch (e) {
134 error.OutOfMemory => return Error.OutOfMemory,
135 else => return Error.OutStream,
136 };
137 }
138};
139
140pub const SerializeTarget = union(enum) {
141 file: std.fs.File,
142 writer: std.io.AnyWriter,
143
144 pub fn writev(target: SerializeTarget, vecs: []std.posix.iovec_const) !void {
145 return switch (target) {
146 .file => |file| try file.writevAll(vecs),
147 .writer => |w| for (vecs) |iov| {
148 try w.writeAll(iov.base[0..iov.len]);
149 },
150 };
151 }
152};
153
154pub fn iovec(slice: anytype) if (@typeInfo(@TypeOf(slice)).pointer.is_const)
155 std.posix.iovec_const
156else
157 std.posix.iovec {
158 return .{ .base = slice.ptr, .len = slice.len };
159}
160pub fn iovecConst(slice: []const u8) std.posix.iovec_const {
161 return .{ .base = slice.ptr, .len = slice.len };
162}
163
164const std = @import("std");
165const assert = std.debug.assert;
166
167comptime {
168 std.testing.refAllDecls(@This());
169}
170
171pub fn FieldType(comptime T: type, comptime field: FieldEnum(T)) type {
172 if (@typeInfo(T) != .@"struct" and @typeInfo(T) != .@"union") {
173 @compileError("Expected struct or union, found '" ++ @typeName(T) ++ "'");
174 }
175
176 @setEvalBranchQuota(std.math.maxInt(u32)); // i'm so sorry mlugg
177 return std.meta.fieldInfo(T, field).type;
178}
179
180/// Returns an enum with a variant named after each field of `T`.
181pub fn FieldEnum(comptime T: type) type {
182 @setEvalBranchQuota(std.math.maxInt(u32)); // i'm so sorry mlugg
183 return std.meta.FieldEnum(T);
184}