Commit 0de35af98b

antlilja <liljaanton2001@gmail.com>
2020-06-22 12:14:52
Add duplicate checking for switch on types * Add compile error tests
1 parent 78c6d39
Changed files (2)
src/ir.cpp
@@ -28861,7 +28861,37 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
         ir_add_error(ira, &instruction->base.base,
             buf_sprintf("else prong required when switching on type '%s'", buf_ptr(&switch_type->name)));
         return ira->codegen->invalid_inst_gen;
-    }
+    } else if(switch_type->id == ZigTypeIdMetaType) {
+        HashMap<const ZigType*, IrInstGen*, type_ptr_hash, type_ptr_eql> prevs;
+        // HashMap doubles capacity when reaching 60% capacity,
+        // because we know the size at init we can avoid reallocation by doubling it here
+        prevs.init(instruction->range_count * 2);
+        for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) {
+            IrInstSrcCheckSwitchProngsRange *range = &instruction->ranges[range_i];
+
+            IrInstGen *value = range->start->child;
+            IrInstGen *casted_value = ir_implicit_cast(ira, value, switch_type);
+            if (type_is_invalid(casted_value->value->type)) {
+                prevs.deinit();
+                return ira->codegen->invalid_inst_gen;
+            }
+
+            ZigValue *const_expr_val = ir_resolve_const(ira, casted_value, UndefBad);
+            if (!const_expr_val) {
+                prevs.deinit();
+                return ira->codegen->invalid_inst_gen;
+            }
+
+            auto entry = prevs.put_unique(const_expr_val->data.x_type, value);
+            if(entry != nullptr) {
+                ErrorMsg *msg = ir_add_error(ira, &value->base, buf_sprintf("duplicate switch value"));
+                add_error_note(ira->codegen, msg, entry->value->base.source_node, buf_sprintf("previous value is here"));
+                prevs.deinit();
+                return ira->codegen->invalid_inst_gen;
+            }
+        }
+        prevs.deinit();
+    } 
     return ir_const_void(ira, &instruction->base.base);
 }
 
test/compile_errors.zig
@@ -4362,6 +4362,40 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         "tmp.zig:5:14: note: previous value is here",
     });
 
+    cases.add("switch expression - duplicate type",
+        \\fn foo(comptime T: type, x: T) u8 {
+        \\    return switch (T) {
+        \\        u32 => 0,
+        \\        u64 => 1,
+        \\        u32 => 2,
+        \\        else => 3,
+        \\    };
+        \\}
+        \\export fn entry() usize { return @sizeOf(@TypeOf(foo(u32, 0))); }
+    , &[_][]const u8{
+        "tmp.zig:5:9: error: duplicate switch value",
+        "tmp.zig:3:9: note: previous value is here",
+    });
+
+    cases.add("switch expression - duplicate type (struct alias)",
+        \\const Test = struct {
+        \\    bar: i32,
+        \\};
+        \\const Test2 = Test;
+        \\fn foo(comptime T: type, x: T) u8 {
+        \\    return switch (T) {
+        \\        Test => 0,
+        \\        u64 => 1,
+        \\        Test2 => 2,
+        \\        else => 3,
+        \\    };
+        \\}
+        \\export fn entry() usize { return @sizeOf(@TypeOf(foo(u32, 0))); }
+    , &[_][]const u8{
+        "tmp.zig:9:9: error: duplicate switch value",
+        "tmp.zig:7:9: note: previous value is here",
+    });
+
     cases.add("switch expression - switch on pointer type with no else",
         \\fn foo(x: *u8) void {
         \\    switch (x) {