authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-06-09 00:15:23-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-06-09 00:26:26-04:00
log6edd81109d16178f1dc688dacee4b38964b617c4
tree7ca6c9b9b183846af311245176d40927076f9304
parent1a9d2f3aae780873eefedaf3fdf095b3cd87b55f

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.

5 files changed, 322 insertions(+), 252 deletions(-)

src/all_types.hpp+4-1
......@@ -144,6 +144,9 @@ enum ConstPtrSpecial {
144144 // understand the value of pointee at compile time. However, we will still
145145 // emit a binary with a compile time known address.
146146 // In this case index is the numeric address value.
147 // We also use this for null pointer. We need the data layout for ConstCastOnly == true
148 // types to be the same, so all nullables of pointer types use x_ptr
149 // instead of x_nullable
147150 ConstPtrSpecialHardCodedAddr,
148151 // This means that the pointer represents memory of assigning to _.
149152 // That is, storing discards the data, and loading is invalid.
......@@ -251,7 +254,7 @@ struct ConstExprValue {
251254 bool x_bool;
252255 ConstBoundFnValue x_bound_fn;
253256 TypeTableEntry *x_type;
254 ConstExprValue *x_maybe;
257 ConstExprValue *x_nullable;
255258 ConstErrValue x_err_union;
256259 ErrorTableEntry *x_err_set;
257260 BigInt x_enum_tag;
src/analyze.cpp+150-130
......@@ -4578,6 +4578,52 @@ bool fn_type_id_eql(FnTypeId *a, FnTypeId *b) {
45784578 return true;
45794579}
45804580
4581static uint32_t hash_const_val_ptr(ConstExprValue *const_val) {
4582 uint32_t hash_val = 0;
4583 switch (const_val->data.x_ptr.mut) {
4584 case ConstPtrMutRuntimeVar:
4585 hash_val += (uint32_t)3500721036;
4586 break;
4587 case ConstPtrMutComptimeConst:
4588 hash_val += (uint32_t)4214318515;
4589 break;
4590 case ConstPtrMutComptimeVar:
4591 hash_val += (uint32_t)1103195694;
4592 break;
4593 }
4594 switch (const_val->data.x_ptr.special) {
4595 case ConstPtrSpecialInvalid:
4596 zig_unreachable();
4597 case ConstPtrSpecialRef:
4598 hash_val += (uint32_t)2478261866;
4599 hash_val += hash_ptr(const_val->data.x_ptr.data.ref.pointee);
4600 return hash_val;
4601 case ConstPtrSpecialBaseArray:
4602 hash_val += (uint32_t)1764906839;
4603 hash_val += hash_ptr(const_val->data.x_ptr.data.base_array.array_val);
4604 hash_val += hash_size(const_val->data.x_ptr.data.base_array.elem_index);
4605 hash_val += const_val->data.x_ptr.data.base_array.is_cstr ? 1297263887 : 200363492;
4606 return hash_val;
4607 case ConstPtrSpecialBaseStruct:
4608 hash_val += (uint32_t)3518317043;
4609 hash_val += hash_ptr(const_val->data.x_ptr.data.base_struct.struct_val);
4610 hash_val += hash_size(const_val->data.x_ptr.data.base_struct.field_index);
4611 return hash_val;
4612 case ConstPtrSpecialHardCodedAddr:
4613 hash_val += (uint32_t)4048518294;
4614 hash_val += hash_size(const_val->data.x_ptr.data.hard_coded_addr.addr);
4615 return hash_val;
4616 case ConstPtrSpecialDiscard:
4617 hash_val += 2010123162;
4618 return hash_val;
4619 case ConstPtrSpecialFunction:
4620 hash_val += (uint32_t)2590901619;
4621 hash_val += hash_ptr(const_val->data.x_ptr.data.fn.fn_entry);
4622 return hash_val;
4623 }
4624 zig_unreachable();
4625}
4626
45814627static uint32_t hash_const_val(ConstExprValue *const_val) {
45824628 assert(const_val->special == ConstValSpecialStatic);
45834629 switch (const_val->type->id) {
......@@ -4646,51 +4692,7 @@ static uint32_t hash_const_val(ConstExprValue *const_val) {
46464692 assert(const_val->data.x_ptr.special == ConstPtrSpecialFunction);
46474693 return 3677364617 ^ hash_ptr(const_val->data.x_ptr.data.fn.fn_entry);
46484694 case TypeTableEntryIdPointer:
4649 {
4650 uint32_t hash_val = 0;
4651 switch (const_val->data.x_ptr.mut) {
4652 case ConstPtrMutRuntimeVar:
4653 hash_val += (uint32_t)3500721036;
4654 break;
4655 case ConstPtrMutComptimeConst:
4656 hash_val += (uint32_t)4214318515;
4657 break;
4658 case ConstPtrMutComptimeVar:
4659 hash_val += (uint32_t)1103195694;
4660 break;
4661 }
4662 switch (const_val->data.x_ptr.special) {
4663 case ConstPtrSpecialInvalid:
4664 zig_unreachable();
4665 case ConstPtrSpecialRef:
4666 hash_val += (uint32_t)2478261866;
4667 hash_val += hash_ptr(const_val->data.x_ptr.data.ref.pointee);
4668 return hash_val;
4669 case ConstPtrSpecialBaseArray:
4670 hash_val += (uint32_t)1764906839;
4671 hash_val += hash_ptr(const_val->data.x_ptr.data.base_array.array_val);
4672 hash_val += hash_size(const_val->data.x_ptr.data.base_array.elem_index);
4673 hash_val += const_val->data.x_ptr.data.base_array.is_cstr ? 1297263887 : 200363492;
4674 return hash_val;
4675 case ConstPtrSpecialBaseStruct:
4676 hash_val += (uint32_t)3518317043;
4677 hash_val += hash_ptr(const_val->data.x_ptr.data.base_struct.struct_val);
4678 hash_val += hash_size(const_val->data.x_ptr.data.base_struct.field_index);
4679 return hash_val;
4680 case ConstPtrSpecialHardCodedAddr:
4681 hash_val += (uint32_t)4048518294;
4682 hash_val += hash_size(const_val->data.x_ptr.data.hard_coded_addr.addr);
4683 return hash_val;
4684 case ConstPtrSpecialDiscard:
4685 hash_val += 2010123162;
4686 return hash_val;
4687 case ConstPtrSpecialFunction:
4688 hash_val += (uint32_t)2590901619;
4689 hash_val += hash_ptr(const_val->data.x_ptr.data.fn.fn_entry);
4690 return hash_val;
4691 }
4692 zig_unreachable();
4693 }
4695 return hash_const_val_ptr(const_val);
46944696 case TypeTableEntryIdPromise:
46954697 // TODO better hashing algorithm
46964698 return 223048345;
......@@ -4708,10 +4710,14 @@ static uint32_t hash_const_val(ConstExprValue *const_val) {
47084710 // TODO better hashing algorithm
47094711 return 2709806591;
47104712 case TypeTableEntryIdMaybe:
4711 if (const_val->data.x_maybe) {
4712 return hash_const_val(const_val->data.x_maybe) * 1992916303;
4713 if (get_codegen_ptr_type(const_val->type) != nullptr) {
4714 return hash_const_val(const_val) * 1992916303;
47134715 } else {
4714 return 4016830364;
4716 if (const_val->data.x_nullable) {
4717 return hash_const_val(const_val->data.x_nullable) * 1992916303;
4718 } else {
4719 return 4016830364;
4720 }
47154721 }
47164722 case TypeTableEntryIdErrorUnion:
47174723 // TODO better hashing algorithm
......@@ -4812,9 +4818,11 @@ static bool can_mutate_comptime_var_state(ConstExprValue *value) {
48124818 return false;
48134819
48144820 case TypeTableEntryIdMaybe:
4815 if (value->data.x_maybe == nullptr)
4821 if (get_codegen_ptr_type(value->type) != nullptr)
4822 return value->data.x_ptr.mut == ConstPtrMutComptimeVar;
4823 if (value->data.x_nullable == nullptr)
48164824 return false;
4817 return can_mutate_comptime_var_state(value->data.x_maybe);
4825 return can_mutate_comptime_var_state(value->data.x_nullable);
48184826
48194827 case TypeTableEntryIdErrorUnion:
48204828 if (value->data.x_err_union.err != nullptr)
......@@ -5340,6 +5348,52 @@ bool ir_get_var_is_comptime(VariableTableEntry *var) {
53405348 return var->is_comptime->value.data.x_bool;
53415349}
53425350
5351bool const_values_equal_ptr(ConstExprValue *a, ConstExprValue *b) {
5352 if (a->data.x_ptr.special != b->data.x_ptr.special)
5353 return false;
5354 if (a->data.x_ptr.mut != b->data.x_ptr.mut)
5355 return false;
5356 switch (a->data.x_ptr.special) {
5357 case ConstPtrSpecialInvalid:
5358 zig_unreachable();
5359 case ConstPtrSpecialRef:
5360 if (a->data.x_ptr.data.ref.pointee != b->data.x_ptr.data.ref.pointee)
5361 return false;
5362 return true;
5363 case ConstPtrSpecialBaseArray:
5364 if (a->data.x_ptr.data.base_array.array_val != b->data.x_ptr.data.base_array.array_val &&
5365 a->data.x_ptr.data.base_array.array_val->global_refs !=
5366 b->data.x_ptr.data.base_array.array_val->global_refs)
5367 {
5368 return false;
5369 }
5370 if (a->data.x_ptr.data.base_array.elem_index != b->data.x_ptr.data.base_array.elem_index)
5371 return false;
5372 if (a->data.x_ptr.data.base_array.is_cstr != b->data.x_ptr.data.base_array.is_cstr)
5373 return false;
5374 return true;
5375 case ConstPtrSpecialBaseStruct:
5376 if (a->data.x_ptr.data.base_struct.struct_val != b->data.x_ptr.data.base_struct.struct_val &&
5377 a->data.x_ptr.data.base_struct.struct_val->global_refs !=
5378 b->data.x_ptr.data.base_struct.struct_val->global_refs)
5379 {
5380 return false;
5381 }
5382 if (a->data.x_ptr.data.base_struct.field_index != b->data.x_ptr.data.base_struct.field_index)
5383 return false;
5384 return true;
5385 case ConstPtrSpecialHardCodedAddr:
5386 if (a->data.x_ptr.data.hard_coded_addr.addr != b->data.x_ptr.data.hard_coded_addr.addr)
5387 return false;
5388 return true;
5389 case ConstPtrSpecialDiscard:
5390 return true;
5391 case ConstPtrSpecialFunction:
5392 return a->data.x_ptr.data.fn.fn_entry == b->data.x_ptr.data.fn.fn_entry;
5393 }
5394 zig_unreachable();
5395}
5396
53435397bool const_values_equal(ConstExprValue *a, ConstExprValue *b) {
53445398 assert(a->type->id == b->type->id);
53455399 assert(a->special == ConstValSpecialStatic);
......@@ -5391,49 +5445,7 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) {
53915445 return bigint_cmp(&a->data.x_bigint, &b->data.x_bigint) == CmpEQ;
53925446 case TypeTableEntryIdPointer:
53935447 case TypeTableEntryIdFn:
5394 if (a->data.x_ptr.special != b->data.x_ptr.special)
5395 return false;
5396 if (a->data.x_ptr.mut != b->data.x_ptr.mut)
5397 return false;
5398 switch (a->data.x_ptr.special) {
5399 case ConstPtrSpecialInvalid:
5400 zig_unreachable();
5401 case ConstPtrSpecialRef:
5402 if (a->data.x_ptr.data.ref.pointee != b->data.x_ptr.data.ref.pointee)
5403 return false;
5404 return true;
5405 case ConstPtrSpecialBaseArray:
5406 if (a->data.x_ptr.data.base_array.array_val != b->data.x_ptr.data.base_array.array_val &&
5407 a->data.x_ptr.data.base_array.array_val->global_refs !=
5408 b->data.x_ptr.data.base_array.array_val->global_refs)
5409 {
5410 return false;
5411 }
5412 if (a->data.x_ptr.data.base_array.elem_index != b->data.x_ptr.data.base_array.elem_index)
5413 return false;
5414 if (a->data.x_ptr.data.base_array.is_cstr != b->data.x_ptr.data.base_array.is_cstr)
5415 return false;
5416 return true;
5417 case ConstPtrSpecialBaseStruct:
5418 if (a->data.x_ptr.data.base_struct.struct_val != b->data.x_ptr.data.base_struct.struct_val &&
5419 a->data.x_ptr.data.base_struct.struct_val->global_refs !=
5420 b->data.x_ptr.data.base_struct.struct_val->global_refs)
5421 {
5422 return false;
5423 }
5424 if (a->data.x_ptr.data.base_struct.field_index != b->data.x_ptr.data.base_struct.field_index)
5425 return false;
5426 return true;
5427 case ConstPtrSpecialHardCodedAddr:
5428 if (a->data.x_ptr.data.hard_coded_addr.addr != b->data.x_ptr.data.hard_coded_addr.addr)
5429 return false;
5430 return true;
5431 case ConstPtrSpecialDiscard:
5432 return true;
5433 case ConstPtrSpecialFunction:
5434 return a->data.x_ptr.data.fn.fn_entry == b->data.x_ptr.data.fn.fn_entry;
5435 }
5436 zig_unreachable();
5448 return const_values_equal_ptr(a, b);
54375449 case TypeTableEntryIdArray:
54385450 zig_panic("TODO");
54395451 case TypeTableEntryIdStruct:
......@@ -5449,10 +5461,12 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) {
54495461 case TypeTableEntryIdNull:
54505462 zig_panic("TODO");
54515463 case TypeTableEntryIdMaybe:
5452 if (a->data.x_maybe == nullptr || b->data.x_maybe == nullptr) {
5453 return (a->data.x_maybe == nullptr && b->data.x_maybe == nullptr);
5464 if (get_codegen_ptr_type(a->type) != nullptr)
5465 return const_values_equal_ptr(a, b);
5466 if (a->data.x_nullable == nullptr || b->data.x_nullable == nullptr) {
5467 return (a->data.x_nullable == nullptr && b->data.x_nullable == nullptr);
54545468 } else {
5455 return const_values_equal(a->data.x_maybe, b->data.x_maybe);
5469 return const_values_equal(a->data.x_nullable, b->data.x_nullable);
54565470 }
54575471 case TypeTableEntryIdErrorUnion:
54585472 zig_panic("TODO");
......@@ -5525,6 +5539,41 @@ void eval_min_max_value(CodeGen *g, TypeTableEntry *type_entry, ConstExprValue *
55255539 }
55265540}
55275541
5542void render_const_val_ptr(CodeGen *g, Buf *buf, ConstExprValue *const_val, TypeTableEntry *type_entry) {
5543 switch (const_val->data.x_ptr.special) {
5544 case ConstPtrSpecialInvalid:
5545 zig_unreachable();
5546 case ConstPtrSpecialRef:
5547 case ConstPtrSpecialBaseStruct:
5548 buf_appendf(buf, "*");
5549 render_const_value(g, buf, const_ptr_pointee(g, const_val));
5550 return;
5551 case ConstPtrSpecialBaseArray:
5552 if (const_val->data.x_ptr.data.base_array.is_cstr) {
5553 buf_appendf(buf, "*(c str lit)");
5554 return;
5555 } else {
5556 buf_appendf(buf, "*");
5557 render_const_value(g, buf, const_ptr_pointee(g, const_val));
5558 return;
5559 }
5560 case ConstPtrSpecialHardCodedAddr:
5561 buf_appendf(buf, "(*%s)(%" ZIG_PRI_x64 ")", buf_ptr(&type_entry->data.pointer.child_type->name),
5562 const_val->data.x_ptr.data.hard_coded_addr.addr);
5563 return;
5564 case ConstPtrSpecialDiscard:
5565 buf_append_str(buf, "*_");
5566 return;
5567 case ConstPtrSpecialFunction:
5568 {
5569 FnTableEntry *fn_entry = const_val->data.x_ptr.data.fn.fn_entry;
5570 buf_appendf(buf, "@ptrCast(%s, %s)", buf_ptr(&const_val->type->name), buf_ptr(&fn_entry->symbol_name));
5571 return;
5572 }
5573 }
5574 zig_unreachable();
5575}
5576
55285577void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) {
55295578 switch (const_val->special) {
55305579 case ConstValSpecialRuntime:
......@@ -5601,38 +5650,7 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) {
56015650 return;
56025651 }
56035652 case TypeTableEntryIdPointer:
5604 switch (const_val->data.x_ptr.special) {
5605 case ConstPtrSpecialInvalid:
5606 zig_unreachable();
5607 case ConstPtrSpecialRef:
5608 case ConstPtrSpecialBaseStruct:
5609 buf_appendf(buf, "&");
5610 render_const_value(g, buf, const_ptr_pointee(g, const_val));
5611 return;
5612 case ConstPtrSpecialBaseArray:
5613 if (const_val->data.x_ptr.data.base_array.is_cstr) {
5614 buf_appendf(buf, "&(c str lit)");
5615 return;
5616 } else {
5617 buf_appendf(buf, "&");
5618 render_const_value(g, buf, const_ptr_pointee(g, const_val));
5619 return;
5620 }
5621 case ConstPtrSpecialHardCodedAddr:
5622 buf_appendf(buf, "(&%s)(%" ZIG_PRI_x64 ")", buf_ptr(&type_entry->data.pointer.child_type->name),
5623 const_val->data.x_ptr.data.hard_coded_addr.addr);
5624 return;
5625 case ConstPtrSpecialDiscard:
5626 buf_append_str(buf, "&_");
5627 return;
5628 case ConstPtrSpecialFunction:
5629 {
5630 FnTableEntry *fn_entry = const_val->data.x_ptr.data.fn.fn_entry;
5631 buf_appendf(buf, "@ptrCast(%s, %s)", buf_ptr(&const_val->type->name), buf_ptr(&fn_entry->symbol_name));
5632 return;
5633 }
5634 }
5635 zig_unreachable();
5653 return render_const_val_ptr(g, buf, const_val, type_entry);
56365654 case TypeTableEntryIdBlock:
56375655 {
56385656 AstNode *node = const_val->data.x_block->source_node;
......@@ -5692,8 +5710,10 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) {
56925710 }
56935711 case TypeTableEntryIdMaybe:
56945712 {
5695 if (const_val->data.x_maybe) {
5696 render_const_value(g, buf, const_val->data.x_maybe);
5713 if (get_codegen_ptr_type(const_val->type) != nullptr)
5714 return render_const_val_ptr(g, buf, const_val, type_entry->data.maybe.child_type);
5715 if (const_val->data.x_nullable) {
5716 render_const_value(g, buf, const_val->data.x_nullable);
56975717 } else {
56985718 buf_appendf(buf, "null");
56995719 }
src/codegen.cpp+78-80
......@@ -5020,6 +5020,79 @@ static bool is_llvm_value_unnamed_type(TypeTableEntry *type_entry, LLVMValueRef
50205020 return LLVMTypeOf(val) != type_entry->type_ref;
50215021}
50225022
5023static LLVMValueRef gen_const_val_ptr(CodeGen *g, ConstExprValue *const_val, const char *name) {
5024 render_const_val_global(g, const_val, name);
5025 switch (const_val->data.x_ptr.special) {
5026 case ConstPtrSpecialInvalid:
5027 case ConstPtrSpecialDiscard:
5028 zig_unreachable();
5029 case ConstPtrSpecialRef:
5030 {
5031 ConstExprValue *pointee = const_val->data.x_ptr.data.ref.pointee;
5032 render_const_val(g, pointee, "");
5033 render_const_val_global(g, pointee, "");
5034 ConstExprValue *other_val = pointee;
5035 const_val->global_refs->llvm_value = LLVMConstBitCast(other_val->global_refs->llvm_global, const_val->type->type_ref);
5036 render_const_val_global(g, const_val, "");
5037 return const_val->global_refs->llvm_value;
5038 }
5039 case ConstPtrSpecialBaseArray:
5040 {
5041 ConstExprValue *array_const_val = const_val->data.x_ptr.data.base_array.array_val;
5042 size_t elem_index = const_val->data.x_ptr.data.base_array.elem_index;
5043 assert(array_const_val->type->id == TypeTableEntryIdArray);
5044 if (array_const_val->type->zero_bits) {
5045 // make this a null pointer
5046 TypeTableEntry *usize = g->builtin_types.entry_usize;
5047 const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref),
5048 const_val->type->type_ref);
5049 render_const_val_global(g, const_val, "");
5050 return const_val->global_refs->llvm_value;
5051 }
5052 LLVMValueRef uncasted_ptr_val = gen_const_ptr_array_recursive(g, array_const_val,
5053 elem_index);
5054 LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref);
5055 const_val->global_refs->llvm_value = ptr_val;
5056 render_const_val_global(g, const_val, "");
5057 return ptr_val;
5058 }
5059 case ConstPtrSpecialBaseStruct:
5060 {
5061 ConstExprValue *struct_const_val = const_val->data.x_ptr.data.base_struct.struct_val;
5062 assert(struct_const_val->type->id == TypeTableEntryIdStruct);
5063 if (struct_const_val->type->zero_bits) {
5064 // make this a null pointer
5065 TypeTableEntry *usize = g->builtin_types.entry_usize;
5066 const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref),
5067 const_val->type->type_ref);
5068 render_const_val_global(g, const_val, "");
5069 return const_val->global_refs->llvm_value;
5070 }
5071 size_t src_field_index = const_val->data.x_ptr.data.base_struct.field_index;
5072 size_t gen_field_index =
5073 struct_const_val->type->data.structure.fields[src_field_index].gen_index;
5074 LLVMValueRef uncasted_ptr_val = gen_const_ptr_struct_recursive(g, struct_const_val,
5075 gen_field_index);
5076 LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref);
5077 const_val->global_refs->llvm_value = ptr_val;
5078 render_const_val_global(g, const_val, "");
5079 return ptr_val;
5080 }
5081 case ConstPtrSpecialHardCodedAddr:
5082 {
5083 uint64_t addr_value = const_val->data.x_ptr.data.hard_coded_addr.addr;
5084 TypeTableEntry *usize = g->builtin_types.entry_usize;
5085 const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstInt(usize->type_ref, addr_value, false),
5086 const_val->type->type_ref);
5087 render_const_val_global(g, const_val, "");
5088 return const_val->global_refs->llvm_value;
5089 }
5090 case ConstPtrSpecialFunction:
5091 return LLVMConstBitCast(fn_llvm_value(g, const_val->data.x_ptr.data.fn.fn_entry), const_val->type->type_ref);
5092 }
5093 zig_unreachable();
5094}
5095
50235096static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const char *name) {
50245097 TypeTableEntry *type_entry = const_val->type;
50255098 assert(!type_entry->zero_bits);
......@@ -5068,19 +5141,15 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c
50685141 {
50695142 TypeTableEntry *child_type = type_entry->data.maybe.child_type;
50705143 if (child_type->zero_bits) {
5071 return LLVMConstInt(LLVMInt1Type(), const_val->data.x_maybe ? 1 : 0, false);
5144 return LLVMConstInt(LLVMInt1Type(), const_val->data.x_nullable ? 1 : 0, false);
50725145 } else if (type_is_codegen_pointer(child_type)) {
5073 if (const_val->data.x_maybe) {
5074 return gen_const_val(g, const_val->data.x_maybe, "");
5075 } else {
5076 return LLVMConstNull(child_type->type_ref);
5077 }
5146 return gen_const_val_ptr(g, const_val, name);
50785147 } else {
50795148 LLVMValueRef child_val;
50805149 LLVMValueRef maybe_val;
50815150 bool make_unnamed_struct;
5082 if (const_val->data.x_maybe) {
5083 child_val = gen_const_val(g, const_val->data.x_maybe, "");
5151 if (const_val->data.x_nullable) {
5152 child_val = gen_const_val(g, const_val->data.x_nullable, "");
50845153 maybe_val = LLVMConstAllOnes(LLVMInt1Type());
50855154
50865155 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
52705339 assert(const_val->data.x_ptr.mut == ConstPtrMutComptimeConst);
52715340 return fn_llvm_value(g, const_val->data.x_ptr.data.fn.fn_entry);
52725341 case TypeTableEntryIdPointer:
5273 {
5274 render_const_val_global(g, const_val, name);
5275 switch (const_val->data.x_ptr.special) {
5276 case ConstPtrSpecialInvalid:
5277 case ConstPtrSpecialDiscard:
5278 zig_unreachable();
5279 case ConstPtrSpecialRef:
5280 {
5281 ConstExprValue *pointee = const_val->data.x_ptr.data.ref.pointee;
5282 render_const_val(g, pointee, "");
5283 render_const_val_global(g, pointee, "");
5284 ConstExprValue *other_val = pointee;
5285 const_val->global_refs->llvm_value = LLVMConstBitCast(other_val->global_refs->llvm_global, const_val->type->type_ref);
5286 render_const_val_global(g, const_val, "");
5287 return const_val->global_refs->llvm_value;
5288 }
5289 case ConstPtrSpecialBaseArray:
5290 {
5291 ConstExprValue *array_const_val = const_val->data.x_ptr.data.base_array.array_val;
5292 size_t elem_index = const_val->data.x_ptr.data.base_array.elem_index;
5293 assert(array_const_val->type->id == TypeTableEntryIdArray);
5294 if (array_const_val->type->zero_bits) {
5295 // make this a null pointer
5296 TypeTableEntry *usize = g->builtin_types.entry_usize;
5297 const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref),
5298 const_val->type->type_ref);
5299 render_const_val_global(g, const_val, "");
5300 return const_val->global_refs->llvm_value;
5301 }
5302 LLVMValueRef uncasted_ptr_val = gen_const_ptr_array_recursive(g, array_const_val,
5303 elem_index);
5304 LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref);
5305 const_val->global_refs->llvm_value = ptr_val;
5306 render_const_val_global(g, const_val, "");
5307 return ptr_val;
5308 }
5309 case ConstPtrSpecialBaseStruct:
5310 {
5311 ConstExprValue *struct_const_val = const_val->data.x_ptr.data.base_struct.struct_val;
5312 assert(struct_const_val->type->id == TypeTableEntryIdStruct);
5313 if (struct_const_val->type->zero_bits) {
5314 // make this a null pointer
5315 TypeTableEntry *usize = g->builtin_types.entry_usize;
5316 const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref),
5317 const_val->type->type_ref);
5318 render_const_val_global(g, const_val, "");
5319 return const_val->global_refs->llvm_value;
5320 }
5321 size_t src_field_index = const_val->data.x_ptr.data.base_struct.field_index;
5322 size_t gen_field_index =
5323 struct_const_val->type->data.structure.fields[src_field_index].gen_index;
5324 LLVMValueRef uncasted_ptr_val = gen_const_ptr_struct_recursive(g, struct_const_val,
5325 gen_field_index);
5326 LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref);
5327 const_val->global_refs->llvm_value = ptr_val;
5328 render_const_val_global(g, const_val, "");
5329 return ptr_val;
5330 }
5331 case ConstPtrSpecialHardCodedAddr:
5332 {
5333 uint64_t addr_value = const_val->data.x_ptr.data.hard_coded_addr.addr;
5334 TypeTableEntry *usize = g->builtin_types.entry_usize;
5335 const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstInt(usize->type_ref, addr_value, false),
5336 const_val->type->type_ref);
5337 render_const_val_global(g, const_val, "");
5338 return const_val->global_refs->llvm_value;
5339 }
5340 case ConstPtrSpecialFunction:
5341 return LLVMConstBitCast(fn_llvm_value(g, const_val->data.x_ptr.data.fn.fn_entry), const_val->type->type_ref);
5342 }
5343 }
5344 zig_unreachable();
5342 return gen_const_val_ptr(g, const_val, name);
53455343 case TypeTableEntryIdErrorUnion:
53465344 {
53475345 TypeTableEntry *payload_type = type_entry->data.error_union.payload_type;
src/ir.cpp+82-39
......@@ -62,6 +62,7 @@ enum ConstCastResultId {
6262 ConstCastResultIdType,
6363 ConstCastResultIdUnresolvedInferredErrSet,
6464 ConstCastResultIdAsyncAllocatorType,
65 ConstCastResultIdNullWrapPtr,
6566};
6667
6768struct ConstCastErrSetMismatch {
......@@ -90,6 +91,7 @@ struct ConstCastOnly {
9091 ConstCastOnly *error_union_error_set;
9192 ConstCastOnly *return_type;
9293 ConstCastOnly *async_allocator_type;
94 ConstCastOnly *null_wrap_ptr_child;
9395 ConstCastArg fn_arg;
9496 ConstCastArgNoAlias arg_no_alias;
9597 } data;
......@@ -7660,6 +7662,21 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry
76607662 if (expected_type == actual_type)
76617663 return result;
76627664
7665 // * and [*] can do a const-cast-only to ?* and ?[*], respectively
7666 if (expected_type->id == TypeTableEntryIdMaybe &&
7667 expected_type->data.maybe.child_type->id == TypeTableEntryIdPointer &&
7668 actual_type->id == TypeTableEntryIdPointer)
7669 {
7670 ConstCastOnly child = types_match_const_cast_only(ira,
7671 expected_type->data.maybe.child_type, actual_type, source_node);
7672 if (child.id != ConstCastResultIdOk) {
7673 result.id = ConstCastResultIdNullWrapPtr;
7674 result.data.null_wrap_ptr_child = allocate_nonzero<ConstCastOnly>(1);
7675 *result.data.null_wrap_ptr_child = child;
7676 }
7677 return result;
7678 }
7679
76637680 // pointer const
76647681 if (expected_type->id == TypeTableEntryIdPointer &&
76657682 actual_type->id == TypeTableEntryIdPointer &&
......@@ -8741,7 +8758,8 @@ static void eval_const_expr_implicit_cast(CastOp cast_op,
87418758 zig_panic("TODO");
87428759 case CastOpNoop:
87438760 {
8744 copy_const_val(const_val, other_val, other_val->special == ConstValSpecialStatic);
8761 bool same_global_refs = other_val->special == ConstValSpecialStatic;
8762 copy_const_val(const_val, other_val, same_global_refs);
87458763 const_val->type = new_type;
87468764 break;
87478765 }
......@@ -9189,9 +9207,13 @@ static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *sourc
91899207
91909208 IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(&ira->new_irb,
91919209 source_instr->scope, source_instr->source_node);
9192 const_instruction->base.value.type = wanted_type;
91939210 const_instruction->base.value.special = ConstValSpecialStatic;
9194 const_instruction->base.value.data.x_maybe = val;
9211 if (get_codegen_ptr_type(wanted_type) != nullptr) {
9212 copy_const_val(&const_instruction->base.value, val, val->data.x_ptr.mut == ConstPtrMutComptimeConst);
9213 } else {
9214 const_instruction->base.value.data.x_nullable = val;
9215 }
9216 const_instruction->base.value.type = wanted_type;
91959217 return &const_instruction->base;
91969218 }
91979219
......@@ -9346,9 +9368,14 @@ static IrInstruction *ir_analyze_null_to_maybe(IrAnalyze *ira, IrInstruction *so
93469368 assert(val);
93479369
93489370 IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(&ira->new_irb, source_instr->scope, source_instr->source_node);
9349 const_instruction->base.value.type = wanted_type;
93509371 const_instruction->base.value.special = ConstValSpecialStatic;
9351 const_instruction->base.value.data.x_maybe = nullptr;
9372 if (get_codegen_ptr_type(wanted_type) != nullptr) {
9373 const_instruction->base.value.data.x_ptr.special = ConstPtrSpecialHardCodedAddr;
9374 const_instruction->base.value.data.x_ptr.data.hard_coded_addr.addr = 0;
9375 } else {
9376 const_instruction->base.value.data.x_nullable = nullptr;
9377 }
9378 const_instruction->base.value.type = wanted_type;
93529379 return &const_instruction->base;
93539380}
93549381
......@@ -10062,7 +10089,8 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
1006210089 }
1006310090
1006410091
10065 // explicit cast from child type of maybe type to maybe type
10092 // explicit cast from T to ?T
10093 // note that the *T to ?*T case is handled via the "ConstCastOnly" mechanism
1006610094 if (wanted_type->id == TypeTableEntryIdMaybe) {
1006710095 TypeTableEntry *wanted_child_type = wanted_type->data.maybe.child_type;
1006810096 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
1011310141 }
1011410142 }
1011510143
10116 // explicit cast from [N]T to %[]const T
10144 // explicit cast from [N]T to E![]const T
1011710145 if (wanted_type->id == TypeTableEntryIdErrorUnion &&
1011810146 is_slice(wanted_type->data.error_union.payload_type) &&
1011910147 actual_type->id == TypeTableEntryIdArray)
......@@ -10143,7 +10171,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
1014310171 return ir_analyze_err_wrap_code(ira, source_instr, value, wanted_type);
1014410172 }
1014510173
10146 // explicit cast from T to %?T
10174 // explicit cast from T to E!?T
1014710175 if (wanted_type->id == TypeTableEntryIdErrorUnion &&
1014810176 wanted_type->data.error_union.payload_type->id == TypeTableEntryIdMaybe &&
1014910177 actual_type->id != TypeTableEntryIdMaybe)
......@@ -10167,7 +10195,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
1016710195 }
1016810196
1016910197 // explicit cast from number literal to another type
10170 // explicit cast from number literal to &const integer
10198 // explicit cast from number literal to *const integer
1017110199 if (actual_type->id == TypeTableEntryIdComptimeFloat ||
1017210200 actual_type->id == TypeTableEntryIdComptimeInt)
1017310201 {
......@@ -10391,6 +10419,7 @@ static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruc
1039110419 IrInstruction *result = ir_create_const(&ira->new_irb, source_instruction->scope,
1039210420 source_instruction->source_node, child_type);
1039310421 copy_const_val(&result->value, pointee, ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst);
10422 result->value.type = child_type;
1039410423 return result;
1039510424 }
1039610425 }
......@@ -10708,6 +10737,16 @@ static bool resolve_cmp_op_id(IrBinOp op_id, Cmp cmp) {
1070810737 }
1070910738}
1071010739
10740static bool nullable_value_is_null(ConstExprValue *val) {
10741 assert(val->special == ConstValSpecialStatic);
10742 if (get_codegen_ptr_type(val->type) != nullptr) {
10743 return val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr &&
10744 val->data.x_ptr.data.hard_coded_addr.addr == 0;
10745 } else {
10746 return val->data.x_nullable == nullptr;
10747 }
10748}
10749
1071110750static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) {
1071210751 IrInstruction *op1 = bin_op_instruction->op1->other;
1071310752 IrInstruction *op2 = bin_op_instruction->op2->other;
......@@ -10737,7 +10776,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp
1073710776 ConstExprValue *maybe_val = ir_resolve_const(ira, maybe_op, UndefBad);
1073810777 if (!maybe_val)
1073910778 return ira->codegen->builtin_types.entry_invalid;
10740 bool is_null = (maybe_val->data.x_maybe == nullptr);
10779 bool is_null = nullable_value_is_null(maybe_val);
1074110780 ConstExprValue *out_val = ir_build_const_from(ira, &bin_op_instruction->base);
1074210781 out_val->data.x_bool = (op_id == IrBinOpCmpEq) ? is_null : !is_null;
1074310782 return ira->codegen->builtin_types.entry_bool;
......@@ -12015,7 +12054,9 @@ static TypeTableEntry *ir_analyze_instruction_error_return_trace(IrAnalyze *ira,
1201512054 TypeTableEntry *nullable_type = get_maybe_type(ira->codegen, ptr_to_stack_trace_type);
1201612055 if (!exec_has_err_ret_trace(ira->codegen, ira->new_irb.exec)) {
1201712056 ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
12018 out_val->data.x_maybe = nullptr;
12057 assert(get_codegen_ptr_type(nullable_type) != nullptr);
12058 out_val->data.x_ptr.special = ConstPtrSpecialHardCodedAddr;
12059 out_val->data.x_ptr.data.hard_coded_addr.addr = 0;
1201912060 return nullable_type;
1202012061 }
1202112062 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
1420714248
1420814249static TypeTableEntry *ir_analyze_instruction_load_ptr(IrAnalyze *ira, IrInstructionLoadPtr *load_ptr_instruction) {
1420914250 IrInstruction *ptr = load_ptr_instruction->ptr->other;
14251 if (type_is_invalid(ptr->value.type))
14252 return ira->codegen->builtin_types.entry_invalid;
14253
1421014254 IrInstruction *result = ir_get_deref(ira, &load_ptr_instruction->base, ptr);
1421114255 ir_link_new_instruction(result, &load_ptr_instruction->base);
1421214256 assert(result->value.type);
......@@ -14773,7 +14817,7 @@ static TypeTableEntry *ir_analyze_instruction_test_non_null(IrAnalyze *ira, IrIn
1477314817 return ira->codegen->builtin_types.entry_invalid;
1477414818
1477514819 ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
14776 out_val->data.x_bool = (maybe_val->data.x_maybe != nullptr);
14820 out_val->data.x_bool = !nullable_value_is_null(maybe_val);
1477714821 return ira->codegen->builtin_types.entry_bool;
1477814822 }
1477914823
......@@ -14837,13 +14881,18 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_maybe(IrAnalyze *ira,
1483714881 ConstExprValue *maybe_val = const_ptr_pointee(ira->codegen, val);
1483814882
1483914883 if (val->data.x_ptr.mut != ConstPtrMutRuntimeVar) {
14840 if (!maybe_val->data.x_maybe) {
14884 if (nullable_value_is_null(maybe_val)) {
1484114885 ir_add_error(ira, &unwrap_maybe_instruction->base, buf_sprintf("unable to unwrap null"));
1484214886 return ira->codegen->builtin_types.entry_invalid;
1484314887 }
1484414888 ConstExprValue *out_val = ir_build_const_from(ira, &unwrap_maybe_instruction->base);
1484514889 out_val->data.x_ptr.special = ConstPtrSpecialRef;
14846 out_val->data.x_ptr.data.ref.pointee = maybe_val->data.x_maybe;
14890 out_val->data.x_ptr.mut = val->data.x_ptr.mut;
14891 if (type_is_codegen_pointer(child_type)) {
14892 out_val->data.x_ptr.data.ref.pointee = maybe_val;
14893 } else {
14894 out_val->data.x_ptr.data.ref.pointee = maybe_val->data.x_nullable;
14895 }
1484714896 return result_type;
1484814897 }
1484914898 }
......@@ -16206,12 +16255,12 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop
1620616255 0, 0);
1620716256 fn_def_fields[6].type = get_maybe_type(ira->codegen, get_slice_type(ira->codegen, u8_ptr));
1620816257 if (fn_node->is_extern && buf_len(fn_node->lib_name) > 0) {
16209 fn_def_fields[6].data.x_maybe = create_const_vals(1);
16258 fn_def_fields[6].data.x_nullable = create_const_vals(1);
1621016259 ConstExprValue *lib_name = create_const_str_lit(ira->codegen, fn_node->lib_name);
16211 init_const_slice(ira->codegen, fn_def_fields[6].data.x_maybe, lib_name, 0, buf_len(fn_node->lib_name), true);
16260 init_const_slice(ira->codegen, fn_def_fields[6].data.x_nullable, lib_name, 0, buf_len(fn_node->lib_name), true);
16261 } else {
16262 fn_def_fields[6].data.x_nullable = nullptr;
1621216263 }
16213 else
16214 fn_def_fields[6].data.x_maybe = nullptr;
1621516264 // return_type: type
1621616265 ensure_field_index(fn_def_val->type, "return_type", 7);
1621716266 fn_def_fields[7].special = ConstValSpecialStatic;
......@@ -16664,8 +16713,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
1666416713
1666516714 TypeTableEntry *type_info_enum_field_type = ir_type_info_get_type(ira, "EnumField");
1666616715
16667 for (uint32_t union_field_index = 0; union_field_index < union_field_count; union_field_index++)
16668 {
16716 for (uint32_t union_field_index = 0; union_field_index < union_field_count; union_field_index++) {
1666916717 TypeUnionField *union_field = &type_entry->data.unionation.fields[union_field_index];
1667016718 ConstExprValue *union_field_val = &union_field_array->data.x_array.s_none.elements[union_field_index];
1667116719
......@@ -16676,12 +16724,11 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
1667616724 inner_fields[1].special = ConstValSpecialStatic;
1667716725 inner_fields[1].type = get_maybe_type(ira->codegen, type_info_enum_field_type);
1667816726
16679 if (fields[1].data.x_type == ira->codegen->builtin_types.entry_undef)
16680 inner_fields[1].data.x_maybe = nullptr;
16681 else
16682 {
16683 inner_fields[1].data.x_maybe = create_const_vals(1);
16684 make_enum_field_val(inner_fields[1].data.x_maybe, union_field->enum_field, type_info_enum_field_type);
16727 if (fields[1].data.x_type == ira->codegen->builtin_types.entry_undef) {
16728 inner_fields[1].data.x_nullable = nullptr;
16729 } else {
16730 inner_fields[1].data.x_nullable = create_const_vals(1);
16731 make_enum_field_val(inner_fields[1].data.x_nullable, union_field->enum_field, type_info_enum_field_type);
1668516732 }
1668616733
1668716734 inner_fields[2].special = ConstValSpecialStatic;
......@@ -16737,8 +16784,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
1673716784
1673816785 init_const_slice(ira->codegen, &fields[1], struct_field_array, 0, struct_field_count, false);
1673916786
16740 for (uint32_t struct_field_index = 0; struct_field_index < struct_field_count; struct_field_index++)
16741 {
16787 for (uint32_t struct_field_index = 0; struct_field_index < struct_field_count; struct_field_index++) {
1674216788 TypeStructField *struct_field = &type_entry->data.structure.fields[struct_field_index];
1674316789 ConstExprValue *struct_field_val = &struct_field_array->data.x_array.s_none.elements[struct_field_index];
1674416790
......@@ -16749,15 +16795,14 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
1674916795 inner_fields[1].special = ConstValSpecialStatic;
1675016796 inner_fields[1].type = get_maybe_type(ira->codegen, ira->codegen->builtin_types.entry_usize);
1675116797
16752 if (!type_has_bits(struct_field->type_entry))
16753 inner_fields[1].data.x_maybe = nullptr;
16754 else
16755 {
16798 if (!type_has_bits(struct_field->type_entry)) {
16799 inner_fields[1].data.x_nullable = nullptr;
16800 } else {
1675616801 size_t byte_offset = LLVMOffsetOfElement(ira->codegen->target_data_ref, type_entry->type_ref, struct_field->gen_index);
16757 inner_fields[1].data.x_maybe = create_const_vals(1);
16758 inner_fields[1].data.x_maybe->special = ConstValSpecialStatic;
16759 inner_fields[1].data.x_maybe->type = ira->codegen->builtin_types.entry_usize;
16760 bigint_init_unsigned(&inner_fields[1].data.x_maybe->data.x_bigint, byte_offset);
16802 inner_fields[1].data.x_nullable = create_const_vals(1);
16803 inner_fields[1].data.x_nullable->special = ConstValSpecialStatic;
16804 inner_fields[1].data.x_nullable->type = ira->codegen->builtin_types.entry_usize;
16805 bigint_init_unsigned(&inner_fields[1].data.x_nullable->data.x_bigint, byte_offset);
1676116806 }
1676216807
1676316808 inner_fields[2].special = ConstValSpecialStatic;
......@@ -19008,9 +19053,6 @@ static TypeTableEntry *ir_analyze_instruction_ptr_to_int(IrAnalyze *ira, IrInstr
1900819053 ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
1900919054 if (!val)
1901019055 return ira->codegen->builtin_types.entry_invalid;
19011 if (target->value.type->id == TypeTableEntryIdMaybe) {
19012 val = val->data.x_maybe;
19013 }
1901419056 if (val->type->id == TypeTableEntryIdPointer && val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) {
1901519057 IrInstruction *result = ir_create_const(&ira->new_irb, instruction->base.scope,
1901619058 instruction->base.source_node, usize);
......@@ -19936,6 +19978,7 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
1993619978static TypeTableEntry *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *instruction) {
1993719979 TypeTableEntry *instruction_type = ir_analyze_instruction_nocast(ira, instruction);
1993819980 instruction->value.type = instruction_type;
19981
1993919982 if (instruction->other) {
1994019983 instruction->other->value.type = instruction_type;
1994119984 } else {
test/cases/cast.zig+8-2
......@@ -1,5 +1,6 @@
1const assert = @import("std").debug.assert;
2const mem = @import("std").mem;
1const std = @import("std");
2const assert = std.debug.assert;
3const mem = std.mem;
34
45test "int to ptr cast" {
56 const x = usize(13);
......@@ -400,3 +401,8 @@ fn testCastPtrOfArrayToSliceAndPtr() void {
400401 assert(mem.eql(u8, array[0..], "coeu"));
401402}
402403
404test "cast *[1][*]const u8 to [*]const ?[*]const u8" {
405 const window_name = [1][*]const u8{c"window name"};
406 const x: [*]const ?[*]const u8 = &window_name;
407 assert(mem.eql(u8, std.cstr.toSliceConst(??x[0]), "window name"));
408}