master
  1//! Builds of the Zig compiler are distributed partly in source form. That
  2//! source lives here. These APIs are provided as-is and have absolutely no API
  3//! guarantees whatsoever.
  4
  5const std = @import("std.zig");
  6const tokenizer = @import("zig/tokenizer.zig");
  7const assert = std.debug.assert;
  8const Allocator = std.mem.Allocator;
  9const Io = std.Io;
 10const Writer = std.Io.Writer;
 11
 12pub const ErrorBundle = @import("zig/ErrorBundle.zig");
 13pub const Server = @import("zig/Server.zig");
 14pub const Client = @import("zig/Client.zig");
 15pub const Token = tokenizer.Token;
 16pub const Tokenizer = tokenizer.Tokenizer;
 17pub const string_literal = @import("zig/string_literal.zig");
 18pub const number_literal = @import("zig/number_literal.zig");
 19pub const primitives = @import("zig/primitives.zig");
 20pub const isPrimitive = primitives.isPrimitive;
 21pub const Ast = @import("zig/Ast.zig");
 22pub const AstGen = @import("zig/AstGen.zig");
 23pub const Zir = @import("zig/Zir.zig");
 24pub const Zoir = @import("zig/Zoir.zig");
 25pub const ZonGen = @import("zig/ZonGen.zig");
 26pub const system = @import("zig/system.zig");
 27pub const BuiltinFn = @import("zig/BuiltinFn.zig");
 28pub const AstRlAnnotate = @import("zig/AstRlAnnotate.zig");
 29pub const LibCInstallation = @import("zig/LibCInstallation.zig");
 30pub const WindowsSdk = @import("zig/WindowsSdk.zig");
 31pub const LibCDirs = @import("zig/LibCDirs.zig");
 32pub const target = @import("zig/target.zig");
 33pub const llvm = @import("zig/llvm.zig");
 34
 35// Character literal parsing
 36pub const ParsedCharLiteral = string_literal.ParsedCharLiteral;
 37pub const parseCharLiteral = string_literal.parseCharLiteral;
 38pub const parseNumberLiteral = number_literal.parseNumberLiteral;
 39
 40pub const c_translation = struct {
 41    pub const builtins = @import("zig/c_translation/builtins.zig");
 42    pub const helpers = @import("zig/c_translation/helpers.zig");
 43};
 44
 45pub const SrcHasher = std.crypto.hash.Blake3;
 46pub const SrcHash = [16]u8;
 47
 48pub const Color = enum {
 49    /// Determine whether stderr is a terminal or not automatically.
 50    auto,
 51    /// Assume stderr is not a terminal.
 52    off,
 53    /// Assume stderr is a terminal.
 54    on,
 55
 56    pub fn getTtyConf(color: Color, detected: Io.tty.Config) Io.tty.Config {
 57        return switch (color) {
 58            .auto => detected,
 59            .on => .escape_codes,
 60            .off => .no_color,
 61        };
 62    }
 63    pub fn detectTtyConf(color: Color) Io.tty.Config {
 64        return switch (color) {
 65            .auto => .detect(.stderr()),
 66            .on => .escape_codes,
 67            .off => .no_color,
 68        };
 69    }
 70};
 71
 72/// There are many assumptions in the entire codebase that Zig source files can
 73/// be byte-indexed with a u32 integer.
 74pub const max_src_size = std.math.maxInt(u32);
 75
 76pub fn hashSrc(src: []const u8) SrcHash {
 77    var out: SrcHash = undefined;
 78    SrcHasher.hash(src, &out, .{});
 79    return out;
 80}
 81
 82pub fn srcHashEql(a: SrcHash, b: SrcHash) bool {
 83    return @as(u128, @bitCast(a)) == @as(u128, @bitCast(b));
 84}
 85
 86pub fn hashName(parent_hash: SrcHash, sep: []const u8, name: []const u8) SrcHash {
 87    var out: SrcHash = undefined;
 88    var hasher = SrcHasher.init(.{});
 89    hasher.update(&parent_hash);
 90    hasher.update(sep);
 91    hasher.update(name);
 92    hasher.final(&out);
 93    return out;
 94}
 95
 96pub const Loc = struct {
 97    line: usize,
 98    column: usize,
 99    /// Does not include the trailing newline.
100    source_line: []const u8,
101
102    pub fn eql(a: Loc, b: Loc) bool {
103        return a.line == b.line and a.column == b.column and std.mem.eql(u8, a.source_line, b.source_line);
104    }
105};
106
107pub fn findLineColumn(source: []const u8, byte_offset: usize) Loc {
108    var line: usize = 0;
109    var column: usize = 0;
110    var line_start: usize = 0;
111    var i: usize = 0;
112    while (i < byte_offset) : (i += 1) {
113        switch (source[i]) {
114            '\n' => {
115                line += 1;
116                column = 0;
117                line_start = i + 1;
118            },
119            else => {
120                column += 1;
121            },
122        }
123    }
124    while (i < source.len and source[i] != '\n') {
125        i += 1;
126    }
127    return .{
128        .line = line,
129        .column = column,
130        .source_line = source[line_start..i],
131    };
132}
133
134pub fn lineDelta(source: []const u8, start: usize, end: usize) isize {
135    var line: isize = 0;
136    if (end >= start) {
137        for (source[start..end]) |byte| switch (byte) {
138            '\n' => line += 1,
139            else => continue,
140        };
141    } else {
142        for (source[end..start]) |byte| switch (byte) {
143            '\n' => line -= 1,
144            else => continue,
145        };
146    }
147    return line;
148}
149
150pub const BinNameOptions = struct {
151    root_name: []const u8,
152    target: *const std.Target,
153    output_mode: std.builtin.OutputMode,
154    link_mode: ?std.builtin.LinkMode = null,
155    version: ?std.SemanticVersion = null,
156};
157
158/// Returns the standard file system basename of a binary generated by the Zig compiler.
159pub fn binNameAlloc(allocator: Allocator, options: BinNameOptions) error{OutOfMemory}![]u8 {
160    const root_name = options.root_name;
161    const t = options.target;
162    switch (t.ofmt) {
163        .coff => switch (options.output_mode) {
164            .Exe => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, t.exeFileExt() }),
165            .Lib => {
166                const suffix = switch (options.link_mode orelse .static) {
167                    .static => ".lib",
168                    .dynamic => ".dll",
169                };
170                return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, suffix });
171            },
172            .Obj => return std.fmt.allocPrint(allocator, "{s}.obj", .{root_name}),
173        },
174        .elf => switch (options.output_mode) {
175            .Exe => return allocator.dupe(u8, root_name),
176            .Lib => {
177                switch (options.link_mode orelse .static) {
178                    .static => return std.fmt.allocPrint(allocator, "{s}{s}.a", .{
179                        t.libPrefix(), root_name,
180                    }),
181                    .dynamic => {
182                        if (options.version) |ver| {
183                            return std.fmt.allocPrint(allocator, "{s}{s}.so.{d}.{d}.{d}", .{
184                                t.libPrefix(), root_name, ver.major, ver.minor, ver.patch,
185                            });
186                        } else {
187                            return std.fmt.allocPrint(allocator, "{s}{s}.so", .{
188                                t.libPrefix(), root_name,
189                            });
190                        }
191                    },
192                }
193            },
194            .Obj => return std.fmt.allocPrint(allocator, "{s}.o", .{root_name}),
195        },
196        .macho => switch (options.output_mode) {
197            .Exe => return allocator.dupe(u8, root_name),
198            .Lib => {
199                switch (options.link_mode orelse .static) {
200                    .static => return std.fmt.allocPrint(allocator, "{s}{s}.a", .{
201                        t.libPrefix(), root_name,
202                    }),
203                    .dynamic => {
204                        if (options.version) |ver| {
205                            return std.fmt.allocPrint(allocator, "{s}{s}.{d}.{d}.{d}.dylib", .{
206                                t.libPrefix(), root_name, ver.major, ver.minor, ver.patch,
207                            });
208                        } else {
209                            return std.fmt.allocPrint(allocator, "{s}{s}.dylib", .{
210                                t.libPrefix(), root_name,
211                            });
212                        }
213                    },
214                }
215            },
216            .Obj => return std.fmt.allocPrint(allocator, "{s}.o", .{root_name}),
217        },
218        .wasm => switch (options.output_mode) {
219            .Exe => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, t.exeFileExt() }),
220            .Lib => {
221                switch (options.link_mode orelse .static) {
222                    .static => return std.fmt.allocPrint(allocator, "{s}{s}.a", .{
223                        t.libPrefix(), root_name,
224                    }),
225                    .dynamic => return std.fmt.allocPrint(allocator, "{s}.wasm", .{root_name}),
226                }
227            },
228            .Obj => return std.fmt.allocPrint(allocator, "{s}.o", .{root_name}),
229        },
230        .c => return std.fmt.allocPrint(allocator, "{s}.c", .{root_name}),
231        .spirv => return std.fmt.allocPrint(allocator, "{s}.spv", .{root_name}),
232        .hex => return std.fmt.allocPrint(allocator, "{s}.ihex", .{root_name}),
233        .raw => return std.fmt.allocPrint(allocator, "{s}.bin", .{root_name}),
234        .plan9 => switch (options.output_mode) {
235            .Exe => return allocator.dupe(u8, root_name),
236            .Obj => return std.fmt.allocPrint(allocator, "{s}{s}", .{
237                root_name, t.ofmt.fileExt(t.cpu.arch),
238            }),
239            .Lib => return std.fmt.allocPrint(allocator, "{s}{s}.a", .{
240                t.libPrefix(), root_name,
241            }),
242        },
243    }
244}
245
246pub const SanitizeC = enum {
247    off,
248    trap,
249    full,
250};
251
252pub const BuildId = union(enum) {
253    none,
254    fast,
255    uuid,
256    sha1,
257    md5,
258    hexstring: HexString,
259
260    pub fn eql(a: BuildId, b: BuildId) bool {
261        const Tag = @typeInfo(BuildId).@"union".tag_type.?;
262        const a_tag: Tag = a;
263        const b_tag: Tag = b;
264        if (a_tag != b_tag) return false;
265        return switch (a) {
266            .none, .fast, .uuid, .sha1, .md5 => true,
267            .hexstring => |a_hexstring| std.mem.eql(u8, a_hexstring.toSlice(), b.hexstring.toSlice()),
268        };
269    }
270
271    pub const HexString = struct {
272        bytes: [32]u8,
273        len: u8,
274
275        /// Result is byte values, *not* hex-encoded.
276        pub fn toSlice(hs: *const HexString) []const u8 {
277            return hs.bytes[0..hs.len];
278        }
279    };
280
281    /// Input is byte values, *not* hex-encoded.
282    /// Asserts `bytes` fits inside `HexString`
283    pub fn initHexString(bytes: []const u8) BuildId {
284        var result: BuildId = .{ .hexstring = .{
285            .bytes = undefined,
286            .len = @intCast(bytes.len),
287        } };
288        @memcpy(result.hexstring.bytes[0..bytes.len], bytes);
289        return result;
290    }
291
292    /// Converts UTF-8 text to a `BuildId`.
293    pub fn parse(text: []const u8) !BuildId {
294        if (std.mem.eql(u8, text, "none")) {
295            return .none;
296        } else if (std.mem.eql(u8, text, "fast")) {
297            return .fast;
298        } else if (std.mem.eql(u8, text, "uuid")) {
299            return .uuid;
300        } else if (std.mem.eql(u8, text, "sha1") or std.mem.eql(u8, text, "tree")) {
301            return .sha1;
302        } else if (std.mem.eql(u8, text, "md5")) {
303            return .md5;
304        } else if (std.mem.startsWith(u8, text, "0x")) {
305            var result: BuildId = .{ .hexstring = undefined };
306            const slice = try std.fmt.hexToBytes(&result.hexstring.bytes, text[2..]);
307            result.hexstring.len = @as(u8, @intCast(slice.len));
308            return result;
309        }
310        return error.InvalidBuildIdStyle;
311    }
312
313    test parse {
314        try std.testing.expectEqual(BuildId.md5, try parse("md5"));
315        try std.testing.expectEqual(BuildId.none, try parse("none"));
316        try std.testing.expectEqual(BuildId.fast, try parse("fast"));
317        try std.testing.expectEqual(BuildId.uuid, try parse("uuid"));
318        try std.testing.expectEqual(BuildId.sha1, try parse("sha1"));
319        try std.testing.expectEqual(BuildId.sha1, try parse("tree"));
320
321        try std.testing.expect(BuildId.initHexString("").eql(try parse("0x")));
322        try std.testing.expect(BuildId.initHexString("\x12\x34\x56").eql(try parse("0x123456")));
323        try std.testing.expectError(error.InvalidLength, parse("0x12-34"));
324        try std.testing.expectError(error.InvalidCharacter, parse("0xfoobbb"));
325        try std.testing.expectError(error.InvalidBuildIdStyle, parse("yaddaxxx"));
326    }
327
328    pub fn format(id: BuildId, writer: *Writer) Writer.Error!void {
329        switch (id) {
330            .none, .fast, .uuid, .sha1, .md5 => {
331                try writer.writeAll(@tagName(id));
332            },
333            .hexstring => |hs| {
334                try writer.print("0x{x}", .{hs.toSlice()});
335            },
336        }
337    }
338
339    test format {
340        try std.testing.expectFmt("none", "{f}", .{@as(BuildId, .none)});
341        try std.testing.expectFmt("fast", "{f}", .{@as(BuildId, .fast)});
342        try std.testing.expectFmt("uuid", "{f}", .{@as(BuildId, .uuid)});
343        try std.testing.expectFmt("sha1", "{f}", .{@as(BuildId, .sha1)});
344        try std.testing.expectFmt("md5", "{f}", .{@as(BuildId, .md5)});
345        try std.testing.expectFmt("0x", "{f}", .{BuildId.initHexString("")});
346        try std.testing.expectFmt("0x1234cdef", "{f}", .{BuildId.initHexString("\x12\x34\xcd\xef")});
347    }
348};
349
350pub const LtoMode = enum { none, full, thin };
351
352pub const Subsystem = enum {
353    console,
354    windows,
355    posix,
356    native,
357    efi_application,
358    efi_boot_service_driver,
359    efi_rom,
360    efi_runtime_driver,
361
362    /// Deprecated; use '.console' instead. To be removed after 0.16.0 is tagged.
363    pub const Console: Subsystem = .console;
364    /// Deprecated; use '.windows' instead. To be removed after 0.16.0 is tagged.
365    pub const Windows: Subsystem = .windows;
366    /// Deprecated; use '.posix' instead. To be removed after 0.16.0 is tagged.
367    pub const Posix: Subsystem = .posix;
368    /// Deprecated; use '.native' instead. To be removed after 0.16.0 is tagged.
369    pub const Native: Subsystem = .native;
370    /// Deprecated; use '.efi_application' instead. To be removed after 0.16.0 is tagged.
371    pub const EfiApplication: Subsystem = .efi_application;
372    /// Deprecated; use '.efi_boot_service_driver' instead. To be removed after 0.16.0 is tagged.
373    pub const EfiBootServiceDriver: Subsystem = .efi_boot_service_driver;
374    /// Deprecated; use '.efi_rom' instead. To be removed after 0.16.0 is tagged.
375    pub const EfiRom: Subsystem = .efi_rom;
376    /// Deprecated; use '.efi_runtime_driver' instead. To be removed after 0.16.0 is tagged.
377    pub const EfiRuntimeDriver: Subsystem = .efi_runtime_driver;
378};
379
380pub const CompressDebugSections = enum { none, zlib, zstd };
381
382pub const RcIncludes = enum {
383    /// Use MSVC if available, fall back to MinGW.
384    any,
385    /// Use MSVC include paths (MSVC install + Windows SDK, must be present on the system).
386    msvc,
387    /// Use MinGW include paths (distributed with Zig).
388    gnu,
389    /// Do not use any autodetected include paths.
390    none,
391};
392
393/// Renders a `std.Target.Cpu` value into a textual representation that can be parsed
394/// via the `-mcpu` flag passed to the Zig compiler.
395/// Appends the result to `buffer`.
396pub fn serializeCpu(buffer: *std.array_list.Managed(u8), cpu: std.Target.Cpu) Allocator.Error!void {
397    const all_features = cpu.arch.allFeaturesList();
398    var populated_cpu_features = cpu.model.features;
399    populated_cpu_features.populateDependencies(all_features);
400
401    try buffer.appendSlice(cpu.model.name);
402
403    if (populated_cpu_features.eql(cpu.features)) {
404        // The CPU name alone is sufficient.
405        return;
406    }
407
408    for (all_features, 0..) |feature, i_usize| {
409        const i: std.Target.Cpu.Feature.Set.Index = @intCast(i_usize);
410        const in_cpu_set = populated_cpu_features.isEnabled(i);
411        const in_actual_set = cpu.features.isEnabled(i);
412        try buffer.ensureUnusedCapacity(feature.name.len + 1);
413        if (in_cpu_set and !in_actual_set) {
414            buffer.appendAssumeCapacity('-');
415            buffer.appendSliceAssumeCapacity(feature.name);
416        } else if (!in_cpu_set and in_actual_set) {
417            buffer.appendAssumeCapacity('+');
418            buffer.appendSliceAssumeCapacity(feature.name);
419        }
420    }
421}
422
423pub fn serializeCpuAlloc(ally: Allocator, cpu: std.Target.Cpu) Allocator.Error![]u8 {
424    var buffer = std.array_list.Managed(u8).init(ally);
425    try serializeCpu(&buffer, cpu);
426    return buffer.toOwnedSlice();
427}
428
429/// Return a Formatter for a Zig identifier, escaping it with `@""` syntax if needed.
430///
431/// See also `fmtIdFlags`.
432pub fn fmtId(bytes: []const u8) FormatId {
433    return .{ .bytes = bytes, .flags = .{} };
434}
435
436/// Return a Formatter for a Zig identifier, escaping it with `@""` syntax if needed.
437///
438/// See also `fmtId`.
439pub fn fmtIdFlags(bytes: []const u8, flags: FormatId.Flags) FormatId {
440    return .{ .bytes = bytes, .flags = flags };
441}
442
443pub fn fmtIdPU(bytes: []const u8) FormatId {
444    return .{ .bytes = bytes, .flags = .{ .allow_primitive = true, .allow_underscore = true } };
445}
446
447pub fn fmtIdP(bytes: []const u8) FormatId {
448    return .{ .bytes = bytes, .flags = .{ .allow_primitive = true } };
449}
450
451test fmtId {
452    const expectFmt = std.testing.expectFmt;
453    try expectFmt("@\"while\"", "{f}", .{fmtId("while")});
454    try expectFmt("@\"while\"", "{f}", .{fmtIdFlags("while", .{ .allow_primitive = true })});
455    try expectFmt("@\"while\"", "{f}", .{fmtIdFlags("while", .{ .allow_underscore = true })});
456    try expectFmt("@\"while\"", "{f}", .{fmtIdFlags("while", .{ .allow_primitive = true, .allow_underscore = true })});
457
458    try expectFmt("hello", "{f}", .{fmtId("hello")});
459    try expectFmt("hello", "{f}", .{fmtIdFlags("hello", .{ .allow_primitive = true })});
460    try expectFmt("hello", "{f}", .{fmtIdFlags("hello", .{ .allow_underscore = true })});
461    try expectFmt("hello", "{f}", .{fmtIdFlags("hello", .{ .allow_primitive = true, .allow_underscore = true })});
462
463    try expectFmt("@\"type\"", "{f}", .{fmtId("type")});
464    try expectFmt("type", "{f}", .{fmtIdFlags("type", .{ .allow_primitive = true })});
465    try expectFmt("@\"type\"", "{f}", .{fmtIdFlags("type", .{ .allow_underscore = true })});
466    try expectFmt("type", "{f}", .{fmtIdFlags("type", .{ .allow_primitive = true, .allow_underscore = true })});
467
468    try expectFmt("@\"_\"", "{f}", .{fmtId("_")});
469    try expectFmt("@\"_\"", "{f}", .{fmtIdFlags("_", .{ .allow_primitive = true })});
470    try expectFmt("_", "{f}", .{fmtIdFlags("_", .{ .allow_underscore = true })});
471    try expectFmt("_", "{f}", .{fmtIdFlags("_", .{ .allow_primitive = true, .allow_underscore = true })});
472
473    try expectFmt("@\"i123\"", "{f}", .{fmtId("i123")});
474    try expectFmt("i123", "{f}", .{fmtIdFlags("i123", .{ .allow_primitive = true })});
475    try expectFmt("@\"4four\"", "{f}", .{fmtId("4four")});
476    try expectFmt("_underscore", "{f}", .{fmtId("_underscore")});
477    try expectFmt("@\"11\\\"23\"", "{f}", .{fmtId("11\"23")});
478    try expectFmt("@\"11\\x0f23\"", "{f}", .{fmtId("11\x0F23")});
479
480    // These are technically not currently legal in Zig.
481    try expectFmt("@\"\"", "{f}", .{fmtId("")});
482    try expectFmt("@\"\\x00\"", "{f}", .{fmtId("\x00")});
483}
484
485pub const FormatId = struct {
486    bytes: []const u8,
487    flags: Flags,
488    pub const Flags = struct {
489        allow_primitive: bool = false,
490        allow_underscore: bool = false,
491    };
492
493    /// Print the string as a Zig identifier, escaping it with `@""` syntax if needed.
494    pub fn format(ctx: FormatId, writer: *Writer) Writer.Error!void {
495        const bytes = ctx.bytes;
496        if (isValidId(bytes) and
497            (ctx.flags.allow_primitive or !std.zig.isPrimitive(bytes)) and
498            (ctx.flags.allow_underscore or !isUnderscore(bytes)))
499        {
500            return writer.writeAll(bytes);
501        }
502        try writer.writeAll("@\"");
503        try stringEscape(bytes, writer);
504        try writer.writeByte('"');
505    }
506};
507
508/// Return a formatter for escaping a double quoted Zig string.
509pub fn fmtString(bytes: []const u8) std.fmt.Alt([]const u8, stringEscape) {
510    return .{ .data = bytes };
511}
512
513/// Return a formatter for escaping a single quoted Zig string.
514pub fn fmtChar(c: u21) std.fmt.Alt(u21, charEscape) {
515    return .{ .data = c };
516}
517
518test fmtString {
519    try std.testing.expectFmt("\\x0f", "{f}", .{fmtString("\x0f")});
520    try std.testing.expectFmt(
521        \\" \\ hi \x07 \x11 \" derp '"
522    , "\"{f}\"", .{fmtString(" \\ hi \x07 \x11 \" derp '")});
523}
524
525test fmtChar {
526    try std.testing.expectFmt("c \\u{26a1}", "{f} {f}", .{ fmtChar('c'), fmtChar('âš¡') });
527}
528
529/// Print the string as escaped contents of a double quoted string.
530pub fn stringEscape(bytes: []const u8, w: *Writer) Writer.Error!void {
531    for (bytes) |byte| switch (byte) {
532        '\n' => try w.writeAll("\\n"),
533        '\r' => try w.writeAll("\\r"),
534        '\t' => try w.writeAll("\\t"),
535        '\\' => try w.writeAll("\\\\"),
536        '"' => try w.writeAll("\\\""),
537        '\'' => try w.writeByte('\''),
538        ' ', '!', '#'...'&', '('...'[', ']'...'~' => try w.writeByte(byte),
539        else => {
540            try w.writeAll("\\x");
541            try w.printInt(byte, 16, .lower, .{ .width = 2, .fill = '0' });
542        },
543    };
544}
545
546/// Print as escaped contents of a single-quoted string.
547pub fn charEscape(codepoint: u21, w: *Writer) Writer.Error!void {
548    switch (codepoint) {
549        '\n' => try w.writeAll("\\n"),
550        '\r' => try w.writeAll("\\r"),
551        '\t' => try w.writeAll("\\t"),
552        '\\' => try w.writeAll("\\\\"),
553        '\'' => try w.writeAll("\\'"),
554        '"', ' ', '!', '#'...'&', '('...'[', ']'...'~' => try w.writeByte(@intCast(codepoint)),
555        else => {
556            if (std.math.cast(u8, codepoint)) |byte| {
557                try w.writeAll("\\x");
558                try w.printInt(byte, 16, .lower, .{ .width = 2, .fill = '0' });
559            } else {
560                try w.writeAll("\\u{");
561                try w.printInt(codepoint, 16, .lower, .{});
562                try w.writeByte('}');
563            }
564        },
565    }
566}
567
568pub fn isValidId(bytes: []const u8) bool {
569    if (bytes.len == 0) return false;
570    for (bytes, 0..) |c, i| {
571        switch (c) {
572            '_', 'a'...'z', 'A'...'Z' => {},
573            '0'...'9' => if (i == 0) return false,
574            else => return false,
575        }
576    }
577    return std.zig.Token.getKeyword(bytes) == null;
578}
579
580test isValidId {
581    try std.testing.expect(!isValidId(""));
582    try std.testing.expect(isValidId("foobar"));
583    try std.testing.expect(!isValidId("a b c"));
584    try std.testing.expect(!isValidId("3d"));
585    try std.testing.expect(!isValidId("enum"));
586    try std.testing.expect(isValidId("i386"));
587}
588
589pub fn isUnderscore(bytes: []const u8) bool {
590    return bytes.len == 1 and bytes[0] == '_';
591}
592
593test isUnderscore {
594    try std.testing.expect(isUnderscore("_"));
595    try std.testing.expect(!isUnderscore("__"));
596    try std.testing.expect(!isUnderscore("_foo"));
597    try std.testing.expect(isUnderscore("\x5f"));
598    try std.testing.expect(!isUnderscore("\\x5f"));
599}
600
601/// If the source can be UTF-16LE encoded, this function asserts that `gpa`
602/// will align a byte-sized allocation to at least 2. Allocators that don't do
603/// this are rare.
604pub fn readSourceFileToEndAlloc(gpa: Allocator, file_reader: *Io.File.Reader) ![:0]u8 {
605    var buffer: std.ArrayList(u8) = .empty;
606    defer buffer.deinit(gpa);
607
608    if (file_reader.getSize()) |size| {
609        const casted_size = std.math.cast(u32, size) orelse return error.StreamTooLong;
610        // +1 to avoid resizing for the null byte added in toOwnedSliceSentinel below.
611        try buffer.ensureTotalCapacityPrecise(gpa, casted_size + 1);
612    } else |_| {}
613
614    try file_reader.interface.appendRemaining(gpa, &buffer, .limited(max_src_size));
615
616    // Detect unsupported file types with their Byte Order Mark
617    const unsupported_boms = [_][]const u8{
618        "\xff\xfe\x00\x00", // UTF-32 little endian
619        "\xfe\xff\x00\x00", // UTF-32 big endian
620        "\xfe\xff", // UTF-16 big endian
621    };
622    for (unsupported_boms) |bom| {
623        if (std.mem.startsWith(u8, buffer.items, bom)) {
624            return error.UnsupportedEncoding;
625        }
626    }
627
628    // If the file starts with a UTF-16 little endian BOM, translate it to UTF-8
629    if (std.mem.startsWith(u8, buffer.items, "\xff\xfe")) {
630        if (buffer.items.len % 2 != 0) return error.InvalidEncoding;
631        return std.unicode.utf16LeToUtf8AllocZ(gpa, @ptrCast(@alignCast(buffer.items))) catch |err| switch (err) {
632            error.DanglingSurrogateHalf => error.UnsupportedEncoding,
633            error.ExpectedSecondSurrogateHalf => error.UnsupportedEncoding,
634            error.UnexpectedSecondSurrogateHalf => error.UnsupportedEncoding,
635            else => |e| return e,
636        };
637    }
638
639    return buffer.toOwnedSliceSentinel(gpa, 0);
640}
641
642pub fn printAstErrorsToStderr(gpa: Allocator, tree: Ast, path: []const u8, color: Color) !void {
643    var wip_errors: std.zig.ErrorBundle.Wip = undefined;
644    try wip_errors.init(gpa);
645    defer wip_errors.deinit();
646
647    try putAstErrorsIntoBundle(gpa, tree, path, &wip_errors);
648
649    var error_bundle = try wip_errors.toOwnedBundle("");
650    defer error_bundle.deinit(gpa);
651    error_bundle.renderToStdErr(.{}, color);
652}
653
654pub fn putAstErrorsIntoBundle(
655    gpa: Allocator,
656    tree: Ast,
657    path: []const u8,
658    wip_errors: *std.zig.ErrorBundle.Wip,
659) Allocator.Error!void {
660    var zir = try AstGen.generate(gpa, tree);
661    defer zir.deinit(gpa);
662
663    try wip_errors.addZirErrorMessages(zir, tree, tree.source, path);
664}
665
666pub fn resolveTargetQueryOrFatal(io: Io, target_query: std.Target.Query) std.Target {
667    return std.zig.system.resolveTargetQuery(io, target_query) catch |err|
668        std.process.fatal("unable to resolve target: {s}", .{@errorName(err)});
669}
670
671pub fn parseTargetQueryOrReportFatalError(
672    allocator: Allocator,
673    opts: std.Target.Query.ParseOptions,
674) std.Target.Query {
675    var opts_with_diags = opts;
676    var diags: std.Target.Query.ParseOptions.Diagnostics = .{};
677    if (opts_with_diags.diagnostics == null) {
678        opts_with_diags.diagnostics = &diags;
679    }
680    return std.Target.Query.parse(opts_with_diags) catch |err| switch (err) {
681        error.UnknownCpuModel => {
682            help: {
683                var help_text = std.array_list.Managed(u8).init(allocator);
684                defer help_text.deinit();
685                for (diags.arch.?.allCpuModels()) |cpu| {
686                    help_text.print(" {s}\n", .{cpu.name}) catch break :help;
687                }
688                std.log.info("available CPUs for architecture '{s}':\n{s}", .{
689                    @tagName(diags.arch.?), help_text.items,
690                });
691            }
692            std.process.fatal("unknown CPU: '{s}'", .{diags.cpu_name.?});
693        },
694        error.UnknownCpuFeature => {
695            help: {
696                var help_text = std.array_list.Managed(u8).init(allocator);
697                defer help_text.deinit();
698                for (diags.arch.?.allFeaturesList()) |feature| {
699                    help_text.print(" {s}: {s}\n", .{ feature.name, feature.description }) catch break :help;
700                }
701                std.log.info("available CPU features for architecture '{s}':\n{s}", .{
702                    @tagName(diags.arch.?), help_text.items,
703                });
704            }
705            std.process.fatal("unknown CPU feature: '{s}'", .{diags.unknown_feature_name.?});
706        },
707        error.UnknownObjectFormat => {
708            help: {
709                var help_text = std.array_list.Managed(u8).init(allocator);
710                defer help_text.deinit();
711                inline for (@typeInfo(std.Target.ObjectFormat).@"enum".fields) |field| {
712                    help_text.print(" {s}\n", .{field.name}) catch break :help;
713                }
714                std.log.info("available object formats:\n{s}", .{help_text.items});
715            }
716            std.process.fatal("unknown object format: '{s}'", .{opts.object_format.?});
717        },
718        error.UnknownArchitecture => {
719            help: {
720                var help_text = std.array_list.Managed(u8).init(allocator);
721                defer help_text.deinit();
722                inline for (@typeInfo(std.Target.Cpu.Arch).@"enum".fields) |field| {
723                    help_text.print(" {s}\n", .{field.name}) catch break :help;
724                }
725                std.log.info("available architectures:\n{s} native\n", .{help_text.items});
726            }
727            std.process.fatal("unknown architecture: '{s}'", .{diags.unknown_architecture_name.?});
728        },
729        else => |e| std.process.fatal("unable to parse target query '{s}': {s}", .{
730            opts.arch_os_abi, @errorName(e),
731        }),
732    };
733}
734
735/// Collects all the environment variables that Zig could possibly inspect, so
736/// that we can do reflection on this and print them with `zig env`.
737pub const EnvVar = enum {
738    ZIG_GLOBAL_CACHE_DIR,
739    ZIG_LOCAL_CACHE_DIR,
740    ZIG_LIB_DIR,
741    ZIG_LIBC,
742    ZIG_BUILD_RUNNER,
743    ZIG_BUILD_ERROR_STYLE,
744    ZIG_BUILD_MULTILINE_ERRORS,
745    ZIG_VERBOSE_LINK,
746    ZIG_VERBOSE_CC,
747    ZIG_BTRFS_WORKAROUND,
748    ZIG_DEBUG_CMD,
749    CC,
750    NO_COLOR,
751    CLICOLOR_FORCE,
752    XDG_CACHE_HOME,
753    HOME,
754
755    pub fn isSet(comptime ev: EnvVar) bool {
756        return std.process.hasNonEmptyEnvVarConstant(@tagName(ev));
757    }
758
759    pub fn get(ev: EnvVar, arena: std.mem.Allocator) !?[]u8 {
760        if (std.process.getEnvVarOwned(arena, @tagName(ev))) |value| {
761            return value;
762        } else |err| switch (err) {
763            error.EnvironmentVariableNotFound => return null,
764            else => |e| return e,
765        }
766    }
767
768    pub fn getPosix(comptime ev: EnvVar) ?[:0]const u8 {
769        return std.posix.getenvZ(@tagName(ev));
770    }
771};
772
773pub const SimpleComptimeReason = enum(u32) {
774    // Evaluating at comptime because a builtin operand must be comptime-known.
775    // These messages all mention a specific builtin.
776    operand_setEvalBranchQuota,
777    operand_setFloatMode,
778    operand_branchHint,
779    operand_setRuntimeSafety,
780    operand_embedFile,
781    operand_cImport,
782    operand_cDefine_macro_name,
783    operand_cDefine_macro_value,
784    operand_cInclude_file_name,
785    operand_cUndef_macro_name,
786    operand_shuffle_mask,
787    operand_atomicRmw_operation,
788    operand_reduce_operation,
789
790    // Evaluating at comptime because an operand must be comptime-known.
791    // These messages do not mention a specific builtin (and may not be about a builtin at all).
792    export_target,
793    export_options,
794    extern_options,
795    prefetch_options,
796    call_modifier,
797    compile_error_string,
798    inline_assembly_code,
799    atomic_order,
800    array_mul_factor,
801    slice_cat_operand,
802    inline_call_target,
803    generic_call_target,
804    wasm_memory_index,
805    work_group_dim_index,
806    clobber,
807
808    // Evaluating at comptime because types must be comptime-known.
809    // Reasons other than `.type` are just more specific messages.
810    type,
811    int_signedness,
812    int_bit_width,
813    array_sentinel,
814    array_length,
815    pointer_size,
816    pointer_attrs,
817    pointer_sentinel,
818    slice_sentinel,
819    vector_length,
820    fn_ret_ty,
821    fn_param_types,
822    fn_param_attrs,
823    fn_attrs,
824    struct_layout,
825    struct_field_names,
826    struct_field_types,
827    struct_field_attrs,
828    union_layout,
829    union_field_names,
830    union_field_types,
831    union_field_attrs,
832    tuple_field_types,
833    enum_field_names,
834    enum_field_values,
835
836    // Evaluating at comptime because decl/field name must be comptime-known.
837    decl_name,
838    field_name,
839    tuple_field_index,
840
841    // Evaluating at comptime because it is an attribute of a global declaration.
842    container_var_init,
843    @"callconv",
844    @"align",
845    @"addrspace",
846    @"linksection",
847
848    // Miscellaneous reasons.
849    comptime_keyword,
850    comptime_call_modifier,
851    inline_loop_operand,
852    switch_item,
853    tuple_field_default_value,
854    struct_field_default_value,
855    enum_field_tag_value,
856    slice_single_item_ptr_bounds,
857    stored_to_comptime_field,
858    stored_to_comptime_var,
859    casted_to_comptime_enum,
860    casted_to_comptime_int,
861    casted_to_comptime_float,
862    panic_handler,
863
864    pub fn message(r: SimpleComptimeReason) []const u8 {
865        return switch (r) {
866            // zig fmt: off
867            .operand_setEvalBranchQuota  => "operand to '@setEvalBranchQuota' must be comptime-known",
868            .operand_setFloatMode        => "operand to '@setFloatMode' must be comptime-known",
869            .operand_branchHint          => "operand to '@branchHint' must be comptime-known",
870            .operand_setRuntimeSafety    => "operand to '@setRuntimeSafety' must be comptime-known",
871            .operand_embedFile           => "operand to '@embedFile' must be comptime-known",
872            .operand_cImport             => "operand to '@cImport' is evaluated at comptime",
873            .operand_cDefine_macro_name  => "'@cDefine' macro name must be comptime-known",
874            .operand_cDefine_macro_value => "'@cDefine' macro value must be comptime-known",
875            .operand_cInclude_file_name  => "'@cInclude' file name must be comptime-known",
876            .operand_cUndef_macro_name   => "'@cUndef' macro name must be comptime-known",
877            .operand_shuffle_mask        => "'@shuffle' mask must be comptime-known",
878            .operand_atomicRmw_operation => "'@atomicRmw' operation must be comptime-known",
879            .operand_reduce_operation    => "'@reduce' operation must be comptime-known",
880
881            .export_target        => "export target must be comptime-known",
882            .export_options       => "export options must be comptime-known",
883            .extern_options       => "extern options must be comptime-known",
884            .prefetch_options     => "prefetch options must be comptime-known",
885            .call_modifier        => "call modifier must be comptime-known",
886            .compile_error_string => "compile error string must be comptime-known",
887            .inline_assembly_code => "inline assembly code must be comptime-known",
888            .atomic_order         => "atomic order must be comptime-known",
889            .array_mul_factor     => "array multiplication factor must be comptime-known",
890            .slice_cat_operand    => "slice being concatenated must be comptime-known",
891            .inline_call_target   => "function being called inline must be comptime-known",
892            .generic_call_target  => "generic function being called must be comptime-known",
893            .wasm_memory_index    => "wasm memory index must be comptime-known",
894            .work_group_dim_index => "work group dimension index must be comptime-known",
895            .clobber              => "clobber must be comptime-known",
896
897            .type                => "types must be comptime-known",
898            .int_signedness      => "integer signedness must be comptime-known",
899            .int_bit_width       => "integer bit width must be comptime-known",
900            .array_sentinel      => "array sentinel value must be comptime-known",
901            .array_length        => "array length must be comptime-known",
902            .pointer_size        => "pointer size must be comptime-known",
903            .pointer_attrs       => "pointer attributes must be comptime-known",
904            .pointer_sentinel    => "pointer sentinel value must be comptime-known",
905            .slice_sentinel      => "slice sentinel value must be comptime-known",
906            .vector_length       => "vector length must be comptime-known",
907            .fn_ret_ty           => "function return type must be comptime-known",
908            .fn_param_types      => "function parameter types must be comptime-known",
909            .fn_param_attrs      => "function parameter attributes must be comptime-known",
910            .fn_attrs            => "function attributes must be comptime-known",
911            .struct_layout       => "struct layout must be comptime-known",
912            .struct_field_names  => "struct field names must be comptime-known",
913            .struct_field_types  => "struct field types must be comptime-known",
914            .struct_field_attrs  => "struct field attributes must be comptime-known",
915            .union_layout        => "union layout must be comptime-known",
916            .union_field_names   => "union field names must be comptime-known",
917            .union_field_types   => "union field types must be comptime-known",
918            .union_field_attrs   => "union field attributes must be comptime-known",
919            .tuple_field_types   => "tuple field types must be comptime-known",
920            .enum_field_names    => "enum field names must be comptime-known",
921            .enum_field_values   => "enum field values must be comptime-known",
922
923            .decl_name         => "declaration name must be comptime-known",
924            .field_name        => "field name must be comptime-known",
925            .tuple_field_index => "tuple field index must be comptime-known",
926
927            .container_var_init => "initializer of container-level variable must be comptime-known",
928            .@"callconv"        => "calling convention must be comptime-known",
929            .@"align"           => "alignment must be comptime-known",
930            .@"addrspace"       => "address space must be comptime-known",
931            .@"linksection"     => "linksection must be comptime-known",
932
933            .comptime_keyword             => "'comptime' keyword forces comptime evaluation",
934            .comptime_call_modifier       => "'.compile_time' call modifier forces comptime evaluation",
935            .inline_loop_operand          => "inline loop condition must be comptime-known",
936            .switch_item                  => "switch prong values must be comptime-known",
937            .tuple_field_default_value    => "tuple field default value must be comptime-known",
938            .struct_field_default_value   => "struct field default value must be comptime-known",
939            .enum_field_tag_value         => "enum field tag value must be comptime-known",
940            .slice_single_item_ptr_bounds => "slice of single-item pointer must have comptime-known bounds",
941            .stored_to_comptime_field     => "value stored to a comptime field must be comptime-known",
942            .stored_to_comptime_var       => "value stored to a comptime variable must be comptime-known",
943            .casted_to_comptime_enum      => "value casted to enum with 'comptime_int' tag type must be comptime-known",
944            .casted_to_comptime_int       => "value casted to 'comptime_int' must be comptime-known",
945            .casted_to_comptime_float     => "value casted to 'comptime_float' must be comptime-known",
946            .panic_handler                => "panic handler must be comptime-known",
947            // zig fmt: on
948        };
949    }
950};
951
952/// Every kind of artifact which the compiler can emit.
953pub const EmitArtifact = enum {
954    bin,
955    @"asm",
956    implib,
957    llvm_ir,
958    llvm_bc,
959    docs,
960    pdb,
961    h,
962
963    /// If using `Server` to communicate with the compiler, it will place requested artifacts in
964    /// paths under the output directory, where those paths are named according to this function.
965    /// Returned string is allocated with `gpa` and owned by the caller.
966    pub fn cacheName(ea: EmitArtifact, gpa: Allocator, opts: BinNameOptions) Allocator.Error![]const u8 {
967        const suffix: []const u8 = switch (ea) {
968            .bin => return binNameAlloc(gpa, opts),
969            .@"asm" => ".s",
970            .implib => ".lib",
971            .llvm_ir => ".ll",
972            .llvm_bc => ".bc",
973            .docs => "-docs",
974            .pdb => ".pdb",
975            .h => ".h",
976        };
977        return std.fmt.allocPrint(gpa, "{s}{s}", .{ opts.root_name, suffix });
978    }
979};
980
981test {
982    _ = Ast;
983    _ = AstRlAnnotate;
984    _ = BuiltinFn;
985    _ = Client;
986    _ = ErrorBundle;
987    _ = LibCDirs;
988    _ = LibCInstallation;
989    _ = Server;
990    _ = WindowsSdk;
991    _ = number_literal;
992    _ = primitives;
993    _ = string_literal;
994    _ = system;
995    _ = target;
996    _ = c_translation;
997    _ = llvm;
998}