authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2020-12-06 19:31:07+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-12-06 12:37:18-07:00
log01ff3684dc05c9ddf954eeafa53065aef4dc5cfc
tree2ee69d1551c27e0ac99b4b67960b0780e31528fe
parent48c8948f49c2084365776cd9e9624162021e5ec7

Merge pull request #7313 from LemonBoy/booo

Fix a few unsound optimizations on single-element union/enum

3 files changed, 114 insertions(+), 20 deletions(-)

src/stage1/analyze.cpp+6-1
...@@ -6054,7 +6054,7 @@ ZigValue *get_the_one_possible_value(CodeGen *g, ZigType *type_entry) {...@@ -6054,7 +6054,7 @@ ZigValue *get_the_one_possible_value(CodeGen *g, ZigType *type_entry) {
6054 TypeUnionField *only_field = &union_type->data.unionation.fields[0];6054 TypeUnionField *only_field = &union_type->data.unionation.fields[0];
6055 ZigType *field_type = resolve_union_field_type(g, only_field);6055 ZigType *field_type = resolve_union_field_type(g, only_field);
6056 assert(field_type);6056 assert(field_type);
6057 bigint_init_unsigned(&result->data.x_union.tag, 0);6057 bigint_init_bigint(&result->data.x_union.tag, &only_field->enum_field->value);
6058 result->data.x_union.payload = g->pass1_arena->create<ZigValue>();6058 result->data.x_union.payload = g->pass1_arena->create<ZigValue>();
6059 copy_const_val(g, result->data.x_union.payload,6059 copy_const_val(g, result->data.x_union.payload,
6060 get_the_one_possible_value(g, field_type));6060 get_the_one_possible_value(g, field_type));
...@@ -6063,6 +6063,11 @@ ZigValue *get_the_one_possible_value(CodeGen *g, ZigType *type_entry) {...@@ -6063,6 +6063,11 @@ ZigValue *get_the_one_possible_value(CodeGen *g, ZigType *type_entry) {
6063 result->data.x_ptr.mut = ConstPtrMutComptimeConst;6063 result->data.x_ptr.mut = ConstPtrMutComptimeConst;
6064 result->data.x_ptr.special = ConstPtrSpecialRef;6064 result->data.x_ptr.special = ConstPtrSpecialRef;
6065 result->data.x_ptr.data.ref.pointee = get_the_one_possible_value(g, result->type->data.pointer.child_type);6065 result->data.x_ptr.data.ref.pointee = get_the_one_possible_value(g, result->type->data.pointer.child_type);
6066 } else if (result->type->id == ZigTypeIdEnum) {
6067 ZigType *enum_type = result->type;
6068 assert(enum_type->data.enumeration.src_field_count == 1);
6069 TypeEnumField *only_field = &result->type->data.enumeration.fields[0];
6070 bigint_init_bigint(&result->data.x_enum_tag, &only_field->value);
6066 }6071 }
6067 g->one_possible_values.put(type_entry, result);6072 g->one_possible_values.put(type_entry, result);
6068 return result;6073 return result;
src/stage1/ir.cpp+29-19
...@@ -14123,6 +14123,18 @@ static ZigType *ir_resolve_union_tag_type(IrAnalyze *ira, AstNode *source_node,...@@ -14123,6 +14123,18 @@ static ZigType *ir_resolve_union_tag_type(IrAnalyze *ira, AstNode *source_node,
14123 }14123 }
14124}14124}
1412514125
14126static bool can_fold_enum_type(ZigType *ty) {
14127 assert(ty->id == ZigTypeIdEnum);
14128 // We can fold the enum type (and avoid any check, be it at runtime or at
14129 // compile time) iff it has only a single element and its tag type is
14130 // zero-sized.
14131 ZigType *tag_int_type = ty->data.enumeration.tag_int_type;
14132 return ty->data.enumeration.layout == ContainerLayoutAuto &&
14133 ty->data.enumeration.src_field_count == 1 &&
14134 !ty->data.enumeration.non_exhaustive &&
14135 (tag_int_type->id == ZigTypeIdInt && tag_int_type->data.integral.bit_count == 0);
14136}
14137
14126static IrInstGen *ir_analyze_enum_to_int(IrAnalyze *ira, IrInst *source_instr, IrInstGen *target) {14138static IrInstGen *ir_analyze_enum_to_int(IrAnalyze *ira, IrInst *source_instr, IrInstGen *target) {
14127 Error err;14139 Error err;
1412814140
...@@ -14151,10 +14163,7 @@ static IrInstGen *ir_analyze_enum_to_int(IrAnalyze *ira, IrInst *source_instr, I...@@ -14151,10 +14163,7 @@ static IrInstGen *ir_analyze_enum_to_int(IrAnalyze *ira, IrInst *source_instr, I
14151 assert(tag_type->id == ZigTypeIdInt || tag_type->id == ZigTypeIdComptimeInt);14163 assert(tag_type->id == ZigTypeIdInt || tag_type->id == ZigTypeIdComptimeInt);
1415214164
14153 // If there is only one possible tag, then we know at comptime what it is.14165 // If there is only one possible tag, then we know at comptime what it is.
14154 if (enum_type->data.enumeration.layout == ContainerLayoutAuto &&14166 if (can_fold_enum_type(enum_type)) {
14155 enum_type->data.enumeration.src_field_count == 1 &&
14156 !enum_type->data.enumeration.non_exhaustive)
14157 {
14158 IrInstGen *result = ir_const(ira, source_instr, tag_type);14167 IrInstGen *result = ir_const(ira, source_instr, tag_type);
14159 init_const_bigint(result->value, tag_type,14168 init_const_bigint(result->value, tag_type,
14160 &enum_type->data.enumeration.fields[0].value);14169 &enum_type->data.enumeration.fields[0].value);
...@@ -14192,10 +14201,7 @@ static IrInstGen *ir_analyze_union_to_tag(IrAnalyze *ira, IrInst* source_instr,...@@ -14192,10 +14201,7 @@ static IrInstGen *ir_analyze_union_to_tag(IrAnalyze *ira, IrInst* source_instr,
14192 }14201 }
1419314202
14194 // If there is only 1 possible tag, then we know at comptime what it is.14203 // If there is only 1 possible tag, then we know at comptime what it is.
14195 if (wanted_type->data.enumeration.layout == ContainerLayoutAuto &&14204 if (can_fold_enum_type(wanted_type)) {
14196 wanted_type->data.enumeration.src_field_count == 1 &&
14197 !wanted_type->data.enumeration.non_exhaustive)
14198 {
14199 IrInstGen *result = ir_const(ira, source_instr, wanted_type);14205 IrInstGen *result = ir_const(ira, source_instr, wanted_type);
14200 result->value->special = ConstValSpecialStatic;14206 result->value->special = ConstValSpecialStatic;
14201 result->value->type = wanted_type;14207 result->value->type = wanted_type;
...@@ -23837,7 +23843,8 @@ static IrInstGen *ir_analyze_instruction_switch_target(IrAnalyze *ira,...@@ -23837,7 +23843,8 @@ static IrInstGen *ir_analyze_instruction_switch_target(IrAnalyze *ira,
23837 bigint_init_bigint(&result->value->data.x_enum_tag, &pointee_val->data.x_union.tag);23843 bigint_init_bigint(&result->value->data.x_enum_tag, &pointee_val->data.x_union.tag);
23838 return result;23844 return result;
23839 }23845 }
23840 if (tag_type->data.enumeration.src_field_count == 1 && !tag_type->data.enumeration.non_exhaustive) {23846
23847 if (can_fold_enum_type(tag_type)) {
23841 IrInstGen *result = ir_const(ira, &switch_target_instruction->base.base, tag_type);23848 IrInstGen *result = ir_const(ira, &switch_target_instruction->base.base, tag_type);
23842 TypeEnumField *only_field = &tag_type->data.enumeration.fields[0];23849 TypeEnumField *only_field = &tag_type->data.enumeration.fields[0];
23843 bigint_init_bigint(&result->value->data.x_enum_tag, &only_field->value);23850 bigint_init_bigint(&result->value->data.x_enum_tag, &only_field->value);
...@@ -23852,7 +23859,8 @@ static IrInstGen *ir_analyze_instruction_switch_target(IrAnalyze *ira,...@@ -23852,7 +23859,8 @@ static IrInstGen *ir_analyze_instruction_switch_target(IrAnalyze *ira,
23852 case ZigTypeIdEnum: {23859 case ZigTypeIdEnum: {
23853 if ((err = type_resolve(ira->codegen, target_type, ResolveStatusZeroBitsKnown)))23860 if ((err = type_resolve(ira->codegen, target_type, ResolveStatusZeroBitsKnown)))
23854 return ira->codegen->invalid_inst_gen;23861 return ira->codegen->invalid_inst_gen;
23855 if (target_type->data.enumeration.src_field_count == 1 && !target_type->data.enumeration.non_exhaustive) {23862
23863 if (can_fold_enum_type(target_type)) {
23856 TypeEnumField *only_field = &target_type->data.enumeration.fields[0];23864 TypeEnumField *only_field = &target_type->data.enumeration.fields[0];
23857 IrInstGen *result = ir_const(ira, &switch_target_instruction->base.base, target_type);23865 IrInstGen *result = ir_const(ira, &switch_target_instruction->base.base, target_type);
23858 bigint_init_bigint(&result->value->data.x_enum_tag, &only_field->value);23866 bigint_init_bigint(&result->value->data.x_enum_tag, &only_field->value);
...@@ -24587,7 +24595,9 @@ static IrInstGen *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrInstSrc...@@ -24587,7 +24595,9 @@ static IrInstGen *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrInstSrc
24587 if (type_is_invalid(target->value->type))24595 if (type_is_invalid(target->value->type))
24588 return ira->codegen->invalid_inst_gen;24596 return ira->codegen->invalid_inst_gen;
2458924597
24590 if (target->value->type->id == ZigTypeIdEnumLiteral) {24598 ZigType *target_type = target->value->type;
24599
24600 if (target_type->id == ZigTypeIdEnumLiteral) {
24591 IrInstGen *result = ir_const(ira, &instruction->base.base, nullptr);24601 IrInstGen *result = ir_const(ira, &instruction->base.base, nullptr);
24592 Buf *field_name = target->value->data.x_enum_literal;24602 Buf *field_name = target->value->data.x_enum_literal;
24593 ZigValue *array_val = create_const_str_lit(ira->codegen, field_name)->data.x_ptr.data.ref.pointee;24603 ZigValue *array_val = create_const_str_lit(ira->codegen, field_name)->data.x_ptr.data.ref.pointee;
...@@ -24595,21 +24605,21 @@ static IrInstGen *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrInstSrc...@@ -24595,21 +24605,21 @@ static IrInstGen *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrInstSrc
24595 return result;24605 return result;
24596 }24606 }
2459724607
24598 if (target->value->type->id == ZigTypeIdUnion) {24608 if (target_type->id == ZigTypeIdUnion) {
24599 target = ir_analyze_union_tag(ira, &instruction->base.base, target, instruction->base.is_gen);24609 target = ir_analyze_union_tag(ira, &instruction->base.base, target, instruction->base.is_gen);
24600 if (type_is_invalid(target->value->type))24610 if (type_is_invalid(target->value->type))
24601 return ira->codegen->invalid_inst_gen;24611 return ira->codegen->invalid_inst_gen;
24612 target_type = target->value->type;
24602 }24613 }
2460324614
24604 if (target->value->type->id != ZigTypeIdEnum) {24615 if (target_type->id != ZigTypeIdEnum) {
24605 ir_add_error(ira, &target->base,24616 ir_add_error(ira, &target->base,
24606 buf_sprintf("expected enum tag, found '%s'", buf_ptr(&target->value->type->name)));24617 buf_sprintf("expected enum tag, found '%s'", buf_ptr(&target_type->name)));
24607 return ira->codegen->invalid_inst_gen;24618 return ira->codegen->invalid_inst_gen;
24608 }24619 }
2460924620
24610 if (target->value->type->data.enumeration.src_field_count == 1 &&24621 if (can_fold_enum_type(target_type)) {
24611 !target->value->type->data.enumeration.non_exhaustive) {24622 TypeEnumField *only_field = &target_type->data.enumeration.fields[0];
24612 TypeEnumField *only_field = &target->value->type->data.enumeration.fields[0];
24613 ZigValue *array_val = create_const_str_lit(ira->codegen, only_field->name)->data.x_ptr.data.ref.pointee;24623 ZigValue *array_val = create_const_str_lit(ira->codegen, only_field->name)->data.x_ptr.data.ref.pointee;
24614 IrInstGen *result = ir_const(ira, &instruction->base.base, nullptr);24624 IrInstGen *result = ir_const(ira, &instruction->base.base, nullptr);
24615 init_const_slice(ira->codegen, result->value, array_val, 0, buf_len(only_field->name), true);24625 init_const_slice(ira->codegen, result->value, array_val, 0, buf_len(only_field->name), true);
...@@ -24617,9 +24627,9 @@ static IrInstGen *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrInstSrc...@@ -24617,9 +24627,9 @@ static IrInstGen *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrInstSrc
24617 }24627 }
2461824628
24619 if (instr_is_comptime(target)) {24629 if (instr_is_comptime(target)) {
24620 if ((err = type_resolve(ira->codegen, target->value->type, ResolveStatusZeroBitsKnown)))24630 if ((err = type_resolve(ira->codegen, target_type, ResolveStatusZeroBitsKnown)))
24621 return ira->codegen->invalid_inst_gen;24631 return ira->codegen->invalid_inst_gen;
24622 TypeEnumField *field = find_enum_field_by_tag(target->value->type, &target->value->data.x_bigint);24632 TypeEnumField *field = find_enum_field_by_tag(target_type, &target->value->data.x_bigint);
24623 if (field == nullptr) {24633 if (field == nullptr) {
24624 Buf *int_buf = buf_alloc();24634 Buf *int_buf = buf_alloc();
24625 bigint_append_buf(int_buf, &target->value->data.x_bigint, 10);24635 bigint_append_buf(int_buf, &target->value->data.x_bigint, 10);
test/runtime_safety.zig+79
...@@ -1,6 +1,85 @@...@@ -1,6 +1,85 @@
1const tests = @import("tests.zig");1const tests = @import("tests.zig");
22
3pub fn addCases(cases: *tests.CompareOutputContext) void {3pub fn addCases(cases: *tests.CompareOutputContext) void {
4 {
5 const check_panic_msg =
6 \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
7 \\ if (std.mem.eql(u8, message, "reached unreachable code")) {
8 \\ std.process.exit(126); // good
9 \\ }
10 \\ std.process.exit(0); // test failed
11 \\}
12 ;
13
14 cases.addRuntimeSafety("switch on corrupted enum value",
15 \\const std = @import("std");
16 ++ check_panic_msg ++
17 \\const E = enum(u32) {
18 \\ X = 1,
19 \\};
20 \\pub fn main() void {
21 \\ var e: E = undefined;
22 \\ @memset(@ptrCast([*]u8, &e), 0x55, @sizeOf(E));
23 \\ switch (e) {
24 \\ .X => @breakpoint(),
25 \\ }
26 \\}
27 );
28
29 cases.addRuntimeSafety("switch on corrupted union value",
30 \\const std = @import("std");
31 ++ check_panic_msg ++
32 \\const U = union(enum(u32)) {
33 \\ X: u8,
34 \\};
35 \\pub fn main() void {
36 \\ var u: U = undefined;
37 \\ @memset(@ptrCast([*]u8, &u), 0x55, @sizeOf(U));
38 \\ switch (u) {
39 \\ .X => @breakpoint(),
40 \\ }
41 \\}
42 );
43 }
44
45 {
46 const check_panic_msg =
47 \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
48 \\ if (std.mem.eql(u8, message, "invalid enum value")) {
49 \\ std.process.exit(126); // good
50 \\ }
51 \\ std.process.exit(0); // test failed
52 \\}
53 ;
54
55 cases.addRuntimeSafety("@tagName on corrupted enum value",
56 \\const std = @import("std");
57 ++ check_panic_msg ++
58 \\const E = enum(u32) {
59 \\ X = 1,
60 \\};
61 \\pub fn main() void {
62 \\ var e: E = undefined;
63 \\ @memset(@ptrCast([*]u8, &e), 0x55, @sizeOf(E));
64 \\ var n = @tagName(e);
65 \\}
66 );
67
68 cases.addRuntimeSafety("@tagName on corrupted union value",
69 \\const std = @import("std");
70 ++ check_panic_msg ++
71 \\const U = union(enum(u32)) {
72 \\ X: u8,
73 \\};
74 \\pub fn main() void {
75 \\ var u: U = undefined;
76 \\ @memset(@ptrCast([*]u8, &u), 0x55, @sizeOf(U));
77 \\ var t: @TagType(U) = u;
78 \\ var n = @tagName(t);
79 \\}
80 );
81 }
82
4 {83 {
5 const check_panic_msg =84 const check_panic_msg =
6 \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {85 \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {