Commit 0c22358cc1
Changed files (10)
doc/langref.md
@@ -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)
src/all_types.hpp
@@ -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;
src/ast_render.cpp
@@ -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:
src/codegen.cpp
@@ -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:
src/ir.cpp
@@ -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);
//
src/ir_print.cpp
@@ -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");
src/list.hpp
@@ -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 {
src/parser.cpp
@@ -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);
}
test/self_hosted2.zig
@@ -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 {
README.md
@@ -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)