Merge remote-tracking branch 'origin/master' into llvm6
commit
91ef68f9b1
|
@ -339,7 +339,7 @@ set(ZIG_SOURCES
|
||||||
"${CMAKE_SOURCE_DIR}/src/target.cpp"
|
"${CMAKE_SOURCE_DIR}/src/target.cpp"
|
||||||
"${CMAKE_SOURCE_DIR}/src/tokenizer.cpp"
|
"${CMAKE_SOURCE_DIR}/src/tokenizer.cpp"
|
||||||
"${CMAKE_SOURCE_DIR}/src/util.cpp"
|
"${CMAKE_SOURCE_DIR}/src/util.cpp"
|
||||||
"${CMAKE_SOURCE_DIR}/src/parsec.cpp"
|
"${CMAKE_SOURCE_DIR}/src/translate_c.cpp"
|
||||||
"${CMAKE_SOURCE_DIR}/src/zig_llvm.cpp"
|
"${CMAKE_SOURCE_DIR}/src/zig_llvm.cpp"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -58,5 +58,5 @@ pub fn build(b: &Builder) {
|
||||||
test_step.dependOn(tests.addCompileErrorTests(b, test_filter));
|
test_step.dependOn(tests.addCompileErrorTests(b, test_filter));
|
||||||
test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter));
|
test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter));
|
||||||
test_step.dependOn(tests.addDebugSafetyTests(b, test_filter));
|
test_step.dependOn(tests.addDebugSafetyTests(b, test_filter));
|
||||||
test_step.dependOn(tests.addParseCTests(b, test_filter));
|
test_step.dependOn(tests.addTranslateCTests(b, test_filter));
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,4 +22,4 @@ make install
|
||||||
./zig build --build-file ../build.zig test-compile-errors --verbose
|
./zig build --build-file ../build.zig test-compile-errors --verbose
|
||||||
./zig build --build-file ../build.zig test-asm-link --verbose
|
./zig build --build-file ../build.zig test-asm-link --verbose
|
||||||
./zig build --build-file ../build.zig test-debug-safety --verbose
|
./zig build --build-file ../build.zig test-debug-safety --verbose
|
||||||
./zig build --build-file ../build.zig test-parsec --verbose
|
./zig build --build-file ../build.zig test-translate-c --verbose
|
||||||
|
|
|
@ -70,10 +70,14 @@
|
||||||
<li><a href="#mersenne">Mersenne Twister Random Number Generator</a></li>
|
<li><a href="#mersenne">Mersenne Twister Random Number Generator</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<h3 id="hello">Hello World</h3>
|
<h3 id="hello">Hello World</h3>
|
||||||
<pre><code class="zig">const io = @import("std").io;
|
<pre><code class="zig">const std = @import("std");
|
||||||
|
|
||||||
pub fn main() -> %void {
|
pub fn main() -> %void {
|
||||||
%return io.stdout.printf("Hello, world!\n");
|
// If this program is run without stdout attached, exit with an error.
|
||||||
|
var stdout_file = %return std.io.getStdOut();
|
||||||
|
// If this program encounters pipe failure when printing to stdout, exit
|
||||||
|
// with an error.
|
||||||
|
%return stdout_file.write("Hello, world!\n");
|
||||||
}</code></pre>
|
}</code></pre>
|
||||||
<p>Build this with:</p>
|
<p>Build this with:</p>
|
||||||
<pre>zig build-exe hello.zig</pre>
|
<pre>zig build-exe hello.zig</pre>
|
||||||
|
|
|
@ -75,6 +75,7 @@
|
||||||
<li><a href="#slices">Slices</a></li>
|
<li><a href="#slices">Slices</a></li>
|
||||||
<li><a href="#struct">struct</a></li>
|
<li><a href="#struct">struct</a></li>
|
||||||
<li><a href="#enum">enum</a></li>
|
<li><a href="#enum">enum</a></li>
|
||||||
|
<li><a href="#union">union</a></li>
|
||||||
<li><a href="#switch">switch</a></li>
|
<li><a href="#switch">switch</a></li>
|
||||||
<li><a href="#while">while</a></li>
|
<li><a href="#while">while</a></li>
|
||||||
<li><a href="#for">for</a></li>
|
<li><a href="#for">for</a></li>
|
||||||
|
@ -209,6 +210,7 @@
|
||||||
<li><a href="#undef-invalid-error-code">Invalid Error Code</a></li>
|
<li><a href="#undef-invalid-error-code">Invalid Error Code</a></li>
|
||||||
<li><a href="#undef-invalid-enum-cast">Invalid Enum Cast</a></li>
|
<li><a href="#undef-invalid-enum-cast">Invalid Enum Cast</a></li>
|
||||||
<li><a href="#undef-incorrect-pointer-alignment">Incorrect Pointer Alignment</a></li>
|
<li><a href="#undef-incorrect-pointer-alignment">Incorrect Pointer Alignment</a></li>
|
||||||
|
<li><a href="#undef-bad-union-field">Wrong Union Field Access</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="#memory">Memory</a></li>
|
<li><a href="#memory">Memory</a></li>
|
||||||
|
@ -2189,6 +2191,8 @@ Test 4/4 enum builtins...OK</code></pre>
|
||||||
<li><a href="#builtin-enumTagName">@enumTagName</a></li>
|
<li><a href="#builtin-enumTagName">@enumTagName</a></li>
|
||||||
<li><a href="#builtin-memberCount">@memberCount</a></li>
|
<li><a href="#builtin-memberCount">@memberCount</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<h2 id="union">union</h2>
|
||||||
|
<p>TODO union documentation</p>
|
||||||
<h2 id="switch">switch</h2>
|
<h2 id="switch">switch</h2>
|
||||||
<pre><code class="zig">const assert = @import("std").debug.assert;
|
<pre><code class="zig">const assert = @import("std").debug.assert;
|
||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
|
@ -5117,6 +5121,9 @@ comptime {
|
||||||
<h3 id="undef-incorrect-pointer-alignment">Incorrect Pointer Alignment</h3>
|
<h3 id="undef-incorrect-pointer-alignment">Incorrect Pointer Alignment</h3>
|
||||||
<p>TODO</p>
|
<p>TODO</p>
|
||||||
|
|
||||||
|
<h3 id="undef-bad-union-field">Wrong Union Field Access</h3>
|
||||||
|
<p>TODO</p>
|
||||||
|
|
||||||
<h2 id="memory">Memory</h2>
|
<h2 id="memory">Memory</h2>
|
||||||
<p>TODO: explain no default allocator in zig</p>
|
<p>TODO: explain no default allocator in zig</p>
|
||||||
<p>TODO: show how to use the allocator interface</p>
|
<p>TODO: show how to use the allocator interface</p>
|
||||||
|
@ -5405,10 +5412,14 @@ const c = @cImport({
|
||||||
export fn decode_base_64(dest_ptr: &u8, dest_len: usize,
|
export fn decode_base_64(dest_ptr: &u8, dest_len: usize,
|
||||||
source_ptr: &const u8, source_len: usize) -> usize
|
source_ptr: &const u8, source_len: usize) -> usize
|
||||||
{
|
{
|
||||||
const src = source_ptr[0...source_len];
|
const src = source_ptr[0..source_len];
|
||||||
const dest = dest_ptr[0...dest_len];
|
const dest = dest_ptr[0..dest_len];
|
||||||
return base64.decode(dest, src).len;
|
const base64_decoder = base64.standard_decoder_unsafe;
|
||||||
}</code></pre>
|
const decoded_size = base64_decoder.calcSize(src);
|
||||||
|
base64_decoder.decode(dest[0..decoded_size], src);
|
||||||
|
return decoded_size;
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
<h4>test.c</h4>
|
<h4>test.c</h4>
|
||||||
<pre><code class="c">// This header is generated by zig from base64.zig
|
<pre><code class="c">// This header is generated by zig from base64.zig
|
||||||
#include "base64.h"
|
#include "base64.h"
|
||||||
|
|
|
@ -3,5 +3,8 @@ const base64 = @import("std").base64;
|
||||||
export fn decode_base_64(dest_ptr: &u8, dest_len: usize, source_ptr: &const u8, source_len: usize) -> usize {
|
export fn decode_base_64(dest_ptr: &u8, dest_len: usize, source_ptr: &const u8, source_len: usize) -> usize {
|
||||||
const src = source_ptr[0..source_len];
|
const src = source_ptr[0..source_len];
|
||||||
const dest = dest_ptr[0..dest_len];
|
const dest = dest_ptr[0..dest_len];
|
||||||
return base64.decode(dest, src).len;
|
const base64_decoder = base64.standard_decoder_unsafe;
|
||||||
|
const decoded_size = base64_decoder.calcSize(src);
|
||||||
|
base64_decoder.decode(dest[0..decoded_size], src);
|
||||||
|
return decoded_size;
|
||||||
}
|
}
|
||||||
|
|
|
@ -208,7 +208,7 @@ fn printUsage(outstream: &io.OutStream) -> %void {
|
||||||
\\ build-exe [source] create executable from source or object files
|
\\ build-exe [source] create executable from source or object files
|
||||||
\\ build-lib [source] create library from source or object files
|
\\ build-lib [source] create library from source or object files
|
||||||
\\ build-obj [source] create object from source or assembly
|
\\ build-obj [source] create object from source or assembly
|
||||||
\\ parsec [source] convert c code to zig code
|
\\ translate-c [source] convert c code to zig code
|
||||||
\\ targets list available compilation targets
|
\\ targets list available compilation targets
|
||||||
\\ test [source] create and run a test build
|
\\ test [source] create and run a test build
|
||||||
\\ version print version number and exit
|
\\ version print version number and exit
|
||||||
|
|
|
@ -36,6 +36,7 @@ struct IrInstructionCast;
|
||||||
struct IrBasicBlock;
|
struct IrBasicBlock;
|
||||||
struct ScopeDecls;
|
struct ScopeDecls;
|
||||||
struct ZigWindowsSDK;
|
struct ZigWindowsSDK;
|
||||||
|
struct Tld;
|
||||||
|
|
||||||
struct IrGotoItem {
|
struct IrGotoItem {
|
||||||
AstNode *source_node;
|
AstNode *source_node;
|
||||||
|
@ -59,7 +60,9 @@ struct IrExecutable {
|
||||||
Buf *c_import_buf;
|
Buf *c_import_buf;
|
||||||
AstNode *source_node;
|
AstNode *source_node;
|
||||||
IrExecutable *parent_exec;
|
IrExecutable *parent_exec;
|
||||||
|
IrExecutable *source_exec;
|
||||||
Scope *begin_scope;
|
Scope *begin_scope;
|
||||||
|
ZigList<Tld *> tld_list;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum OutType {
|
enum OutType {
|
||||||
|
@ -73,6 +76,7 @@ enum ConstParentId {
|
||||||
ConstParentIdNone,
|
ConstParentIdNone,
|
||||||
ConstParentIdStruct,
|
ConstParentIdStruct,
|
||||||
ConstParentIdArray,
|
ConstParentIdArray,
|
||||||
|
ConstParentIdUnion,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ConstParent {
|
struct ConstParent {
|
||||||
|
@ -87,6 +91,9 @@ struct ConstParent {
|
||||||
ConstExprValue *struct_val;
|
ConstExprValue *struct_val;
|
||||||
size_t field_index;
|
size_t field_index;
|
||||||
} p_struct;
|
} p_struct;
|
||||||
|
struct {
|
||||||
|
ConstExprValue *union_val;
|
||||||
|
} p_union;
|
||||||
} data;
|
} data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -100,6 +107,12 @@ struct ConstStructValue {
|
||||||
ConstParent parent;
|
ConstParent parent;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ConstUnionValue {
|
||||||
|
uint64_t tag;
|
||||||
|
ConstExprValue *payload;
|
||||||
|
ConstParent parent;
|
||||||
|
};
|
||||||
|
|
||||||
enum ConstArraySpecial {
|
enum ConstArraySpecial {
|
||||||
ConstArraySpecialNone,
|
ConstArraySpecialNone,
|
||||||
ConstArraySpecialUndef,
|
ConstArraySpecialUndef,
|
||||||
|
@ -238,6 +251,7 @@ struct ConstExprValue {
|
||||||
ErrorTableEntry *x_pure_err;
|
ErrorTableEntry *x_pure_err;
|
||||||
ConstEnumValue x_enum;
|
ConstEnumValue x_enum;
|
||||||
ConstStructValue x_struct;
|
ConstStructValue x_struct;
|
||||||
|
ConstUnionValue x_union;
|
||||||
ConstArrayValue x_array;
|
ConstArrayValue x_array;
|
||||||
ConstPtrValue x_ptr;
|
ConstPtrValue x_ptr;
|
||||||
ImportTableEntry *x_import;
|
ImportTableEntry *x_import;
|
||||||
|
@ -336,6 +350,13 @@ struct TypeEnumField {
|
||||||
uint32_t gen_index;
|
uint32_t gen_index;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct TypeUnionField {
|
||||||
|
Buf *name;
|
||||||
|
TypeTableEntry *type_entry;
|
||||||
|
uint32_t value;
|
||||||
|
uint32_t gen_index;
|
||||||
|
};
|
||||||
|
|
||||||
enum NodeType {
|
enum NodeType {
|
||||||
NodeTypeRoot,
|
NodeTypeRoot,
|
||||||
NodeTypeFnProto,
|
NodeTypeFnProto,
|
||||||
|
@ -1021,14 +1042,19 @@ struct TypeTableEntryEnumTag {
|
||||||
LLVMValueRef name_table;
|
LLVMValueRef name_table;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
uint32_t type_ptr_hash(const TypeTableEntry *ptr);
|
||||||
|
bool type_ptr_eql(const TypeTableEntry *a, const TypeTableEntry *b);
|
||||||
|
|
||||||
struct TypeTableEntryUnion {
|
struct TypeTableEntryUnion {
|
||||||
AstNode *decl_node;
|
AstNode *decl_node;
|
||||||
ContainerLayout layout;
|
ContainerLayout layout;
|
||||||
uint32_t src_field_count;
|
uint32_t src_field_count;
|
||||||
uint32_t gen_field_count;
|
uint32_t gen_field_count;
|
||||||
TypeStructField *fields;
|
TypeUnionField *fields;
|
||||||
uint64_t size_bytes;
|
|
||||||
bool is_invalid; // true if any fields are invalid
|
bool is_invalid; // true if any fields are invalid
|
||||||
|
TypeTableEntry *tag_type;
|
||||||
|
LLVMTypeRef union_type_ref;
|
||||||
|
|
||||||
ScopeDecls *decls_scope;
|
ScopeDecls *decls_scope;
|
||||||
|
|
||||||
// set this flag temporarily to detect infinite loops
|
// set this flag temporarily to detect infinite loops
|
||||||
|
@ -1039,6 +1065,13 @@ struct TypeTableEntryUnion {
|
||||||
|
|
||||||
bool zero_bits_loop_flag;
|
bool zero_bits_loop_flag;
|
||||||
bool zero_bits_known;
|
bool zero_bits_known;
|
||||||
|
uint32_t abi_alignment; // also figured out with zero_bits pass
|
||||||
|
|
||||||
|
size_t gen_union_index;
|
||||||
|
size_t gen_tag_index;
|
||||||
|
|
||||||
|
uint32_t union_size_bytes;
|
||||||
|
TypeTableEntry *most_aligned_union_member;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FnGenParamInfo {
|
struct FnGenParamInfo {
|
||||||
|
@ -1287,6 +1320,7 @@ enum PanicMsgId {
|
||||||
PanicMsgIdUnwrapMaybeFail,
|
PanicMsgIdUnwrapMaybeFail,
|
||||||
PanicMsgIdInvalidErrorCode,
|
PanicMsgIdInvalidErrorCode,
|
||||||
PanicMsgIdIncorrectAlignment,
|
PanicMsgIdIncorrectAlignment,
|
||||||
|
PanicMsgIdBadUnionField,
|
||||||
|
|
||||||
PanicMsgIdCount,
|
PanicMsgIdCount,
|
||||||
};
|
};
|
||||||
|
@ -1796,6 +1830,7 @@ enum IrInstructionId {
|
||||||
IrInstructionIdFieldPtr,
|
IrInstructionIdFieldPtr,
|
||||||
IrInstructionIdStructFieldPtr,
|
IrInstructionIdStructFieldPtr,
|
||||||
IrInstructionIdEnumFieldPtr,
|
IrInstructionIdEnumFieldPtr,
|
||||||
|
IrInstructionIdUnionFieldPtr,
|
||||||
IrInstructionIdElemPtr,
|
IrInstructionIdElemPtr,
|
||||||
IrInstructionIdVarPtr,
|
IrInstructionIdVarPtr,
|
||||||
IrInstructionIdCall,
|
IrInstructionIdCall,
|
||||||
|
@ -1805,6 +1840,7 @@ enum IrInstructionId {
|
||||||
IrInstructionIdContainerInitList,
|
IrInstructionIdContainerInitList,
|
||||||
IrInstructionIdContainerInitFields,
|
IrInstructionIdContainerInitFields,
|
||||||
IrInstructionIdStructInit,
|
IrInstructionIdStructInit,
|
||||||
|
IrInstructionIdUnionInit,
|
||||||
IrInstructionIdUnreachable,
|
IrInstructionIdUnreachable,
|
||||||
IrInstructionIdTypeOf,
|
IrInstructionIdTypeOf,
|
||||||
IrInstructionIdToPtrType,
|
IrInstructionIdToPtrType,
|
||||||
|
@ -2060,6 +2096,14 @@ struct IrInstructionEnumFieldPtr {
|
||||||
bool is_const;
|
bool is_const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct IrInstructionUnionFieldPtr {
|
||||||
|
IrInstruction base;
|
||||||
|
|
||||||
|
IrInstruction *union_ptr;
|
||||||
|
TypeUnionField *field;
|
||||||
|
bool is_const;
|
||||||
|
};
|
||||||
|
|
||||||
struct IrInstructionElemPtr {
|
struct IrInstructionElemPtr {
|
||||||
IrInstruction base;
|
IrInstruction base;
|
||||||
|
|
||||||
|
@ -2150,6 +2194,15 @@ struct IrInstructionStructInit {
|
||||||
LLVMValueRef tmp_ptr;
|
LLVMValueRef tmp_ptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct IrInstructionUnionInit {
|
||||||
|
IrInstruction base;
|
||||||
|
|
||||||
|
TypeTableEntry *union_type;
|
||||||
|
TypeUnionField *field;
|
||||||
|
IrInstruction *init_value;
|
||||||
|
LLVMValueRef tmp_ptr;
|
||||||
|
};
|
||||||
|
|
||||||
struct IrInstructionUnreachable {
|
struct IrInstructionUnreachable {
|
||||||
IrInstruction base;
|
IrInstruction base;
|
||||||
};
|
};
|
||||||
|
|
343
src/analyze.cpp
343
src/analyze.cpp
|
@ -28,7 +28,7 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type);
|
||||||
|
|
||||||
ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg) {
|
ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg) {
|
||||||
if (node->owner->c_import_node != nullptr) {
|
if (node->owner->c_import_node != nullptr) {
|
||||||
// if this happens, then parsec generated code that
|
// if this happens, then translate_c generated code that
|
||||||
// failed semantic analysis, which isn't supposed to happen
|
// failed semantic analysis, which isn't supposed to happen
|
||||||
ErrorMsg *err = add_node_error(g, node->owner->c_import_node,
|
ErrorMsg *err = add_node_error(g, node->owner->c_import_node,
|
||||||
buf_sprintf("compiler bug: @cImport generated invalid zig code"));
|
buf_sprintf("compiler bug: @cImport generated invalid zig code"));
|
||||||
|
@ -48,7 +48,7 @@ ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg) {
|
||||||
|
|
||||||
ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *msg) {
|
ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *msg) {
|
||||||
if (node->owner->c_import_node != nullptr) {
|
if (node->owner->c_import_node != nullptr) {
|
||||||
// if this happens, then parsec generated code that
|
// if this happens, then translate_c generated code that
|
||||||
// failed semantic analysis, which isn't supposed to happen
|
// failed semantic analysis, which isn't supposed to happen
|
||||||
|
|
||||||
Buf *note_path = buf_create_from_str("?.c");
|
Buf *note_path = buf_create_from_str("?.c");
|
||||||
|
@ -338,7 +338,7 @@ TypeTableEntry *get_smallest_unsigned_int_type(CodeGen *g, uint64_t x) {
|
||||||
TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type, bool is_const,
|
TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type, bool is_const,
|
||||||
bool is_volatile, uint32_t byte_alignment, uint32_t bit_offset, uint32_t unaligned_bit_count)
|
bool is_volatile, uint32_t byte_alignment, uint32_t bit_offset, uint32_t unaligned_bit_count)
|
||||||
{
|
{
|
||||||
assert(child_type->id != TypeTableEntryIdInvalid);
|
assert(!type_is_invalid(child_type));
|
||||||
|
|
||||||
TypeId type_id = {};
|
TypeId type_id = {};
|
||||||
TypeTableEntry **parent_pointer = nullptr;
|
TypeTableEntry **parent_pointer = nullptr;
|
||||||
|
@ -1008,11 +1008,12 @@ TypeTableEntry *get_partial_container_type(CodeGen *g, Scope *scope, ContainerKi
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t line = decl_node ? decl_node->line : 0;
|
size_t line = decl_node ? decl_node->line : 0;
|
||||||
|
unsigned dwarf_kind = ZigLLVMTag_DW_structure_type();
|
||||||
|
|
||||||
ImportTableEntry *import = get_scope_import(scope);
|
ImportTableEntry *import = get_scope_import(scope);
|
||||||
entry->type_ref = LLVMStructCreateNamed(LLVMGetGlobalContext(), name);
|
entry->type_ref = LLVMStructCreateNamed(LLVMGetGlobalContext(), name);
|
||||||
entry->di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder,
|
entry->di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder,
|
||||||
ZigLLVMTag_DW_structure_type(), name,
|
dwarf_kind, name,
|
||||||
ZigLLVMFileToScope(import->di_file), import->di_file, (unsigned)(line + 1));
|
ZigLLVMFileToScope(import->di_file), import->di_file, (unsigned)(line + 1));
|
||||||
|
|
||||||
buf_init_from_str(&entry->name, name);
|
buf_init_from_str(&entry->name, name);
|
||||||
|
@ -1285,7 +1286,7 @@ static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
resolve_enum_zero_bits(g, enum_type);
|
resolve_enum_zero_bits(g, enum_type);
|
||||||
if (enum_type->data.enumeration.is_invalid)
|
if (type_is_invalid(enum_type))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
AstNode *decl_node = enum_type->data.enumeration.decl_node;
|
AstNode *decl_node = enum_type->data.enumeration.decl_node;
|
||||||
|
@ -1834,7 +1835,246 @@ static void resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) {
|
static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) {
|
||||||
zig_panic("TODO");
|
assert(union_type->id == TypeTableEntryIdUnion);
|
||||||
|
|
||||||
|
if (union_type->data.unionation.complete)
|
||||||
|
return;
|
||||||
|
|
||||||
|
resolve_union_zero_bits(g, union_type);
|
||||||
|
if (type_is_invalid(union_type))
|
||||||
|
return;
|
||||||
|
|
||||||
|
AstNode *decl_node = union_type->data.unionation.decl_node;
|
||||||
|
|
||||||
|
if (union_type->data.unionation.embedded_in_current) {
|
||||||
|
if (!union_type->data.unionation.reported_infinite_err) {
|
||||||
|
union_type->data.unionation.reported_infinite_err = true;
|
||||||
|
add_node_error(g, decl_node, buf_sprintf("union '%s' contains itself", buf_ptr(&union_type->name)));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(!union_type->data.unionation.zero_bits_loop_flag);
|
||||||
|
assert(decl_node->type == NodeTypeContainerDecl);
|
||||||
|
assert(union_type->di_type);
|
||||||
|
|
||||||
|
uint32_t field_count = union_type->data.unionation.src_field_count;
|
||||||
|
|
||||||
|
assert(union_type->data.unionation.fields);
|
||||||
|
|
||||||
|
uint32_t gen_field_count = union_type->data.unionation.gen_field_count;
|
||||||
|
ZigLLVMDIType **union_inner_di_types = allocate<ZigLLVMDIType*>(gen_field_count);
|
||||||
|
|
||||||
|
TypeTableEntry *most_aligned_union_member = nullptr;
|
||||||
|
uint64_t size_of_most_aligned_member_in_bits = 0;
|
||||||
|
uint64_t biggest_align_in_bits = 0;
|
||||||
|
uint64_t biggest_size_in_bits = 0;
|
||||||
|
|
||||||
|
bool auto_layout = (union_type->data.unionation.layout == ContainerLayoutAuto);
|
||||||
|
ZigLLVMDIEnumerator **di_enumerators = allocate<ZigLLVMDIEnumerator*>(field_count);
|
||||||
|
|
||||||
|
Scope *scope = &union_type->data.unionation.decls_scope->base;
|
||||||
|
ImportTableEntry *import = get_scope_import(scope);
|
||||||
|
|
||||||
|
// set temporary flag
|
||||||
|
union_type->data.unionation.embedded_in_current = true;
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < field_count; i += 1) {
|
||||||
|
AstNode *field_node = decl_node->data.container_decl.fields.at(i);
|
||||||
|
TypeUnionField *type_union_field = &union_type->data.unionation.fields[i];
|
||||||
|
TypeTableEntry *field_type = type_union_field->type_entry;
|
||||||
|
|
||||||
|
ensure_complete_type(g, field_type);
|
||||||
|
if (type_is_invalid(field_type)) {
|
||||||
|
union_type->data.unionation.is_invalid = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!type_has_bits(field_type))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
di_enumerators[i] = ZigLLVMCreateDebugEnumerator(g->dbuilder, buf_ptr(type_union_field->name), i);
|
||||||
|
|
||||||
|
uint64_t store_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, field_type->type_ref);
|
||||||
|
uint64_t abi_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, field_type->type_ref);
|
||||||
|
|
||||||
|
assert(store_size_in_bits > 0);
|
||||||
|
assert(abi_align_in_bits > 0);
|
||||||
|
|
||||||
|
union_inner_di_types[type_union_field->gen_index] = ZigLLVMCreateDebugMemberType(g->dbuilder,
|
||||||
|
ZigLLVMTypeToScope(union_type->di_type), buf_ptr(type_union_field->name),
|
||||||
|
import->di_file, (unsigned)(field_node->line + 1),
|
||||||
|
store_size_in_bits,
|
||||||
|
abi_align_in_bits,
|
||||||
|
0,
|
||||||
|
0, field_type->di_type);
|
||||||
|
|
||||||
|
biggest_size_in_bits = max(biggest_size_in_bits, store_size_in_bits);
|
||||||
|
|
||||||
|
if (!most_aligned_union_member || abi_align_in_bits > biggest_align_in_bits) {
|
||||||
|
most_aligned_union_member = field_type;
|
||||||
|
biggest_align_in_bits = abi_align_in_bits;
|
||||||
|
size_of_most_aligned_member_in_bits = store_size_in_bits;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// unset temporary flag
|
||||||
|
union_type->data.unionation.embedded_in_current = false;
|
||||||
|
union_type->data.unionation.complete = true;
|
||||||
|
union_type->data.unionation.union_size_bytes = biggest_size_in_bits / 8;
|
||||||
|
union_type->data.unionation.most_aligned_union_member = most_aligned_union_member;
|
||||||
|
|
||||||
|
if (union_type->data.unionation.is_invalid)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (union_type->zero_bits) {
|
||||||
|
union_type->type_ref = LLVMVoidType();
|
||||||
|
|
||||||
|
uint64_t debug_size_in_bits = 0;
|
||||||
|
uint64_t debug_align_in_bits = 0;
|
||||||
|
ZigLLVMDIType **di_root_members = nullptr;
|
||||||
|
size_t debug_member_count = 0;
|
||||||
|
ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugUnionType(g->dbuilder,
|
||||||
|
ZigLLVMFileToScope(import->di_file),
|
||||||
|
buf_ptr(&union_type->name),
|
||||||
|
import->di_file, (unsigned)(decl_node->line + 1),
|
||||||
|
debug_size_in_bits,
|
||||||
|
debug_align_in_bits,
|
||||||
|
0, di_root_members, (int)debug_member_count, 0, "");
|
||||||
|
|
||||||
|
ZigLLVMReplaceTemporary(g->dbuilder, union_type->di_type, replacement_di_type);
|
||||||
|
union_type->di_type = replacement_di_type;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(most_aligned_union_member != nullptr);
|
||||||
|
|
||||||
|
bool want_safety = auto_layout && (field_count >= 2);
|
||||||
|
uint64_t padding_in_bits = biggest_size_in_bits - size_of_most_aligned_member_in_bits;
|
||||||
|
|
||||||
|
|
||||||
|
if (!want_safety) {
|
||||||
|
if (padding_in_bits > 0) {
|
||||||
|
TypeTableEntry *u8_type = get_int_type(g, false, 8);
|
||||||
|
TypeTableEntry *padding_array = get_array_type(g, u8_type, padding_in_bits / 8);
|
||||||
|
LLVMTypeRef union_element_types[] = {
|
||||||
|
most_aligned_union_member->type_ref,
|
||||||
|
padding_array->type_ref,
|
||||||
|
};
|
||||||
|
LLVMStructSetBody(union_type->type_ref, union_element_types, 2, false);
|
||||||
|
} else {
|
||||||
|
LLVMStructSetBody(union_type->type_ref, &most_aligned_union_member->type_ref, 1, false);
|
||||||
|
}
|
||||||
|
union_type->data.unionation.union_type_ref = union_type->type_ref;
|
||||||
|
union_type->data.unionation.gen_tag_index = SIZE_MAX;
|
||||||
|
union_type->data.unionation.gen_union_index = SIZE_MAX;
|
||||||
|
|
||||||
|
assert(8*LLVMABIAlignmentOfType(g->target_data_ref, union_type->type_ref) >= biggest_align_in_bits);
|
||||||
|
assert(8*LLVMStoreSizeOfType(g->target_data_ref, union_type->type_ref) >= biggest_size_in_bits);
|
||||||
|
|
||||||
|
// create debug type for union
|
||||||
|
ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugUnionType(g->dbuilder,
|
||||||
|
ZigLLVMFileToScope(import->di_file), buf_ptr(&union_type->name),
|
||||||
|
import->di_file, (unsigned)(decl_node->line + 1),
|
||||||
|
biggest_size_in_bits, biggest_align_in_bits, 0, union_inner_di_types,
|
||||||
|
gen_field_count, 0, "");
|
||||||
|
|
||||||
|
ZigLLVMReplaceTemporary(g->dbuilder, union_type->di_type, replacement_di_type);
|
||||||
|
union_type->di_type = replacement_di_type;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LLVMTypeRef union_type_ref;
|
||||||
|
if (padding_in_bits > 0) {
|
||||||
|
TypeTableEntry *u8_type = get_int_type(g, false, 8);
|
||||||
|
TypeTableEntry *padding_array = get_array_type(g, u8_type, padding_in_bits / 8);
|
||||||
|
LLVMTypeRef union_element_types[] = {
|
||||||
|
most_aligned_union_member->type_ref,
|
||||||
|
padding_array->type_ref,
|
||||||
|
};
|
||||||
|
union_type_ref = LLVMStructType(union_element_types, 2, false);
|
||||||
|
} else {
|
||||||
|
union_type_ref = most_aligned_union_member->type_ref;
|
||||||
|
}
|
||||||
|
union_type->data.unionation.union_type_ref = union_type_ref;
|
||||||
|
|
||||||
|
assert(8*LLVMABIAlignmentOfType(g->target_data_ref, union_type_ref) >= biggest_align_in_bits);
|
||||||
|
assert(8*LLVMStoreSizeOfType(g->target_data_ref, union_type_ref) >= biggest_size_in_bits);
|
||||||
|
|
||||||
|
// create llvm type for root struct
|
||||||
|
TypeTableEntry *tag_int_type = get_smallest_unsigned_int_type(g, field_count - 1);
|
||||||
|
TypeTableEntry *tag_type_entry = tag_int_type;
|
||||||
|
union_type->data.unionation.tag_type = tag_type_entry;
|
||||||
|
uint64_t align_of_tag_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_int_type->type_ref);
|
||||||
|
|
||||||
|
if (align_of_tag_in_bits >= biggest_align_in_bits) {
|
||||||
|
union_type->data.unionation.gen_tag_index = 0;
|
||||||
|
union_type->data.unionation.gen_union_index = 1;
|
||||||
|
} else {
|
||||||
|
union_type->data.unionation.gen_union_index = 0;
|
||||||
|
union_type->data.unionation.gen_tag_index = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
LLVMTypeRef root_struct_element_types[2];
|
||||||
|
root_struct_element_types[union_type->data.unionation.gen_tag_index] = tag_type_entry->type_ref;
|
||||||
|
root_struct_element_types[union_type->data.unionation.gen_union_index] = union_type_ref;
|
||||||
|
LLVMStructSetBody(union_type->type_ref, root_struct_element_types, 2, false);
|
||||||
|
|
||||||
|
|
||||||
|
// create debug type for root struct
|
||||||
|
|
||||||
|
// create debug type for tag
|
||||||
|
uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, tag_type_entry->type_ref);
|
||||||
|
uint64_t tag_debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_type_entry->type_ref);
|
||||||
|
ZigLLVMDIType *tag_di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder,
|
||||||
|
ZigLLVMTypeToScope(union_type->di_type), "AnonEnum",
|
||||||
|
import->di_file, (unsigned)(decl_node->line + 1),
|
||||||
|
tag_debug_size_in_bits, tag_debug_align_in_bits, di_enumerators, field_count,
|
||||||
|
tag_type_entry->di_type, "");
|
||||||
|
|
||||||
|
// create debug type for union
|
||||||
|
ZigLLVMDIType *union_di_type = ZigLLVMCreateDebugUnionType(g->dbuilder,
|
||||||
|
ZigLLVMTypeToScope(union_type->di_type), "AnonUnion",
|
||||||
|
import->di_file, (unsigned)(decl_node->line + 1),
|
||||||
|
biggest_size_in_bits, biggest_align_in_bits, 0, union_inner_di_types,
|
||||||
|
gen_field_count, 0, "");
|
||||||
|
|
||||||
|
uint64_t union_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, union_type->type_ref,
|
||||||
|
union_type->data.unionation.gen_union_index);
|
||||||
|
uint64_t tag_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, union_type->type_ref,
|
||||||
|
union_type->data.unionation.gen_tag_index);
|
||||||
|
|
||||||
|
ZigLLVMDIType *union_member_di_type = ZigLLVMCreateDebugMemberType(g->dbuilder,
|
||||||
|
ZigLLVMTypeToScope(union_type->di_type), "union_field",
|
||||||
|
import->di_file, (unsigned)(decl_node->line + 1),
|
||||||
|
biggest_size_in_bits,
|
||||||
|
biggest_align_in_bits,
|
||||||
|
union_offset_in_bits,
|
||||||
|
0, union_di_type);
|
||||||
|
ZigLLVMDIType *tag_member_di_type = ZigLLVMCreateDebugMemberType(g->dbuilder,
|
||||||
|
ZigLLVMTypeToScope(union_type->di_type), "tag_field",
|
||||||
|
import->di_file, (unsigned)(decl_node->line + 1),
|
||||||
|
tag_debug_size_in_bits,
|
||||||
|
tag_debug_align_in_bits,
|
||||||
|
tag_offset_in_bits,
|
||||||
|
0, tag_di_type);
|
||||||
|
|
||||||
|
ZigLLVMDIType *di_root_members[2];
|
||||||
|
di_root_members[union_type->data.unionation.gen_tag_index] = tag_member_di_type;
|
||||||
|
di_root_members[union_type->data.unionation.gen_union_index] = union_member_di_type;
|
||||||
|
|
||||||
|
uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, union_type->type_ref);
|
||||||
|
uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, union_type->type_ref);
|
||||||
|
ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder,
|
||||||
|
ZigLLVMFileToScope(import->di_file),
|
||||||
|
buf_ptr(&union_type->name),
|
||||||
|
import->di_file, (unsigned)(decl_node->line + 1),
|
||||||
|
debug_size_in_bits,
|
||||||
|
debug_align_in_bits,
|
||||||
|
0, nullptr, di_root_members, 2, 0, nullptr, "");
|
||||||
|
|
||||||
|
ZigLLVMReplaceTemporary(g->dbuilder, union_type->di_type, replacement_di_type);
|
||||||
|
union_type->di_type = replacement_di_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type) {
|
static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type) {
|
||||||
|
@ -1873,7 +2113,7 @@ static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type) {
|
||||||
type_enum_field->value = i;
|
type_enum_field->value = i;
|
||||||
|
|
||||||
type_ensure_zero_bits_known(g, field_type);
|
type_ensure_zero_bits_known(g, field_type);
|
||||||
if (field_type->id == TypeTableEntryIdInvalid) {
|
if (type_is_invalid(field_type)) {
|
||||||
enum_type->data.enumeration.is_invalid = true;
|
enum_type->data.enumeration.is_invalid = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -1980,7 +2220,69 @@ static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {
|
static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {
|
||||||
zig_panic("TODO resolve_union_zero_bits");
|
assert(union_type->id == TypeTableEntryIdUnion);
|
||||||
|
|
||||||
|
if (union_type->data.unionation.zero_bits_known)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (union_type->data.unionation.zero_bits_loop_flag) {
|
||||||
|
union_type->data.unionation.zero_bits_known = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
union_type->data.unionation.zero_bits_loop_flag = true;
|
||||||
|
|
||||||
|
AstNode *decl_node = union_type->data.unionation.decl_node;
|
||||||
|
assert(decl_node->type == NodeTypeContainerDecl);
|
||||||
|
assert(union_type->di_type);
|
||||||
|
|
||||||
|
assert(!union_type->data.unionation.fields);
|
||||||
|
uint32_t field_count = (uint32_t)decl_node->data.container_decl.fields.length;
|
||||||
|
union_type->data.unionation.src_field_count = field_count;
|
||||||
|
union_type->data.unionation.fields = allocate<TypeUnionField>(field_count);
|
||||||
|
|
||||||
|
uint32_t biggest_align_bytes = 0;
|
||||||
|
|
||||||
|
Scope *scope = &union_type->data.unionation.decls_scope->base;
|
||||||
|
|
||||||
|
uint32_t gen_field_index = 0;
|
||||||
|
for (uint32_t i = 0; i < field_count; i += 1) {
|
||||||
|
AstNode *field_node = decl_node->data.container_decl.fields.at(i);
|
||||||
|
TypeUnionField *type_union_field = &union_type->data.unionation.fields[i];
|
||||||
|
type_union_field->name = field_node->data.struct_field.name;
|
||||||
|
TypeTableEntry *field_type = analyze_type_expr(g, scope, field_node->data.struct_field.type);
|
||||||
|
type_union_field->type_entry = field_type;
|
||||||
|
type_union_field->value = i;
|
||||||
|
|
||||||
|
type_ensure_zero_bits_known(g, field_type);
|
||||||
|
if (type_is_invalid(field_type)) {
|
||||||
|
union_type->data.unionation.is_invalid = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!type_has_bits(field_type))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
type_union_field->gen_index = gen_field_index;
|
||||||
|
gen_field_index += 1;
|
||||||
|
|
||||||
|
uint32_t field_align_bytes = get_abi_alignment(g, field_type);
|
||||||
|
if (field_align_bytes > biggest_align_bytes) {
|
||||||
|
biggest_align_bytes = field_align_bytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool auto_layout = (union_type->data.unionation.layout == ContainerLayoutAuto);
|
||||||
|
|
||||||
|
union_type->data.unionation.zero_bits_loop_flag = false;
|
||||||
|
union_type->data.unionation.gen_field_count = gen_field_index;
|
||||||
|
union_type->zero_bits = (gen_field_index == 0 && (field_count < 2 || !auto_layout));
|
||||||
|
union_type->data.unionation.zero_bits_known = true;
|
||||||
|
|
||||||
|
// also compute abi_alignment
|
||||||
|
if (!union_type->zero_bits) {
|
||||||
|
union_type->data.unionation.abi_alignment = biggest_align_bytes;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void get_fully_qualified_decl_name_internal(Buf *buf, Scope *scope, uint8_t sep) {
|
static void get_fully_qualified_decl_name_internal(Buf *buf, Scope *scope, uint8_t sep) {
|
||||||
|
@ -2851,6 +3153,18 @@ TypeStructField *find_struct_type_field(TypeTableEntry *type_entry, Buf *name) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TypeUnionField *find_union_type_field(TypeTableEntry *type_entry, Buf *name) {
|
||||||
|
assert(type_entry->id == TypeTableEntryIdUnion);
|
||||||
|
assert(type_entry->data.unionation.complete);
|
||||||
|
for (uint32_t i = 0; i < type_entry->data.unionation.src_field_count; i += 1) {
|
||||||
|
TypeUnionField *field = &type_entry->data.unionation.fields[i];
|
||||||
|
if (buf_eql_buf(field->name, name)) {
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
static bool is_container(TypeTableEntry *type_entry) {
|
static bool is_container(TypeTableEntry *type_entry) {
|
||||||
switch (type_entry->id) {
|
switch (type_entry->id) {
|
||||||
case TypeTableEntryIdInvalid:
|
case TypeTableEntryIdInvalid:
|
||||||
|
@ -4703,6 +5017,8 @@ ConstParent *get_const_val_parent(CodeGen *g, ConstExprValue *value) {
|
||||||
return &value->data.x_array.s_none.parent;
|
return &value->data.x_array.s_none.parent;
|
||||||
} else if (type_entry->id == TypeTableEntryIdStruct) {
|
} else if (type_entry->id == TypeTableEntryIdStruct) {
|
||||||
return &value->data.x_struct.parent;
|
return &value->data.x_struct.parent;
|
||||||
|
} else if (type_entry->id == TypeTableEntryIdUnion) {
|
||||||
|
return &value->data.x_union.parent;
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -4914,7 +5230,8 @@ uint32_t get_abi_alignment(CodeGen *g, TypeTableEntry *type_entry) {
|
||||||
assert(type_entry->data.enumeration.abi_alignment != 0);
|
assert(type_entry->data.enumeration.abi_alignment != 0);
|
||||||
return type_entry->data.enumeration.abi_alignment;
|
return type_entry->data.enumeration.abi_alignment;
|
||||||
} else if (type_entry->id == TypeTableEntryIdUnion) {
|
} else if (type_entry->id == TypeTableEntryIdUnion) {
|
||||||
zig_panic("TODO");
|
assert(type_entry->data.unionation.abi_alignment != 0);
|
||||||
|
return type_entry->data.unionation.abi_alignment;
|
||||||
} else if (type_entry->id == TypeTableEntryIdOpaque) {
|
} else if (type_entry->id == TypeTableEntryIdOpaque) {
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
|
@ -4929,3 +5246,11 @@ TypeTableEntry *get_align_amt_type(CodeGen *g) {
|
||||||
}
|
}
|
||||||
return g->align_amt_type;
|
return g->align_amt_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t type_ptr_hash(const TypeTableEntry *ptr) {
|
||||||
|
return hash_ptr((void*)ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool type_ptr_eql(const TypeTableEntry *a, const TypeTableEntry *b) {
|
||||||
|
return a == b;
|
||||||
|
}
|
||||||
|
|
|
@ -63,6 +63,7 @@ void resolve_container_type(CodeGen *g, TypeTableEntry *type_entry);
|
||||||
TypeStructField *find_struct_type_field(TypeTableEntry *type_entry, Buf *name);
|
TypeStructField *find_struct_type_field(TypeTableEntry *type_entry, Buf *name);
|
||||||
ScopeDecls *get_container_scope(TypeTableEntry *type_entry);
|
ScopeDecls *get_container_scope(TypeTableEntry *type_entry);
|
||||||
TypeEnumField *find_enum_type_field(TypeTableEntry *enum_type, Buf *name);
|
TypeEnumField *find_enum_type_field(TypeTableEntry *enum_type, Buf *name);
|
||||||
|
TypeUnionField *find_union_type_field(TypeTableEntry *type_entry, Buf *name);
|
||||||
bool is_container_ref(TypeTableEntry *type_entry);
|
bool is_container_ref(TypeTableEntry *type_entry);
|
||||||
void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node);
|
void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node);
|
||||||
void scan_import(CodeGen *g, ImportTableEntry *import);
|
void scan_import(CodeGen *g, ImportTableEntry *import);
|
||||||
|
|
|
@ -120,6 +120,7 @@ static void begin_token(CTokenize *ctok, CTokId id) {
|
||||||
case CTokIdLParen:
|
case CTokIdLParen:
|
||||||
case CTokIdRParen:
|
case CTokIdRParen:
|
||||||
case CTokIdEOF:
|
case CTokIdEOF:
|
||||||
|
case CTokIdDot:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -216,9 +217,8 @@ void tokenize_c_macro(CTokenize *ctok, const uint8_t *c) {
|
||||||
buf_append_char(&ctok->buf, '0');
|
buf_append_char(&ctok->buf, '0');
|
||||||
break;
|
break;
|
||||||
case '.':
|
case '.':
|
||||||
begin_token(ctok, CTokIdNumLitFloat);
|
begin_token(ctok, CTokIdDot);
|
||||||
ctok->state = CTokStateFloat;
|
end_token(ctok);
|
||||||
buf_init_from_str(&ctok->buf, "0.");
|
|
||||||
break;
|
break;
|
||||||
case '(':
|
case '(':
|
||||||
begin_token(ctok, CTokIdLParen);
|
begin_token(ctok, CTokIdLParen);
|
||||||
|
@ -238,6 +238,8 @@ void tokenize_c_macro(CTokenize *ctok, const uint8_t *c) {
|
||||||
break;
|
break;
|
||||||
case CTokStateFloat:
|
case CTokStateFloat:
|
||||||
switch (*c) {
|
switch (*c) {
|
||||||
|
case '.':
|
||||||
|
break;
|
||||||
case 'e':
|
case 'e':
|
||||||
case 'E':
|
case 'E':
|
||||||
buf_append_char(&ctok->buf, 'e');
|
buf_append_char(&ctok->buf, 'e');
|
||||||
|
|
|
@ -21,6 +21,7 @@ enum CTokId {
|
||||||
CTokIdLParen,
|
CTokIdLParen,
|
||||||
CTokIdRParen,
|
CTokIdRParen,
|
||||||
CTokIdEOF,
|
CTokIdEOF,
|
||||||
|
CTokIdDot,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum CNumLitSuffix {
|
enum CNumLitSuffix {
|
||||||
|
|
161
src/codegen.cpp
161
src/codegen.cpp
|
@ -15,7 +15,7 @@
|
||||||
#include "ir.hpp"
|
#include "ir.hpp"
|
||||||
#include "link.hpp"
|
#include "link.hpp"
|
||||||
#include "os.hpp"
|
#include "os.hpp"
|
||||||
#include "parsec.hpp"
|
#include "translate_c.hpp"
|
||||||
#include "target.hpp"
|
#include "target.hpp"
|
||||||
#include "zig_llvm.hpp"
|
#include "zig_llvm.hpp"
|
||||||
|
|
||||||
|
@ -810,6 +810,8 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) {
|
||||||
return buf_create_from_str("invalid error code");
|
return buf_create_from_str("invalid error code");
|
||||||
case PanicMsgIdIncorrectAlignment:
|
case PanicMsgIdIncorrectAlignment:
|
||||||
return buf_create_from_str("incorrect alignment");
|
return buf_create_from_str("incorrect alignment");
|
||||||
|
case PanicMsgIdBadUnionField:
|
||||||
|
return buf_create_from_str("access of inactive union field");
|
||||||
}
|
}
|
||||||
zig_unreachable();
|
zig_unreachable();
|
||||||
}
|
}
|
||||||
|
@ -2393,6 +2395,50 @@ static LLVMValueRef ir_render_enum_field_ptr(CodeGen *g, IrExecutable *executabl
|
||||||
return bitcasted_union_field_ptr;
|
return bitcasted_union_field_ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static LLVMValueRef ir_render_union_field_ptr(CodeGen *g, IrExecutable *executable,
|
||||||
|
IrInstructionUnionFieldPtr *instruction)
|
||||||
|
{
|
||||||
|
TypeTableEntry *union_ptr_type = instruction->union_ptr->value.type;
|
||||||
|
assert(union_ptr_type->id == TypeTableEntryIdPointer);
|
||||||
|
TypeTableEntry *union_type = union_ptr_type->data.pointer.child_type;
|
||||||
|
assert(union_type->id == TypeTableEntryIdUnion);
|
||||||
|
|
||||||
|
TypeUnionField *field = instruction->field;
|
||||||
|
|
||||||
|
if (!type_has_bits(field->type_entry))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
LLVMValueRef union_ptr = ir_llvm_value(g, instruction->union_ptr);
|
||||||
|
LLVMTypeRef field_type_ref = LLVMPointerType(field->type_entry->type_ref, 0);
|
||||||
|
|
||||||
|
if (union_type->data.unionation.gen_tag_index == SIZE_MAX) {
|
||||||
|
LLVMValueRef union_field_ptr = LLVMBuildStructGEP(g->builder, union_ptr, 0, "");
|
||||||
|
LLVMValueRef bitcasted_union_field_ptr = LLVMBuildBitCast(g->builder, union_field_ptr, field_type_ref, "");
|
||||||
|
return bitcasted_union_field_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ir_want_debug_safety(g, &instruction->base)) {
|
||||||
|
LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, union_ptr, union_type->data.unionation.gen_tag_index, "");
|
||||||
|
LLVMValueRef tag_value = gen_load_untyped(g, tag_field_ptr, 0, false, "");
|
||||||
|
LLVMValueRef expected_tag_value = LLVMConstInt(union_type->data.unionation.tag_type->type_ref,
|
||||||
|
field->value, false);
|
||||||
|
|
||||||
|
LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnionCheckOk");
|
||||||
|
LLVMBasicBlockRef bad_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnionCheckFail");
|
||||||
|
LLVMValueRef ok_val = LLVMBuildICmp(g->builder, LLVMIntEQ, tag_value, expected_tag_value, "");
|
||||||
|
LLVMBuildCondBr(g->builder, ok_val, ok_block, bad_block);
|
||||||
|
|
||||||
|
LLVMPositionBuilderAtEnd(g->builder, bad_block);
|
||||||
|
gen_debug_safety_crash(g, PanicMsgIdBadUnionField);
|
||||||
|
|
||||||
|
LLVMPositionBuilderAtEnd(g->builder, ok_block);
|
||||||
|
}
|
||||||
|
|
||||||
|
LLVMValueRef union_field_ptr = LLVMBuildStructGEP(g->builder, union_ptr, union_type->data.unionation.gen_union_index, "");
|
||||||
|
LLVMValueRef bitcasted_union_field_ptr = LLVMBuildBitCast(g->builder, union_field_ptr, field_type_ref, "");
|
||||||
|
return bitcasted_union_field_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
static size_t find_asm_index(CodeGen *g, AstNode *node, AsmToken *tok) {
|
static size_t find_asm_index(CodeGen *g, AstNode *node, AsmToken *tok) {
|
||||||
const char *ptr = buf_ptr(node->data.asm_expr.asm_template) + tok->start + 2;
|
const char *ptr = buf_ptr(node->data.asm_expr.asm_template) + tok->start + 2;
|
||||||
size_t len = tok->end - tok->start - 2;
|
size_t len = tok->end - tok->start - 2;
|
||||||
|
@ -3365,6 +3411,42 @@ static LLVMValueRef ir_render_struct_init(CodeGen *g, IrExecutable *executable,
|
||||||
return instruction->tmp_ptr;
|
return instruction->tmp_ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static LLVMValueRef ir_render_union_init(CodeGen *g, IrExecutable *executable, IrInstructionUnionInit *instruction) {
|
||||||
|
TypeUnionField *type_union_field = instruction->field;
|
||||||
|
|
||||||
|
if (!type_has_bits(type_union_field->type_entry))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
uint32_t field_align_bytes = get_abi_alignment(g, type_union_field->type_entry);
|
||||||
|
TypeTableEntry *ptr_type = get_pointer_to_type_extra(g, type_union_field->type_entry,
|
||||||
|
false, false, field_align_bytes,
|
||||||
|
0, 0);
|
||||||
|
|
||||||
|
LLVMValueRef uncasted_union_ptr;
|
||||||
|
// Even if safety is off in this block, if the union type has the safety field, we have to populate it
|
||||||
|
// correctly. Otherwise safety code somewhere other than here could fail.
|
||||||
|
TypeTableEntry *union_type = instruction->union_type;
|
||||||
|
if (union_type->data.unionation.gen_tag_index != SIZE_MAX) {
|
||||||
|
LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr,
|
||||||
|
union_type->data.unionation.gen_tag_index, "");
|
||||||
|
LLVMValueRef tag_value = LLVMConstInt(union_type->data.unionation.tag_type->type_ref,
|
||||||
|
type_union_field->value, false);
|
||||||
|
gen_store_untyped(g, tag_value, tag_field_ptr, 0, false);
|
||||||
|
|
||||||
|
uncasted_union_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr,
|
||||||
|
(unsigned)union_type->data.unionation.gen_union_index, "");
|
||||||
|
} else {
|
||||||
|
uncasted_union_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, (unsigned)0, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
LLVMValueRef field_ptr = LLVMBuildBitCast(g->builder, uncasted_union_ptr, ptr_type->type_ref, "");
|
||||||
|
LLVMValueRef value = ir_llvm_value(g, instruction->init_value);
|
||||||
|
|
||||||
|
gen_assign_raw(g, field_ptr, ptr_type, value);
|
||||||
|
|
||||||
|
return instruction->tmp_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
static LLVMValueRef ir_render_container_init_list(CodeGen *g, IrExecutable *executable,
|
static LLVMValueRef ir_render_container_init_list(CodeGen *g, IrExecutable *executable,
|
||||||
IrInstructionContainerInitList *instruction)
|
IrInstructionContainerInitList *instruction)
|
||||||
{
|
{
|
||||||
|
@ -3486,6 +3568,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
|
||||||
return ir_render_struct_field_ptr(g, executable, (IrInstructionStructFieldPtr *)instruction);
|
return ir_render_struct_field_ptr(g, executable, (IrInstructionStructFieldPtr *)instruction);
|
||||||
case IrInstructionIdEnumFieldPtr:
|
case IrInstructionIdEnumFieldPtr:
|
||||||
return ir_render_enum_field_ptr(g, executable, (IrInstructionEnumFieldPtr *)instruction);
|
return ir_render_enum_field_ptr(g, executable, (IrInstructionEnumFieldPtr *)instruction);
|
||||||
|
case IrInstructionIdUnionFieldPtr:
|
||||||
|
return ir_render_union_field_ptr(g, executable, (IrInstructionUnionFieldPtr *)instruction);
|
||||||
case IrInstructionIdAsm:
|
case IrInstructionIdAsm:
|
||||||
return ir_render_asm(g, executable, (IrInstructionAsm *)instruction);
|
return ir_render_asm(g, executable, (IrInstructionAsm *)instruction);
|
||||||
case IrInstructionIdTestNonNull:
|
case IrInstructionIdTestNonNull:
|
||||||
|
@ -3544,6 +3628,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
|
||||||
return ir_render_init_enum(g, executable, (IrInstructionInitEnum *)instruction);
|
return ir_render_init_enum(g, executable, (IrInstructionInitEnum *)instruction);
|
||||||
case IrInstructionIdStructInit:
|
case IrInstructionIdStructInit:
|
||||||
return ir_render_struct_init(g, executable, (IrInstructionStructInit *)instruction);
|
return ir_render_struct_init(g, executable, (IrInstructionStructInit *)instruction);
|
||||||
|
case IrInstructionIdUnionInit:
|
||||||
|
return ir_render_union_init(g, executable, (IrInstructionUnionInit *)instruction);
|
||||||
case IrInstructionIdPtrCast:
|
case IrInstructionIdPtrCast:
|
||||||
return ir_render_ptr_cast(g, executable, (IrInstructionPtrCast *)instruction);
|
return ir_render_ptr_cast(g, executable, (IrInstructionPtrCast *)instruction);
|
||||||
case IrInstructionIdBitCast:
|
case IrInstructionIdBitCast:
|
||||||
|
@ -3595,6 +3681,7 @@ static void ir_render(CodeGen *g, FnTableEntry *fn_entry) {
|
||||||
|
|
||||||
static LLVMValueRef gen_const_ptr_struct_recursive(CodeGen *g, ConstExprValue *struct_const_val, size_t field_index);
|
static LLVMValueRef gen_const_ptr_struct_recursive(CodeGen *g, ConstExprValue *struct_const_val, size_t field_index);
|
||||||
static LLVMValueRef gen_const_ptr_array_recursive(CodeGen *g, ConstExprValue *array_const_val, size_t index);
|
static LLVMValueRef gen_const_ptr_array_recursive(CodeGen *g, ConstExprValue *array_const_val, size_t index);
|
||||||
|
static LLVMValueRef gen_const_ptr_union_recursive(CodeGen *g, ConstExprValue *array_const_val);
|
||||||
|
|
||||||
static LLVMValueRef gen_parent_ptr(CodeGen *g, ConstExprValue *val, ConstParent *parent) {
|
static LLVMValueRef gen_parent_ptr(CodeGen *g, ConstExprValue *val, ConstParent *parent) {
|
||||||
switch (parent->id) {
|
switch (parent->id) {
|
||||||
|
@ -3608,6 +3695,8 @@ static LLVMValueRef gen_parent_ptr(CodeGen *g, ConstExprValue *val, ConstParent
|
||||||
case ConstParentIdArray:
|
case ConstParentIdArray:
|
||||||
return gen_const_ptr_array_recursive(g, parent->data.p_array.array_val,
|
return gen_const_ptr_array_recursive(g, parent->data.p_array.array_val,
|
||||||
parent->data.p_array.elem_index);
|
parent->data.p_array.elem_index);
|
||||||
|
case ConstParentIdUnion:
|
||||||
|
return gen_const_ptr_union_recursive(g, parent->data.p_union.union_val);
|
||||||
}
|
}
|
||||||
zig_unreachable();
|
zig_unreachable();
|
||||||
}
|
}
|
||||||
|
@ -3637,6 +3726,18 @@ static LLVMValueRef gen_const_ptr_struct_recursive(CodeGen *g, ConstExprValue *s
|
||||||
return LLVMConstInBoundsGEP(base_ptr, indices, 2);
|
return LLVMConstInBoundsGEP(base_ptr, indices, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static LLVMValueRef gen_const_ptr_union_recursive(CodeGen *g, ConstExprValue *union_const_val) {
|
||||||
|
ConstParent *parent = &union_const_val->data.x_union.parent;
|
||||||
|
LLVMValueRef base_ptr = gen_parent_ptr(g, union_const_val, parent);
|
||||||
|
|
||||||
|
TypeTableEntry *u32 = g->builtin_types.entry_u32;
|
||||||
|
LLVMValueRef indices[] = {
|
||||||
|
LLVMConstNull(u32->type_ref),
|
||||||
|
LLVMConstInt(u32->type_ref, 0, false),
|
||||||
|
};
|
||||||
|
return LLVMConstInBoundsGEP(base_ptr, indices, 2);
|
||||||
|
}
|
||||||
|
|
||||||
static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, ConstExprValue *const_val) {
|
static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, ConstExprValue *const_val) {
|
||||||
switch (const_val->special) {
|
switch (const_val->special) {
|
||||||
case ConstValSpecialRuntime:
|
case ConstValSpecialRuntime:
|
||||||
|
@ -3872,10 +3973,6 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
|
||||||
return LLVMConstNamedStruct(type_entry->type_ref, fields, type_entry->data.structure.gen_field_count);
|
return LLVMConstNamedStruct(type_entry->type_ref, fields, type_entry->data.structure.gen_field_count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case TypeTableEntryIdUnion:
|
|
||||||
{
|
|
||||||
zig_panic("TODO");
|
|
||||||
}
|
|
||||||
case TypeTableEntryIdArray:
|
case TypeTableEntryIdArray:
|
||||||
{
|
{
|
||||||
uint64_t len = type_entry->data.array.len;
|
uint64_t len = type_entry->data.array.len;
|
||||||
|
@ -3898,6 +3995,55 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
|
||||||
return LLVMConstArray(element_type_ref, values, (unsigned)len);
|
return LLVMConstArray(element_type_ref, values, (unsigned)len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case TypeTableEntryIdUnion:
|
||||||
|
{
|
||||||
|
LLVMTypeRef union_type_ref = type_entry->data.unionation.union_type_ref;
|
||||||
|
ConstExprValue *payload_value = const_val->data.x_union.payload;
|
||||||
|
assert(payload_value != nullptr);
|
||||||
|
|
||||||
|
if (!type_has_bits(payload_value->type)) {
|
||||||
|
return LLVMGetUndef(union_type_ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t field_type_bytes = LLVMStoreSizeOfType(g->target_data_ref, payload_value->type->type_ref);
|
||||||
|
uint64_t pad_bytes = type_entry->data.unionation.union_size_bytes - field_type_bytes;
|
||||||
|
LLVMValueRef correctly_typed_value = gen_const_val(g, payload_value);
|
||||||
|
bool make_unnamed_struct = is_llvm_value_unnamed_type(payload_value->type, correctly_typed_value) ||
|
||||||
|
payload_value->type != type_entry->data.unionation.most_aligned_union_member;
|
||||||
|
|
||||||
|
LLVMValueRef union_value_ref;
|
||||||
|
{
|
||||||
|
if (pad_bytes == 0) {
|
||||||
|
union_value_ref = correctly_typed_value;
|
||||||
|
} else {
|
||||||
|
LLVMValueRef fields[2];
|
||||||
|
fields[0] = correctly_typed_value;
|
||||||
|
fields[1] = LLVMGetUndef(LLVMArrayType(LLVMInt8Type(), (unsigned)pad_bytes));
|
||||||
|
if (make_unnamed_struct || type_entry->data.unionation.gen_tag_index != SIZE_MAX) {
|
||||||
|
union_value_ref = LLVMConstStruct(fields, 2, false);
|
||||||
|
} else {
|
||||||
|
union_value_ref = LLVMConstNamedStruct(union_type_ref, fields, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type_entry->data.unionation.gen_tag_index == SIZE_MAX) {
|
||||||
|
return union_value_ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
LLVMValueRef tag_value = LLVMConstInt(type_entry->data.unionation.tag_type->type_ref, const_val->data.x_union.tag, false);
|
||||||
|
|
||||||
|
LLVMValueRef fields[2];
|
||||||
|
fields[type_entry->data.unionation.gen_union_index] = union_value_ref;
|
||||||
|
fields[type_entry->data.unionation.gen_tag_index] = tag_value;
|
||||||
|
|
||||||
|
if (make_unnamed_struct) {
|
||||||
|
return LLVMConstStruct(fields, 2, false);
|
||||||
|
} else {
|
||||||
|
return LLVMConstNamedStruct(type_entry->type_ref, fields, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
case TypeTableEntryIdEnum:
|
case TypeTableEntryIdEnum:
|
||||||
{
|
{
|
||||||
LLVMTypeRef tag_type_ref = type_entry->data.enumeration.tag_type->type_ref;
|
LLVMTypeRef tag_type_ref = type_entry->data.enumeration.tag_type->type_ref;
|
||||||
|
@ -4376,6 +4522,9 @@ static void do_code_gen(CodeGen *g) {
|
||||||
} else if (instruction->id == IrInstructionIdStructInit) {
|
} else if (instruction->id == IrInstructionIdStructInit) {
|
||||||
IrInstructionStructInit *struct_init_instruction = (IrInstructionStructInit *)instruction;
|
IrInstructionStructInit *struct_init_instruction = (IrInstructionStructInit *)instruction;
|
||||||
slot = &struct_init_instruction->tmp_ptr;
|
slot = &struct_init_instruction->tmp_ptr;
|
||||||
|
} else if (instruction->id == IrInstructionIdUnionInit) {
|
||||||
|
IrInstructionUnionInit *union_init_instruction = (IrInstructionUnionInit *)instruction;
|
||||||
|
slot = &union_init_instruction->tmp_ptr;
|
||||||
} else if (instruction->id == IrInstructionIdCall) {
|
} else if (instruction->id == IrInstructionIdCall) {
|
||||||
IrInstructionCall *call_instruction = (IrInstructionCall *)instruction;
|
IrInstructionCall *call_instruction = (IrInstructionCall *)instruction;
|
||||||
slot = &call_instruction->tmp_ptr;
|
slot = &call_instruction->tmp_ptr;
|
||||||
|
@ -5204,7 +5353,7 @@ static void init(CodeGen *g) {
|
||||||
define_builtin_compile_vars(g);
|
define_builtin_compile_vars(g);
|
||||||
}
|
}
|
||||||
|
|
||||||
void codegen_parsec(CodeGen *g, Buf *full_path) {
|
void codegen_translate_c(CodeGen *g, Buf *full_path) {
|
||||||
find_libc_include_path(g);
|
find_libc_include_path(g);
|
||||||
|
|
||||||
Buf *src_basename = buf_alloc();
|
Buf *src_basename = buf_alloc();
|
||||||
|
|
|
@ -56,7 +56,7 @@ PackageTableEntry *codegen_create_package(CodeGen *g, const char *root_src_dir,
|
||||||
void codegen_add_assembly(CodeGen *g, Buf *path);
|
void codegen_add_assembly(CodeGen *g, Buf *path);
|
||||||
void codegen_add_object(CodeGen *g, Buf *object_path);
|
void codegen_add_object(CodeGen *g, Buf *object_path);
|
||||||
|
|
||||||
void codegen_parsec(CodeGen *g, Buf *path);
|
void codegen_translate_c(CodeGen *g, Buf *path);
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
261
src/ir.cpp
261
src/ir.cpp
|
@ -11,7 +11,7 @@
|
||||||
#include "ir.hpp"
|
#include "ir.hpp"
|
||||||
#include "ir_print.hpp"
|
#include "ir_print.hpp"
|
||||||
#include "os.hpp"
|
#include "os.hpp"
|
||||||
#include "parsec.hpp"
|
#include "translate_c.hpp"
|
||||||
#include "range_set.hpp"
|
#include "range_set.hpp"
|
||||||
#include "softfloat.hpp"
|
#include "softfloat.hpp"
|
||||||
|
|
||||||
|
@ -227,6 +227,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionEnumFieldPtr *)
|
||||||
return IrInstructionIdEnumFieldPtr;
|
return IrInstructionIdEnumFieldPtr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionUnionFieldPtr *) {
|
||||||
|
return IrInstructionIdUnionFieldPtr;
|
||||||
|
}
|
||||||
|
|
||||||
static constexpr IrInstructionId ir_instruction_id(IrInstructionElemPtr *) {
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionElemPtr *) {
|
||||||
return IrInstructionIdElemPtr;
|
return IrInstructionIdElemPtr;
|
||||||
}
|
}
|
||||||
|
@ -351,6 +355,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionStructInit *) {
|
||||||
return IrInstructionIdStructInit;
|
return IrInstructionIdStructInit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionUnionInit *) {
|
||||||
|
return IrInstructionIdUnionInit;
|
||||||
|
}
|
||||||
|
|
||||||
static constexpr IrInstructionId ir_instruction_id(IrInstructionMinValue *) {
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionMinValue *) {
|
||||||
return IrInstructionIdMinValue;
|
return IrInstructionIdMinValue;
|
||||||
}
|
}
|
||||||
|
@ -922,6 +930,27 @@ static IrInstruction *ir_build_enum_field_ptr_from(IrBuilder *irb, IrInstruction
|
||||||
return new_instruction;
|
return new_instruction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static IrInstruction *ir_build_union_field_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
||||||
|
IrInstruction *union_ptr, TypeUnionField *field)
|
||||||
|
{
|
||||||
|
IrInstructionUnionFieldPtr *instruction = ir_build_instruction<IrInstructionUnionFieldPtr>(irb, scope, source_node);
|
||||||
|
instruction->union_ptr = union_ptr;
|
||||||
|
instruction->field = field;
|
||||||
|
|
||||||
|
ir_ref_instruction(union_ptr, irb->current_basic_block);
|
||||||
|
|
||||||
|
return &instruction->base;
|
||||||
|
}
|
||||||
|
|
||||||
|
static IrInstruction *ir_build_union_field_ptr_from(IrBuilder *irb, IrInstruction *old_instruction,
|
||||||
|
IrInstruction *union_ptr, TypeUnionField *type_union_field)
|
||||||
|
{
|
||||||
|
IrInstruction *new_instruction = ir_build_union_field_ptr(irb, old_instruction->scope,
|
||||||
|
old_instruction->source_node, union_ptr, type_union_field);
|
||||||
|
ir_link_new_instruction(new_instruction, old_instruction);
|
||||||
|
return new_instruction;
|
||||||
|
}
|
||||||
|
|
||||||
static IrInstruction *ir_build_call(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
static IrInstruction *ir_build_call(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
||||||
FnTableEntry *fn_entry, IrInstruction *fn_ref, size_t arg_count, IrInstruction **args,
|
FnTableEntry *fn_entry, IrInstruction *fn_ref, size_t arg_count, IrInstruction **args,
|
||||||
bool is_comptime, bool is_inline)
|
bool is_comptime, bool is_inline)
|
||||||
|
@ -1112,6 +1141,28 @@ static IrInstruction *ir_build_struct_init_from(IrBuilder *irb, IrInstruction *o
|
||||||
return new_instruction;
|
return new_instruction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static IrInstruction *ir_build_union_init(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
||||||
|
TypeTableEntry *union_type, TypeUnionField *field, IrInstruction *init_value)
|
||||||
|
{
|
||||||
|
IrInstructionUnionInit *union_init_instruction = ir_build_instruction<IrInstructionUnionInit>(irb, scope, source_node);
|
||||||
|
union_init_instruction->union_type = union_type;
|
||||||
|
union_init_instruction->field = field;
|
||||||
|
union_init_instruction->init_value = init_value;
|
||||||
|
|
||||||
|
ir_ref_instruction(init_value, irb->current_basic_block);
|
||||||
|
|
||||||
|
return &union_init_instruction->base;
|
||||||
|
}
|
||||||
|
|
||||||
|
static IrInstruction *ir_build_union_init_from(IrBuilder *irb, IrInstruction *old_instruction,
|
||||||
|
TypeTableEntry *union_type, TypeUnionField *field, IrInstruction *init_value)
|
||||||
|
{
|
||||||
|
IrInstruction *new_instruction = ir_build_union_init(irb, old_instruction->scope,
|
||||||
|
old_instruction->source_node, union_type, field, init_value);
|
||||||
|
ir_link_new_instruction(new_instruction, old_instruction);
|
||||||
|
return new_instruction;
|
||||||
|
}
|
||||||
|
|
||||||
static IrInstruction *ir_build_unreachable(IrBuilder *irb, Scope *scope, AstNode *source_node) {
|
static IrInstruction *ir_build_unreachable(IrBuilder *irb, Scope *scope, AstNode *source_node) {
|
||||||
IrInstructionUnreachable *unreachable_instruction =
|
IrInstructionUnreachable *unreachable_instruction =
|
||||||
ir_build_instruction<IrInstructionUnreachable>(irb, scope, source_node);
|
ir_build_instruction<IrInstructionUnreachable>(irb, scope, source_node);
|
||||||
|
@ -2422,6 +2473,13 @@ static IrInstruction *ir_instruction_enumfieldptr_get_dep(IrInstructionEnumField
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static IrInstruction *ir_instruction_unionfieldptr_get_dep(IrInstructionUnionFieldPtr *instruction, size_t index) {
|
||||||
|
switch (index) {
|
||||||
|
case 0: return instruction->union_ptr;
|
||||||
|
default: return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static IrInstruction *ir_instruction_elemptr_get_dep(IrInstructionElemPtr *instruction, size_t index) {
|
static IrInstruction *ir_instruction_elemptr_get_dep(IrInstructionElemPtr *instruction, size_t index) {
|
||||||
switch (index) {
|
switch (index) {
|
||||||
case 0: return instruction->array_ptr;
|
case 0: return instruction->array_ptr;
|
||||||
|
@ -2485,6 +2543,13 @@ static IrInstruction *ir_instruction_structinit_get_dep(IrInstructionStructInit
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static IrInstruction *ir_instruction_unioninit_get_dep(IrInstructionUnionInit *instruction, size_t index) {
|
||||||
|
switch (index) {
|
||||||
|
case 0: return instruction->init_value;
|
||||||
|
default: return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static IrInstruction *ir_instruction_unreachable_get_dep(IrInstructionUnreachable *instruction, size_t index) {
|
static IrInstruction *ir_instruction_unreachable_get_dep(IrInstructionUnreachable *instruction, size_t index) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -3099,6 +3164,8 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t
|
||||||
return ir_instruction_structfieldptr_get_dep((IrInstructionStructFieldPtr *) instruction, index);
|
return ir_instruction_structfieldptr_get_dep((IrInstructionStructFieldPtr *) instruction, index);
|
||||||
case IrInstructionIdEnumFieldPtr:
|
case IrInstructionIdEnumFieldPtr:
|
||||||
return ir_instruction_enumfieldptr_get_dep((IrInstructionEnumFieldPtr *) instruction, index);
|
return ir_instruction_enumfieldptr_get_dep((IrInstructionEnumFieldPtr *) instruction, index);
|
||||||
|
case IrInstructionIdUnionFieldPtr:
|
||||||
|
return ir_instruction_unionfieldptr_get_dep((IrInstructionUnionFieldPtr *) instruction, index);
|
||||||
case IrInstructionIdElemPtr:
|
case IrInstructionIdElemPtr:
|
||||||
return ir_instruction_elemptr_get_dep((IrInstructionElemPtr *) instruction, index);
|
return ir_instruction_elemptr_get_dep((IrInstructionElemPtr *) instruction, index);
|
||||||
case IrInstructionIdVarPtr:
|
case IrInstructionIdVarPtr:
|
||||||
|
@ -3117,6 +3184,8 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t
|
||||||
return ir_instruction_containerinitfields_get_dep((IrInstructionContainerInitFields *) instruction, index);
|
return ir_instruction_containerinitfields_get_dep((IrInstructionContainerInitFields *) instruction, index);
|
||||||
case IrInstructionIdStructInit:
|
case IrInstructionIdStructInit:
|
||||||
return ir_instruction_structinit_get_dep((IrInstructionStructInit *) instruction, index);
|
return ir_instruction_structinit_get_dep((IrInstructionStructInit *) instruction, index);
|
||||||
|
case IrInstructionIdUnionInit:
|
||||||
|
return ir_instruction_unioninit_get_dep((IrInstructionUnionInit *) instruction, index);
|
||||||
case IrInstructionIdUnreachable:
|
case IrInstructionIdUnreachable:
|
||||||
return ir_instruction_unreachable_get_dep((IrInstructionUnreachable *) instruction, index);
|
return ir_instruction_unreachable_get_dep((IrInstructionUnreachable *) instruction, index);
|
||||||
case IrInstructionIdTypeOf:
|
case IrInstructionIdTypeOf:
|
||||||
|
@ -6233,8 +6302,9 @@ static Buf *get_anon_type_name(CodeGen *codegen, IrExecutable *exec, const char
|
||||||
buf_appendf(name, ")");
|
buf_appendf(name, ")");
|
||||||
return name;
|
return name;
|
||||||
} else {
|
} else {
|
||||||
|
//Note: C-imports do not have valid location information
|
||||||
return buf_sprintf("(anonymous %s at %s:%" ZIG_PRI_usize ":%" ZIG_PRI_usize ")", kind_name,
|
return buf_sprintf("(anonymous %s at %s:%" ZIG_PRI_usize ":%" ZIG_PRI_usize ")", kind_name,
|
||||||
buf_ptr(source_node->owner->path), source_node->line + 1, source_node->column + 1);
|
(source_node->owner->path != nullptr) ? buf_ptr(source_node->owner->path) : "(null)", source_node->line + 1, source_node->column + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6263,6 +6333,9 @@ static IrInstruction *ir_gen_container_decl(IrBuilder *irb, Scope *parent_scope,
|
||||||
}
|
}
|
||||||
irb->codegen->resolve_queue.append(&tld_container->base);
|
irb->codegen->resolve_queue.append(&tld_container->base);
|
||||||
|
|
||||||
|
// Add this to the list to mark as invalid if analyzing this exec fails.
|
||||||
|
irb->exec->tld_list.append(&tld_container->base);
|
||||||
|
|
||||||
return ir_build_const_type(irb, parent_scope, node, container_type);
|
return ir_build_const_type(irb, parent_scope, node, container_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6485,6 +6558,20 @@ static bool ir_goto_pass2(IrBuilder *irb) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void invalidate_exec(IrExecutable *exec) {
|
||||||
|
if (exec->invalid)
|
||||||
|
return;
|
||||||
|
|
||||||
|
exec->invalid = true;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < exec->tld_list.length; i += 1) {
|
||||||
|
exec->tld_list.items[i]->resolution = TldResolutionInvalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exec->source_exec != nullptr)
|
||||||
|
invalidate_exec(exec->source_exec);
|
||||||
|
}
|
||||||
|
|
||||||
bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_executable) {
|
bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_executable) {
|
||||||
assert(node->owner);
|
assert(node->owner);
|
||||||
|
|
||||||
|
@ -6508,7 +6595,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ir_goto_pass2(irb)) {
|
if (!ir_goto_pass2(irb)) {
|
||||||
irb->exec->invalid = true;
|
invalidate_exec(ir_executable);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6534,7 +6621,7 @@ static void add_call_stack_errors(CodeGen *codegen, IrExecutable *exec, ErrorMsg
|
||||||
}
|
}
|
||||||
|
|
||||||
static ErrorMsg *exec_add_error_node(CodeGen *codegen, IrExecutable *exec, AstNode *source_node, Buf *msg) {
|
static ErrorMsg *exec_add_error_node(CodeGen *codegen, IrExecutable *exec, AstNode *source_node, Buf *msg) {
|
||||||
exec->invalid = true;
|
invalidate_exec(exec);
|
||||||
ErrorMsg *err_msg = add_node_error(codegen, source_node, msg);
|
ErrorMsg *err_msg = add_node_error(codegen, source_node, msg);
|
||||||
if (exec->parent_exec) {
|
if (exec->parent_exec) {
|
||||||
add_call_stack_errors(codegen, exec, err_msg, 10);
|
add_call_stack_errors(codegen, exec, err_msg, 10);
|
||||||
|
@ -7897,6 +7984,9 @@ static IrInstruction *ir_get_const_ptr(IrAnalyze *ira, IrInstruction *instructio
|
||||||
ConstExprValue *const_val = &const_instr->value;
|
ConstExprValue *const_val = &const_instr->value;
|
||||||
const_val->type = pointee_type;
|
const_val->type = pointee_type;
|
||||||
type_ensure_zero_bits_known(ira->codegen, type_entry);
|
type_ensure_zero_bits_known(ira->codegen, type_entry);
|
||||||
|
if (type_is_invalid(type_entry)) {
|
||||||
|
return ira->codegen->invalid_instruction;
|
||||||
|
}
|
||||||
const_val->data.x_type = get_pointer_to_type_extra(ira->codegen, type_entry,
|
const_val->data.x_type = get_pointer_to_type_extra(ira->codegen, type_entry,
|
||||||
ptr_is_const, ptr_is_volatile, get_abi_alignment(ira->codegen, type_entry), 0, 0);
|
ptr_is_const, ptr_is_volatile, get_abi_alignment(ira->codegen, type_entry), 0, 0);
|
||||||
return const_instr;
|
return const_instr;
|
||||||
|
@ -7984,6 +8074,7 @@ IrInstruction *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node
|
||||||
IrExecutable analyzed_executable = {0};
|
IrExecutable analyzed_executable = {0};
|
||||||
analyzed_executable.source_node = source_node;
|
analyzed_executable.source_node = source_node;
|
||||||
analyzed_executable.parent_exec = parent_exec;
|
analyzed_executable.parent_exec = parent_exec;
|
||||||
|
analyzed_executable.source_exec = &ir_executable;
|
||||||
analyzed_executable.name = exec_name;
|
analyzed_executable.name = exec_name;
|
||||||
analyzed_executable.is_inline = true;
|
analyzed_executable.is_inline = true;
|
||||||
analyzed_executable.fn_entry = fn_entry;
|
analyzed_executable.fn_entry = fn_entry;
|
||||||
|
@ -10279,30 +10370,40 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction,
|
||||||
|
|
||||||
bool is_const = (var->value->type->id == TypeTableEntryIdMetaType) ? is_const_ptr : var->src_is_const;
|
bool is_const = (var->value->type->id == TypeTableEntryIdMetaType) ? is_const_ptr : var->src_is_const;
|
||||||
bool is_volatile = (var->value->type->id == TypeTableEntryIdMetaType) ? is_volatile_ptr : false;
|
bool is_volatile = (var->value->type->id == TypeTableEntryIdMetaType) ? is_volatile_ptr : false;
|
||||||
if (mem_slot && mem_slot->special != ConstValSpecialRuntime) {
|
if (mem_slot != nullptr) {
|
||||||
ConstPtrMut ptr_mut;
|
switch (mem_slot->special) {
|
||||||
if (comptime_var_mem) {
|
case ConstValSpecialRuntime:
|
||||||
ptr_mut = ConstPtrMutComptimeVar;
|
goto no_mem_slot;
|
||||||
} else if (var->gen_is_const) {
|
case ConstValSpecialStatic: // fallthrough
|
||||||
ptr_mut = ConstPtrMutComptimeConst;
|
case ConstValSpecialUndef: {
|
||||||
} else {
|
ConstPtrMut ptr_mut;
|
||||||
assert(!comptime_var_mem);
|
if (comptime_var_mem) {
|
||||||
ptr_mut = ConstPtrMutRuntimeVar;
|
ptr_mut = ConstPtrMutComptimeVar;
|
||||||
|
} else if (var->gen_is_const) {
|
||||||
|
ptr_mut = ConstPtrMutComptimeConst;
|
||||||
|
} else {
|
||||||
|
assert(!comptime_var_mem);
|
||||||
|
ptr_mut = ConstPtrMutRuntimeVar;
|
||||||
|
}
|
||||||
|
return ir_get_const_ptr(ira, instruction, mem_slot, var->value->type,
|
||||||
|
ptr_mut, is_const, is_volatile, var->align_bytes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return ir_get_const_ptr(ira, instruction, mem_slot, var->value->type,
|
zig_unreachable();
|
||||||
ptr_mut, is_const, is_volatile, var->align_bytes);
|
|
||||||
} else {
|
|
||||||
IrInstruction *var_ptr_instruction = ir_build_var_ptr(&ira->new_irb,
|
|
||||||
instruction->scope, instruction->source_node, var, is_const, is_volatile);
|
|
||||||
var_ptr_instruction->value.type = get_pointer_to_type_extra(ira->codegen, var->value->type,
|
|
||||||
var->src_is_const, is_volatile, var->align_bytes, 0, 0);
|
|
||||||
type_ensure_zero_bits_known(ira->codegen, var->value->type);
|
|
||||||
|
|
||||||
bool in_fn_scope = (scope_fn_entry(var->parent_scope) != nullptr);
|
|
||||||
var_ptr_instruction->value.data.rh_ptr = in_fn_scope ? RuntimeHintPtrStack : RuntimeHintPtrNonStack;
|
|
||||||
|
|
||||||
return var_ptr_instruction;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
no_mem_slot:
|
||||||
|
|
||||||
|
IrInstruction *var_ptr_instruction = ir_build_var_ptr(&ira->new_irb,
|
||||||
|
instruction->scope, instruction->source_node, var, is_const, is_volatile);
|
||||||
|
var_ptr_instruction->value.type = get_pointer_to_type_extra(ira->codegen, var->value->type,
|
||||||
|
var->src_is_const, is_volatile, var->align_bytes, 0, 0);
|
||||||
|
type_ensure_zero_bits_known(ira->codegen, var->value->type);
|
||||||
|
|
||||||
|
bool in_fn_scope = (scope_fn_entry(var->parent_scope) != nullptr);
|
||||||
|
var_ptr_instruction->value.data.rh_ptr = in_fn_scope ? RuntimeHintPtrStack : RuntimeHintPtrNonStack;
|
||||||
|
|
||||||
|
return var_ptr_instruction;
|
||||||
}
|
}
|
||||||
|
|
||||||
static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call_instruction,
|
static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call_instruction,
|
||||||
|
@ -10408,10 +10509,11 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
|
||||||
result = ir_eval_const_value(ira->codegen, exec_scope, body_node, return_type,
|
result = ir_eval_const_value(ira->codegen, exec_scope, body_node, return_type,
|
||||||
ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, fn_entry,
|
ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, fn_entry,
|
||||||
nullptr, call_instruction->base.source_node, nullptr, ira->new_irb.exec);
|
nullptr, call_instruction->base.source_node, nullptr, ira->new_irb.exec);
|
||||||
if (type_is_invalid(result->value.type))
|
|
||||||
return ira->codegen->builtin_types.entry_invalid;
|
|
||||||
|
|
||||||
ira->codegen->memoized_fn_eval_table.put(exec_scope, result);
|
ira->codegen->memoized_fn_eval_table.put(exec_scope, result);
|
||||||
|
|
||||||
|
if (type_is_invalid(result->value.type))
|
||||||
|
return ira->codegen->builtin_types.entry_invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
ConstExprValue *out_val = ir_build_const_from(ira, &call_instruction->base);
|
ConstExprValue *out_val = ir_build_const_from(ira, &call_instruction->base);
|
||||||
|
@ -11417,8 +11519,20 @@ static TypeTableEntry *ir_analyze_container_member_access_inner(IrAnalyze *ira,
|
||||||
return ir_analyze_ref(ira, &field_ptr_instruction->base, bound_fn_value, true, false);
|
return ir_analyze_ref(ira, &field_ptr_instruction->base, bound_fn_value, true, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const char *prefix_name;
|
||||||
|
if (is_slice(bare_struct_type)) {
|
||||||
|
prefix_name = "";
|
||||||
|
} else if (bare_struct_type->id == TypeTableEntryIdStruct) {
|
||||||
|
prefix_name = "struct ";
|
||||||
|
} else if (bare_struct_type->id == TypeTableEntryIdEnum) {
|
||||||
|
prefix_name = "enum ";
|
||||||
|
} else if (bare_struct_type->id == TypeTableEntryIdUnion) {
|
||||||
|
prefix_name = "union ";
|
||||||
|
} else {
|
||||||
|
prefix_name = "";
|
||||||
|
}
|
||||||
ir_add_error_node(ira, field_ptr_instruction->base.source_node,
|
ir_add_error_node(ira, field_ptr_instruction->base.source_node,
|
||||||
buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&bare_struct_type->name)));
|
buf_sprintf("no member named '%s' in %s'%s'", buf_ptr(field_name), prefix_name, buf_ptr(&bare_struct_type->name)));
|
||||||
return ira->codegen->builtin_types.entry_invalid;
|
return ira->codegen->builtin_types.entry_invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11428,14 +11542,13 @@ static TypeTableEntry *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field
|
||||||
{
|
{
|
||||||
TypeTableEntry *bare_type = container_ref_type(container_type);
|
TypeTableEntry *bare_type = container_ref_type(container_type);
|
||||||
ensure_complete_type(ira->codegen, bare_type);
|
ensure_complete_type(ira->codegen, bare_type);
|
||||||
|
if (type_is_invalid(bare_type))
|
||||||
|
return ira->codegen->builtin_types.entry_invalid;
|
||||||
|
|
||||||
assert(container_ptr->value.type->id == TypeTableEntryIdPointer);
|
assert(container_ptr->value.type->id == TypeTableEntryIdPointer);
|
||||||
bool is_const = container_ptr->value.type->data.pointer.is_const;
|
bool is_const = container_ptr->value.type->data.pointer.is_const;
|
||||||
bool is_volatile = container_ptr->value.type->data.pointer.is_volatile;
|
bool is_volatile = container_ptr->value.type->data.pointer.is_volatile;
|
||||||
if (bare_type->id == TypeTableEntryIdStruct) {
|
if (bare_type->id == TypeTableEntryIdStruct) {
|
||||||
if (bare_type->data.structure.is_invalid)
|
|
||||||
return ira->codegen->builtin_types.entry_invalid;
|
|
||||||
|
|
||||||
TypeStructField *field = find_struct_type_field(bare_type, field_name);
|
TypeStructField *field = find_struct_type_field(bare_type, field_name);
|
||||||
if (field) {
|
if (field) {
|
||||||
bool is_packed = (bare_type->data.structure.layout == ContainerLayoutPacked);
|
bool is_packed = (bare_type->data.structure.layout == ContainerLayoutPacked);
|
||||||
|
@ -11476,9 +11589,6 @@ static TypeTableEntry *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field
|
||||||
field_ptr_instruction, container_ptr, container_type);
|
field_ptr_instruction, container_ptr, container_type);
|
||||||
}
|
}
|
||||||
} else if (bare_type->id == TypeTableEntryIdEnum) {
|
} else if (bare_type->id == TypeTableEntryIdEnum) {
|
||||||
if (bare_type->data.enumeration.is_invalid)
|
|
||||||
return ira->codegen->builtin_types.entry_invalid;
|
|
||||||
|
|
||||||
TypeEnumField *field = find_enum_type_field(bare_type, field_name);
|
TypeEnumField *field = find_enum_type_field(bare_type, field_name);
|
||||||
if (field) {
|
if (field) {
|
||||||
ir_build_enum_field_ptr_from(&ira->new_irb, &field_ptr_instruction->base, container_ptr, field);
|
ir_build_enum_field_ptr_from(&ira->new_irb, &field_ptr_instruction->base, container_ptr, field);
|
||||||
|
@ -11489,7 +11599,15 @@ static TypeTableEntry *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field
|
||||||
field_ptr_instruction, container_ptr, container_type);
|
field_ptr_instruction, container_ptr, container_type);
|
||||||
}
|
}
|
||||||
} else if (bare_type->id == TypeTableEntryIdUnion) {
|
} else if (bare_type->id == TypeTableEntryIdUnion) {
|
||||||
zig_panic("TODO");
|
TypeUnionField *field = find_union_type_field(bare_type, field_name);
|
||||||
|
if (field) {
|
||||||
|
ir_build_union_field_ptr_from(&ira->new_irb, &field_ptr_instruction->base, container_ptr, field);
|
||||||
|
return get_pointer_to_type_extra(ira->codegen, field->type_entry, is_const, is_volatile,
|
||||||
|
get_abi_alignment(ira->codegen, field->type_entry), 0, 0);
|
||||||
|
} else {
|
||||||
|
return ir_analyze_container_member_access_inner(ira, bare_type, field_name,
|
||||||
|
field_ptr_instruction, container_ptr, container_type);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
zig_unreachable();
|
zig_unreachable();
|
||||||
}
|
}
|
||||||
|
@ -13033,9 +13151,71 @@ static TypeTableEntry *ir_analyze_instruction_ref(IrAnalyze *ira, IrInstructionR
|
||||||
return ir_analyze_ref(ira, &ref_instruction->base, value, ref_instruction->is_const, ref_instruction->is_volatile);
|
return ir_analyze_ref(ira, &ref_instruction->base, value, ref_instruction->is_const, ref_instruction->is_volatile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static TypeTableEntry *ir_analyze_container_init_fields_union(IrAnalyze *ira, IrInstruction *instruction,
|
||||||
|
TypeTableEntry *container_type, size_t instr_field_count, IrInstructionContainerInitFieldsField *fields)
|
||||||
|
{
|
||||||
|
assert(container_type->id == TypeTableEntryIdUnion);
|
||||||
|
|
||||||
|
ensure_complete_type(ira->codegen, container_type);
|
||||||
|
|
||||||
|
if (instr_field_count != 1) {
|
||||||
|
ir_add_error(ira, instruction,
|
||||||
|
buf_sprintf("union initialization expects exactly one field"));
|
||||||
|
return ira->codegen->builtin_types.entry_invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
IrInstructionContainerInitFieldsField *field = &fields[0];
|
||||||
|
IrInstruction *field_value = field->value->other;
|
||||||
|
if (type_is_invalid(field_value->value.type))
|
||||||
|
return ira->codegen->builtin_types.entry_invalid;
|
||||||
|
|
||||||
|
TypeUnionField *type_field = find_union_type_field(container_type, field->name);
|
||||||
|
if (!type_field) {
|
||||||
|
ir_add_error_node(ira, field->source_node,
|
||||||
|
buf_sprintf("no member named '%s' in union '%s'",
|
||||||
|
buf_ptr(field->name), buf_ptr(&container_type->name)));
|
||||||
|
return ira->codegen->builtin_types.entry_invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type_is_invalid(type_field->type_entry))
|
||||||
|
return ira->codegen->builtin_types.entry_invalid;
|
||||||
|
|
||||||
|
IrInstruction *casted_field_value = ir_implicit_cast(ira, field_value, type_field->type_entry);
|
||||||
|
if (casted_field_value == ira->codegen->invalid_instruction)
|
||||||
|
return ira->codegen->builtin_types.entry_invalid;
|
||||||
|
|
||||||
|
bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->scope);
|
||||||
|
if (is_comptime || casted_field_value->value.special != ConstValSpecialRuntime) {
|
||||||
|
ConstExprValue *field_val = ir_resolve_const(ira, casted_field_value, UndefOk);
|
||||||
|
if (!field_val)
|
||||||
|
return ira->codegen->builtin_types.entry_invalid;
|
||||||
|
|
||||||
|
ConstExprValue *out_val = ir_build_const_from(ira, instruction);
|
||||||
|
out_val->data.x_union.payload = field_val;
|
||||||
|
out_val->data.x_union.tag = type_field->value;
|
||||||
|
|
||||||
|
ConstParent *parent = get_const_val_parent(ira->codegen, field_val);
|
||||||
|
if (parent != nullptr) {
|
||||||
|
parent->id = ConstParentIdUnion;
|
||||||
|
parent->data.p_union.union_val = out_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
return container_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
IrInstruction *new_instruction = ir_build_union_init_from(&ira->new_irb, instruction,
|
||||||
|
container_type, type_field, casted_field_value);
|
||||||
|
|
||||||
|
ir_add_alloca(ira, new_instruction, container_type);
|
||||||
|
return container_type;
|
||||||
|
}
|
||||||
|
|
||||||
static TypeTableEntry *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruction *instruction,
|
static TypeTableEntry *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruction *instruction,
|
||||||
TypeTableEntry *container_type, size_t instr_field_count, IrInstructionContainerInitFieldsField *fields)
|
TypeTableEntry *container_type, size_t instr_field_count, IrInstructionContainerInitFieldsField *fields)
|
||||||
{
|
{
|
||||||
|
if (container_type->id == TypeTableEntryIdUnion) {
|
||||||
|
return ir_analyze_container_init_fields_union(ira, instruction, container_type, instr_field_count, fields);
|
||||||
|
}
|
||||||
if (container_type->id != TypeTableEntryIdStruct || is_slice(container_type)) {
|
if (container_type->id != TypeTableEntryIdStruct || is_slice(container_type)) {
|
||||||
ir_add_error(ira, instruction,
|
ir_add_error(ira, instruction,
|
||||||
buf_sprintf("type '%s' does not support struct initialization syntax",
|
buf_sprintf("type '%s' does not support struct initialization syntax",
|
||||||
|
@ -13043,8 +13223,7 @@ static TypeTableEntry *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstru
|
||||||
return ira->codegen->builtin_types.entry_invalid;
|
return ira->codegen->builtin_types.entry_invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!type_is_complete(container_type))
|
ensure_complete_type(ira->codegen, container_type);
|
||||||
resolve_container_type(ira->codegen, container_type);
|
|
||||||
|
|
||||||
size_t actual_field_count = container_type->data.structure.src_field_count;
|
size_t actual_field_count = container_type->data.structure.src_field_count;
|
||||||
|
|
||||||
|
@ -13070,7 +13249,7 @@ static TypeTableEntry *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstru
|
||||||
TypeStructField *type_field = find_struct_type_field(container_type, field->name);
|
TypeStructField *type_field = find_struct_type_field(container_type, field->name);
|
||||||
if (!type_field) {
|
if (!type_field) {
|
||||||
ir_add_error_node(ira, field->source_node,
|
ir_add_error_node(ira, field->source_node,
|
||||||
buf_sprintf("no member named '%s' in '%s'",
|
buf_sprintf("no member named '%s' in struct '%s'",
|
||||||
buf_ptr(field->name), buf_ptr(&container_type->name)));
|
buf_ptr(field->name), buf_ptr(&container_type->name)));
|
||||||
return ira->codegen->builtin_types.entry_invalid;
|
return ira->codegen->builtin_types.entry_invalid;
|
||||||
}
|
}
|
||||||
|
@ -15657,8 +15836,10 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
|
||||||
case IrInstructionIdIntToErr:
|
case IrInstructionIdIntToErr:
|
||||||
case IrInstructionIdErrToInt:
|
case IrInstructionIdErrToInt:
|
||||||
case IrInstructionIdStructInit:
|
case IrInstructionIdStructInit:
|
||||||
|
case IrInstructionIdUnionInit:
|
||||||
case IrInstructionIdStructFieldPtr:
|
case IrInstructionIdStructFieldPtr:
|
||||||
case IrInstructionIdEnumFieldPtr:
|
case IrInstructionIdEnumFieldPtr:
|
||||||
|
case IrInstructionIdUnionFieldPtr:
|
||||||
case IrInstructionIdInitEnum:
|
case IrInstructionIdInitEnum:
|
||||||
case IrInstructionIdMaybeWrap:
|
case IrInstructionIdMaybeWrap:
|
||||||
case IrInstructionIdErrWrapCode:
|
case IrInstructionIdErrWrapCode:
|
||||||
|
@ -15968,6 +16149,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
|
||||||
case IrInstructionIdContainerInitList:
|
case IrInstructionIdContainerInitList:
|
||||||
case IrInstructionIdContainerInitFields:
|
case IrInstructionIdContainerInitFields:
|
||||||
case IrInstructionIdStructInit:
|
case IrInstructionIdStructInit:
|
||||||
|
case IrInstructionIdUnionInit:
|
||||||
case IrInstructionIdFieldPtr:
|
case IrInstructionIdFieldPtr:
|
||||||
case IrInstructionIdElemPtr:
|
case IrInstructionIdElemPtr:
|
||||||
case IrInstructionIdVarPtr:
|
case IrInstructionIdVarPtr:
|
||||||
|
@ -15977,6 +16159,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
|
||||||
case IrInstructionIdArrayLen:
|
case IrInstructionIdArrayLen:
|
||||||
case IrInstructionIdStructFieldPtr:
|
case IrInstructionIdStructFieldPtr:
|
||||||
case IrInstructionIdEnumFieldPtr:
|
case IrInstructionIdEnumFieldPtr:
|
||||||
|
case IrInstructionIdUnionFieldPtr:
|
||||||
case IrInstructionIdArrayType:
|
case IrInstructionIdArrayType:
|
||||||
case IrInstructionIdSliceType:
|
case IrInstructionIdSliceType:
|
||||||
case IrInstructionIdSizeOf:
|
case IrInstructionIdSizeOf:
|
||||||
|
|
|
@ -290,6 +290,15 @@ static void ir_print_struct_init(IrPrint *irp, IrInstructionStructInit *instruct
|
||||||
fprintf(irp->f, "} // struct init");
|
fprintf(irp->f, "} // struct init");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ir_print_union_init(IrPrint *irp, IrInstructionUnionInit *instruction) {
|
||||||
|
Buf *field_name = instruction->field->name;
|
||||||
|
|
||||||
|
fprintf(irp->f, "%s {", buf_ptr(&instruction->union_type->name));
|
||||||
|
fprintf(irp->f, ".%s = ", buf_ptr(field_name));
|
||||||
|
ir_print_other_instruction(irp, instruction->init_value);
|
||||||
|
fprintf(irp->f, "} // union init");
|
||||||
|
}
|
||||||
|
|
||||||
static void ir_print_unreachable(IrPrint *irp, IrInstructionUnreachable *instruction) {
|
static void ir_print_unreachable(IrPrint *irp, IrInstructionUnreachable *instruction) {
|
||||||
fprintf(irp->f, "unreachable");
|
fprintf(irp->f, "unreachable");
|
||||||
}
|
}
|
||||||
|
@ -359,6 +368,13 @@ static void ir_print_enum_field_ptr(IrPrint *irp, IrInstructionEnumFieldPtr *ins
|
||||||
fprintf(irp->f, ")");
|
fprintf(irp->f, ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ir_print_union_field_ptr(IrPrint *irp, IrInstructionUnionFieldPtr *instruction) {
|
||||||
|
fprintf(irp->f, "@UnionFieldPtr(&");
|
||||||
|
ir_print_other_instruction(irp, instruction->union_ptr);
|
||||||
|
fprintf(irp->f, ".%s", buf_ptr(instruction->field->name));
|
||||||
|
fprintf(irp->f, ")");
|
||||||
|
}
|
||||||
|
|
||||||
static void ir_print_set_debug_safety(IrPrint *irp, IrInstructionSetDebugSafety *instruction) {
|
static void ir_print_set_debug_safety(IrPrint *irp, IrInstructionSetDebugSafety *instruction) {
|
||||||
fprintf(irp->f, "@setDebugSafety(");
|
fprintf(irp->f, "@setDebugSafety(");
|
||||||
ir_print_other_instruction(irp, instruction->scope_value);
|
ir_print_other_instruction(irp, instruction->scope_value);
|
||||||
|
@ -1023,6 +1039,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
|
||||||
case IrInstructionIdStructInit:
|
case IrInstructionIdStructInit:
|
||||||
ir_print_struct_init(irp, (IrInstructionStructInit *)instruction);
|
ir_print_struct_init(irp, (IrInstructionStructInit *)instruction);
|
||||||
break;
|
break;
|
||||||
|
case IrInstructionIdUnionInit:
|
||||||
|
ir_print_union_init(irp, (IrInstructionUnionInit *)instruction);
|
||||||
|
break;
|
||||||
case IrInstructionIdUnreachable:
|
case IrInstructionIdUnreachable:
|
||||||
ir_print_unreachable(irp, (IrInstructionUnreachable *)instruction);
|
ir_print_unreachable(irp, (IrInstructionUnreachable *)instruction);
|
||||||
break;
|
break;
|
||||||
|
@ -1056,6 +1075,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
|
||||||
case IrInstructionIdEnumFieldPtr:
|
case IrInstructionIdEnumFieldPtr:
|
||||||
ir_print_enum_field_ptr(irp, (IrInstructionEnumFieldPtr *)instruction);
|
ir_print_enum_field_ptr(irp, (IrInstructionEnumFieldPtr *)instruction);
|
||||||
break;
|
break;
|
||||||
|
case IrInstructionIdUnionFieldPtr:
|
||||||
|
ir_print_union_field_ptr(irp, (IrInstructionUnionFieldPtr *)instruction);
|
||||||
|
break;
|
||||||
case IrInstructionIdSetDebugSafety:
|
case IrInstructionIdSetDebugSafety:
|
||||||
ir_print_set_debug_safety(irp, (IrInstructionSetDebugSafety *)instruction);
|
ir_print_set_debug_safety(irp, (IrInstructionSetDebugSafety *)instruction);
|
||||||
break;
|
break;
|
||||||
|
|
22
src/main.cpp
22
src/main.cpp
|
@ -23,7 +23,7 @@ static int usage(const char *arg0) {
|
||||||
" build-exe [source] create executable from source or object files\n"
|
" build-exe [source] create executable from source or object files\n"
|
||||||
" build-lib [source] create library from source or object files\n"
|
" build-lib [source] create library from source or object files\n"
|
||||||
" build-obj [source] create object from source or assembly\n"
|
" build-obj [source] create object from source or assembly\n"
|
||||||
" parsec [source] convert c code to zig code\n"
|
" translate-c [source] convert c code to zig code\n"
|
||||||
" targets list available compilation targets\n"
|
" targets list available compilation targets\n"
|
||||||
" test [source] create and run a test build\n"
|
" test [source] create and run a test build\n"
|
||||||
" version print version number and exit\n"
|
" version print version number and exit\n"
|
||||||
|
@ -229,7 +229,7 @@ enum Cmd {
|
||||||
CmdTest,
|
CmdTest,
|
||||||
CmdVersion,
|
CmdVersion,
|
||||||
CmdZen,
|
CmdZen,
|
||||||
CmdParseC,
|
CmdTranslateC,
|
||||||
CmdTargets,
|
CmdTargets,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -632,8 +632,8 @@ int main(int argc, char **argv) {
|
||||||
cmd = CmdVersion;
|
cmd = CmdVersion;
|
||||||
} else if (strcmp(arg, "zen") == 0) {
|
} else if (strcmp(arg, "zen") == 0) {
|
||||||
cmd = CmdZen;
|
cmd = CmdZen;
|
||||||
} else if (strcmp(arg, "parsec") == 0) {
|
} else if (strcmp(arg, "translate-c") == 0) {
|
||||||
cmd = CmdParseC;
|
cmd = CmdTranslateC;
|
||||||
} else if (strcmp(arg, "test") == 0) {
|
} else if (strcmp(arg, "test") == 0) {
|
||||||
cmd = CmdTest;
|
cmd = CmdTest;
|
||||||
out_type = OutTypeExe;
|
out_type = OutTypeExe;
|
||||||
|
@ -646,7 +646,7 @@ int main(int argc, char **argv) {
|
||||||
} else {
|
} else {
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case CmdBuild:
|
case CmdBuild:
|
||||||
case CmdParseC:
|
case CmdTranslateC:
|
||||||
case CmdTest:
|
case CmdTest:
|
||||||
if (!in_file) {
|
if (!in_file) {
|
||||||
in_file = arg;
|
in_file = arg;
|
||||||
|
@ -703,13 +703,13 @@ int main(int argc, char **argv) {
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case CmdBuild:
|
case CmdBuild:
|
||||||
case CmdParseC:
|
case CmdTranslateC:
|
||||||
case CmdTest:
|
case CmdTest:
|
||||||
{
|
{
|
||||||
if (cmd == CmdBuild && !in_file && objects.length == 0 && asm_files.length == 0) {
|
if (cmd == CmdBuild && !in_file && objects.length == 0 && asm_files.length == 0) {
|
||||||
fprintf(stderr, "Expected source file argument or at least one --object or --assembly argument.\n");
|
fprintf(stderr, "Expected source file argument or at least one --object or --assembly argument.\n");
|
||||||
return usage(arg0);
|
return usage(arg0);
|
||||||
} else if ((cmd == CmdParseC || cmd == CmdTest) && !in_file) {
|
} else if ((cmd == CmdTranslateC || cmd == CmdTest) && !in_file) {
|
||||||
fprintf(stderr, "Expected source file argument.\n");
|
fprintf(stderr, "Expected source file argument.\n");
|
||||||
return usage(arg0);
|
return usage(arg0);
|
||||||
} else if (cmd == CmdBuild && out_type == OutTypeObj && objects.length != 0) {
|
} else if (cmd == CmdBuild && out_type == OutTypeObj && objects.length != 0) {
|
||||||
|
@ -719,7 +719,7 @@ int main(int argc, char **argv) {
|
||||||
|
|
||||||
assert(cmd != CmdBuild || out_type != OutTypeUnknown);
|
assert(cmd != CmdBuild || out_type != OutTypeUnknown);
|
||||||
|
|
||||||
bool need_name = (cmd == CmdBuild || cmd == CmdParseC);
|
bool need_name = (cmd == CmdBuild || cmd == CmdTranslateC);
|
||||||
|
|
||||||
Buf *in_file_buf = nullptr;
|
Buf *in_file_buf = nullptr;
|
||||||
|
|
||||||
|
@ -742,7 +742,7 @@ int main(int argc, char **argv) {
|
||||||
return usage(arg0);
|
return usage(arg0);
|
||||||
}
|
}
|
||||||
|
|
||||||
Buf *zig_root_source_file = (cmd == CmdParseC) ? nullptr : in_file_buf;
|
Buf *zig_root_source_file = (cmd == CmdTranslateC) ? nullptr : in_file_buf;
|
||||||
|
|
||||||
Buf *full_cache_dir = buf_alloc();
|
Buf *full_cache_dir = buf_alloc();
|
||||||
os_path_resolve(buf_create_from_str("."),
|
os_path_resolve(buf_create_from_str("."),
|
||||||
|
@ -841,8 +841,8 @@ int main(int argc, char **argv) {
|
||||||
if (timing_info)
|
if (timing_info)
|
||||||
codegen_print_timing_report(g, stdout);
|
codegen_print_timing_report(g, stdout);
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
} else if (cmd == CmdParseC) {
|
} else if (cmd == CmdTranslateC) {
|
||||||
codegen_parsec(g, in_file_buf);
|
codegen_translate_c(g, in_file_buf);
|
||||||
ast_render(g, stdout, g->root_import->root, 4);
|
ast_render(g, stdout, g->root_import->root, 4);
|
||||||
if (timing_info)
|
if (timing_info)
|
||||||
codegen_print_timing_report(g, stdout);
|
codegen_print_timing_report(g, stdout);
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -403,6 +403,10 @@ unsigned ZigLLVMTag_DW_structure_type(void) {
|
||||||
return dwarf::DW_TAG_structure_type;
|
return dwarf::DW_TAG_structure_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned ZigLLVMTag_DW_union_type(void) {
|
||||||
|
return dwarf::DW_TAG_union_type;
|
||||||
|
}
|
||||||
|
|
||||||
ZigLLVMDIBuilder *ZigLLVMCreateDIBuilder(LLVMModuleRef module, bool allow_unresolved) {
|
ZigLLVMDIBuilder *ZigLLVMCreateDIBuilder(LLVMModuleRef module, bool allow_unresolved) {
|
||||||
DIBuilder *di_builder = new DIBuilder(*unwrap(module), allow_unresolved);
|
DIBuilder *di_builder = new DIBuilder(*unwrap(module), allow_unresolved);
|
||||||
return reinterpret_cast<ZigLLVMDIBuilder *>(di_builder);
|
return reinterpret_cast<ZigLLVMDIBuilder *>(di_builder);
|
||||||
|
|
|
@ -117,6 +117,7 @@ unsigned ZigLLVMEncoding_DW_ATE_signed_char(void);
|
||||||
unsigned ZigLLVMLang_DW_LANG_C99(void);
|
unsigned ZigLLVMLang_DW_LANG_C99(void);
|
||||||
unsigned ZigLLVMTag_DW_variable(void);
|
unsigned ZigLLVMTag_DW_variable(void);
|
||||||
unsigned ZigLLVMTag_DW_structure_type(void);
|
unsigned ZigLLVMTag_DW_structure_type(void);
|
||||||
|
unsigned ZigLLVMTag_DW_union_type(void);
|
||||||
|
|
||||||
ZigLLVMDIBuilder *ZigLLVMCreateDIBuilder(LLVMModuleRef module, bool allow_unresolved);
|
ZigLLVMDIBuilder *ZigLLVMCreateDIBuilder(LLVMModuleRef module, bool allow_unresolved);
|
||||||
void ZigLLVMAddModuleDebugInfoFlag(LLVMModuleRef module);
|
void ZigLLVMAddModuleDebugInfoFlag(LLVMModuleRef module);
|
||||||
|
|
583
std/base64.zig
583
std/base64.zig
|
@ -1,186 +1,485 @@
|
||||||
const assert = @import("debug.zig").assert;
|
const assert = @import("debug.zig").assert;
|
||||||
const mem = @import("mem.zig");
|
const mem = @import("mem.zig");
|
||||||
|
|
||||||
pub const standard_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
pub const standard_alphabet_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||||
|
pub const standard_pad_char = '=';
|
||||||
|
pub const standard_encoder = Base64Encoder.init(standard_alphabet_chars, standard_pad_char);
|
||||||
|
|
||||||
pub fn encode(dest: []u8, source: []const u8) -> []u8 {
|
pub const Base64Encoder = struct {
|
||||||
return encodeWithAlphabet(dest, source, standard_alphabet);
|
alphabet_chars: []const u8,
|
||||||
}
|
pad_char: u8,
|
||||||
|
|
||||||
/// invalid characters in source are allowed, but they cause the value of dest to be undefined.
|
/// a bunch of assertions, then simply pass the data right through.
|
||||||
pub fn decode(dest: []u8, source: []const u8) -> []u8 {
|
pub fn init(alphabet_chars: []const u8, pad_char: u8) -> Base64Encoder {
|
||||||
return decodeWithAlphabet(dest, source, standard_alphabet);
|
assert(alphabet_chars.len == 64);
|
||||||
}
|
var char_in_alphabet = []bool{false} ** 256;
|
||||||
|
for (alphabet_chars) |c| {
|
||||||
|
assert(!char_in_alphabet[c]);
|
||||||
|
assert(c != pad_char);
|
||||||
|
char_in_alphabet[c] = true;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn encodeWithAlphabet(dest: []u8, source: []const u8, alphabet: []const u8) -> []u8 {
|
return Base64Encoder{
|
||||||
assert(alphabet.len == 65);
|
.alphabet_chars = alphabet_chars,
|
||||||
assert(dest.len >= calcEncodedSize(source.len));
|
.pad_char = pad_char,
|
||||||
|
};
|
||||||
var i: usize = 0;
|
|
||||||
var out_index: usize = 0;
|
|
||||||
while (i + 2 < source.len) : (i += 3) {
|
|
||||||
dest[out_index] = alphabet[(source[i] >> 2) & 0x3f];
|
|
||||||
out_index += 1;
|
|
||||||
|
|
||||||
dest[out_index] = alphabet[((source[i] & 0x3) << 4) |
|
|
||||||
((source[i + 1] & 0xf0) >> 4)];
|
|
||||||
out_index += 1;
|
|
||||||
|
|
||||||
dest[out_index] = alphabet[((source[i + 1] & 0xf) << 2) |
|
|
||||||
((source[i + 2] & 0xc0) >> 6)];
|
|
||||||
out_index += 1;
|
|
||||||
|
|
||||||
dest[out_index] = alphabet[source[i + 2] & 0x3f];
|
|
||||||
out_index += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i < source.len) {
|
/// ceil(source_len * 4/3)
|
||||||
dest[out_index] = alphabet[(source[i] >> 2) & 0x3f];
|
pub fn calcSize(source_len: usize) -> usize {
|
||||||
out_index += 1;
|
return @divTrunc(source_len + 2, 3) * 4;
|
||||||
|
}
|
||||||
|
|
||||||
if (i + 1 == source.len) {
|
/// dest.len must be what you get from ::calcSize.
|
||||||
dest[out_index] = alphabet[(source[i] & 0x3) << 4];
|
pub fn encode(encoder: &const Base64Encoder, dest: []u8, source: []const u8) {
|
||||||
|
assert(dest.len == Base64Encoder.calcSize(source.len));
|
||||||
|
|
||||||
|
var i: usize = 0;
|
||||||
|
var out_index: usize = 0;
|
||||||
|
while (i + 2 < source.len) : (i += 3) {
|
||||||
|
dest[out_index] = encoder.alphabet_chars[(source[i] >> 2) & 0x3f];
|
||||||
out_index += 1;
|
out_index += 1;
|
||||||
|
|
||||||
dest[out_index] = alphabet[64];
|
dest[out_index] = encoder.alphabet_chars[((source[i] & 0x3) << 4) |
|
||||||
out_index += 1;
|
|
||||||
} else {
|
|
||||||
dest[out_index] = alphabet[((source[i] & 0x3) << 4) |
|
|
||||||
((source[i + 1] & 0xf0) >> 4)];
|
((source[i + 1] & 0xf0) >> 4)];
|
||||||
out_index += 1;
|
out_index += 1;
|
||||||
|
|
||||||
dest[out_index] = alphabet[(source[i + 1] & 0xf) << 2];
|
dest[out_index] = encoder.alphabet_chars[((source[i + 1] & 0xf) << 2) |
|
||||||
|
((source[i + 2] & 0xc0) >> 6)];
|
||||||
|
out_index += 1;
|
||||||
|
|
||||||
|
dest[out_index] = encoder.alphabet_chars[source[i + 2] & 0x3f];
|
||||||
out_index += 1;
|
out_index += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
dest[out_index] = alphabet[64];
|
if (i < source.len) {
|
||||||
out_index += 1;
|
dest[out_index] = encoder.alphabet_chars[(source[i] >> 2) & 0x3f];
|
||||||
|
out_index += 1;
|
||||||
|
|
||||||
|
if (i + 1 == source.len) {
|
||||||
|
dest[out_index] = encoder.alphabet_chars[(source[i] & 0x3) << 4];
|
||||||
|
out_index += 1;
|
||||||
|
|
||||||
|
dest[out_index] = encoder.pad_char;
|
||||||
|
out_index += 1;
|
||||||
|
} else {
|
||||||
|
dest[out_index] = encoder.alphabet_chars[((source[i] & 0x3) << 4) |
|
||||||
|
((source[i + 1] & 0xf0) >> 4)];
|
||||||
|
out_index += 1;
|
||||||
|
|
||||||
|
dest[out_index] = encoder.alphabet_chars[(source[i + 1] & 0xf) << 2];
|
||||||
|
out_index += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
dest[out_index] = encoder.pad_char;
|
||||||
|
out_index += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const standard_decoder = Base64Decoder.init(standard_alphabet_chars, standard_pad_char);
|
||||||
|
error InvalidPadding;
|
||||||
|
error InvalidCharacter;
|
||||||
|
|
||||||
|
pub const Base64Decoder = struct {
|
||||||
|
/// e.g. 'A' => 0.
|
||||||
|
/// undefined for any value not in the 64 alphabet chars.
|
||||||
|
char_to_index: [256]u8,
|
||||||
|
/// true only for the 64 chars in the alphabet, not the pad char.
|
||||||
|
char_in_alphabet: [256]bool,
|
||||||
|
pad_char: u8,
|
||||||
|
|
||||||
|
pub fn init(alphabet_chars: []const u8, pad_char: u8) -> Base64Decoder {
|
||||||
|
assert(alphabet_chars.len == 64);
|
||||||
|
|
||||||
|
var result = Base64Decoder{
|
||||||
|
.char_to_index = undefined,
|
||||||
|
.char_in_alphabet = []bool{false} ** 256,
|
||||||
|
.pad_char = pad_char,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (alphabet_chars) |c, i| {
|
||||||
|
assert(!result.char_in_alphabet[c]);
|
||||||
|
assert(c != pad_char);
|
||||||
|
|
||||||
|
result.char_to_index[c] = u8(i);
|
||||||
|
result.char_in_alphabet[c] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
return dest[0..out_index];
|
/// If the encoded buffer is detected to be invalid, returns error.InvalidPadding.
|
||||||
}
|
pub fn calcSize(decoder: &const Base64Decoder, source: []const u8) -> %usize {
|
||||||
|
if (source.len % 4 != 0) return error.InvalidPadding;
|
||||||
/// invalid characters in source are allowed, but they cause the value of dest to be undefined.
|
return calcDecodedSizeExactUnsafe(source, decoder.pad_char);
|
||||||
pub fn decodeWithAlphabet(dest: []u8, source: []const u8, alphabet: []const u8) -> []u8 {
|
|
||||||
assert(alphabet.len == 65);
|
|
||||||
|
|
||||||
var ascii6 = []u8{64} ** 256;
|
|
||||||
for (alphabet) |c, i| {
|
|
||||||
ascii6[c] = u8(i);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return decodeWithAscii6BitMap(dest, source, ascii6[0..], alphabet[64]);
|
/// dest.len must be what you get from ::calcSize.
|
||||||
}
|
/// invalid characters result in error.InvalidCharacter.
|
||||||
|
/// invalid padding results in error.InvalidPadding.
|
||||||
|
pub fn decode(decoder: &const Base64Decoder, dest: []u8, source: []const u8) -> %void {
|
||||||
|
assert(dest.len == %%decoder.calcSize(source));
|
||||||
|
assert(source.len % 4 == 0);
|
||||||
|
|
||||||
pub fn decodeWithAscii6BitMap(dest: []u8, source: []const u8, ascii6: []const u8, pad_char: u8) -> []u8 {
|
var src_cursor: usize = 0;
|
||||||
assert(ascii6.len == 256);
|
var dest_cursor: usize = 0;
|
||||||
assert(dest.len >= calcExactDecodedSizeWithPadChar(source, pad_char));
|
|
||||||
|
|
||||||
var src_index: usize = 0;
|
while (src_cursor < source.len) : (src_cursor += 4) {
|
||||||
var dest_index: usize = 0;
|
if (!decoder.char_in_alphabet[source[src_cursor + 0]]) return error.InvalidCharacter;
|
||||||
var in_buf_len: usize = source.len;
|
if (!decoder.char_in_alphabet[source[src_cursor + 1]]) return error.InvalidCharacter;
|
||||||
|
if (src_cursor < source.len - 4 or source[src_cursor + 3] != decoder.pad_char) {
|
||||||
|
// common case
|
||||||
|
if (!decoder.char_in_alphabet[source[src_cursor + 2]]) return error.InvalidCharacter;
|
||||||
|
if (!decoder.char_in_alphabet[source[src_cursor + 3]]) return error.InvalidCharacter;
|
||||||
|
dest[dest_cursor + 0] = decoder.char_to_index[source[src_cursor + 0]] << 2 |
|
||||||
|
decoder.char_to_index[source[src_cursor + 1]] >> 4;
|
||||||
|
dest[dest_cursor + 1] = decoder.char_to_index[source[src_cursor + 1]] << 4 |
|
||||||
|
decoder.char_to_index[source[src_cursor + 2]] >> 2;
|
||||||
|
dest[dest_cursor + 2] = decoder.char_to_index[source[src_cursor + 2]] << 6 |
|
||||||
|
decoder.char_to_index[source[src_cursor + 3]];
|
||||||
|
dest_cursor += 3;
|
||||||
|
} else if (source[src_cursor + 2] != decoder.pad_char) {
|
||||||
|
// one pad char
|
||||||
|
if (!decoder.char_in_alphabet[source[src_cursor + 2]]) return error.InvalidCharacter;
|
||||||
|
dest[dest_cursor + 0] = decoder.char_to_index[source[src_cursor + 0]] << 2 |
|
||||||
|
decoder.char_to_index[source[src_cursor + 1]] >> 4;
|
||||||
|
dest[dest_cursor + 1] = decoder.char_to_index[source[src_cursor + 1]] << 4 |
|
||||||
|
decoder.char_to_index[source[src_cursor + 2]] >> 2;
|
||||||
|
if (decoder.char_to_index[source[src_cursor + 2]] << 6 != 0) return error.InvalidPadding;
|
||||||
|
dest_cursor += 2;
|
||||||
|
} else {
|
||||||
|
// two pad chars
|
||||||
|
dest[dest_cursor + 0] = decoder.char_to_index[source[src_cursor + 0]] << 2 |
|
||||||
|
decoder.char_to_index[source[src_cursor + 1]] >> 4;
|
||||||
|
if (decoder.char_to_index[source[src_cursor + 1]] << 4 != 0) return error.InvalidPadding;
|
||||||
|
dest_cursor += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
while (in_buf_len > 0 and source[in_buf_len - 1] == pad_char) {
|
assert(src_cursor == source.len);
|
||||||
in_buf_len -= 1;
|
assert(dest_cursor == dest.len);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
error OutputTooSmall;
|
||||||
|
|
||||||
|
pub const Base64DecoderWithIgnore = struct {
|
||||||
|
decoder: Base64Decoder,
|
||||||
|
char_is_ignored: [256]bool,
|
||||||
|
pub fn init(alphabet_chars: []const u8, pad_char: u8, ignore_chars: []const u8) -> Base64DecoderWithIgnore {
|
||||||
|
var result = Base64DecoderWithIgnore {
|
||||||
|
.decoder = Base64Decoder.init(alphabet_chars, pad_char),
|
||||||
|
.char_is_ignored = []bool{false} ** 256,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (ignore_chars) |c| {
|
||||||
|
assert(!result.decoder.char_in_alphabet[c]);
|
||||||
|
assert(!result.char_is_ignored[c]);
|
||||||
|
assert(result.decoder.pad_char != c);
|
||||||
|
result.char_is_ignored[c] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (in_buf_len > 4) {
|
/// If no characters end up being ignored or padding, this will be the exact decoded size.
|
||||||
dest[dest_index] = ascii6[source[src_index + 0]] << 2 |
|
pub fn calcSizeUpperBound(encoded_len: usize) -> %usize {
|
||||||
ascii6[source[src_index + 1]] >> 4;
|
return @divTrunc(encoded_len, 4) * 3;
|
||||||
dest_index += 1;
|
|
||||||
|
|
||||||
dest[dest_index] = ascii6[source[src_index + 1]] << 4 |
|
|
||||||
ascii6[source[src_index + 2]] >> 2;
|
|
||||||
dest_index += 1;
|
|
||||||
|
|
||||||
dest[dest_index] = ascii6[source[src_index + 2]] << 6 |
|
|
||||||
ascii6[source[src_index + 3]];
|
|
||||||
dest_index += 1;
|
|
||||||
|
|
||||||
src_index += 4;
|
|
||||||
in_buf_len -= 4;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_buf_len > 1) {
|
/// Invalid characters that are not ignored result in error.InvalidCharacter.
|
||||||
dest[dest_index] = ascii6[source[src_index + 0]] << 2 |
|
/// Invalid padding results in error.InvalidPadding.
|
||||||
ascii6[source[src_index + 1]] >> 4;
|
/// Decoding more data than can fit in dest results in error.OutputTooSmall. See also ::calcSizeUpperBound.
|
||||||
dest_index += 1;
|
/// Returns the number of bytes writen to dest.
|
||||||
|
pub fn decode(decoder_with_ignore: &const Base64DecoderWithIgnore, dest: []u8, source: []const u8) -> %usize {
|
||||||
|
const decoder = &const decoder_with_ignore.decoder;
|
||||||
|
|
||||||
|
var src_cursor: usize = 0;
|
||||||
|
var dest_cursor: usize = 0;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
// get the next 4 chars, if available
|
||||||
|
var next_4_chars: [4]u8 = undefined;
|
||||||
|
var available_chars: usize = 0;
|
||||||
|
var pad_char_count: usize = 0;
|
||||||
|
while (available_chars < 4 and src_cursor < source.len) {
|
||||||
|
var c = source[src_cursor];
|
||||||
|
src_cursor += 1;
|
||||||
|
|
||||||
|
if (decoder.char_in_alphabet[c]) {
|
||||||
|
// normal char
|
||||||
|
next_4_chars[available_chars] = c;
|
||||||
|
available_chars += 1;
|
||||||
|
} else if (decoder_with_ignore.char_is_ignored[c]) {
|
||||||
|
// we're told to skip this one
|
||||||
|
continue;
|
||||||
|
} else if (c == decoder.pad_char) {
|
||||||
|
// the padding has begun. count the pad chars.
|
||||||
|
pad_char_count += 1;
|
||||||
|
while (src_cursor < source.len) {
|
||||||
|
c = source[src_cursor];
|
||||||
|
src_cursor += 1;
|
||||||
|
if (c == decoder.pad_char) {
|
||||||
|
pad_char_count += 1;
|
||||||
|
if (pad_char_count > 2) return error.InvalidCharacter;
|
||||||
|
} else if (decoder_with_ignore.char_is_ignored[c]) {
|
||||||
|
// we can even ignore chars during the padding
|
||||||
|
continue;
|
||||||
|
} else return error.InvalidCharacter;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} else return error.InvalidCharacter;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (available_chars) {
|
||||||
|
4 => {
|
||||||
|
// common case
|
||||||
|
if (dest_cursor + 3 > dest.len) return error.OutputTooSmall;
|
||||||
|
assert(pad_char_count == 0);
|
||||||
|
dest[dest_cursor + 0] = decoder.char_to_index[next_4_chars[0]] << 2 |
|
||||||
|
decoder.char_to_index[next_4_chars[1]] >> 4;
|
||||||
|
dest[dest_cursor + 1] = decoder.char_to_index[next_4_chars[1]] << 4 |
|
||||||
|
decoder.char_to_index[next_4_chars[2]] >> 2;
|
||||||
|
dest[dest_cursor + 2] = decoder.char_to_index[next_4_chars[2]] << 6 |
|
||||||
|
decoder.char_to_index[next_4_chars[3]];
|
||||||
|
dest_cursor += 3;
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
3 => {
|
||||||
|
if (dest_cursor + 2 > dest.len) return error.OutputTooSmall;
|
||||||
|
if (pad_char_count != 1) return error.InvalidPadding;
|
||||||
|
dest[dest_cursor + 0] = decoder.char_to_index[next_4_chars[0]] << 2 |
|
||||||
|
decoder.char_to_index[next_4_chars[1]] >> 4;
|
||||||
|
dest[dest_cursor + 1] = decoder.char_to_index[next_4_chars[1]] << 4 |
|
||||||
|
decoder.char_to_index[next_4_chars[2]] >> 2;
|
||||||
|
if (decoder.char_to_index[next_4_chars[2]] << 6 != 0) return error.InvalidPadding;
|
||||||
|
dest_cursor += 2;
|
||||||
|
break;
|
||||||
|
},
|
||||||
|
2 => {
|
||||||
|
if (dest_cursor + 1 > dest.len) return error.OutputTooSmall;
|
||||||
|
if (pad_char_count != 2) return error.InvalidPadding;
|
||||||
|
dest[dest_cursor + 0] = decoder.char_to_index[next_4_chars[0]] << 2 |
|
||||||
|
decoder.char_to_index[next_4_chars[1]] >> 4;
|
||||||
|
if (decoder.char_to_index[next_4_chars[1]] << 4 != 0) return error.InvalidPadding;
|
||||||
|
dest_cursor += 1;
|
||||||
|
break;
|
||||||
|
},
|
||||||
|
1 => {
|
||||||
|
return error.InvalidPadding;
|
||||||
|
},
|
||||||
|
0 => {
|
||||||
|
if (pad_char_count != 0) return error.InvalidPadding;
|
||||||
|
break;
|
||||||
|
},
|
||||||
|
else => unreachable,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(src_cursor == source.len);
|
||||||
|
|
||||||
|
return dest_cursor;
|
||||||
}
|
}
|
||||||
if (in_buf_len > 2) {
|
};
|
||||||
dest[dest_index] = ascii6[source[src_index + 1]] << 4 |
|
|
||||||
ascii6[source[src_index + 2]] >> 2;
|
|
||||||
dest_index += 1;
|
pub const standard_decoder_unsafe = Base64DecoderUnsafe.init(standard_alphabet_chars, standard_pad_char);
|
||||||
}
|
|
||||||
if (in_buf_len > 3) {
|
pub const Base64DecoderUnsafe = struct {
|
||||||
dest[dest_index] = ascii6[source[src_index + 2]] << 6 |
|
/// e.g. 'A' => 0.
|
||||||
ascii6[source[src_index + 3]];
|
/// undefined for any value not in the 64 alphabet chars.
|
||||||
dest_index += 1;
|
char_to_index: [256]u8,
|
||||||
|
pad_char: u8,
|
||||||
|
|
||||||
|
pub fn init(alphabet_chars: []const u8, pad_char: u8) -> Base64DecoderUnsafe {
|
||||||
|
assert(alphabet_chars.len == 64);
|
||||||
|
var result = Base64DecoderUnsafe {
|
||||||
|
.char_to_index = undefined,
|
||||||
|
.pad_char = pad_char,
|
||||||
|
};
|
||||||
|
for (alphabet_chars) |c, i| {
|
||||||
|
assert(c != pad_char);
|
||||||
|
result.char_to_index[c] = u8(i);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
return dest[0..dest_index];
|
/// The source buffer must be valid.
|
||||||
}
|
pub fn calcSize(decoder: &const Base64DecoderUnsafe, source: []const u8) -> usize {
|
||||||
|
return calcDecodedSizeExactUnsafe(source, decoder.pad_char);
|
||||||
pub fn calcEncodedSize(source_len: usize) -> usize {
|
|
||||||
return (((source_len * 4) / 3 + 3) / 4) * 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Computes the upper bound of the decoded size based only on the encoded length.
|
|
||||||
/// To compute the exact decoded size, see ::calcExactDecodedSize
|
|
||||||
pub fn calcMaxDecodedSize(encoded_len: usize) -> usize {
|
|
||||||
return @divExact(encoded_len * 3, 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Computes the number of decoded bytes there will be. This function must
|
|
||||||
/// be given the encoded buffer because there might be padding
|
|
||||||
/// bytes at the end ('=' in the standard alphabet)
|
|
||||||
pub fn calcExactDecodedSize(encoded: []const u8) -> usize {
|
|
||||||
return calcExactDecodedSizeWithAlphabet(encoded, standard_alphabet);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn calcExactDecodedSizeWithAlphabet(encoded: []const u8, alphabet: []const u8) -> usize {
|
|
||||||
assert(alphabet.len == 65);
|
|
||||||
return calcExactDecodedSizeWithPadChar(encoded, alphabet[64]);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn calcExactDecodedSizeWithPadChar(encoded: []const u8, pad_char: u8) -> usize {
|
|
||||||
var buf_len = encoded.len;
|
|
||||||
|
|
||||||
while (buf_len > 0 and encoded[buf_len - 1] == pad_char) {
|
|
||||||
buf_len -= 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (buf_len * 3) / 4;
|
/// dest.len must be what you get from ::calcDecodedSizeExactUnsafe.
|
||||||
|
/// invalid characters or padding will result in undefined values.
|
||||||
|
pub fn decode(decoder: &const Base64DecoderUnsafe, dest: []u8, source: []const u8) {
|
||||||
|
assert(dest.len == decoder.calcSize(source));
|
||||||
|
|
||||||
|
var src_index: usize = 0;
|
||||||
|
var dest_index: usize = 0;
|
||||||
|
var in_buf_len: usize = source.len;
|
||||||
|
|
||||||
|
while (in_buf_len > 0 and source[in_buf_len - 1] == decoder.pad_char) {
|
||||||
|
in_buf_len -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (in_buf_len > 4) {
|
||||||
|
dest[dest_index] = decoder.char_to_index[source[src_index + 0]] << 2 |
|
||||||
|
decoder.char_to_index[source[src_index + 1]] >> 4;
|
||||||
|
dest_index += 1;
|
||||||
|
|
||||||
|
dest[dest_index] = decoder.char_to_index[source[src_index + 1]] << 4 |
|
||||||
|
decoder.char_to_index[source[src_index + 2]] >> 2;
|
||||||
|
dest_index += 1;
|
||||||
|
|
||||||
|
dest[dest_index] = decoder.char_to_index[source[src_index + 2]] << 6 |
|
||||||
|
decoder.char_to_index[source[src_index + 3]];
|
||||||
|
dest_index += 1;
|
||||||
|
|
||||||
|
src_index += 4;
|
||||||
|
in_buf_len -= 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_buf_len > 1) {
|
||||||
|
dest[dest_index] = decoder.char_to_index[source[src_index + 0]] << 2 |
|
||||||
|
decoder.char_to_index[source[src_index + 1]] >> 4;
|
||||||
|
dest_index += 1;
|
||||||
|
}
|
||||||
|
if (in_buf_len > 2) {
|
||||||
|
dest[dest_index] = decoder.char_to_index[source[src_index + 1]] << 4 |
|
||||||
|
decoder.char_to_index[source[src_index + 2]] >> 2;
|
||||||
|
dest_index += 1;
|
||||||
|
}
|
||||||
|
if (in_buf_len > 3) {
|
||||||
|
dest[dest_index] = decoder.char_to_index[source[src_index + 2]] << 6 |
|
||||||
|
decoder.char_to_index[source[src_index + 3]];
|
||||||
|
dest_index += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fn calcDecodedSizeExactUnsafe(source: []const u8, pad_char: u8) -> usize {
|
||||||
|
if (source.len == 0) return 0;
|
||||||
|
var result = @divExact(source.len, 4) * 3;
|
||||||
|
if (source[source.len - 1] == pad_char) {
|
||||||
|
result -= 1;
|
||||||
|
if (source[source.len - 2] == pad_char) {
|
||||||
|
result -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
test "base64" {
|
test "base64" {
|
||||||
testBase64();
|
@setEvalBranchQuota(5000);
|
||||||
comptime testBase64();
|
%%testBase64();
|
||||||
|
comptime %%testBase64();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn testBase64() {
|
fn testBase64() -> %void {
|
||||||
testBase64Case("", "");
|
%return testAllApis("", "");
|
||||||
testBase64Case("f", "Zg==");
|
%return testAllApis("f", "Zg==");
|
||||||
testBase64Case("fo", "Zm8=");
|
%return testAllApis("fo", "Zm8=");
|
||||||
testBase64Case("foo", "Zm9v");
|
%return testAllApis("foo", "Zm9v");
|
||||||
testBase64Case("foob", "Zm9vYg==");
|
%return testAllApis("foob", "Zm9vYg==");
|
||||||
testBase64Case("fooba", "Zm9vYmE=");
|
%return testAllApis("fooba", "Zm9vYmE=");
|
||||||
testBase64Case("foobar", "Zm9vYmFy");
|
%return testAllApis("foobar", "Zm9vYmFy");
|
||||||
|
|
||||||
|
%return testDecodeIgnoreSpace("", " ");
|
||||||
|
%return testDecodeIgnoreSpace("f", "Z g= =");
|
||||||
|
%return testDecodeIgnoreSpace("fo", " Zm8=");
|
||||||
|
%return testDecodeIgnoreSpace("foo", "Zm9v ");
|
||||||
|
%return testDecodeIgnoreSpace("foob", "Zm9vYg = = ");
|
||||||
|
%return testDecodeIgnoreSpace("fooba", "Zm9v YmE=");
|
||||||
|
%return testDecodeIgnoreSpace("foobar", " Z m 9 v Y m F y ");
|
||||||
|
|
||||||
|
// test getting some api errors
|
||||||
|
%return testError("A", error.InvalidPadding);
|
||||||
|
%return testError("AA", error.InvalidPadding);
|
||||||
|
%return testError("AAA", error.InvalidPadding);
|
||||||
|
%return testError("A..A", error.InvalidCharacter);
|
||||||
|
%return testError("AA=A", error.InvalidCharacter);
|
||||||
|
%return testError("AA/=", error.InvalidPadding);
|
||||||
|
%return testError("A/==", error.InvalidPadding);
|
||||||
|
%return testError("A===", error.InvalidCharacter);
|
||||||
|
%return testError("====", error.InvalidCharacter);
|
||||||
|
|
||||||
|
%return testOutputTooSmallError("AA==");
|
||||||
|
%return testOutputTooSmallError("AAA=");
|
||||||
|
%return testOutputTooSmallError("AAAA");
|
||||||
|
%return testOutputTooSmallError("AAAAAA==");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn testBase64Case(expected_decoded: []const u8, expected_encoded: []const u8) {
|
fn testAllApis(expected_decoded: []const u8, expected_encoded: []const u8) -> %void {
|
||||||
const calculated_decoded_len = calcExactDecodedSize(expected_encoded);
|
// Base64Encoder
|
||||||
assert(calculated_decoded_len == expected_decoded.len);
|
{
|
||||||
|
var buffer: [0x100]u8 = undefined;
|
||||||
|
var encoded = buffer[0..Base64Encoder.calcSize(expected_decoded.len)];
|
||||||
|
standard_encoder.encode(encoded, expected_decoded);
|
||||||
|
assert(mem.eql(u8, encoded, expected_encoded));
|
||||||
|
}
|
||||||
|
|
||||||
const calculated_encoded_len = calcEncodedSize(expected_decoded.len);
|
// Base64Decoder
|
||||||
assert(calculated_encoded_len == expected_encoded.len);
|
{
|
||||||
|
var buffer: [0x100]u8 = undefined;
|
||||||
|
var decoded = buffer[0..%return standard_decoder.calcSize(expected_encoded)];
|
||||||
|
%return standard_decoder.decode(decoded, expected_encoded);
|
||||||
|
assert(mem.eql(u8, decoded, expected_decoded));
|
||||||
|
}
|
||||||
|
|
||||||
var buf: [100]u8 = undefined;
|
// Base64DecoderWithIgnore
|
||||||
|
{
|
||||||
|
const standard_decoder_ignore_nothing = Base64DecoderWithIgnore.init(
|
||||||
|
standard_alphabet_chars, standard_pad_char, "");
|
||||||
|
var buffer: [0x100]u8 = undefined;
|
||||||
|
var decoded = buffer[0..%return Base64DecoderWithIgnore.calcSizeUpperBound(expected_encoded.len)];
|
||||||
|
var written = %return standard_decoder_ignore_nothing.decode(decoded, expected_encoded);
|
||||||
|
assert(written <= decoded.len);
|
||||||
|
assert(mem.eql(u8, decoded[0..written], expected_decoded));
|
||||||
|
}
|
||||||
|
|
||||||
const actual_decoded = decode(buf[0..], expected_encoded);
|
// Base64DecoderUnsafe
|
||||||
assert(actual_decoded.len == expected_decoded.len);
|
{
|
||||||
assert(mem.eql(u8, expected_decoded, actual_decoded));
|
var buffer: [0x100]u8 = undefined;
|
||||||
|
var decoded = buffer[0..standard_decoder_unsafe.calcSize(expected_encoded)];
|
||||||
const actual_encoded = encode(buf[0..], expected_decoded);
|
standard_decoder_unsafe.decode(decoded, expected_encoded);
|
||||||
assert(actual_encoded.len == expected_encoded.len);
|
assert(mem.eql(u8, decoded, expected_decoded));
|
||||||
assert(mem.eql(u8, expected_encoded, actual_encoded));
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn testDecodeIgnoreSpace(expected_decoded: []const u8, encoded: []const u8) -> %void {
|
||||||
|
const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(
|
||||||
|
standard_alphabet_chars, standard_pad_char, " ");
|
||||||
|
var buffer: [0x100]u8 = undefined;
|
||||||
|
var decoded = buffer[0..%return Base64DecoderWithIgnore.calcSizeUpperBound(encoded.len)];
|
||||||
|
var written = %return standard_decoder_ignore_space.decode(decoded, encoded);
|
||||||
|
assert(mem.eql(u8, decoded[0..written], expected_decoded));
|
||||||
|
}
|
||||||
|
|
||||||
|
error ExpectedError;
|
||||||
|
fn testError(encoded: []const u8, expected_err: error) -> %void {
|
||||||
|
const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(
|
||||||
|
standard_alphabet_chars, standard_pad_char, " ");
|
||||||
|
var buffer: [0x100]u8 = undefined;
|
||||||
|
if (standard_decoder.calcSize(encoded)) |decoded_size| {
|
||||||
|
var decoded = buffer[0..decoded_size];
|
||||||
|
if (standard_decoder.decode(decoded, encoded)) |_| {
|
||||||
|
return error.ExpectedError;
|
||||||
|
} else |err| if (err != expected_err) return err;
|
||||||
|
} else |err| if (err != expected_err) return err;
|
||||||
|
|
||||||
|
if (standard_decoder_ignore_space.decode(buffer[0..], encoded)) |_| {
|
||||||
|
return error.ExpectedError;
|
||||||
|
} else |err| if (err != expected_err) return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn testOutputTooSmallError(encoded: []const u8) -> %void {
|
||||||
|
const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(
|
||||||
|
standard_alphabet_chars, standard_pad_char, " ");
|
||||||
|
var buffer: [0x100]u8 = undefined;
|
||||||
|
var decoded = buffer[0..calcDecodedSizeExactUnsafe(encoded, standard_pad_char) - 1];
|
||||||
|
if (standard_decoder_ignore_space.decode(decoded, encoded)) |_| {
|
||||||
|
return error.ExpectedError;
|
||||||
|
} else |err| if (err != error.OutputTooSmall) return err;
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,8 @@ pub const windowsWaitSingle = windows_util.windowsWaitSingle;
|
||||||
pub const windowsWrite = windows_util.windowsWrite;
|
pub const windowsWrite = windows_util.windowsWrite;
|
||||||
pub const windowsIsCygwinPty = windows_util.windowsIsCygwinPty;
|
pub const windowsIsCygwinPty = windows_util.windowsIsCygwinPty;
|
||||||
pub const windowsOpen = windows_util.windowsOpen;
|
pub const windowsOpen = windows_util.windowsOpen;
|
||||||
|
pub const windowsLoadDll = windows_util.windowsLoadDll;
|
||||||
|
pub const windowsUnloadDll = windows_util.windowsUnloadDll;
|
||||||
pub const createWindowsEnvBlock = windows_util.createWindowsEnvBlock;
|
pub const createWindowsEnvBlock = windows_util.createWindowsEnvBlock;
|
||||||
|
|
||||||
pub const FileHandle = if (is_windows) windows.HANDLE else i32;
|
pub const FileHandle = if (is_windows) windows.HANDLE else i32;
|
||||||
|
@ -620,7 +622,9 @@ pub fn symLinkPosix(allocator: &Allocator, existing_path: []const u8, new_path:
|
||||||
}
|
}
|
||||||
|
|
||||||
// here we replace the standard +/ with -_ so that it can be used in a file name
|
// here we replace the standard +/ with -_ so that it can be used in a file name
|
||||||
const b64_fs_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_=";
|
const b64_fs_encoder = base64.Base64Encoder.init(
|
||||||
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",
|
||||||
|
base64.standard_pad_char);
|
||||||
|
|
||||||
pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) -> %void {
|
pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) -> %void {
|
||||||
if (symLink(allocator, existing_path, new_path)) {
|
if (symLink(allocator, existing_path, new_path)) {
|
||||||
|
@ -632,12 +636,12 @@ pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path:
|
||||||
}
|
}
|
||||||
|
|
||||||
var rand_buf: [12]u8 = undefined;
|
var rand_buf: [12]u8 = undefined;
|
||||||
const tmp_path = %return allocator.alloc(u8, new_path.len + base64.calcEncodedSize(rand_buf.len));
|
const tmp_path = %return allocator.alloc(u8, new_path.len + base64.Base64Encoder.calcSize(rand_buf.len));
|
||||||
defer allocator.free(tmp_path);
|
defer allocator.free(tmp_path);
|
||||||
mem.copy(u8, tmp_path[0..], new_path);
|
mem.copy(u8, tmp_path[0..], new_path);
|
||||||
while (true) {
|
while (true) {
|
||||||
%return getRandomBytes(rand_buf[0..]);
|
%return getRandomBytes(rand_buf[0..]);
|
||||||
_ = base64.encodeWithAlphabet(tmp_path[new_path.len..], rand_buf, b64_fs_alphabet);
|
b64_fs_encoder.encode(tmp_path[new_path.len..], rand_buf);
|
||||||
if (symLink(allocator, existing_path, tmp_path)) {
|
if (symLink(allocator, existing_path, tmp_path)) {
|
||||||
return rename(allocator, tmp_path, new_path);
|
return rename(allocator, tmp_path, new_path);
|
||||||
} else |err| {
|
} else |err| {
|
||||||
|
@ -715,11 +719,11 @@ pub fn copyFile(allocator: &Allocator, source_path: []const u8, dest_path: []con
|
||||||
/// Guaranteed to be atomic.
|
/// Guaranteed to be atomic.
|
||||||
pub fn copyFileMode(allocator: &Allocator, source_path: []const u8, dest_path: []const u8, mode: usize) -> %void {
|
pub fn copyFileMode(allocator: &Allocator, source_path: []const u8, dest_path: []const u8, mode: usize) -> %void {
|
||||||
var rand_buf: [12]u8 = undefined;
|
var rand_buf: [12]u8 = undefined;
|
||||||
const tmp_path = %return allocator.alloc(u8, dest_path.len + base64.calcEncodedSize(rand_buf.len));
|
const tmp_path = %return allocator.alloc(u8, dest_path.len + base64.Base64Encoder.calcSize(rand_buf.len));
|
||||||
defer allocator.free(tmp_path);
|
defer allocator.free(tmp_path);
|
||||||
mem.copy(u8, tmp_path[0..], dest_path);
|
mem.copy(u8, tmp_path[0..], dest_path);
|
||||||
%return getRandomBytes(rand_buf[0..]);
|
%return getRandomBytes(rand_buf[0..]);
|
||||||
_ = base64.encodeWithAlphabet(tmp_path[dest_path.len..], rand_buf, b64_fs_alphabet);
|
b64_fs_encoder.encode(tmp_path[dest_path.len..], rand_buf);
|
||||||
|
|
||||||
var out_file = %return io.File.openWriteMode(tmp_path, mode, allocator);
|
var out_file = %return io.File.openWriteMode(tmp_path, mode, allocator);
|
||||||
defer out_file.close();
|
defer out_file.close();
|
||||||
|
|
|
@ -84,6 +84,11 @@ pub extern "kernel32" stdcallcc fn WriteFile(in_hFile: HANDLE, in_lpBuffer: &con
|
||||||
in_nNumberOfBytesToWrite: DWORD, out_lpNumberOfBytesWritten: ?&DWORD,
|
in_nNumberOfBytesToWrite: DWORD, out_lpNumberOfBytesWritten: ?&DWORD,
|
||||||
in_out_lpOverlapped: ?&OVERLAPPED) -> BOOL;
|
in_out_lpOverlapped: ?&OVERLAPPED) -> BOOL;
|
||||||
|
|
||||||
|
//TODO: call unicode versions instead of relying on ANSI code page
|
||||||
|
pub extern "kernel32" stdcallcc fn LoadLibraryA(lpLibFileName: LPCSTR) -> ?HMODULE;
|
||||||
|
|
||||||
|
pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) -> BOOL;
|
||||||
|
|
||||||
pub extern "user32" stdcallcc fn MessageBoxA(hWnd: ?HANDLE, lpText: ?LPCTSTR, lpCaption: ?LPCTSTR, uType: UINT) -> c_int;
|
pub extern "user32" stdcallcc fn MessageBoxA(hWnd: ?HANDLE, lpText: ?LPCTSTR, lpCaption: ?LPCTSTR, uType: UINT) -> c_int;
|
||||||
|
|
||||||
pub const PROV_RSA_FULL = 1;
|
pub const PROV_RSA_FULL = 1;
|
||||||
|
@ -97,6 +102,7 @@ pub const FLOAT = f32;
|
||||||
pub const HANDLE = &c_void;
|
pub const HANDLE = &c_void;
|
||||||
pub const HCRYPTPROV = ULONG_PTR;
|
pub const HCRYPTPROV = ULONG_PTR;
|
||||||
pub const HINSTANCE = &@OpaqueType();
|
pub const HINSTANCE = &@OpaqueType();
|
||||||
|
pub const HMODULE = &@OpaqueType();
|
||||||
pub const INT = c_int;
|
pub const INT = c_int;
|
||||||
pub const LPBYTE = &BYTE;
|
pub const LPBYTE = &BYTE;
|
||||||
pub const LPCH = &CHAR;
|
pub const LPCH = &CHAR;
|
||||||
|
|
|
@ -4,6 +4,7 @@ const windows = std.os.windows;
|
||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
const mem = std.mem;
|
const mem = std.mem;
|
||||||
const BufMap = std.BufMap;
|
const BufMap = std.BufMap;
|
||||||
|
const cstr = std.cstr;
|
||||||
|
|
||||||
error WaitAbandoned;
|
error WaitAbandoned;
|
||||||
error WaitTimeOut;
|
error WaitTimeOut;
|
||||||
|
@ -149,3 +150,25 @@ pub fn createWindowsEnvBlock(allocator: &mem.Allocator, env_map: &const BufMap)
|
||||||
result[i] = 0;
|
result[i] = 0;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
error DllNotFound;
|
||||||
|
pub fn windowsLoadDll(allocator: &mem.Allocator, dll_path: []const u8) -> %windows.HMODULE {
|
||||||
|
const padded_buff = %return cstr.addNullByte(allocator, dll_path);
|
||||||
|
defer allocator.free(padded_buff);
|
||||||
|
return windows.LoadLibraryA(padded_buff.ptr) ?? error.DllNotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn windowsUnloadDll(hModule: windows.HMODULE) {
|
||||||
|
assert(windows.FreeLibrary(hModule)!= 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
test "InvalidDll" {
|
||||||
|
const DllName = "asdf.dll";
|
||||||
|
const allocator = std.debug.global_allocator;
|
||||||
|
const handle = os.windowsLoadDll(allocator, DllName) %% |err| {
|
||||||
|
assert(err == error.DllNotFound);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,3 +31,47 @@ test "unions embedded in aggregate types" {
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const Foo = union {
|
||||||
|
float: f64,
|
||||||
|
int: i32,
|
||||||
|
};
|
||||||
|
|
||||||
|
test "basic unions" {
|
||||||
|
var foo = Foo { .int = 1 };
|
||||||
|
assert(foo.int == 1);
|
||||||
|
foo = Foo {.float = 12.34};
|
||||||
|
assert(foo.float == 12.34);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "init union with runtime value" {
|
||||||
|
var foo: Foo = undefined;
|
||||||
|
|
||||||
|
setFloat(&foo, 12.34);
|
||||||
|
assert(foo.float == 12.34);
|
||||||
|
|
||||||
|
setInt(&foo, 42);
|
||||||
|
assert(foo.int == 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setFloat(foo: &Foo, x: f64) {
|
||||||
|
*foo = Foo { .float = x };
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setInt(foo: &Foo, x: i32) {
|
||||||
|
*foo = Foo { .int = x };
|
||||||
|
}
|
||||||
|
|
||||||
|
const FooExtern = extern union {
|
||||||
|
float: f64,
|
||||||
|
int: i32,
|
||||||
|
};
|
||||||
|
|
||||||
|
test "basic extern unions" {
|
||||||
|
var foo = FooExtern { .int = 1 };
|
||||||
|
assert(foo.int == 1);
|
||||||
|
foo.float = 12.34;
|
||||||
|
assert(foo.float == 12.34);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -389,8 +389,8 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
|
||||||
\\ const y = a.bar;
|
\\ const y = a.bar;
|
||||||
\\}
|
\\}
|
||||||
,
|
,
|
||||||
".tmp_source.zig:4:6: error: no member named 'foo' in 'A'",
|
".tmp_source.zig:4:6: error: no member named 'foo' in struct 'A'",
|
||||||
".tmp_source.zig:5:16: error: no member named 'bar' in 'A'");
|
".tmp_source.zig:5:16: error: no member named 'bar' in struct 'A'");
|
||||||
|
|
||||||
cases.add("redefinition of struct",
|
cases.add("redefinition of struct",
|
||||||
\\const A = struct { x : i32, };
|
\\const A = struct { x : i32, };
|
||||||
|
@ -454,7 +454,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
|
||||||
\\ .foo = 42,
|
\\ .foo = 42,
|
||||||
\\ };
|
\\ };
|
||||||
\\}
|
\\}
|
||||||
, ".tmp_source.zig:10:9: error: no member named 'foo' in 'A'");
|
, ".tmp_source.zig:10:9: error: no member named 'foo' in struct 'A'");
|
||||||
|
|
||||||
cases.add("invalid break expression",
|
cases.add("invalid break expression",
|
||||||
\\export fn f() {
|
\\export fn f() {
|
||||||
|
@ -2343,4 +2343,23 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
|
||||||
\\pub extern fn foo(format: &const u8, ...);
|
\\pub extern fn foo(format: &const u8, ...);
|
||||||
,
|
,
|
||||||
".tmp_source.zig:2:9: error: expected type '&const u8', found '[5]u8'");
|
".tmp_source.zig:2:9: error: expected type '&const u8', found '[5]u8'");
|
||||||
|
|
||||||
|
cases.add("constant inside comptime function has compile error",
|
||||||
|
\\const ContextAllocator = MemoryPool(usize);
|
||||||
|
\\
|
||||||
|
\\pub fn MemoryPool(comptime T: type) -> type {
|
||||||
|
\\ const free_list_t = @compileError("aoeu");
|
||||||
|
\\
|
||||||
|
\\ struct {
|
||||||
|
\\ free_list: free_list_t,
|
||||||
|
\\ }
|
||||||
|
\\}
|
||||||
|
\\
|
||||||
|
\\export fn entry() {
|
||||||
|
\\ var allocator: ContextAllocator = undefined;
|
||||||
|
\\}
|
||||||
|
,
|
||||||
|
".tmp_source.zig:4:25: error: aoeu",
|
||||||
|
".tmp_source.zig:1:36: note: called from here",
|
||||||
|
".tmp_source.zig:12:20: note: referenced here");
|
||||||
}
|
}
|
||||||
|
|
|
@ -260,4 +260,24 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
|
||||||
\\ return int_slice[0];
|
\\ return int_slice[0];
|
||||||
\\}
|
\\}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
cases.addDebugSafety("bad union field access",
|
||||||
|
\\pub fn panic(message: []const u8) -> noreturn {
|
||||||
|
\\ @import("std").os.exit(126);
|
||||||
|
\\}
|
||||||
|
\\
|
||||||
|
\\const Foo = union {
|
||||||
|
\\ float: f32,
|
||||||
|
\\ int: u32,
|
||||||
|
\\};
|
||||||
|
\\
|
||||||
|
\\pub fn main() -> %void {
|
||||||
|
\\ var f = Foo { .int = 42 };
|
||||||
|
\\ bar(&f);
|
||||||
|
\\}
|
||||||
|
\\
|
||||||
|
\\fn bar(f: &Foo) {
|
||||||
|
\\ f.float = 12.34;
|
||||||
|
\\}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ const build_examples = @import("build_examples.zig");
|
||||||
const compile_errors = @import("compile_errors.zig");
|
const compile_errors = @import("compile_errors.zig");
|
||||||
const assemble_and_link = @import("assemble_and_link.zig");
|
const assemble_and_link = @import("assemble_and_link.zig");
|
||||||
const debug_safety = @import("debug_safety.zig");
|
const debug_safety = @import("debug_safety.zig");
|
||||||
const parsec = @import("parsec.zig");
|
const translate_c = @import("translate_c.zig");
|
||||||
|
|
||||||
const TestTarget = struct {
|
const TestTarget = struct {
|
||||||
os: builtin.Os,
|
os: builtin.Os,
|
||||||
|
@ -123,16 +123,16 @@ pub fn addAssembleAndLinkTests(b: &build.Builder, test_filter: ?[]const u8) -> &
|
||||||
return cases.step;
|
return cases.step;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn addParseCTests(b: &build.Builder, test_filter: ?[]const u8) -> &build.Step {
|
pub fn addTranslateCTests(b: &build.Builder, test_filter: ?[]const u8) -> &build.Step {
|
||||||
const cases = %%b.allocator.create(ParseCContext);
|
const cases = %%b.allocator.create(TranslateCContext);
|
||||||
*cases = ParseCContext {
|
*cases = TranslateCContext {
|
||||||
.b = b,
|
.b = b,
|
||||||
.step = b.step("test-parsec", "Run the C header file parsing tests"),
|
.step = b.step("test-translate-c", "Run the C header file parsing tests"),
|
||||||
.test_index = 0,
|
.test_index = 0,
|
||||||
.test_filter = test_filter,
|
.test_filter = test_filter,
|
||||||
};
|
};
|
||||||
|
|
||||||
parsec.addCases(cases);
|
translate_c.addCases(cases);
|
||||||
|
|
||||||
return cases.step;
|
return cases.step;
|
||||||
}
|
}
|
||||||
|
@ -770,7 +770,7 @@ pub const BuildExamplesContext = struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const ParseCContext = struct {
|
pub const TranslateCContext = struct {
|
||||||
b: &build.Builder,
|
b: &build.Builder,
|
||||||
step: &build.Step,
|
step: &build.Step,
|
||||||
test_index: usize,
|
test_index: usize,
|
||||||
|
@ -799,17 +799,17 @@ pub const ParseCContext = struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const ParseCCmpOutputStep = struct {
|
const TranslateCCmpOutputStep = struct {
|
||||||
step: build.Step,
|
step: build.Step,
|
||||||
context: &ParseCContext,
|
context: &TranslateCContext,
|
||||||
name: []const u8,
|
name: []const u8,
|
||||||
test_index: usize,
|
test_index: usize,
|
||||||
case: &const TestCase,
|
case: &const TestCase,
|
||||||
|
|
||||||
pub fn create(context: &ParseCContext, name: []const u8, case: &const TestCase) -> &ParseCCmpOutputStep {
|
pub fn create(context: &TranslateCContext, name: []const u8, case: &const TestCase) -> &TranslateCCmpOutputStep {
|
||||||
const allocator = context.b.allocator;
|
const allocator = context.b.allocator;
|
||||||
const ptr = %%allocator.create(ParseCCmpOutputStep);
|
const ptr = %%allocator.create(TranslateCCmpOutputStep);
|
||||||
*ptr = ParseCCmpOutputStep {
|
*ptr = TranslateCCmpOutputStep {
|
||||||
.step = build.Step.init("ParseCCmpOutput", allocator, make),
|
.step = build.Step.init("ParseCCmpOutput", allocator, make),
|
||||||
.context = context,
|
.context = context,
|
||||||
.name = name,
|
.name = name,
|
||||||
|
@ -821,7 +821,7 @@ pub const ParseCContext = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make(step: &build.Step) -> %void {
|
fn make(step: &build.Step) -> %void {
|
||||||
const self = @fieldParentPtr(ParseCCmpOutputStep, "step", step);
|
const self = @fieldParentPtr(TranslateCCmpOutputStep, "step", step);
|
||||||
const b = self.context.b;
|
const b = self.context.b;
|
||||||
|
|
||||||
const root_src = %%os.path.join(b.allocator, b.cache_root, self.case.sources.items[0].filename);
|
const root_src = %%os.path.join(b.allocator, b.cache_root, self.case.sources.items[0].filename);
|
||||||
|
@ -829,7 +829,7 @@ pub const ParseCContext = struct {
|
||||||
var zig_args = ArrayList([]const u8).init(b.allocator);
|
var zig_args = ArrayList([]const u8).init(b.allocator);
|
||||||
%%zig_args.append(b.zig_exe);
|
%%zig_args.append(b.zig_exe);
|
||||||
|
|
||||||
%%zig_args.append("parsec");
|
%%zig_args.append("translate-c");
|
||||||
%%zig_args.append(b.pathFromRoot(root_src));
|
%%zig_args.append(b.pathFromRoot(root_src));
|
||||||
|
|
||||||
warn("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);
|
warn("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);
|
||||||
|
@ -882,7 +882,7 @@ pub const ParseCContext = struct {
|
||||||
|
|
||||||
if (stderr.len != 0 and !self.case.allow_warnings) {
|
if (stderr.len != 0 and !self.case.allow_warnings) {
|
||||||
warn(
|
warn(
|
||||||
\\====== parsec emitted warnings: ============
|
\\====== translate-c emitted warnings: =======
|
||||||
\\{}
|
\\{}
|
||||||
\\============================================
|
\\============================================
|
||||||
\\
|
\\
|
||||||
|
@ -914,7 +914,7 @@ pub const ParseCContext = struct {
|
||||||
warn("\n");
|
warn("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create(self: &ParseCContext, allow_warnings: bool, filename: []const u8, name: []const u8,
|
pub fn create(self: &TranslateCContext, allow_warnings: bool, filename: []const u8, name: []const u8,
|
||||||
source: []const u8, expected_lines: ...) -> &TestCase
|
source: []const u8, expected_lines: ...) -> &TestCase
|
||||||
{
|
{
|
||||||
const tc = %%self.b.allocator.create(TestCase);
|
const tc = %%self.b.allocator.create(TestCase);
|
||||||
|
@ -932,37 +932,37 @@ pub const ParseCContext = struct {
|
||||||
return tc;
|
return tc;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add(self: &ParseCContext, name: []const u8, source: []const u8, expected_lines: ...) {
|
pub fn add(self: &TranslateCContext, name: []const u8, source: []const u8, expected_lines: ...) {
|
||||||
const tc = self.create(false, "source.h", name, source, expected_lines);
|
const tc = self.create(false, "source.h", name, source, expected_lines);
|
||||||
self.addCase(tc);
|
self.addCase(tc);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn addC(self: &ParseCContext, name: []const u8, source: []const u8, expected_lines: ...) {
|
pub fn addC(self: &TranslateCContext, name: []const u8, source: []const u8, expected_lines: ...) {
|
||||||
const tc = self.create(false, "source.c", name, source, expected_lines);
|
const tc = self.create(false, "source.c", name, source, expected_lines);
|
||||||
self.addCase(tc);
|
self.addCase(tc);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn addAllowWarnings(self: &ParseCContext, name: []const u8, source: []const u8, expected_lines: ...) {
|
pub fn addAllowWarnings(self: &TranslateCContext, name: []const u8, source: []const u8, expected_lines: ...) {
|
||||||
const tc = self.create(true, "source.h", name, source, expected_lines);
|
const tc = self.create(true, "source.h", name, source, expected_lines);
|
||||||
self.addCase(tc);
|
self.addCase(tc);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn addCase(self: &ParseCContext, case: &const TestCase) {
|
pub fn addCase(self: &TranslateCContext, case: &const TestCase) {
|
||||||
const b = self.b;
|
const b = self.b;
|
||||||
|
|
||||||
const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "parsec {}", case.name);
|
const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "translate-c {}", case.name);
|
||||||
if (self.test_filter) |filter| {
|
if (self.test_filter) |filter| {
|
||||||
if (mem.indexOf(u8, annotated_case_name, filter) == null)
|
if (mem.indexOf(u8, annotated_case_name, filter) == null)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const parsec_and_cmp = ParseCCmpOutputStep.create(self, annotated_case_name, case);
|
const translate_c_and_cmp = TranslateCCmpOutputStep.create(self, annotated_case_name, case);
|
||||||
self.step.dependOn(&parsec_and_cmp.step);
|
self.step.dependOn(&translate_c_and_cmp.step);
|
||||||
|
|
||||||
for (case.sources.toSliceConst()) |src_file| {
|
for (case.sources.toSliceConst()) |src_file| {
|
||||||
const expanded_src_path = %%os.path.join(b.allocator, b.cache_root, src_file.filename);
|
const expanded_src_path = %%os.path.join(b.allocator, b.cache_root, src_file.filename);
|
||||||
const write_src = b.addWriteFile(expanded_src_path, src_file.source);
|
const write_src = b.addWriteFile(expanded_src_path, src_file.source);
|
||||||
parsec_and_cmp.step.dependOn(&write_src.step);
|
translate_c_and_cmp.step.dependOn(&write_src.step);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
const tests = @import("tests.zig");
|
const tests = @import("tests.zig");
|
||||||
|
|
||||||
pub fn addCases(cases: &tests.ParseCContext) {
|
pub fn addCases(cases: &tests.TranslateCContext) {
|
||||||
cases.addAllowWarnings("simple data types",
|
cases.addAllowWarnings("simple data types",
|
||||||
\\#include <stdint.h>
|
\\#include <stdint.h>
|
||||||
\\int foo(char a, unsigned char b, signed char c);
|
\\int foo(char a, unsigned char b, signed char c);
|
||||||
|
@ -677,12 +677,12 @@ pub fn addCases(cases: &tests.ParseCContext) {
|
||||||
\\ };
|
\\ };
|
||||||
\\ a >>= @import("std").math.Log2Int(c_int)({
|
\\ a >>= @import("std").math.Log2Int(c_int)({
|
||||||
\\ const _ref = &a;
|
\\ const _ref = &a;
|
||||||
\\ (*_ref) = c_int(c_int(*_ref) >> @import("std").math.Log2Int(c_int)(1));
|
\\ (*_ref) = ((*_ref) >> @import("std").math.Log2Int(c_int)(1));
|
||||||
\\ *_ref
|
\\ *_ref
|
||||||
\\ });
|
\\ });
|
||||||
\\ a <<= @import("std").math.Log2Int(c_int)({
|
\\ a <<= @import("std").math.Log2Int(c_int)({
|
||||||
\\ const _ref = &a;
|
\\ const _ref = &a;
|
||||||
\\ (*_ref) = c_int(c_int(*_ref) << @import("std").math.Log2Int(c_int)(1));
|
\\ (*_ref) = ((*_ref) << @import("std").math.Log2Int(c_int)(1));
|
||||||
\\ *_ref
|
\\ *_ref
|
||||||
\\ });
|
\\ });
|
||||||
\\}
|
\\}
|
||||||
|
@ -735,12 +735,12 @@ pub fn addCases(cases: &tests.ParseCContext) {
|
||||||
\\ };
|
\\ };
|
||||||
\\ a >>= @import("std").math.Log2Int(c_uint)({
|
\\ a >>= @import("std").math.Log2Int(c_uint)({
|
||||||
\\ const _ref = &a;
|
\\ const _ref = &a;
|
||||||
\\ (*_ref) = c_uint(c_uint(*_ref) >> @import("std").math.Log2Int(c_uint)(1));
|
\\ (*_ref) = ((*_ref) >> @import("std").math.Log2Int(c_uint)(1));
|
||||||
\\ *_ref
|
\\ *_ref
|
||||||
\\ });
|
\\ });
|
||||||
\\ a <<= @import("std").math.Log2Int(c_uint)({
|
\\ a <<= @import("std").math.Log2Int(c_uint)({
|
||||||
\\ const _ref = &a;
|
\\ const _ref = &a;
|
||||||
\\ (*_ref) = c_uint(c_uint(*_ref) << @import("std").math.Log2Int(c_uint)(1));
|
\\ (*_ref) = ((*_ref) << @import("std").math.Log2Int(c_uint)(1));
|
||||||
\\ *_ref
|
\\ *_ref
|
||||||
\\ });
|
\\ });
|
||||||
\\}
|
\\}
|
||||||
|
@ -805,6 +805,50 @@ pub fn addCases(cases: &tests.ParseCContext) {
|
||||||
\\}
|
\\}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
cases.addC("pre increment/decrement",
|
||||||
|
\\void foo(void) {
|
||||||
|
\\ int i = 0;
|
||||||
|
\\ unsigned u = 0;
|
||||||
|
\\ ++i;
|
||||||
|
\\ --i;
|
||||||
|
\\ ++u;
|
||||||
|
\\ --u;
|
||||||
|
\\ i = ++i;
|
||||||
|
\\ i = --i;
|
||||||
|
\\ u = ++u;
|
||||||
|
\\ u = --u;
|
||||||
|
\\}
|
||||||
|
,
|
||||||
|
\\export fn foo() {
|
||||||
|
\\ var i: c_int = 0;
|
||||||
|
\\ var u: c_uint = c_uint(0);
|
||||||
|
\\ i += 1;
|
||||||
|
\\ i -= 1;
|
||||||
|
\\ u +%= 1;
|
||||||
|
\\ u -%= 1;
|
||||||
|
\\ i = {
|
||||||
|
\\ const _ref = &i;
|
||||||
|
\\ (*_ref) += 1;
|
||||||
|
\\ *_ref
|
||||||
|
\\ };
|
||||||
|
\\ i = {
|
||||||
|
\\ const _ref = &i;
|
||||||
|
\\ (*_ref) -= 1;
|
||||||
|
\\ *_ref
|
||||||
|
\\ };
|
||||||
|
\\ u = {
|
||||||
|
\\ const _ref = &u;
|
||||||
|
\\ (*_ref) +%= 1;
|
||||||
|
\\ *_ref
|
||||||
|
\\ };
|
||||||
|
\\ u = {
|
||||||
|
\\ const _ref = &u;
|
||||||
|
\\ (*_ref) -%= 1;
|
||||||
|
\\ *_ref
|
||||||
|
\\ };
|
||||||
|
\\}
|
||||||
|
);
|
||||||
|
|
||||||
cases.addC("do loop",
|
cases.addC("do loop",
|
||||||
\\void foo(void) {
|
\\void foo(void) {
|
||||||
\\ int a = 2;
|
\\ int a = 2;
|
||||||
|
@ -834,17 +878,21 @@ pub fn addCases(cases: &tests.ParseCContext) {
|
||||||
|
|
||||||
cases.addC("deref function pointer",
|
cases.addC("deref function pointer",
|
||||||
\\void foo(void) {}
|
\\void foo(void) {}
|
||||||
|
\\void baz(void) {}
|
||||||
\\void bar(void) {
|
\\void bar(void) {
|
||||||
\\ void(*f)(void) = foo;
|
\\ void(*f)(void) = foo;
|
||||||
\\ f();
|
\\ f();
|
||||||
\\ (*(f))();
|
\\ (*(f))();
|
||||||
|
\\ baz();
|
||||||
\\}
|
\\}
|
||||||
,
|
,
|
||||||
\\export fn foo() {}
|
\\export fn foo() {}
|
||||||
|
\\export fn baz() {}
|
||||||
\\export fn bar() {
|
\\export fn bar() {
|
||||||
\\ var f: ?extern fn() = foo;
|
\\ var f: ?extern fn() = foo;
|
||||||
\\ (??f)();
|
\\ (??f)();
|
||||||
\\ (??f)();
|
\\ (??f)();
|
||||||
|
\\ baz();
|
||||||
\\}
|
\\}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -857,15 +905,277 @@ pub fn addCases(cases: &tests.ParseCContext) {
|
||||||
\\ (*(??x)) = 1;
|
\\ (*(??x)) = 1;
|
||||||
\\}
|
\\}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
cases.add("simple union",
|
||||||
|
\\union Foo {
|
||||||
|
\\ int x;
|
||||||
|
\\ double y;
|
||||||
|
\\};
|
||||||
|
,
|
||||||
|
\\pub const union_Foo = extern union {
|
||||||
|
\\ x: c_int,
|
||||||
|
\\ y: f64,
|
||||||
|
\\};
|
||||||
|
,
|
||||||
|
\\pub const Foo = union_Foo;
|
||||||
|
);
|
||||||
|
|
||||||
|
cases.add("address of operator",
|
||||||
|
\\int foo(void) {
|
||||||
|
\\ int x = 1234;
|
||||||
|
\\ int *ptr = &x;
|
||||||
|
\\ return *ptr;
|
||||||
|
\\}
|
||||||
|
,
|
||||||
|
\\pub fn foo() -> c_int {
|
||||||
|
\\ var x: c_int = 1234;
|
||||||
|
\\ var ptr: ?&c_int = &x;
|
||||||
|
\\ return *(??ptr);
|
||||||
|
\\}
|
||||||
|
);
|
||||||
|
|
||||||
|
cases.add("string literal",
|
||||||
|
\\const char *foo(void) {
|
||||||
|
\\ return "bar";
|
||||||
|
\\}
|
||||||
|
,
|
||||||
|
\\pub fn foo() -> ?&const u8 {
|
||||||
|
\\ return c"bar";
|
||||||
|
\\}
|
||||||
|
);
|
||||||
|
|
||||||
|
cases.add("return void",
|
||||||
|
\\void foo(void) {
|
||||||
|
\\ return;
|
||||||
|
\\}
|
||||||
|
,
|
||||||
|
\\pub fn foo() {
|
||||||
|
\\ return;
|
||||||
|
\\}
|
||||||
|
);
|
||||||
|
|
||||||
|
cases.add("for loop",
|
||||||
|
\\void foo(void) {
|
||||||
|
\\ for (int i = 0; i < 10; i += 1) { }
|
||||||
|
\\}
|
||||||
|
,
|
||||||
|
\\pub fn foo() {
|
||||||
|
\\ {
|
||||||
|
\\ var i: c_int = 0;
|
||||||
|
\\ while (i < 10) : (i += 1) {};
|
||||||
|
\\ };
|
||||||
|
\\}
|
||||||
|
);
|
||||||
|
|
||||||
|
cases.add("empty for loop",
|
||||||
|
\\void foo(void) {
|
||||||
|
\\ for (;;) { }
|
||||||
|
\\}
|
||||||
|
,
|
||||||
|
\\pub fn foo() {
|
||||||
|
\\ while (true) {};
|
||||||
|
\\}
|
||||||
|
);
|
||||||
|
|
||||||
|
cases.add("break statement",
|
||||||
|
\\void foo(void) {
|
||||||
|
\\ for (;;) {
|
||||||
|
\\ break;
|
||||||
|
\\ }
|
||||||
|
\\}
|
||||||
|
,
|
||||||
|
\\pub fn foo() {
|
||||||
|
\\ while (true) {
|
||||||
|
\\ break;
|
||||||
|
\\ };
|
||||||
|
\\}
|
||||||
|
);
|
||||||
|
|
||||||
|
cases.add("continue statement",
|
||||||
|
\\void foo(void) {
|
||||||
|
\\ for (;;) {
|
||||||
|
\\ continue;
|
||||||
|
\\ }
|
||||||
|
\\}
|
||||||
|
,
|
||||||
|
\\pub fn foo() {
|
||||||
|
\\ while (true) {
|
||||||
|
\\ continue;
|
||||||
|
\\ };
|
||||||
|
\\}
|
||||||
|
);
|
||||||
|
|
||||||
|
cases.add("switch statement",
|
||||||
|
\\int foo(int x) {
|
||||||
|
\\ switch (x) {
|
||||||
|
\\ case 1:
|
||||||
|
\\ x += 1;
|
||||||
|
\\ case 2:
|
||||||
|
\\ break;
|
||||||
|
\\ case 3:
|
||||||
|
\\ case 4:
|
||||||
|
\\ return x + 1;
|
||||||
|
\\ default:
|
||||||
|
\\ return 10;
|
||||||
|
\\ }
|
||||||
|
\\ return x + 13;
|
||||||
|
\\}
|
||||||
|
,
|
||||||
|
\\fn foo(_arg_x: c_int) -> c_int {
|
||||||
|
\\ var x = _arg_x;
|
||||||
|
\\ {
|
||||||
|
\\ switch (x) {
|
||||||
|
\\ 1 => goto case_0,
|
||||||
|
\\ 2 => goto case_1,
|
||||||
|
\\ 3 => goto case_2,
|
||||||
|
\\ 4 => goto case_3,
|
||||||
|
\\ else => goto default,
|
||||||
|
\\ };
|
||||||
|
\\ case_0:
|
||||||
|
\\ x += 1;
|
||||||
|
\\ case_1:
|
||||||
|
\\ goto end;
|
||||||
|
\\ case_2:
|
||||||
|
\\ case_3:
|
||||||
|
\\ return x + 1;
|
||||||
|
\\ default:
|
||||||
|
\\ return 10;
|
||||||
|
\\ goto end;
|
||||||
|
\\ end:
|
||||||
|
\\ };
|
||||||
|
\\ return x + 13;
|
||||||
|
\\}
|
||||||
|
);
|
||||||
|
|
||||||
|
cases.add("macros with field targets",
|
||||||
|
\\typedef unsigned int GLbitfield;
|
||||||
|
\\typedef void (*PFNGLCLEARPROC) (GLbitfield mask);
|
||||||
|
\\typedef void(*OpenGLProc)(void);
|
||||||
|
\\union OpenGLProcs {
|
||||||
|
\\ OpenGLProc ptr[1];
|
||||||
|
\\ struct {
|
||||||
|
\\ PFNGLCLEARPROC Clear;
|
||||||
|
\\ } gl;
|
||||||
|
\\};
|
||||||
|
\\extern union OpenGLProcs glProcs;
|
||||||
|
\\#define glClearUnion glProcs.gl.Clear
|
||||||
|
\\#define glClearPFN PFNGLCLEARPROC
|
||||||
|
,
|
||||||
|
\\pub const GLbitfield = c_uint;
|
||||||
|
,
|
||||||
|
\\pub const PFNGLCLEARPROC = ?extern fn(GLbitfield);
|
||||||
|
,
|
||||||
|
\\pub const OpenGLProc = ?extern fn();
|
||||||
|
,
|
||||||
|
\\pub const union_OpenGLProcs = extern union {
|
||||||
|
\\ ptr: [1]OpenGLProc,
|
||||||
|
\\ gl: extern struct {
|
||||||
|
\\ Clear: PFNGLCLEARPROC,
|
||||||
|
\\ },
|
||||||
|
\\};
|
||||||
|
,
|
||||||
|
\\pub extern var glProcs: union_OpenGLProcs;
|
||||||
|
,
|
||||||
|
\\pub const glClearPFN = PFNGLCLEARPROC;
|
||||||
|
,
|
||||||
|
\\pub inline fn glClearUnion(arg0: GLbitfield) {
|
||||||
|
\\ (??glProcs.gl.Clear)(arg0)
|
||||||
|
\\}
|
||||||
|
,
|
||||||
|
\\pub const OpenGLProcs = union_OpenGLProcs;
|
||||||
|
);
|
||||||
|
|
||||||
|
cases.add("switch statement with no default",
|
||||||
|
\\int foo(int x) {
|
||||||
|
\\ switch (x) {
|
||||||
|
\\ case 1:
|
||||||
|
\\ x += 1;
|
||||||
|
\\ case 2:
|
||||||
|
\\ break;
|
||||||
|
\\ case 3:
|
||||||
|
\\ case 4:
|
||||||
|
\\ return x + 1;
|
||||||
|
\\ }
|
||||||
|
\\ return x + 13;
|
||||||
|
\\}
|
||||||
|
,
|
||||||
|
\\fn foo(_arg_x: c_int) -> c_int {
|
||||||
|
\\ var x = _arg_x;
|
||||||
|
\\ {
|
||||||
|
\\ switch (x) {
|
||||||
|
\\ 1 => goto case_0,
|
||||||
|
\\ 2 => goto case_1,
|
||||||
|
\\ 3 => goto case_2,
|
||||||
|
\\ 4 => goto case_3,
|
||||||
|
\\ else => goto end,
|
||||||
|
\\ };
|
||||||
|
\\ case_0:
|
||||||
|
\\ x += 1;
|
||||||
|
\\ case_1:
|
||||||
|
\\ goto end;
|
||||||
|
\\ case_2:
|
||||||
|
\\ case_3:
|
||||||
|
\\ return x + 1;
|
||||||
|
\\ goto end;
|
||||||
|
\\ end:
|
||||||
|
\\ };
|
||||||
|
\\ return x + 13;
|
||||||
|
\\}
|
||||||
|
);
|
||||||
|
|
||||||
|
cases.add("variable name shadowing",
|
||||||
|
\\int foo(void) {
|
||||||
|
\\ int x = 1;
|
||||||
|
\\ {
|
||||||
|
\\ int x = 2;
|
||||||
|
\\ x += 1;
|
||||||
|
\\ }
|
||||||
|
\\ return x;
|
||||||
|
\\}
|
||||||
|
,
|
||||||
|
\\pub fn foo() -> c_int {
|
||||||
|
\\ var x: c_int = 1;
|
||||||
|
\\ {
|
||||||
|
\\ var x_0: c_int = 2;
|
||||||
|
\\ x_0 += 1;
|
||||||
|
\\ };
|
||||||
|
\\ return x;
|
||||||
|
\\}
|
||||||
|
);
|
||||||
|
|
||||||
|
cases.add("pointer casting",
|
||||||
|
\\float *ptrcast(int *a) {
|
||||||
|
\\ return (float *)a;
|
||||||
|
\\}
|
||||||
|
,
|
||||||
|
\\fn ptrcast(a: ?&c_int) -> ?&f32 {
|
||||||
|
\\ return @ptrCast(?&f32, a);
|
||||||
|
\\}
|
||||||
|
);
|
||||||
|
|
||||||
|
cases.add("bin not",
|
||||||
|
\\int foo(int x) {
|
||||||
|
\\ return ~x;
|
||||||
|
\\}
|
||||||
|
,
|
||||||
|
\\pub fn foo(x: c_int) -> c_int {
|
||||||
|
\\ return ~x;
|
||||||
|
\\}
|
||||||
|
);
|
||||||
|
|
||||||
|
cases.add("primitive types included in defined symbols",
|
||||||
|
\\int foo(int u32) {
|
||||||
|
\\ return u32;
|
||||||
|
\\}
|
||||||
|
,
|
||||||
|
\\pub fn foo(u32_0: c_int) -> c_int {
|
||||||
|
\\ return u32_0;
|
||||||
|
\\}
|
||||||
|
);
|
||||||
|
|
||||||
|
cases.add("const ptr initializer",
|
||||||
|
\\static const char *v0 = "0.0.0";
|
||||||
|
,
|
||||||
|
\\pub var v0: ?&const u8 = c"0.0.0";
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
//float *ptrcast(int *a) {
|
|
||||||
// return (float *)a;
|
|
||||||
//}
|
|
||||||
// should translate to
|
|
||||||
// fn ptrcast(a: ?&c_int) -> ?&f32 {
|
|
||||||
// return @ptrCast(?&f32, a);
|
|
||||||
// }
|
|
Loading…
Reference in New Issue