mirror of
https://github.com/ziglang/zig.git
synced 2026-01-20 14:25:16 +00:00
if statements can be const expr evaluated
also introduce error for unnecessary if statement but if the condition depends on a compile variable, then the if statement is OK
This commit is contained in:
parent
f45c374664
commit
d8f6388b63
@ -13,7 +13,7 @@ pub fn main(args: [][]u8) -> %void {
|
||||
for (arg, args[1...]) {
|
||||
if (arg == "-") {
|
||||
catted_anything = true;
|
||||
%return cat_stream(stdin);
|
||||
cat_stream(stdin) %% |err| return err;
|
||||
} else if (arg[0] == '-') {
|
||||
return usage(exe);
|
||||
} else {
|
||||
@ -24,11 +24,11 @@ pub fn main(args: [][]u8) -> %void {
|
||||
defer is.close();
|
||||
|
||||
catted_anything = true;
|
||||
%return cat_stream(is);
|
||||
cat_stream(is) %% |err| return err;
|
||||
}
|
||||
}
|
||||
if (!catted_anything) {
|
||||
%return cat_stream(stdin)
|
||||
cat_stream(stdin) %% |err| return err;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1049,6 +1049,19 @@ struct BuiltinFnEntry {
|
||||
LLVMValueRef fn_val;
|
||||
};
|
||||
|
||||
enum CIntType {
|
||||
CIntTypeShort,
|
||||
CIntTypeUShort,
|
||||
CIntTypeInt,
|
||||
CIntTypeUInt,
|
||||
CIntTypeLong,
|
||||
CIntTypeULong,
|
||||
CIntTypeLongLong,
|
||||
CIntTypeULongLong,
|
||||
|
||||
CIntTypeCount,
|
||||
};
|
||||
|
||||
struct CodeGen {
|
||||
LLVMModuleRef module;
|
||||
ZigList<ErrorMsg*> errors;
|
||||
@ -1072,7 +1085,7 @@ struct CodeGen {
|
||||
struct {
|
||||
TypeTableEntry *entry_bool;
|
||||
TypeTableEntry *entry_int[2][4]; // [signed,unsigned][8,16,32,64]
|
||||
TypeTableEntry *entry_c_int[8];
|
||||
TypeTableEntry *entry_c_int[CIntTypeCount];
|
||||
TypeTableEntry *entry_c_long_double;
|
||||
TypeTableEntry *entry_u8;
|
||||
TypeTableEntry *entry_u16;
|
||||
@ -1196,16 +1209,6 @@ struct BlockContext {
|
||||
Buf *c_import_buf;
|
||||
};
|
||||
|
||||
enum CIntType {
|
||||
CIntTypeShort,
|
||||
CIntTypeUShort,
|
||||
CIntTypeInt,
|
||||
CIntTypeUInt,
|
||||
CIntTypeLong,
|
||||
CIntTypeULong,
|
||||
CIntTypeLongLong,
|
||||
CIntTypeULongLong,
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
136
src/analyze.cpp
136
src/analyze.cpp
@ -2159,6 +2159,7 @@ static TypeTableEntry *analyze_container_init_expr(CodeGen *g, ImportTableEntry
|
||||
&get_resolved_expr(val_field_node->data.struct_val_field.expr)->const_val;
|
||||
if (field_val->ok) {
|
||||
const_val->data.x_struct.fields[field_index] = field_val;
|
||||
const_val->depends_on_compile_var = const_val->depends_on_compile_var || field_val->depends_on_compile_var;
|
||||
} else {
|
||||
const_val->ok = false;
|
||||
}
|
||||
@ -2197,6 +2198,8 @@ static TypeTableEntry *analyze_container_init_expr(CodeGen *g, ImportTableEntry
|
||||
ConstExprValue *elem_const_val = &get_resolved_expr(*elem_node)->const_val;
|
||||
if (elem_const_val->ok) {
|
||||
const_val->data.x_array.fields[i] = elem_const_val;
|
||||
const_val->depends_on_compile_var = const_val->depends_on_compile_var ||
|
||||
elem_const_val->depends_on_compile_var;
|
||||
} else {
|
||||
const_val->ok = false;
|
||||
}
|
||||
@ -2431,9 +2434,12 @@ static TypeTableEntry *resolve_expr_const_val_as_err(CodeGen *g, AstNode *node,
|
||||
return g->builtin_types.entry_pure_error;
|
||||
}
|
||||
|
||||
static TypeTableEntry *resolve_expr_const_val_as_bool(CodeGen *g, AstNode *node, bool value) {
|
||||
static TypeTableEntry *resolve_expr_const_val_as_bool(CodeGen *g, AstNode *node, bool value,
|
||||
bool depends_on_compile_var)
|
||||
{
|
||||
Expr *expr = get_resolved_expr(node);
|
||||
expr->const_val.ok = true;
|
||||
expr->const_val.depends_on_compile_var = depends_on_compile_var;
|
||||
expr->const_val.data.x_bool = value;
|
||||
return g->builtin_types.entry_bool;
|
||||
}
|
||||
@ -2817,7 +2823,8 @@ static TypeTableEntry *analyze_bool_bin_op_expr(CodeGen *g, ImportTableEntry *im
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
return resolve_expr_const_val_as_bool(g, node, answer);
|
||||
bool depends_on_compile_var = op1_val->depends_on_compile_var || op2_val->depends_on_compile_var;
|
||||
return resolve_expr_const_val_as_bool(g, node, answer, depends_on_compile_var);
|
||||
}
|
||||
|
||||
static TypeTableEntry *analyze_logic_bin_op_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
|
||||
@ -2844,7 +2851,8 @@ static TypeTableEntry *analyze_logic_bin_op_expr(CodeGen *g, ImportTableEntry *i
|
||||
}
|
||||
|
||||
bool answer = eval_bool_bin_op_bool(op1_val->data.x_bool, bin_op_type, op2_val->data.x_bool);
|
||||
return resolve_expr_const_val_as_bool(g, node, answer);
|
||||
bool depends_on_compile_var = op1_val->depends_on_compile_var || op2_val->depends_on_compile_var;
|
||||
return resolve_expr_const_val_as_bool(g, node, answer, depends_on_compile_var);
|
||||
}
|
||||
|
||||
static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
|
||||
@ -3001,6 +3009,8 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import,
|
||||
}
|
||||
ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
|
||||
const_val->ok = true;
|
||||
const_val->depends_on_compile_var = op1_val->depends_on_compile_var ||
|
||||
op2_val->depends_on_compile_var;
|
||||
|
||||
ConstExprValue *all_fields = allocate<ConstExprValue>(2);
|
||||
ConstExprValue *ptr_field = &all_fields[0];
|
||||
@ -3444,51 +3454,111 @@ static TypeTableEntry *analyze_continue_expr(CodeGen *g, ImportTableEntry *impor
|
||||
return g->builtin_types.entry_unreachable;
|
||||
}
|
||||
|
||||
static TypeTableEntry *analyze_if_then_else(CodeGen *g, ImportTableEntry *import, BlockContext *context,
|
||||
TypeTableEntry *expected_type, AstNode *then_block, AstNode *else_node, AstNode *parent_node)
|
||||
static TypeTableEntry *analyze_if(CodeGen *g, ImportTableEntry *import, BlockContext *context,
|
||||
TypeTableEntry *expected_type, AstNode *node,
|
||||
AstNode **then_node, AstNode **else_node, bool cond_is_const, bool cond_bool_val)
|
||||
{
|
||||
TypeTableEntry *then_type = analyze_expression(g, import, context, expected_type, then_block);
|
||||
|
||||
TypeTableEntry *else_type;
|
||||
if (else_node) {
|
||||
else_type = analyze_expression(g, import, context, expected_type, else_node);
|
||||
} else {
|
||||
else_type = resolve_type_compatibility(g, import, context, parent_node, expected_type,
|
||||
g->builtin_types.entry_void);
|
||||
if (!*else_node) {
|
||||
*else_node = create_ast_void_node(g, import, node);
|
||||
normalize_parent_ptrs(node);
|
||||
}
|
||||
|
||||
TypeTableEntry *then_type = analyze_expression(g, import, context, expected_type, *then_node);
|
||||
TypeTableEntry *else_type = analyze_expression(g, import, context, expected_type, *else_node);
|
||||
|
||||
if (then_type->id == TypeTableEntryIdInvalid || else_type->id == TypeTableEntryIdInvalid) {
|
||||
return g->builtin_types.entry_invalid;
|
||||
}
|
||||
|
||||
TypeTableEntry *result_type;
|
||||
if (expected_type) {
|
||||
return (then_type->id == TypeTableEntryIdUnreachable) ? else_type : then_type;
|
||||
result_type = (then_type->id == TypeTableEntryIdUnreachable) ? else_type : then_type;
|
||||
} else {
|
||||
AstNode *op_nodes[] = {then_block, else_node};
|
||||
AstNode *op_nodes[] = {*then_node, *else_node};
|
||||
TypeTableEntry *op_types[] = {then_type, else_type};
|
||||
return resolve_peer_type_compatibility(g, import, context, parent_node, op_nodes, op_types, 2);
|
||||
result_type = resolve_peer_type_compatibility(g, import, context, node, op_nodes, op_types, 2);
|
||||
}
|
||||
|
||||
if (!cond_is_const) {
|
||||
return result_type;
|
||||
}
|
||||
|
||||
ConstExprValue *other_const_val;
|
||||
if (cond_bool_val) {
|
||||
other_const_val = &get_resolved_expr(*then_node)->const_val;
|
||||
} else {
|
||||
other_const_val = &get_resolved_expr(*else_node)->const_val;
|
||||
}
|
||||
if (!other_const_val->ok) {
|
||||
return result_type;
|
||||
}
|
||||
|
||||
ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
|
||||
*const_val = *other_const_val;
|
||||
return result_type;
|
||||
}
|
||||
|
||||
static TypeTableEntry *analyze_if_bool_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
|
||||
TypeTableEntry *expected_type, AstNode *node)
|
||||
{
|
||||
analyze_expression(g, import, context, g->builtin_types.entry_bool, node->data.if_bool_expr.condition);
|
||||
AstNode **cond = &node->data.if_bool_expr.condition;
|
||||
TypeTableEntry *cond_type = analyze_expression(g, import, context, g->builtin_types.entry_bool, *cond);
|
||||
|
||||
return analyze_if_then_else(g, import, context, expected_type,
|
||||
node->data.if_bool_expr.then_block,
|
||||
node->data.if_bool_expr.else_node,
|
||||
node);
|
||||
if (cond_type->id == TypeTableEntryIdInvalid) {
|
||||
return cond_type;
|
||||
}
|
||||
|
||||
ConstExprValue *cond_val = &get_resolved_expr(*cond)->const_val;
|
||||
if (cond_val->ok && !cond_val->depends_on_compile_var) {
|
||||
const char *str_val = cond_val->data.x_bool ? "true" : "false";
|
||||
add_node_error(g, first_executing_node(*cond),
|
||||
buf_sprintf("condition is always %s; unnecessary if statement", str_val));
|
||||
}
|
||||
|
||||
bool cond_is_const = cond_val->ok;
|
||||
bool cond_bool_val = cond_val->data.x_bool;
|
||||
|
||||
AstNode **then_node = &node->data.if_bool_expr.then_block;
|
||||
AstNode **else_node = &node->data.if_bool_expr.else_node;
|
||||
|
||||
return analyze_if(g, import, context, expected_type, node,
|
||||
then_node, else_node, cond_is_const, cond_bool_val);
|
||||
}
|
||||
|
||||
static TypeTableEntry *analyze_if_var_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
|
||||
static TypeTableEntry *analyze_if_var_expr(CodeGen *g, ImportTableEntry *import, BlockContext *parent_context,
|
||||
TypeTableEntry *expected_type, AstNode *node)
|
||||
{
|
||||
assert(node->type == NodeTypeIfVarExpr);
|
||||
|
||||
BlockContext *child_context = new_block_context(node, context);
|
||||
BlockContext *child_context = new_block_context(node, parent_context);
|
||||
|
||||
analyze_variable_declaration_raw(g, import, child_context, node, &node->data.if_var_expr.var_decl, true);
|
||||
VariableTableEntry *var = node->data.if_var_expr.var_decl.variable;
|
||||
if (var->type->id == TypeTableEntryIdInvalid) {
|
||||
return g->builtin_types.entry_invalid;
|
||||
}
|
||||
AstNode *var_expr_node = node->data.if_var_expr.var_decl.expr;
|
||||
ConstExprValue *var_const_val = &get_resolved_expr(var_expr_node)->const_val;
|
||||
bool cond_is_const = var_const_val->ok;
|
||||
bool cond_bool_val = cond_is_const ? (var_const_val->data.x_maybe != nullptr) : false;
|
||||
|
||||
return analyze_if_then_else(g, import, child_context, expected_type,
|
||||
node->data.if_var_expr.then_block, node->data.if_var_expr.else_node, node);
|
||||
|
||||
AstNode **then_node = &node->data.if_var_expr.then_block;
|
||||
AstNode **else_node = &node->data.if_var_expr.else_node;
|
||||
|
||||
return analyze_if(g, import, child_context, expected_type,
|
||||
node, then_node, else_node, cond_is_const, cond_bool_val);
|
||||
}
|
||||
|
||||
static bool int_type_depends_on_compile_var(CodeGen *g, TypeTableEntry *int_type) {
|
||||
assert(int_type->id == TypeTableEntryIdInt);
|
||||
|
||||
for (int i = 0; i < CIntTypeCount; i += 1) {
|
||||
if (int_type == g->builtin_types.entry_c_int[i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static TypeTableEntry *analyze_min_max_value(CodeGen *g, ImportTableEntry *import, BlockContext *context,
|
||||
@ -3504,6 +3574,7 @@ static TypeTableEntry *analyze_min_max_value(CodeGen *g, ImportTableEntry *impor
|
||||
} else if (type_entry->id == TypeTableEntryIdInt) {
|
||||
ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
|
||||
const_val->ok = true;
|
||||
const_val->depends_on_compile_var = int_type_depends_on_compile_var(g, type_entry);
|
||||
if (is_max) {
|
||||
if (type_entry->data.integral.is_signed) {
|
||||
int64_t val;
|
||||
@ -3558,7 +3629,7 @@ static TypeTableEntry *analyze_min_max_value(CodeGen *g, ImportTableEntry *impor
|
||||
zig_panic("TODO analyze_min_max_value float");
|
||||
return type_entry;
|
||||
} else if (type_entry->id == TypeTableEntryIdBool) {
|
||||
return resolve_expr_const_val_as_bool(g, node, is_max);
|
||||
return resolve_expr_const_val_as_bool(g, node, is_max, false);
|
||||
} else {
|
||||
add_node_error(g, node,
|
||||
buf_sprintf(err_format, buf_ptr(&type_entry->name)));
|
||||
@ -3573,6 +3644,8 @@ static void eval_const_expr_implicit_cast(CodeGen *g, AstNode *node, AstNode *ex
|
||||
if (!other_val->ok) {
|
||||
return;
|
||||
}
|
||||
const_val->depends_on_compile_var = other_val->depends_on_compile_var;
|
||||
|
||||
assert(other_val != const_val);
|
||||
switch (node->data.fn_call_expr.cast_op) {
|
||||
case CastOpNoCast:
|
||||
@ -4132,11 +4205,11 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry
|
||||
const_val->depends_on_compile_var = true;
|
||||
|
||||
if (buf_eql_str(&var_name, "is_big_endian")) {
|
||||
return resolve_expr_const_val_as_bool(g, node, g->is_big_endian);
|
||||
return resolve_expr_const_val_as_bool(g, node, g->is_big_endian, true);
|
||||
} else if (buf_eql_str(&var_name, "is_release")) {
|
||||
return resolve_expr_const_val_as_bool(g, node, g->is_release_build);
|
||||
return resolve_expr_const_val_as_bool(g, node, g->is_release_build, true);
|
||||
} else if (buf_eql_str(&var_name, "is_test")) {
|
||||
return resolve_expr_const_val_as_bool(g, node, g->is_test_build);
|
||||
return resolve_expr_const_val_as_bool(g, node, g->is_test_build, true);
|
||||
} else {
|
||||
add_node_error(g, *str_node,
|
||||
buf_sprintf("unrecognized compile variable: '%s'", buf_ptr(&var_name)));
|
||||
@ -4353,7 +4426,7 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo
|
||||
}
|
||||
|
||||
bool answer = !target_const_val->data.x_bool;
|
||||
return resolve_expr_const_val_as_bool(g, node, answer);
|
||||
return resolve_expr_const_val_as_bool(g, node, answer, target_const_val->depends_on_compile_var);
|
||||
}
|
||||
case PrefixOpBinNot:
|
||||
{
|
||||
@ -4390,6 +4463,7 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo
|
||||
}
|
||||
ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
|
||||
const_val->ok = true;
|
||||
const_val->depends_on_compile_var = target_const_val->depends_on_compile_var;
|
||||
bignum_negate(&const_val->data.x_bignum, &target_const_val->data.x_bignum);
|
||||
return expr_type;
|
||||
} else {
|
||||
@ -4880,7 +4954,7 @@ static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import,
|
||||
node->data.char_literal.value);
|
||||
break;
|
||||
case NodeTypeBoolLiteral:
|
||||
return_type = resolve_expr_const_val_as_bool(g, node, node->data.bool_literal.value);
|
||||
return_type = resolve_expr_const_val_as_bool(g, node, node->data.bool_literal.value, false);
|
||||
break;
|
||||
case NodeTypeNullLiteral:
|
||||
return_type = analyze_null_literal_expr(g, import, context, expected_type, node);
|
||||
|
||||
119
src/codegen.cpp
119
src/codegen.cpp
@ -1737,81 +1737,60 @@ static LLVMValueRef gen_return_expr(CodeGen *g, AstNode *node) {
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
static LLVMValueRef gen_defer(CodeGen *g, AstNode *node) {
|
||||
assert(node->type == NodeTypeDefer);
|
||||
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static LLVMValueRef gen_if_bool_expr_raw(CodeGen *g, AstNode *source_node, LLVMValueRef cond_value,
|
||||
AstNode *then_node, AstNode *else_node)
|
||||
{
|
||||
assert(then_node);
|
||||
assert(else_node);
|
||||
|
||||
TypeTableEntry *then_type = get_expr_type(then_node);
|
||||
bool use_expr_value = (then_type->id != TypeTableEntryIdUnreachable &&
|
||||
then_type->id != TypeTableEntryIdVoid);
|
||||
TypeTableEntry *else_type = get_expr_type(else_node);
|
||||
|
||||
if (else_node) {
|
||||
LLVMBasicBlockRef then_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "Then");
|
||||
LLVMBasicBlockRef else_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "Else");
|
||||
|
||||
LLVMBasicBlockRef endif_block;
|
||||
bool then_endif_reachable = get_expr_type(then_node)->id != TypeTableEntryIdUnreachable;
|
||||
bool else_endif_reachable = get_expr_type(else_node)->id != TypeTableEntryIdUnreachable;
|
||||
if (then_endif_reachable || else_endif_reachable) {
|
||||
endif_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "EndIf");
|
||||
}
|
||||
|
||||
LLVMBuildCondBr(g->builder, cond_value, then_block, else_block);
|
||||
|
||||
LLVMPositionBuilderAtEnd(g->builder, then_block);
|
||||
LLVMValueRef then_expr_result = gen_expr(g, then_node);
|
||||
if (then_endif_reachable) {
|
||||
LLVMBuildBr(g->builder, endif_block);
|
||||
}
|
||||
LLVMBasicBlockRef after_then_block = LLVMGetInsertBlock(g->builder);
|
||||
|
||||
LLVMPositionBuilderAtEnd(g->builder, else_block);
|
||||
LLVMValueRef else_expr_result = gen_expr(g, else_node);
|
||||
if (else_endif_reachable) {
|
||||
LLVMBuildBr(g->builder, endif_block);
|
||||
}
|
||||
LLVMBasicBlockRef after_else_block = LLVMGetInsertBlock(g->builder);
|
||||
|
||||
if (then_endif_reachable || else_endif_reachable) {
|
||||
LLVMPositionBuilderAtEnd(g->builder, endif_block);
|
||||
if (use_expr_value) {
|
||||
LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMTypeOf(then_expr_result), "");
|
||||
LLVMValueRef incoming_values[2] = {then_expr_result, else_expr_result};
|
||||
LLVMBasicBlockRef incoming_blocks[2] = {after_then_block, after_else_block};
|
||||
LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2);
|
||||
|
||||
return phi;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
assert(!use_expr_value || then_type->id == TypeTableEntryIdErrorUnion);
|
||||
bool use_then_value = type_has_bits(then_type);
|
||||
bool use_else_value = type_has_bits(else_type);
|
||||
|
||||
LLVMBasicBlockRef then_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "Then");
|
||||
LLVMBasicBlockRef endif_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "EndIf");
|
||||
LLVMBasicBlockRef else_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "Else");
|
||||
|
||||
LLVMBuildCondBr(g->builder, cond_value, then_block, endif_block);
|
||||
LLVMBasicBlockRef endif_block;
|
||||
bool then_endif_reachable = then_type->id != TypeTableEntryIdUnreachable;
|
||||
bool else_endif_reachable = else_type->id != TypeTableEntryIdUnreachable;
|
||||
if (then_endif_reachable || else_endif_reachable) {
|
||||
endif_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "EndIf");
|
||||
}
|
||||
|
||||
LLVMBuildCondBr(g->builder, cond_value, then_block, else_block);
|
||||
|
||||
LLVMPositionBuilderAtEnd(g->builder, then_block);
|
||||
gen_expr(g, then_node);
|
||||
if (get_expr_type(then_node)->id != TypeTableEntryIdUnreachable)
|
||||
LLVMValueRef then_expr_result = gen_expr(g, then_node);
|
||||
if (then_endif_reachable) {
|
||||
LLVMBuildBr(g->builder, endif_block);
|
||||
|
||||
LLVMPositionBuilderAtEnd(g->builder, endif_block);
|
||||
|
||||
if (use_expr_value) {
|
||||
return LLVMConstNull(g->err_tag_type->type_ref);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
LLVMBasicBlockRef after_then_block = LLVMGetInsertBlock(g->builder);
|
||||
|
||||
LLVMPositionBuilderAtEnd(g->builder, else_block);
|
||||
LLVMValueRef else_expr_result = gen_expr(g, else_node);
|
||||
if (else_endif_reachable) {
|
||||
LLVMBuildBr(g->builder, endif_block);
|
||||
}
|
||||
LLVMBasicBlockRef after_else_block = LLVMGetInsertBlock(g->builder);
|
||||
|
||||
if (then_endif_reachable || else_endif_reachable) {
|
||||
LLVMPositionBuilderAtEnd(g->builder, endif_block);
|
||||
if (use_then_value && use_else_value) {
|
||||
LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMTypeOf(then_expr_result), "");
|
||||
LLVMValueRef incoming_values[2] = {then_expr_result, else_expr_result};
|
||||
LLVMBasicBlockRef incoming_blocks[2] = {after_then_block, after_else_block};
|
||||
LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2);
|
||||
return phi;
|
||||
} else if (use_then_value) {
|
||||
return then_expr_result;
|
||||
} else if (use_else_value) {
|
||||
return else_expr_result;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static LLVMValueRef gen_if_bool_expr(CodeGen *g, AstNode *node) {
|
||||
@ -1866,14 +1845,6 @@ static LLVMValueRef gen_if_var_expr(CodeGen *g, AstNode *node) {
|
||||
return return_value;
|
||||
}
|
||||
|
||||
//static int block_exit_path_count(BlockContext *block_context) {
|
||||
// int sum = 0;
|
||||
// for (int i = 0; i < BlockExitPathCount; i += 1) {
|
||||
// sum += block_context->block_exit_paths[i] ? 1 : 0;
|
||||
// }
|
||||
// return sum;
|
||||
//}
|
||||
|
||||
static LLVMValueRef gen_block(CodeGen *g, AstNode *block_node, TypeTableEntry *implicit_return_type) {
|
||||
assert(block_node->type == NodeTypeBlock);
|
||||
|
||||
@ -2553,7 +2524,8 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
|
||||
case NodeTypeReturnExpr:
|
||||
return gen_return_expr(g, node);
|
||||
case NodeTypeDefer:
|
||||
return gen_defer(g, node);
|
||||
// nothing to do
|
||||
return nullptr;
|
||||
case NodeTypeVariableDeclaration:
|
||||
return gen_var_decl_expr(g, node);
|
||||
case NodeTypePrefixOpExpr:
|
||||
@ -3191,6 +3163,7 @@ static const CIntTypeInfo c_int_type_infos[] = {
|
||||
|
||||
static int get_c_type_size_in_bits(CodeGen *g, CIntType id) {
|
||||
// TODO other architectures besides x86_64
|
||||
// other operating systems besides linux
|
||||
switch (id) {
|
||||
case CIntTypeShort:
|
||||
case CIntTypeUShort:
|
||||
@ -3203,6 +3176,8 @@ static int get_c_type_size_in_bits(CodeGen *g, CIntType id) {
|
||||
case CIntTypeLongLong:
|
||||
case CIntTypeULongLong:
|
||||
return 64;
|
||||
case CIntTypeCount:
|
||||
zig_unreachable();
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
@ -16,6 +16,7 @@ pub fn run_tests() -> %void {
|
||||
%%stderr.print_str(" ");
|
||||
%%stderr.print_str(test_fn.name);
|
||||
%%stderr.print_str("...");
|
||||
%%stderr.flush();
|
||||
|
||||
test_fn.func();
|
||||
|
||||
|
||||
@ -225,26 +225,6 @@ pub fn foo_function() -> bool {
|
||||
)SOURCE");
|
||||
}
|
||||
|
||||
add_simple_case("if statements", R"SOURCE(
|
||||
import "std.zig";
|
||||
|
||||
pub fn main(args: [][]u8) -> %void {
|
||||
if (1 != 0) {
|
||||
%%stdout.printf("1 is true\n");
|
||||
} else {
|
||||
%%stdout.printf("1 is false\n");
|
||||
}
|
||||
if (0 != 0) {
|
||||
%%stdout.printf("0 is true\n");
|
||||
} else if (1 - 1 != 0) {
|
||||
%%stdout.printf("1 - 1 is true\n");
|
||||
}
|
||||
if (!(0 != 0)) {
|
||||
%%stdout.printf("!0 is true\n");
|
||||
}
|
||||
}
|
||||
)SOURCE", "1 is true\n!0 is true\n");
|
||||
|
||||
add_simple_case("params", R"SOURCE(
|
||||
import "std.zig";
|
||||
|
||||
@ -259,46 +239,6 @@ pub fn main(args: [][]u8) -> %void {
|
||||
}
|
||||
)SOURCE", "pass\n");
|
||||
|
||||
add_simple_case("local variables", R"SOURCE(
|
||||
import "std.zig";
|
||||
|
||||
pub fn main(args: [][]u8) -> %void {
|
||||
const a : i32 = 1;
|
||||
const b = i32(2);
|
||||
if (a + b == 3) {
|
||||
%%stdout.printf("OK\n");
|
||||
}
|
||||
}
|
||||
)SOURCE", "OK\n");
|
||||
|
||||
add_simple_case("bool literals", R"SOURCE(
|
||||
import "std.zig";
|
||||
|
||||
pub fn main(args: [][]u8) -> %void {
|
||||
if (true) { %%stdout.printf("OK 1\n"); }
|
||||
if (false) { %%stdout.printf("BAD 1\n"); }
|
||||
if (!true) { %%stdout.printf("BAD 2\n"); }
|
||||
if (!false) { %%stdout.printf("OK 2\n"); }
|
||||
}
|
||||
)SOURCE", "OK 1\nOK 2\n");
|
||||
|
||||
add_simple_case("separate block scopes", R"SOURCE(
|
||||
import "std.zig";
|
||||
|
||||
pub fn main(args: [][]u8) -> %void {
|
||||
if (true) {
|
||||
const no_conflict : i32 = 5;
|
||||
if (no_conflict == 5) { %%stdout.printf("OK 1\n"); }
|
||||
}
|
||||
|
||||
const c = {
|
||||
const no_conflict = i32(10);
|
||||
no_conflict
|
||||
};
|
||||
if (c == 10) { %%stdout.printf("OK 2\n"); }
|
||||
}
|
||||
)SOURCE", "OK 1\nOK 2\n");
|
||||
|
||||
add_simple_case("void parameters", R"SOURCE(
|
||||
import "std.zig";
|
||||
|
||||
@ -314,48 +254,6 @@ fn void_fun(a : i32, b : void, c : i32) {
|
||||
}
|
||||
)SOURCE", "OK\n");
|
||||
|
||||
add_simple_case("void struct fields", R"SOURCE(
|
||||
import "std.zig";
|
||||
struct Foo {
|
||||
a : void,
|
||||
b : i32,
|
||||
c : void,
|
||||
}
|
||||
pub fn main(args: [][]u8) -> %void {
|
||||
const foo = Foo {
|
||||
.a = void{},
|
||||
.b = 1,
|
||||
.c = void{},
|
||||
};
|
||||
if (foo.b != 1) {
|
||||
%%stdout.printf("BAD\n");
|
||||
}
|
||||
if (@sizeof(Foo) != 4) {
|
||||
%%stdout.printf("BAD\n");
|
||||
}
|
||||
%%stdout.printf("OK\n");
|
||||
}
|
||||
|
||||
)SOURCE", "OK\n");
|
||||
|
||||
add_simple_case("void arrays", R"SOURCE(
|
||||
import "std.zig";
|
||||
|
||||
pub fn main(args: [][]u8) -> %void {
|
||||
var array: [4]void = undefined;
|
||||
array[0] = void{};
|
||||
array[1] = array[2];
|
||||
if (@sizeof(@typeof(array)) != 0) {
|
||||
%%stdout.printf("BAD sizeof\n");
|
||||
}
|
||||
if (array.len != 4) {
|
||||
%%stdout.printf("BAD len\n");
|
||||
}
|
||||
%%stdout.printf("OK\n");
|
||||
}
|
||||
)SOURCE", "OK\n");
|
||||
|
||||
|
||||
add_simple_case("mutable local variables", R"SOURCE(
|
||||
import "std.zig";
|
||||
|
||||
@ -414,27 +312,6 @@ pub fn main(args: [][]u8) -> %void {
|
||||
)SOURCE", "Hello, world!\n");
|
||||
|
||||
|
||||
add_simple_case("a + b + c", R"SOURCE(
|
||||
import "std.zig";
|
||||
|
||||
pub fn main(args: [][]u8) -> %void {
|
||||
if (false || false || false) { %%stdout.printf("BAD 1\n"); }
|
||||
if (true && true && false) { %%stdout.printf("BAD 2\n"); }
|
||||
if (1 | 2 | 4 != 7) { %%stdout.printf("BAD 3\n"); }
|
||||
if (3 ^ 6 ^ 8 != 13) { %%stdout.printf("BAD 4\n"); }
|
||||
if (7 & 14 & 28 != 4) { %%stdout.printf("BAD 5\n"); }
|
||||
if (9 << 1 << 2 != 9 << 3) { %%stdout.printf("BAD 6\n"); }
|
||||
if (90 >> 1 >> 2 != 90 >> 3) { %%stdout.printf("BAD 7\n"); }
|
||||
if (100 - 1 + 1000 != 1099) { %%stdout.printf("BAD 8\n"); }
|
||||
if (5 * 4 / 2 % 3 != 1) { %%stdout.printf("BAD 9\n"); }
|
||||
if (i32(i32(5)) != 5) { %%stdout.printf("BAD 10\n"); }
|
||||
if (!!false) { %%stdout.printf("BAD 11\n"); }
|
||||
if (i32(7) != --(i32(7))) { %%stdout.printf("BAD 12\n"); }
|
||||
|
||||
%%stdout.printf("OK\n");
|
||||
}
|
||||
)SOURCE", "OK\n");
|
||||
|
||||
add_simple_case("short circuit", R"SOURCE(
|
||||
import "std.zig";
|
||||
|
||||
@ -729,39 +606,6 @@ pub fn main(args: [][]u8) -> %void {
|
||||
}
|
||||
)SOURCE", "loop\nloop\nloop\nloop\n");
|
||||
|
||||
add_simple_case("maybe type", R"SOURCE(
|
||||
import "std.zig";
|
||||
pub fn main(args: [][]u8) -> %void {
|
||||
const x : ?bool = true;
|
||||
|
||||
if (const y ?= x) {
|
||||
if (y) {
|
||||
%%stdout.printf("x is true\n");
|
||||
} else {
|
||||
%%stdout.printf("x is false\n");
|
||||
}
|
||||
} else {
|
||||
%%stdout.printf("x is none\n");
|
||||
}
|
||||
|
||||
const next_x : ?i32 = null;
|
||||
|
||||
const z = next_x ?? 1234;
|
||||
|
||||
if (z != 1234) {
|
||||
%%stdout.printf("BAD\n");
|
||||
}
|
||||
|
||||
const final_x : ?i32 = 13;
|
||||
|
||||
const num = final_x ?? unreachable{};
|
||||
|
||||
if (num != 13) {
|
||||
%%stdout.printf("BAD\n");
|
||||
}
|
||||
}
|
||||
)SOURCE", "x is true\n");
|
||||
|
||||
add_simple_case("implicit cast after unreachable", R"SOURCE(
|
||||
import "std.zig";
|
||||
pub fn main(args: [][]u8) -> %void {
|
||||
@ -971,73 +815,6 @@ fn print_ok(val: @typeof(x)) -> @typeof(foo) {
|
||||
const foo : i32 = 0;
|
||||
)SOURCE", "OK\n");
|
||||
|
||||
add_simple_case("enum type", R"SOURCE(
|
||||
import "std.zig";
|
||||
|
||||
struct Point {
|
||||
x: u64,
|
||||
y: u64,
|
||||
}
|
||||
|
||||
enum Foo {
|
||||
One: i32,
|
||||
Two: Point,
|
||||
Three: void,
|
||||
}
|
||||
|
||||
enum Bar {
|
||||
A,
|
||||
B,
|
||||
C,
|
||||
D,
|
||||
}
|
||||
|
||||
pub fn main(args: [][]u8) -> %void {
|
||||
const foo1 = Foo.One(13);
|
||||
const foo2 = Foo.Two(Point { .x = 1234, .y = 5678, });
|
||||
const bar = Bar.B;
|
||||
|
||||
if (bar != Bar.B) {
|
||||
%%stdout.printf("BAD 1\n");
|
||||
}
|
||||
|
||||
if (@member_count(Foo) != 3) {
|
||||
%%stdout.printf("BAD 2\n");
|
||||
}
|
||||
|
||||
if (@member_count(Bar) != 4) {
|
||||
%%stdout.printf("BAD 3\n");
|
||||
}
|
||||
|
||||
if (@sizeof(Foo) != 24) {
|
||||
%%stdout.printf("BAD 4\n");
|
||||
}
|
||||
if (@sizeof(Bar) != 1) {
|
||||
%%stdout.printf("BAD 5\n");
|
||||
}
|
||||
|
||||
%%stdout.printf("OK\n");
|
||||
}
|
||||
)SOURCE", "OK\n");
|
||||
|
||||
add_simple_case("array literal", R"SOURCE(
|
||||
import "std.zig";
|
||||
|
||||
pub fn main(args: [][]u8) -> %void {
|
||||
const HEX_MULT = []u16{4096, 256, 16, 1};
|
||||
|
||||
if (HEX_MULT.len != 4) {
|
||||
%%stdout.printf("BAD\n");
|
||||
}
|
||||
|
||||
if (HEX_MULT[1] != 256) {
|
||||
%%stdout.printf("BAD\n");
|
||||
}
|
||||
|
||||
%%stdout.printf("OK\n");
|
||||
}
|
||||
)SOURCE", "OK\n");
|
||||
|
||||
add_simple_case("nested arrays", R"SOURCE(
|
||||
import "std.zig";
|
||||
|
||||
@ -1092,23 +869,6 @@ fn fn3() -> u32 {7}
|
||||
fn fn4() -> u32 {8}
|
||||
)SOURCE", "5\n6\n7\n8\n");
|
||||
|
||||
add_simple_case("const number literal", R"SOURCE(
|
||||
import "std.zig";
|
||||
|
||||
const ten = 10;
|
||||
|
||||
pub fn main(args: [][]u8) -> %void {
|
||||
const one = 1;
|
||||
const eleven = ten + one;
|
||||
|
||||
if (eleven != 11) {
|
||||
%%stdout.printf("BAD\n");
|
||||
}
|
||||
|
||||
%%stdout.printf("OK\n");
|
||||
}
|
||||
)SOURCE", "OK\n");
|
||||
|
||||
add_simple_case("statically initialized struct", R"SOURCE(
|
||||
import "std.zig";
|
||||
struct Foo {
|
||||
@ -1139,21 +899,6 @@ pub fn main(args: [][]u8) -> %void {
|
||||
}
|
||||
)SOURCE", "OK\n");
|
||||
|
||||
add_simple_case("error values", R"SOURCE(
|
||||
import "std.zig";
|
||||
error err1;
|
||||
error err2;
|
||||
pub fn main(args: [][]u8) -> %void {
|
||||
const a = i32(error.err1);
|
||||
const b = i32(error.err2);
|
||||
if (a == b) {
|
||||
%%stdout.printf("BAD\n");
|
||||
}
|
||||
|
||||
%%stdout.printf("OK\n");
|
||||
}
|
||||
)SOURCE", "OK\n");
|
||||
|
||||
add_simple_case("return with implicit cast from while loop", R"SOURCE(
|
||||
import "std.zig";
|
||||
pub fn main(args: [][]u8) -> %void {
|
||||
@ -1988,6 +1733,13 @@ struct Foo {
|
||||
}
|
||||
fn get() -> isize { 1 }
|
||||
)SOURCE", 1, ".tmp_source.zig:3:9: error: unable to evaluate constant expression");
|
||||
|
||||
|
||||
add_compile_fail_case("unnecessary if statement", R"SOURCE(
|
||||
fn f() {
|
||||
if (true) { }
|
||||
}
|
||||
)SOURCE", 1, ".tmp_source.zig:3:9: error: condition is always true; unnecessary if statement");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@ -5,7 +5,6 @@ fn empty_function() {}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* multi line doc comment
|
||||
*/
|
||||
@ -19,6 +18,200 @@ fn comments() {
|
||||
fn comments_f1(s: []u8) {}
|
||||
|
||||
|
||||
#attribute("test")
|
||||
fn if_statements() {
|
||||
should_be_equal(1, 1);
|
||||
first_eql_third(2, 1, 2);
|
||||
}
|
||||
fn should_be_equal(a: i32, b: i32) {
|
||||
if (a != b) {
|
||||
unreachable{};
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
fn first_eql_third(a: i32, b: i32, c: i32) {
|
||||
if (a == b) {
|
||||
unreachable{};
|
||||
} else if (b == c) {
|
||||
unreachable{};
|
||||
} else if (a == c) {
|
||||
return;
|
||||
} else {
|
||||
unreachable{};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#attribute("test")
|
||||
fn local_variables() {
|
||||
test_loc_vars(2);
|
||||
}
|
||||
fn test_loc_vars(b: i32) {
|
||||
const a: i32 = 1;
|
||||
if (a + b != 3) unreachable{};
|
||||
}
|
||||
|
||||
#attribute("test")
|
||||
fn bool_literals() {
|
||||
should_be_true(true);
|
||||
should_be_false(false);
|
||||
}
|
||||
fn should_be_true(b: bool) {
|
||||
if (!b) unreachable{};
|
||||
}
|
||||
fn should_be_false(b: bool) {
|
||||
if (b) unreachable{};
|
||||
}
|
||||
|
||||
|
||||
#attribute("test")
|
||||
fn separate_block_scopes() {
|
||||
{
|
||||
const no_conflict : i32 = 5;
|
||||
assert(no_conflict == 5);
|
||||
}
|
||||
|
||||
const c = {
|
||||
const no_conflict = i32(10);
|
||||
no_conflict
|
||||
};
|
||||
assert(c == 10);
|
||||
}
|
||||
|
||||
|
||||
#attribute("test")
|
||||
fn void_struct_fields() {
|
||||
const foo = VoidStructFieldsFoo {
|
||||
.a = void{},
|
||||
.b = 1,
|
||||
.c = void{},
|
||||
};
|
||||
assert(foo.b == 1);
|
||||
assert(@sizeof(VoidStructFieldsFoo) == 4);
|
||||
}
|
||||
struct VoidStructFieldsFoo {
|
||||
a : void,
|
||||
b : i32,
|
||||
c : void,
|
||||
}
|
||||
|
||||
|
||||
#attribute("test")
|
||||
fn void_arrays() {
|
||||
var array: [4]void = undefined;
|
||||
array[0] = void{};
|
||||
array[1] = array[2];
|
||||
assert(@sizeof(@typeof(array)) == 0);
|
||||
assert(array.len == 4);
|
||||
}
|
||||
|
||||
|
||||
#attribute("test")
|
||||
fn three_expr_in_a_row() {
|
||||
assert_false(false || false || false);
|
||||
assert_false(true && true && false);
|
||||
assert_false(1 | 2 | 4 != 7);
|
||||
assert_false(3 ^ 6 ^ 8 != 13);
|
||||
assert_false(7 & 14 & 28 != 4);
|
||||
assert_false(9 << 1 << 2 != 9 << 3);
|
||||
assert_false(90 >> 1 >> 2 != 90 >> 3);
|
||||
assert_false(100 - 1 + 1000 != 1099);
|
||||
assert_false(5 * 4 / 2 % 3 != 1);
|
||||
assert_false(i32(i32(5)) != 5);
|
||||
assert_false(!!false);
|
||||
assert_false(i32(7) != --(i32(7)));
|
||||
}
|
||||
fn assert_false(b: bool) {
|
||||
assert(!b);
|
||||
}
|
||||
|
||||
|
||||
#attribute("test")
|
||||
fn maybe_type() {
|
||||
const x : ?bool = true;
|
||||
|
||||
if (const y ?= x) {
|
||||
if (y) {
|
||||
// OK
|
||||
} else {
|
||||
unreachable{};
|
||||
}
|
||||
} else {
|
||||
unreachable{};
|
||||
}
|
||||
|
||||
const next_x : ?i32 = null;
|
||||
|
||||
const z = next_x ?? 1234;
|
||||
|
||||
assert(z == 1234);
|
||||
|
||||
const final_x : ?i32 = 13;
|
||||
|
||||
const num = final_x ?? unreachable{};
|
||||
|
||||
assert(num == 13);
|
||||
}
|
||||
|
||||
|
||||
#attribute("test")
|
||||
fn enum_type() {
|
||||
const foo1 = EnumTypeFoo.One(13);
|
||||
const foo2 = EnumTypeFoo.Two(EnumType { .x = 1234, .y = 5678, });
|
||||
const bar = EnumTypeBar.B;
|
||||
|
||||
assert(bar == EnumTypeBar.B);
|
||||
assert(@member_count(EnumTypeFoo) == 3);
|
||||
assert(@member_count(EnumTypeBar) == 4);
|
||||
assert(@sizeof(EnumTypeFoo) == 24);
|
||||
assert(@sizeof(EnumTypeBar) == 1);
|
||||
}
|
||||
struct EnumType {
|
||||
x: u64,
|
||||
y: u64,
|
||||
}
|
||||
enum EnumTypeFoo {
|
||||
One: i32,
|
||||
Two: EnumType,
|
||||
Three: void,
|
||||
}
|
||||
enum EnumTypeBar {
|
||||
A,
|
||||
B,
|
||||
C,
|
||||
D,
|
||||
}
|
||||
|
||||
|
||||
#attribute("test")
|
||||
fn array_literal() {
|
||||
const HEX_MULT = []u16{4096, 256, 16, 1};
|
||||
|
||||
assert(HEX_MULT.len == 4);
|
||||
assert(HEX_MULT[1] == 256);
|
||||
}
|
||||
|
||||
|
||||
#attribute("test")
|
||||
fn const_number_literal() {
|
||||
const one = 1;
|
||||
const eleven = ten + one;
|
||||
|
||||
assert(eleven == 11);
|
||||
}
|
||||
const ten = 10;
|
||||
|
||||
|
||||
#attribute("test")
|
||||
fn error_values() {
|
||||
const a = i32(error.err1);
|
||||
const b = i32(error.err2);
|
||||
assert(a != b);
|
||||
}
|
||||
error err1;
|
||||
error err2;
|
||||
|
||||
|
||||
|
||||
#attribute("test")
|
||||
@ -42,11 +235,14 @@ fn call_struct_field(foo: Foo) -> i32 {
|
||||
|
||||
#attribute("test")
|
||||
fn redefinition_of_error_values_allowed() {
|
||||
if (error.AnError == error.SecondError) unreachable{}
|
||||
should_be_not_equal(error.AnError, error.SecondError);
|
||||
}
|
||||
error AnError;
|
||||
error AnError;
|
||||
error SecondError;
|
||||
fn should_be_not_equal(a: error, b: error) {
|
||||
if (a == b) unreachable{}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -98,14 +294,14 @@ fn continue_in_for_loop() {
|
||||
fn cast_bool_to_int() {
|
||||
const t = true;
|
||||
const f = false;
|
||||
if (i32(t) != i32(1)) unreachable{}
|
||||
if (i32(f) != i32(0)) unreachable{}
|
||||
assert(i32(t) == i32(1));
|
||||
assert(i32(f) == i32(0));
|
||||
non_const_cast_bool_to_int(t, f);
|
||||
}
|
||||
|
||||
fn non_const_cast_bool_to_int(t: bool, f: bool) {
|
||||
if (i32(t) != i32(1)) unreachable{}
|
||||
if (i32(f) != i32(0)) unreachable{}
|
||||
assert(i32(t) == i32(1));
|
||||
assert(i32(f) == i32(0));
|
||||
}
|
||||
|
||||
|
||||
@ -240,7 +436,7 @@ fn const_expr_eval_on_single_expr_blocks_fn(x: i32, b: bool) -> i32 {
|
||||
#attribute("test")
|
||||
fn builtin_const_eval() {
|
||||
const x : i32 = @const_eval(1 + 2 + 3);
|
||||
if (x != @const_eval(6)) unreachable{};
|
||||
assert(x == @const_eval(6));
|
||||
}
|
||||
|
||||
#attribute("test")
|
||||
@ -279,3 +475,10 @@ struct ArrayDotLenConstExpr {
|
||||
y: [@const_eval(some_array.len)]u8,
|
||||
}
|
||||
const some_array = []u8 {0, 1, 2, 3};
|
||||
|
||||
|
||||
|
||||
|
||||
fn assert(b: bool) {
|
||||
if (!b) unreachable{}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user