Commit 2dc4ac0e21
Changed files (7)
src/all_types.hpp
@@ -1108,6 +1108,8 @@ struct CodeGen {
TypeTableEntry *entry_num_lit_float;
TypeTableEntry *entry_undef;
TypeTableEntry *entry_pure_error;
+ TypeTableEntry *entry_os_enum;
+ TypeTableEntry *entry_arch_enum;
} builtin_types;
ZigTarget zig_target;
@@ -1126,6 +1128,8 @@ struct CodeGen {
Buf triple_str;
bool is_release_build;
bool is_test_build;
+ uint32_t target_os_index;
+ uint32_t target_arch_index;
LLVMTargetMachineRef target_machine;
LLVMZigDIFile *dummy_di_file;
bool is_native_target;
src/analyze.cpp
@@ -3526,6 +3526,8 @@ static TypeTableEntry *analyze_if(CodeGen *g, ImportTableEntry *import, BlockCon
ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
*const_val = *other_const_val;
+ // the condition depends on a compile var, so the entire if statement does too
+ const_val->depends_on_compile_var = true;
return result_type;
}
@@ -4233,6 +4235,7 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry
}
ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
+ const_val->ok = true;
const_val->depends_on_compile_var = true;
if (buf_eql_str(&var_name, "is_big_endian")) {
@@ -4242,7 +4245,11 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry
} else if (buf_eql_str(&var_name, "is_test")) {
return resolve_expr_const_val_as_bool(g, node, g->is_test_build, true);
} else if (buf_eql_str(&var_name, "os")) {
- zig_panic("TODO");
+ const_val->data.x_enum.tag = g->target_os_index;
+ return g->builtin_types.entry_os_enum;
+ } else if (buf_eql_str(&var_name, "arch")) {
+ const_val->data.x_enum.tag = g->target_arch_index;
+ return g->builtin_types.entry_arch_enum;
} else {
add_node_error(g, *str_node,
buf_sprintf("unrecognized compile variable: '%s'", buf_ptr(&var_name)));
@@ -4638,17 +4645,18 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo
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);
+ AstNode **expr_node = &node->data.switch_expr.expr;
+ TypeTableEntry *expr_type = analyze_expression(g, import, context, nullptr, *expr_node);
int 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),
+ add_node_error(g, first_executing_node(*expr_node),
buf_sprintf("switch on unreachable expression not allowed"));
return g->builtin_types.entry_invalid;
} else {
@@ -4666,6 +4674,7 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import,
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;
}
@@ -4700,14 +4709,17 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import,
add_node_error(g, item_node,
buf_sprintf("duplicate switch value: '%s'",
buf_ptr(type_enum_field->name)));
+ any_errors = true;
}
} 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 {
TypeTableEntry *item_type = analyze_expression(g, import, context, expr_type, item_node);
@@ -4716,6 +4728,7 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import,
if (!const_val->ok) {
add_node_error(g, item_node,
buf_sprintf("unable to resolve constant expression"));
+ any_errors = true;
}
}
}
@@ -4751,11 +4764,64 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import,
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 *resolved_type = resolve_peer_type_compatibility(g, import, context, node,
+ peer_nodes, peer_types, prong_count);
+
+ if (resolved_type->id == TypeTableEntryIdInvalid) {
+ return resolved_type;
+ }
+
+ 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"));
+ }
+
+ 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");
}
}
}
- return resolve_peer_type_compatibility(g, import, context, node, peer_nodes, peer_types, prong_count);
+
+ zig_unreachable();
}
static TypeTableEntry *analyze_return_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
src/codegen.cpp
@@ -352,10 +352,11 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) {
case BuiltinFnIdMinValue:
case BuiltinFnIdMaxValue:
case BuiltinFnIdMemberCount:
- case BuiltinFnIdCompileVar:
case BuiltinFnIdConstEval:
// caught by constant expression eval codegen
zig_unreachable();
+ case BuiltinFnIdCompileVar:
+ return nullptr;
}
zig_unreachable();
}
@@ -3443,6 +3444,62 @@ static void define_builtin_types(CodeGen *g) {
g->builtin_types.entry_i16 = get_int_type(g, true, 16);
g->builtin_types.entry_i32 = get_int_type(g, true, 32);
g->builtin_types.entry_i64 = get_int_type(g, true, 64);
+
+ {
+ TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdEnum);
+ entry->zero_bits = true; // only allowed at compile time
+ buf_init_from_str(&entry->name, "@OS");
+ uint32_t field_count = target_os_count();
+ entry->data.enumeration.field_count = field_count;
+ entry->data.enumeration.fields = allocate<TypeEnumField>(field_count);
+ for (uint32_t i = 0; i < field_count; i += 1) {
+ TypeEnumField *type_enum_field = &entry->data.enumeration.fields[i];
+ ZigLLVM_OSType os_type = get_target_os(i);
+ type_enum_field->name = buf_create_from_str(get_target_os_name(os_type));
+ type_enum_field->value = i;
+
+ if (os_type == g->zig_target.os) {
+ g->target_os_index = i;
+ }
+ }
+ entry->data.enumeration.complete = true;
+
+ TypeTableEntry *tag_type_entry = get_smallest_unsigned_int_type(g, field_count);
+ entry->data.enumeration.tag_type = tag_type_entry;
+
+ g->builtin_types.entry_os_enum = entry;
+ }
+
+ {
+ TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdEnum);
+ entry->zero_bits = true; // only allowed at compile time
+ buf_init_from_str(&entry->name, "@Arch");
+ uint32_t field_count = target_arch_count();
+ entry->data.enumeration.field_count = field_count;
+ entry->data.enumeration.fields = allocate<TypeEnumField>(field_count);
+ for (uint32_t i = 0; i < field_count; i += 1) {
+ TypeEnumField *type_enum_field = &entry->data.enumeration.fields[i];
+ const ArchType *arch_type = get_target_arch(i);
+ type_enum_field->name = buf_alloc();
+ buf_resize(type_enum_field->name, 50);
+ get_arch_name(buf_ptr(type_enum_field->name), arch_type);
+ buf_resize(type_enum_field->name, strlen(buf_ptr(type_enum_field->name)));
+
+ type_enum_field->value = i;
+
+ if (arch_type->arch == g->zig_target.arch.arch &&
+ arch_type->sub_arch == g->zig_target.arch.sub_arch)
+ {
+ g->target_arch_index = i;
+ }
+ }
+ entry->data.enumeration.complete = true;
+
+ TypeTableEntry *tag_type_entry = get_smallest_unsigned_int_type(g, field_count);
+ entry->data.enumeration.tag_type = tag_type_entry;
+
+ g->builtin_types.entry_arch_enum = entry;
+ }
}
src/main.cpp
@@ -58,11 +58,11 @@ static int print_target_list(FILE *f) {
int arch_count = target_arch_count();
for (int arch_i = 0; arch_i < arch_count; arch_i += 1) {
const ArchType *arch = get_target_arch(arch_i);
- const char *sub_arch_str = (arch->sub_arch == ZigLLVM_NoSubArch) ?
- "" : ZigLLVMGetSubArchTypeName(arch->sub_arch);
+ char arch_name[50];
+ get_arch_name(arch_name, arch);
const char *native_str = (native.arch.arch == arch->arch && native.arch.sub_arch == arch->sub_arch) ?
" (native)" : "";
- fprintf(f, " %s%s%s\n", ZigLLVMGetArchTypeName(arch->arch), sub_arch_str, native_str);
+ fprintf(f, " %s%s\n", arch_name, native_str);
}
fprintf(f, "\nOperating Systems:\n");
src/target.cpp
@@ -181,13 +181,20 @@ void get_unknown_target(ZigTarget *target) {
target->oformat = ZigLLVM_UnknownObjectFormat;
}
+static void get_arch_name_raw(char *out_str, ZigLLVM_ArchType arch, ZigLLVM_SubArchType sub_arch) {
+ const char *sub_str = (sub_arch == ZigLLVM_NoSubArch) ? "" : ZigLLVMGetSubArchTypeName(sub_arch);
+ sprintf(out_str, "%s%s", ZigLLVMGetArchTypeName(arch), sub_str);
+}
+
+void get_arch_name(char *out_str, const ArchType *arch) {
+ return get_arch_name_raw(out_str, arch->arch, arch->sub_arch);
+}
+
int parse_target_arch(const char *str, ArchType *out_arch) {
for (int i = 0; i < array_length(arch_list); i += 1) {
const ArchType *arch = &arch_list[i];
char arch_name[50];
- const char *sub_str = (arch->sub_arch == ZigLLVM_NoSubArch) ?
- "" : ZigLLVMGetSubArchTypeName(arch->sub_arch);
- sprintf(arch_name, "%s%s", ZigLLVMGetArchTypeName(arch->arch), sub_str);
+ get_arch_name_raw(arch_name, arch->arch, arch->sub_arch);
if (strcmp(arch_name, str) == 0) {
*out_arch = *arch;
return 0;
src/target.hpp
@@ -27,6 +27,7 @@ struct ZigTarget {
int target_arch_count(void);
const ArchType *get_target_arch(int index);
+void get_arch_name(char *out_str, const ArchType *arch);
int target_vendor_count(void);
ZigLLVM_VendorType get_target_vendor(int index);
test/self_hosted.zig
@@ -308,11 +308,6 @@ fn non_const_cast_bool_to_int(t: bool, f: bool) {
#attribute("test")
fn switch_on_enum() {
const fruit = Fruit.Orange;
- switch (fruit) {
- Apple => unreachable{},
- Orange => {},
- Banana => unreachable{},
- }
non_const_switch_on_enum(fruit);
}
enum Fruit {
@@ -330,7 +325,9 @@ fn non_const_switch_on_enum(fruit: Fruit) {
#attribute("test")
fn switch_statement() {
- const foo = SwitchStatmentFoo.C;
+ non_const_switch(SwitchStatmentFoo.C);
+}
+fn non_const_switch(foo: SwitchStatmentFoo) {
const val: i32 = switch (foo) {
A => 1,
B => 2,