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}