Commit f11b948019

Ben Noordhuis <info@bnoordhuis.nl>
2018-02-23 15:25:42
allow implicit cast from `S` to `?&const S`
Allow implicit casts from container types to nullable const pointers to said container type. That is: fn f() void { const s = S {}; g(s); // Works. g(&s); // So does this. } fn g(_: ?&const S) void { // Nullable const pointer. } Fixes #731.
1 parent b66547e
Changed files (2)
src
test
cases
src/ir.cpp
@@ -8817,16 +8817,29 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
 
     // explicit cast from child type of maybe type to maybe type
     if (wanted_type->id == TypeTableEntryIdMaybe) {
-        if (types_match_const_cast_only(ira, wanted_type->data.maybe.child_type, actual_type, source_node).id == ConstCastResultIdOk) {
+        TypeTableEntry *wanted_child_type = wanted_type->data.maybe.child_type;
+        if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node).id == ConstCastResultIdOk) {
             return ir_analyze_maybe_wrap(ira, source_instr, value, wanted_type);
         } else if (actual_type->id == TypeTableEntryIdNumLitInt ||
                    actual_type->id == TypeTableEntryIdNumLitFloat)
         {
-            if (ir_num_lit_fits_in_other_type(ira, value, wanted_type->data.maybe.child_type, true)) {
+            if (ir_num_lit_fits_in_other_type(ira, value, wanted_child_type, true)) {
                 return ir_analyze_maybe_wrap(ira, source_instr, value, wanted_type);
             } else {
                 return ira->codegen->invalid_instruction;
             }
+        } else if (wanted_child_type->id == TypeTableEntryIdPointer &&
+                   wanted_child_type->data.pointer.is_const &&
+                   is_container(actual_type)) {
+            IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_child_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;
         }
     }
 
test/cases/cast.zig
@@ -32,6 +32,77 @@ fn funcWithConstPtrPtr(x: &const &i32) void {
     **x += 1;
 }
 
+test "implicitly cast a container to a const pointer of it" {
+    const z = Struct(void) { .x = void{} };
+    assert(0 == @sizeOf(@typeOf(z)));
+    assert(void{} == Struct(void).pointer(z).x);
+    assert(void{} == Struct(void).pointer(&z).x);
+    assert(void{} == Struct(void).maybePointer(z).x);
+    assert(void{} == Struct(void).maybePointer(&z).x);
+    assert(void{} == Struct(void).maybePointer(null).x);
+    const s = Struct(u8) { .x = 42 };
+    assert(0 != @sizeOf(@typeOf(s)));
+    assert(42 == Struct(u8).pointer(s).x);
+    assert(42 == Struct(u8).pointer(&s).x);
+    assert(42 == Struct(u8).maybePointer(s).x);
+    assert(42 == Struct(u8).maybePointer(&s).x);
+    assert(0 == Struct(u8).maybePointer(null).x);
+    const u = Union { .x = 42 };
+    assert(42 == Union.pointer(u).x);
+    assert(42 == Union.pointer(&u).x);
+    assert(42 == Union.maybePointer(u).x);
+    assert(42 == Union.maybePointer(&u).x);
+    assert(0 == Union.maybePointer(null).x);
+    const e = Enum.Some;
+    assert(Enum.Some == Enum.pointer(e));
+    assert(Enum.Some == Enum.pointer(&e));
+    assert(Enum.Some == Enum.maybePointer(e));
+    assert(Enum.Some == Enum.maybePointer(&e));
+    assert(Enum.None == Enum.maybePointer(null));
+}
+
+fn Struct(comptime T: type) type {
+    return struct {
+        const Self = this;
+        x: T,
+
+        fn pointer(self: &const Self) Self {
+            return *self;
+        }
+
+        fn maybePointer(self: ?&const Self) Self {
+            const none = Self { .x = if (T == void) void{} else 0 };
+            return *(self ?? &none);
+        }
+    };
+}
+
+const Union = union {
+    x: u8,
+
+    fn pointer(self: &const Union) Union {
+        return *self;
+    }
+
+    fn maybePointer(self: ?&const Union) Union {
+        const none = Union { .x = 0 };
+        return *(self ?? &none);
+    }
+};
+
+const Enum = enum {
+    None,
+    Some,
+
+    fn pointer(self: &const Enum) Enum {
+        return *self;
+    }
+
+    fn maybePointer(self: ?&const Enum) Enum {
+        return *(self ?? &Enum.None);
+    }
+};
+
 test "explicit cast from integer to error type" {
     testCastIntToErr(error.ItBroke);
     comptime testCastIntToErr(error.ItBroke);