Merge remote-tracking branch 'upstream/master' into arm-support-improvement
This commit is contained in:
@@ -55,7 +55,7 @@ brew install cmake llvm@8
|
||||
brew outdated llvm@8 || brew upgrade llvm@8
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_PREFIX_PATH=/usr/local/Cellar/llvm/8.0.0_1
|
||||
cmake .. -DCMAKE_PREFIX_PATH=$(brew --prefix llvm)
|
||||
make install
|
||||
```
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ struct IrInstruction;
|
||||
struct IrInstructionCast;
|
||||
struct IrInstructionAllocaGen;
|
||||
struct IrInstructionCallGen;
|
||||
struct IrInstructionAwaitGen;
|
||||
struct IrBasicBlock;
|
||||
struct ScopeDecls;
|
||||
struct ZigWindowsSDK;
|
||||
@@ -308,10 +309,12 @@ struct ConstGlobalRefs {
|
||||
enum LazyValueId {
|
||||
LazyValueIdInvalid,
|
||||
LazyValueIdAlignOf,
|
||||
LazyValueIdSizeOf,
|
||||
LazyValueIdPtrType,
|
||||
LazyValueIdOptType,
|
||||
LazyValueIdSliceType,
|
||||
LazyValueIdFnType,
|
||||
LazyValueIdErrUnionType,
|
||||
};
|
||||
|
||||
struct LazyValue {
|
||||
@@ -325,6 +328,13 @@ struct LazyValueAlignOf {
|
||||
IrInstruction *target_type;
|
||||
};
|
||||
|
||||
struct LazyValueSizeOf {
|
||||
LazyValue base;
|
||||
|
||||
IrAnalyze *ira;
|
||||
IrInstruction *target_type;
|
||||
};
|
||||
|
||||
struct LazyValueSliceType {
|
||||
LazyValue base;
|
||||
|
||||
@@ -372,6 +382,14 @@ struct LazyValueFnType {
|
||||
bool is_generic;
|
||||
};
|
||||
|
||||
struct LazyValueErrUnionType {
|
||||
LazyValue base;
|
||||
|
||||
IrAnalyze *ira;
|
||||
IrInstruction *err_set_type;
|
||||
IrInstruction *payload_type;
|
||||
};
|
||||
|
||||
struct ConstExprValue {
|
||||
ZigType *type;
|
||||
ConstValSpecial special;
|
||||
@@ -1205,6 +1223,7 @@ struct ZigTypeStruct {
|
||||
HashMap<Buf *, TypeStructField *, buf_hash, buf_eql_buf> fields_by_name;
|
||||
RootStruct *root_struct;
|
||||
uint32_t *host_int_bytes; // available for packed structs, indexed by gen_index
|
||||
size_t llvm_full_type_queue_index;
|
||||
|
||||
uint32_t src_field_count;
|
||||
uint32_t gen_field_count;
|
||||
@@ -1468,6 +1487,7 @@ struct ZigFn {
|
||||
AstNode **param_source_nodes;
|
||||
Buf **param_names;
|
||||
IrInstruction *err_code_spill;
|
||||
AstNode *assumed_non_async;
|
||||
|
||||
AstNode *fn_no_inline_set_node;
|
||||
AstNode *fn_static_eval_set_node;
|
||||
@@ -1485,6 +1505,7 @@ struct ZigFn {
|
||||
|
||||
ZigList<GlobalExport> export_list;
|
||||
ZigList<IrInstructionCallGen *> call_list;
|
||||
ZigList<IrInstructionAwaitGen *> await_list;
|
||||
|
||||
LLVMValueRef valgrind_client_request_array;
|
||||
|
||||
@@ -1852,6 +1873,7 @@ struct CodeGen {
|
||||
ZigList<ErrorTableEntry *> errors_by_index;
|
||||
ZigList<CacheHash *> caches_to_release;
|
||||
size_t largest_err_name_len;
|
||||
ZigList<ZigType *> type_resolve_stack;
|
||||
|
||||
ZigPackage *std_package;
|
||||
ZigPackage *panic_package;
|
||||
@@ -3698,6 +3720,7 @@ struct IrInstructionAwaitGen {
|
||||
|
||||
IrInstruction *frame;
|
||||
IrInstruction *result_loc;
|
||||
ZigFn *target_fn;
|
||||
};
|
||||
|
||||
struct IrInstructionResume {
|
||||
|
||||
237
src/analyze.cpp
237
src/analyze.cpp
@@ -31,6 +31,7 @@ static void analyze_fn_body(CodeGen *g, ZigFn *fn_table_entry);
|
||||
static void resolve_llvm_types(CodeGen *g, ZigType *type, ResolveStatus wanted_resolve_status);
|
||||
static void preview_use_decl(CodeGen *g, TldUsingNamespace *using_namespace, ScopeDecls *dest_decls_scope);
|
||||
static void resolve_use_decl(CodeGen *g, TldUsingNamespace *tld_using_namespace, ScopeDecls *dest_decls_scope);
|
||||
static void analyze_fn_async(CodeGen *g, ZigFn *fn, bool resolve_frame);
|
||||
|
||||
// nullptr means not analyzed yet; this one means currently being analyzed
|
||||
static const AstNode *inferred_async_checking = reinterpret_cast<AstNode *>(0x1);
|
||||
@@ -973,7 +974,7 @@ ConstExprValue *analyze_const_value(CodeGen *g, Scope *scope, AstNode *node, Zig
|
||||
nullptr, nullptr, node, type_name, nullptr, nullptr, undef);
|
||||
}
|
||||
|
||||
static Error type_val_resolve_zero_bits(CodeGen *g, ConstExprValue *type_val, ZigType *parent_type,
|
||||
Error type_val_resolve_zero_bits(CodeGen *g, ConstExprValue *type_val, ZigType *parent_type,
|
||||
ConstExprValue *parent_type_val, bool *is_zero_bits)
|
||||
{
|
||||
Error err;
|
||||
@@ -997,6 +998,7 @@ static Error type_val_resolve_zero_bits(CodeGen *g, ConstExprValue *type_val, Zi
|
||||
switch (type_val->data.x_lazy->id) {
|
||||
case LazyValueIdInvalid:
|
||||
case LazyValueIdAlignOf:
|
||||
case LazyValueIdSizeOf:
|
||||
zig_unreachable();
|
||||
case LazyValueIdPtrType: {
|
||||
LazyValuePtrType *lazy_ptr_type = reinterpret_cast<LazyValuePtrType *>(type_val->data.x_lazy);
|
||||
@@ -1015,6 +1017,7 @@ static Error type_val_resolve_zero_bits(CodeGen *g, ConstExprValue *type_val, Zi
|
||||
}
|
||||
case LazyValueIdOptType:
|
||||
case LazyValueIdSliceType:
|
||||
case LazyValueIdErrUnionType:
|
||||
*is_zero_bits = false;
|
||||
return ErrorNone;
|
||||
case LazyValueIdFnType: {
|
||||
@@ -1035,11 +1038,13 @@ Error type_val_resolve_is_opaque_type(CodeGen *g, ConstExprValue *type_val, bool
|
||||
switch (type_val->data.x_lazy->id) {
|
||||
case LazyValueIdInvalid:
|
||||
case LazyValueIdAlignOf:
|
||||
case LazyValueIdSizeOf:
|
||||
zig_unreachable();
|
||||
case LazyValueIdSliceType:
|
||||
case LazyValueIdPtrType:
|
||||
case LazyValueIdFnType:
|
||||
case LazyValueIdOptType:
|
||||
case LazyValueIdErrUnionType:
|
||||
*is_opaque_type = false;
|
||||
return ErrorNone;
|
||||
}
|
||||
@@ -1053,6 +1058,7 @@ static ReqCompTime type_val_resolve_requires_comptime(CodeGen *g, ConstExprValue
|
||||
switch (type_val->data.x_lazy->id) {
|
||||
case LazyValueIdInvalid:
|
||||
case LazyValueIdAlignOf:
|
||||
case LazyValueIdSizeOf:
|
||||
zig_unreachable();
|
||||
case LazyValueIdSliceType: {
|
||||
LazyValueSliceType *lazy_slice_type = reinterpret_cast<LazyValueSliceType *>(type_val->data.x_lazy);
|
||||
@@ -1094,18 +1100,21 @@ static ReqCompTime type_val_resolve_requires_comptime(CodeGen *g, ConstExprValue
|
||||
}
|
||||
return ReqCompTimeNo;
|
||||
}
|
||||
case LazyValueIdErrUnionType: {
|
||||
LazyValueErrUnionType *lazy_err_union_type =
|
||||
reinterpret_cast<LazyValueErrUnionType *>(type_val->data.x_lazy);
|
||||
return type_val_resolve_requires_comptime(g, &lazy_err_union_type->payload_type->value);
|
||||
}
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
static Error type_val_resolve_abi_size(CodeGen *g, AstNode *source_node, ConstExprValue *type_val,
|
||||
Error type_val_resolve_abi_size(CodeGen *g, AstNode *source_node, ConstExprValue *type_val,
|
||||
size_t *abi_size, size_t *size_in_bits)
|
||||
{
|
||||
Error err;
|
||||
if (type_val->data.x_lazy->id == LazyValueIdOptType) {
|
||||
if ((err = ir_resolve_lazy(g, source_node, type_val)))
|
||||
return err;
|
||||
}
|
||||
|
||||
start_over:
|
||||
if (type_val->special != ConstValSpecialLazy) {
|
||||
assert(type_val->special == ConstValSpecialStatic);
|
||||
ZigType *ty = type_val->data.x_type;
|
||||
@@ -1118,18 +1127,51 @@ static Error type_val_resolve_abi_size(CodeGen *g, AstNode *source_node, ConstEx
|
||||
switch (type_val->data.x_lazy->id) {
|
||||
case LazyValueIdInvalid:
|
||||
case LazyValueIdAlignOf:
|
||||
case LazyValueIdSizeOf:
|
||||
zig_unreachable();
|
||||
case LazyValueIdSliceType:
|
||||
*abi_size = g->builtin_types.entry_usize->abi_size * 2;
|
||||
*size_in_bits = g->builtin_types.entry_usize->size_in_bits * 2;
|
||||
case LazyValueIdSliceType: {
|
||||
LazyValueSliceType *lazy_slice_type = reinterpret_cast<LazyValueSliceType *>(type_val->data.x_lazy);
|
||||
bool is_zero_bits;
|
||||
if ((err = type_val_resolve_zero_bits(g, &lazy_slice_type->elem_type->value, nullptr,
|
||||
nullptr, &is_zero_bits)))
|
||||
{
|
||||
return err;
|
||||
}
|
||||
if (is_zero_bits) {
|
||||
*abi_size = g->builtin_types.entry_usize->abi_size;
|
||||
*size_in_bits = g->builtin_types.entry_usize->size_in_bits;
|
||||
} else {
|
||||
*abi_size = g->builtin_types.entry_usize->abi_size * 2;
|
||||
*size_in_bits = g->builtin_types.entry_usize->size_in_bits * 2;
|
||||
}
|
||||
return ErrorNone;
|
||||
case LazyValueIdPtrType:
|
||||
}
|
||||
case LazyValueIdPtrType: {
|
||||
LazyValuePtrType *lazy_ptr_type = reinterpret_cast<LazyValuePtrType *>(type_val->data.x_lazy);
|
||||
bool is_zero_bits;
|
||||
if ((err = type_val_resolve_zero_bits(g, &lazy_ptr_type->elem_type->value, nullptr,
|
||||
nullptr, &is_zero_bits)))
|
||||
{
|
||||
return err;
|
||||
}
|
||||
if (is_zero_bits) {
|
||||
*abi_size = 0;
|
||||
*size_in_bits = 0;
|
||||
} else {
|
||||
*abi_size = g->builtin_types.entry_usize->abi_size;
|
||||
*size_in_bits = g->builtin_types.entry_usize->size_in_bits;
|
||||
}
|
||||
return ErrorNone;
|
||||
}
|
||||
case LazyValueIdFnType:
|
||||
*abi_size = g->builtin_types.entry_usize->abi_size;
|
||||
*size_in_bits = g->builtin_types.entry_usize->size_in_bits;
|
||||
return ErrorNone;
|
||||
case LazyValueIdOptType:
|
||||
zig_unreachable();
|
||||
case LazyValueIdErrUnionType:
|
||||
if ((err = ir_resolve_lazy(g, source_node, type_val)))
|
||||
return err;
|
||||
goto start_over;
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
@@ -1151,6 +1193,7 @@ Error type_val_resolve_abi_align(CodeGen *g, ConstExprValue *type_val, uint32_t
|
||||
switch (type_val->data.x_lazy->id) {
|
||||
case LazyValueIdInvalid:
|
||||
case LazyValueIdAlignOf:
|
||||
case LazyValueIdSizeOf:
|
||||
zig_unreachable();
|
||||
case LazyValueIdSliceType:
|
||||
case LazyValueIdPtrType:
|
||||
@@ -1161,6 +1204,19 @@ Error type_val_resolve_abi_align(CodeGen *g, ConstExprValue *type_val, uint32_t
|
||||
LazyValueOptType *lazy_opt_type = reinterpret_cast<LazyValueOptType *>(type_val->data.x_lazy);
|
||||
return type_val_resolve_abi_align(g, &lazy_opt_type->payload_type->value, abi_align);
|
||||
}
|
||||
case LazyValueIdErrUnionType: {
|
||||
LazyValueErrUnionType *lazy_err_union_type =
|
||||
reinterpret_cast<LazyValueErrUnionType *>(type_val->data.x_lazy);
|
||||
uint32_t payload_abi_align;
|
||||
if ((err = type_val_resolve_abi_align(g, &lazy_err_union_type->payload_type->value,
|
||||
&payload_abi_align)))
|
||||
{
|
||||
return err;
|
||||
}
|
||||
*abi_align = (payload_abi_align > g->err_tag_type->abi_align) ?
|
||||
payload_abi_align : g->err_tag_type->abi_align;
|
||||
return ErrorNone;
|
||||
}
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
@@ -1172,6 +1228,7 @@ static OnePossibleValue type_val_resolve_has_one_possible_value(CodeGen *g, Cons
|
||||
switch (type_val->data.x_lazy->id) {
|
||||
case LazyValueIdInvalid:
|
||||
case LazyValueIdAlignOf:
|
||||
case LazyValueIdSizeOf:
|
||||
zig_unreachable();
|
||||
case LazyValueIdSliceType: // it has the len field
|
||||
case LazyValueIdOptType: // it has the optional bit
|
||||
@@ -1189,6 +1246,18 @@ static OnePossibleValue type_val_resolve_has_one_possible_value(CodeGen *g, Cons
|
||||
return OnePossibleValueNo;
|
||||
}
|
||||
}
|
||||
case LazyValueIdErrUnionType: {
|
||||
LazyValueErrUnionType *lazy_err_union_type =
|
||||
reinterpret_cast<LazyValueErrUnionType *>(type_val->data.x_lazy);
|
||||
switch (type_val_resolve_has_one_possible_value(g, &lazy_err_union_type->err_set_type->value)) {
|
||||
case OnePossibleValueInvalid:
|
||||
return OnePossibleValueInvalid;
|
||||
case OnePossibleValueNo:
|
||||
return OnePossibleValueNo;
|
||||
case OnePossibleValueYes:
|
||||
return type_val_resolve_has_one_possible_value(g, &lazy_err_union_type->payload_type->value);
|
||||
}
|
||||
}
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
@@ -4105,8 +4174,14 @@ static void add_async_error_notes(CodeGen *g, ErrorMsg *msg, ZigFn *fn) {
|
||||
assert(fn->inferred_async_node != inferred_async_checking);
|
||||
assert(fn->inferred_async_node != inferred_async_none);
|
||||
if (fn->inferred_async_fn != nullptr) {
|
||||
ErrorMsg *new_msg = add_error_note(g, msg, fn->inferred_async_node,
|
||||
buf_sprintf("async function call here"));
|
||||
ErrorMsg *new_msg;
|
||||
if (fn->inferred_async_node->type == NodeTypeAwaitExpr) {
|
||||
new_msg = add_error_note(g, msg, fn->inferred_async_node,
|
||||
buf_create_from_str("await here is a suspend point"));
|
||||
} else {
|
||||
new_msg = add_error_note(g, msg, fn->inferred_async_node,
|
||||
buf_sprintf("async function call here"));
|
||||
}
|
||||
return add_async_error_notes(g, new_msg, fn->inferred_async_fn);
|
||||
} else if (fn->inferred_async_node->type == NodeTypeFnProto) {
|
||||
add_error_note(g, msg, fn->inferred_async_node,
|
||||
@@ -4116,7 +4191,7 @@ static void add_async_error_notes(CodeGen *g, ErrorMsg *msg, ZigFn *fn) {
|
||||
buf_sprintf("suspends here"));
|
||||
} else if (fn->inferred_async_node->type == NodeTypeAwaitExpr) {
|
||||
add_error_note(g, msg, fn->inferred_async_node,
|
||||
buf_sprintf("await is a suspend point"));
|
||||
buf_sprintf("await here is a suspend point"));
|
||||
} else if (fn->inferred_async_node->type == NodeTypeFnCallExpr &&
|
||||
fn->inferred_async_node->data.fn_call_expr.is_builtin)
|
||||
{
|
||||
@@ -4128,6 +4203,64 @@ static void add_async_error_notes(CodeGen *g, ErrorMsg *msg, ZigFn *fn) {
|
||||
}
|
||||
}
|
||||
|
||||
// ErrorNone - not async
|
||||
// ErrorIsAsync - yes async
|
||||
// ErrorSemanticAnalyzeFail - compile error emitted result is invalid
|
||||
static Error analyze_callee_async(CodeGen *g, ZigFn *fn, ZigFn *callee, AstNode *call_node,
|
||||
bool must_not_be_async)
|
||||
{
|
||||
if (callee->type_entry->data.fn.fn_type_id.cc != CallingConventionUnspecified)
|
||||
return ErrorNone;
|
||||
if (callee->anal_state == FnAnalStateReady) {
|
||||
analyze_fn_body(g, callee);
|
||||
if (callee->anal_state == FnAnalStateInvalid) {
|
||||
return ErrorSemanticAnalyzeFail;
|
||||
}
|
||||
}
|
||||
bool callee_is_async;
|
||||
if (callee->anal_state == FnAnalStateComplete) {
|
||||
analyze_fn_async(g, callee, true);
|
||||
if (callee->anal_state == FnAnalStateInvalid) {
|
||||
return ErrorSemanticAnalyzeFail;
|
||||
}
|
||||
callee_is_async = fn_is_async(callee);
|
||||
} else {
|
||||
// If it's already been determined, use that value. Otherwise
|
||||
// assume non-async, emit an error later if it turned out to be async.
|
||||
if (callee->inferred_async_node == nullptr ||
|
||||
callee->inferred_async_node == inferred_async_checking)
|
||||
{
|
||||
callee->assumed_non_async = call_node;
|
||||
callee_is_async = false;
|
||||
} else {
|
||||
callee_is_async = callee->inferred_async_node != inferred_async_none;
|
||||
}
|
||||
}
|
||||
if (callee_is_async) {
|
||||
fn->inferred_async_node = call_node;
|
||||
fn->inferred_async_fn = callee;
|
||||
if (must_not_be_async) {
|
||||
ErrorMsg *msg = add_node_error(g, fn->proto_node,
|
||||
buf_sprintf("function with calling convention '%s' cannot be async",
|
||||
calling_convention_name(fn->type_entry->data.fn.fn_type_id.cc)));
|
||||
add_async_error_notes(g, msg, fn);
|
||||
return ErrorSemanticAnalyzeFail;
|
||||
}
|
||||
if (fn->assumed_non_async != nullptr) {
|
||||
ErrorMsg *msg = add_node_error(g, fn->proto_node,
|
||||
buf_sprintf("unable to infer whether '%s' should be async",
|
||||
buf_ptr(&fn->symbol_name)));
|
||||
add_error_note(g, msg, fn->assumed_non_async,
|
||||
buf_sprintf("assumed to be non-async here"));
|
||||
add_async_error_notes(g, msg, fn);
|
||||
fn->anal_state = FnAnalStateInvalid;
|
||||
return ErrorSemanticAnalyzeFail;
|
||||
}
|
||||
return ErrorIsAsync;
|
||||
}
|
||||
return ErrorNone;
|
||||
}
|
||||
|
||||
// This function resolves functions being inferred async.
|
||||
static void analyze_fn_async(CodeGen *g, ZigFn *fn, bool resolve_frame) {
|
||||
if (fn->inferred_async_node == inferred_async_checking) {
|
||||
@@ -4154,42 +4287,40 @@ static void analyze_fn_async(CodeGen *g, ZigFn *fn, bool resolve_frame) {
|
||||
|
||||
for (size_t i = 0; i < fn->call_list.length; i += 1) {
|
||||
IrInstructionCallGen *call = fn->call_list.at(i);
|
||||
ZigFn *callee = call->fn_entry;
|
||||
if (callee == nullptr) {
|
||||
if (call->fn_entry == nullptr) {
|
||||
// TODO function pointer call here, could be anything
|
||||
continue;
|
||||
}
|
||||
|
||||
if (callee->type_entry->data.fn.fn_type_id.cc != CallingConventionUnspecified)
|
||||
continue;
|
||||
if (callee->anal_state == FnAnalStateReady) {
|
||||
analyze_fn_body(g, callee);
|
||||
if (callee->anal_state == FnAnalStateInvalid) {
|
||||
switch (analyze_callee_async(g, fn, call->fn_entry, call->base.source_node, must_not_be_async)) {
|
||||
case ErrorSemanticAnalyzeFail:
|
||||
fn->anal_state = FnAnalStateInvalid;
|
||||
return;
|
||||
}
|
||||
case ErrorNone:
|
||||
continue;
|
||||
case ErrorIsAsync:
|
||||
if (resolve_frame) {
|
||||
resolve_async_fn_frame(g, fn);
|
||||
}
|
||||
return;
|
||||
default:
|
||||
zig_unreachable();
|
||||
}
|
||||
assert(callee->anal_state == FnAnalStateComplete);
|
||||
analyze_fn_async(g, callee, true);
|
||||
if (callee->anal_state == FnAnalStateInvalid) {
|
||||
fn->anal_state = FnAnalStateInvalid;
|
||||
return;
|
||||
}
|
||||
if (fn_is_async(callee)) {
|
||||
fn->inferred_async_node = call->base.source_node;
|
||||
fn->inferred_async_fn = callee;
|
||||
if (must_not_be_async) {
|
||||
ErrorMsg *msg = add_node_error(g, fn->proto_node,
|
||||
buf_sprintf("function with calling convention '%s' cannot be async",
|
||||
calling_convention_name(fn->type_entry->data.fn.fn_type_id.cc)));
|
||||
add_async_error_notes(g, msg, fn);
|
||||
}
|
||||
for (size_t i = 0; i < fn->await_list.length; i += 1) {
|
||||
IrInstructionAwaitGen *await = fn->await_list.at(i);
|
||||
switch (analyze_callee_async(g, fn, await->target_fn, await->base.source_node, must_not_be_async)) {
|
||||
case ErrorSemanticAnalyzeFail:
|
||||
fn->anal_state = FnAnalStateInvalid;
|
||||
return;
|
||||
}
|
||||
if (resolve_frame) {
|
||||
resolve_async_fn_frame(g, fn);
|
||||
}
|
||||
return;
|
||||
case ErrorNone:
|
||||
continue;
|
||||
case ErrorIsAsync:
|
||||
if (resolve_frame) {
|
||||
resolve_async_fn_frame(g, fn);
|
||||
}
|
||||
return;
|
||||
default:
|
||||
zig_unreachable();
|
||||
}
|
||||
}
|
||||
fn->inferred_async_node = inferred_async_none;
|
||||
@@ -4447,6 +4578,8 @@ void semantic_analyze(CodeGen *g) {
|
||||
ZigFn *fn = g->fn_defs.at(g->fn_defs_index);
|
||||
g->trace_err = nullptr;
|
||||
analyze_fn_async(g, fn, true);
|
||||
if (fn->anal_state == FnAnalStateInvalid)
|
||||
continue;
|
||||
if (fn_is_async(fn) && fn->non_async_node != nullptr) {
|
||||
ErrorMsg *msg = add_node_error(g, fn->proto_node,
|
||||
buf_sprintf("'%s' cannot be async", buf_ptr(&fn->symbol_name)));
|
||||
@@ -5599,6 +5732,11 @@ static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) {
|
||||
return ErrorSemanticAnalyzeFail;
|
||||
}
|
||||
analyze_fn_async(g, callee, true);
|
||||
if (callee->inferred_async_node == inferred_async_checking) {
|
||||
assert(g->errors.length != 0);
|
||||
frame_type->data.frame.locals_struct = g->builtin_types.entry_invalid;
|
||||
return ErrorSemanticAnalyzeFail;
|
||||
}
|
||||
if (!fn_is_async(callee))
|
||||
continue;
|
||||
|
||||
@@ -7238,7 +7376,13 @@ static void resolve_llvm_types_struct(CodeGen *g, ZigType *struct_type, ResolveS
|
||||
di_scope, di_file, line);
|
||||
|
||||
struct_type->data.structure.resolve_status = ResolveStatusLLVMFwdDecl;
|
||||
if (ResolveStatusLLVMFwdDecl >= wanted_resolve_status) return;
|
||||
if (ResolveStatusLLVMFwdDecl >= wanted_resolve_status) {
|
||||
struct_type->data.structure.llvm_full_type_queue_index = g->type_resolve_stack.length;
|
||||
g->type_resolve_stack.append(struct_type);
|
||||
return;
|
||||
} else {
|
||||
struct_type->data.structure.llvm_full_type_queue_index = SIZE_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
size_t field_count = struct_type->data.structure.src_field_count;
|
||||
@@ -7442,6 +7586,13 @@ static void resolve_llvm_types_struct(CodeGen *g, ZigType *struct_type, ResolveS
|
||||
ZigLLVMReplaceTemporary(g->dbuilder, struct_type->llvm_di_type, replacement_di_type);
|
||||
struct_type->llvm_di_type = replacement_di_type;
|
||||
struct_type->data.structure.resolve_status = ResolveStatusLLVMFull;
|
||||
if (struct_type->data.structure.llvm_full_type_queue_index != SIZE_MAX) {
|
||||
ZigType *last = g->type_resolve_stack.last();
|
||||
assert(last->id == ZigTypeIdStruct);
|
||||
last->data.structure.llvm_full_type_queue_index = struct_type->data.structure.llvm_full_type_queue_index;
|
||||
g->type_resolve_stack.swap_remove(struct_type->data.structure.llvm_full_type_queue_index);
|
||||
struct_type->data.structure.llvm_full_type_queue_index = SIZE_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
static void resolve_llvm_types_enum(CodeGen *g, ZigType *enum_type, ResolveStatus wanted_resolve_status) {
|
||||
|
||||
@@ -247,6 +247,10 @@ void resolve_llvm_types_fn(CodeGen *g, ZigFn *fn);
|
||||
bool fn_is_async(ZigFn *fn);
|
||||
|
||||
Error type_val_resolve_abi_align(CodeGen *g, ConstExprValue *type_val, uint32_t *abi_align);
|
||||
Error type_val_resolve_abi_size(CodeGen *g, AstNode *source_node, ConstExprValue *type_val,
|
||||
size_t *abi_size, size_t *size_in_bits);
|
||||
Error type_val_resolve_zero_bits(CodeGen *g, ConstExprValue *type_val, ZigType *parent_type,
|
||||
ConstExprValue *parent_type_val, bool *is_zero_bits);
|
||||
ZigType *resolve_union_field_type(CodeGen *g, TypeUnionField *union_field);
|
||||
ZigType *resolve_struct_field_type(CodeGen *g, TypeStructField *struct_field);
|
||||
|
||||
|
||||
117
src/codegen.cpp
117
src/codegen.cpp
@@ -3052,8 +3052,10 @@ static LLVMValueRef ir_render_ptr_of_array_to_slice(CodeGen *g, IrExecutable *ex
|
||||
IrInstructionPtrOfArrayToSlice *instruction)
|
||||
{
|
||||
ZigType *actual_type = instruction->operand->value.type;
|
||||
LLVMValueRef expr_val = ir_llvm_value(g, instruction->operand);
|
||||
assert(expr_val);
|
||||
ZigType *slice_type = instruction->base.value.type;
|
||||
ZigType *slice_ptr_type = slice_type->data.structure.fields[slice_ptr_index].type_entry;
|
||||
size_t ptr_index = slice_type->data.structure.fields[slice_ptr_index].gen_index;
|
||||
size_t len_index = slice_type->data.structure.fields[slice_len_index].gen_index;
|
||||
|
||||
LLVMValueRef result_loc = ir_llvm_value(g, instruction->result_loc);
|
||||
|
||||
@@ -3061,15 +3063,21 @@ static LLVMValueRef ir_render_ptr_of_array_to_slice(CodeGen *g, IrExecutable *ex
|
||||
ZigType *array_type = actual_type->data.pointer.child_type;
|
||||
assert(array_type->id == ZigTypeIdArray);
|
||||
|
||||
LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, result_loc, slice_ptr_index, "");
|
||||
LLVMValueRef indices[] = {
|
||||
LLVMConstNull(g->builtin_types.entry_usize->llvm_type),
|
||||
LLVMConstInt(g->builtin_types.entry_usize->llvm_type, 0, false),
|
||||
};
|
||||
LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, expr_val, indices, 2, "");
|
||||
gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false);
|
||||
if (type_has_bits(actual_type)) {
|
||||
LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, result_loc, ptr_index, "");
|
||||
LLVMValueRef indices[] = {
|
||||
LLVMConstNull(g->builtin_types.entry_usize->llvm_type),
|
||||
LLVMConstInt(g->builtin_types.entry_usize->llvm_type, 0, false),
|
||||
};
|
||||
LLVMValueRef expr_val = ir_llvm_value(g, instruction->operand);
|
||||
LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, expr_val, indices, 2, "");
|
||||
gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false);
|
||||
} else if (ir_want_runtime_safety(g, &instruction->base)) {
|
||||
LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, result_loc, ptr_index, "");
|
||||
gen_undef_init(g, slice_ptr_type->abi_align, slice_ptr_type, ptr_field_ptr);
|
||||
}
|
||||
|
||||
LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, result_loc, slice_len_index, "");
|
||||
LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, result_loc, len_index, "");
|
||||
LLVMValueRef len_value = LLVMConstInt(g->builtin_types.entry_usize->llvm_type,
|
||||
array_type->data.array.len, false);
|
||||
gen_store_untyped(g, len_value, len_field_ptr, 0, false);
|
||||
@@ -3916,7 +3924,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
|
||||
LLVMBuildStore(g->builder, awaiter_init_val, awaiter_ptr);
|
||||
|
||||
if (ret_has_bits) {
|
||||
LLVMValueRef ret_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, frame_ret_start + 2, "");
|
||||
ret_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, frame_ret_start + 2, "");
|
||||
LLVMValueRef ret_ptr_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, frame_ret_start, "");
|
||||
LLVMBuildStore(g->builder, ret_ptr, ret_ptr_ptr);
|
||||
|
||||
@@ -4059,6 +4067,9 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
|
||||
LLVMValueRef store_instr = LLVMBuildStore(g->builder, result, result_loc);
|
||||
LLVMSetAlignment(store_instr, get_ptr_align(g, instruction->result_loc->value.type));
|
||||
return result_loc;
|
||||
} else if (!callee_is_async && instruction->is_async) {
|
||||
LLVMBuildStore(g->builder, result, ret_ptr);
|
||||
return result_loc;
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
@@ -5490,6 +5501,44 @@ static LLVMValueRef ir_render_suspend_finish(CodeGen *g, IrExecutable *executabl
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static LLVMValueRef gen_await_early_return(CodeGen *g, IrInstruction *source_instr,
|
||||
LLVMValueRef target_frame_ptr, ZigType *result_type, ZigType *ptr_result_type,
|
||||
LLVMValueRef result_loc, bool non_async)
|
||||
{
|
||||
LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type;
|
||||
LLVMValueRef their_result_ptr = nullptr;
|
||||
if (type_has_bits(result_type) && (non_async || result_loc != nullptr)) {
|
||||
LLVMValueRef their_result_ptr_ptr = LLVMBuildStructGEP(g->builder, target_frame_ptr, frame_ret_start, "");
|
||||
their_result_ptr = LLVMBuildLoad(g->builder, their_result_ptr_ptr, "");
|
||||
if (result_loc != nullptr) {
|
||||
LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0);
|
||||
LLVMValueRef dest_ptr_casted = LLVMBuildBitCast(g->builder, result_loc, ptr_u8, "");
|
||||
LLVMValueRef src_ptr_casted = LLVMBuildBitCast(g->builder, their_result_ptr, ptr_u8, "");
|
||||
bool is_volatile = false;
|
||||
uint32_t abi_align = get_abi_alignment(g, result_type);
|
||||
LLVMValueRef byte_count_val = LLVMConstInt(usize_type_ref, type_size(g, result_type), false);
|
||||
ZigLLVMBuildMemCpy(g->builder,
|
||||
dest_ptr_casted, abi_align,
|
||||
src_ptr_casted, abi_align, byte_count_val, is_volatile);
|
||||
}
|
||||
}
|
||||
if (codegen_fn_has_err_ret_tracing_arg(g, result_type)) {
|
||||
LLVMValueRef their_trace_ptr_ptr = LLVMBuildStructGEP(g->builder, target_frame_ptr,
|
||||
frame_index_trace_arg(g, result_type), "");
|
||||
LLVMValueRef src_trace_ptr = LLVMBuildLoad(g->builder, their_trace_ptr_ptr, "");
|
||||
LLVMValueRef dest_trace_ptr = get_cur_err_ret_trace_val(g, source_instr->scope);
|
||||
LLVMValueRef args[] = { dest_trace_ptr, src_trace_ptr };
|
||||
ZigLLVMBuildCall(g->builder, get_merge_err_ret_traces_fn_val(g), args, 2,
|
||||
get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, "");
|
||||
}
|
||||
if (non_async && type_has_bits(result_type)) {
|
||||
LLVMValueRef result_ptr = (result_loc == nullptr) ? their_result_ptr : result_loc;
|
||||
return get_handle_value(g, result_ptr, result_type, ptr_result_type);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_render_await(CodeGen *g, IrExecutable *executable, IrInstructionAwaitGen *instruction) {
|
||||
LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type;
|
||||
LLVMValueRef zero = LLVMConstNull(usize_type_ref);
|
||||
@@ -5497,6 +5546,14 @@ static LLVMValueRef ir_render_await(CodeGen *g, IrExecutable *executable, IrInst
|
||||
ZigType *result_type = instruction->base.value.type;
|
||||
ZigType *ptr_result_type = get_pointer_to_type(g, result_type, true);
|
||||
|
||||
LLVMValueRef result_loc = (instruction->result_loc == nullptr) ?
|
||||
nullptr : ir_llvm_value(g, instruction->result_loc);
|
||||
|
||||
if (instruction->target_fn != nullptr && !fn_is_async(instruction->target_fn)) {
|
||||
return gen_await_early_return(g, &instruction->base, target_frame_ptr, result_type,
|
||||
ptr_result_type, result_loc, true);
|
||||
}
|
||||
|
||||
// Prepare to be suspended
|
||||
LLVMBasicBlockRef resume_bb = gen_suspend_begin(g, "AwaitResume");
|
||||
LLVMBasicBlockRef end_bb = LLVMAppendBasicBlock(g->cur_fn_val, "AwaitEnd");
|
||||
@@ -5504,9 +5561,8 @@ static LLVMValueRef ir_render_await(CodeGen *g, IrExecutable *executable, IrInst
|
||||
// At this point resuming the function will continue from resume_bb.
|
||||
// This code is as if it is running inside the suspend block.
|
||||
|
||||
|
||||
// supply the awaiter return pointer
|
||||
LLVMValueRef result_loc = (instruction->result_loc == nullptr) ?
|
||||
nullptr : ir_llvm_value(g, instruction->result_loc);
|
||||
if (type_has_bits(result_type)) {
|
||||
LLVMValueRef awaiter_ret_ptr_ptr = LLVMBuildStructGEP(g->builder, target_frame_ptr, frame_ret_start + 1, "");
|
||||
if (result_loc == nullptr) {
|
||||
@@ -5554,28 +5610,8 @@ static LLVMValueRef ir_render_await(CodeGen *g, IrExecutable *executable, IrInst
|
||||
// Early return: The async function has already completed. We must copy the result and
|
||||
// the error return trace if applicable.
|
||||
LLVMPositionBuilderAtEnd(g->builder, early_return_block);
|
||||
if (type_has_bits(result_type) && result_loc != nullptr) {
|
||||
LLVMValueRef their_result_ptr_ptr = LLVMBuildStructGEP(g->builder, target_frame_ptr, frame_ret_start, "");
|
||||
LLVMValueRef their_result_ptr = LLVMBuildLoad(g->builder, their_result_ptr_ptr, "");
|
||||
LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0);
|
||||
LLVMValueRef dest_ptr_casted = LLVMBuildBitCast(g->builder, result_loc, ptr_u8, "");
|
||||
LLVMValueRef src_ptr_casted = LLVMBuildBitCast(g->builder, their_result_ptr, ptr_u8, "");
|
||||
bool is_volatile = false;
|
||||
uint32_t abi_align = get_abi_alignment(g, result_type);
|
||||
LLVMValueRef byte_count_val = LLVMConstInt(usize_type_ref, type_size(g, result_type), false);
|
||||
ZigLLVMBuildMemCpy(g->builder,
|
||||
dest_ptr_casted, abi_align,
|
||||
src_ptr_casted, abi_align, byte_count_val, is_volatile);
|
||||
}
|
||||
if (codegen_fn_has_err_ret_tracing_arg(g, result_type)) {
|
||||
LLVMValueRef their_trace_ptr_ptr = LLVMBuildStructGEP(g->builder, target_frame_ptr,
|
||||
frame_index_trace_arg(g, result_type), "");
|
||||
LLVMValueRef src_trace_ptr = LLVMBuildLoad(g->builder, their_trace_ptr_ptr, "");
|
||||
LLVMValueRef dest_trace_ptr = get_cur_err_ret_trace_val(g, instruction->base.scope);
|
||||
LLVMValueRef args[] = { dest_trace_ptr, src_trace_ptr };
|
||||
ZigLLVMBuildCall(g->builder, get_merge_err_ret_traces_fn_val(g), args, 2,
|
||||
get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, "");
|
||||
}
|
||||
gen_await_early_return(g, &instruction->base, target_frame_ptr, result_type, ptr_result_type,
|
||||
result_loc, false);
|
||||
LLVMBuildBr(g->builder, end_bb);
|
||||
|
||||
LLVMPositionBuilderAtEnd(g->builder, resume_bb);
|
||||
@@ -6837,6 +6873,7 @@ static void set_global_tls(CodeGen *g, ZigVar *var, LLVMValueRef global_value) {
|
||||
}
|
||||
|
||||
static void do_code_gen(CodeGen *g) {
|
||||
Error err;
|
||||
assert(!g->errors.length);
|
||||
|
||||
generate_error_name_table(g);
|
||||
@@ -6850,6 +6887,8 @@ static void do_code_gen(CodeGen *g) {
|
||||
// Generate debug info for it but that's it.
|
||||
ConstExprValue *const_val = var->const_value;
|
||||
assert(const_val->special != ConstValSpecialRuntime);
|
||||
if ((err = ir_resolve_lazy(g, var->decl_node, const_val)))
|
||||
zig_unreachable();
|
||||
if (const_val->type != var->var_type) {
|
||||
zig_panic("TODO debug info for var with ptr casted value");
|
||||
}
|
||||
@@ -6867,6 +6906,8 @@ static void do_code_gen(CodeGen *g) {
|
||||
// Generate debug info for it but that's it.
|
||||
ConstExprValue *const_val = var->const_value;
|
||||
assert(const_val->special != ConstValSpecialRuntime);
|
||||
if ((err = ir_resolve_lazy(g, var->decl_node, const_val)))
|
||||
zig_unreachable();
|
||||
if (const_val->type != var->var_type) {
|
||||
zig_panic("TODO debug info for var with ptr casted value");
|
||||
}
|
||||
@@ -7200,6 +7241,12 @@ static void do_code_gen(CodeGen *g) {
|
||||
LLVMSetModuleInlineAsm(g->module, buf_ptr(&g->global_asm));
|
||||
}
|
||||
|
||||
while (g->type_resolve_stack.length != 0) {
|
||||
ZigType *ty = g->type_resolve_stack.last();
|
||||
if (type_resolve(g, ty, ResolveStatusLLVMFull))
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
ZigLLVMDIBuilderFinalize(g->dbuilder);
|
||||
|
||||
if (g->verbose_llvm_ir) {
|
||||
|
||||
@@ -55,6 +55,8 @@ const char *err_str(Error err) {
|
||||
case ErrorBrokenPipe: return "broken pipe";
|
||||
case ErrorNoSpaceLeft: return "no space left";
|
||||
case ErrorNoCCompilerInstalled: return "no C compiler installed";
|
||||
case ErrorNotLazy: return "not lazy";
|
||||
case ErrorIsAsync: return "is async";
|
||||
}
|
||||
return "(invalid error)";
|
||||
}
|
||||
|
||||
335
src/ir.cpp
335
src/ir.cpp
@@ -3268,7 +3268,7 @@ static IrInstruction *ir_build_await_src(IrBuilder *irb, Scope *scope, AstNode *
|
||||
return &instruction->base;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_await_gen(IrAnalyze *ira, IrInstruction *source_instruction,
|
||||
static IrInstructionAwaitGen *ir_build_await_gen(IrAnalyze *ira, IrInstruction *source_instruction,
|
||||
IrInstruction *frame, ZigType *result_type, IrInstruction *result_loc)
|
||||
{
|
||||
IrInstructionAwaitGen *instruction = ir_build_instruction<IrInstructionAwaitGen>(&ira->new_irb,
|
||||
@@ -3280,7 +3280,7 @@ static IrInstruction *ir_build_await_gen(IrAnalyze *ira, IrInstruction *source_i
|
||||
ir_ref_instruction(frame, ira->new_irb.current_basic_block);
|
||||
if (result_loc != nullptr) ir_ref_instruction(result_loc, ira->new_irb.current_basic_block);
|
||||
|
||||
return &instruction->base;
|
||||
return instruction;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_resume(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *frame) {
|
||||
@@ -10640,7 +10640,9 @@ static void ir_finish_bb(IrAnalyze *ira) {
|
||||
|
||||
static IrInstruction *ir_unreach_error(IrAnalyze *ira) {
|
||||
ira->old_bb_index = SIZE_MAX;
|
||||
assert(ira->new_irb.exec->first_err_trace_msg != nullptr);
|
||||
if (ira->new_irb.exec->first_err_trace_msg == nullptr) {
|
||||
ira->new_irb.exec->first_err_trace_msg = ira->codegen->trace_err;
|
||||
}
|
||||
return ira->codegen->unreach_instruction;
|
||||
}
|
||||
|
||||
@@ -12932,7 +12934,52 @@ static bool optional_value_is_null(ConstExprValue *val) {
|
||||
}
|
||||
}
|
||||
|
||||
// Returns ErrorNotLazy when the value cannot be determined
|
||||
static Error lazy_cmp_zero(AstNode *source_node, ConstExprValue *val, Cmp *result) {
|
||||
Error err;
|
||||
|
||||
switch (val->special) {
|
||||
case ConstValSpecialRuntime:
|
||||
case ConstValSpecialUndef:
|
||||
return ErrorNotLazy;
|
||||
case ConstValSpecialStatic:
|
||||
switch (val->type->id) {
|
||||
case ZigTypeIdComptimeInt:
|
||||
case ZigTypeIdInt:
|
||||
*result = bigint_cmp_zero(&val->data.x_bigint);
|
||||
return ErrorNone;
|
||||
default:
|
||||
return ErrorNotLazy;
|
||||
}
|
||||
case ConstValSpecialLazy:
|
||||
switch (val->data.x_lazy->id) {
|
||||
case LazyValueIdInvalid:
|
||||
zig_unreachable();
|
||||
case LazyValueIdAlignOf:
|
||||
*result = CmpGT;
|
||||
return ErrorNone;
|
||||
case LazyValueIdSizeOf: {
|
||||
LazyValueSizeOf *lazy_size_of = reinterpret_cast<LazyValueSizeOf *>(val->data.x_lazy);
|
||||
IrAnalyze *ira = lazy_size_of->ira;
|
||||
bool is_zero_bits;
|
||||
if ((err = type_val_resolve_zero_bits(ira->codegen, &lazy_size_of->target_type->value,
|
||||
nullptr, nullptr, &is_zero_bits)))
|
||||
{
|
||||
return err;
|
||||
}
|
||||
*result = is_zero_bits ? CmpEQ : CmpGT;
|
||||
return ErrorNone;
|
||||
}
|
||||
default:
|
||||
return ErrorNotLazy;
|
||||
}
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) {
|
||||
Error err;
|
||||
|
||||
IrInstruction *op1 = bin_op_instruction->op1->child;
|
||||
if (type_is_invalid(op1->value.type))
|
||||
return ira->codegen->invalid_instruction;
|
||||
@@ -13182,6 +13229,50 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *
|
||||
}
|
||||
|
||||
if (one_possible_value || (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2))) {
|
||||
{
|
||||
// Before resolving the values, we special case comparisons against zero. These can often be done
|
||||
// without resolving lazy values, preventing potential dependency loops.
|
||||
Cmp op1_cmp_zero;
|
||||
if ((err = lazy_cmp_zero(bin_op_instruction->base.source_node, &casted_op1->value, &op1_cmp_zero))) {
|
||||
if (err == ErrorNotLazy) goto never_mind_just_calculate_it_normally;
|
||||
return ira->codegen->invalid_instruction;
|
||||
}
|
||||
Cmp op2_cmp_zero;
|
||||
if ((err = lazy_cmp_zero(bin_op_instruction->base.source_node, &casted_op2->value, &op2_cmp_zero))) {
|
||||
if (err == ErrorNotLazy) goto never_mind_just_calculate_it_normally;
|
||||
return ira->codegen->invalid_instruction;
|
||||
}
|
||||
bool can_cmp_zero = false;
|
||||
Cmp cmp_result;
|
||||
if (op1_cmp_zero == CmpEQ && op2_cmp_zero == CmpEQ) {
|
||||
can_cmp_zero = true;
|
||||
cmp_result = CmpEQ;
|
||||
} else if (op1_cmp_zero == CmpGT && op2_cmp_zero == CmpEQ) {
|
||||
can_cmp_zero = true;
|
||||
cmp_result = CmpGT;
|
||||
} else if (op1_cmp_zero == CmpEQ && op2_cmp_zero == CmpGT) {
|
||||
can_cmp_zero = true;
|
||||
cmp_result = CmpLT;
|
||||
} else if (op1_cmp_zero == CmpLT && op2_cmp_zero == CmpEQ) {
|
||||
can_cmp_zero = true;
|
||||
cmp_result = CmpLT;
|
||||
} else if (op1_cmp_zero == CmpEQ && op2_cmp_zero == CmpLT) {
|
||||
can_cmp_zero = true;
|
||||
cmp_result = CmpGT;
|
||||
} else if (op1_cmp_zero == CmpLT && op2_cmp_zero == CmpGT) {
|
||||
can_cmp_zero = true;
|
||||
cmp_result = CmpLT;
|
||||
} else if (op1_cmp_zero == CmpGT && op2_cmp_zero == CmpLT) {
|
||||
can_cmp_zero = true;
|
||||
cmp_result = CmpGT;
|
||||
}
|
||||
if (can_cmp_zero) {
|
||||
bool answer = resolve_cmp_op_id(op_id, cmp_result);
|
||||
return ir_const_bool(ira, &bin_op_instruction->base, answer);
|
||||
}
|
||||
}
|
||||
never_mind_just_calculate_it_normally:
|
||||
|
||||
ConstExprValue *op1_val = one_possible_value ? &casted_op1->value : ir_resolve_const(ira, casted_op1, UndefBad);
|
||||
if (op1_val == nullptr)
|
||||
return ira->codegen->invalid_instruction;
|
||||
@@ -14626,28 +14717,23 @@ static IrInstruction *ir_analyze_instruction_error_return_trace(IrAnalyze *ira,
|
||||
static IrInstruction *ir_analyze_instruction_error_union(IrAnalyze *ira,
|
||||
IrInstructionErrorUnion *instruction)
|
||||
{
|
||||
Error err;
|
||||
IrInstruction *result = ir_const(ira, &instruction->base, ira->codegen->builtin_types.entry_type);
|
||||
result->value.special = ConstValSpecialLazy;
|
||||
|
||||
ZigType *err_set_type = ir_resolve_type(ira, instruction->err_set->child);
|
||||
if (type_is_invalid(err_set_type))
|
||||
LazyValueErrUnionType *lazy_err_union_type = allocate<LazyValueErrUnionType>(1);
|
||||
lazy_err_union_type->ira = ira;
|
||||
result->value.data.x_lazy = &lazy_err_union_type->base;
|
||||
lazy_err_union_type->base.id = LazyValueIdErrUnionType;
|
||||
|
||||
lazy_err_union_type->err_set_type = instruction->err_set->child;
|
||||
if (ir_resolve_type_lazy(ira, lazy_err_union_type->err_set_type) == nullptr)
|
||||
return ira->codegen->invalid_instruction;
|
||||
|
||||
ZigType *payload_type = ir_resolve_type(ira, instruction->payload->child);
|
||||
if (type_is_invalid(payload_type))
|
||||
lazy_err_union_type->payload_type = instruction->payload->child;
|
||||
if (ir_resolve_type_lazy(ira, lazy_err_union_type->payload_type) == nullptr)
|
||||
return ira->codegen->invalid_instruction;
|
||||
|
||||
if (err_set_type->id != ZigTypeIdErrorSet) {
|
||||
ir_add_error(ira, instruction->err_set->child,
|
||||
buf_sprintf("expected error set type, found type '%s'",
|
||||
buf_ptr(&err_set_type->name)));
|
||||
return ira->codegen->invalid_instruction;
|
||||
}
|
||||
|
||||
if ((err = type_resolve(ira->codegen, payload_type, ResolveStatusSizeKnown)))
|
||||
return ira->codegen->invalid_instruction;
|
||||
ZigType *result_type = get_error_union_type(ira->codegen, err_set_type, payload_type);
|
||||
|
||||
return ir_const_type(ira, &instruction->base, result_type);
|
||||
return result;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_analyze_alloca(IrAnalyze *ira, IrInstruction *source_inst, ZigType *var_type,
|
||||
@@ -16815,12 +16901,6 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct
|
||||
return ira->codegen->invalid_instruction;
|
||||
|
||||
bool safety_check_on = elem_ptr_instruction->safety_check_on;
|
||||
if ((err = type_resolve(ira->codegen, return_type->data.pointer.child_type, ResolveStatusSizeKnown)))
|
||||
return ira->codegen->invalid_instruction;
|
||||
|
||||
uint64_t elem_size = type_size(ira->codegen, return_type->data.pointer.child_type);
|
||||
uint64_t abi_align = get_abi_alignment(ira->codegen, return_type->data.pointer.child_type);
|
||||
uint64_t ptr_align = get_ptr_align(ira->codegen, return_type);
|
||||
if (instr_is_comptime(casted_elem_index)) {
|
||||
uint64_t index = bigint_as_u64(&casted_elem_index->value.data.x_bigint);
|
||||
if (array_type->id == ZigTypeIdArray) {
|
||||
@@ -16834,8 +16914,16 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct
|
||||
safety_check_on = false;
|
||||
}
|
||||
|
||||
{
|
||||
if (return_type->data.pointer.explicit_alignment != 0) {
|
||||
// figure out the largest alignment possible
|
||||
|
||||
if ((err = type_resolve(ira->codegen, return_type->data.pointer.child_type, ResolveStatusSizeKnown)))
|
||||
return ira->codegen->invalid_instruction;
|
||||
|
||||
uint64_t elem_size = type_size(ira->codegen, return_type->data.pointer.child_type);
|
||||
uint64_t abi_align = get_abi_alignment(ira->codegen, return_type->data.pointer.child_type);
|
||||
uint64_t ptr_align = get_ptr_align(ira->codegen, return_type);
|
||||
|
||||
uint64_t chosen_align = abi_align;
|
||||
if (ptr_align >= abi_align) {
|
||||
while (ptr_align > abi_align) {
|
||||
@@ -17064,15 +17152,24 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct
|
||||
case ReqCompTimeNo:
|
||||
break;
|
||||
}
|
||||
if (ptr_align < abi_align) {
|
||||
if (elem_size >= ptr_align && elem_size % ptr_align == 0) {
|
||||
return_type = adjust_ptr_align(ira->codegen, return_type, ptr_align);
|
||||
|
||||
if (return_type->data.pointer.explicit_alignment != 0) {
|
||||
if ((err = type_resolve(ira->codegen, return_type->data.pointer.child_type, ResolveStatusSizeKnown)))
|
||||
return ira->codegen->invalid_instruction;
|
||||
|
||||
uint64_t elem_size = type_size(ira->codegen, return_type->data.pointer.child_type);
|
||||
uint64_t abi_align = get_abi_alignment(ira->codegen, return_type->data.pointer.child_type);
|
||||
uint64_t ptr_align = get_ptr_align(ira->codegen, return_type);
|
||||
if (ptr_align < abi_align) {
|
||||
if (elem_size >= ptr_align && elem_size % ptr_align == 0) {
|
||||
return_type = adjust_ptr_align(ira->codegen, return_type, ptr_align);
|
||||
} else {
|
||||
// can't get here because guaranteed elem_size >= abi_align
|
||||
zig_unreachable();
|
||||
}
|
||||
} else {
|
||||
// can't get here because guaranteed elem_size >= abi_align
|
||||
zig_unreachable();
|
||||
return_type = adjust_ptr_align(ira->codegen, return_type, abi_align);
|
||||
}
|
||||
} else {
|
||||
return_type = adjust_ptr_align(ira->codegen, return_type, abi_align);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18071,54 +18168,20 @@ static IrInstruction *ir_analyze_instruction_array_type(IrAnalyze *ira,
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
static IrInstruction *ir_analyze_instruction_size_of(IrAnalyze *ira,
|
||||
IrInstructionSizeOf *size_of_instruction)
|
||||
{
|
||||
Error err;
|
||||
IrInstruction *type_value = size_of_instruction->type_value->child;
|
||||
ZigType *type_entry = ir_resolve_type(ira, type_value);
|
||||
static IrInstruction *ir_analyze_instruction_size_of(IrAnalyze *ira, IrInstructionSizeOf *instruction) {
|
||||
IrInstruction *result = ir_const(ira, &instruction->base, ira->codegen->builtin_types.entry_num_lit_int);
|
||||
result->value.special = ConstValSpecialLazy;
|
||||
|
||||
if ((err = type_resolve(ira->codegen, type_entry, ResolveStatusSizeKnown)))
|
||||
LazyValueSizeOf *lazy_size_of = allocate<LazyValueSizeOf>(1);
|
||||
lazy_size_of->ira = ira;
|
||||
result->value.data.x_lazy = &lazy_size_of->base;
|
||||
lazy_size_of->base.id = LazyValueIdSizeOf;
|
||||
|
||||
lazy_size_of->target_type = instruction->type_value->child;
|
||||
if (ir_resolve_type_lazy(ira, lazy_size_of->target_type) == nullptr)
|
||||
return ira->codegen->invalid_instruction;
|
||||
|
||||
switch (type_entry->id) {
|
||||
case ZigTypeIdInvalid: // handled above
|
||||
zig_unreachable();
|
||||
case ZigTypeIdUnreachable:
|
||||
case ZigTypeIdUndefined:
|
||||
case ZigTypeIdNull:
|
||||
case ZigTypeIdBoundFn:
|
||||
case ZigTypeIdArgTuple:
|
||||
case ZigTypeIdOpaque:
|
||||
ir_add_error_node(ira, type_value->source_node,
|
||||
buf_sprintf("no size available for type '%s'", buf_ptr(&type_entry->name)));
|
||||
return ira->codegen->invalid_instruction;
|
||||
case ZigTypeIdMetaType:
|
||||
case ZigTypeIdEnumLiteral:
|
||||
case ZigTypeIdComptimeFloat:
|
||||
case ZigTypeIdComptimeInt:
|
||||
case ZigTypeIdVoid:
|
||||
case ZigTypeIdBool:
|
||||
case ZigTypeIdInt:
|
||||
case ZigTypeIdFloat:
|
||||
case ZigTypeIdPointer:
|
||||
case ZigTypeIdArray:
|
||||
case ZigTypeIdStruct:
|
||||
case ZigTypeIdOptional:
|
||||
case ZigTypeIdErrorUnion:
|
||||
case ZigTypeIdErrorSet:
|
||||
case ZigTypeIdEnum:
|
||||
case ZigTypeIdUnion:
|
||||
case ZigTypeIdFn:
|
||||
case ZigTypeIdVector:
|
||||
case ZigTypeIdFnFrame:
|
||||
case ZigTypeIdAnyFrame:
|
||||
{
|
||||
uint64_t size_in_bytes = type_size(ira->codegen, type_entry);
|
||||
return ir_const_unsigned(ira, &size_of_instruction->base, size_in_bytes);
|
||||
}
|
||||
}
|
||||
zig_unreachable();
|
||||
return result;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_analyze_test_non_null(IrAnalyze *ira, IrInstruction *source_inst, IrInstruction *value) {
|
||||
@@ -24702,18 +24765,22 @@ static IrInstruction *ir_analyze_instruction_suspend_finish(IrAnalyze *ira,
|
||||
}
|
||||
|
||||
static IrInstruction *analyze_frame_ptr_to_anyframe_T(IrAnalyze *ira, IrInstruction *source_instr,
|
||||
IrInstruction *frame_ptr)
|
||||
IrInstruction *frame_ptr, ZigFn **target_fn)
|
||||
{
|
||||
if (type_is_invalid(frame_ptr->value.type))
|
||||
return ira->codegen->invalid_instruction;
|
||||
|
||||
*target_fn = nullptr;
|
||||
|
||||
ZigType *result_type;
|
||||
IrInstruction *frame;
|
||||
if (frame_ptr->value.type->id == ZigTypeIdPointer &&
|
||||
frame_ptr->value.type->data.pointer.ptr_len == PtrLenSingle &&
|
||||
frame_ptr->value.type->data.pointer.child_type->id == ZigTypeIdFnFrame)
|
||||
{
|
||||
result_type = frame_ptr->value.type->data.pointer.child_type->data.frame.fn->type_entry->data.fn.fn_type_id.return_type;
|
||||
ZigFn *func = frame_ptr->value.type->data.pointer.child_type->data.frame.fn;
|
||||
result_type = func->type_entry->data.fn.fn_type_id.return_type;
|
||||
*target_fn = func;
|
||||
frame = frame_ptr;
|
||||
} else {
|
||||
frame = ir_get_deref(ira, source_instr, frame_ptr, nullptr);
|
||||
@@ -24721,7 +24788,9 @@ static IrInstruction *analyze_frame_ptr_to_anyframe_T(IrAnalyze *ira, IrInstruct
|
||||
frame->value.type->data.pointer.ptr_len == PtrLenSingle &&
|
||||
frame->value.type->data.pointer.child_type->id == ZigTypeIdFnFrame)
|
||||
{
|
||||
result_type = frame->value.type->data.pointer.child_type->data.frame.fn->type_entry->data.fn.fn_type_id.return_type;
|
||||
ZigFn *func = frame->value.type->data.pointer.child_type->data.frame.fn;
|
||||
result_type = func->type_entry->data.fn.fn_type_id.return_type;
|
||||
*target_fn = func;
|
||||
} else if (frame->value.type->id != ZigTypeIdAnyFrame ||
|
||||
frame->value.type->data.any_frame.result_type == nullptr)
|
||||
{
|
||||
@@ -24742,7 +24811,11 @@ static IrInstruction *analyze_frame_ptr_to_anyframe_T(IrAnalyze *ira, IrInstruct
|
||||
}
|
||||
|
||||
static IrInstruction *ir_analyze_instruction_await(IrAnalyze *ira, IrInstructionAwaitSrc *instruction) {
|
||||
IrInstruction *frame = analyze_frame_ptr_to_anyframe_T(ira, &instruction->base, instruction->frame->child);
|
||||
IrInstruction *operand = instruction->frame->child;
|
||||
if (type_is_invalid(operand->value.type))
|
||||
return ira->codegen->invalid_instruction;
|
||||
ZigFn *target_fn;
|
||||
IrInstruction *frame = analyze_frame_ptr_to_anyframe_T(ira, &instruction->base, operand, &target_fn);
|
||||
if (type_is_invalid(frame->value.type))
|
||||
return ira->codegen->invalid_instruction;
|
||||
|
||||
@@ -24751,8 +24824,11 @@ static IrInstruction *ir_analyze_instruction_await(IrAnalyze *ira, IrInstruction
|
||||
ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec);
|
||||
ir_assert(fn_entry != nullptr, &instruction->base);
|
||||
|
||||
if (fn_entry->inferred_async_node == nullptr) {
|
||||
fn_entry->inferred_async_node = instruction->base.source_node;
|
||||
// If it's not @Frame(func) then it's definitely a suspend point
|
||||
if (target_fn == nullptr) {
|
||||
if (fn_entry->inferred_async_node == nullptr) {
|
||||
fn_entry->inferred_async_node = instruction->base.source_node;
|
||||
}
|
||||
}
|
||||
|
||||
if (type_can_fail(result_type)) {
|
||||
@@ -24769,8 +24845,10 @@ static IrInstruction *ir_analyze_instruction_await(IrAnalyze *ira, IrInstruction
|
||||
result_loc = nullptr;
|
||||
}
|
||||
|
||||
IrInstruction *result = ir_build_await_gen(ira, &instruction->base, frame, result_type, result_loc);
|
||||
return ir_finish_anal(ira, result);
|
||||
IrInstructionAwaitGen *result = ir_build_await_gen(ira, &instruction->base, frame, result_type, result_loc);
|
||||
result->target_fn = target_fn;
|
||||
fn_entry->await_list.append(result);
|
||||
return ir_finish_anal(ira, &result->base);
|
||||
}
|
||||
|
||||
static IrInstruction *ir_analyze_instruction_resume(IrAnalyze *ira, IrInstructionResume *instruction) {
|
||||
@@ -25553,6 +25631,61 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ConstExprValue *val) {
|
||||
bigint_init_unsigned(&val->data.x_bigint, align_in_bytes);
|
||||
return ErrorNone;
|
||||
}
|
||||
case LazyValueIdSizeOf: {
|
||||
LazyValueSizeOf *lazy_size_of = reinterpret_cast<LazyValueSizeOf *>(val->data.x_lazy);
|
||||
IrAnalyze *ira = lazy_size_of->ira;
|
||||
|
||||
if (lazy_size_of->target_type->value.special == ConstValSpecialStatic) {
|
||||
switch (lazy_size_of->target_type->value.data.x_type->id) {
|
||||
case ZigTypeIdInvalid: // handled above
|
||||
zig_unreachable();
|
||||
case ZigTypeIdUnreachable:
|
||||
case ZigTypeIdUndefined:
|
||||
case ZigTypeIdNull:
|
||||
case ZigTypeIdBoundFn:
|
||||
case ZigTypeIdArgTuple:
|
||||
case ZigTypeIdOpaque:
|
||||
ir_add_error(ira, lazy_size_of->target_type,
|
||||
buf_sprintf("no size available for type '%s'",
|
||||
buf_ptr(&lazy_size_of->target_type->value.data.x_type->name)));
|
||||
return ErrorSemanticAnalyzeFail;
|
||||
case ZigTypeIdMetaType:
|
||||
case ZigTypeIdEnumLiteral:
|
||||
case ZigTypeIdComptimeFloat:
|
||||
case ZigTypeIdComptimeInt:
|
||||
case ZigTypeIdVoid:
|
||||
case ZigTypeIdBool:
|
||||
case ZigTypeIdInt:
|
||||
case ZigTypeIdFloat:
|
||||
case ZigTypeIdPointer:
|
||||
case ZigTypeIdArray:
|
||||
case ZigTypeIdStruct:
|
||||
case ZigTypeIdOptional:
|
||||
case ZigTypeIdErrorUnion:
|
||||
case ZigTypeIdErrorSet:
|
||||
case ZigTypeIdEnum:
|
||||
case ZigTypeIdUnion:
|
||||
case ZigTypeIdFn:
|
||||
case ZigTypeIdVector:
|
||||
case ZigTypeIdFnFrame:
|
||||
case ZigTypeIdAnyFrame:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t abi_size;
|
||||
uint64_t size_in_bits;
|
||||
if ((err = type_val_resolve_abi_size(ira->codegen, source_node, &lazy_size_of->target_type->value,
|
||||
&abi_size, &size_in_bits)))
|
||||
{
|
||||
return err;
|
||||
}
|
||||
|
||||
val->special = ConstValSpecialStatic;
|
||||
assert(val->type->id == ZigTypeIdComptimeInt);
|
||||
bigint_init_unsigned(&val->data.x_bigint, abi_size);
|
||||
return ErrorNone;
|
||||
}
|
||||
case LazyValueIdSliceType: {
|
||||
LazyValueSliceType *lazy_slice_type = reinterpret_cast<LazyValueSliceType *>(val->data.x_lazy);
|
||||
IrAnalyze *ira = lazy_slice_type->ira;
|
||||
@@ -25698,6 +25831,34 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ConstExprValue *val) {
|
||||
val->data.x_type = fn_type;
|
||||
return ErrorNone;
|
||||
}
|
||||
case LazyValueIdErrUnionType: {
|
||||
LazyValueErrUnionType *lazy_err_union_type =
|
||||
reinterpret_cast<LazyValueErrUnionType *>(val->data.x_lazy);
|
||||
IrAnalyze *ira = lazy_err_union_type->ira;
|
||||
|
||||
ZigType *err_set_type = ir_resolve_type(ira, lazy_err_union_type->err_set_type);
|
||||
if (type_is_invalid(err_set_type))
|
||||
return ErrorSemanticAnalyzeFail;
|
||||
|
||||
ZigType *payload_type = ir_resolve_type(ira, lazy_err_union_type->payload_type);
|
||||
if (type_is_invalid(payload_type))
|
||||
return ErrorSemanticAnalyzeFail;
|
||||
|
||||
if (err_set_type->id != ZigTypeIdErrorSet) {
|
||||
ir_add_error(ira, lazy_err_union_type->err_set_type,
|
||||
buf_sprintf("expected error set type, found type '%s'",
|
||||
buf_ptr(&err_set_type->name)));
|
||||
return ErrorSemanticAnalyzeFail;
|
||||
}
|
||||
|
||||
if ((err = type_resolve(ira->codegen, payload_type, ResolveStatusSizeKnown)))
|
||||
return ErrorSemanticAnalyzeFail;
|
||||
|
||||
assert(val->type->id == ZigTypeIdMetaType);
|
||||
val->data.x_type = get_error_union_type(ira->codegen, err_set_type, payload_type);
|
||||
val->special = ConstValSpecialStatic;
|
||||
return ErrorNone;
|
||||
}
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
11
src/list.hpp
11
src/list.hpp
@@ -74,6 +74,17 @@ struct ZigList {
|
||||
capacity = better_capacity;
|
||||
}
|
||||
|
||||
T swap_remove(size_t index) {
|
||||
if (length - 1 == index) return pop();
|
||||
|
||||
assert(index != SIZE_MAX);
|
||||
assert(index < length);
|
||||
|
||||
T old_item = items[index];
|
||||
items[index] = pop();
|
||||
return old_item;
|
||||
}
|
||||
|
||||
T *items;
|
||||
size_t length;
|
||||
size_t capacity;
|
||||
|
||||
@@ -75,6 +75,8 @@ enum Error {
|
||||
ErrorOperationAborted,
|
||||
ErrorBrokenPipe,
|
||||
ErrorNoSpaceLeft,
|
||||
ErrorNotLazy,
|
||||
ErrorIsAsync,
|
||||
};
|
||||
|
||||
// ABI warning
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const assert = std.debug.assert;
|
||||
const mem = std.mem;
|
||||
const meta = std.meta;
|
||||
|
||||
@@ -165,8 +166,17 @@ pub fn hash(hasher: var, key: var, comptime strat: HashStrategy) void {
|
||||
/// Slices are rejected to avoid ambiguity on the user's intention.
|
||||
pub fn autoHash(hasher: var, key: var) void {
|
||||
const Key = @typeOf(key);
|
||||
if (comptime meta.trait.isSlice(Key))
|
||||
@compileError("std.auto_hash.autoHash does not allow slices (here " ++ @typeName(Key) ++ " because the intent is unclear. Consider using std.auto_hash.hash or providing your own hash function instead.");
|
||||
if (comptime meta.trait.isSlice(Key)) {
|
||||
comptime assert(@hasDecl(std, "StringHashMap")); // detect when the following message needs updated
|
||||
const extra_help = if (Key == []const u8)
|
||||
" Consider std.StringHashMap for hashing the contents of []const u8."
|
||||
else
|
||||
"";
|
||||
|
||||
@compileError("std.auto_hash.autoHash does not allow slices (here " ++ @typeName(Key) ++
|
||||
") because the intent is unclear. Consider using std.auto_hash.hash or providing your own hash function instead." ++
|
||||
extra_help);
|
||||
}
|
||||
|
||||
hash(hasher, key, .Shallow);
|
||||
}
|
||||
|
||||
11
std/mem.zig
11
std/mem.zig
@@ -75,15 +75,16 @@ pub const Allocator = struct {
|
||||
new_alignment: u29,
|
||||
) []u8,
|
||||
|
||||
/// Call `destroy` with the result.
|
||||
/// Returns undefined memory.
|
||||
/// Returns a pointer to undefined memory.
|
||||
/// Call `destroy` with the result to free the memory.
|
||||
pub fn create(self: *Allocator, comptime T: type) Error!*T {
|
||||
if (@sizeOf(T) == 0) return &(T{});
|
||||
const slice = try self.alloc(T, 1);
|
||||
return &slice[0];
|
||||
}
|
||||
|
||||
/// `ptr` should be the return value of `create`
|
||||
/// `ptr` should be the return value of `create`, or otherwise
|
||||
/// have the same address and alignment property.
|
||||
pub fn destroy(self: *Allocator, ptr: var) void {
|
||||
const T = @typeOf(ptr).Child;
|
||||
if (@sizeOf(T) == 0) return;
|
||||
@@ -92,7 +93,7 @@ pub const Allocator = struct {
|
||||
assert(shrink_result.len == 0);
|
||||
}
|
||||
|
||||
pub fn alloc(self: *Allocator, comptime T: type, n: usize) ![]T {
|
||||
pub fn alloc(self: *Allocator, comptime T: type, n: usize) Error![]T {
|
||||
return self.alignedAlloc(T, @alignOf(T), n);
|
||||
}
|
||||
|
||||
@@ -101,7 +102,7 @@ pub const Allocator = struct {
|
||||
comptime T: type,
|
||||
comptime alignment: u29,
|
||||
n: usize,
|
||||
) ![]align(alignment) T {
|
||||
) Error![]align(alignment) T {
|
||||
if (n == 0) {
|
||||
return ([*]align(alignment) T)(undefined)[0..0];
|
||||
}
|
||||
|
||||
@@ -210,6 +210,103 @@ test "zig fmt: comment to disable/enable zig fmt" {
|
||||
);
|
||||
}
|
||||
|
||||
test "zig fmt: line comment following 'zig fmt: off'" {
|
||||
try testCanonical(
|
||||
\\// zig fmt: off
|
||||
\\// Test
|
||||
\\const e = f;
|
||||
);
|
||||
}
|
||||
|
||||
test "zig fmt: doc comment following 'zig fmt: off'" {
|
||||
try testCanonical(
|
||||
\\// zig fmt: off
|
||||
\\/// test
|
||||
\\const e = f;
|
||||
);
|
||||
}
|
||||
|
||||
test "zig fmt: line and doc comment following 'zig fmt: off'" {
|
||||
try testCanonical(
|
||||
\\// zig fmt: off
|
||||
\\// test 1
|
||||
\\/// test 2
|
||||
\\const e = f;
|
||||
);
|
||||
}
|
||||
|
||||
test "zig fmt: doc and line comment following 'zig fmt: off'" {
|
||||
try testCanonical(
|
||||
\\// zig fmt: off
|
||||
\\/// test 1
|
||||
\\// test 2
|
||||
\\const e = f;
|
||||
);
|
||||
}
|
||||
|
||||
test "zig fmt: alternating 'zig fmt: off' and 'zig fmt: on'" {
|
||||
try testCanonical(
|
||||
\\// zig fmt: off
|
||||
\\// zig fmt: on
|
||||
\\// zig fmt: off
|
||||
\\const e = f;
|
||||
\\// zig fmt: off
|
||||
\\// zig fmt: on
|
||||
\\// zig fmt: off
|
||||
\\const a = b;
|
||||
\\// zig fmt: on
|
||||
\\const c = d;
|
||||
\\// zig fmt: on
|
||||
\\
|
||||
);
|
||||
}
|
||||
|
||||
test "zig fmt: line comment following 'zig fmt: on'" {
|
||||
try testCanonical(
|
||||
\\// zig fmt: off
|
||||
\\const e = f;
|
||||
\\// zig fmt: on
|
||||
\\// test
|
||||
\\const e = f;
|
||||
\\
|
||||
);
|
||||
}
|
||||
|
||||
test "zig fmt: doc comment following 'zig fmt: on'" {
|
||||
try testCanonical(
|
||||
\\// zig fmt: off
|
||||
\\const e = f;
|
||||
\\// zig fmt: on
|
||||
\\/// test
|
||||
\\const e = f;
|
||||
\\
|
||||
);
|
||||
}
|
||||
|
||||
test "zig fmt: line and doc comment following 'zig fmt: on'" {
|
||||
try testCanonical(
|
||||
\\// zig fmt: off
|
||||
\\const e = f;
|
||||
\\// zig fmt: on
|
||||
\\// test1
|
||||
\\/// test2
|
||||
\\const e = f;
|
||||
\\
|
||||
);
|
||||
}
|
||||
|
||||
test "zig fmt: doc and line comment following 'zig fmt: on'" {
|
||||
try testCanonical(
|
||||
\\// zig fmt: off
|
||||
\\const e = f;
|
||||
\\// zig fmt: on
|
||||
\\/// test1
|
||||
\\// test2
|
||||
\\const e = f;
|
||||
\\
|
||||
);
|
||||
}
|
||||
|
||||
test "zig fmt: pointer of unknown length" {
|
||||
try testCanonical(
|
||||
\\fn foo(ptr: [*]u8) void {}
|
||||
@@ -2278,7 +2375,6 @@ test "zig fmt: if type expr" {
|
||||
\\
|
||||
);
|
||||
}
|
||||
|
||||
test "zig fmt: file ends with struct field" {
|
||||
try testTransform(
|
||||
\\a: bool
|
||||
@@ -2288,6 +2384,20 @@ test "zig fmt: file ends with struct field" {
|
||||
);
|
||||
}
|
||||
|
||||
test "zig fmt: comment after empty comment" {
|
||||
try testTransform(
|
||||
\\const x = true; //
|
||||
\\//
|
||||
\\//
|
||||
\\//a
|
||||
\\
|
||||
,
|
||||
\\const x = true;
|
||||
\\//a
|
||||
\\
|
||||
);
|
||||
}
|
||||
|
||||
test "zig fmt: comments at several places in struct init" {
|
||||
try testTransform(
|
||||
\\var bar = Bar{
|
||||
|
||||
@@ -89,41 +89,98 @@ fn renderRoot(
|
||||
var it = tree.root_node.decls.iterator(0);
|
||||
while (true) {
|
||||
var decl = (it.next() orelse return).*;
|
||||
// look for zig fmt: off comment
|
||||
var start_token_index = decl.firstToken();
|
||||
zig_fmt_loop: while (start_token_index != 0) {
|
||||
start_token_index -= 1;
|
||||
const start_token = tree.tokens.at(start_token_index);
|
||||
switch (start_token.id) {
|
||||
|
||||
// This loop does the following:
|
||||
//
|
||||
// - Iterates through line/doc comment tokens that precedes the current
|
||||
// decl.
|
||||
// - Figures out the first token index (`copy_start_token_index`) which
|
||||
// hasn't been copied to the output stream yet.
|
||||
// - Detects `zig fmt: (off|on)` in the line comment tokens, and
|
||||
// determines whether the current decl should be reformatted or not.
|
||||
//
|
||||
var token_index = decl.firstToken();
|
||||
var fmt_active = true;
|
||||
var found_fmt_directive = false;
|
||||
|
||||
var copy_start_token_index = token_index;
|
||||
|
||||
while (token_index != 0) {
|
||||
token_index -= 1;
|
||||
const token = tree.tokens.at(token_index);
|
||||
switch (token.id) {
|
||||
Token.Id.LineComment => {},
|
||||
Token.Id.DocComment => continue,
|
||||
Token.Id.DocComment => {
|
||||
copy_start_token_index = token_index;
|
||||
continue;
|
||||
},
|
||||
else => break,
|
||||
}
|
||||
if (mem.eql(u8, mem.trim(u8, tree.tokenSlicePtr(start_token)[2..], " "), "zig fmt: off")) {
|
||||
var end_token_index = start_token_index;
|
||||
while (true) {
|
||||
end_token_index += 1;
|
||||
const end_token = tree.tokens.at(end_token_index);
|
||||
switch (end_token.id) {
|
||||
|
||||
if (mem.eql(u8, mem.trim(u8, tree.tokenSlicePtr(token)[2..], " "), "zig fmt: off")) {
|
||||
if (!found_fmt_directive) {
|
||||
fmt_active = false;
|
||||
found_fmt_directive = true;
|
||||
}
|
||||
} else if (mem.eql(u8, mem.trim(u8, tree.tokenSlicePtr(token)[2..], " "), "zig fmt: on")) {
|
||||
if (!found_fmt_directive) {
|
||||
fmt_active = true;
|
||||
found_fmt_directive = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!fmt_active) {
|
||||
// Reformatting is disabled for the current decl and possibly some
|
||||
// more decls that follow.
|
||||
// Find the next `decl` for which reformatting is re-enabled.
|
||||
token_index = decl.firstToken();
|
||||
|
||||
while (!fmt_active) {
|
||||
decl = (it.next() orelse {
|
||||
// If there's no next reformatted `decl`, just copy the
|
||||
// remaining input tokens and bail out.
|
||||
const start = tree.tokens.at(copy_start_token_index).start;
|
||||
try copyFixingWhitespace(stream, tree.source[start..]);
|
||||
return;
|
||||
}).*;
|
||||
var decl_first_token_index = decl.firstToken();
|
||||
|
||||
while (token_index < decl_first_token_index) : (token_index += 1) {
|
||||
const token = tree.tokens.at(token_index);
|
||||
switch (token.id) {
|
||||
Token.Id.LineComment => {},
|
||||
Token.Id.Eof => {
|
||||
const start = tree.tokens.at(start_token_index + 1).start;
|
||||
try copyFixingWhitespace(stream, tree.source[start..]);
|
||||
return;
|
||||
},
|
||||
Token.Id.Eof => unreachable,
|
||||
else => continue,
|
||||
}
|
||||
if (mem.eql(u8, mem.trim(u8, tree.tokenSlicePtr(end_token)[2..], " "), "zig fmt: on")) {
|
||||
const start = tree.tokens.at(start_token_index + 1).start;
|
||||
try copyFixingWhitespace(stream, tree.source[start..end_token.end]);
|
||||
try stream.writeByte('\n');
|
||||
while (tree.tokens.at(decl.firstToken()).start < end_token.end) {
|
||||
decl = (it.next() orelse return).*;
|
||||
}
|
||||
break :zig_fmt_loop;
|
||||
if (mem.eql(u8, mem.trim(u8, tree.tokenSlicePtr(token)[2..], " "), "zig fmt: on")) {
|
||||
fmt_active = true;
|
||||
} else if (mem.eql(u8, mem.trim(u8, tree.tokenSlicePtr(token)[2..], " "), "zig fmt: off")) {
|
||||
fmt_active = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Found the next `decl` for which reformatting is enabled. Copy
|
||||
// the input tokens before the `decl` that haven't been copied yet.
|
||||
var copy_end_token_index = decl.firstToken();
|
||||
token_index = copy_end_token_index;
|
||||
while (token_index != 0) {
|
||||
token_index -= 1;
|
||||
const token = tree.tokens.at(token_index);
|
||||
switch (token.id) {
|
||||
Token.Id.LineComment => {},
|
||||
Token.Id.DocComment => {
|
||||
copy_end_token_index = token_index;
|
||||
continue;
|
||||
},
|
||||
else => break,
|
||||
}
|
||||
}
|
||||
|
||||
const start = tree.tokens.at(copy_start_token_index).start;
|
||||
const end = tree.tokens.at(copy_end_token_index).start;
|
||||
try copyFixingWhitespace(stream, tree.source[start..end]);
|
||||
}
|
||||
|
||||
try renderTopLevelDecl(allocator, stream, tree, 0, &start_col, decl);
|
||||
@@ -1937,15 +1994,24 @@ fn renderTokenOffset(
|
||||
}
|
||||
}
|
||||
|
||||
const comment_is_empty = mem.trimRight(u8, tree.tokenSlicePtr(next_token), " ").len == 2;
|
||||
if (comment_is_empty) {
|
||||
switch (space) {
|
||||
Space.Newline => {
|
||||
try stream.writeByte('\n');
|
||||
start_col.* = 0;
|
||||
return;
|
||||
},
|
||||
else => {},
|
||||
while (true) {
|
||||
const comment_is_empty = mem.trimRight(u8, tree.tokenSlicePtr(next_token), " ").len == 2;
|
||||
if (comment_is_empty) {
|
||||
switch (space) {
|
||||
Space.Newline => {
|
||||
offset += 1;
|
||||
token = next_token;
|
||||
next_token = tree.tokens.at(token_index + offset);
|
||||
if (next_token.id != .LineComment) {
|
||||
try stream.writeByte('\n');
|
||||
start_col.* = 0;
|
||||
return;
|
||||
}
|
||||
},
|
||||
else => break,
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -273,7 +273,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
|
||||
\\}
|
||||
,
|
||||
"tmp.zig:1:1: error: function with calling convention 'ccc' cannot be async",
|
||||
"tmp.zig:3:18: note: await is a suspend point",
|
||||
"tmp.zig:3:18: note: await here is a suspend point",
|
||||
);
|
||||
|
||||
cases.add(
|
||||
@@ -507,11 +507,11 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
|
||||
|
||||
cases.add(
|
||||
"@sizeOf bad type",
|
||||
\\export fn entry() void {
|
||||
\\ _ = @sizeOf(@typeOf(null));
|
||||
\\export fn entry() usize {
|
||||
\\ return @sizeOf(@typeOf(null));
|
||||
\\}
|
||||
,
|
||||
"tmp.zig:2:17: error: no size available for type '(null)'",
|
||||
"tmp.zig:2:20: error: no size available for type '(null)'",
|
||||
);
|
||||
|
||||
cases.add(
|
||||
|
||||
@@ -292,3 +292,9 @@ test "read/write through global variable array of struct fields initialized via
|
||||
};
|
||||
S.doTheTest();
|
||||
}
|
||||
|
||||
test "implicit cast zero sized array ptr to slice" {
|
||||
var b = "";
|
||||
const c: []const u8 = &b;
|
||||
expect(c.len == 0);
|
||||
}
|
||||
|
||||
@@ -844,3 +844,13 @@ test "cast fn to async fn when it is inferred to be async" {
|
||||
resume S.frame;
|
||||
expect(S.ok);
|
||||
}
|
||||
|
||||
test "await does not force async if callee is blocking" {
|
||||
const S = struct {
|
||||
fn simple() i32 {
|
||||
return 1234;
|
||||
}
|
||||
};
|
||||
var x = async S.simple();
|
||||
expect(await x == 1234);
|
||||
}
|
||||
|
||||
@@ -375,3 +375,23 @@ test "implicit cast to optional to error union to return result loc" {
|
||||
S.entry();
|
||||
//comptime S.entry(); TODO
|
||||
}
|
||||
|
||||
test "function pointer with return type that is error union with payload which is pointer of parent struct" {
|
||||
const S = struct {
|
||||
const Foo = struct {
|
||||
fun: fn (a: i32) (anyerror!*Foo),
|
||||
};
|
||||
|
||||
const Err = error{UnspecifiedErr};
|
||||
|
||||
fn bar(a: i32) anyerror!*Foo {
|
||||
return Err.UnspecifiedErr;
|
||||
}
|
||||
|
||||
fn doTheTest() void {
|
||||
var x = Foo{ .fun = bar };
|
||||
expectError(error.UnspecifiedErr, x.fun(1));
|
||||
}
|
||||
};
|
||||
S.doTheTest();
|
||||
}
|
||||
|
||||
@@ -74,3 +74,18 @@ test "@sizeOf on compile-time types" {
|
||||
expect(@sizeOf(@typeOf(.hi)) == 0);
|
||||
expect(@sizeOf(@typeOf(type)) == 0);
|
||||
}
|
||||
|
||||
test "@sizeOf(T) == 0 doesn't force resolving struct size" {
|
||||
const S = struct {
|
||||
const Foo = struct {
|
||||
y: if (@sizeOf(Foo) == 0) u64 else u32,
|
||||
};
|
||||
const Bar = struct {
|
||||
x: i32,
|
||||
y: if (0 == @sizeOf(Bar)) u64 else u32,
|
||||
};
|
||||
};
|
||||
|
||||
expect(@sizeOf(S.Foo) == 4);
|
||||
expect(@sizeOf(S.Bar) == 8);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user