Merge branch 'master' into self-hosted

This commit is contained in:
Andrew Kelley 2017-12-05 20:49:03 -05:00
commit 6018dbd339
6 changed files with 171 additions and 20 deletions

View File

@ -2244,12 +2244,10 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {
TypeTableEntry *enum_type = analyze_type_expr(g, scope, enum_type_node);
if (type_is_invalid(enum_type)) {
union_type->data.unionation.is_invalid = true;
union_type->data.unionation.embedded_in_current = false;
return;
}
if (enum_type->id != TypeTableEntryIdEnum) {
union_type->data.unionation.is_invalid = true;
union_type->data.unionation.embedded_in_current = false;
add_node_error(g, enum_type_node,
buf_sprintf("expected enum tag type, found '%s'", buf_ptr(&enum_type->name)));
return;
@ -3319,7 +3317,7 @@ TypeStructField *find_struct_type_field(TypeTableEntry *type_entry, Buf *name) {
TypeUnionField *find_union_type_field(TypeTableEntry *type_entry, Buf *name) {
assert(type_entry->id == TypeTableEntryIdUnion);
assert(type_entry->data.unionation.complete);
assert(type_entry->data.unionation.zero_bits_known);
for (uint32_t i = 0; i < type_entry->data.unionation.src_field_count; i += 1) {
TypeUnionField *field = &type_entry->data.unionation.fields[i];
if (buf_eql_buf(field->enum_field->name, name)) {
@ -3331,7 +3329,7 @@ TypeUnionField *find_union_type_field(TypeTableEntry *type_entry, Buf *name) {
TypeUnionField *find_union_field_by_tag(TypeTableEntry *type_entry, const BigInt *tag) {
assert(type_entry->id == TypeTableEntryIdUnion);
assert(type_entry->data.unionation.complete);
assert(type_entry->data.unionation.zero_bits_known);
assert(type_entry->data.unionation.gen_tag_index != SIZE_MAX);
for (uint32_t i = 0; i < type_entry->data.unionation.src_field_count; i += 1) {
TypeUnionField *field = &type_entry->data.unionation.fields[i];
@ -3888,7 +3886,6 @@ bool handle_is_ptr(TypeTableEntry *type_entry) {
return false;
case TypeTableEntryIdArray:
case TypeTableEntryIdStruct:
case TypeTableEntryIdUnion:
return type_has_bits(type_entry);
case TypeTableEntryIdErrorUnion:
return type_has_bits(type_entry->data.error.child_type);
@ -3896,6 +3893,14 @@ bool handle_is_ptr(TypeTableEntry *type_entry) {
return type_has_bits(type_entry->data.maybe.child_type) &&
type_entry->data.maybe.child_type->id != TypeTableEntryIdPointer &&
type_entry->data.maybe.child_type->id != TypeTableEntryIdFn;
case TypeTableEntryIdUnion:
assert(type_entry->data.unionation.complete);
if (type_entry->data.unionation.gen_field_count == 0)
return false;
if (!type_has_bits(type_entry))
return false;
return true;
}
zig_unreachable();
}

View File

@ -3946,8 +3946,6 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
case TypeTableEntryIdUnion:
{
LLVMTypeRef union_type_ref = type_entry->data.unionation.union_type_ref;
ConstExprValue *payload_value = const_val->data.x_union.payload;
assert(payload_value != nullptr);
if (type_entry->data.unionation.gen_field_count == 0) {
if (type_entry->data.unionation.gen_tag_index == SIZE_MAX) {
@ -3960,7 +3958,8 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
LLVMValueRef union_value_ref;
bool make_unnamed_struct;
if (!type_has_bits(payload_value->type)) {
ConstExprValue *payload_value = const_val->data.x_union.payload;
if (payload_value == nullptr || !type_has_bits(payload_value->type)) {
if (type_entry->data.unionation.gen_tag_index == SIZE_MAX)
return LLVMGetUndef(type_entry->type_ref);
@ -5298,18 +5297,19 @@ void codegen_translate_c(CodeGen *g, Buf *full_path) {
ZigList<ErrorMsg *> errors = {0};
int err = parse_h_file(import, &errors, buf_ptr(full_path), g, nullptr);
if (err) {
fprintf(stderr, "unable to parse C file: %s\n", err_str(err));
exit(1);
}
if (errors.length > 0) {
if (err == ErrorCCompileErrors && errors.length > 0) {
for (size_t i = 0; i < errors.length; i += 1) {
ErrorMsg *err_msg = errors.at(i);
print_err_msg(err_msg, g->err_color);
}
exit(1);
}
if (err) {
fprintf(stderr, "unable to parse C file: %s\n", err_str(err));
exit(1);
}
}
static ImportTableEntry *add_special_code(CodeGen *g, PackageTableEntry *package, const char *basename) {

View File

@ -7468,6 +7468,17 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira,
}
}
// implicit enum to union which has the enum as the tag type
if (expected_type->id == TypeTableEntryIdUnion && actual_type->id == TypeTableEntryIdEnum &&
(expected_type->data.unionation.decl_node->data.container_decl.auto_enum ||
expected_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr))
{
type_ensure_zero_bits_known(ira->codegen, expected_type);
if (expected_type->data.unionation.tag_type == actual_type) {
return ImplicitCastMatchResultYes;
}
}
// implicit undefined literal to anything
if (actual_type->id == TypeTableEntryIdUndefLit) {
return ImplicitCastMatchResultYes;
@ -8370,6 +8381,63 @@ static IrInstruction *ir_analyze_undefined_to_anything(IrAnalyze *ira, IrInstruc
return result;
}
static IrInstruction *ir_analyze_enum_to_union(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *target, TypeTableEntry *wanted_type)
{
assert(wanted_type->id == TypeTableEntryIdUnion);
assert(target->value.type->id == TypeTableEntryIdEnum);
if (instr_is_comptime(target)) {
ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
if (!val)
return ira->codegen->invalid_instruction;
TypeUnionField *union_field = find_union_field_by_tag(wanted_type, &val->data.x_enum_tag);
assert(union_field != nullptr);
type_ensure_zero_bits_known(ira->codegen, union_field->type_entry);
if (!union_field->type_entry->zero_bits) {
AstNode *field_node = wanted_type->data.unionation.decl_node->data.container_decl.fields.at(
union_field->enum_field->decl_index);
ErrorMsg *msg = ir_add_error(ira, source_instr,
buf_sprintf("cast to union '%s' must initialize '%s' field '%s'",
buf_ptr(&wanted_type->name),
buf_ptr(&union_field->type_entry->name),
buf_ptr(union_field->name)));
add_error_note(ira->codegen, msg, field_node,
buf_sprintf("field '%s' declared here", buf_ptr(union_field->name)));
return ira->codegen->invalid_instruction;
}
IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope,
source_instr->source_node, wanted_type);
result->value.special = ConstValSpecialStatic;
result->value.type = wanted_type;
bigint_init_bigint(&result->value.data.x_union.tag, &val->data.x_enum_tag);
return result;
}
// if the union has all fields 0 bits, we can do it
// and in fact it's a noop cast because the union value is just the enum value
if (wanted_type->data.unionation.gen_field_count == 0) {
IrInstruction *result = ir_build_cast(&ira->new_irb, target->scope, target->source_node, wanted_type, target, CastOpNoop);
result->value.type = wanted_type;
return result;
}
ErrorMsg *msg = ir_add_error(ira, source_instr,
buf_sprintf("runtime cast to union '%s' which has non-void fields",
buf_ptr(&wanted_type->name)));
for (uint32_t i = 0; i < wanted_type->data.unionation.src_field_count; i += 1) {
TypeUnionField *union_field = &wanted_type->data.unionation.fields[i];
if (type_has_bits(union_field->type_entry)) {
AstNode *field_node = wanted_type->data.unionation.decl_node->data.container_decl.fields.at(i);
add_error_note(ira->codegen, msg, field_node,
buf_sprintf("field '%s' has type '%s'",
buf_ptr(union_field->name),
buf_ptr(&union_field->type_entry->name)));
}
}
return ira->codegen->invalid_instruction;
}
static IrInstruction *ir_analyze_widen_or_shorten(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *target, TypeTableEntry *wanted_type)
{
@ -8426,14 +8494,16 @@ static IrInstruction *ir_analyze_int_to_enum(IrAnalyze *ira, IrInstruction *sour
ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
if (!val)
return ira->codegen->invalid_instruction;
BigInt enum_member_count;
bigint_init_unsigned(&enum_member_count, wanted_type->data.enumeration.src_field_count);
if (bigint_cmp(&val->data.x_bigint, &enum_member_count) != CmpLT) {
TypeEnumField *field = find_enum_field_by_tag(wanted_type, &val->data.x_bigint);
if (field == nullptr) {
Buf *val_buf = buf_alloc();
bigint_append_buf(val_buf, &val->data.x_bigint, 10);
ir_add_error(ira, source_instr,
buf_sprintf("integer value %s too big for enum '%s' which has %" PRIu32 " fields",
buf_ptr(val_buf), buf_ptr(&wanted_type->name), wanted_type->data.enumeration.src_field_count));
ErrorMsg *msg = ir_add_error(ira, source_instr,
buf_sprintf("enum '%s' has no tag matching integer value %s",
buf_ptr(&wanted_type->name), buf_ptr(val_buf)));
add_error_note(ira->codegen, msg, wanted_type->data.enumeration.decl_node,
buf_sprintf("'%s' declared here", buf_ptr(&wanted_type->name)));
return ira->codegen->invalid_instruction;
}
@ -8842,7 +8912,17 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
if (actual_type->id == TypeTableEntryIdNumLitFloat ||
actual_type->id == TypeTableEntryIdNumLitInt)
{
if (wanted_type->id == TypeTableEntryIdPointer &&
if (wanted_type->id == TypeTableEntryIdEnum) {
IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.enumeration.tag_int_type, value);
if (type_is_invalid(cast1->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1);
if (type_is_invalid(cast2->value.type))
return ira->codegen->invalid_instruction;
return cast2;
} else if (wanted_type->id == TypeTableEntryIdPointer &&
wanted_type->data.pointer.is_const)
{
IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.pointer.child_type, value);
@ -8912,6 +8992,17 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
}
}
// explicit enum to union which has the enum as the tag type
if (wanted_type->id == TypeTableEntryIdUnion && actual_type->id == TypeTableEntryIdEnum &&
(wanted_type->data.unionation.decl_node->data.container_decl.auto_enum ||
wanted_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr))
{
type_ensure_zero_bits_known(ira->codegen, wanted_type);
if (wanted_type->data.unionation.tag_type == actual_type) {
return ir_analyze_enum_to_union(ira, source_instr, value, wanted_type);
}
}
// explicit cast from undefined to anything
if (actual_type->id == TypeTableEntryIdUndefLit) {
return ir_analyze_undefined_to_anything(ira, source_instr, value, wanted_type);

View File

@ -344,3 +344,8 @@ fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) {
MultipleChoice2.Unspecified5 => 9,
});
}
test "cast integer literal to enum" {
assert(MultipleChoice2(0) == MultipleChoice2.Unspecified1);
assert(MultipleChoice2(40) == MultipleChoice2.B);
}

View File

@ -190,3 +190,10 @@ test "cast union to tag type of union" {
fn testCastUnionToTagType(x: &const TheUnion) {
assert(TheTag(*x) == TheTag.B);
}
test "cast tag type of union to union" {
var x: Value2 = Letter2.B;
assert(Letter2(x) == Letter2.B);
}
const Letter2 = enum { A, B, C };
const Value2 = union(Letter2) { A: i32, B, C, };

View File

@ -2684,4 +2684,47 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
,
".tmp_source.zig:11:13: error: switch on union which has no attached enum",
".tmp_source.zig:1:17: note: consider 'union(enum)' here");
cases.add("enum in field count range but not matching tag",
\\const Foo = enum(u32) {
\\ A = 10,
\\ B = 11,
\\};
\\export fn entry() {
\\ var x = Foo(0);
\\}
,
".tmp_source.zig:6:16: error: enum 'Foo' has no tag matching integer value 0",
".tmp_source.zig:1:13: note: 'Foo' declared here");
cases.add("comptime cast enum to union but field has payload",
\\const Letter = enum { A, B, C };
\\const Value = union(Letter) {
\\ A: i32,
\\ B,
\\ C,
\\};
\\export fn entry() {
\\ var x: Value = Letter.A;
\\}
,
".tmp_source.zig:8:26: error: cast to union 'Value' must initialize 'i32' field 'A'",
".tmp_source.zig:3:5: note: field 'A' declared here");
cases.add("runtime cast to union which has non-void fields",
\\const Letter = enum { A, B, C };
\\const Value = union(Letter) {
\\ A: i32,
\\ B,
\\ C,
\\};
\\export fn entry() {
\\ foo(Letter.A);
\\}
\\fn foo(l: Letter) {
\\ var x: Value = l;
\\}
,
".tmp_source.zig:11:20: error: runtime cast to union 'Value' which has non-void fields",
".tmp_source.zig:3:5: note: field 'A' has type 'i32'");
}