aboutsummaryrefslogtreecommitdiff
path: root/src/analyze.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/analyze.cpp')
-rw-r--r--src/analyze.cpp65
1 files changed, 61 insertions, 4 deletions
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 46f05fb646..737fff0a7b 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -5116,8 +5116,11 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import,
int *field_use_counts = nullptr;
+ HashMap<int, AstNode *, int_hash, int_eq> err_use_nodes;
if (expr_type->id == TypeTableEntryIdEnum) {
field_use_counts = allocate<int>(expr_type->data.enumeration.field_count);
+ } else if (expr_type->id == TypeTableEntryIdErrorUnion) {
+ err_use_nodes.init(10);
}
int *const_chosen_prong_index = &node->data.switch_expr.const_chosen_prong_index;
@@ -5186,8 +5189,54 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import,
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);
@@ -5252,17 +5301,25 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import,
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 != -1);
*const_val = get_resolved_expr(peer_nodes[*const_chosen_prong_index])->const_val;
- // the target expr depends on a compile var,
- // so the entire if statement does too
+ // 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 resolve_peer_type_compatibility(g, import, context, node, peer_nodes, peer_types, prong_count);
+ return result_type;
}
static TypeTableEntry *analyze_return_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,