Commit b8a1cb299e
Changed files (3)
src/all_types.hpp
@@ -1207,6 +1207,9 @@ struct BlockContext {
LLVMZigDIScope *di_scope;
Buf *c_import_buf;
+
+ // if this is true, then this code will not be generated
+ bool codegen_excluded;
};
src/analyze.cpp
@@ -27,7 +27,8 @@ static TypeTableEntry *analyze_error_literal_expr(CodeGen *g, ImportTableEntry *
static TypeTableEntry *analyze_block_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
TypeTableEntry *expected_type, AstNode *node);
static TypeTableEntry *resolve_expr_const_val_as_void(CodeGen *g, AstNode *node);
-static TypeTableEntry *resolve_expr_const_val_as_fn(CodeGen *g, AstNode *node, FnTableEntry *fn);
+static TypeTableEntry *resolve_expr_const_val_as_fn(CodeGen *g, AstNode *node, BlockContext *context,
+ FnTableEntry *fn);
static TypeTableEntry *resolve_expr_const_val_as_type(CodeGen *g, AstNode *node, TypeTableEntry *type);
static TypeTableEntry *resolve_expr_const_val_as_unsigned_num_lit(CodeGen *g, AstNode *node,
TypeTableEntry *expected_type, uint64_t x);
@@ -1990,6 +1991,7 @@ BlockContext *new_block_context(AstNode *node, BlockContext *parent) {
if (parent) {
context->parent_loop_node = parent->parent_loop_node;
context->c_import_buf = parent->c_import_buf;
+ context->codegen_excluded = parent->codegen_excluded;
}
if (node && node->type == NodeTypeFnDef) {
@@ -2271,7 +2273,7 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i
auto table_entry = bare_struct_type->data.structure.fn_table.maybe_get(field_name);
if (table_entry) {
node->data.field_access_expr.is_member_fn = true;
- return resolve_expr_const_val_as_fn(g, node, table_entry->value);
+ return resolve_expr_const_val_as_fn(g, node, context, table_entry->value);
} else {
add_node_error(g, node, buf_sprintf("no member named '%s' in '%s'",
buf_ptr(field_name), buf_ptr(&bare_struct_type->name)));
@@ -2304,7 +2306,7 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i
} else if (child_type->id == TypeTableEntryIdStruct) {
auto entry = child_type->data.structure.fn_table.maybe_get(field_name);
if (entry) {
- return resolve_expr_const_val_as_fn(g, node, entry->value);
+ return resolve_expr_const_val_as_fn(g, node, context, entry->value);
} else {
add_node_error(g, node,
buf_sprintf("struct '%s' has no function called '%s'",
@@ -2421,8 +2423,12 @@ static TypeTableEntry *resolve_expr_const_val_as_other_expr(CodeGen *g, AstNode
return other_expr->type_entry;
}
-static TypeTableEntry *resolve_expr_const_val_as_fn(CodeGen *g, AstNode *node, FnTableEntry *fn) {
- fn->ref_count += 1;
+static TypeTableEntry *resolve_expr_const_val_as_fn(CodeGen *g, AstNode *node, BlockContext *context,
+ FnTableEntry *fn)
+{
+ if (!context->codegen_excluded) {
+ fn->ref_count += 1;
+ }
Expr *expr = get_resolved_expr(node);
expr->const_val.ok = true;
expr->const_val.data.x_fn = fn;
@@ -2604,7 +2610,7 @@ static TypeTableEntry *analyze_symbol_expr(CodeGen *g, ImportTableEntry *import,
if (fn_table_entry) {
assert(fn_table_entry->value->type_entry);
node->data.symbol_expr.fn_entry = fn_table_entry->value;
- return resolve_expr_const_val_as_fn(g, node, fn_table_entry->value);
+ return resolve_expr_const_val_as_fn(g, node, context, fn_table_entry->value);
}
add_node_error(g, node, buf_sprintf("use of undeclared identifier '%s'", buf_ptr(variable_name)));
@@ -4344,7 +4350,9 @@ static TypeTableEntry *analyze_fn_call_raw(CodeGen *g, ImportTableEntry *import,
node->data.fn_call_expr.fn_entry = fn_table_entry;
- fn_table_entry->ref_count += 1;
+ if (!context->codegen_excluded) {
+ fn_table_entry->ref_count += 1;
+ }
return analyze_fn_call_ptr(g, import, context, expected_type, node, fn_table_entry->type_entry, struct_type);
@@ -4650,6 +4658,13 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import,
{
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;
+
int prong_count = node->data.switch_expr.prongs.length;
AstNode **peer_nodes = allocate<AstNode*>(prong_count);
@@ -4662,113 +4677,132 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import,
add_node_error(g, first_executing_node(*expr_node),
buf_sprintf("switch on unreachable expression not allowed"));
return g->builtin_types.entry_invalid;
- } else {
- int *field_use_counts = nullptr;
- if (expr_type->id == TypeTableEntryIdEnum) {
- field_use_counts = allocate<int>(expr_type->data.enumeration.field_count);
- }
+ }
- AstNode *else_prong = nullptr;
- for (int 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;
+ int *field_use_counts = nullptr;
+ if (expr_type->id == TypeTableEntryIdEnum) {
+ field_use_counts = allocate<int>(expr_type->data.enumeration.field_count);
+ }
+
+ int const_chosen_prong_index = -1;
+ AstNode *else_prong = nullptr;
+ for (int 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 {
- bool all_agree_on_var_type = true;
- var_type = nullptr;
+ else_prong = prong_node;
+ }
+ var_type = expr_type;
+ var_is_target_expr = true;
+ if (const_chosen_prong_index == -1) {
+ const_chosen_prong_index = prong_i;
+ }
+ } else {
+ bool all_agree_on_var_type = true;
+ var_type = nullptr;
- for (int 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");
- }
+ for (int 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 = get_enum_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;
- }
- } else {
+ if (expr_type->id == TypeTableEntryIdEnum) {
+ if (item_node->type == NodeTypeSymbol) {
+ Buf *field_name = &item_node->data.symbol_expr.symbol;
+ TypeEnumField *type_enum_field = get_enum_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("enum '%s' has no field '%s'",
- buf_ptr(&expr_type->name), buf_ptr(field_name)));
+ 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("expected enum tag name"));
+ 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 {
- 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 resolve constant expression"));
- any_errors = true;
- }
- }
+ add_node_error(g, item_node, buf_sprintf("expected enum tag name"));
+ 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;
+ if (!any_errors && expr_val->ok) {
+ 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 resolve constant expression"));
+ any_errors = true;
+ }
+ }
}
}
-
- 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);
- prong_node->data.switch_prong.var_is_target_expr = var_is_target_expr;
+ if (!var_type || !all_agree_on_var_type) {
+ var_type = expr_type;
+ var_is_target_expr = true;
+ } else {
+ var_is_target_expr = false;
}
+ }
- peer_types[prong_i] = analyze_expression(g, import, child_context, expected_type,
- prong_node->data.switch_prong.expr);
- peer_nodes[prong_i] = prong_node->data.switch_prong.expr;
+ 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);
+ prong_node->data.switch_prong.var_is_target_expr = var_is_target_expr;
}
+ }
- if (expr_type->id == TypeTableEntryIdEnum && !else_prong) {
- for (uint32_t i = 0; i < expr_type->data.enumeration.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;
- }
+ for (int 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);
+
+ peer_types[prong_i] = analyze_expression(g, import, child_context, expected_type,
+ prong_node->data.switch_prong.expr);
+ 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.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;
}
}
}
@@ -4782,49 +4816,18 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import,
return g->builtin_types.entry_invalid;
}
- TypeTableEntry *resolved_type = resolve_peer_type_compatibility(g, import, context, node,
- peer_nodes, peer_types, prong_count);
-
- if (resolved_type->id == TypeTableEntryIdInvalid) {
- return resolved_type;
- }
+ if (expr_val->ok) {
+ assert(const_chosen_prong_index != -1);
- ConstExprValue *expr_val = &get_resolved_expr(*expr_node)->const_val;
- if (!expr_val->ok) {
- return resolved_type;
- }
-
- 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"));
+ *const_val = get_resolved_expr(peer_nodes[const_chosen_prong_index])->const_val;
+ const_val->ok = true;
+ // the target expr depends on a compile var,
+ // so the entire if statement does too
+ const_val->depends_on_compile_var = true;
}
- if (!expr_val->ok) {
- return resolved_type;
- }
- ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
-
- for (int prong_i = 0; prong_i < prong_count; prong_i += 1) {
- AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i);
- for (int 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 (expr_type->id == TypeTableEntryIdEnum) {
- TypeEnumField *type_enum_field = item_node->data.symbol_expr.enum_field;
- if (expr_val->data.x_enum.tag == type_enum_field->value) {
- *const_val = get_resolved_expr(peer_nodes[prong_i])->const_val;
- const_val->ok = true;
- // the target expr depends on a compile var, so the entire if statement does too
- const_val->depends_on_compile_var = true;
- return resolved_type;
- }
- } else {
- zig_panic("TODO determine if const exprs are equal");
- }
- }
- }
-
- zig_unreachable();
+ return resolve_peer_type_compatibility(g, import, context, node, peer_nodes, peer_types, prong_count);
}
static TypeTableEntry *analyze_return_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
test/run_tests.cpp
@@ -1584,8 +1584,8 @@ fn f(Foo: i32) {
".tmp_source.zig:6:5: error: variable shadows type 'Bar'");
add_compile_fail_case("multiple else prongs in a switch", R"SOURCE(
-fn f() {
- const value: bool = switch (u32(111)) {
+fn f(x: u32) {
+ const value: bool = switch (x) {
1234 => false,
else => true,
else => true,