From 5da76ae34bac2f54b1bda5528cf49c0551e88bba Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 15 Apr 2023 15:36:21 +0100 Subject: [PATCH] Add `struct #no_copy` --- core/runtime/core.odin | 1 + src/check_decl.cpp | 8 ++++++++ src/check_expr.cpp | 20 ++++++++++++++++++++ src/check_stmt.cpp | 4 +++- src/check_type.cpp | 5 +++-- src/llvm_backend_type.cpp | 16 +++++++++------- src/parser.cpp | 11 +++++++++-- src/parser.hpp | 1 + src/types.cpp | 7 +++++++ 9 files changed, 61 insertions(+), 12 deletions(-) diff --git a/core/runtime/core.odin b/core/runtime/core.odin index 040590b4a..9939bfc5c 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -119,6 +119,7 @@ Type_Info_Struct :: struct { tags: []string, is_packed: bool, is_raw_union: bool, + is_no_copy: bool, custom_align: bool, equal: Equal_Proc, // set only when the struct has .Comparable set but does not have .Simple_Compare set diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 5c25dd6a3..5a8080f96 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -131,6 +131,14 @@ gb_internal void check_init_variables(CheckerContext *ctx, Entity **lhs, isize l if (d != nullptr) { d->init_expr = o->expr; } + + if (o->type && is_type_no_copy(o->type)) { + begin_error_block(); + if (check_no_copy_assignment(*o, str_lit("initialization"))) { + error_line("\tInitialization of a #no_copy type must be either implicitly zero, a constant literal, or from a return value a call expression"); + } + end_error_block(); + } } if (rhs_count > 0 && lhs_count != rhs_count) { error(lhs[0]->token, "Assignment count mismatch '%td' = '%td'", lhs_count, rhs_count); diff --git a/src/check_expr.cpp b/src/check_expr.cpp index f27675301..e55765ff8 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5043,6 +5043,21 @@ gb_internal isize add_dependencies_from_unpacking(CheckerContext *c, Entity **lh return tuple_count; } +gb_internal bool check_no_copy_assignment(Operand const &o, String const &context) { + if (o.type && is_type_no_copy(o.type)) { + Ast *expr = unparen_expr(o.expr); + if (expr && o.mode != Addressing_Constant) { + if (expr->kind == Ast_CallExpr) { + // Okay + } else { + error(o.expr, "Invalid use a #no_copy value in %.*s", LIT(context)); + return true; + } + } + } + return false; +} + gb_internal bool check_assignment_arguments(CheckerContext *ctx, Array const &lhs, Array *operands, Slice const &rhs) { bool optional_ok = false; @@ -5114,6 +5129,7 @@ gb_internal bool check_assignment_arguments(CheckerContext *ctx, Array for (Entity *e : tuple->variables) { o.type = e->type; array_add(operands, o); + check_no_copy_assignment(o, str_lit("assignment")); } tuple_index += tuple->variables.count; @@ -5952,6 +5968,10 @@ gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *op } } + for (Operand const &o : operands) { + check_no_copy_assignment(o, str_lit("call expression")); + } + if (operand->mode == Addressing_ProcGroup) { check_entity_decl(c, operand->proc_group, nullptr, nullptr); diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 4e6623fc1..388a64e00 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -326,7 +326,6 @@ gb_internal bool check_is_terminating(Ast *node, String const &label) { - gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, Operand *rhs) { if (rhs->mode == Addressing_Invalid) { return nullptr; @@ -339,6 +338,8 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O Ast *node = unparen_expr(lhs->expr); + check_no_copy_assignment(*rhs, str_lit("assignment")); + // NOTE(bill): Ignore assignments to '_' if (is_blank_ident(node)) { check_assignment(ctx, rhs, nullptr, str_lit("assignment to '_' identifier")); @@ -400,6 +401,7 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O } Type *assignment_type = lhs->type; + switch (lhs->mode) { case Addressing_Invalid: return nullptr; diff --git a/src/check_type.cpp b/src/check_type.cpp index 7444f88be..b687e838e 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -609,8 +609,9 @@ gb_internal void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast * context = str_lit("struct #raw_union"); } - struct_type->Struct.scope = ctx->scope; - struct_type->Struct.is_packed = st->is_packed; + struct_type->Struct.scope = ctx->scope; + struct_type->Struct.is_packed = st->is_packed; + struct_type->Struct.is_no_copy = st->is_no_copy; struct_type->Struct.polymorphic_params = check_record_polymorphic_params( ctx, st->polymorphic_params, &struct_type->Struct.is_polymorphic, diff --git a/src/llvm_backend_type.cpp b/src/llvm_backend_type.cpp index e8d286fda..3af10112f 100644 --- a/src/llvm_backend_type.cpp +++ b/src/llvm_backend_type.cpp @@ -691,32 +691,34 @@ gb_internal void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup case Type_Struct: { tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_struct_ptr); - LLVMValueRef vals[12] = {}; + LLVMValueRef vals[13] = {}; { lbValue is_packed = lb_const_bool(m, t_bool, t->Struct.is_packed); lbValue is_raw_union = lb_const_bool(m, t_bool, t->Struct.is_raw_union); + lbValue is_no_copy = lb_const_bool(m, t_bool, t->Struct.is_no_copy); lbValue is_custom_align = lb_const_bool(m, t_bool, t->Struct.custom_align != 0); vals[5] = is_packed.value; vals[6] = is_raw_union.value; - vals[7] = is_custom_align.value; + vals[7] = is_no_copy.value; + vals[8] = is_custom_align.value; if (is_type_comparable(t) && !is_type_simple_compare(t)) { - vals[8] = lb_equal_proc_for_type(m, t).value; + vals[9] = lb_equal_proc_for_type(m, t).value; } if (t->Struct.soa_kind != StructSoa_None) { - lbValue kind = lb_emit_struct_ep(p, tag, 9); + lbValue kind = lb_emit_struct_ep(p, tag, 10); Type *kind_type = type_deref(kind.type); lbValue soa_kind = lb_const_value(m, kind_type, exact_value_i64(t->Struct.soa_kind)); lbValue soa_type = lb_type_info(m, t->Struct.soa_elem); lbValue soa_len = lb_const_int(m, t_int, t->Struct.soa_count); - vals[9] = soa_kind.value; - vals[10] = soa_type.value; - vals[11] = soa_len.value; + vals[10] = soa_kind.value; + vals[11] = soa_type.value; + vals[12] = soa_len.value; } } diff --git a/src/parser.cpp b/src/parser.cpp index 50a9ba766..790e67db6 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1047,7 +1047,7 @@ gb_internal Ast *ast_dynamic_array_type(AstFile *f, Token token, Ast *elem) { } gb_internal Ast *ast_struct_type(AstFile *f, Token token, Slice fields, isize field_count, - Ast *polymorphic_params, bool is_packed, bool is_raw_union, + Ast *polymorphic_params, bool is_packed, bool is_raw_union, bool is_no_copy, Ast *align, Token where_token, Array const &where_clauses) { Ast *result = alloc_ast_node(f, Ast_StructType); @@ -1057,6 +1057,7 @@ gb_internal Ast *ast_struct_type(AstFile *f, Token token, Slice fields, i result->StructType.polymorphic_params = polymorphic_params; result->StructType.is_packed = is_packed; result->StructType.is_raw_union = is_raw_union; + result->StructType.is_no_copy = is_no_copy; result->StructType.align = align; result->StructType.where_token = where_token; result->StructType.where_clauses = slice_from_array(where_clauses); @@ -2392,6 +2393,7 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { Ast *polymorphic_params = nullptr; bool is_packed = false; bool is_raw_union = false; + bool no_copy = false; Ast *align = nullptr; if (allow_token(f, Token_OpenParen)) { @@ -2427,6 +2429,11 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { syntax_error(tag, "Duplicate struct tag '#%.*s'", LIT(tag.string)); } is_raw_union = true; + } else if (tag.string == "no_copy") { + if (is_packed) { + syntax_error(tag, "Duplicate struct tag '#%.*s'", LIT(tag.string)); + } + no_copy = true; } else { syntax_error(tag, "Invalid struct tag '#%.*s'", LIT(tag.string)); } @@ -2465,7 +2472,7 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { decls = fields->FieldList.list; } - return ast_struct_type(f, token, decls, name_count, polymorphic_params, is_packed, is_raw_union, align, where_token, where_clauses); + return ast_struct_type(f, token, decls, name_count, polymorphic_params, is_packed, is_raw_union, no_copy, align, where_token, where_clauses); } break; case Token_union: { diff --git a/src/parser.hpp b/src/parser.hpp index 5e1878cf2..5302fd274 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -693,6 +693,7 @@ AST_KIND(_TypeBegin, "", bool) \ Slice where_clauses; \ bool is_packed; \ bool is_raw_union; \ + bool is_no_copy; \ }) \ AST_KIND(UnionType, "union type", struct { \ Scope *scope; \ diff --git a/src/types.cpp b/src/types.cpp index 5addb5b7b..889269564 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -149,6 +149,7 @@ struct TypeStruct { bool are_offsets_being_processed : 1; bool is_packed : 1; bool is_raw_union : 1; + bool is_no_copy : 1; bool is_poly_specialized : 1; }; @@ -1670,6 +1671,10 @@ gb_internal bool is_type_raw_union(Type *t) { t = base_type(t); return (t->kind == Type_Struct && t->Struct.is_raw_union); } +gb_internal bool is_type_no_copy(Type *t) { + t = base_type(t); + return (t->kind == Type_Struct && t->Struct.is_no_copy); +} gb_internal bool is_type_enum(Type *t) { t = base_type(t); return (t->kind == Type_Enum); @@ -2655,6 +2660,7 @@ gb_internal bool are_types_identical_internal(Type *x, Type *y, bool check_tuple case Type_Struct: if (x->Struct.is_raw_union == y->Struct.is_raw_union && + x->Struct.is_no_copy == y->Struct.is_no_copy && x->Struct.fields.count == y->Struct.fields.count && x->Struct.is_packed == y->Struct.is_packed && x->Struct.custom_align == y->Struct.custom_align && @@ -4207,6 +4213,7 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha str = gb_string_appendc(str, "struct"); if (type->Struct.is_packed) str = gb_string_appendc(str, " #packed"); if (type->Struct.is_raw_union) str = gb_string_appendc(str, " #raw_union"); + if (type->Struct.is_no_copy) str = gb_string_appendc(str, " #no_copy"); if (type->Struct.custom_align != 0) str = gb_string_append_fmt(str, " #align %d", cast(int)type->Struct.custom_align); str = gb_string_appendc(str, " {");