mirror of
https://github.com/ziglang/zig.git
synced 2026-02-14 21:38:33 +00:00
parent
5c04730534
commit
7c53230a61
@ -1037,6 +1037,7 @@ struct TypeTableEntry {
|
||||
ZigLLVMDIType *di_type;
|
||||
|
||||
bool zero_bits;
|
||||
bool is_copyable;
|
||||
|
||||
union {
|
||||
TypeTableEntryPointer pointer;
|
||||
|
||||
@ -300,6 +300,18 @@ uint64_t type_size_bits(CodeGen *g, TypeTableEntry *type_entry) {
|
||||
return LLVMSizeOfTypeInBits(g->target_data_ref, canon_type->type_ref);
|
||||
}
|
||||
|
||||
static bool type_is_copyable(CodeGen *g, TypeTableEntry *type_entry) {
|
||||
type_ensure_zero_bits_known(g, type_entry);
|
||||
if (!type_has_bits(type_entry))
|
||||
return true;
|
||||
|
||||
if (!handle_is_ptr(type_entry))
|
||||
return true;
|
||||
|
||||
ensure_complete_type(g, type_entry);
|
||||
return type_entry->is_copyable;
|
||||
}
|
||||
|
||||
static bool is_slice(TypeTableEntry *type) {
|
||||
return type->id == TypeTableEntryIdStruct && type->data.structure.is_slice;
|
||||
}
|
||||
@ -336,6 +348,7 @@ TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type
|
||||
type_ensure_zero_bits_known(g, child_type);
|
||||
|
||||
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdPointer);
|
||||
entry->is_copyable = true;
|
||||
|
||||
const char *const_str = is_const ? "const " : "";
|
||||
const char *volatile_str = is_volatile ? "volatile " : "";
|
||||
@ -392,6 +405,7 @@ TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type) {
|
||||
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdMaybe);
|
||||
assert(child_type->type_ref);
|
||||
assert(child_type->di_type);
|
||||
entry->is_copyable = type_is_copyable(g, child_type);
|
||||
|
||||
buf_resize(&entry->name, 0);
|
||||
buf_appendf(&entry->name, "?%s", buf_ptr(&child_type->name));
|
||||
@ -471,6 +485,7 @@ TypeTableEntry *get_error_type(CodeGen *g, TypeTableEntry *child_type) {
|
||||
return child_type->error_parent;
|
||||
|
||||
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdErrorUnion);
|
||||
entry->is_copyable = true;
|
||||
assert(child_type->type_ref);
|
||||
assert(child_type->di_type);
|
||||
ensure_complete_type(g, child_type);
|
||||
@ -557,6 +572,7 @@ TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, uint64_t
|
||||
|
||||
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdArray);
|
||||
entry->zero_bits = (array_size == 0) || child_type->zero_bits;
|
||||
entry->is_copyable = false;
|
||||
|
||||
buf_resize(&entry->name, 0);
|
||||
buf_appendf(&entry->name, "[%" PRIu64 "]%s", array_size, buf_ptr(&child_type->name));
|
||||
@ -614,6 +630,7 @@ TypeTableEntry *get_slice_type(CodeGen *g, TypeTableEntry *child_type, bool is_c
|
||||
} else if (is_const) {
|
||||
TypeTableEntry *var_peer = get_slice_type(g, child_type, false);
|
||||
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdStruct);
|
||||
entry->is_copyable = true;
|
||||
|
||||
buf_resize(&entry->name, 0);
|
||||
buf_appendf(&entry->name, "[]const %s", buf_ptr(&child_type->name));
|
||||
@ -629,6 +646,7 @@ TypeTableEntry *get_slice_type(CodeGen *g, TypeTableEntry *child_type, bool is_c
|
||||
return entry;
|
||||
} else {
|
||||
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdStruct);
|
||||
entry->is_copyable = true;
|
||||
|
||||
// If the child type is []const T then we need to make sure the type ref
|
||||
// and debug info is the same as if the child type were []T.
|
||||
@ -752,6 +770,7 @@ TypeTableEntry *get_typedecl_type(CodeGen *g, const char *name, TypeTableEntry *
|
||||
|
||||
buf_init_from_str(&entry->name, name);
|
||||
|
||||
entry->is_copyable = type_is_copyable(g, child_type);
|
||||
entry->type_ref = child_type->type_ref;
|
||||
entry->di_type = child_type->di_type;
|
||||
entry->zero_bits = child_type->zero_bits;
|
||||
@ -768,6 +787,7 @@ TypeTableEntry *get_bound_fn_type(CodeGen *g, FnTableEntry *fn_entry) {
|
||||
return fn_type->data.fn.bound_fn_parent;
|
||||
|
||||
TypeTableEntry *bound_fn_type = new_type_table_entry(TypeTableEntryIdBoundFn);
|
||||
bound_fn_type->is_copyable = false;
|
||||
bound_fn_type->data.bound_fn.fn_type = fn_type;
|
||||
bound_fn_type->zero_bits = true;
|
||||
|
||||
@ -786,6 +806,7 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
|
||||
ensure_complete_type(g, fn_type_id->return_type);
|
||||
|
||||
TypeTableEntry *fn_type = new_type_table_entry(TypeTableEntryIdFn);
|
||||
fn_type->is_copyable = true;
|
||||
fn_type->data.fn.fn_type_id = *fn_type_id;
|
||||
|
||||
if (fn_type_id->is_cold) {
|
||||
@ -972,6 +993,7 @@ TypeTableEntry *analyze_type_expr(CodeGen *g, Scope *scope, AstNode *node) {
|
||||
|
||||
static TypeTableEntry *get_generic_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
|
||||
TypeTableEntry *fn_type = new_type_table_entry(TypeTableEntryIdFn);
|
||||
fn_type->is_copyable = false;
|
||||
buf_init_from_str(&fn_type->name, "fn(");
|
||||
size_t i = 0;
|
||||
for (; i < fn_type_id->next_param_index; i += 1) {
|
||||
@ -1078,6 +1100,12 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c
|
||||
case TypeTableEntryIdFn:
|
||||
case TypeTableEntryIdTypeDecl:
|
||||
case TypeTableEntryIdEnumTag:
|
||||
ensure_complete_type(g, type_entry);
|
||||
if (!fn_type_id.is_extern && !type_is_copyable(g, type_entry)) {
|
||||
add_node_error(g, param_node->data.param_decl.type,
|
||||
buf_sprintf("type '%s' is not copyable; cannot pass by value", buf_ptr(&type_entry->name)));
|
||||
return g->builtin_types.entry_invalid;
|
||||
}
|
||||
break;
|
||||
}
|
||||
FnTypeParamInfo *param_info = &fn_type_id.param_info[fn_type_id.next_param_index];
|
||||
@ -1159,6 +1187,7 @@ static TypeTableEntry *create_enum_tag_type(CodeGen *g, TypeTableEntry *enum_typ
|
||||
buf_resize(&entry->name, 0);
|
||||
buf_appendf(&entry->name, "@enumTagType(%s)", buf_ptr(&enum_type->name));
|
||||
|
||||
entry->is_copyable = true;
|
||||
entry->data.enum_tag.enum_type = enum_type;
|
||||
entry->data.enum_tag.int_type = int_type;
|
||||
entry->type_ref = int_type->type_ref;
|
||||
@ -4007,6 +4036,7 @@ void render_const_value(Buf *buf, ConstExprValue *const_val) {
|
||||
|
||||
TypeTableEntry *make_int_type(CodeGen *g, bool is_signed, size_t size_in_bits) {
|
||||
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
|
||||
entry->is_copyable = true;
|
||||
entry->type_ref = LLVMIntType(size_in_bits);
|
||||
|
||||
const char u_or_i = is_signed ? 'i' : 'u';
|
||||
|
||||
@ -261,6 +261,13 @@ static void addLLVMArgAttr(LLVMValueRef arg_val, unsigned param_index, const cha
|
||||
return addLLVMAttr(arg_val, param_index + 1, attr_name);
|
||||
}
|
||||
|
||||
static void addLLVMCallsiteAttr(LLVMValueRef call_instr, unsigned param_index, const char *attr_name) {
|
||||
unsigned kind_id = LLVMGetEnumAttributeKindForName(attr_name, strlen(attr_name));
|
||||
assert(kind_id != 0);
|
||||
LLVMAttributeRef llvm_attr = LLVMCreateEnumAttribute(LLVMGetGlobalContext(), kind_id, 0);
|
||||
LLVMAddCallSiteAttribute(call_instr, param_index + 1, llvm_attr);
|
||||
}
|
||||
|
||||
static Buf *get_mangled_name(CodeGen *g, Buf *original_name, bool external_linkage) {
|
||||
if (external_linkage || g->external_symbol_names.maybe_get(original_name) == nullptr) {
|
||||
return original_name;
|
||||
@ -1578,11 +1585,12 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
|
||||
fn_type = instruction->fn_ref->value.type;
|
||||
}
|
||||
|
||||
TypeTableEntry *src_return_type = fn_type->data.fn.fn_type_id.return_type;
|
||||
FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
|
||||
TypeTableEntry *src_return_type = fn_type_id->return_type;
|
||||
bool ret_has_bits = type_has_bits(src_return_type);
|
||||
bool first_arg_ret = ret_has_bits && handle_is_ptr(src_return_type);
|
||||
size_t actual_param_count = instruction->arg_count + (first_arg_ret ? 1 : 0);
|
||||
bool is_var_args = fn_type->data.fn.fn_type_id.is_var_args;
|
||||
bool is_var_args = fn_type_id->is_var_args;
|
||||
LLVMValueRef *gen_param_values = allocate<LLVMValueRef>(actual_param_count);
|
||||
size_t gen_param_index = 0;
|
||||
if (first_arg_ret) {
|
||||
@ -1603,6 +1611,13 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
|
||||
LLVMValueRef result = ZigLLVMBuildCall(g->builder, fn_val,
|
||||
gen_param_values, gen_param_index, fn_type->data.fn.calling_convention, "");
|
||||
|
||||
for (size_t param_i = 0; param_i < fn_type_id->param_count; param_i += 1) {
|
||||
FnGenParamInfo *gen_info = &fn_type->data.fn.gen_param_info[param_i];
|
||||
if (gen_info->is_byval) {
|
||||
addLLVMCallsiteAttr(result, gen_info->gen_index, "byval");
|
||||
}
|
||||
}
|
||||
|
||||
if (src_return_type->id == TypeTableEntryIdUnreachable) {
|
||||
return LLVMBuildUnreachable(g->builder);
|
||||
} else if (!ret_has_bits) {
|
||||
@ -3376,7 +3391,7 @@ static void do_code_gen(CodeGen *g) {
|
||||
addLLVMArgAttr(fn_val, gen_index, "nonnull");
|
||||
}
|
||||
if (is_byval) {
|
||||
// TODO add byval attr?
|
||||
addLLVMArgAttr(fn_val, gen_index, "byval");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -9905,7 +9905,6 @@ static TypeTableEntry *ir_analyze_instruction_size_of(IrAnalyze *ira,
|
||||
case TypeTableEntryIdNumLitInt:
|
||||
case TypeTableEntryIdBoundFn:
|
||||
case TypeTableEntryIdMetaType:
|
||||
case TypeTableEntryIdFn:
|
||||
case TypeTableEntryIdNamespace:
|
||||
case TypeTableEntryIdArgTuple:
|
||||
ir_add_error_node(ira, size_of_instruction->base.source_node,
|
||||
@ -9925,6 +9924,7 @@ static TypeTableEntry *ir_analyze_instruction_size_of(IrAnalyze *ira,
|
||||
case TypeTableEntryIdEnum:
|
||||
case TypeTableEntryIdUnion:
|
||||
case TypeTableEntryIdEnumTag:
|
||||
case TypeTableEntryIdFn:
|
||||
{
|
||||
uint64_t size_in_bytes = type_size(ira->codegen, type_entry);
|
||||
ConstExprValue *out_val = ir_build_const_from(ira, &size_of_instruction->base);
|
||||
|
||||
@ -34,9 +34,9 @@ pub fn List(comptime T: type) -> type{
|
||||
return l.items[0...l.len];
|
||||
}
|
||||
|
||||
pub fn append(l: &Self, item: T) -> %void {
|
||||
pub fn append(l: &Self, item: &const T) -> %void {
|
||||
const new_item_ptr = %return l.addOne();
|
||||
*new_item_ptr = item;
|
||||
*new_item_ptr = *item;
|
||||
}
|
||||
|
||||
pub fn resize(l: &Self, new_len: usize) -> %void {
|
||||
|
||||
@ -48,15 +48,15 @@ test "constantEnumWithPayload" {
|
||||
shouldBeNotEmpty(full);
|
||||
}
|
||||
|
||||
fn shouldBeEmpty(x: AnEnumWithPayload) {
|
||||
switch (x) {
|
||||
fn shouldBeEmpty(x: &const AnEnumWithPayload) {
|
||||
switch (*x) {
|
||||
AnEnumWithPayload.Empty => {},
|
||||
else => @unreachable(),
|
||||
}
|
||||
}
|
||||
|
||||
fn shouldBeNotEmpty(x: AnEnumWithPayload) {
|
||||
switch (x) {
|
||||
fn shouldBeNotEmpty(x: &const AnEnumWithPayload) {
|
||||
switch (*x) {
|
||||
AnEnumWithPayload.Empty => @unreachable(),
|
||||
else => {},
|
||||
}
|
||||
|
||||
32
test/cases/incomplete_struct_param_tld.zig
Normal file
32
test/cases/incomplete_struct_param_tld.zig
Normal file
@ -0,0 +1,32 @@
|
||||
const assert = @import("std").debug.assert;
|
||||
|
||||
const A = struct {
|
||||
b: B,
|
||||
};
|
||||
|
||||
const B = struct {
|
||||
c: C,
|
||||
};
|
||||
|
||||
const C = struct {
|
||||
x: i32,
|
||||
|
||||
fn d(c: &const C) -> i32 {
|
||||
return c.x;
|
||||
}
|
||||
};
|
||||
|
||||
fn foo(a: &const A) -> i32 {
|
||||
return a.b.c.d();
|
||||
}
|
||||
|
||||
test "incomplete struct param top level declaration" {
|
||||
const a = A {
|
||||
.b = B {
|
||||
.c = C {
|
||||
.x = 13,
|
||||
},
|
||||
},
|
||||
};
|
||||
assert(foo(a) == 13);
|
||||
}
|
||||
@ -338,8 +338,8 @@ const Test3Point = struct {
|
||||
};
|
||||
const test3_foo = Test3Foo.Three{Test3Point {.x = 3, .y = 4}};
|
||||
const test3_bar = Test3Foo.Two{13};
|
||||
fn test3_1(f: Test3Foo) {
|
||||
switch (f) {
|
||||
fn test3_1(f: &const Test3Foo) {
|
||||
switch (*f) {
|
||||
Test3Foo.Three => |pt| {
|
||||
assert(pt.x == 3);
|
||||
assert(pt.y == 4);
|
||||
@ -347,8 +347,8 @@ fn test3_1(f: Test3Foo) {
|
||||
else => @unreachable(),
|
||||
}
|
||||
}
|
||||
fn test3_2(f: Test3Foo) {
|
||||
switch (f) {
|
||||
fn test3_2(f: &const Test3Foo) {
|
||||
switch (*f) {
|
||||
Test3Foo.Two => |x| {
|
||||
assert(x == 13);
|
||||
},
|
||||
|
||||
@ -62,8 +62,8 @@ fn foo(x: ?i32) -> ?bool {
|
||||
test "ifVarMaybePointer" {
|
||||
assert(shouldBeAPlus1(Particle {.a = 14, .b = 1, .c = 1, .d = 1}) == 15);
|
||||
}
|
||||
fn shouldBeAPlus1(p: Particle) -> u64 {
|
||||
var maybe_particle: ?Particle = p;
|
||||
fn shouldBeAPlus1(p: &const Particle) -> u64 {
|
||||
var maybe_particle: ?Particle = *p;
|
||||
if (const *particle ?= maybe_particle) {
|
||||
particle.a += 1;
|
||||
}
|
||||
|
||||
@ -53,10 +53,10 @@ const StructFoo = struct {
|
||||
b : bool,
|
||||
c : f32,
|
||||
};
|
||||
fn testFoo(foo : StructFoo) {
|
||||
fn testFoo(foo: &const StructFoo) {
|
||||
assert(foo.b);
|
||||
}
|
||||
fn testMutation(foo : &StructFoo) {
|
||||
fn testMutation(foo: &StructFoo) {
|
||||
foo.c = 100;
|
||||
}
|
||||
|
||||
@ -110,7 +110,7 @@ const Foo = struct {
|
||||
|
||||
fn aFunc() -> i32 { 13 }
|
||||
|
||||
fn callStructField(foo: Foo) -> i32 {
|
||||
fn callStructField(foo: &const Foo) -> i32 {
|
||||
return foo.ptr();
|
||||
}
|
||||
|
||||
@ -123,7 +123,7 @@ test "storeMemberFunctionInVariable" {
|
||||
}
|
||||
const MemberFnTestFoo = struct {
|
||||
x: i32,
|
||||
fn member(foo: MemberFnTestFoo) -> i32 { foo.x }
|
||||
fn member(foo: &const MemberFnTestFoo) -> i32 { foo.x }
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -92,8 +92,8 @@ const SwitchProngWithVarEnum = enum {
|
||||
Two: f32,
|
||||
Meh,
|
||||
};
|
||||
fn switchProngWithVarFn(a: SwitchProngWithVarEnum) {
|
||||
switch(a) {
|
||||
fn switchProngWithVarFn(a: &const SwitchProngWithVarEnum) {
|
||||
switch(*a) {
|
||||
SwitchProngWithVarEnum.One => |x| {
|
||||
if (x != 13) @unreachable();
|
||||
},
|
||||
|
||||
@ -512,42 +512,6 @@ export fn main(argc: c_int, argv: &&u8) -> c_int {
|
||||
)SOURCE", "3.25\n3\n3.00\n-0.40\n");
|
||||
|
||||
|
||||
add_simple_case("incomplete struct parameter top level decl", R"SOURCE(
|
||||
const io = @import("std").io;
|
||||
const A = struct {
|
||||
b: B,
|
||||
};
|
||||
|
||||
const B = struct {
|
||||
c: C,
|
||||
};
|
||||
|
||||
const C = struct {
|
||||
x: i32,
|
||||
|
||||
fn d(c: &const C) {
|
||||
%%io.stdout.printf("OK\n");
|
||||
}
|
||||
};
|
||||
|
||||
fn foo(a: A) {
|
||||
a.b.c.d();
|
||||
}
|
||||
|
||||
pub fn main(args: [][]u8) -> %void {
|
||||
const a = A {
|
||||
.b = B {
|
||||
.c = C {
|
||||
.x = 13,
|
||||
},
|
||||
},
|
||||
};
|
||||
foo(a);
|
||||
}
|
||||
|
||||
)SOURCE", "OK\n");
|
||||
|
||||
|
||||
add_simple_case("same named methods in incomplete struct", R"SOURCE(
|
||||
const io = @import("std").io;
|
||||
|
||||
@ -1037,9 +1001,10 @@ export fn entry() -> usize { @sizeOf(@typeOf(a)) }
|
||||
)SOURCE", 1, ".tmp_source.zig:4:11: error: expected array or C string literal, found 'usize'");
|
||||
|
||||
add_compile_fail_case("non compile time array concatenation", R"SOURCE(
|
||||
fn f(s: [10]u8) -> []u8 {
|
||||
fn f() -> []u8 {
|
||||
s ++ "foo"
|
||||
}
|
||||
var s: [10]u8 = undefined;
|
||||
export fn entry() -> usize { @sizeOf(@typeOf(f)) }
|
||||
)SOURCE", 1, ".tmp_source.zig:3:5: error: unable to evaluate constant expression");
|
||||
|
||||
@ -1071,10 +1036,10 @@ const Foo = struct {
|
||||
a: i32,
|
||||
b: i32,
|
||||
|
||||
fn member_a(foo: Foo) -> i32 {
|
||||
fn member_a(foo: &const Foo) -> i32 {
|
||||
return foo.a;
|
||||
}
|
||||
fn member_b(foo: Foo) -> i32 {
|
||||
fn member_b(foo: &const Foo) -> i32 {
|
||||
return foo.b;
|
||||
}
|
||||
};
|
||||
@ -1085,7 +1050,7 @@ const members = []member_fn_type {
|
||||
Foo.member_b,
|
||||
};
|
||||
|
||||
fn f(foo: Foo, index: usize) {
|
||||
fn f(foo: &const Foo, index: usize) {
|
||||
const result = members[index]();
|
||||
}
|
||||
|
||||
@ -1335,15 +1300,15 @@ const EnumWithData = enum {
|
||||
One,
|
||||
Two: i32,
|
||||
};
|
||||
fn bad_eql_2(a: EnumWithData, b: EnumWithData) -> bool {
|
||||
a == b
|
||||
fn bad_eql_2(a: &const EnumWithData, b: &const EnumWithData) -> bool {
|
||||
*a == *b
|
||||
}
|
||||
|
||||
export fn entry1() -> usize { @sizeOf(@typeOf(bad_eql_1)) }
|
||||
export fn entry2() -> usize { @sizeOf(@typeOf(bad_eql_2)) }
|
||||
)SOURCE", 2,
|
||||
".tmp_source.zig:3:7: error: operator not allowed for type '[]u8'",
|
||||
".tmp_source.zig:10:7: error: operator not allowed for type 'EnumWithData'");
|
||||
".tmp_source.zig:10:8: error: operator not allowed for type 'EnumWithData'");
|
||||
|
||||
add_compile_fail_case("non-const switch number literal", R"SOURCE(
|
||||
export fn foo() {
|
||||
@ -1845,6 +1810,12 @@ export fn bar() {}
|
||||
pub const baz = 1234;
|
||||
)SOURCE");
|
||||
}
|
||||
|
||||
add_compile_fail_case("pass non-copyable type by value to function", R"SOURCE(
|
||||
const Point = struct { x: i32, y: i32, };
|
||||
fn foo(p: Point) { }
|
||||
export fn entry() -> usize { @sizeOf(@typeOf(foo)) }
|
||||
)SOURCE", 1, ".tmp_source.zig:3:11: error: type 'Point' is not copyable; cannot pass by value");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@ -15,6 +15,7 @@ const test_generics = @import("cases/generics.zig");
|
||||
const test_goto = @import("cases/goto.zig");
|
||||
const test_if = @import("cases/if.zig");
|
||||
const test_import = @import("cases/import.zig");
|
||||
const test_incomplete_struct_param_tld = @import("cases/incomplete_struct_param_tld.zig");
|
||||
const test_ir_block_deps = @import("cases/ir_block_deps.zig");
|
||||
const test_math = @import("cases/math.zig");
|
||||
const test_misc = @import("cases/misc.zig");
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user