IR: generating a switch statement

This commit is contained in:
Andrew Kelley 2016-11-24 02:44:03 -05:00
parent a2257e4b81
commit 0c22358cc1
10 changed files with 334 additions and 447 deletions

View File

@ -65,6 +65,12 @@ compromises backward compatibility.
* Compiler exposes itself as a library.
* Support for all popular architectures and operating systems.
## Community
* IRC: `#zig` on Freenode.
* Reddit: [/r/zig](https://www.reddit.com/r/zig)
* Email list: [ziglang@googlegroups.com](https://groups.google.com/forum/#!forum/ziglang)
## Building
### Dependencies
@ -147,9 +153,3 @@ To fix this, you have 2 options:
* Compile Zig with the same compiler that LLVM was compiled with.
* Add `-DZIG_LLVM_OLD_CXX_ABI=yes` to the cmake configure line.
## Community
* IRC chat: `#zig` on Freenode.
* Reddit: [/r/zig](https://www.reddit.com/r/zig)
* Email list: [ziglang@googlegroups.com](https://groups.google.com/forum/#!forum/ziglang)

View File

@ -75,7 +75,7 @@ BlockExpression = IfExpression | Block | WhileExpression | ForExpression | Switc
SwitchExpression = "switch" "(" Expression ")" "{" many(SwitchProng) "}"
SwitchProng = (list(SwitchItem, ",") | "else") "=>" option("|" Symbol "|") Expression ","
SwitchProng = (list(SwitchItem, ",") | "else") "=>" option("|" option("*") Symbol "|") Expression ","
SwitchItem = Expression | (Expression "..." Expression)

View File

@ -560,18 +560,14 @@ struct AstNodeSwitchExpr {
// populated by semantic analyzer
Expr resolved_expr;
size_t const_chosen_prong_index;
};
struct AstNodeSwitchProng {
ZigList<AstNode *> items;
AstNode *var_symbol;
AstNode *expr;
// populated by semantic analyzer
BlockContext *block_context;
VariableTableEntry *var;
bool var_is_target_expr;
bool var_is_ptr;
bool any_items_are_range;
};
struct AstNodeSwitchRange {
@ -1426,6 +1422,8 @@ enum IrInstructionId {
IrInstructionIdBr,
IrInstructionIdCondBr,
IrInstructionIdSwitchBr,
IrInstructionIdSwitchVar,
IrInstructionIdSwitchTarget,
IrInstructionIdPhi,
IrInstructionIdUnOp,
IrInstructionIdBinOp,
@ -1509,6 +1507,19 @@ struct IrInstructionSwitchBr {
bool is_inline;
};
struct IrInstructionSwitchVar {
IrInstruction base;
IrInstruction *target_value_ptr;
IrInstruction *prong_value;
};
struct IrInstructionSwitchTarget {
IrInstruction base;
IrInstruction *target_value_ptr;
};
struct IrInstructionPhi {
IrInstruction base;

View File

@ -727,6 +727,7 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
}
break;
}
case NodeTypeSwitchExpr:
case NodeTypeFnDecl:
case NodeTypeParamDecl:
case NodeTypeErrorValueDecl:
@ -737,7 +738,6 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
case NodeTypeUse:
case NodeTypeZeroesLiteral:
case NodeTypeForExpr:
case NodeTypeSwitchExpr:
case NodeTypeSwitchProng:
case NodeTypeSwitchRange:
case NodeTypeLabel:

View File

@ -1695,6 +1695,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
case IrInstructionIdCtz:
return ir_render_ctz(g, executable, (IrInstructionCtz *)instruction);
case IrInstructionIdSwitchBr:
case IrInstructionIdSwitchTarget:
case IrInstructionIdSwitchVar:
case IrInstructionIdPhi:
case IrInstructionIdContainerInitList:
case IrInstructionIdContainerInitFields:

View File

@ -114,6 +114,14 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionSwitchBr *) {
return IrInstructionIdSwitchBr;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionSwitchVar *) {
return IrInstructionIdSwitchVar;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionSwitchTarget *) {
return IrInstructionIdSwitchTarget;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionPhi *) {
return IrInstructionIdPhi;
}
@ -981,6 +989,51 @@ static IrInstruction *ir_build_ctz_from(IrBuilder *irb, IrInstruction *old_instr
return new_instruction;
}
static IrInstruction *ir_build_switch_br(IrBuilder *irb, AstNode *source_node, IrInstruction *target_value,
IrBasicBlock *else_block, size_t case_count, IrInstructionSwitchBrCase *cases, bool is_inline)
{
IrInstructionSwitchBr *instruction = ir_build_instruction<IrInstructionSwitchBr>(irb, source_node);
instruction->target_value = target_value;
instruction->else_block = else_block;
instruction->case_count = case_count;
instruction->cases = cases;
instruction->is_inline = is_inline;
ir_ref_instruction(target_value);
ir_ref_bb(else_block);
for (size_t i = 0; i < case_count; i += 1) {
ir_ref_instruction(cases[i].value);
ir_ref_bb(cases[i].block);
}
return &instruction->base;
}
static IrInstruction *ir_build_switch_target(IrBuilder *irb, AstNode *source_node,
IrInstruction *target_value_ptr)
{
IrInstructionSwitchTarget *instruction = ir_build_instruction<IrInstructionSwitchTarget>(irb, source_node);
instruction->target_value_ptr = target_value_ptr;
ir_ref_instruction(target_value_ptr);
return &instruction->base;
}
static IrInstruction *ir_build_switch_var(IrBuilder *irb, AstNode *source_node,
IrInstruction *target_value_ptr, IrInstruction *prong_value)
{
IrInstructionSwitchVar *instruction = ir_build_instruction<IrInstructionSwitchVar>(irb, source_node);
instruction->target_value_ptr = target_value_ptr;
instruction->prong_value = prong_value;
ir_ref_instruction(target_value_ptr);
ir_ref_instruction(prong_value);
return &instruction->base;
}
static void ir_gen_defers_for_block(IrBuilder *irb, BlockContext *inner_block, BlockContext *outer_block,
bool gen_error_defers, bool gen_maybe_defers)
{
@ -1047,7 +1100,7 @@ static VariableTableEntry *add_local_var(CodeGen *codegen, AstNode *node, BlockC
if (name) {
buf_init_from_buf(&variable_entry->name, name);
VariableTableEntry *existing_var = find_variable(codegen, node->block_context, name);
VariableTableEntry *existing_var = find_variable(codegen, scope, name);
if (existing_var && !existing_var->shadowable) {
ErrorMsg *msg = add_node_error(codegen, node,
buf_sprintf("redeclaration of variable '%s'", buf_ptr(name)));
@ -1061,7 +1114,7 @@ static VariableTableEntry *add_local_var(CodeGen *codegen, AstNode *node, BlockC
buf_sprintf("variable shadows type '%s'", buf_ptr(&type->name)));
variable_entry->type = codegen->builtin_types.entry_invalid;
} else {
AstNode *decl_node = find_decl(node->block_context, name);
AstNode *decl_node = find_decl(scope, name);
if (decl_node && decl_node->type != NodeTypeVariableDeclaration) {
ErrorMsg *msg = add_node_error(codegen, node,
buf_sprintf("redefinition of '%s'", buf_ptr(name)));
@ -1071,7 +1124,7 @@ static VariableTableEntry *add_local_var(CodeGen *codegen, AstNode *node, BlockC
}
}
node->block_context->var_table.put(&variable_entry->name, variable_entry);
scope->var_table.put(&variable_entry->name, variable_entry);
} else {
assert(is_shadowable);
// TODO replace _anon with @anon and make sure all tests still pass
@ -2052,8 +2105,11 @@ static IrInstruction *ir_gen_if_var_expr(IrBuilder *irb, AstNode *node) {
ir_set_cursor_at_end(irb, then_block);
IrInstruction *var_type = nullptr;
if (var_decl->type)
if (var_decl->type) {
var_type = ir_gen_node(irb, var_decl->type, node->block_context);
if (var_type == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
}
BlockContext *child_scope = new_block_context(node, node->block_context);
bool is_shadowable = false;
bool is_const = var_decl->is_const;
@ -2091,6 +2147,190 @@ static IrInstruction *ir_gen_if_var_expr(IrBuilder *irb, AstNode *node) {
return ir_build_phi(irb, node, 2, incoming_blocks, incoming_values);
}
static bool ir_gen_switch_prong_expr(IrBuilder *irb, AstNode *switch_node, AstNode *prong_node,
IrBasicBlock *end_block, bool is_inline, IrInstruction *target_value_ptr, IrInstruction *prong_value,
ZigList<IrBasicBlock *> incoming_blocks, ZigList<IrInstruction *> incoming_values)
{
assert(switch_node->type == NodeTypeSwitchExpr);
assert(prong_node->type == NodeTypeSwitchProng);
AstNode *expr_node = prong_node->data.switch_prong.expr;
AstNode *var_symbol_node = prong_node->data.switch_prong.var_symbol;
BlockContext *child_scope;
if (var_symbol_node) {
assert(var_symbol_node->type == NodeTypeSymbol);
Buf *var_name = var_symbol_node->data.symbol_expr.symbol;
bool var_is_ptr = prong_node->data.switch_prong.var_is_ptr;
child_scope = new_block_context(switch_node, switch_node->block_context);
bool is_shadowable = false;
bool is_const = true;
VariableTableEntry *var = ir_add_local_var(irb, var_symbol_node, child_scope,
var_name, is_const, is_const, is_shadowable, is_inline);
IrInstruction *var_value;
if (prong_value) {
IrInstruction *var_ptr_value = ir_build_switch_var(irb, var_symbol_node, target_value_ptr, prong_value);
var_value = var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, var_symbol_node, var_ptr_value);
} else {
var_value = var_is_ptr ? target_value_ptr : ir_build_load_ptr(irb, var_symbol_node, target_value_ptr);
}
IrInstruction *var_type = nullptr; // infer the type
ir_build_var_decl(irb, var_symbol_node, var, var_type, var_value);
} else {
child_scope = switch_node->block_context;
}
IrInstruction *expr_result = ir_gen_node(irb, expr_node, child_scope);
if (expr_result == irb->codegen->invalid_instruction)
return false;
ir_build_br(irb, switch_node, end_block, is_inline);
incoming_blocks.append(irb->current_basic_block);
incoming_values.append(expr_result);
return true;
}
static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, AstNode *node) {
assert(node->type == NodeTypeSwitchExpr);
AstNode *target_node = node->data.switch_expr.expr;
IrInstruction *target_value_ptr = ir_gen_node_extra(irb, target_node, node->block_context, LValPurposeAddressOf);
if (target_value_ptr == irb->codegen->invalid_instruction)
return target_value_ptr;
IrInstruction *target_value = ir_build_switch_target(irb, node, target_value_ptr);
IrBasicBlock *else_block = ir_build_basic_block(irb, "SwitchElse");
IrBasicBlock *end_block = ir_build_basic_block(irb, "SwitchEnd");
size_t prong_count = node->data.switch_expr.prongs.length;
ZigList<IrInstructionSwitchBrCase> cases = {0};
bool is_inline = (node->block_context->fn_entry == nullptr);
ZigList<IrInstruction *> incoming_values = {0};
ZigList<IrBasicBlock *> incoming_blocks = {0};
AstNode *else_prong = nullptr;
for (size_t prong_i = 0; prong_i < prong_count; prong_i += 1) {
AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i);
size_t prong_item_count = prong_node->data.switch_prong.items.length;
if (prong_item_count == 0) {
if (else_prong) {
ErrorMsg *msg = add_node_error(irb->codegen, prong_node,
buf_sprintf("multiple else prongs in switch expression"));
add_error_note(irb->codegen, msg, else_prong,
buf_sprintf("previous else prong is here"));
return irb->codegen->invalid_instruction;
}
else_prong = prong_node;
if (!ir_gen_switch_prong_expr(irb, node, prong_node, end_block,
is_inline, target_value_ptr, nullptr, incoming_blocks, incoming_values))
{
return irb->codegen->invalid_instruction;
}
} else {
if (prong_node->data.switch_prong.any_items_are_range) {
IrInstruction *ok_bit = nullptr;
AstNode *last_item_node = nullptr;
for (size_t item_i = 0; item_i < prong_item_count; item_i += 1) {
AstNode *item_node = prong_node->data.switch_prong.items.at(item_i);
last_item_node = item_node;
if (item_node->type == NodeTypeSwitchRange) {
AstNode *start_node = item_node->data.switch_range.start;
AstNode *end_node = item_node->data.switch_range.end;
IrInstruction *start_value = ir_gen_node(irb, start_node, node->block_context);
if (start_value == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
IrInstruction *end_value = ir_gen_node(irb, end_node, node->block_context);
if (end_value == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
IrInstruction *lower_range_ok = ir_build_bin_op(irb, item_node, IrBinOpCmpGreaterOrEq,
target_value, start_value);
IrInstruction *upper_range_ok = ir_build_bin_op(irb, item_node, IrBinOpCmpLessOrEq,
target_value, end_value);
IrInstruction *both_ok = ir_build_bin_op(irb, item_node, IrBinOpBoolAnd,
lower_range_ok, upper_range_ok);
if (ok_bit) {
ok_bit = ir_build_bin_op(irb, item_node, IrBinOpBoolAnd, both_ok, ok_bit);
} else {
ok_bit = both_ok;
}
} else {
IrInstruction *item_value = ir_gen_node(irb, item_node, node->block_context);
if (item_value == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
IrInstruction *cmp_ok = ir_build_bin_op(irb, item_node, IrBinOpCmpEq,
item_value, target_value);
if (ok_bit) {
ok_bit = ir_build_bin_op(irb, item_node, IrBinOpBoolAnd, cmp_ok, ok_bit);
} else {
ok_bit = cmp_ok;
}
}
}
IrBasicBlock *range_block_yes = ir_build_basic_block(irb, "SwitchRangeYes");
IrBasicBlock *range_block_no = ir_build_basic_block(irb, "SwitchRangeNo");
assert(ok_bit);
assert(last_item_node);
ir_build_cond_br(irb, last_item_node, ok_bit, range_block_yes, range_block_no, is_inline);
ir_set_cursor_at_end(irb, range_block_yes);
if (!ir_gen_switch_prong_expr(irb, node, prong_node, end_block,
is_inline, target_value_ptr, nullptr, incoming_blocks, incoming_values))
{
return irb->codegen->invalid_instruction;
}
ir_set_cursor_at_end(irb, range_block_no);
} else {
for (size_t item_i = 0; item_i < prong_item_count; item_i += 1) {
AstNode *item_node = prong_node->data.switch_prong.items.at(item_i);
assert(item_node->type != NodeTypeSwitchRange);
IrInstruction *item_value = ir_gen_node(irb, item_node, node->block_context);
if (item_value == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
IrBasicBlock *prong_block = ir_build_basic_block(irb, "SwitchProng");
IrBasicBlock *prev_block = irb->current_basic_block;
ir_set_cursor_at_end(irb, prong_block);
if (!ir_gen_switch_prong_expr(irb, node, prong_node, end_block,
is_inline, target_value_ptr, item_value, incoming_blocks, incoming_values))
{
return irb->codegen->invalid_instruction;
}
IrInstructionSwitchBrCase *this_case = cases.add_one();
this_case->value = item_value;
this_case->block = prong_block;
ir_set_cursor_at_end(irb, prev_block);
}
}
}
}
if (cases.length == 0) {
ir_build_br(irb, node, else_block, is_inline);
} else {
ir_build_switch_br(irb, node, target_value, else_block, cases.length, cases.items, is_inline);
}
if (!else_prong) {
ir_set_cursor_at_end(irb, else_block);
ir_build_unreachable(irb, node);
}
ir_set_cursor_at_end(irb, end_block);
assert(incoming_blocks.length == incoming_values.length);
return ir_build_phi(irb, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items);
}
static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, BlockContext *block_context,
LValPurpose lval)
{
@ -2142,6 +2382,8 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, BlockContex
return ir_gen_null_literal(irb, node);
case NodeTypeIfVarExpr:
return ir_gen_if_var_expr(irb, node);
case NodeTypeSwitchExpr:
return ir_gen_switch_expr(irb, node);
case NodeTypeUnwrapErrorExpr:
case NodeTypeDefer:
case NodeTypeSliceExpr:
@ -2149,7 +2391,6 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, BlockContex
case NodeTypeBreak:
case NodeTypeContinue:
case NodeTypeLabel:
case NodeTypeSwitchExpr:
case NodeTypeCharLiteral:
case NodeTypeZeroesLiteral:
case NodeTypeErrorType:
@ -4870,6 +5111,24 @@ static TypeTableEntry *ir_analyze_instruction_clz(IrAnalyze *ira, IrInstructionC
}
}
static TypeTableEntry *ir_analyze_instruction_switch_br(IrAnalyze *ira,
IrInstructionSwitchBr *switch_br_instruction)
{
zig_panic("TODO switch br analyze");
}
static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira,
IrInstructionSwitchTarget *switch_target_instruction)
{
zig_panic("TODO switch target analyze");
}
static TypeTableEntry *ir_analyze_instruction_switch_var(IrAnalyze *ira,
IrInstructionSwitchVar *switch_var_instruction)
{
zig_panic("TODO switch var analyze");
}
static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {
switch (instruction->id) {
case IrInstructionIdInvalid:
@ -4937,6 +5196,11 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
case IrInstructionIdCtz:
return ir_analyze_instruction_ctz(ira, (IrInstructionCtz *)instruction);
case IrInstructionIdSwitchBr:
return ir_analyze_instruction_switch_br(ira, (IrInstructionSwitchBr *)instruction);
case IrInstructionIdSwitchTarget:
return ir_analyze_instruction_switch_target(ira, (IrInstructionSwitchTarget *)instruction);
case IrInstructionIdSwitchVar:
return ir_analyze_instruction_switch_var(ira, (IrInstructionSwitchVar *)instruction);
case IrInstructionIdCast:
case IrInstructionIdContainerInitList:
case IrInstructionIdContainerInitFields:
@ -5053,6 +5317,8 @@ bool ir_has_side_effects(IrInstruction *instruction) {
case IrInstructionIdUnwrapMaybe:
case IrInstructionIdClz:
case IrInstructionIdCtz:
case IrInstructionIdSwitchVar:
case IrInstructionIdSwitchTarget:
return false;
case IrInstructionIdAsm:
{
@ -7037,246 +7303,6 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) {
// return g->builtin_types.entry_invalid;
// }
//}
//static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
// TypeTableEntry *expected_type, AstNode *node)
//{
// AstNode **expr_node = &node->data.switch_expr.expr;
// TypeTableEntry *expr_type = analyze_expression(g, import, context, nullptr, *expr_node);
// ConstExprValue *expr_val = &get_resolved_expr(*expr_node)->const_val;
// if (expr_val->ok && !expr_val->depends_on_compile_var) {
// add_node_error(g, first_executing_node(*expr_node),
// buf_sprintf("value is constant; unnecessary switch statement"));
// }
// ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
//
//
// size_t prong_count = node->data.switch_expr.prongs.length;
// AstNode **peer_nodes = allocate<AstNode*>(prong_count);
// TypeTableEntry **peer_types = allocate<TypeTableEntry*>(prong_count);
//
// bool any_errors = false;
// if (expr_type->id == TypeTableEntryIdInvalid) {
// return expr_type;
// } else if (expr_type->id == TypeTableEntryIdUnreachable) {
// add_node_error(g, first_executing_node(*expr_node),
// buf_sprintf("switch on unreachable expression not allowed"));
// return g->builtin_types.entry_invalid;
// }
//
//
// size_t *field_use_counts = nullptr;
// HashMap<int, AstNode *, int_hash, int_eq> err_use_nodes = {};
// if (expr_type->id == TypeTableEntryIdEnum) {
// field_use_counts = allocate<size_t>(expr_type->data.enumeration.src_field_count);
// } else if (expr_type->id == TypeTableEntryIdErrorUnion) {
// err_use_nodes.init(10);
// }
//
// size_t *const_chosen_prong_index = &node->data.switch_expr.const_chosen_prong_index;
// *const_chosen_prong_index = SIZE_MAX;
// AstNode *else_prong = nullptr;
// for (size_t prong_i = 0; prong_i < prong_count; prong_i += 1) {
// AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i);
//
// TypeTableEntry *var_type;
// bool var_is_target_expr;
// if (prong_node->data.switch_prong.items.length == 0) {
// if (else_prong) {
// add_node_error(g, prong_node, buf_sprintf("multiple else prongs in switch expression"));
// any_errors = true;
// } else {
// else_prong = prong_node;
// }
// var_type = expr_type;
// var_is_target_expr = true;
// if (*const_chosen_prong_index == SIZE_MAX && expr_val->ok) {
// *const_chosen_prong_index = prong_i;
// }
// } else {
// bool all_agree_on_var_type = true;
// var_type = nullptr;
//
// for (size_t item_i = 0; item_i < prong_node->data.switch_prong.items.length; item_i += 1) {
// AstNode *item_node = prong_node->data.switch_prong.items.at(item_i);
// if (item_node->type == NodeTypeSwitchRange) {
// zig_panic("TODO range in switch statement");
// }
//
// if (expr_type->id == TypeTableEntryIdEnum) {
// if (item_node->type == NodeTypeSymbol) {
// Buf *field_name = item_node->data.symbol_expr.symbol;
// TypeEnumField *type_enum_field = find_enum_type_field(expr_type, field_name);
// if (type_enum_field) {
// item_node->data.symbol_expr.enum_field = type_enum_field;
// if (!var_type) {
// var_type = type_enum_field->type_entry;
// }
// if (type_enum_field->type_entry != var_type) {
// all_agree_on_var_type = false;
// }
// uint32_t field_index = type_enum_field->value;
// assert(field_use_counts);
// field_use_counts[field_index] += 1;
// if (field_use_counts[field_index] > 1) {
// add_node_error(g, item_node,
// buf_sprintf("duplicate switch value: '%s'",
// buf_ptr(type_enum_field->name)));
// any_errors = true;
// }
// if (!any_errors && expr_val->ok) {
// if (expr_val->data.x_enum.tag == type_enum_field->value) {
// *const_chosen_prong_index = prong_i;
// }
// }
// } else {
// add_node_error(g, item_node,
// buf_sprintf("enum '%s' has no field '%s'",
// buf_ptr(&expr_type->name), buf_ptr(field_name)));
// any_errors = true;
// }
// } else {
// add_node_error(g, item_node, buf_sprintf("expected enum tag name"));
// any_errors = true;
// }
// } else if (expr_type->id == TypeTableEntryIdErrorUnion) {
// if (item_node->type == NodeTypeSymbol) {
// Buf *err_name = item_node->data.symbol_expr.symbol;
// bool is_ok_case = buf_eql_str(err_name, "Ok");
// auto err_table_entry = is_ok_case ? nullptr: g->error_table.maybe_get(err_name);
// if (is_ok_case || err_table_entry) {
// uint32_t err_value = is_ok_case ? 0 : err_table_entry->value->value;
// item_node->data.symbol_expr.err_value = err_value;
// TypeTableEntry *this_var_type;
// if (is_ok_case) {
// this_var_type = expr_type->data.error.child_type;
// } else {
// this_var_type = g->builtin_types.entry_pure_error;
// }
// if (!var_type) {
// var_type = this_var_type;
// }
// if (this_var_type != var_type) {
// all_agree_on_var_type = false;
// }
//
// // detect duplicate switch values
// auto existing_entry = err_use_nodes.maybe_get(err_value);
// if (existing_entry) {
// add_node_error(g, existing_entry->value,
// buf_sprintf("duplicate switch value: '%s'", buf_ptr(err_name)));
// any_errors = true;
// } else {
// err_use_nodes.put(err_value, item_node);
// }
//
// if (!any_errors && expr_val->ok) {
// if (expr_val->data.x_err.err->value == err_value) {
// *const_chosen_prong_index = prong_i;
// }
// }
// } else {
// add_node_error(g, item_node,
// buf_sprintf("use of undeclared error value '%s'", buf_ptr(err_name)));
// any_errors = true;
// }
// } else {
// add_node_error(g, item_node, buf_sprintf("expected error value name"));
// any_errors = true;
// }
// } else {
// if (!any_errors && expr_val->ok) {
// // note: there is now a function in eval.cpp for doing const expr comparison
// zig_panic("TODO determine if const exprs are equal");
// }
// TypeTableEntry *item_type = analyze_expression(g, import, context, expr_type, item_node);
// if (item_type->id != TypeTableEntryIdInvalid) {
// ConstExprValue *const_val = &get_resolved_expr(item_node)->const_val;
// if (!const_val->ok) {
// add_node_error(g, item_node,
// buf_sprintf("unable to evaluate constant expression"));
// any_errors = true;
// }
// }
// }
// }
// if (!var_type || !all_agree_on_var_type) {
// var_type = expr_type;
// var_is_target_expr = true;
// } else {
// var_is_target_expr = false;
// }
// }
//
// BlockContext *child_context = new_block_context(node, context);
// prong_node->data.switch_prong.block_context = child_context;
// AstNode *var_node = prong_node->data.switch_prong.var_symbol;
// if (var_node) {
// assert(var_node->type == NodeTypeSymbol);
// Buf *var_name = var_node->data.symbol_expr.symbol;
// var_node->block_context = child_context;
// prong_node->data.switch_prong.var = add_local_var(g, var_node, import,
// child_context, var_name, var_type, true, nullptr);
// prong_node->data.switch_prong.var_is_target_expr = var_is_target_expr;
// }
// }
//
// for (size_t prong_i = 0; prong_i < prong_count; prong_i += 1) {
// AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i);
// BlockContext *child_context = prong_node->data.switch_prong.block_context;
// child_context->codegen_excluded = expr_val->ok && (*const_chosen_prong_index != prong_i);
//
// if (child_context->codegen_excluded) {
// peer_types[prong_i] = g->builtin_types.entry_unreachable;
// } else {
// peer_types[prong_i] = analyze_expression(g, import, child_context, expected_type,
// prong_node->data.switch_prong.expr);
// }
// // This must go after the analyze_expression for
// // prong_node->data.switch_prong.expr because of AST rewriting.
// peer_nodes[prong_i] = prong_node->data.switch_prong.expr;
// }
//
// if (expr_type->id == TypeTableEntryIdEnum && !else_prong) {
// for (uint32_t i = 0; i < expr_type->data.enumeration.src_field_count; i += 1) {
// if (field_use_counts[i] == 0) {
// add_node_error(g, node,
// buf_sprintf("enumeration value '%s' not handled in switch",
// buf_ptr(expr_type->data.enumeration.fields[i].name)));
// any_errors = true;
// }
// }
// }
//
// if (any_errors) {
// return g->builtin_types.entry_invalid;
// }
//
// if (prong_count == 0) {
// add_node_error(g, node, buf_sprintf("switch statement has no prongs"));
// return g->builtin_types.entry_invalid;
// }
//
// TypeTableEntry *result_type = resolve_peer_type_compatibility(g, import, context, node,
// peer_nodes, peer_types, prong_count);
//
// if (expr_val->ok) {
// assert(*const_chosen_prong_index != SIZE_MAX);
//
// *const_val = get_resolved_expr(peer_nodes[*const_chosen_prong_index])->const_val;
// // the target expr depends on a compile var because we have an error on unnecessary
// // switch statement, so the entire switch statement does too
// const_val->depends_on_compile_var = true;
//
// if (!const_val->ok) {
// return add_error_if_type_is_num_lit(g, result_type, node);
// }
// } else {
// return add_error_if_type_is_num_lit(g, result_type, node);
// }
//
// return result_type;
//}
//
//static TypeTableEntry *analyze_return_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
// TypeTableEntry *expected_type, AstNode *node)
//{
@ -9338,190 +9364,6 @@ static void analyze_goto_pass2(CodeGen *g, ImportTableEntry *import, AstNode *no
// return nullptr;
//}
//
//static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) {
// assert(node->type == NodeTypeSwitchExpr);
//
// if (node->data.switch_expr.const_chosen_prong_index != SIZE_MAX) {
// AstNode *prong_node = node->data.switch_expr.prongs.at(node->data.switch_expr.const_chosen_prong_index);
// assert(prong_node->type == NodeTypeSwitchProng);
// AstNode *prong_expr = prong_node->data.switch_prong.expr;
// return gen_expr(g, prong_expr);
// }
//
// TypeTableEntry *target_type = get_expr_type(node->data.switch_expr.expr);
// LLVMValueRef target_value_handle = gen_expr(g, node->data.switch_expr.expr);
// LLVMValueRef target_value;
// if (handle_is_ptr(target_type)) {
// if (target_type->id == TypeTableEntryIdEnum) {
// set_debug_source_node(g, node);
// LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, target_value_handle, 0, "");
// target_value = LLVMBuildLoad(g->builder, tag_field_ptr, "");
// } else if (target_type->id == TypeTableEntryIdErrorUnion) {
// set_debug_source_node(g, node);
// LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, target_value_handle, 0, "");
// target_value = LLVMBuildLoad(g->builder, tag_field_ptr, "");
// } else {
// zig_unreachable();
// }
// } else {
// target_value = target_value_handle;
// }
//
//
// TypeTableEntry *switch_type = get_expr_type(node);
// bool result_has_bits = type_has_bits(switch_type);
// bool end_unreachable = (switch_type->id == TypeTableEntryIdUnreachable);
//
// LLVMBasicBlockRef end_block = end_unreachable ?
// nullptr : LLVMAppendBasicBlock(g->cur_fn->fn_value, "SwitchEnd");
// LLVMBasicBlockRef else_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "SwitchElse");
// size_t prong_count = node->data.switch_expr.prongs.length;
//
// set_debug_source_node(g, node);
// LLVMValueRef switch_instr = LLVMBuildSwitch(g->builder, target_value, else_block, prong_count);
//
// ZigList<LLVMValueRef> incoming_values = {0};
// ZigList<LLVMBasicBlockRef> incoming_blocks = {0};
//
// AstNode *else_prong = nullptr;
// for (size_t prong_i = 0; prong_i < prong_count; prong_i += 1) {
// AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i);
// VariableTableEntry *prong_var = prong_node->data.switch_prong.var;
//
// LLVMBasicBlockRef prong_block;
// if (prong_node->data.switch_prong.items.length == 0) {
// assert(!else_prong);
// else_prong = prong_node;
// prong_block = else_block;
// } else {
// prong_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "SwitchProng");
// size_t prong_item_count = prong_node->data.switch_prong.items.length;
// bool make_item_blocks = prong_var && prong_item_count > 1;
//
// for (size_t item_i = 0; item_i < prong_item_count; item_i += 1) {
// AstNode *item_node = prong_node->data.switch_prong.items.at(item_i);
//
// assert(item_node->type != NodeTypeSwitchRange);
// LLVMValueRef val;
// if (target_type->id == TypeTableEntryIdEnum ||
// target_type->id == TypeTableEntryIdErrorUnion)
// {
// assert(item_node->type == NodeTypeSymbol);
// TypeEnumField *enum_field = nullptr;
// uint32_t err_value = 0;
// if (target_type->id == TypeTableEntryIdEnum) {
// enum_field = item_node->data.symbol_expr.enum_field;
// assert(enum_field);
// val = LLVMConstInt(target_type->data.enumeration.tag_type->type_ref,
// enum_field->value, false);
// } else if (target_type->id == TypeTableEntryIdErrorUnion) {
// err_value = item_node->data.symbol_expr.err_value;
// val = LLVMConstInt(g->err_tag_type->type_ref, err_value, false);
// } else {
// zig_unreachable();
// }
//
// if (prong_var && type_has_bits(prong_var->type)) {
// LLVMBasicBlockRef item_block;
//
// if (make_item_blocks) {
// item_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "SwitchProngItem");
// LLVMAddCase(switch_instr, val, item_block);
// LLVMPositionBuilderAtEnd(g->builder, item_block);
// } else {
// LLVMAddCase(switch_instr, val, prong_block);
// LLVMPositionBuilderAtEnd(g->builder, prong_block);
// }
//
// AstNode *var_node = prong_node->data.switch_prong.var_symbol;
// set_debug_source_node(g, var_node);
// if (prong_node->data.switch_prong.var_is_target_expr) {
// gen_assign_raw(g, var_node, BinOpTypeAssign,
// prong_var->value_ref, target_value, prong_var->type, target_type);
// } else if (target_type->id == TypeTableEntryIdEnum) {
// assert(enum_field);
// assert(type_has_bits(enum_field->type_entry));
// LLVMValueRef union_field_ptr = LLVMBuildStructGEP(g->builder, target_value_handle,
// 1, "");
// LLVMValueRef bitcasted_union_field_ptr = LLVMBuildBitCast(g->builder, union_field_ptr,
// LLVMPointerType(enum_field->type_entry->type_ref, 0), "");
// LLVMValueRef handle_val = get_handle_value(g, bitcasted_union_field_ptr,
// enum_field->type_entry);
//
// gen_assign_raw(g, var_node, BinOpTypeAssign,
// prong_var->value_ref, handle_val, prong_var->type, enum_field->type_entry);
// } else if (target_type->id == TypeTableEntryIdErrorUnion) {
// if (err_value == 0) {
// // variable is the payload
// LLVMValueRef err_payload_ptr = LLVMBuildStructGEP(g->builder,
// target_value_handle, 1, "");
// LLVMValueRef handle_val = get_handle_value(g, err_payload_ptr, prong_var->type);
// gen_assign_raw(g, var_node, BinOpTypeAssign,
// prong_var->value_ref, handle_val, prong_var->type, prong_var->type);
// } else {
// // variable is the pure error value
// LLVMValueRef err_tag_ptr = LLVMBuildStructGEP(g->builder,
// target_value_handle, 0, "");
// LLVMValueRef handle_val = LLVMBuildLoad(g->builder, err_tag_ptr, "");
// gen_assign_raw(g, var_node, BinOpTypeAssign,
// prong_var->value_ref, handle_val, prong_var->type, g->err_tag_type);
// }
// } else {
// zig_unreachable();
// }
// if (make_item_blocks) {
// set_debug_source_node(g, var_node);
// LLVMBuildBr(g->builder, prong_block);
// }
// } else {
// LLVMAddCase(switch_instr, val, prong_block);
// }
// } else {
// assert(get_resolved_expr(item_node)->const_val.ok);
// val = gen_expr(g, item_node);
// LLVMAddCase(switch_instr, val, prong_block);
// }
// }
// }
//
// LLVMPositionBuilderAtEnd(g->builder, prong_block);
// AstNode *prong_expr = prong_node->data.switch_prong.expr;
// LLVMValueRef prong_val = gen_expr(g, prong_expr);
//
// if (get_expr_type(prong_expr)->id != TypeTableEntryIdUnreachable) {
// set_debug_source_node(g, prong_expr);
// LLVMBuildBr(g->builder, end_block);
// incoming_values.append(prong_val);
// incoming_blocks.append(LLVMGetInsertBlock(g->builder));
// }
// }
//
// if (!else_prong) {
// LLVMPositionBuilderAtEnd(g->builder, else_block);
// set_debug_source_node(g, node);
// if (want_debug_safety(g, node)) {
// gen_debug_safety_crash(g);
// } else {
// LLVMBuildUnreachable(g->builder);
// }
// }
//
// if (end_unreachable) {
// return nullptr;
// }
//
// LLVMPositionBuilderAtEnd(g->builder, end_block);
//
// if (result_has_bits) {
// set_debug_source_node(g, node);
// LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMTypeOf(incoming_values.at(0)), "");
// LLVMAddIncoming(phi, incoming_values.items, incoming_blocks.items, incoming_values.length);
// return phi;
// } else {
// return nullptr;
// }
//}
//
//static LLVMValueRef gen_array_access_expr(CodeGen *g, AstNode *node, bool is_lvalue) {
// assert(node->type == NodeTypeArrayAccessExpr);
//

View File

@ -645,6 +645,8 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
ir_print_clz(irp, (IrInstructionClz *)instruction);
break;
case IrInstructionIdSwitchBr:
case IrInstructionIdSwitchVar:
case IrInstructionIdSwitchTarget:
zig_panic("TODO print more IR instructions");
}
fprintf(irp->f, "\n");

View File

@ -38,8 +38,9 @@ struct ZigList {
return items[--length];
}
void add_one() {
return resize(length + 1);
T *add_one() {
resize(length + 1);
return &last();
}
const T & last() const {

View File

@ -1643,7 +1643,7 @@ static AstNode *ast_parse_for_expr(ParseContext *pc, size_t *token_index, bool m
/*
SwitchExpression : "switch" "(" Expression ")" "{" many(SwitchProng) "}"
SwitchProng = (list(SwitchItem, ",") | "else") "=>" option("|" "Symbol" "|") Expression ","
SwitchProng = (list(SwitchItem, ",") | "else") "=>" option("|" option("*") Symbol "|") Expression ","
SwitchItem : Expression | (Expression "..." Expression)
*/
static AstNode *ast_parse_switch_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
@ -1691,6 +1691,7 @@ static AstNode *ast_parse_switch_expr(ParseContext *pc, size_t *token_index, boo
range_node->data.switch_range.start = expr1;
range_node->data.switch_range.end = ast_parse_expression(pc, token_index, true);
prong_node->data.switch_prong.any_items_are_range = true;
} else {
prong_node->data.switch_prong.items.append(expr1);
}
@ -1707,7 +1708,21 @@ static AstNode *ast_parse_switch_expr(ParseContext *pc, size_t *token_index, boo
Token *maybe_bar = &pc->tokens->at(*token_index);
if (maybe_bar->id == TokenIdBinOr) {
*token_index += 1;
prong_node->data.switch_prong.var_symbol = ast_parse_symbol(pc, token_index);
Token *star_or_symbol = &pc->tokens->at(*token_index);
AstNode *var_symbol_node;
bool var_is_ptr;
if (star_or_symbol->id == TokenIdStar) {
*token_index += 1;
var_is_ptr = true;
var_symbol_node = ast_parse_symbol(pc, token_index);
} else {
var_is_ptr = false;
var_symbol_node = ast_parse_symbol(pc, token_index);
}
prong_node->data.switch_prong.var_symbol = var_symbol_node;
prong_node->data.switch_prong.var_is_ptr = var_is_ptr;
ast_eat_token(pc, token_index, TokenIdBinOr);
}

View File

@ -12,9 +12,23 @@ export fn disabledExternFn() {
@setFnVisible(this, false);
}
fn inlinedLoop() {
inline var i = 0;
inline var sum = 0;
inline while (i <= 5; i += 1)
sum += i;
assert(sum == 15);
}
fn assert(ok: bool) {
if (!ok)
@unreachable();
}
fn runAllTests() {
emptyFunctionWithComments();
disabledExternFn();
inlinedLoop();
}
export nakedcc fn _start() -> unreachable {