Skip to content

Commit f0b6dac

Browse files
committed
add implicit casts from *[N]T
* to `[]T` * to `[*]T` See #770
1 parent b65203f commit f0b6dac

File tree

4 files changed

+163
-2
lines changed

4 files changed

+163
-2
lines changed

src/all_types.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -583,6 +583,7 @@ enum CastOp {
583583
CastOpNumLitToConcrete,
584584
CastOpErrSet,
585585
CastOpBitCast,
586+
CastOpPtrOfArrayToSlice,
586587
};
587588

588589
struct AstNodeFnCallExpr {

src/codegen.cpp

+24-2
Original file line numberDiff line numberDiff line change
@@ -2530,7 +2530,7 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable,
25302530
assert(wanted_type->data.structure.is_slice);
25312531
assert(actual_type->id == TypeTableEntryIdArray);
25322532

2533-
TypeTableEntry *wanted_pointer_type = wanted_type->data.structure.fields[0].type_entry;
2533+
TypeTableEntry *wanted_pointer_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry;
25342534
TypeTableEntry *wanted_child_type = wanted_pointer_type->data.pointer.child_type;
25352535

25362536

@@ -2576,6 +2576,29 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable,
25762576
return expr_val;
25772577
case CastOpBitCast:
25782578
return LLVMBuildBitCast(g->builder, expr_val, wanted_type->type_ref, "");
2579+
case CastOpPtrOfArrayToSlice: {
2580+
assert(cast_instruction->tmp_ptr);
2581+
assert(actual_type->id == TypeTableEntryIdPointer);
2582+
TypeTableEntry *array_type = actual_type->data.pointer.child_type;
2583+
assert(array_type->id == TypeTableEntryIdArray);
2584+
2585+
LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr,
2586+
slice_ptr_index, "");
2587+
LLVMValueRef indices[] = {
2588+
LLVMConstNull(g->builtin_types.entry_usize->type_ref),
2589+
LLVMConstInt(g->builtin_types.entry_usize->type_ref, 0, false),
2590+
};
2591+
LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, expr_val, indices, 2, "");
2592+
gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false);
2593+
2594+
LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr,
2595+
slice_len_index, "");
2596+
LLVMValueRef len_value = LLVMConstInt(g->builtin_types.entry_usize->type_ref,
2597+
array_type->data.array.len, false);
2598+
gen_store_untyped(g, len_value, len_field_ptr, 0, false);
2599+
2600+
return cast_instruction->tmp_ptr;
2601+
}
25792602
}
25802603
zig_unreachable();
25812604
}
@@ -3815,7 +3838,6 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst
38153838
} else {
38163839
end_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, array_type->data.array.len, false);
38173840
}
3818-
38193841
if (want_runtime_safety) {
38203842
add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val);
38213843
if (instruction->end) {

src/ir.cpp

+122
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction,
108108
static TypeTableEntry *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstruction *op);
109109
static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction *value, LVal lval);
110110
static TypeTableEntry *adjust_ptr_align(CodeGen *g, TypeTableEntry *ptr_type, uint32_t new_align);
111+
static TypeTableEntry *adjust_slice_align(CodeGen *g, TypeTableEntry *slice_type, uint32_t new_align);
111112

112113
ConstExprValue *const_ptr_pointee(CodeGen *g, ConstExprValue *const_val) {
113114
assert(const_val->type->id == TypeTableEntryIdPointer);
@@ -8024,6 +8025,33 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira,
80248025
}
80258026
}
80268027

8028+
// implicit *[N]T to [*]T
8029+
if (expected_type->id == TypeTableEntryIdPointer &&
8030+
expected_type->data.pointer.ptr_len == PtrLenUnknown &&
8031+
actual_type->id == TypeTableEntryIdPointer &&
8032+
actual_type->data.pointer.ptr_len == PtrLenSingle &&
8033+
actual_type->data.pointer.child_type->id == TypeTableEntryIdArray &&
8034+
types_match_const_cast_only(ira, expected_type->data.pointer.child_type,
8035+
actual_type->data.pointer.child_type->data.array.child_type, source_node).id == ConstCastResultIdOk)
8036+
{
8037+
return ImplicitCastMatchResultYes;
8038+
}
8039+
8040+
// implicit *[N]T to []T
8041+
if (is_slice(expected_type) &&
8042+
actual_type->id == TypeTableEntryIdPointer &&
8043+
actual_type->data.pointer.ptr_len == PtrLenSingle &&
8044+
actual_type->data.pointer.child_type->id == TypeTableEntryIdArray)
8045+
{
8046+
TypeTableEntry *slice_ptr_type = expected_type->data.structure.fields[slice_ptr_index].type_entry;
8047+
assert(slice_ptr_type->id == TypeTableEntryIdPointer);
8048+
if (types_match_const_cast_only(ira, slice_ptr_type->data.pointer.child_type,
8049+
actual_type->data.pointer.child_type->data.array.child_type, source_node).id == ConstCastResultIdOk)
8050+
{
8051+
return ImplicitCastMatchResultYes;
8052+
}
8053+
}
8054+
80278055
// implicit [N]T to ?[]const T
80288056
if (expected_type->id == TypeTableEntryIdMaybe &&
80298057
is_slice(expected_type->data.maybe.child_type) &&
@@ -8699,6 +8727,7 @@ static void eval_const_expr_implicit_cast(CastOp cast_op,
86998727
zig_unreachable();
87008728
case CastOpErrSet:
87018729
case CastOpBitCast:
8730+
case CastOpPtrOfArrayToSlice:
87028731
zig_panic("TODO");
87038732
case CastOpNoop:
87048733
{
@@ -8786,6 +8815,63 @@ static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_inst
87868815
}
87878816
}
87888817

8818+
static IrInstruction *ir_resolve_ptr_of_array_to_unknown_len_ptr(IrAnalyze *ira, IrInstruction *source_instr,
8819+
IrInstruction *value, TypeTableEntry *wanted_type)
8820+
{
8821+
assert(value->value.type->id == TypeTableEntryIdPointer);
8822+
wanted_type = adjust_ptr_align(ira->codegen, wanted_type, value->value.type->data.pointer.alignment);
8823+
8824+
if (instr_is_comptime(value)) {
8825+
ConstExprValue *pointee = const_ptr_pointee(ira->codegen, &value->value);
8826+
if (pointee->special != ConstValSpecialRuntime) {
8827+
IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope,
8828+
source_instr->source_node, wanted_type);
8829+
result->value.type = wanted_type;
8830+
result->value.data.x_ptr.special = ConstPtrSpecialBaseArray;
8831+
result->value.data.x_ptr.mut = value->value.data.x_ptr.mut;
8832+
result->value.data.x_ptr.data.base_array.array_val = pointee;
8833+
result->value.data.x_ptr.data.base_array.elem_index = 0;
8834+
result->value.data.x_ptr.data.base_array.is_cstr = false;
8835+
return result;
8836+
}
8837+
}
8838+
8839+
IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node,
8840+
wanted_type, value, CastOpBitCast);
8841+
result->value.type = wanted_type;
8842+
return result;
8843+
}
8844+
8845+
static IrInstruction *ir_resolve_ptr_of_array_to_slice(IrAnalyze *ira, IrInstruction *source_instr,
8846+
IrInstruction *value, TypeTableEntry *wanted_type)
8847+
{
8848+
wanted_type = adjust_slice_align(ira->codegen, wanted_type, value->value.type->data.pointer.alignment);
8849+
8850+
if (instr_is_comptime(value)) {
8851+
ConstExprValue *pointee = const_ptr_pointee(ira->codegen, &value->value);
8852+
if (pointee->special != ConstValSpecialRuntime) {
8853+
assert(value->value.type->id == TypeTableEntryIdPointer);
8854+
TypeTableEntry *array_type = value->value.type->data.pointer.child_type;
8855+
assert(is_slice(wanted_type));
8856+
bool is_const = wanted_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const;
8857+
8858+
IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope,
8859+
source_instr->source_node, wanted_type);
8860+
init_const_slice(ira->codegen, &result->value, pointee, 0, array_type->data.array.len, is_const);
8861+
result->value.data.x_struct.fields[slice_ptr_index].data.x_ptr.mut =
8862+
value->value.data.x_ptr.mut;
8863+
result->value.type = wanted_type;
8864+
return result;
8865+
}
8866+
}
8867+
8868+
IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node,
8869+
wanted_type, value, CastOpPtrOfArrayToSlice);
8870+
result->value.type = wanted_type;
8871+
ir_add_alloca(ira, result, wanted_type);
8872+
return result;
8873+
}
8874+
87898875
static bool is_container(TypeTableEntry *type) {
87908876
return type->id == TypeTableEntryIdStruct ||
87918877
type->id == TypeTableEntryIdEnum ||
@@ -9937,6 +10023,35 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
993710023
}
993810024
}
993910025

10026+
// explicit *[N]T to [*]T
10027+
if (wanted_type->id == TypeTableEntryIdPointer &&
10028+
wanted_type->data.pointer.ptr_len == PtrLenUnknown &&
10029+
actual_type->id == TypeTableEntryIdPointer &&
10030+
actual_type->data.pointer.ptr_len == PtrLenSingle &&
10031+
actual_type->data.pointer.child_type->id == TypeTableEntryIdArray &&
10032+
actual_type->data.pointer.alignment >= wanted_type->data.pointer.alignment &&
10033+
types_match_const_cast_only(ira, wanted_type->data.pointer.child_type,
10034+
actual_type->data.pointer.child_type->data.array.child_type, source_node).id == ConstCastResultIdOk)
10035+
{
10036+
return ir_resolve_ptr_of_array_to_unknown_len_ptr(ira, source_instr, value, wanted_type);
10037+
}
10038+
10039+
// explicit *[N]T to []T
10040+
if (is_slice(wanted_type) &&
10041+
actual_type->id == TypeTableEntryIdPointer &&
10042+
actual_type->data.pointer.ptr_len == PtrLenSingle &&
10043+
actual_type->data.pointer.child_type->id == TypeTableEntryIdArray)
10044+
{
10045+
TypeTableEntry *slice_ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry;
10046+
assert(slice_ptr_type->id == TypeTableEntryIdPointer);
10047+
if (types_match_const_cast_only(ira, slice_ptr_type->data.pointer.child_type,
10048+
actual_type->data.pointer.child_type->data.array.child_type, source_node).id == ConstCastResultIdOk)
10049+
{
10050+
return ir_resolve_ptr_of_array_to_slice(ira, source_instr, value, wanted_type);
10051+
}
10052+
}
10053+
10054+
994010055
// explicit cast from child type of maybe type to maybe type
994110056
if (wanted_type->id == TypeTableEntryIdMaybe) {
994210057
TypeTableEntry *wanted_child_type = wanted_type->data.maybe.child_type;
@@ -13150,6 +13265,13 @@ static TypeTableEntry *adjust_ptr_align(CodeGen *g, TypeTableEntry *ptr_type, ui
1315013265
ptr_type->data.pointer.bit_offset, ptr_type->data.pointer.unaligned_bit_count);
1315113266
}
1315213267

13268+
static TypeTableEntry *adjust_slice_align(CodeGen *g, TypeTableEntry *slice_type, uint32_t new_align) {
13269+
assert(is_slice(slice_type));
13270+
TypeTableEntry *ptr_type = adjust_ptr_align(g, slice_type->data.structure.fields[slice_ptr_index].type_entry,
13271+
new_align);
13272+
return get_slice_type(g, ptr_type);
13273+
}
13274+
1315313275
static TypeTableEntry *adjust_ptr_len(CodeGen *g, TypeTableEntry *ptr_type, PtrLen ptr_len) {
1315413276
assert(ptr_type->id == TypeTableEntryIdPointer);
1315513277
return get_pointer_to_type_extra(g,

test/cases/cast.zig

+16
Original file line numberDiff line numberDiff line change
@@ -384,3 +384,19 @@ test "const slice widen cast" {
384384

385385
assert(@bitCast(u32, bytes) == 0x12121212);
386386
}
387+
388+
test "single-item pointer of array to slice and to unknown length pointer" {
389+
testCastPtrOfArrayToSliceAndPtr();
390+
comptime testCastPtrOfArrayToSliceAndPtr();
391+
}
392+
393+
fn testCastPtrOfArrayToSliceAndPtr() void {
394+
var array = "ao" ++ "eu"; // TODO https://github.com/ziglang/zig/issues/1076
395+
const x: [*]u8 = &array;
396+
x[0] += 1;
397+
assert(mem.eql(u8, array[0..], "boeu"));
398+
const y: []u8 = &array;
399+
y[0] += 1;
400+
assert(mem.eql(u8, array[0..], "coeu"));
401+
}
402+

0 commit comments

Comments
 (0)