Sema: fix slice field modification at comptime

This commit is contained in:
Veikka Tuominen 2022-04-28 17:45:33 +03:00
parent 6f4343b61a
commit 095d51164f
3 changed files with 114 additions and 40 deletions

View File

@ -16979,44 +16979,44 @@ fn fieldPtr(
const buf = try sema.arena.create(Type.SlicePtrFieldTypeBuffer);
const slice_ptr_ty = inner_ty.slicePtrFieldType(buf);
if (try sema.resolveDefinedValue(block, object_ptr_src, inner_ptr)) |val| {
var anon_decl = try block.startAnonDecl(src);
defer anon_decl.deinit();
return sema.analyzeDeclRef(try anon_decl.finish(
try slice_ptr_ty.copy(anon_decl.arena()),
try val.slicePtr().copy(anon_decl.arena()),
0, // default alignment
));
}
try sema.requireRuntimeBlock(block, src);
const result_ty = try Type.ptr(sema.arena, sema.mod, .{
.pointee_type = slice_ptr_ty,
.mutable = object_ptr_ty.ptrIsMutable(),
.@"addrspace" = object_ptr_ty.ptrAddressSpace(),
});
return block.addTyOp(.ptr_slice_ptr_ptr, result_ty, inner_ptr);
} else if (mem.eql(u8, field_name, "len")) {
if (try sema.resolveDefinedValue(block, object_ptr_src, inner_ptr)) |val| {
var anon_decl = try block.startAnonDecl(src);
defer anon_decl.deinit();
return sema.analyzeDeclRef(try anon_decl.finish(
Type.usize,
try Value.Tag.int_u64.create(anon_decl.arena(), val.sliceLen(sema.mod)),
0, // default alignment
));
return sema.addConstant(
result_ty,
try Value.Tag.field_ptr.create(sema.arena, .{
.container_ptr = val,
.container_ty = inner_ty,
.field_index = Value.Payload.Slice.ptr_index,
}),
);
}
try sema.requireRuntimeBlock(block, src);
return block.addTyOp(.ptr_slice_ptr_ptr, result_ty, inner_ptr);
} else if (mem.eql(u8, field_name, "len")) {
const result_ty = try Type.ptr(sema.arena, sema.mod, .{
.pointee_type = Type.usize,
.mutable = object_ptr_ty.ptrIsMutable(),
.@"addrspace" = object_ptr_ty.ptrAddressSpace(),
});
if (try sema.resolveDefinedValue(block, object_ptr_src, inner_ptr)) |val| {
return sema.addConstant(
result_ty,
try Value.Tag.field_ptr.create(sema.arena, .{
.container_ptr = val,
.container_ty = inner_ty,
.field_index = Value.Payload.Slice.len_index,
}),
);
}
try sema.requireRuntimeBlock(block, src);
return block.addTyOp(.ptr_slice_len_ptr, result_ty, inner_ptr);
} else {
return sema.fail(
@ -19297,7 +19297,6 @@ fn beginComptimePtrMutation(
const field_ptr = ptr_val.castTag(.field_ptr).?.data;
var parent = try beginComptimePtrMutation(sema, block, src, field_ptr.container_ptr);
const field_index = @intCast(u32, field_ptr.field_index);
const field_ty = parent.ty.structFieldType(field_index);
switch (parent.val.tag()) {
.undef => {
// A struct or union has been initialized to undefined at comptime and now we
@ -19316,7 +19315,7 @@ fn beginComptimePtrMutation(
return ComptimePtrMutationKit{
.decl_ref_mut = parent.decl_ref_mut,
.val = &fields[field_index],
.ty = field_ty,
.ty = parent.ty.structFieldType(field_index),
};
},
.Union => {
@ -19331,16 +19330,37 @@ fn beginComptimePtrMutation(
return ComptimePtrMutationKit{
.decl_ref_mut = parent.decl_ref_mut,
.val = &payload.data.val,
.ty = field_ty,
.ty = parent.ty.structFieldType(field_index),
};
},
.Pointer => {
assert(parent.ty.isSlice());
parent.val.* = try Value.Tag.slice.create(arena, .{
.ptr = Value.undef,
.len = Value.undef,
});
switch (field_index) {
Value.Payload.Slice.ptr_index => return ComptimePtrMutationKit{
.decl_ref_mut = parent.decl_ref_mut,
.val = &parent.val.castTag(.slice).?.data.ptr,
.ty = parent.ty.slicePtrFieldType(try sema.arena.create(Type.SlicePtrFieldTypeBuffer)),
},
Value.Payload.Slice.len_index => return ComptimePtrMutationKit{
.decl_ref_mut = parent.decl_ref_mut,
.val = &parent.val.castTag(.slice).?.data.len,
.ty = Type.usize,
},
else => unreachable,
}
},
else => unreachable,
}
},
.aggregate => return ComptimePtrMutationKit{
.decl_ref_mut = parent.decl_ref_mut,
.val = &parent.val.castTag(.aggregate).?.data[field_index],
.ty = field_ty,
.ty = parent.ty.structFieldType(field_index),
},
.@"union" => {
// We need to set the active field of the union.
@ -19353,9 +19373,22 @@ fn beginComptimePtrMutation(
return ComptimePtrMutationKit{
.decl_ref_mut = parent.decl_ref_mut,
.val = &payload.val,
.ty = field_ty,
.ty = parent.ty.structFieldType(field_index),
};
},
.slice => switch (field_index) {
Value.Payload.Slice.ptr_index => return ComptimePtrMutationKit{
.decl_ref_mut = parent.decl_ref_mut,
.val = &parent.val.castTag(.slice).?.data.ptr,
.ty = parent.ty.slicePtrFieldType(try sema.arena.create(Type.SlicePtrFieldTypeBuffer)),
},
Value.Payload.Slice.len_index => return ComptimePtrMutationKit{
.decl_ref_mut = parent.decl_ref_mut,
.val = &parent.val.castTag(.slice).?.data.len,
.ty = Type.usize,
},
else => unreachable,
},
else => unreachable,
}
@ -19555,7 +19588,6 @@ fn beginComptimePtrLoad(
.field_ptr => blk: {
const field_ptr = ptr_val.castTag(.field_ptr).?.data;
const field_index = @intCast(u32, field_ptr.field_index);
const field_ty = field_ptr.container_ty.structFieldType(field_index);
var deref = try beginComptimePtrLoad(sema, block, src, field_ptr.container_ptr, field_ptr.container_ty);
if (field_ptr.container_ty.hasWellDefinedLayout()) {
@ -19570,19 +19602,38 @@ fn beginComptimePtrLoad(
deref.ty_without_well_defined_layout = field_ptr.container_ty;
}
if (deref.pointee) |*tv| {
const coerce_in_mem_ok =
(try sema.coerceInMemoryAllowed(block, field_ptr.container_ty, tv.ty, false, target, src, src)) == .ok or
(try sema.coerceInMemoryAllowed(block, tv.ty, field_ptr.container_ty, false, target, src, src)) == .ok;
if (coerce_in_mem_ok) {
deref.pointee = TypedValue{
.ty = field_ty,
.val = tv.val.fieldValue(tv.ty, field_index),
};
break :blk deref;
}
const tv = &(deref.pointee orelse {
deref.pointee = null;
break :blk deref;
});
const coerce_in_mem_ok =
(try sema.coerceInMemoryAllowed(block, field_ptr.container_ty, tv.ty, false, target, src, src)) == .ok or
(try sema.coerceInMemoryAllowed(block, tv.ty, field_ptr.container_ty, false, target, src, src)) == .ok;
if (!coerce_in_mem_ok) {
deref.pointee = null;
break :blk deref;
}
if (field_ptr.container_ty.isSlice()) {
const slice_val = tv.val.castTag(.slice).?.data;
deref.pointee = switch (field_index) {
Value.Payload.Slice.ptr_index => TypedValue{
.ty = field_ptr.container_ty.slicePtrFieldType(try sema.arena.create(Type.SlicePtrFieldTypeBuffer)),
.val = slice_val.ptr,
},
Value.Payload.Slice.len_index => TypedValue{
.ty = Type.usize,
.val = slice_val.len,
},
else => unreachable,
};
} else {
const field_ty = field_ptr.container_ty.structFieldType(field_index);
deref.pointee = TypedValue{
.ty = field_ty,
.val = tv.val.fieldValue(tv.ty, field_index),
};
}
deref.pointee = null;
break :blk deref;
},

View File

@ -2542,6 +2542,15 @@ pub const Value = extern union {
return 1;
}
},
.decl_ref_mut => {
const decl_index = val.castTag(.decl_ref_mut).?.data.decl_index;
const decl = mod.declPtr(decl_index);
if (decl.ty.zigTypeTag() == .Array) {
return decl.ty.arrayLen();
} else {
return 1;
}
},
else => unreachable,
};
}
@ -5116,6 +5125,9 @@ pub const Value = extern union {
ptr: Value,
len: Value,
},
pub const ptr_index = 0;
pub const len_index = 1;
};
pub const Ty = struct {

View File

@ -682,3 +682,14 @@ test "slicing slice with sentinel as end index" {
try S.do();
comptime try S.do();
}
test "slice len modification at comptime" {
comptime {
var buf: [10]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var items: []u8 = buf[0..0];
items.len += 2;
try expect(items.len == 2);
try expect(items[0] == 0);
try expect(items[1] == 1);
}
}