Commit 6edd81109d

Andrew Kelley <superjoe30@gmail.com>
2018-06-09 06:15:23
nullable pointers follow const-casting rules
any *T -> ?*T cast is allowed implicitly, even when it occurs deep inside the type, and the cast is a no-op at runtime. in order to add this I had to make the comptime value representation of nullable pointers the same as the comptime value representation of normal pointers, so that we don't have to do any recursive transformation of values when doing this kind of cast.
1 parent 1a9d2f3
src/all_types.hpp
@@ -144,6 +144,9 @@ enum ConstPtrSpecial {
     // understand the value of pointee at compile time. However, we will still
     // emit a binary with a compile time known address.
     // In this case index is the numeric address value.
+    // We also use this for null pointer. We need the data layout for ConstCastOnly == true
+    // types to be the same, so all nullables of pointer types use x_ptr
+    // instead of x_nullable
     ConstPtrSpecialHardCodedAddr,
     // This means that the pointer represents memory of assigning to _.
     // That is, storing discards the data, and loading is invalid.
@@ -251,7 +254,7 @@ struct ConstExprValue {
         bool x_bool;
         ConstBoundFnValue x_bound_fn;
         TypeTableEntry *x_type;
-        ConstExprValue *x_maybe;
+        ConstExprValue *x_nullable;
         ConstErrValue x_err_union;
         ErrorTableEntry *x_err_set;
         BigInt x_enum_tag;
src/analyze.cpp
@@ -4578,6 +4578,52 @@ bool fn_type_id_eql(FnTypeId *a, FnTypeId *b) {
     return true;
 }
 
+static uint32_t hash_const_val_ptr(ConstExprValue *const_val) {
+    uint32_t hash_val = 0;
+    switch (const_val->data.x_ptr.mut) {
+        case ConstPtrMutRuntimeVar:
+            hash_val += (uint32_t)3500721036;
+            break;
+        case ConstPtrMutComptimeConst:
+            hash_val += (uint32_t)4214318515;
+            break;
+        case ConstPtrMutComptimeVar:
+            hash_val += (uint32_t)1103195694;
+            break;
+    }
+    switch (const_val->data.x_ptr.special) {
+        case ConstPtrSpecialInvalid:
+            zig_unreachable();
+        case ConstPtrSpecialRef:
+            hash_val += (uint32_t)2478261866;
+            hash_val += hash_ptr(const_val->data.x_ptr.data.ref.pointee);
+            return hash_val;
+        case ConstPtrSpecialBaseArray:
+            hash_val += (uint32_t)1764906839;
+            hash_val += hash_ptr(const_val->data.x_ptr.data.base_array.array_val);
+            hash_val += hash_size(const_val->data.x_ptr.data.base_array.elem_index);
+            hash_val += const_val->data.x_ptr.data.base_array.is_cstr ? 1297263887 : 200363492;
+            return hash_val;
+        case ConstPtrSpecialBaseStruct:
+            hash_val += (uint32_t)3518317043;
+            hash_val += hash_ptr(const_val->data.x_ptr.data.base_struct.struct_val);
+            hash_val += hash_size(const_val->data.x_ptr.data.base_struct.field_index);
+            return hash_val;
+        case ConstPtrSpecialHardCodedAddr:
+            hash_val += (uint32_t)4048518294;
+            hash_val += hash_size(const_val->data.x_ptr.data.hard_coded_addr.addr);
+            return hash_val;
+        case ConstPtrSpecialDiscard:
+            hash_val += 2010123162;
+            return hash_val;
+        case ConstPtrSpecialFunction:
+            hash_val += (uint32_t)2590901619;
+            hash_val += hash_ptr(const_val->data.x_ptr.data.fn.fn_entry);
+            return hash_val;
+    }
+    zig_unreachable();
+}
+
 static uint32_t hash_const_val(ConstExprValue *const_val) {
     assert(const_val->special == ConstValSpecialStatic);
     switch (const_val->type->id) {
@@ -4646,51 +4692,7 @@ static uint32_t hash_const_val(ConstExprValue *const_val) {
             assert(const_val->data.x_ptr.special == ConstPtrSpecialFunction);
             return 3677364617 ^ hash_ptr(const_val->data.x_ptr.data.fn.fn_entry);
         case TypeTableEntryIdPointer:
-            {
-                uint32_t hash_val = 0;
-                switch (const_val->data.x_ptr.mut) {
-                    case ConstPtrMutRuntimeVar:
-                        hash_val += (uint32_t)3500721036;
-                        break;
-                    case ConstPtrMutComptimeConst:
-                        hash_val += (uint32_t)4214318515;
-                        break;
-                    case ConstPtrMutComptimeVar:
-                        hash_val += (uint32_t)1103195694;
-                        break;
-                }
-                switch (const_val->data.x_ptr.special) {
-                    case ConstPtrSpecialInvalid:
-                        zig_unreachable();
-                    case ConstPtrSpecialRef:
-                        hash_val += (uint32_t)2478261866;
-                        hash_val += hash_ptr(const_val->data.x_ptr.data.ref.pointee);
-                        return hash_val;
-                    case ConstPtrSpecialBaseArray:
-                        hash_val += (uint32_t)1764906839;
-                        hash_val += hash_ptr(const_val->data.x_ptr.data.base_array.array_val);
-                        hash_val += hash_size(const_val->data.x_ptr.data.base_array.elem_index);
-                        hash_val += const_val->data.x_ptr.data.base_array.is_cstr ? 1297263887 : 200363492;
-                        return hash_val;
-                    case ConstPtrSpecialBaseStruct:
-                        hash_val += (uint32_t)3518317043;
-                        hash_val += hash_ptr(const_val->data.x_ptr.data.base_struct.struct_val);
-                        hash_val += hash_size(const_val->data.x_ptr.data.base_struct.field_index);
-                        return hash_val;
-                    case ConstPtrSpecialHardCodedAddr:
-                        hash_val += (uint32_t)4048518294;
-                        hash_val += hash_size(const_val->data.x_ptr.data.hard_coded_addr.addr);
-                        return hash_val;
-                    case ConstPtrSpecialDiscard:
-                        hash_val += 2010123162;
-                        return hash_val;
-                    case ConstPtrSpecialFunction:
-                        hash_val += (uint32_t)2590901619;
-                        hash_val += hash_ptr(const_val->data.x_ptr.data.fn.fn_entry);
-                        return hash_val;
-                }
-                zig_unreachable();
-            }
+            return hash_const_val_ptr(const_val);
         case TypeTableEntryIdPromise:
             // TODO better hashing algorithm
             return 223048345;
@@ -4708,10 +4710,14 @@ static uint32_t hash_const_val(ConstExprValue *const_val) {
             // TODO better hashing algorithm
             return 2709806591;
         case TypeTableEntryIdMaybe:
-            if (const_val->data.x_maybe) {
-                return hash_const_val(const_val->data.x_maybe) * 1992916303;
+            if (get_codegen_ptr_type(const_val->type) != nullptr) {
+                return hash_const_val(const_val) * 1992916303;
             } else {
-                return 4016830364;
+                if (const_val->data.x_nullable) {
+                    return hash_const_val(const_val->data.x_nullable) * 1992916303;
+                } else {
+                    return 4016830364;
+                }
             }
         case TypeTableEntryIdErrorUnion:
             // TODO better hashing algorithm
@@ -4812,9 +4818,11 @@ static bool can_mutate_comptime_var_state(ConstExprValue *value) {
             return false;
 
         case TypeTableEntryIdMaybe:
-            if (value->data.x_maybe == nullptr)
+            if (get_codegen_ptr_type(value->type) != nullptr)
+                return value->data.x_ptr.mut == ConstPtrMutComptimeVar;
+            if (value->data.x_nullable == nullptr)
                 return false;
-            return can_mutate_comptime_var_state(value->data.x_maybe);
+            return can_mutate_comptime_var_state(value->data.x_nullable);
 
         case TypeTableEntryIdErrorUnion:
             if (value->data.x_err_union.err != nullptr)
@@ -5340,6 +5348,52 @@ bool ir_get_var_is_comptime(VariableTableEntry *var) {
     return var->is_comptime->value.data.x_bool;
 }
 
+bool const_values_equal_ptr(ConstExprValue *a, ConstExprValue *b) {
+    if (a->data.x_ptr.special != b->data.x_ptr.special)
+        return false;
+    if (a->data.x_ptr.mut != b->data.x_ptr.mut)
+        return false;
+    switch (a->data.x_ptr.special) {
+        case ConstPtrSpecialInvalid:
+            zig_unreachable();
+        case ConstPtrSpecialRef:
+            if (a->data.x_ptr.data.ref.pointee != b->data.x_ptr.data.ref.pointee)
+                return false;
+            return true;
+        case ConstPtrSpecialBaseArray:
+            if (a->data.x_ptr.data.base_array.array_val != b->data.x_ptr.data.base_array.array_val &&
+                a->data.x_ptr.data.base_array.array_val->global_refs !=
+                b->data.x_ptr.data.base_array.array_val->global_refs)
+            {
+                return false;
+            }
+            if (a->data.x_ptr.data.base_array.elem_index != b->data.x_ptr.data.base_array.elem_index)
+                return false;
+            if (a->data.x_ptr.data.base_array.is_cstr != b->data.x_ptr.data.base_array.is_cstr)
+                return false;
+            return true;
+        case ConstPtrSpecialBaseStruct:
+            if (a->data.x_ptr.data.base_struct.struct_val != b->data.x_ptr.data.base_struct.struct_val &&
+                a->data.x_ptr.data.base_struct.struct_val->global_refs !=
+                b->data.x_ptr.data.base_struct.struct_val->global_refs)
+            {
+                return false;
+            }
+            if (a->data.x_ptr.data.base_struct.field_index != b->data.x_ptr.data.base_struct.field_index)
+                return false;
+            return true;
+        case ConstPtrSpecialHardCodedAddr:
+            if (a->data.x_ptr.data.hard_coded_addr.addr != b->data.x_ptr.data.hard_coded_addr.addr)
+                return false;
+            return true;
+        case ConstPtrSpecialDiscard:
+            return true;
+        case ConstPtrSpecialFunction:
+            return a->data.x_ptr.data.fn.fn_entry == b->data.x_ptr.data.fn.fn_entry;
+    }
+    zig_unreachable();
+}
+
 bool const_values_equal(ConstExprValue *a, ConstExprValue *b) {
     assert(a->type->id == b->type->id);
     assert(a->special == ConstValSpecialStatic);
@@ -5391,49 +5445,7 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) {
             return bigint_cmp(&a->data.x_bigint, &b->data.x_bigint) == CmpEQ;
         case TypeTableEntryIdPointer:
         case TypeTableEntryIdFn:
-            if (a->data.x_ptr.special != b->data.x_ptr.special)
-                return false;
-            if (a->data.x_ptr.mut != b->data.x_ptr.mut)
-                return false;
-            switch (a->data.x_ptr.special) {
-                case ConstPtrSpecialInvalid:
-                    zig_unreachable();
-                case ConstPtrSpecialRef:
-                    if (a->data.x_ptr.data.ref.pointee != b->data.x_ptr.data.ref.pointee)
-                        return false;
-                    return true;
-                case ConstPtrSpecialBaseArray:
-                    if (a->data.x_ptr.data.base_array.array_val != b->data.x_ptr.data.base_array.array_val &&
-                        a->data.x_ptr.data.base_array.array_val->global_refs !=
-                        b->data.x_ptr.data.base_array.array_val->global_refs)
-                    {
-                        return false;
-                    }
-                    if (a->data.x_ptr.data.base_array.elem_index != b->data.x_ptr.data.base_array.elem_index)
-                        return false;
-                    if (a->data.x_ptr.data.base_array.is_cstr != b->data.x_ptr.data.base_array.is_cstr)
-                        return false;
-                    return true;
-                case ConstPtrSpecialBaseStruct:
-                    if (a->data.x_ptr.data.base_struct.struct_val != b->data.x_ptr.data.base_struct.struct_val &&
-                        a->data.x_ptr.data.base_struct.struct_val->global_refs !=
-                        b->data.x_ptr.data.base_struct.struct_val->global_refs)
-                    {
-                        return false;
-                    }
-                    if (a->data.x_ptr.data.base_struct.field_index != b->data.x_ptr.data.base_struct.field_index)
-                        return false;
-                    return true;
-                case ConstPtrSpecialHardCodedAddr:
-                    if (a->data.x_ptr.data.hard_coded_addr.addr != b->data.x_ptr.data.hard_coded_addr.addr)
-                        return false;
-                    return true;
-                case ConstPtrSpecialDiscard:
-                    return true;
-                case ConstPtrSpecialFunction:
-                    return a->data.x_ptr.data.fn.fn_entry == b->data.x_ptr.data.fn.fn_entry;
-            }
-            zig_unreachable();
+            return const_values_equal_ptr(a, b);
         case TypeTableEntryIdArray:
             zig_panic("TODO");
         case TypeTableEntryIdStruct:
@@ -5449,10 +5461,12 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) {
         case TypeTableEntryIdNull:
             zig_panic("TODO");
         case TypeTableEntryIdMaybe:
-            if (a->data.x_maybe == nullptr || b->data.x_maybe == nullptr) {
-                return (a->data.x_maybe == nullptr && b->data.x_maybe == nullptr);
+            if (get_codegen_ptr_type(a->type) != nullptr)
+                return const_values_equal_ptr(a, b);
+            if (a->data.x_nullable == nullptr || b->data.x_nullable == nullptr) {
+                return (a->data.x_nullable == nullptr && b->data.x_nullable == nullptr);
             } else {
-                return const_values_equal(a->data.x_maybe, b->data.x_maybe);
+                return const_values_equal(a->data.x_nullable, b->data.x_nullable);
             }
         case TypeTableEntryIdErrorUnion:
             zig_panic("TODO");
@@ -5525,6 +5539,41 @@ void eval_min_max_value(CodeGen *g, TypeTableEntry *type_entry, ConstExprValue *
     }
 }
 
+void render_const_val_ptr(CodeGen *g, Buf *buf, ConstExprValue *const_val, TypeTableEntry *type_entry) {
+    switch (const_val->data.x_ptr.special) {
+        case ConstPtrSpecialInvalid:
+            zig_unreachable();
+        case ConstPtrSpecialRef:
+        case ConstPtrSpecialBaseStruct:
+            buf_appendf(buf, "*");
+            render_const_value(g, buf, const_ptr_pointee(g, const_val));
+            return;
+        case ConstPtrSpecialBaseArray:
+            if (const_val->data.x_ptr.data.base_array.is_cstr) {
+                buf_appendf(buf, "*(c str lit)");
+                return;
+            } else {
+                buf_appendf(buf, "*");
+                render_const_value(g, buf, const_ptr_pointee(g, const_val));
+                return;
+            }
+        case ConstPtrSpecialHardCodedAddr:
+            buf_appendf(buf, "(*%s)(%" ZIG_PRI_x64 ")", buf_ptr(&type_entry->data.pointer.child_type->name),
+                    const_val->data.x_ptr.data.hard_coded_addr.addr);
+            return;
+        case ConstPtrSpecialDiscard:
+            buf_append_str(buf, "*_");
+            return;
+        case ConstPtrSpecialFunction:
+            {
+                FnTableEntry *fn_entry = const_val->data.x_ptr.data.fn.fn_entry;
+                buf_appendf(buf, "@ptrCast(%s, %s)", buf_ptr(&const_val->type->name), buf_ptr(&fn_entry->symbol_name));
+                return;
+            }
+    }
+    zig_unreachable();
+}
+
 void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) {
     switch (const_val->special) {
         case ConstValSpecialRuntime:
@@ -5601,38 +5650,7 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) {
                 return;
             }
         case TypeTableEntryIdPointer:
-            switch (const_val->data.x_ptr.special) {
-                case ConstPtrSpecialInvalid:
-                    zig_unreachable();
-                case ConstPtrSpecialRef:
-                case ConstPtrSpecialBaseStruct:
-                    buf_appendf(buf, "&");
-                    render_const_value(g, buf, const_ptr_pointee(g, const_val));
-                    return;
-                case ConstPtrSpecialBaseArray:
-                    if (const_val->data.x_ptr.data.base_array.is_cstr) {
-                        buf_appendf(buf, "&(c str lit)");
-                        return;
-                    } else {
-                        buf_appendf(buf, "&");
-                        render_const_value(g, buf, const_ptr_pointee(g, const_val));
-                        return;
-                    }
-                case ConstPtrSpecialHardCodedAddr:
-                    buf_appendf(buf, "(&%s)(%" ZIG_PRI_x64 ")", buf_ptr(&type_entry->data.pointer.child_type->name),
-                            const_val->data.x_ptr.data.hard_coded_addr.addr);
-                    return;
-                case ConstPtrSpecialDiscard:
-                    buf_append_str(buf, "&_");
-                    return;
-                case ConstPtrSpecialFunction:
-                    {
-                        FnTableEntry *fn_entry = const_val->data.x_ptr.data.fn.fn_entry;
-                        buf_appendf(buf, "@ptrCast(%s, %s)", buf_ptr(&const_val->type->name), buf_ptr(&fn_entry->symbol_name));
-                        return;
-                    }
-            }
-            zig_unreachable();
+            return render_const_val_ptr(g, buf, const_val, type_entry);
         case TypeTableEntryIdBlock:
             {
                 AstNode *node = const_val->data.x_block->source_node;
@@ -5692,8 +5710,10 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) {
             }
         case TypeTableEntryIdMaybe:
             {
-                if (const_val->data.x_maybe) {
-                    render_const_value(g, buf, const_val->data.x_maybe);
+                if (get_codegen_ptr_type(const_val->type) != nullptr)
+                    return render_const_val_ptr(g, buf, const_val, type_entry->data.maybe.child_type);
+                if (const_val->data.x_nullable) {
+                    render_const_value(g, buf, const_val->data.x_nullable);
                 } else {
                     buf_appendf(buf, "null");
                 }
src/codegen.cpp
@@ -5020,6 +5020,79 @@ static bool is_llvm_value_unnamed_type(TypeTableEntry *type_entry, LLVMValueRef
     return LLVMTypeOf(val) != type_entry->type_ref;
 }
 
+static LLVMValueRef gen_const_val_ptr(CodeGen *g, ConstExprValue *const_val, const char *name) {
+    render_const_val_global(g, const_val, name);
+    switch (const_val->data.x_ptr.special) {
+        case ConstPtrSpecialInvalid:
+        case ConstPtrSpecialDiscard:
+            zig_unreachable();
+        case ConstPtrSpecialRef:
+            {
+                ConstExprValue *pointee = const_val->data.x_ptr.data.ref.pointee;
+                render_const_val(g, pointee, "");
+                render_const_val_global(g, pointee, "");
+                ConstExprValue *other_val = pointee;
+                const_val->global_refs->llvm_value = LLVMConstBitCast(other_val->global_refs->llvm_global, const_val->type->type_ref);
+                render_const_val_global(g, const_val, "");
+                return const_val->global_refs->llvm_value;
+            }
+        case ConstPtrSpecialBaseArray:
+            {
+                ConstExprValue *array_const_val = const_val->data.x_ptr.data.base_array.array_val;
+                size_t elem_index = const_val->data.x_ptr.data.base_array.elem_index;
+                assert(array_const_val->type->id == TypeTableEntryIdArray);
+                if (array_const_val->type->zero_bits) {
+                    // make this a null pointer
+                    TypeTableEntry *usize = g->builtin_types.entry_usize;
+                    const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref),
+                            const_val->type->type_ref);
+                    render_const_val_global(g, const_val, "");
+                    return const_val->global_refs->llvm_value;
+                }
+                LLVMValueRef uncasted_ptr_val = gen_const_ptr_array_recursive(g, array_const_val,
+                        elem_index);
+                LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref);
+                const_val->global_refs->llvm_value = ptr_val;
+                render_const_val_global(g, const_val, "");
+                return ptr_val;
+            }
+        case ConstPtrSpecialBaseStruct:
+            {
+                ConstExprValue *struct_const_val = const_val->data.x_ptr.data.base_struct.struct_val;
+                assert(struct_const_val->type->id == TypeTableEntryIdStruct);
+                if (struct_const_val->type->zero_bits) {
+                    // make this a null pointer
+                    TypeTableEntry *usize = g->builtin_types.entry_usize;
+                    const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref),
+                            const_val->type->type_ref);
+                    render_const_val_global(g, const_val, "");
+                    return const_val->global_refs->llvm_value;
+                }
+                size_t src_field_index = const_val->data.x_ptr.data.base_struct.field_index;
+                size_t gen_field_index =
+                    struct_const_val->type->data.structure.fields[src_field_index].gen_index;
+                LLVMValueRef uncasted_ptr_val = gen_const_ptr_struct_recursive(g, struct_const_val,
+                        gen_field_index);
+                LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref);
+                const_val->global_refs->llvm_value = ptr_val;
+                render_const_val_global(g, const_val, "");
+                return ptr_val;
+            }
+        case ConstPtrSpecialHardCodedAddr:
+            {
+                uint64_t addr_value = const_val->data.x_ptr.data.hard_coded_addr.addr;
+                TypeTableEntry *usize = g->builtin_types.entry_usize;
+                const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstInt(usize->type_ref, addr_value, false),
+                        const_val->type->type_ref);
+                render_const_val_global(g, const_val, "");
+                return const_val->global_refs->llvm_value;
+            }
+        case ConstPtrSpecialFunction:
+            return LLVMConstBitCast(fn_llvm_value(g, const_val->data.x_ptr.data.fn.fn_entry), const_val->type->type_ref);
+    }
+    zig_unreachable();
+}
+
 static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const char *name) {
     TypeTableEntry *type_entry = const_val->type;
     assert(!type_entry->zero_bits);
@@ -5068,19 +5141,15 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c
             {
                 TypeTableEntry *child_type = type_entry->data.maybe.child_type;
                 if (child_type->zero_bits) {
-                    return LLVMConstInt(LLVMInt1Type(), const_val->data.x_maybe ? 1 : 0, false);
+                    return LLVMConstInt(LLVMInt1Type(), const_val->data.x_nullable ? 1 : 0, false);
                 } else if (type_is_codegen_pointer(child_type)) {
-                    if (const_val->data.x_maybe) {
-                        return gen_const_val(g, const_val->data.x_maybe, "");
-                    } else {
-                        return LLVMConstNull(child_type->type_ref);
-                    }
+                    return gen_const_val_ptr(g, const_val, name);
                 } else {
                     LLVMValueRef child_val;
                     LLVMValueRef maybe_val;
                     bool make_unnamed_struct;
-                    if (const_val->data.x_maybe) {
-                        child_val = gen_const_val(g, const_val->data.x_maybe, "");
+                    if (const_val->data.x_nullable) {
+                        child_val = gen_const_val(g, const_val->data.x_nullable, "");
                         maybe_val = LLVMConstAllOnes(LLVMInt1Type());
 
                         make_unnamed_struct = is_llvm_value_unnamed_type(const_val->type, child_val);
@@ -5270,78 +5339,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c
             assert(const_val->data.x_ptr.mut == ConstPtrMutComptimeConst);
             return fn_llvm_value(g, const_val->data.x_ptr.data.fn.fn_entry);
         case TypeTableEntryIdPointer:
-            {
-                render_const_val_global(g, const_val, name);
-                switch (const_val->data.x_ptr.special) {
-                    case ConstPtrSpecialInvalid:
-                    case ConstPtrSpecialDiscard:
-                        zig_unreachable();
-                    case ConstPtrSpecialRef:
-                        {
-                            ConstExprValue *pointee = const_val->data.x_ptr.data.ref.pointee;
-                            render_const_val(g, pointee, "");
-                            render_const_val_global(g, pointee, "");
-                            ConstExprValue *other_val = pointee;
-                            const_val->global_refs->llvm_value = LLVMConstBitCast(other_val->global_refs->llvm_global, const_val->type->type_ref);
-                            render_const_val_global(g, const_val, "");
-                            return const_val->global_refs->llvm_value;
-                        }
-                    case ConstPtrSpecialBaseArray:
-                        {
-                            ConstExprValue *array_const_val = const_val->data.x_ptr.data.base_array.array_val;
-                            size_t elem_index = const_val->data.x_ptr.data.base_array.elem_index;
-                            assert(array_const_val->type->id == TypeTableEntryIdArray);
-                            if (array_const_val->type->zero_bits) {
-                                // make this a null pointer
-                                TypeTableEntry *usize = g->builtin_types.entry_usize;
-                                const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref),
-                                        const_val->type->type_ref);
-                                render_const_val_global(g, const_val, "");
-                                return const_val->global_refs->llvm_value;
-                            }
-                            LLVMValueRef uncasted_ptr_val = gen_const_ptr_array_recursive(g, array_const_val,
-                                    elem_index);
-                            LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref);
-                            const_val->global_refs->llvm_value = ptr_val;
-                            render_const_val_global(g, const_val, "");
-                            return ptr_val;
-                        }
-                    case ConstPtrSpecialBaseStruct:
-                        {
-                            ConstExprValue *struct_const_val = const_val->data.x_ptr.data.base_struct.struct_val;
-                            assert(struct_const_val->type->id == TypeTableEntryIdStruct);
-                            if (struct_const_val->type->zero_bits) {
-                                // make this a null pointer
-                                TypeTableEntry *usize = g->builtin_types.entry_usize;
-                                const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref),
-                                        const_val->type->type_ref);
-                                render_const_val_global(g, const_val, "");
-                                return const_val->global_refs->llvm_value;
-                            }
-                            size_t src_field_index = const_val->data.x_ptr.data.base_struct.field_index;
-                            size_t gen_field_index =
-                                struct_const_val->type->data.structure.fields[src_field_index].gen_index;
-                            LLVMValueRef uncasted_ptr_val = gen_const_ptr_struct_recursive(g, struct_const_val,
-                                    gen_field_index);
-                            LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref);
-                            const_val->global_refs->llvm_value = ptr_val;
-                            render_const_val_global(g, const_val, "");
-                            return ptr_val;
-                        }
-                    case ConstPtrSpecialHardCodedAddr:
-                        {
-                            uint64_t addr_value = const_val->data.x_ptr.data.hard_coded_addr.addr;
-                            TypeTableEntry *usize = g->builtin_types.entry_usize;
-                            const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstInt(usize->type_ref, addr_value, false),
-                                    const_val->type->type_ref);
-                            render_const_val_global(g, const_val, "");
-                            return const_val->global_refs->llvm_value;
-                        }
-                    case ConstPtrSpecialFunction:
-                        return LLVMConstBitCast(fn_llvm_value(g, const_val->data.x_ptr.data.fn.fn_entry), const_val->type->type_ref);
-                }
-            }
-            zig_unreachable();
+            return gen_const_val_ptr(g, const_val, name);
         case TypeTableEntryIdErrorUnion:
             {
                 TypeTableEntry *payload_type = type_entry->data.error_union.payload_type;
src/ir.cpp
@@ -62,6 +62,7 @@ enum ConstCastResultId {
     ConstCastResultIdType,
     ConstCastResultIdUnresolvedInferredErrSet,
     ConstCastResultIdAsyncAllocatorType,
+    ConstCastResultIdNullWrapPtr,
 };
 
 struct ConstCastErrSetMismatch {
@@ -90,6 +91,7 @@ struct ConstCastOnly {
         ConstCastOnly *error_union_error_set;
         ConstCastOnly *return_type;
         ConstCastOnly *async_allocator_type;
+        ConstCastOnly *null_wrap_ptr_child;
         ConstCastArg fn_arg;
         ConstCastArgNoAlias arg_no_alias;
     } data;
@@ -7660,6 +7662,21 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry
     if (expected_type == actual_type)
         return result;
 
+    // * and [*] can do a const-cast-only to ?* and ?[*], respectively
+    if (expected_type->id == TypeTableEntryIdMaybe &&
+        expected_type->data.maybe.child_type->id == TypeTableEntryIdPointer &&
+        actual_type->id == TypeTableEntryIdPointer)
+    {
+        ConstCastOnly child = types_match_const_cast_only(ira,
+                expected_type->data.maybe.child_type, actual_type, source_node);
+        if (child.id != ConstCastResultIdOk) {
+            result.id = ConstCastResultIdNullWrapPtr;
+            result.data.null_wrap_ptr_child = allocate_nonzero<ConstCastOnly>(1);
+            *result.data.null_wrap_ptr_child = child;
+        }
+        return result;
+    }
+
     // pointer const
     if (expected_type->id == TypeTableEntryIdPointer &&
         actual_type->id == TypeTableEntryIdPointer &&
@@ -8741,7 +8758,8 @@ static void eval_const_expr_implicit_cast(CastOp cast_op,
             zig_panic("TODO");
         case CastOpNoop:
             {
-                copy_const_val(const_val, other_val, other_val->special == ConstValSpecialStatic);
+                bool same_global_refs = other_val->special == ConstValSpecialStatic;
+                copy_const_val(const_val, other_val, same_global_refs);
                 const_val->type = new_type;
                 break;
             }
@@ -9189,9 +9207,13 @@ static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *sourc
 
         IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(&ira->new_irb,
                 source_instr->scope, source_instr->source_node);
-        const_instruction->base.value.type = wanted_type;
         const_instruction->base.value.special = ConstValSpecialStatic;
-        const_instruction->base.value.data.x_maybe = val;
+        if (get_codegen_ptr_type(wanted_type) != nullptr) {
+            copy_const_val(&const_instruction->base.value, val, val->data.x_ptr.mut == ConstPtrMutComptimeConst);
+        } else {
+            const_instruction->base.value.data.x_nullable = val;
+        }
+        const_instruction->base.value.type = wanted_type;
         return &const_instruction->base;
     }
 
@@ -9346,9 +9368,14 @@ static IrInstruction *ir_analyze_null_to_maybe(IrAnalyze *ira, IrInstruction *so
     assert(val);
 
     IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(&ira->new_irb, source_instr->scope, source_instr->source_node);
-    const_instruction->base.value.type = wanted_type;
     const_instruction->base.value.special = ConstValSpecialStatic;
-    const_instruction->base.value.data.x_maybe = nullptr;
+    if (get_codegen_ptr_type(wanted_type) != nullptr) {
+        const_instruction->base.value.data.x_ptr.special = ConstPtrSpecialHardCodedAddr;
+        const_instruction->base.value.data.x_ptr.data.hard_coded_addr.addr = 0;
+    } else {
+        const_instruction->base.value.data.x_nullable = nullptr;
+    }
+    const_instruction->base.value.type = wanted_type;
     return &const_instruction->base;
 }
 
@@ -10062,7 +10089,8 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
     }
 
 
-    // explicit cast from child type of maybe type to maybe type
+    // explicit cast from T to ?T
+    // note that the *T to ?*T case is handled via the "ConstCastOnly" mechanism
     if (wanted_type->id == TypeTableEntryIdMaybe) {
         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) {
@@ -10113,7 +10141,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
         }
     }
 
-    // explicit cast from [N]T to %[]const T
+    // explicit cast from [N]T to E![]const T
     if (wanted_type->id == TypeTableEntryIdErrorUnion &&
         is_slice(wanted_type->data.error_union.payload_type) &&
         actual_type->id == TypeTableEntryIdArray)
@@ -10143,7 +10171,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
         return ir_analyze_err_wrap_code(ira, source_instr, value, wanted_type);
     }
 
-    // explicit cast from T to %?T
+    // explicit cast from T to E!?T
     if (wanted_type->id == TypeTableEntryIdErrorUnion &&
         wanted_type->data.error_union.payload_type->id == TypeTableEntryIdMaybe &&
         actual_type->id != TypeTableEntryIdMaybe)
@@ -10167,7 +10195,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
     }
 
     // explicit cast from number literal to another type
-    // explicit cast from number literal to &const integer
+    // explicit cast from number literal to *const integer
     if (actual_type->id == TypeTableEntryIdComptimeFloat ||
         actual_type->id == TypeTableEntryIdComptimeInt)
     {
@@ -10391,6 +10419,7 @@ static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruc
                     IrInstruction *result = ir_create_const(&ira->new_irb, source_instruction->scope,
                         source_instruction->source_node, child_type);
                     copy_const_val(&result->value, pointee, ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst);
+                    result->value.type = child_type;
                     return result;
                 }
             }
@@ -10708,6 +10737,16 @@ static bool resolve_cmp_op_id(IrBinOp op_id, Cmp cmp) {
     }
 }
 
+static bool nullable_value_is_null(ConstExprValue *val) {
+    assert(val->special == ConstValSpecialStatic);
+    if (get_codegen_ptr_type(val->type) != nullptr) {
+        return val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr &&
+            val->data.x_ptr.data.hard_coded_addr.addr == 0;
+    } else {
+        return val->data.x_nullable == nullptr;
+    }
+}
+
 static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) {
     IrInstruction *op1 = bin_op_instruction->op1->other;
     IrInstruction *op2 = bin_op_instruction->op2->other;
@@ -10737,7 +10776,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp
             ConstExprValue *maybe_val = ir_resolve_const(ira, maybe_op, UndefBad);
             if (!maybe_val)
                 return ira->codegen->builtin_types.entry_invalid;
-            bool is_null = (maybe_val->data.x_maybe == nullptr);
+            bool is_null = nullable_value_is_null(maybe_val);
             ConstExprValue *out_val = ir_build_const_from(ira, &bin_op_instruction->base);
             out_val->data.x_bool = (op_id == IrBinOpCmpEq) ? is_null : !is_null;
             return ira->codegen->builtin_types.entry_bool;
@@ -12015,7 +12054,9 @@ static TypeTableEntry *ir_analyze_instruction_error_return_trace(IrAnalyze *ira,
         TypeTableEntry *nullable_type = get_maybe_type(ira->codegen, ptr_to_stack_trace_type);
         if (!exec_has_err_ret_trace(ira->codegen, ira->new_irb.exec)) {
             ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
-            out_val->data.x_maybe = nullptr;
+            assert(get_codegen_ptr_type(nullable_type) != nullptr);
+            out_val->data.x_ptr.special = ConstPtrSpecialHardCodedAddr;
+            out_val->data.x_ptr.data.hard_coded_addr.addr = 0;
             return nullable_type;
         }
         IrInstruction *new_instruction = ir_build_error_return_trace(&ira->new_irb, instruction->base.scope,
@@ -14207,6 +14248,9 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru
 
 static TypeTableEntry *ir_analyze_instruction_load_ptr(IrAnalyze *ira, IrInstructionLoadPtr *load_ptr_instruction) {
     IrInstruction *ptr = load_ptr_instruction->ptr->other;
+    if (type_is_invalid(ptr->value.type))
+        return ira->codegen->builtin_types.entry_invalid;
+
     IrInstruction *result = ir_get_deref(ira, &load_ptr_instruction->base, ptr);
     ir_link_new_instruction(result, &load_ptr_instruction->base);
     assert(result->value.type);
@@ -14773,7 +14817,7 @@ static TypeTableEntry *ir_analyze_instruction_test_non_null(IrAnalyze *ira, IrIn
                 return ira->codegen->builtin_types.entry_invalid;
 
             ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
-            out_val->data.x_bool = (maybe_val->data.x_maybe != nullptr);
+            out_val->data.x_bool = !nullable_value_is_null(maybe_val);
             return ira->codegen->builtin_types.entry_bool;
         }
 
@@ -14837,13 +14881,18 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_maybe(IrAnalyze *ira,
         ConstExprValue *maybe_val = const_ptr_pointee(ira->codegen, val);
 
         if (val->data.x_ptr.mut != ConstPtrMutRuntimeVar) {
-            if (!maybe_val->data.x_maybe) {
+            if (nullable_value_is_null(maybe_val)) {
                 ir_add_error(ira, &unwrap_maybe_instruction->base, buf_sprintf("unable to unwrap null"));
                 return ira->codegen->builtin_types.entry_invalid;
             }
             ConstExprValue *out_val = ir_build_const_from(ira, &unwrap_maybe_instruction->base);
             out_val->data.x_ptr.special = ConstPtrSpecialRef;
-            out_val->data.x_ptr.data.ref.pointee = maybe_val->data.x_maybe;
+            out_val->data.x_ptr.mut = val->data.x_ptr.mut;
+            if (type_is_codegen_pointer(child_type)) {
+                out_val->data.x_ptr.data.ref.pointee = maybe_val;
+            } else {
+                out_val->data.x_ptr.data.ref.pointee = maybe_val->data.x_nullable;
+            }
             return result_type;
         }
     }
@@ -16206,12 +16255,12 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop
                         0, 0);
                     fn_def_fields[6].type = get_maybe_type(ira->codegen, get_slice_type(ira->codegen, u8_ptr));
                     if (fn_node->is_extern && buf_len(fn_node->lib_name) > 0) {
-                        fn_def_fields[6].data.x_maybe = create_const_vals(1);
+                        fn_def_fields[6].data.x_nullable = create_const_vals(1);
                         ConstExprValue *lib_name = create_const_str_lit(ira->codegen, fn_node->lib_name);
-                        init_const_slice(ira->codegen, fn_def_fields[6].data.x_maybe, lib_name, 0, buf_len(fn_node->lib_name), true);
+                        init_const_slice(ira->codegen, fn_def_fields[6].data.x_nullable, lib_name, 0, buf_len(fn_node->lib_name), true);
+                    } else {
+                        fn_def_fields[6].data.x_nullable = nullptr;
                     }
-                    else
-                        fn_def_fields[6].data.x_maybe = nullptr;
                     // return_type: type
                     ensure_field_index(fn_def_val->type, "return_type", 7);
                     fn_def_fields[7].special = ConstValSpecialStatic;
@@ -16664,8 +16713,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
 
                 TypeTableEntry *type_info_enum_field_type = ir_type_info_get_type(ira, "EnumField");
 
-                for (uint32_t union_field_index = 0; union_field_index < union_field_count; union_field_index++)
-                {
+                for (uint32_t union_field_index = 0; union_field_index < union_field_count; union_field_index++) {
                     TypeUnionField *union_field = &type_entry->data.unionation.fields[union_field_index];
                     ConstExprValue *union_field_val = &union_field_array->data.x_array.s_none.elements[union_field_index];
 
@@ -16676,12 +16724,11 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
                     inner_fields[1].special = ConstValSpecialStatic;
                     inner_fields[1].type = get_maybe_type(ira->codegen, type_info_enum_field_type);
 
-                    if (fields[1].data.x_type == ira->codegen->builtin_types.entry_undef)
-                        inner_fields[1].data.x_maybe = nullptr;
-                    else
-                    {
-                        inner_fields[1].data.x_maybe = create_const_vals(1);
-                        make_enum_field_val(inner_fields[1].data.x_maybe, union_field->enum_field, type_info_enum_field_type);
+                    if (fields[1].data.x_type == ira->codegen->builtin_types.entry_undef) {
+                        inner_fields[1].data.x_nullable = nullptr;
+                    } else {
+                        inner_fields[1].data.x_nullable = create_const_vals(1);
+                        make_enum_field_val(inner_fields[1].data.x_nullable, union_field->enum_field, type_info_enum_field_type);
                     }
 
                     inner_fields[2].special = ConstValSpecialStatic;
@@ -16737,8 +16784,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
 
                 init_const_slice(ira->codegen, &fields[1], struct_field_array, 0, struct_field_count, false);
 
-                for (uint32_t struct_field_index = 0; struct_field_index < struct_field_count; struct_field_index++)
-                {
+                for (uint32_t struct_field_index = 0; struct_field_index < struct_field_count; struct_field_index++) {
                     TypeStructField *struct_field = &type_entry->data.structure.fields[struct_field_index];
                     ConstExprValue *struct_field_val = &struct_field_array->data.x_array.s_none.elements[struct_field_index];
 
@@ -16749,15 +16795,14 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
                     inner_fields[1].special = ConstValSpecialStatic;
                     inner_fields[1].type = get_maybe_type(ira->codegen, ira->codegen->builtin_types.entry_usize);
 
-                    if (!type_has_bits(struct_field->type_entry))
-                        inner_fields[1].data.x_maybe = nullptr;
-                    else
-                    {
+                    if (!type_has_bits(struct_field->type_entry)) {
+                        inner_fields[1].data.x_nullable = nullptr;
+                    } else {
                         size_t byte_offset = LLVMOffsetOfElement(ira->codegen->target_data_ref, type_entry->type_ref, struct_field->gen_index);
-                        inner_fields[1].data.x_maybe = create_const_vals(1);
-                        inner_fields[1].data.x_maybe->special = ConstValSpecialStatic;
-                        inner_fields[1].data.x_maybe->type = ira->codegen->builtin_types.entry_usize;
-                        bigint_init_unsigned(&inner_fields[1].data.x_maybe->data.x_bigint, byte_offset);
+                        inner_fields[1].data.x_nullable = create_const_vals(1);
+                        inner_fields[1].data.x_nullable->special = ConstValSpecialStatic;
+                        inner_fields[1].data.x_nullable->type = ira->codegen->builtin_types.entry_usize;
+                        bigint_init_unsigned(&inner_fields[1].data.x_nullable->data.x_bigint, byte_offset);
                     }
 
                     inner_fields[2].special = ConstValSpecialStatic;
@@ -19008,9 +19053,6 @@ static TypeTableEntry *ir_analyze_instruction_ptr_to_int(IrAnalyze *ira, IrInstr
         ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
         if (!val)
             return ira->codegen->builtin_types.entry_invalid;
-        if (target->value.type->id == TypeTableEntryIdMaybe) {
-            val = val->data.x_maybe;
-        }
         if (val->type->id == TypeTableEntryIdPointer && val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) {
             IrInstruction *result = ir_create_const(&ira->new_irb, instruction->base.scope,
                     instruction->base.source_node, usize);
@@ -19936,6 +19978,7 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
 static TypeTableEntry *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *instruction) {
     TypeTableEntry *instruction_type = ir_analyze_instruction_nocast(ira, instruction);
     instruction->value.type = instruction_type;
+
     if (instruction->other) {
         instruction->other->value.type = instruction_type;
     } else {
test/cases/cast.zig
@@ -1,5 +1,6 @@
-const assert = @import("std").debug.assert;
-const mem = @import("std").mem;
+const std = @import("std");
+const assert = std.debug.assert;
+const mem = std.mem;
 
 test "int to ptr cast" {
     const x = usize(13);
@@ -400,3 +401,8 @@ fn testCastPtrOfArrayToSliceAndPtr() void {
     assert(mem.eql(u8, array[0..], "coeu"));
 }
 
+test "cast *[1][*]const u8 to [*]const ?[*]const u8" {
+    const window_name = [1][*]const u8{c"window name"};
+    const x: [*]const ?[*]const u8 = &window_name;
+    assert(mem.eql(u8, std.cstr.toSliceConst(??x[0]), "window name"));
+}