//! Builds of the Zig compiler are distributed partly in source form. That //! source lives here. These APIs are provided as-is and have absolutely no API //! guarantees whatsoever. pub const ErrorBundle = @import("zig/ErrorBundle.zig"); pub const Server = @import("zig/Server.zig"); pub const Client = @import("zig/Client.zig"); pub const Token = tokenizer.Token; pub const Tokenizer = tokenizer.Tokenizer; pub const string_literal = @import("zig/string_literal.zig"); pub const number_literal = @import("zig/number_literal.zig"); pub const primitives = @import("zig/primitives.zig"); pub const isPrimitive = primitives.isPrimitive; pub const Ast = @import("zig/Ast.zig"); pub const AstGen = @import("zig/AstGen.zig"); pub const Zir = @import("zig/Zir.zig"); pub const system = @import("zig/system.zig"); /// Deprecated: use `std.Target.Query`. pub const CrossTarget = std.Target.Query; pub const BuiltinFn = @import("zig/BuiltinFn.zig"); pub const AstRlAnnotate = @import("zig/AstRlAnnotate.zig"); pub const LibCInstallation = @import("zig/LibCInstallation.zig"); pub const WindowsSdk = @import("zig/WindowsSdk.zig"); pub const LibCDirs = @import("zig/LibCDirs.zig"); pub const target = @import("zig/target.zig"); // Character literal parsing pub const ParsedCharLiteral = string_literal.ParsedCharLiteral; pub const parseCharLiteral = string_literal.parseCharLiteral; pub const parseNumberLiteral = number_literal.parseNumberLiteral; // Files needed by translate-c. pub const c_builtins = @import("zig/c_builtins.zig"); pub const c_translation = @import("zig/c_translation.zig"); pub const SrcHasher = std.crypto.hash.Blake3; pub const SrcHash = [16]u8; pub const Color = enum { /// Determine whether stderr is a terminal or not automatically. auto, /// Assume stderr is not a terminal. off, /// Assume stderr is a terminal. on, pub fn get_tty_conf(color: Color) std.io.tty.Config { return switch (color) { .auto => std.io.tty.detectConfig(std.io.getStdErr()), .on => .escape_codes, .off => .no_color, }; } pub fn renderOptions(color: Color) std.zig.ErrorBundle.RenderOptions { const ttyconf = get_tty_conf(color); return .{ .ttyconf = ttyconf, .include_source_line = ttyconf != .no_color, .include_reference_trace = ttyconf != .no_color, }; } }; /// There are many assumptions in the entire codebase that Zig source files can /// be byte-indexed with a u32 integer. pub const max_src_size = std.math.maxInt(u32); pub fn hashSrc(src: []const u8) SrcHash { var out: SrcHash = undefined; SrcHasher.hash(src, &out, .{}); return out; } pub fn srcHashEql(a: SrcHash, b: SrcHash) bool { return @as(u128, @bitCast(a)) == @as(u128, @bitCast(b)); } pub fn hashName(parent_hash: SrcHash, sep: []const u8, name: []const u8) SrcHash { var out: SrcHash = undefined; var hasher = SrcHasher.init(.{}); hasher.update(&parent_hash); hasher.update(sep); hasher.update(name); hasher.final(&out); return out; } pub const Loc = struct { line: usize, column: usize, /// Does not include the trailing newline. source_line: []const u8, pub fn eql(a: Loc, b: Loc) bool { return a.line == b.line and a.column == b.column and std.mem.eql(u8, a.source_line, b.source_line); } }; pub fn findLineColumn(source: []const u8, byte_offset: usize) Loc { var line: usize = 0; var column: usize = 0; var line_start: usize = 0; var i: usize = 0; while (i < byte_offset) : (i += 1) { switch (source[i]) { '\n' => { line += 1; column = 0; line_start = i + 1; }, else => { column += 1; }, } } while (i < source.len and source[i] != '\n') { i += 1; } return .{ .line = line, .column = column, .source_line = source[line_start..i], }; } pub fn lineDelta(source: []const u8, start: usize, end: usize) isize { var line: isize = 0; if (end >= start) { for (source[start..end]) |byte| switch (byte) { '\n' => line += 1, else => continue, }; } else { for (source[end..start]) |byte| switch (byte) { '\n' => line -= 1, else => continue, }; } return line; } pub const BinNameOptions = struct { root_name: []const u8, target: std.Target, output_mode: std.builtin.OutputMode, link_mode: ?std.builtin.LinkMode = null, version: ?std.SemanticVersion = null, }; /// Returns the standard file system basename of a binary generated by the Zig compiler. pub fn binNameAlloc(allocator: Allocator, options: BinNameOptions) error{OutOfMemory}![]u8 { const root_name = options.root_name; const t = options.target; switch (t.ofmt) { .coff => switch (options.output_mode) { .Exe => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, t.exeFileExt() }), .Lib => { const suffix = switch (options.link_mode orelse .static) { .static => ".lib", .dynamic => ".dll", }; return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, suffix }); }, .Obj => return std.fmt.allocPrint(allocator, "{s}.obj", .{root_name}), }, .elf => switch (options.output_mode) { .Exe => return allocator.dupe(u8, root_name), .Lib => { switch (options.link_mode orelse .static) { .static => return std.fmt.allocPrint(allocator, "{s}{s}.a", .{ t.libPrefix(), root_name, }), .dynamic => { if (options.version) |ver| { return std.fmt.allocPrint(allocator, "{s}{s}.so.{d}.{d}.{d}", .{ t.libPrefix(), root_name, ver.major, ver.minor, ver.patch, }); } else { return std.fmt.allocPrint(allocator, "{s}{s}.so", .{ t.libPrefix(), root_name, }); } }, } }, .Obj => return std.fmt.allocPrint(allocator, "{s}.o", .{root_name}), }, .macho => switch (options.output_mode) { .Exe => return allocator.dupe(u8, root_name), .Lib => { switch (options.link_mode orelse .static) { .static => return std.fmt.allocPrint(allocator, "{s}{s}.a", .{ t.libPrefix(), root_name, }), .dynamic => { if (options.version) |ver| { return std.fmt.allocPrint(allocator, "{s}{s}.{d}.{d}.{d}.dylib", .{ t.libPrefix(), root_name, ver.major, ver.minor, ver.patch, }); } else { return std.fmt.allocPrint(allocator, "{s}{s}.dylib", .{ t.libPrefix(), root_name, }); } }, } }, .Obj => return std.fmt.allocPrint(allocator, "{s}.o", .{root_name}), }, .wasm => switch (options.output_mode) { .Exe => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, t.exeFileExt() }), .Lib => { switch (options.link_mode orelse .static) { .static => return std.fmt.allocPrint(allocator, "{s}{s}.a", .{ t.libPrefix(), root_name, }), .dynamic => return std.fmt.allocPrint(allocator, "{s}.wasm", .{root_name}), } }, .Obj => return std.fmt.allocPrint(allocator, "{s}.o", .{root_name}), }, .c => return std.fmt.allocPrint(allocator, "{s}.c", .{root_name}), .spirv => return std.fmt.allocPrint(allocator, "{s}.spv", .{root_name}), .hex => return std.fmt.allocPrint(allocator, "{s}.ihex", .{root_name}), .raw => return std.fmt.allocPrint(allocator, "{s}.bin", .{root_name}), .plan9 => switch (options.output_mode) { .Exe => return allocator.dupe(u8, root_name), .Obj => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, t.ofmt.fileExt(t.cpu.arch), }), .Lib => return std.fmt.allocPrint(allocator, "{s}{s}.a", .{ t.libPrefix(), root_name, }), }, .nvptx => return std.fmt.allocPrint(allocator, "{s}.ptx", .{root_name}), .dxcontainer => return std.fmt.allocPrint(allocator, "{s}.dxil", .{root_name}), } } pub const BuildId = union(enum) { none, fast, uuid, sha1, md5, hexstring: HexString, pub fn eql(a: BuildId, b: BuildId) bool { const Tag = @typeInfo(BuildId).Union.tag_type.?; const a_tag: Tag = a; const b_tag: Tag = b; if (a_tag != b_tag) return false; return switch (a) { .none, .fast, .uuid, .sha1, .md5 => true, .hexstring => |a_hexstring| std.mem.eql(u8, a_hexstring.toSlice(), b.hexstring.toSlice()), }; } pub const HexString = struct { bytes: [32]u8, len: u8, /// Result is byte values, *not* hex-encoded. pub fn toSlice(hs: *const HexString) []const u8 { return hs.bytes[0..hs.len]; } }; /// Input is byte values, *not* hex-encoded. /// Asserts `bytes` fits inside `HexString` pub fn initHexString(bytes: []const u8) BuildId { var result: BuildId = .{ .hexstring = .{ .bytes = undefined, .len = @intCast(bytes.len), } }; @memcpy(result.hexstring.bytes[0..bytes.len], bytes); return result; } /// Converts UTF-8 text to a `BuildId`. pub fn parse(text: []const u8) !BuildId { if (std.mem.eql(u8, text, "none")) { return .none; } else if (std.mem.eql(u8, text, "fast")) { return .fast; } else if (std.mem.eql(u8, text, "uuid")) { return .uuid; } else if (std.mem.eql(u8, text, "sha1") or std.mem.eql(u8, text, "tree")) { return .sha1; } else if (std.mem.eql(u8, text, "md5")) { return .md5; } else if (std.mem.startsWith(u8, text, "0x")) { var result: BuildId = .{ .hexstring = undefined }; const slice = try std.fmt.hexToBytes(&result.hexstring.bytes, text[2..]); result.hexstring.len = @as(u8, @intCast(slice.len)); return result; } return error.InvalidBuildIdStyle; } test parse { try std.testing.expectEqual(BuildId.md5, try parse("md5")); try std.testing.expectEqual(BuildId.none, try parse("none")); try std.testing.expectEqual(BuildId.fast, try parse("fast")); try std.testing.expectEqual(BuildId.uuid, try parse("uuid")); try std.testing.expectEqual(BuildId.sha1, try parse("sha1")); try std.testing.expectEqual(BuildId.sha1, try parse("tree")); try std.testing.expect(BuildId.initHexString("").eql(try parse("0x"))); try std.testing.expect(BuildId.initHexString("\x12\x34\x56").eql(try parse("0x123456"))); try std.testing.expectError(error.InvalidLength, parse("0x12-34")); try std.testing.expectError(error.InvalidCharacter, parse("0xfoobbb")); try std.testing.expectError(error.InvalidBuildIdStyle, parse("yaddaxxx")); } }; /// Renders a `std.Target.Cpu` value into a textual representation that can be parsed /// via the `-mcpu` flag passed to the Zig compiler. /// Appends the result to `buffer`. pub fn serializeCpu(buffer: *std.ArrayList(u8), cpu: std.Target.Cpu) Allocator.Error!void { const all_features = cpu.arch.allFeaturesList(); var populated_cpu_features = cpu.model.features; populated_cpu_features.populateDependencies(all_features); try buffer.appendSlice(cpu.model.name); if (populated_cpu_features.eql(cpu.features)) { // The CPU name alone is sufficient. return; } for (all_features, 0..) |feature, i_usize| { const i: std.Target.Cpu.Feature.Set.Index = @intCast(i_usize); const in_cpu_set = populated_cpu_features.isEnabled(i); const in_actual_set = cpu.features.isEnabled(i); try buffer.ensureUnusedCapacity(feature.name.len + 1); if (in_cpu_set and !in_actual_set) { buffer.appendAssumeCapacity('-'); buffer.appendSliceAssumeCapacity(feature.name); } else if (!in_cpu_set and in_actual_set) { buffer.appendAssumeCapacity('+'); buffer.appendSliceAssumeCapacity(feature.name); } } } pub fn serializeCpuAlloc(ally: Allocator, cpu: std.Target.Cpu) Allocator.Error![]u8 { var buffer = std.ArrayList(u8).init(ally); try serializeCpu(&buffer, cpu); return buffer.toOwnedSlice(); } pub const DeclIndex = enum(u32) { _, pub fn toOptional(i: DeclIndex) OptionalDeclIndex { return @enumFromInt(@intFromEnum(i)); } }; pub const OptionalDeclIndex = enum(u32) { none = std.math.maxInt(u32), _, pub fn init(oi: ?DeclIndex) OptionalDeclIndex { return @enumFromInt(@intFromEnum(oi orelse return .none)); } pub fn unwrap(oi: OptionalDeclIndex) ?DeclIndex { if (oi == .none) return null; return @enumFromInt(@intFromEnum(oi)); } }; /// Resolving a source location into a byte offset may require doing work /// that we would rather not do unless the error actually occurs. /// Therefore we need a data structure that contains the information necessary /// to lazily produce a `SrcLoc` as required. /// Most of the offsets in this data structure are relative to the containing Decl. /// This makes the source location resolve properly even when a Decl gets /// shifted up or down in the file, as long as the Decl's contents itself /// do not change. pub const LazySrcLoc = union(enum) { /// When this tag is set, the code that constructed this `LazySrcLoc` is asserting /// that all code paths which would need to resolve the source location are /// unreachable. If you are debugging this tag incorrectly being this value, /// look into using reverse-continue with a memory watchpoint to see where the /// value is being set to this tag. unneeded, /// Means the source location points to an entire file; not any particular /// location within the file. `file_scope` union field will be active. entire_file, /// The source location points to a byte offset within a source file, /// offset from 0. The source file is determined contextually. /// Inside a `SrcLoc`, the `file_scope` union field will be active. byte_abs: u32, /// The source location points to a token within a source file, /// offset from 0. The source file is determined contextually. /// Inside a `SrcLoc`, the `file_scope` union field will be active. token_abs: u32, /// The source location points to an AST node within a source file, /// offset from 0. The source file is determined contextually. /// Inside a `SrcLoc`, the `file_scope` union field will be active. node_abs: u32, /// The source location points to a byte offset within a source file, /// offset from the byte offset of the Decl within the file. /// The Decl is determined contextually. byte_offset: u32, /// This data is the offset into the token list from the Decl token. /// The Decl is determined contextually. token_offset: u32, /// The source location points to an AST node, which is this value offset /// from its containing Decl node AST index. /// The Decl is determined contextually. node_offset: TracedOffset, /// The source location points to the main token of an AST node, found /// by taking this AST node index offset from the containing Decl AST node. /// The Decl is determined contextually. node_offset_main_token: i32, /// The source location points to the beginning of a struct initializer. /// The Decl is determined contextually. node_offset_initializer: i32, /// The source location points to a variable declaration type expression, /// found by taking this AST node index offset from the containing /// Decl AST node, which points to a variable declaration AST node. Next, navigate /// to the type expression. /// The Decl is determined contextually. node_offset_var_decl_ty: i32, /// The source location points to the alignment expression of a var decl. /// The Decl is determined contextually. node_offset_var_decl_align: i32, /// The source location points to the linksection expression of a var decl. /// The Decl is determined contextually. node_offset_var_decl_section: i32, /// The source location points to the addrspace expression of a var decl. /// The Decl is determined contextually. node_offset_var_decl_addrspace: i32, /// The source location points to the initializer of a var decl. /// The Decl is determined contextually. node_offset_var_decl_init: i32, /// The source location points to the first parameter of a builtin /// function call, found by taking this AST node index offset from the containing /// Decl AST node, which points to a builtin call AST node. Next, navigate /// to the first parameter. /// The Decl is determined contextually. node_offset_builtin_call_arg0: i32, /// Same as `node_offset_builtin_call_arg0` except arg index 1. node_offset_builtin_call_arg1: i32, node_offset_builtin_call_arg2: i32, node_offset_builtin_call_arg3: i32, node_offset_builtin_call_arg4: i32, node_offset_builtin_call_arg5: i32, /// Like `node_offset_builtin_call_arg0` but recurses through arbitrarily many calls /// to pointer cast builtins. node_offset_ptrcast_operand: i32, /// The source location points to the index expression of an array access /// expression, found by taking this AST node index offset from the containing /// Decl AST node, which points to an array access AST node. Next, navigate /// to the index expression. /// The Decl is determined contextually. node_offset_array_access_index: i32, /// The source location points to the LHS of a slice expression /// expression, found by taking this AST node index offset from the containing /// Decl AST node, which points to a slice AST node. Next, navigate /// to the sentinel expression. /// The Decl is determined contextually. node_offset_slice_ptr: i32, /// The source location points to start expression of a slice expression /// expression, found by taking this AST node index offset from the containing /// Decl AST node, which points to a slice AST node. Next, navigate /// to the sentinel expression. /// The Decl is determined contextually. node_offset_slice_start: i32, /// The source location points to the end expression of a slice /// expression, found by taking this AST node index offset from the containing /// Decl AST node, which points to a slice AST node. Next, navigate /// to the sentinel expression. /// The Decl is determined contextually. node_offset_slice_end: i32, /// The source location points to the sentinel expression of a slice /// expression, found by taking this AST node index offset from the containing /// Decl AST node, which points to a slice AST node. Next, navigate /// to the sentinel expression. /// The Decl is determined contextually. node_offset_slice_sentinel: i32, /// The source location points to the callee expression of a function /// call expression, found by taking this AST node index offset from the containing /// Decl AST node, which points to a function call AST node. Next, navigate /// to the callee expression. /// The Decl is determined contextually. node_offset_call_func: i32, /// The payload is offset from the containing Decl AST node. /// The source location points to the field name of: /// * a field access expression (`a.b`), or /// * the callee of a method call (`a.b()`) /// The Decl is determined contextually. node_offset_field_name: i32, /// The payload is offset from the containing Decl AST node. /// The source location points to the field name of the operand ("b" node) /// of a field initialization expression (`.a = b`) /// The Decl is determined contextually. node_offset_field_name_init: i32, /// The source location points to the pointer of a pointer deref expression, /// found by taking this AST node index offset from the containing /// Decl AST node, which points to a pointer deref AST node. Next, navigate /// to the pointer expression. /// The Decl is determined contextually. node_offset_deref_ptr: i32, /// The source location points to the assembly source code of an inline assembly /// expression, found by taking this AST node index offset from the containing /// Decl AST node, which points to inline assembly AST node. Next, navigate /// to the asm template source code. /// The Decl is determined contextually. node_offset_asm_source: i32, /// The source location points to the return type of an inline assembly /// expression, found by taking this AST node index offset from the containing /// Decl AST node, which points to inline assembly AST node. Next, navigate /// to the return type expression. /// The Decl is determined contextually. node_offset_asm_ret_ty: i32, /// The source location points to the condition expression of an if /// expression, found by taking this AST node index offset from the containing /// Decl AST node, which points to an if expression AST node. Next, navigate /// to the condition expression. /// The Decl is determined contextually. node_offset_if_cond: i32, /// The source location points to a binary expression, such as `a + b`, found /// by taking this AST node index offset from the containing Decl AST node. /// The Decl is determined contextually. node_offset_bin_op: i32, /// The source location points to the LHS of a binary expression, found /// by taking this AST node index offset from the containing Decl AST node, /// which points to a binary expression AST node. Next, navigate to the LHS. /// The Decl is determined contextually. node_offset_bin_lhs: i32, /// The source location points to the RHS of a binary expression, found /// by taking this AST node index offset from the containing Decl AST node, /// which points to a binary expression AST node. Next, navigate to the RHS. /// The Decl is determined contextually. node_offset_bin_rhs: i32, /// The source location points to the operand of a switch expression, found /// by taking this AST node index offset from the containing Decl AST node, /// which points to a switch expression AST node. Next, navigate to the operand. /// The Decl is determined contextually. node_offset_switch_operand: i32, /// The source location points to the else/`_` prong of a switch expression, found /// by taking this AST node index offset from the containing Decl AST node, /// which points to a switch expression AST node. Next, navigate to the else/`_` prong. /// The Decl is determined contextually. node_offset_switch_special_prong: i32, /// The source location points to all the ranges of a switch expression, found /// by taking this AST node index offset from the containing Decl AST node, /// which points to a switch expression AST node. Next, navigate to any of the /// range nodes. The error applies to all of them. /// The Decl is determined contextually. node_offset_switch_range: i32, /// The source location points to the capture of a switch_prong. /// The Decl is determined contextually. node_offset_switch_prong_capture: i32, /// The source location points to the tag capture of a switch_prong. /// The Decl is determined contextually. node_offset_switch_prong_tag_capture: i32, /// The source location points to the align expr of a function type /// expression, found by taking this AST node index offset from the containing /// Decl AST node, which points to a function type AST node. Next, navigate to /// the calling convention node. /// The Decl is determined contextually. node_offset_fn_type_align: i32, /// The source location points to the addrspace expr of a function type /// expression, found by taking this AST node index offset from the containing /// Decl AST node, which points to a function type AST node. Next, navigate to /// the calling convention node. /// The Decl is determined contextually. node_offset_fn_type_addrspace: i32, /// The source location points to the linksection expr of a function type /// expression, found by taking this AST node index offset from the containing /// Decl AST node, which points to a function type AST node. Next, navigate to /// the calling convention node. /// The Decl is determined contextually. node_offset_fn_type_section: i32, /// The source location points to the calling convention of a function type /// expression, found by taking this AST node index offset from the containing /// Decl AST node, which points to a function type AST node. Next, navigate to /// the calling convention node. /// The Decl is determined contextually. node_offset_fn_type_cc: i32, /// The source location points to the return type of a function type /// expression, found by taking this AST node index offset from the containing /// Decl AST node, which points to a function type AST node. Next, navigate to /// the return type node. /// The Decl is determined contextually. node_offset_fn_type_ret_ty: i32, node_offset_param: i32, token_offset_param: i32, /// The source location points to the type expression of an `anyframe->T` /// expression, found by taking this AST node index offset from the containing /// Decl AST node, which points to a `anyframe->T` expression AST node. Next, navigate /// to the type expression. /// The Decl is determined contextually. node_offset_anyframe_type: i32, /// The source location points to the string literal of `extern "foo"`, found /// by taking this AST node index offset from the containing /// Decl AST node, which points to a function prototype or variable declaration /// expression AST node. Next, navigate to the string literal of the `extern "foo"`. /// The Decl is determined contextually. node_offset_lib_name: i32, /// The source location points to the len expression of an `[N:S]T` /// expression, found by taking this AST node index offset from the containing /// Decl AST node, which points to an `[N:S]T` expression AST node. Next, navigate /// to the len expression. /// The Decl is determined contextually. node_offset_array_type_len: i32, /// The source location points to the sentinel expression of an `[N:S]T` /// expression, found by taking this AST node index offset from the containing /// Decl AST node, which points to an `[N:S]T` expression AST node. Next, navigate /// to the sentinel expression. /// The Decl is determined contextually. node_offset_array_type_sentinel: i32, /// The source location points to the elem expression of an `[N:S]T` /// expression, found by taking this AST node index offset from the containing /// Decl AST node, which points to an `[N:S]T` expression AST node. Next, navigate /// to the elem expression. /// The Decl is determined contextually. node_offset_array_type_elem: i32, /// The source location points to the operand of an unary expression. /// The Decl is determined contextually. node_offset_un_op: i32, /// The source location points to the elem type of a pointer. /// The Decl is determined contextually. node_offset_ptr_elem: i32, /// The source location points to the sentinel of a pointer. /// The Decl is determined contextually. node_offset_ptr_sentinel: i32, /// The source location points to the align expr of a pointer. /// The Decl is determined contextually. node_offset_ptr_align: i32, /// The source location points to the addrspace expr of a pointer. /// The Decl is determined contextually. node_offset_ptr_addrspace: i32, /// The source location points to the bit-offset of a pointer. /// The Decl is determined contextually. node_offset_ptr_bitoffset: i32, /// The source location points to the host size of a pointer. /// The Decl is determined contextually. node_offset_ptr_hostsize: i32, /// The source location points to the tag type of an union or an enum. /// The Decl is determined contextually. node_offset_container_tag: i32, /// The source location points to the default value of a field. /// The Decl is determined contextually. node_offset_field_default: i32, /// The source location points to the type of an array or struct initializer. /// The Decl is determined contextually. node_offset_init_ty: i32, /// The source location points to the LHS of an assignment. /// The Decl is determined contextually. node_offset_store_ptr: i32, /// The source location points to the RHS of an assignment. /// The Decl is determined contextually. node_offset_store_operand: i32, /// The source location points to the operand of a `return` statement, or /// the `return` itself if there is no explicit operand. /// The Decl is determined contextually. node_offset_return_operand: i32, /// The source location points to a for loop input. /// The Decl is determined contextually. for_input: struct { /// Points to the for loop AST node. for_node_offset: i32, /// Picks one of the inputs from the condition. input_index: u32, }, /// The source location points to one of the captures of a for loop, found /// by taking this AST node index offset from the containing /// Decl AST node, which points to one of the input nodes of a for loop. /// Next, navigate to the corresponding capture. /// The Decl is determined contextually. for_capture_from_input: i32, /// The source location points to the argument node of a function call. call_arg: struct { decl: DeclIndex, /// Points to the function call AST node. call_node_offset: i32, /// The index of the argument the source location points to. arg_index: u32, }, fn_proto_param: struct { decl: DeclIndex, /// Points to the function prototype AST node. fn_proto_node_offset: i32, /// The index of the parameter the source location points to. param_index: u32, }, array_cat_lhs: ArrayCat, array_cat_rhs: ArrayCat, const ArrayCat = struct { /// Points to the array concat AST node. array_cat_offset: i32, /// The index of the element the source location points to. elem_index: u32, }; pub const nodeOffset = if (TracedOffset.want_tracing) nodeOffsetDebug else nodeOffsetRelease; noinline fn nodeOffsetDebug(node_offset: i32) LazySrcLoc { var result: LazySrcLoc = .{ .node_offset = .{ .x = node_offset } }; result.node_offset.trace.addAddr(@returnAddress(), "init"); return result; } fn nodeOffsetRelease(node_offset: i32) LazySrcLoc { return .{ .node_offset = .{ .x = node_offset } }; } /// This wraps a simple integer in debug builds so that later on we can find out /// where in semantic analysis the value got set. pub const TracedOffset = struct { x: i32, trace: std.debug.Trace = std.debug.Trace.init, const want_tracing = false; }; }; const std = @import("std.zig"); const tokenizer = @import("zig/tokenizer.zig"); const assert = std.debug.assert; const Allocator = std.mem.Allocator; /// Return a Formatter for a Zig identifier, escaping it with `@""` syntax if needed. /// /// - An empty `{}` format specifier escapes invalid identifiers, identifiers that shadow primitives /// and the reserved `_` identifier. /// - Add `p` to the specifier to render identifiers that shadow primitives unescaped. /// - Add `_` to the specifier to render the reserved `_` identifier unescaped. /// - `p` and `_` can be combined, e.g. `{p_}`. /// pub fn fmtId(bytes: []const u8) std.fmt.Formatter(formatId) { return .{ .data = bytes }; } test fmtId { const expectFmt = std.testing.expectFmt; try expectFmt("@\"while\"", "{}", .{fmtId("while")}); try expectFmt("@\"while\"", "{p}", .{fmtId("while")}); try expectFmt("@\"while\"", "{_}", .{fmtId("while")}); try expectFmt("@\"while\"", "{p_}", .{fmtId("while")}); try expectFmt("@\"while\"", "{_p}", .{fmtId("while")}); try expectFmt("hello", "{}", .{fmtId("hello")}); try expectFmt("hello", "{p}", .{fmtId("hello")}); try expectFmt("hello", "{_}", .{fmtId("hello")}); try expectFmt("hello", "{p_}", .{fmtId("hello")}); try expectFmt("hello", "{_p}", .{fmtId("hello")}); try expectFmt("@\"type\"", "{}", .{fmtId("type")}); try expectFmt("type", "{p}", .{fmtId("type")}); try expectFmt("@\"type\"", "{_}", .{fmtId("type")}); try expectFmt("type", "{p_}", .{fmtId("type")}); try expectFmt("type", "{_p}", .{fmtId("type")}); try expectFmt("@\"_\"", "{}", .{fmtId("_")}); try expectFmt("@\"_\"", "{p}", .{fmtId("_")}); try expectFmt("_", "{_}", .{fmtId("_")}); try expectFmt("_", "{p_}", .{fmtId("_")}); try expectFmt("_", "{_p}", .{fmtId("_")}); try expectFmt("@\"i123\"", "{}", .{fmtId("i123")}); try expectFmt("i123", "{p}", .{fmtId("i123")}); try expectFmt("@\"4four\"", "{}", .{fmtId("4four")}); try expectFmt("_underscore", "{}", .{fmtId("_underscore")}); try expectFmt("@\"11\\\"23\"", "{}", .{fmtId("11\"23")}); try expectFmt("@\"11\\x0f23\"", "{}", .{fmtId("11\x0F23")}); // These are technically not currently legal in Zig. try expectFmt("@\"\"", "{}", .{fmtId("")}); try expectFmt("@\"\\x00\"", "{}", .{fmtId("\x00")}); } /// Print the string as a Zig identifier, escaping it with `@""` syntax if needed. fn formatId( bytes: []const u8, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype, ) !void { const allow_primitive, const allow_underscore = comptime parse_fmt: { var allow_primitive = false; var allow_underscore = false; for (fmt) |char| { switch (char) { 'p' => if (!allow_primitive) { allow_primitive = true; continue; }, '_' => if (!allow_underscore) { allow_underscore = true; continue; }, else => {}, } @compileError("expected {}, {p}, {_}, {p_} or {_p}, found {" ++ fmt ++ "}"); } break :parse_fmt .{ allow_primitive, allow_underscore }; }; if (isValidId(bytes) and (allow_primitive or !std.zig.isPrimitive(bytes)) and (allow_underscore or !isUnderscore(bytes))) { return writer.writeAll(bytes); } try writer.writeAll("@\""); try stringEscape(bytes, "", options, writer); try writer.writeByte('"'); } /// Return a Formatter for Zig Escapes of a double quoted string. /// The format specifier must be one of: /// * `{}` treats contents as a double-quoted string. /// * `{'}` treats contents as a single-quoted string. pub fn fmtEscapes(bytes: []const u8) std.fmt.Formatter(stringEscape) { return .{ .data = bytes }; } test fmtEscapes { const expectFmt = std.testing.expectFmt; try expectFmt("\\x0f", "{}", .{fmtEscapes("\x0f")}); try expectFmt( \\" \\ hi \x07 \x11 " derp \'" , "\"{'}\"", .{fmtEscapes(" \\ hi \x07 \x11 \" derp '")}); try expectFmt( \\" \\ hi \x07 \x11 \" derp '" , "\"{}\"", .{fmtEscapes(" \\ hi \x07 \x11 \" derp '")}); } /// Print the string as escaped contents of a double quoted or single-quoted string. /// Format `{}` treats contents as a double-quoted string. /// Format `{'}` treats contents as a single-quoted string. pub fn stringEscape( bytes: []const u8, comptime f: []const u8, options: std.fmt.FormatOptions, writer: anytype, ) !void { _ = options; for (bytes) |byte| switch (byte) { '\n' => try writer.writeAll("\\n"), '\r' => try writer.writeAll("\\r"), '\t' => try writer.writeAll("\\t"), '\\' => try writer.writeAll("\\\\"), '"' => { if (f.len == 1 and f[0] == '\'') { try writer.writeByte('"'); } else if (f.len == 0) { try writer.writeAll("\\\""); } else { @compileError("expected {} or {'}, found {" ++ f ++ "}"); } }, '\'' => { if (f.len == 1 and f[0] == '\'') { try writer.writeAll("\\'"); } else if (f.len == 0) { try writer.writeByte('\''); } else { @compileError("expected {} or {'}, found {" ++ f ++ "}"); } }, ' ', '!', '#'...'&', '('...'[', ']'...'~' => try writer.writeByte(byte), // Use hex escapes for rest any unprintable characters. else => { try writer.writeAll("\\x"); try std.fmt.formatInt(byte, 16, .lower, .{ .width = 2, .fill = '0' }, writer); }, }; } pub fn isValidId(bytes: []const u8) bool { if (bytes.len == 0) return false; for (bytes, 0..) |c, i| { switch (c) { '_', 'a'...'z', 'A'...'Z' => {}, '0'...'9' => if (i == 0) return false, else => return false, } } return std.zig.Token.getKeyword(bytes) == null; } test isValidId { try std.testing.expect(!isValidId("")); try std.testing.expect(isValidId("foobar")); try std.testing.expect(!isValidId("a b c")); try std.testing.expect(!isValidId("3d")); try std.testing.expect(!isValidId("enum")); try std.testing.expect(isValidId("i386")); } pub fn isUnderscore(bytes: []const u8) bool { return bytes.len == 1 and bytes[0] == '_'; } test isUnderscore { try std.testing.expect(isUnderscore("_")); try std.testing.expect(!isUnderscore("__")); try std.testing.expect(!isUnderscore("_foo")); try std.testing.expect(isUnderscore("\x5f")); try std.testing.expect(!isUnderscore("\\x5f")); } pub fn readSourceFileToEndAlloc( allocator: Allocator, input: std.fs.File, size_hint: ?usize, ) ![:0]u8 { const source_code = input.readToEndAllocOptions( allocator, max_src_size, size_hint, @alignOf(u16), 0, ) catch |err| switch (err) { error.ConnectionResetByPeer => unreachable, error.ConnectionTimedOut => unreachable, error.NotOpenForReading => unreachable, else => |e| return e, }; errdefer allocator.free(source_code); // Detect unsupported file types with their Byte Order Mark const unsupported_boms = [_][]const u8{ "\xff\xfe\x00\x00", // UTF-32 little endian "\xfe\xff\x00\x00", // UTF-32 big endian "\xfe\xff", // UTF-16 big endian }; for (unsupported_boms) |bom| { if (std.mem.startsWith(u8, source_code, bom)) { return error.UnsupportedEncoding; } } // If the file starts with a UTF-16 little endian BOM, translate it to UTF-8 if (std.mem.startsWith(u8, source_code, "\xff\xfe")) { const source_code_utf16_le = std.mem.bytesAsSlice(u16, source_code); const source_code_utf8 = std.unicode.utf16LeToUtf8AllocZ(allocator, source_code_utf16_le) catch |err| switch (err) { error.DanglingSurrogateHalf => error.UnsupportedEncoding, error.ExpectedSecondSurrogateHalf => error.UnsupportedEncoding, error.UnexpectedSecondSurrogateHalf => error.UnsupportedEncoding, else => |e| return e, }; allocator.free(source_code); return source_code_utf8; } return source_code; } pub fn printAstErrorsToStderr(gpa: Allocator, tree: Ast, path: []const u8, color: Color) !void { var wip_errors: std.zig.ErrorBundle.Wip = undefined; try wip_errors.init(gpa); defer wip_errors.deinit(); try putAstErrorsIntoBundle(gpa, tree, path, &wip_errors); var error_bundle = try wip_errors.toOwnedBundle(""); defer error_bundle.deinit(gpa); error_bundle.renderToStdErr(color.renderOptions()); } pub fn putAstErrorsIntoBundle( gpa: Allocator, tree: Ast, path: []const u8, wip_errors: *std.zig.ErrorBundle.Wip, ) Allocator.Error!void { var zir = try AstGen.generate(gpa, tree); defer zir.deinit(gpa); try wip_errors.addZirErrorMessages(zir, tree, tree.source, path); } pub fn resolveTargetQueryOrFatal(target_query: std.Target.Query) std.Target { return std.zig.system.resolveTargetQuery(target_query) catch |err| fatal("unable to resolve target: {s}", .{@errorName(err)}); } pub fn parseTargetQueryOrReportFatalError( allocator: Allocator, opts: std.Target.Query.ParseOptions, ) std.Target.Query { var opts_with_diags = opts; var diags: std.Target.Query.ParseOptions.Diagnostics = .{}; if (opts_with_diags.diagnostics == null) { opts_with_diags.diagnostics = &diags; } return std.Target.Query.parse(opts_with_diags) catch |err| switch (err) { error.UnknownCpuModel => { help: { var help_text = std.ArrayList(u8).init(allocator); defer help_text.deinit(); for (diags.arch.?.allCpuModels()) |cpu| { help_text.writer().print(" {s}\n", .{cpu.name}) catch break :help; } std.log.info("available CPUs for architecture '{s}':\n{s}", .{ @tagName(diags.arch.?), help_text.items, }); } fatal("unknown CPU: '{s}'", .{diags.cpu_name.?}); }, error.UnknownCpuFeature => { help: { var help_text = std.ArrayList(u8).init(allocator); defer help_text.deinit(); for (diags.arch.?.allFeaturesList()) |feature| { help_text.writer().print(" {s}: {s}\n", .{ feature.name, feature.description }) catch break :help; } std.log.info("available CPU features for architecture '{s}':\n{s}", .{ @tagName(diags.arch.?), help_text.items, }); } fatal("unknown CPU feature: '{s}'", .{diags.unknown_feature_name.?}); }, error.UnknownObjectFormat => { help: { var help_text = std.ArrayList(u8).init(allocator); defer help_text.deinit(); inline for (@typeInfo(std.Target.ObjectFormat).Enum.fields) |field| { help_text.writer().print(" {s}\n", .{field.name}) catch break :help; } std.log.info("available object formats:\n{s}", .{help_text.items}); } fatal("unknown object format: '{s}'", .{opts.object_format.?}); }, else => |e| fatal("unable to parse target query '{s}': {s}", .{ opts.arch_os_abi, @errorName(e), }), }; } pub fn fatal(comptime format: []const u8, args: anytype) noreturn { std.log.err(format, args); std.process.exit(1); } /// Collects all the environment variables that Zig could possibly inspect, so /// that we can do reflection on this and print them with `zig env`. pub const EnvVar = enum { ZIG_GLOBAL_CACHE_DIR, ZIG_LOCAL_CACHE_DIR, ZIG_LIB_DIR, ZIG_LIBC, ZIG_BUILD_RUNNER, ZIG_VERBOSE_LINK, ZIG_VERBOSE_CC, ZIG_BTRFS_WORKAROUND, ZIG_DEBUG_CMD, CC, NO_COLOR, CLICOLOR_FORCE, XDG_CACHE_HOME, HOME, pub fn isSet(comptime ev: EnvVar) bool { return std.process.hasEnvVarConstant(@tagName(ev)); } pub fn get(ev: EnvVar, arena: std.mem.Allocator) !?[]u8 { if (std.process.getEnvVarOwned(arena, @tagName(ev))) |value| { return value; } else |err| switch (err) { error.EnvironmentVariableNotFound => return null, else => |e| return e, } } pub fn getPosix(comptime ev: EnvVar) ?[:0]const u8 { return std.posix.getenvZ(@tagName(ev)); } }; test { _ = Ast; _ = AstRlAnnotate; _ = BuiltinFn; _ = Client; _ = ErrorBundle; _ = LibCDirs; _ = LibCInstallation; _ = Server; _ = WindowsSdk; _ = number_literal; _ = primitives; _ = string_literal; _ = system; _ = target; _ = c_translation; }