mirror of
https://github.com/ziglang/zig.git
synced 2025-01-28 21:04:39 +00:00
Merge remote-tracking branch 'origin/master' into async
This commit is contained in:
commit
de5c0c9f40
@ -386,6 +386,7 @@ set(ZIG_STD_FILES
|
||||
"index.zig"
|
||||
"io.zig"
|
||||
"linked_list.zig"
|
||||
"macho.zig"
|
||||
"math/acos.zig"
|
||||
"math/acosh.zig"
|
||||
"math/asin.zig"
|
||||
|
@ -2782,30 +2782,96 @@ test "fn reflection" {
|
||||
{#header_close#}
|
||||
{#header_close#}
|
||||
{#header_open|Errors#}
|
||||
{#header_open|Error Set Type#}
|
||||
<p>
|
||||
One of the distinguishing features of Zig is its exception handling strategy.
|
||||
An error set is like an {#link|enum#}.
|
||||
However, each error name across the entire compilation gets assigned an unsigned integer
|
||||
greater than 0. You are allowed to declare the same error name more than once, and if you do, it
|
||||
gets assigned the same integer value.
|
||||
</p>
|
||||
<p>
|
||||
TODO rewrite the errors section to take into account error sets
|
||||
The number of unique error values across the entire compilation should determine the size of the error set type.
|
||||
However right now it is hard coded to be a <code>u16</code>. See <a href="https://github.com/zig-lang/zig/issues/786">#768</a>.
|
||||
</p>
|
||||
<p>
|
||||
These error values are assigned an unsigned integer value greater than 0 at
|
||||
compile time. You are allowed to declare the same error value more than once,
|
||||
and if you do, it gets assigned the same integer value.
|
||||
You can implicitly cast an error from a subset to its superset:
|
||||
</p>
|
||||
{#code_begin|test#}
|
||||
const std = @import("std");
|
||||
|
||||
const FileOpenError = error {
|
||||
AccessDenied,
|
||||
OutOfMemory,
|
||||
FileNotFound,
|
||||
};
|
||||
|
||||
const AllocationError = error {
|
||||
OutOfMemory,
|
||||
};
|
||||
|
||||
test "implicit cast subset to superset" {
|
||||
const err = foo(AllocationError.OutOfMemory);
|
||||
std.debug.assert(err == FileOpenError.OutOfMemory);
|
||||
}
|
||||
|
||||
fn foo(err: AllocationError) FileOpenError {
|
||||
return err;
|
||||
}
|
||||
{#code_end#}
|
||||
<p>
|
||||
But you cannot implicitly cast an error from a superset to a subset:
|
||||
</p>
|
||||
{#code_begin|test_err|not a member of destination error set#}
|
||||
const FileOpenError = error {
|
||||
AccessDenied,
|
||||
OutOfMemory,
|
||||
FileNotFound,
|
||||
};
|
||||
|
||||
const AllocationError = error {
|
||||
OutOfMemory,
|
||||
};
|
||||
|
||||
test "implicit cast superset to subset" {
|
||||
foo(FileOpenError.OutOfMemory) catch {};
|
||||
}
|
||||
|
||||
fn foo(err: FileOpenError) AllocationError {
|
||||
return err;
|
||||
}
|
||||
{#code_end#}
|
||||
<p>
|
||||
There is a shortcut for declaring an error set with only 1 value, and then getting that value:
|
||||
</p>
|
||||
{#code_begin|syntax#}
|
||||
const err = error.FileNotFound;
|
||||
{#code_end#}
|
||||
<p>This is equivalent to:</p>
|
||||
{#code_begin|syntax#}
|
||||
const err = (error {FileNotFound}).FileNotFound;
|
||||
{#code_end#}
|
||||
<p>
|
||||
This becomes useful when using {#link|Inferred Error Sets#}.
|
||||
</p>
|
||||
{#header_open|The Global Error Set#}
|
||||
<p><code>error</code> refers to the global error set.
|
||||
This is the error set that contains all errors in the entire compilation unit.
|
||||
It is a superset of all other error sets and a subset of none of them.
|
||||
</p>
|
||||
<p>
|
||||
You can refer to these error values with the error namespace such as
|
||||
<code>error.FileNotFound</code>.
|
||||
You can implicitly cast any error set to the global one, and you can explicitly
|
||||
cast an error of global error set to a non-global one. This inserts a language-level
|
||||
assert to make sure the error value is in fact in the destination error set.
|
||||
</p>
|
||||
<p>
|
||||
Each error value across the entire compilation unit gets a unique integer,
|
||||
and this determines the size of the error set type.
|
||||
The global error set should generally be avoided when possible, because it prevents
|
||||
the compiler from knowing what errors are possible at compile-time. Knowing
|
||||
the error set at compile-time is better for generated documentationt and for
|
||||
helpful error messages such as forgetting a possible error value in a {#link|switch#}.
|
||||
</p>
|
||||
<p>
|
||||
The error set type is one of the error values, and in the same way that pointers
|
||||
cannot be null, a error set instance is always an error.
|
||||
</p>
|
||||
{#code_begin|syntax#}const pure_error = error.FileNotFound;{#code_end#}
|
||||
{#header_close#}
|
||||
{#header_close#}
|
||||
{#header_open|Error Union Type#}
|
||||
<p>
|
||||
Most of the time you will not find yourself using an error set type. Instead,
|
||||
likely you will be using the error union type. This is when you take an error set
|
||||
@ -2918,7 +2984,6 @@ fn doAThing(str: []u8) !void {
|
||||
a panic in Debug and ReleaseSafe modes and undefined behavior in ReleaseFast mode. So, while we're debugging the
|
||||
application, if there <em>was</em> a surprise error here, the application would crash
|
||||
appropriately.
|
||||
TODO: mention error return traces
|
||||
</p>
|
||||
<p>
|
||||
Finally, you may want to take a different action for every situation. For that, we combine
|
||||
@ -2986,7 +3051,7 @@ fn createFoo(param: i32) !Foo {
|
||||
</li>
|
||||
</ul>
|
||||
{#see_also|defer|if|switch#}
|
||||
{#header_open|Error Union Type#}
|
||||
|
||||
<p>An error union is created with the <code>!</code> binary operator.
|
||||
You can use compile-time reflection to access the child type of an error union:</p>
|
||||
{#code_begin|test#}
|
||||
@ -3008,8 +3073,12 @@ test "error union" {
|
||||
comptime assert(@typeOf(foo).ErrorSet == error);
|
||||
}
|
||||
{#code_end#}
|
||||
<p>TODO the <code>||</code> operator for error sets</p>
|
||||
{#header_open|Inferred Error Sets#}
|
||||
<p>TODO</p>
|
||||
{#header_close#}
|
||||
{#header_open|Error Set Type#}
|
||||
{#header_close#}
|
||||
{#header_open|Error Return Traces#}
|
||||
<p>TODO</p>
|
||||
{#header_close#}
|
||||
{#header_close#}
|
||||
|
@ -1083,7 +1083,7 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
|
||||
gen_param_info->src_index = i;
|
||||
gen_param_info->gen_index = SIZE_MAX;
|
||||
|
||||
ensure_complete_type(g, type_entry);
|
||||
type_ensure_zero_bits_known(g, type_entry);
|
||||
if (type_has_bits(type_entry)) {
|
||||
TypeTableEntry *gen_type;
|
||||
if (handle_is_ptr(type_entry)) {
|
||||
@ -2240,6 +2240,7 @@ static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type) {
|
||||
|
||||
if (enum_type->data.enumeration.zero_bits_loop_flag) {
|
||||
enum_type->data.enumeration.zero_bits_known = true;
|
||||
enum_type->data.enumeration.zero_bits_loop_flag = false;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2394,6 +2395,7 @@ static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) {
|
||||
// the alignment is pointer width, then assert that the first field is within that
|
||||
// alignment
|
||||
struct_type->data.structure.zero_bits_known = true;
|
||||
struct_type->data.structure.zero_bits_loop_flag = false;
|
||||
if (struct_type->data.structure.abi_alignment == 0) {
|
||||
if (struct_type->data.structure.layout == ContainerLayoutPacked) {
|
||||
struct_type->data.structure.abi_alignment = 1;
|
||||
|
24
src/ir.cpp
24
src/ir.cpp
@ -4510,7 +4510,13 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod
|
||||
buf_sprintf("cannot set section of local variable '%s'", buf_ptr(variable_declaration->symbol)));
|
||||
}
|
||||
|
||||
// Temporarily set the name of the IrExecutable to the VariableDeclaration
|
||||
// so that the struct or enum from the init expression inherits the name.
|
||||
Buf *old_exec_name = irb->exec->name;
|
||||
irb->exec->name = variable_declaration->symbol;
|
||||
IrInstruction *init_value = ir_gen_node(irb, variable_declaration->expr, scope);
|
||||
irb->exec->name = old_exec_name;
|
||||
|
||||
if (init_value == irb->codegen->invalid_instruction)
|
||||
return init_value;
|
||||
|
||||
@ -9504,16 +9510,30 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
|
||||
|
||||
// explicit cast from child type of maybe type to maybe type
|
||||
if (wanted_type->id == TypeTableEntryIdMaybe) {
|
||||
if (types_match_const_cast_only(ira, wanted_type->data.maybe.child_type, actual_type, source_node).id == ConstCastResultIdOk) {
|
||||
TypeTableEntry *wanted_child_type = wanted_type->data.maybe.child_type;
|
||||
if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node).id == ConstCastResultIdOk) {
|
||||
return ir_analyze_maybe_wrap(ira, source_instr, value, wanted_type);
|
||||
} else if (actual_type->id == TypeTableEntryIdNumLitInt ||
|
||||
actual_type->id == TypeTableEntryIdNumLitFloat)
|
||||
{
|
||||
if (ir_num_lit_fits_in_other_type(ira, value, wanted_type->data.maybe.child_type, true)) {
|
||||
if (ir_num_lit_fits_in_other_type(ira, value, wanted_child_type, true)) {
|
||||
return ir_analyze_maybe_wrap(ira, source_instr, value, wanted_type);
|
||||
} else {
|
||||
return ira->codegen->invalid_instruction;
|
||||
}
|
||||
} else if (wanted_child_type->id == TypeTableEntryIdPointer &&
|
||||
wanted_child_type->data.pointer.is_const &&
|
||||
(actual_type->id == TypeTableEntryIdPointer || is_container(actual_type)))
|
||||
{
|
||||
IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_child_type, value);
|
||||
if (type_is_invalid(cast1->value.type))
|
||||
return ira->codegen->invalid_instruction;
|
||||
|
||||
IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1);
|
||||
if (type_is_invalid(cast2->value.type))
|
||||
return ira->codegen->invalid_instruction;
|
||||
|
||||
return cast2;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@ const io = std.io;
|
||||
const os = std.os;
|
||||
const elf = std.elf;
|
||||
const DW = std.dwarf;
|
||||
const macho = std.macho;
|
||||
const ArrayList = std.ArrayList;
|
||||
const builtin = @import("builtin");
|
||||
|
||||
@ -178,43 +179,57 @@ pub fn writeCurrentStackTrace(out_stream: var, allocator: &mem.Allocator,
|
||||
}
|
||||
|
||||
fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: var, address: usize) !void {
|
||||
if (builtin.os == builtin.Os.windows) {
|
||||
return error.UnsupportedDebugInfo;
|
||||
}
|
||||
// TODO we really should be able to convert @sizeOf(usize) * 2 to a string literal
|
||||
// at compile time. I'll call it issue #313
|
||||
const ptr_hex = if (@sizeOf(usize) == 4) "0x{x8}" else "0x{x16}";
|
||||
|
||||
const compile_unit = findCompileUnit(debug_info, address) catch {
|
||||
try out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n",
|
||||
address);
|
||||
return;
|
||||
};
|
||||
const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name);
|
||||
if (getLineNumberInfo(debug_info, compile_unit, address - 1)) |line_info| {
|
||||
defer line_info.deinit();
|
||||
try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++
|
||||
DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n",
|
||||
line_info.file_name, line_info.line, line_info.column,
|
||||
address, compile_unit_name);
|
||||
if (printLineFromFile(debug_info.allocator(), out_stream, line_info)) {
|
||||
if (line_info.column == 0) {
|
||||
try out_stream.write("\n");
|
||||
} else {
|
||||
{var col_i: usize = 1; while (col_i < line_info.column) : (col_i += 1) {
|
||||
try out_stream.writeByte(' ');
|
||||
}}
|
||||
try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n");
|
||||
}
|
||||
} else |err| switch (err) {
|
||||
error.EndOfFile => {},
|
||||
else => return err,
|
||||
}
|
||||
} else |err| switch (err) {
|
||||
error.MissingDebugInfo, error.InvalidDebugInfo => {
|
||||
try out_stream.print(ptr_hex ++ " in ??? ({})\n", address, compile_unit_name);
|
||||
switch (builtin.os) {
|
||||
builtin.Os.windows => return error.UnsupportedDebugInfo,
|
||||
builtin.Os.macosx => {
|
||||
// TODO(bnoordhuis) It's theoretically possible to obtain the
|
||||
// compilation unit from the symbtab but it's not that useful
|
||||
// in practice because the compiler dumps everything in a single
|
||||
// object file. Future improvement: use external dSYM data when
|
||||
// available.
|
||||
const unknown = macho.Symbol { .name = "???", .address = address };
|
||||
const symbol = debug_info.symbol_table.search(address) ?? &unknown;
|
||||
try out_stream.print(WHITE ++ "{}" ++ RESET ++ ": " ++
|
||||
DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n",
|
||||
symbol.name, address);
|
||||
},
|
||||
else => {
|
||||
const compile_unit = findCompileUnit(debug_info, address) catch {
|
||||
try out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n",
|
||||
address);
|
||||
return;
|
||||
};
|
||||
const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name);
|
||||
if (getLineNumberInfo(debug_info, compile_unit, address - 1)) |line_info| {
|
||||
defer line_info.deinit();
|
||||
try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++
|
||||
DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n",
|
||||
line_info.file_name, line_info.line, line_info.column,
|
||||
address, compile_unit_name);
|
||||
if (printLineFromFile(debug_info.allocator(), out_stream, line_info)) {
|
||||
if (line_info.column == 0) {
|
||||
try out_stream.write("\n");
|
||||
} else {
|
||||
{var col_i: usize = 1; while (col_i < line_info.column) : (col_i += 1) {
|
||||
try out_stream.writeByte(' ');
|
||||
}}
|
||||
try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n");
|
||||
}
|
||||
} else |err| switch (err) {
|
||||
error.EndOfFile => {},
|
||||
else => return err,
|
||||
}
|
||||
} else |err| switch (err) {
|
||||
error.MissingDebugInfo, error.InvalidDebugInfo => {
|
||||
try out_stream.print(ptr_hex ++ " in ??? ({})\n", address, compile_unit_name);
|
||||
},
|
||||
else => return err,
|
||||
}
|
||||
},
|
||||
else => return err,
|
||||
}
|
||||
}
|
||||
|
||||
@ -222,6 +237,7 @@ pub fn openSelfDebugInfo(allocator: &mem.Allocator) !&ElfStackTrace {
|
||||
switch (builtin.object_format) {
|
||||
builtin.ObjectFormat.elf => {
|
||||
const st = try allocator.create(ElfStackTrace);
|
||||
errdefer allocator.destroy(st);
|
||||
*st = ElfStackTrace {
|
||||
.self_exe_file = undefined,
|
||||
.elf = undefined,
|
||||
@ -247,12 +263,22 @@ pub fn openSelfDebugInfo(allocator: &mem.Allocator) !&ElfStackTrace {
|
||||
try scanAllCompileUnits(st);
|
||||
return st;
|
||||
},
|
||||
builtin.ObjectFormat.macho => {
|
||||
var exe_file = try os.openSelfExe();
|
||||
defer exe_file.close();
|
||||
|
||||
const st = try allocator.create(ElfStackTrace);
|
||||
errdefer allocator.destroy(st);
|
||||
|
||||
*st = ElfStackTrace {
|
||||
.symbol_table = try macho.loadSymbols(allocator, &io.FileInStream.init(&exe_file)),
|
||||
};
|
||||
|
||||
return st;
|
||||
},
|
||||
builtin.ObjectFormat.coff => {
|
||||
return error.TodoSupportCoffDebugInfo;
|
||||
},
|
||||
builtin.ObjectFormat.macho => {
|
||||
return error.TodoSupportMachoDebugInfo;
|
||||
},
|
||||
builtin.ObjectFormat.wasm => {
|
||||
return error.TodoSupportCOFFDebugInfo;
|
||||
},
|
||||
@ -295,31 +321,40 @@ fn printLineFromFile(allocator: &mem.Allocator, out_stream: var, line_info: &con
|
||||
}
|
||||
}
|
||||
|
||||
pub const ElfStackTrace = struct {
|
||||
self_exe_file: os.File,
|
||||
elf: elf.Elf,
|
||||
debug_info: &elf.SectionHeader,
|
||||
debug_abbrev: &elf.SectionHeader,
|
||||
debug_str: &elf.SectionHeader,
|
||||
debug_line: &elf.SectionHeader,
|
||||
debug_ranges: ?&elf.SectionHeader,
|
||||
abbrev_table_list: ArrayList(AbbrevTableHeader),
|
||||
compile_unit_list: ArrayList(CompileUnit),
|
||||
pub const ElfStackTrace = switch (builtin.os) {
|
||||
builtin.Os.macosx => struct {
|
||||
symbol_table: macho.SymbolTable,
|
||||
|
||||
pub fn allocator(self: &const ElfStackTrace) &mem.Allocator {
|
||||
return self.abbrev_table_list.allocator;
|
||||
}
|
||||
pub fn close(self: &ElfStackTrace) void {
|
||||
self.symbol_table.deinit();
|
||||
}
|
||||
},
|
||||
else => struct {
|
||||
self_exe_file: os.File,
|
||||
elf: elf.Elf,
|
||||
debug_info: &elf.SectionHeader,
|
||||
debug_abbrev: &elf.SectionHeader,
|
||||
debug_str: &elf.SectionHeader,
|
||||
debug_line: &elf.SectionHeader,
|
||||
debug_ranges: ?&elf.SectionHeader,
|
||||
abbrev_table_list: ArrayList(AbbrevTableHeader),
|
||||
compile_unit_list: ArrayList(CompileUnit),
|
||||
|
||||
pub fn readString(self: &ElfStackTrace) ![]u8 {
|
||||
var in_file_stream = io.FileInStream.init(&self.self_exe_file);
|
||||
const in_stream = &in_file_stream.stream;
|
||||
return readStringRaw(self.allocator(), in_stream);
|
||||
}
|
||||
pub fn allocator(self: &const ElfStackTrace) &mem.Allocator {
|
||||
return self.abbrev_table_list.allocator;
|
||||
}
|
||||
|
||||
pub fn close(self: &ElfStackTrace) void {
|
||||
self.self_exe_file.close();
|
||||
self.elf.close();
|
||||
}
|
||||
pub fn readString(self: &ElfStackTrace) ![]u8 {
|
||||
var in_file_stream = io.FileInStream.init(&self.self_exe_file);
|
||||
const in_stream = &in_file_stream.stream;
|
||||
return readStringRaw(self.allocator(), in_stream);
|
||||
}
|
||||
|
||||
pub fn close(self: &ElfStackTrace) void {
|
||||
self.self_exe_file.close();
|
||||
self.elf.close();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const PcRange = struct {
|
||||
|
@ -550,12 +550,6 @@ test "parse unsigned comptime" {
|
||||
}
|
||||
}
|
||||
|
||||
// Dummy field because of https://github.com/zig-lang/zig/issues/557.
|
||||
// At top level because of https://github.com/zig-lang/zig/issues/675.
|
||||
const Struct = struct {
|
||||
unused: u8,
|
||||
};
|
||||
|
||||
test "fmt.format" {
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
@ -588,6 +582,10 @@ test "fmt.format" {
|
||||
assert(mem.eql(u8, result, "u3: 5\n"));
|
||||
}
|
||||
{
|
||||
// Dummy field because of https://github.com/zig-lang/zig/issues/557.
|
||||
const Struct = struct {
|
||||
unused: u8,
|
||||
};
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value = Struct {
|
||||
.unused = 42,
|
||||
|
@ -21,6 +21,7 @@ pub const endian = @import("endian.zig");
|
||||
pub const fmt = @import("fmt/index.zig");
|
||||
pub const heap = @import("heap.zig");
|
||||
pub const io = @import("io.zig");
|
||||
pub const macho = @import("macho.zig");
|
||||
pub const math = @import("math/index.zig");
|
||||
pub const mem = @import("mem.zig");
|
||||
pub const net = @import("net.zig");
|
||||
@ -51,6 +52,7 @@ test "std" {
|
||||
_ = @import("endian.zig");
|
||||
_ = @import("fmt/index.zig");
|
||||
_ = @import("io.zig");
|
||||
_ = @import("macho.zig");
|
||||
_ = @import("math/index.zig");
|
||||
_ = @import("mem.zig");
|
||||
_ = @import("heap.zig");
|
||||
|
170
std/macho.zig
Normal file
170
std/macho.zig
Normal file
@ -0,0 +1,170 @@
|
||||
const builtin = @import("builtin");
|
||||
const std = @import("index.zig");
|
||||
const io = std.io;
|
||||
const mem = std.mem;
|
||||
|
||||
const MH_MAGIC_64 = 0xFEEDFACF;
|
||||
const MH_PIE = 0x200000;
|
||||
const LC_SYMTAB = 2;
|
||||
|
||||
const MachHeader64 = packed struct {
|
||||
magic: u32,
|
||||
cputype: u32,
|
||||
cpusubtype: u32,
|
||||
filetype: u32,
|
||||
ncmds: u32,
|
||||
sizeofcmds: u32,
|
||||
flags: u32,
|
||||
reserved: u32,
|
||||
};
|
||||
|
||||
const LoadCommand = packed struct {
|
||||
cmd: u32,
|
||||
cmdsize: u32,
|
||||
};
|
||||
|
||||
const SymtabCommand = packed struct {
|
||||
symoff: u32,
|
||||
nsyms: u32,
|
||||
stroff: u32,
|
||||
strsize: u32,
|
||||
};
|
||||
|
||||
const Nlist64 = packed struct {
|
||||
n_strx: u32,
|
||||
n_type: u8,
|
||||
n_sect: u8,
|
||||
n_desc: u16,
|
||||
n_value: u64,
|
||||
};
|
||||
|
||||
pub const Symbol = struct {
|
||||
name: []const u8,
|
||||
address: u64,
|
||||
|
||||
fn addressLessThan(lhs: &const Symbol, rhs: &const Symbol) bool {
|
||||
return lhs.address < rhs.address;
|
||||
}
|
||||
};
|
||||
|
||||
pub const SymbolTable = struct {
|
||||
allocator: &mem.Allocator,
|
||||
symbols: []const Symbol,
|
||||
strings: []const u8,
|
||||
|
||||
// Doubles as an eyecatcher to calculate the PIE slide, see loadSymbols().
|
||||
// Ideally we'd use _mh_execute_header because it's always at 0x100000000
|
||||
// in the image but as it's located in a different section than executable
|
||||
// code, its displacement is different.
|
||||
pub fn deinit(self: &SymbolTable) void {
|
||||
self.allocator.free(self.symbols);
|
||||
self.symbols = []const Symbol {};
|
||||
|
||||
self.allocator.free(self.strings);
|
||||
self.strings = []const u8 {};
|
||||
}
|
||||
|
||||
pub fn search(self: &const SymbolTable, address: usize) ?&const Symbol {
|
||||
var min: usize = 0;
|
||||
var max: usize = self.symbols.len - 1; // Exclude sentinel.
|
||||
while (min < max) {
|
||||
const mid = min + (max - min) / 2;
|
||||
const curr = &self.symbols[mid];
|
||||
const next = &self.symbols[mid + 1];
|
||||
if (address >= next.address) {
|
||||
min = mid + 1;
|
||||
} else if (address < curr.address) {
|
||||
max = mid;
|
||||
} else {
|
||||
return curr;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
pub fn loadSymbols(allocator: &mem.Allocator, in: &io.FileInStream) !SymbolTable {
|
||||
var file = in.file;
|
||||
try file.seekTo(0);
|
||||
|
||||
var hdr: MachHeader64 = undefined;
|
||||
try readOneNoEof(in, MachHeader64, &hdr);
|
||||
if (hdr.magic != MH_MAGIC_64) return error.MissingDebugInfo;
|
||||
const is_pie = MH_PIE == (hdr.flags & MH_PIE);
|
||||
|
||||
var pos: usize = @sizeOf(@typeOf(hdr));
|
||||
var ncmd: u32 = hdr.ncmds;
|
||||
while (ncmd != 0) : (ncmd -= 1) {
|
||||
try file.seekTo(pos);
|
||||
var lc: LoadCommand = undefined;
|
||||
try readOneNoEof(in, LoadCommand, &lc);
|
||||
if (lc.cmd == LC_SYMTAB) break;
|
||||
pos += lc.cmdsize;
|
||||
} else {
|
||||
return error.MissingDebugInfo;
|
||||
}
|
||||
|
||||
var cmd: SymtabCommand = undefined;
|
||||
try readOneNoEof(in, SymtabCommand, &cmd);
|
||||
|
||||
try file.seekTo(cmd.symoff);
|
||||
var syms = try allocator.alloc(Nlist64, cmd.nsyms);
|
||||
defer allocator.free(syms);
|
||||
try readNoEof(in, Nlist64, syms);
|
||||
|
||||
try file.seekTo(cmd.stroff);
|
||||
var strings = try allocator.alloc(u8, cmd.strsize);
|
||||
errdefer allocator.free(strings);
|
||||
try in.stream.readNoEof(strings);
|
||||
|
||||
var nsyms: usize = 0;
|
||||
for (syms) |sym| if (isSymbol(sym)) nsyms += 1;
|
||||
if (nsyms == 0) return error.MissingDebugInfo;
|
||||
|
||||
var symbols = try allocator.alloc(Symbol, nsyms + 1); // Room for sentinel.
|
||||
errdefer allocator.free(symbols);
|
||||
|
||||
var pie_slide: usize = 0;
|
||||
var nsym: usize = 0;
|
||||
for (syms) |sym| {
|
||||
if (!isSymbol(sym)) continue;
|
||||
const start = sym.n_strx;
|
||||
const end = ??mem.indexOfScalarPos(u8, strings, start, 0);
|
||||
const name = strings[start..end];
|
||||
const address = sym.n_value;
|
||||
symbols[nsym] = Symbol { .name = name, .address = address };
|
||||
nsym += 1;
|
||||
if (is_pie and mem.eql(u8, name, "_SymbolTable_deinit")) {
|
||||
pie_slide = @ptrToInt(SymbolTable.deinit) - address;
|
||||
}
|
||||
}
|
||||
|
||||
// Effectively a no-op, lld emits symbols in ascending order.
|
||||
std.sort.insertionSort(Symbol, symbols[0..nsyms], Symbol.addressLessThan);
|
||||
|
||||
// Insert the sentinel. Since we don't know where the last function ends,
|
||||
// we arbitrarily limit it to the start address + 4 KB.
|
||||
const top = symbols[nsyms - 1].address + 4096;
|
||||
symbols[nsyms] = Symbol { .name = "", .address = top };
|
||||
|
||||
if (pie_slide != 0) {
|
||||
for (symbols) |*symbol| symbol.address += pie_slide;
|
||||
}
|
||||
|
||||
return SymbolTable {
|
||||
.allocator = allocator,
|
||||
.symbols = symbols,
|
||||
.strings = strings,
|
||||
};
|
||||
}
|
||||
|
||||
fn readNoEof(in: &io.FileInStream, comptime T: type, result: []T) !void {
|
||||
return in.stream.readNoEof(([]u8)(result));
|
||||
}
|
||||
fn readOneNoEof(in: &io.FileInStream, comptime T: type, result: &T) !void {
|
||||
return readNoEof(in, T, result[0..1]);
|
||||
}
|
||||
|
||||
fn isSymbol(sym: &const Nlist64) bool {
|
||||
return sym.n_value != 0 and sym.n_desc == 0;
|
||||
}
|
@ -32,9 +32,6 @@ pub const ChildProcess = struct {
|
||||
|
||||
pub argv: []const []const u8,
|
||||
|
||||
/// Possibly called from a signal handler. Must set this before calling `spawn`.
|
||||
pub onTerm: ?fn(&ChildProcess)void,
|
||||
|
||||
/// Leave as null to use the current env map using the supplied allocator.
|
||||
pub env_map: ?&const BufMap,
|
||||
|
||||
@ -102,7 +99,6 @@ pub const ChildProcess = struct {
|
||||
.err_pipe = undefined,
|
||||
.llnode = undefined,
|
||||
.term = null,
|
||||
.onTerm = null,
|
||||
.env_map = null,
|
||||
.cwd = null,
|
||||
.uid = if (is_windows) {} else null,
|
||||
@ -124,7 +120,6 @@ pub const ChildProcess = struct {
|
||||
self.gid = user_info.gid;
|
||||
}
|
||||
|
||||
/// onTerm can be called before `spawn` returns.
|
||||
/// On success must call `kill` or `wait`.
|
||||
pub fn spawn(self: &ChildProcess) !void {
|
||||
if (is_windows) {
|
||||
@ -165,9 +160,6 @@ pub const ChildProcess = struct {
|
||||
}
|
||||
|
||||
pub fn killPosix(self: &ChildProcess) !Term {
|
||||
block_SIGCHLD();
|
||||
defer restore_SIGCHLD();
|
||||
|
||||
if (self.term) |term| {
|
||||
self.cleanupStreams();
|
||||
return term;
|
||||
@ -246,9 +238,6 @@ pub const ChildProcess = struct {
|
||||
}
|
||||
|
||||
fn waitPosix(self: &ChildProcess) !Term {
|
||||
block_SIGCHLD();
|
||||
defer restore_SIGCHLD();
|
||||
|
||||
if (self.term) |term| {
|
||||
self.cleanupStreams();
|
||||
return term;
|
||||
@ -298,10 +287,6 @@ pub const ChildProcess = struct {
|
||||
|
||||
fn handleWaitResult(self: &ChildProcess, status: i32) void {
|
||||
self.term = self.cleanupAfterWait(status);
|
||||
|
||||
if (self.onTerm) |onTerm| {
|
||||
onTerm(self);
|
||||
}
|
||||
}
|
||||
|
||||
fn cleanupStreams(self: &ChildProcess) void {
|
||||
@ -347,9 +332,6 @@ pub const ChildProcess = struct {
|
||||
}
|
||||
|
||||
fn spawnPosix(self: &ChildProcess) !void {
|
||||
// TODO atomically set a flag saying that we already did this
|
||||
install_SIGCHLD_handler();
|
||||
|
||||
const stdin_pipe = if (self.stdin_behavior == StdIo.Pipe) try makePipe() else undefined;
|
||||
errdefer if (self.stdin_behavior == StdIo.Pipe) { destroyPipe(stdin_pipe); };
|
||||
|
||||
@ -387,11 +369,9 @@ pub const ChildProcess = struct {
|
||||
const err_pipe = try makePipe();
|
||||
errdefer destroyPipe(err_pipe);
|
||||
|
||||
block_SIGCHLD();
|
||||
const pid_result = posix.fork();
|
||||
const pid_err = posix.getErrno(pid_result);
|
||||
if (pid_err > 0) {
|
||||
restore_SIGCHLD();
|
||||
return switch (pid_err) {
|
||||
posix.EAGAIN, posix.ENOMEM, posix.ENOSYS => error.SystemResources,
|
||||
else => os.unexpectedErrorPosix(pid_err),
|
||||
@ -399,7 +379,6 @@ pub const ChildProcess = struct {
|
||||
}
|
||||
if (pid_result == 0) {
|
||||
// we are the child
|
||||
restore_SIGCHLD();
|
||||
|
||||
setUpChildIo(self.stdin_behavior, stdin_pipe[0], posix.STDIN_FILENO, dev_null_fd) catch
|
||||
|err| forkChildErrReport(err_pipe[1], err);
|
||||
@ -451,8 +430,6 @@ pub const ChildProcess = struct {
|
||||
// TODO make this atomic so it works even with threads
|
||||
children_nodes.prepend(&self.llnode);
|
||||
|
||||
restore_SIGCHLD();
|
||||
|
||||
if (self.stdin_behavior == StdIo.Pipe) { os.close(stdin_pipe[0]); }
|
||||
if (self.stdout_behavior == StdIo.Pipe) { os.close(stdout_pipe[1]); }
|
||||
if (self.stderr_behavior == StdIo.Pipe) { os.close(stderr_pipe[1]); }
|
||||
@ -824,30 +801,3 @@ fn handleTerm(pid: i32, status: i32) void {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const sigchld_set = x: {
|
||||
var signal_set = posix.empty_sigset;
|
||||
posix.sigaddset(&signal_set, posix.SIGCHLD);
|
||||
break :x signal_set;
|
||||
};
|
||||
|
||||
fn block_SIGCHLD() void {
|
||||
const err = posix.getErrno(posix.sigprocmask(posix.SIG_BLOCK, &sigchld_set, null));
|
||||
assert(err == 0);
|
||||
}
|
||||
|
||||
fn restore_SIGCHLD() void {
|
||||
const err = posix.getErrno(posix.sigprocmask(posix.SIG_UNBLOCK, &sigchld_set, null));
|
||||
assert(err == 0);
|
||||
}
|
||||
|
||||
const sigchld_action = posix.Sigaction {
|
||||
.handler = sigchld_handler,
|
||||
.mask = posix.empty_sigset,
|
||||
.flags = posix.SA_RESTART | posix.SA_NOCLDSTOP,
|
||||
};
|
||||
|
||||
fn install_SIGCHLD_handler() void {
|
||||
const err = posix.getErrno(posix.sigaction(posix.SIGCHLD, &sigchld_action, null));
|
||||
assert(err == 0);
|
||||
}
|
||||
|
149
std/unicode.zig
149
std/unicode.zig
@ -1,4 +1,5 @@
|
||||
const std = @import("./index.zig");
|
||||
const debug = std.debug;
|
||||
|
||||
/// Given the first byte of a UTF-8 codepoint,
|
||||
/// returns a number 1-4 indicating the total length of the codepoint in bytes.
|
||||
@ -25,8 +26,8 @@ pub fn utf8Decode(bytes: []const u8) !u32 {
|
||||
};
|
||||
}
|
||||
pub fn utf8Decode2(bytes: []const u8) !u32 {
|
||||
std.debug.assert(bytes.len == 2);
|
||||
std.debug.assert(bytes[0] & 0b11100000 == 0b11000000);
|
||||
debug.assert(bytes.len == 2);
|
||||
debug.assert(bytes[0] & 0b11100000 == 0b11000000);
|
||||
var value: u32 = bytes[0] & 0b00011111;
|
||||
|
||||
if (bytes[1] & 0b11000000 != 0b10000000) return error.Utf8ExpectedContinuation;
|
||||
@ -38,8 +39,8 @@ pub fn utf8Decode2(bytes: []const u8) !u32 {
|
||||
return value;
|
||||
}
|
||||
pub fn utf8Decode3(bytes: []const u8) !u32 {
|
||||
std.debug.assert(bytes.len == 3);
|
||||
std.debug.assert(bytes[0] & 0b11110000 == 0b11100000);
|
||||
debug.assert(bytes.len == 3);
|
||||
debug.assert(bytes[0] & 0b11110000 == 0b11100000);
|
||||
var value: u32 = bytes[0] & 0b00001111;
|
||||
|
||||
if (bytes[1] & 0b11000000 != 0b10000000) return error.Utf8ExpectedContinuation;
|
||||
@ -56,8 +57,8 @@ pub fn utf8Decode3(bytes: []const u8) !u32 {
|
||||
return value;
|
||||
}
|
||||
pub fn utf8Decode4(bytes: []const u8) !u32 {
|
||||
std.debug.assert(bytes.len == 4);
|
||||
std.debug.assert(bytes[0] & 0b11111000 == 0b11110000);
|
||||
debug.assert(bytes.len == 4);
|
||||
debug.assert(bytes[0] & 0b11111000 == 0b11110000);
|
||||
var value: u32 = bytes[0] & 0b00000111;
|
||||
|
||||
if (bytes[1] & 0b11000000 != 0b10000000) return error.Utf8ExpectedContinuation;
|
||||
@ -78,6 +79,136 @@ pub fn utf8Decode4(bytes: []const u8) !u32 {
|
||||
return value;
|
||||
}
|
||||
|
||||
pub fn utf8ValidateSlice(s: []const u8) bool {
|
||||
var i: usize = 0;
|
||||
while (i < s.len) {
|
||||
if (utf8ByteSequenceLength(s[i])) |cp_len| {
|
||||
if (i + cp_len > s.len) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (utf8Decode(s[i..i+cp_len])) |_| {} else |_| { return false; }
|
||||
i += cp_len;
|
||||
} else |err| {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const Utf8View = struct {
|
||||
bytes: []const u8,
|
||||
|
||||
pub fn init(s: []const u8) !Utf8View {
|
||||
if (!utf8ValidateSlice(s)) {
|
||||
return error.InvalidUtf8;
|
||||
}
|
||||
|
||||
return initUnchecked(s);
|
||||
}
|
||||
|
||||
pub fn initUnchecked(s: []const u8) Utf8View {
|
||||
return Utf8View {
|
||||
.bytes = s,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn initComptime(comptime s: []const u8) Utf8View {
|
||||
if (comptime init(s)) |r| {
|
||||
return r;
|
||||
} else |err| switch (err) {
|
||||
error.InvalidUtf8 => {
|
||||
@compileError("invalid utf8");
|
||||
unreachable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn Iterator(s: &const Utf8View) Utf8Iterator {
|
||||
return Utf8Iterator {
|
||||
.bytes = s.bytes,
|
||||
.i = 0,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const Utf8Iterator = struct {
|
||||
bytes: []const u8,
|
||||
i: usize,
|
||||
|
||||
pub fn nextCodepointSlice(it: &Utf8Iterator) ?[]const u8 {
|
||||
if (it.i >= it.bytes.len) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const cp_len = utf8ByteSequenceLength(it.bytes[it.i]) catch unreachable;
|
||||
|
||||
it.i += cp_len;
|
||||
return it.bytes[it.i-cp_len..it.i];
|
||||
}
|
||||
|
||||
pub fn nextCodepoint(it: &Utf8Iterator) ?u32 {
|
||||
const slice = it.nextCodepointSlice() ?? return null;
|
||||
|
||||
const r = switch (slice.len) {
|
||||
1 => u32(slice[0]),
|
||||
2 => utf8Decode2(slice),
|
||||
3 => utf8Decode3(slice),
|
||||
4 => utf8Decode4(slice),
|
||||
else => unreachable,
|
||||
};
|
||||
|
||||
return r catch unreachable;
|
||||
}
|
||||
};
|
||||
|
||||
test "utf8 iterator on ascii" {
|
||||
const s = Utf8View.initComptime("abc");
|
||||
|
||||
var it1 = s.Iterator();
|
||||
debug.assert(std.mem.eql(u8, "a", ??it1.nextCodepointSlice()));
|
||||
debug.assert(std.mem.eql(u8, "b", ??it1.nextCodepointSlice()));
|
||||
debug.assert(std.mem.eql(u8, "c", ??it1.nextCodepointSlice()));
|
||||
debug.assert(it1.nextCodepointSlice() == null);
|
||||
|
||||
var it2 = s.Iterator();
|
||||
debug.assert(??it2.nextCodepoint() == 'a');
|
||||
debug.assert(??it2.nextCodepoint() == 'b');
|
||||
debug.assert(??it2.nextCodepoint() == 'c');
|
||||
debug.assert(it2.nextCodepoint() == null);
|
||||
}
|
||||
|
||||
test "utf8 view bad" {
|
||||
// Compile-time error.
|
||||
// const s3 = Utf8View.initComptime("\xfe\xf2");
|
||||
|
||||
const s = Utf8View.init("hel\xadlo");
|
||||
if (s) |_| { unreachable; } else |err| { debug.assert(err == error.InvalidUtf8); }
|
||||
}
|
||||
|
||||
test "utf8 view ok" {
|
||||
const s = Utf8View.initComptime("東京市");
|
||||
|
||||
var it1 = s.Iterator();
|
||||
debug.assert(std.mem.eql(u8, "東", ??it1.nextCodepointSlice()));
|
||||
debug.assert(std.mem.eql(u8, "京", ??it1.nextCodepointSlice()));
|
||||
debug.assert(std.mem.eql(u8, "市", ??it1.nextCodepointSlice()));
|
||||
debug.assert(it1.nextCodepointSlice() == null);
|
||||
|
||||
var it2 = s.Iterator();
|
||||
debug.assert(??it2.nextCodepoint() == 0x6771);
|
||||
debug.assert(??it2.nextCodepoint() == 0x4eac);
|
||||
debug.assert(??it2.nextCodepoint() == 0x5e02);
|
||||
debug.assert(it2.nextCodepoint() == null);
|
||||
}
|
||||
|
||||
test "bad utf8 slice" {
|
||||
debug.assert(utf8ValidateSlice("abc"));
|
||||
debug.assert(!utf8ValidateSlice("abc\xc0"));
|
||||
debug.assert(!utf8ValidateSlice("abc\xc0abc"));
|
||||
debug.assert(utf8ValidateSlice("abc\xdf\xbf"));
|
||||
}
|
||||
|
||||
test "valid utf8" {
|
||||
testValid("\x00", 0x0);
|
||||
testValid("\x20", 0x20);
|
||||
@ -145,17 +276,17 @@ fn testError(bytes: []const u8, expected_err: error) void {
|
||||
if (testDecode(bytes)) |_| {
|
||||
unreachable;
|
||||
} else |err| {
|
||||
std.debug.assert(err == expected_err);
|
||||
debug.assert(err == expected_err);
|
||||
}
|
||||
}
|
||||
|
||||
fn testValid(bytes: []const u8, expected_codepoint: u32) void {
|
||||
std.debug.assert((testDecode(bytes) catch unreachable) == expected_codepoint);
|
||||
debug.assert((testDecode(bytes) catch unreachable) == expected_codepoint);
|
||||
}
|
||||
|
||||
fn testDecode(bytes: []const u8) !u32 {
|
||||
const length = try utf8ByteSequenceLength(bytes[0]);
|
||||
if (bytes.len < length) return error.UnexpectedEof;
|
||||
std.debug.assert(bytes.len == length);
|
||||
debug.assert(bytes.len == length);
|
||||
return utf8Decode(bytes);
|
||||
}
|
||||
|
@ -32,6 +32,108 @@ fn funcWithConstPtrPtr(x: &const &i32) void {
|
||||
**x += 1;
|
||||
}
|
||||
|
||||
test "implicitly cast a container to a const pointer of it" {
|
||||
const z = Struct(void) { .x = void{} };
|
||||
assert(0 == @sizeOf(@typeOf(z)));
|
||||
assert(void{} == Struct(void).pointer(z).x);
|
||||
assert(void{} == Struct(void).pointer(&z).x);
|
||||
assert(void{} == Struct(void).maybePointer(z).x);
|
||||
assert(void{} == Struct(void).maybePointer(&z).x);
|
||||
assert(void{} == Struct(void).maybePointer(null).x);
|
||||
const s = Struct(u8) { .x = 42 };
|
||||
assert(0 != @sizeOf(@typeOf(s)));
|
||||
assert(42 == Struct(u8).pointer(s).x);
|
||||
assert(42 == Struct(u8).pointer(&s).x);
|
||||
assert(42 == Struct(u8).maybePointer(s).x);
|
||||
assert(42 == Struct(u8).maybePointer(&s).x);
|
||||
assert(0 == Struct(u8).maybePointer(null).x);
|
||||
const u = Union { .x = 42 };
|
||||
assert(42 == Union.pointer(u).x);
|
||||
assert(42 == Union.pointer(&u).x);
|
||||
assert(42 == Union.maybePointer(u).x);
|
||||
assert(42 == Union.maybePointer(&u).x);
|
||||
assert(0 == Union.maybePointer(null).x);
|
||||
const e = Enum.Some;
|
||||
assert(Enum.Some == Enum.pointer(e));
|
||||
assert(Enum.Some == Enum.pointer(&e));
|
||||
assert(Enum.Some == Enum.maybePointer(e));
|
||||
assert(Enum.Some == Enum.maybePointer(&e));
|
||||
assert(Enum.None == Enum.maybePointer(null));
|
||||
}
|
||||
|
||||
fn Struct(comptime T: type) type {
|
||||
return struct {
|
||||
const Self = this;
|
||||
x: T,
|
||||
|
||||
fn pointer(self: &const Self) Self {
|
||||
return *self;
|
||||
}
|
||||
|
||||
fn maybePointer(self: ?&const Self) Self {
|
||||
const none = Self { .x = if (T == void) void{} else 0 };
|
||||
return *(self ?? &none);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const Union = union {
|
||||
x: u8,
|
||||
|
||||
fn pointer(self: &const Union) Union {
|
||||
return *self;
|
||||
}
|
||||
|
||||
fn maybePointer(self: ?&const Union) Union {
|
||||
const none = Union { .x = 0 };
|
||||
return *(self ?? &none);
|
||||
}
|
||||
};
|
||||
|
||||
const Enum = enum {
|
||||
None,
|
||||
Some,
|
||||
|
||||
fn pointer(self: &const Enum) Enum {
|
||||
return *self;
|
||||
}
|
||||
|
||||
fn maybePointer(self: ?&const Enum) Enum {
|
||||
return *(self ?? &Enum.None);
|
||||
}
|
||||
};
|
||||
|
||||
test "implicitly cast indirect pointer to maybe-indirect pointer" {
|
||||
const S = struct {
|
||||
const Self = this;
|
||||
x: u8,
|
||||
fn constConst(p: &const &const Self) u8 {
|
||||
return (*p).x;
|
||||
}
|
||||
fn maybeConstConst(p: ?&const &const Self) u8 {
|
||||
return (*??p).x;
|
||||
}
|
||||
fn constConstConst(p: &const &const &const Self) u8 {
|
||||
return (**p).x;
|
||||
}
|
||||
fn maybeConstConstConst(p: ?&const &const &const Self) u8 {
|
||||
return (**??p).x;
|
||||
}
|
||||
};
|
||||
const s = S { .x = 42 };
|
||||
const p = &s;
|
||||
const q = &p;
|
||||
const r = &q;
|
||||
assert(42 == S.constConst(p));
|
||||
assert(42 == S.constConst(q));
|
||||
assert(42 == S.maybeConstConst(p));
|
||||
assert(42 == S.maybeConstConst(q));
|
||||
assert(42 == S.constConstConst(q));
|
||||
assert(42 == S.constConstConst(r));
|
||||
assert(42 == S.maybeConstConstConst(q));
|
||||
assert(42 == S.maybeConstConstConst(r));
|
||||
}
|
||||
|
||||
test "explicit cast from integer to error type" {
|
||||
testCastIntToErr(error.ItBroke);
|
||||
comptime testCastIntToErr(error.ItBroke);
|
||||
|
@ -499,12 +499,29 @@ test "@canImplicitCast" {
|
||||
}
|
||||
|
||||
test "@typeName" {
|
||||
const Struct = struct {
|
||||
};
|
||||
const Union = union {
|
||||
unused: u8,
|
||||
};
|
||||
const Enum = enum {
|
||||
Unused,
|
||||
};
|
||||
comptime {
|
||||
assert(mem.eql(u8, @typeName(i64), "i64"));
|
||||
assert(mem.eql(u8, @typeName(&usize), "&usize"));
|
||||
// https://github.com/zig-lang/zig/issues/675
|
||||
assert(mem.eql(u8, @typeName(TypeFromFn(u8)), "TypeFromFn(u8)"));
|
||||
assert(mem.eql(u8, @typeName(Struct), "Struct"));
|
||||
assert(mem.eql(u8, @typeName(Union), "Union"));
|
||||
assert(mem.eql(u8, @typeName(Enum), "Enum"));
|
||||
}
|
||||
}
|
||||
|
||||
fn TypeFromFn(comptime T: type) type {
|
||||
return struct {};
|
||||
}
|
||||
|
||||
test "volatile load and store" {
|
||||
var number: i32 = 1234;
|
||||
const ptr = (&volatile i32)(&number);
|
||||
|
@ -3090,4 +3090,16 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
|
||||
,
|
||||
".tmp_source.zig:11:20: error: runtime cast to union 'Value' which has non-void fields",
|
||||
".tmp_source.zig:3:5: note: field 'A' has type 'i32'");
|
||||
|
||||
cases.add("self-referencing function pointer field",
|
||||
\\const S = struct {
|
||||
\\ f: fn(_: S) void,
|
||||
\\};
|
||||
\\fn f(_: S) void {
|
||||
\\}
|
||||
\\export fn entry() void {
|
||||
\\ var _ = S { .f = f };
|
||||
\\}
|
||||
,
|
||||
".tmp_source.zig:4:9: error: type 'S' is not copyable; cannot pass by value");
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user