From 469b1e067961e021e7860f70919b574c142d1f40 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sun, 16 Mar 2025 13:35:58 -0400 Subject: [PATCH] Fixes for opaque external structs --- ast.h | 2 +- compile.c | 41 +++++++++++++++--------------------- enums.c | 2 +- parse.c | 16 +++++++++----- stdlib/arrays.h | 20 +++++++++--------- stdlib/structs.c | 2 +- stdlib/types.h | 2 +- structs.c | 38 +++++++++++++++++++++++----------- structs.h | 2 +- typecheck.c | 54 ++++++++++++++++++++++++++++-------------------- types.c | 7 +++++++ 11 files changed, 108 insertions(+), 78 deletions(-) diff --git a/ast.h b/ast.h index 0b8e786..c9db906 100644 --- a/ast.h +++ b/ast.h @@ -297,7 +297,7 @@ struct ast_s { const char *name; arg_ast_t *fields; ast_t *namespace; - bool secret:1, external:1; + bool secret:1, external:1, opaque:1; } StructDef; struct { const char *name; diff --git a/compile.c b/compile.c index c753fc6..d40ab48 100644 --- a/compile.c +++ b/compile.c @@ -614,11 +614,10 @@ static CORD compile_lvalue(env_t *env, ast_t *ast) if (index->unchecked) { return CORD_all("Array_lvalue_unchecked(", compile_type(item_type), ", ", target_code, ", ", compile_int_to_type(env, index->index, Type(IntType, .bits=TYPE_IBITS64)), - ", ", CORD_asprintf("%ld", type_size(item_type)), ")"); + ", sizeof(", compile_type(item_type), "))"); } else { return CORD_all("Array_lvalue(", compile_type(item_type), ", ", target_code, ", ", compile_int_to_type(env, index->index, Type(IntType, .bits=TYPE_IBITS64)), - ", ", CORD_asprintf("%ld", type_size(item_type)), ", ", heap_strf("%ld", ast->start - ast->file->text), ", ", heap_strf("%ld", ast->end - ast->file->text), ")"); } @@ -1165,7 +1164,7 @@ static CORD _compile_statement(env_t *env, ast_t *ast) if (lhs_t->tag == TextType) { return CORD_all(lhs, " = Texts(", lhs, ", ", rhs, ");"); } else if (lhs_t->tag == ArrayType) { - CORD padded_item_size = CORD_asprintf("%ld", type_size(Match(lhs_t, ArrayType)->item_type)); + CORD padded_item_size = CORD_all("sizeof(", compile_type(Match(lhs_t, ArrayType)->item_type), ")"); // arr ++= [...] if (update->lhs->tag == Var) return CORD_all("Array$insert_all(&", lhs, ", ", rhs, ", I(0), ", padded_item_size, ");"); @@ -1548,11 +1547,9 @@ static CORD _compile_statement(env_t *env, ast_t *ast) type_t *value_t = Match(iter_value_t, TableType)->value_type; CORD value = compile(body_scope, for_->vars->next->ast); - size_t value_offset = type_size(key_t); - if (type_align(value_t) > 1 && value_offset % type_align(value_t)) - value_offset += type_align(value_t) - (value_offset % type_align(value_t)); // padding + CORD value_offset = CORD_all("offsetof(struct { ", compile_declaration(key_t, "k"), "; ", compile_declaration(value_t, "v"), "; }, v)"); loop = CORD_all(loop, compile_declaration(value_t, value), " = *(", compile_type(value_t), "*)(", - "iterating.data + i*iterating.stride + ", heap_strf("%zu", value_offset), ");\n"); + "iterating.data + i*iterating.stride + ", value_offset, ");\n"); } } } @@ -2632,8 +2629,7 @@ CORD compile(env_t *env, ast_t *ast) return CORD_all("Text$concat(", lhs, ", ", rhs, ")"); } case ArrayType: { - CORD padded_item_size = CORD_asprintf("%ld", type_size(Match(operand_t, ArrayType)->item_type)); - return CORD_all("Array$concat(", lhs, ", ", rhs, ", ", padded_item_size, ")"); + return CORD_all("Array$concat(", lhs, ", ", rhs, ", sizeof(", compile_type(Match(operand_t, ArrayType)->item_type), "))"); } default: code_err(ast, "Concatenation isn't supported between %T and %T values", lhs_t, rhs_t); @@ -2769,9 +2765,9 @@ CORD compile(env_t *env, ast_t *ast) case Array: { type_t *array_type = get_type(env, ast); type_t *item_type = Match(array_type, ArrayType)->item_type; - if (type_size(Match(array_type, ArrayType)->item_type) > ARRAY_MAX_STRIDE) - code_err(ast, "This array holds items that take up %ld bytes, but the maximum supported size is %ld bytes. Consider using an array of pointers instead.", - type_size(item_type), ARRAY_MAX_STRIDE); + // if (type_size(Match(array_type, ArrayType)->item_type) > ARRAY_MAX_STRIDE) + // code_err(ast, "This array holds items that take up %ld bytes, but the maximum supported size is %ld bytes. Consider using an array of pointers instead.", + // type_size(item_type), ARRAY_MAX_STRIDE); auto array = Match(ast, Array); if (!array->items) @@ -3063,7 +3059,7 @@ CORD compile(env_t *env, ast_t *ast) switch (self_value_t->tag) { case ArrayType: { type_t *item_t = Match(self_value_t, ArrayType)->item_type; - CORD padded_item_size = CORD_asprintf("%ld", type_size(item_t)); + CORD padded_item_size = CORD_all("sizeof(", compile_type(item_t), ")"); ast_t *default_rng = FakeAST(InlineCCode, .code="default_rng", .type=RNG_TYPE); if (streq(call->name, "insert")) { @@ -3174,7 +3170,7 @@ CORD compile(env_t *env, ast_t *ast) arg_t *arg_spec = new(arg_t, .name="by", .type=Type(ClosureType, .fn=fn_t), .default_val=default_cmp); CORD arg_code = compile_arguments(env, ast, arg_spec, call->args); return CORD_all("Array$heap_pop_value(", self, ", ", arg_code, ", ", compile_type(item_t), ", _, ", - promote_to_optional(item_t, "_"), ", ", compile_none(item_t), ", ", padded_item_size, ")"); + promote_to_optional(item_t, "_"), ", ", compile_none(item_t), ")"); } else if (streq(call->name, "binary_search")) { self = compile_to_pointer_depth(env, call->self, 0, call->args != NULL); type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_stack=true); @@ -3229,7 +3225,7 @@ CORD compile(env_t *env, ast_t *ast) arg_t *arg_spec = new(arg_t, .name="index", .type=INT_TYPE, .default_val=FakeAST(Int, "-1")); CORD index = compile_arguments(env, ast, arg_spec, call->args); return CORD_all("Array$pop(", self, ", ", index, ", ", compile_type(item_t), ", _, ", - promote_to_optional(item_t, "_"), ", ", compile_none(item_t), ", ", padded_item_size, ")"); + promote_to_optional(item_t, "_"), ", ", compile_none(item_t), ")"); } else if (streq(call->name, "counts")) { self = compile_to_pointer_depth(env, call->self, 0, false); (void)compile_arguments(env, ast, NULL, call->args); @@ -3724,14 +3720,11 @@ CORD compile(env_t *env, ast_t *ast) return CORD_all("ARRAY_COPY((", compile_to_pointer_depth(env, f->fielded, 0, false), ").entries)"); } else if (streq(f->field, "values")) { auto table = Match(value_t, TableType); - size_t offset = type_size(table->key_type); - size_t align = type_align(table->value_type); - if (align > 1 && offset % align > 0) - offset += align - (offset % align); + CORD offset = CORD_all("offsetof(struct { ", compile_declaration(table->key_type, "k"), "; ", compile_declaration(table->value_type, "v"), "; }, v)"); return CORD_all("({ Array_t *entries = &(", compile_to_pointer_depth(env, f->fielded, 0, false), ").entries;\n" "ARRAY_INCREF(*entries);\n" "Array_t values = *entries;\n" - "values.data += ", CORD_asprintf("%zu", offset), ";\n" + "values.data += ", offset, ";\n" "values; })"); } else if (streq(f->field, "fallback")) { return CORD_all("({ Table_t *_fallback = (", compile_to_pointer_depth(env, f->fielded, 0, false), ").fallback; _fallback ? *_fallback : NONE_TABLE; })"); @@ -3901,7 +3894,7 @@ CORD compile_type_info(type_t *t) } case OptionalType: { type_t *non_optional = Match(t, OptionalType)->type; - return CORD_asprintf("Optional$info(%zu, %zu, %r)", type_size(t), type_align(t), compile_type_info(non_optional)); + return CORD_asprintf("Optional$info(sizeof(%r), __alignof__(%r), %r)", compile_type(non_optional), compile_type(non_optional), compile_type_info(non_optional)); } case MutexedType: { type_t *mutexed = Match(t, MutexedType)->type; @@ -4165,10 +4158,10 @@ CORD compile_function(env_t *env, CORD name_code, ast_t *ast, CORD *staticdefs) int64_t num_fields = used_names.entries.length; const char *metamethods = is_packed_data(t) ? "PackedData$metamethods" : "Struct$metamethods"; - CORD args_typeinfo = CORD_asprintf("((TypeInfo_t[1]){{.size=%zu, .align=%zu, .metamethods=%s, " + CORD args_typeinfo = CORD_asprintf("((TypeInfo_t[1]){{.size=sizeof(%r), .align=__alignof__(%r), .metamethods=%s, " ".tag=StructInfo, .StructInfo.name=\"FunctionArguments\", " ".StructInfo.num_fields=%ld, .StructInfo.fields=(NamedType_t[%ld]){", - type_size(t), type_align(t), metamethods, + compile_type(t), compile_type(t), metamethods, num_fields, num_fields); CORD args_type = "struct { "; for (arg_t *f = fields; f; f = f->next) { @@ -4271,7 +4264,7 @@ CORD compile_top_level_code(env_t *env, ast_t *ast) auto def = Match(ast, StructDef); type_t *t = Table$str_get(*env->types, def->name); assert(t && t->tag == StructType); - CORD code = compile_struct_typeinfo(env, t, def->name, def->fields, def->secret); + CORD code = compile_struct_typeinfo(env, t, def->name, def->fields, def->secret, def->opaque); env_t *ns_env = namespace_env(env, def->name); return CORD_all(code, def->namespace ? compile_top_level_code(ns_env, def->namespace) : CORD_EMPTY); } diff --git a/enums.c b/enums.c index 811b42b..06af0df 100644 --- a/enums.c +++ b/enums.c @@ -27,7 +27,7 @@ CORD compile_enum_typeinfo(env_t *env, ast_t *ast) type_t *tag_type = Table$str_get(*env->types, tag_name); assert(tag_type && tag_type->tag == StructType); member_typeinfos = CORD_all( - member_typeinfos, compile_struct_typeinfo(env, tag_type, tag_name, tag->fields, tag->secret)); + member_typeinfos, compile_struct_typeinfo(env, tag_type, tag_name, tag->fields, tag->secret, false)); } int num_tags = 0; diff --git a/parse.c b/parse.c index bbe8a70..10fa97d 100644 --- a/parse.c +++ b/parse.c @@ -2081,22 +2081,28 @@ PARSER(parse_struct_def) { arg_ast_t *fields = parse_args(ctx, &pos); whitespace(&pos); - bool secret = false, external = false; + bool secret = false, external = false, opaque = false; if (match(&pos, ";")) { // Extra flags whitespace(&pos); for (;;) { - if (match_word(&pos, "secret")) + if (match_word(&pos, "secret")) { secret = true; - else if (match_word(&pos, "extern")) + } else if (match_word(&pos, "extern")) { external = true; - else + } else if (match_word(&pos, "opaque")) { + if (fields) + parser_err(ctx, pos-strlen("opaque"), pos, "A struct can't be opaque if it has fields defined"); + opaque = true; + } else { break; + } if (!match_separator(&pos)) break; } } + expect_closing(ctx, &pos, ")", "I wasn't able to parse the rest of this struct"); ast_t *namespace = NULL; @@ -2112,7 +2118,7 @@ PARSER(parse_struct_def) { if (!namespace) namespace = NewAST(ctx->file, pos, pos, Block, .statements=NULL); return NewAST(ctx->file, start, pos, StructDef, .name=name, .fields=fields, .namespace=namespace, - .secret=secret, .external=external); + .secret=secret, .external=external, .opaque=opaque); } PARSER(parse_enum_def) { diff --git a/stdlib/arrays.h b/stdlib/arrays.h index 0e75f0e..332a1db 100644 --- a/stdlib/arrays.h +++ b/stdlib/arrays.h @@ -19,22 +19,22 @@ #define Array_get_unchecked(type, x, i) *({ const Array_t arr = x; int64_t index = i; \ int64_t off = index + (index < 0) * (arr.length + 1) - 1; \ (type*)(arr.data + arr.stride * off);}) -#define Array_lvalue(item_type, arr_expr, index_expr, padded_item_size, start, end) *({ \ +#define Array_lvalue(item_type, arr_expr, index_expr, start, end) *({ \ Array_t *arr = arr_expr; int64_t index = index_expr; \ int64_t off = index + (index < 0) * (arr->length + 1) - 1; \ if (unlikely(off < 0 || off >= arr->length)) \ fail_source(__SOURCE_FILE__, start, end, "Invalid array index: %s (array has length %ld)\n", Text$as_c_string(Int64$as_text(&index, no, NULL)), arr->length); \ if (arr->data_refcount > 0) \ - Array$compact(arr, padded_item_size); \ + Array$compact(arr, sizeof(item_type)); \ (item_type*)(arr->data + arr->stride * off); }) -#define Array_lvalue_unchecked(item_type, arr_expr, index_expr, padded_item_size) *({ \ +#define Array_lvalue_unchecked(item_type, arr_expr, index_expr) *({ \ Array_t *arr = arr_expr; int64_t index = index_expr; \ int64_t off = index + (index < 0) * (arr->length + 1) - 1; \ if (arr->data_refcount > 0) \ - Array$compact(arr, padded_item_size); \ + Array$compact(arr, sizeof(item_type)); \ (item_type*)(arr->data + arr->stride * off); }) -#define Array_set(item_type, arr, index, value, padded_item_size, start, end) \ - Array_lvalue(item_type, arr_expr, index, padded_item_size, start, end) = value +#define Array_set(item_type, arr, index, value, start, end) \ + Array_lvalue(item_type, arr_expr, index, start, end) = value #define is_atomic(x) _Generic(x, bool: true, int8_t: true, int16_t: true, int32_t: true, int64_t: true, float: true, double: true, default: false) #define TypedArray(t, ...) ({ t items[] = {__VA_ARGS__}; \ (Array_t){.length=sizeof(items)/sizeof(items[0]), \ @@ -66,14 +66,14 @@ void Array$remove_at(Array_t *arr, Int_t index, Int_t count, int64_t padded_item void Array$remove_item(Array_t *arr, void *item, Int_t max_removals, const TypeInfo_t *type); #define Array$remove_item_value(arr, item_expr, max, type) Array$remove_item(arr, (__typeof(item_expr)[1]){item_expr}, max, type) -#define Array$pop(arr_expr, index_expr, item_type, nonnone_var, nonnone_expr, none_expr, padded_item_size) ({ \ +#define Array$pop(arr_expr, index_expr, item_type, nonnone_var, nonnone_expr, none_expr) ({ \ Array_t *arr = arr_expr; \ Int_t index = index_expr; \ int64_t index64 = Int64$from_int(index, false); \ int64_t off = index64 + (index64 < 0) * (arr->length + 1) - 1; \ (off >= 0 && off < arr->length) ? ({ \ item_type nonnone_var = *(item_type*)(arr->data + off*arr->stride); \ - Array$remove_at(arr, index, I_small(1), padded_item_size); \ + Array$remove_at(arr, index, I_small(1), sizeof(item_type)); \ nonnone_expr; \ }) : none_expr; }) @@ -107,11 +107,11 @@ void Array$heapify(Array_t *heap, Closure_t comparison, int64_t padded_item_size void Array$heap_push(Array_t *heap, const void *item, Closure_t comparison, int64_t padded_item_size); #define Array$heap_push_value(heap, _value, comparison, padded_item_size) ({ __typeof(_value) value = _value; Array$heap_push(heap, &value, comparison, padded_item_size); }) void Array$heap_pop(Array_t *heap, Closure_t comparison, int64_t padded_item_size); -#define Array$heap_pop_value(heap, comparison, type, nonnone_var, nonnone_expr, none_expr, padded_item_size) \ +#define Array$heap_pop_value(heap, comparison, type, nonnone_var, nonnone_expr, none_expr) \ ({ Array_t *_heap = heap; \ (_heap->length > 0) ? ({ \ type nonnone_var = *(type*)_heap->data; \ - Array$heap_pop(_heap, comparison, padded_item_size); \ + Array$heap_pop(_heap, comparison, sizeof(type)); \ nonnone_expr; \ }) : none_expr; }) Int_t Array$binary_search(Array_t array, void *target, Closure_t comparison); diff --git a/stdlib/structs.c b/stdlib/structs.c index b3b26bd..0b1385e 100644 --- a/stdlib/structs.c +++ b/stdlib/structs.c @@ -139,7 +139,7 @@ PUREFUNC public Text_t Struct$as_text(const void *obj, bool colorize, const Type { if (!obj) return Text$from_str(type->StructInfo.name); - if (type->StructInfo.is_secret) + if (type->StructInfo.is_secret || type->StructInfo.is_opaque) return Text$format(colorize ? "\x1b[0;1m%s\x1b[m(...)" : "%s(...)", type->StructInfo.name); Text_t text = Text$format(colorize ? "\x1b[0;1m%s\x1b[m(" : "%s(", type->StructInfo.name); diff --git a/stdlib/types.h b/stdlib/types.h index f74048c..c7b938a 100644 --- a/stdlib/types.h +++ b/stdlib/types.h @@ -63,7 +63,7 @@ struct TypeInfo_s { struct { const char *name; int num_fields; - bool is_secret:1; + bool is_secret:1, is_opaque:1; NamedType_t *fields; } StructInfo; }; diff --git a/structs.c b/structs.c index fe57f47..c5f45c5 100644 --- a/structs.c +++ b/structs.c @@ -12,9 +12,10 @@ #include "typecheck.h" #include "stdlib/util.h" -CORD compile_struct_typeinfo(env_t *env, type_t *t, const char *name, arg_ast_t *fields, bool is_secret) +CORD compile_struct_typeinfo(env_t *env, type_t *t, const char *name, arg_ast_t *fields, bool is_secret, bool is_opaque) { - CORD full_name = CORD_cat(namespace_prefix(env, env->namespace), name); + CORD typeinfo_name = CORD_all(namespace_prefix(env, env->namespace), name, "$$info"); + CORD type_code = Match(t, StructType)->external ? name : CORD_all("struct ", namespace_prefix(env, env->namespace), name, "$$struct"); int num_fields = 0; for (arg_ast_t *f = fields; f; f = f->next) @@ -24,10 +25,11 @@ CORD compile_struct_typeinfo(env_t *env, type_t *t, const char *name, arg_ast_t short_name = strrchr(short_name, '$') + 1; const char *metamethods = is_packed_data(t) ? "PackedData$metamethods" : "Struct$metamethods"; - CORD typeinfo = CORD_asprintf("public const TypeInfo_t %r$$info = {.size=%zu, .align=%zu, .metamethods=%s, " - ".tag=StructInfo, .StructInfo.name=\"%s\"%s, " + CORD typeinfo = CORD_asprintf("public const TypeInfo_t %r = {.size=sizeof(%r), .align=__alignof__(%r), .metamethods=%s, " + ".tag=StructInfo, .StructInfo.name=\"%s\"%s%s, " ".StructInfo.num_fields=%ld", - full_name, type_size(t), type_align(t), metamethods, short_name, is_secret ? ", .StructInfo.is_secret=true" : "", + typeinfo_name, type_code, type_code, metamethods, short_name, is_secret ? ", .StructInfo.is_secret=true" : "", + is_opaque ? ", .StructInfo.is_opaque=true" : "", num_fields); if (fields) { typeinfo = CORD_asprintf("%r, .StructInfo.fields=(NamedType_t[%d]){", typeinfo, num_fields); @@ -44,20 +46,32 @@ CORD compile_struct_typeinfo(env_t *env, type_t *t, const char *name, arg_ast_t CORD compile_struct_header(env_t *env, ast_t *ast) { auto def = Match(ast, StructDef); - CORD full_name = CORD_cat(namespace_prefix(env, env->namespace), def->name); + CORD typeinfo_name = CORD_all(namespace_prefix(env, env->namespace), def->name, "$$info"); + CORD type_code = def->external ? def->name : CORD_all("struct ", namespace_prefix(env, env->namespace), def->name, "$$struct"); CORD fields = CORD_EMPTY; for (arg_ast_t *field = def->fields; field; field = field->next) { type_t *field_t = get_arg_ast_type(env, field); + type_t *check_for_opaque = non_optional(field_t); + if (check_for_opaque->tag == StructType && Match(check_for_opaque, StructType)->opaque) { + if (field->type) + code_err(field->type, "This is an opaque type, so it can't be used as a struct field type"); + else if (field->value) + code_err(field->value, "This is an opaque type, so it can't be used as a struct field type"); + } fields = CORD_all(fields, compile_declaration(field_t, field->name), field_t->tag == BoolType ? ":1" : CORD_EMPTY, ";\n"); } - CORD struct_code = def->external ? CORD_EMPTY : CORD_all("struct ", full_name, "$$struct {\n", fields, "};\n"); + CORD struct_code = def->external ? CORD_EMPTY : CORD_all(type_code, " {\n", fields, "};\n"); type_t *t = Table$str_get(*env->types, def->name); - return CORD_all( - struct_code, - "DEFINE_OPTIONAL_TYPE(", compile_type(t), ", ", heap_strf("%zu", unpadded_struct_size(t)), - ", ", namespace_prefix(env, env->namespace), "$Optional", def->name, "$$type);\n" - "extern const TypeInfo_t ", full_name, "$$info;\n"); + + CORD unpadded_size = def->opaque ? CORD_all("sizeof(", type_code, ")") : CORD_asprintf("%zu", unpadded_struct_size(t)); + CORD typeinfo_code = CORD_all("extern const TypeInfo_t ", typeinfo_name, ";\n"); + CORD optional_code = CORD_EMPTY; + if (!def->opaque) { + optional_code = CORD_all("DEFINE_OPTIONAL_TYPE(", compile_type(t), ", ", unpadded_size, ",", + namespace_prefix(env, env->namespace), "$Optional", def->name, "$$type);\n"); + } + return CORD_all(struct_code, optional_code, typeinfo_code); } // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/structs.h b/structs.h index b189b0d..328972c 100644 --- a/structs.h +++ b/structs.h @@ -7,7 +7,7 @@ #include "ast.h" #include "environment.h" -CORD compile_struct_typeinfo(env_t *env, type_t *t, const char *name, arg_ast_t *fields, bool is_secret); +CORD compile_struct_typeinfo(env_t *env, type_t *t, const char *name, arg_ast_t *fields, bool is_secret, bool is_opaque); CORD compile_struct_header(env_t *env, ast_t *ast); // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/typecheck.c b/typecheck.c index 11b2d6d..0d0690b 100644 --- a/typecheck.c +++ b/typecheck.c @@ -328,32 +328,38 @@ void bind_statement(env_t *env, ast_t *statement) type_t *type = Table$str_get(*env->types, def->name); if (!type) code_err(statement, "Couldn't find type!"); assert(type); - arg_t *fields = NULL; - for (arg_ast_t *field_ast = def->fields; field_ast; field_ast = field_ast->next) { - type_t *field_t = get_arg_ast_type(env, field_ast); - type_t *non_opt_field_t = field_t->tag == OptionalType ? Match(field_t, OptionalType)->type : field_t; - if ((non_opt_field_t->tag == StructType && Match(non_opt_field_t, StructType)->opaque) - || (non_opt_field_t->tag == EnumType && Match(non_opt_field_t, EnumType)->opaque)) { + if (!def->opaque) { + arg_t *fields = NULL; + for (arg_ast_t *field_ast = def->fields; field_ast; field_ast = field_ast->next) { + type_t *field_t = get_arg_ast_type(env, field_ast); + type_t *non_opt_field_t = field_t->tag == OptionalType ? Match(field_t, OptionalType)->type : field_t; + if ((non_opt_field_t->tag == StructType && Match(non_opt_field_t, StructType)->opaque) + || (non_opt_field_t->tag == EnumType && Match(non_opt_field_t, EnumType)->opaque)) { - file_t *file = NULL; - const char *start = NULL, *end = NULL; - if (field_ast->type) { - file = field_ast->type->file, start = field_ast->type->start, end = field_ast->type->end; - } else if (field_ast->value) { - file = field_ast->value->file, start = field_ast->value->start, end = field_ast->value->end; + file_t *file = NULL; + const char *start = NULL, *end = NULL; + if (field_ast->type) { + file = field_ast->type->file, start = field_ast->type->start, end = field_ast->type->end; + } else if (field_ast->value) { + file = field_ast->value->file, start = field_ast->value->start, end = field_ast->value->end; + } + if (non_opt_field_t == type) + compiler_err(file, start, end, "This is a recursive struct that would be infinitely large. Maybe you meant to use an optional '@%T?' pointer instead?", type); + else if (non_opt_field_t->tag == StructType && Match(non_opt_field_t, StructType)->external) + compiler_err(file, start, end, "This is an opaque externally defined struct.\n" + "I can't use it as a member without knowing what its fields are.\n" + "Either specify its fields and remove the `opaque` qualifier, or use something like a @%T pointer.", non_opt_field_t); + else + compiler_err(file, start, end, "I'm still in the process of defining the fields of %T, so I don't know how to use it as a member." + "\nTry using a @%T pointer for this field.", + field_t, field_t); } - if (non_opt_field_t == type) - compiler_err(file, start, end, "This is a recursive struct that would be infinitely large. Maybe you meant to use an optional '@%T?' pointer instead?", type); - else - compiler_err(file, start, end, "I'm still in the process of defining the fields of %T, so I don't know how to use it as a member." - "\nTry using a @%T pointer for this field.", - field_t, field_t); + fields = new(arg_t, .name=field_ast->name, .type=field_t, .default_val=field_ast->value, .next=fields); } - fields = new(arg_t, .name=field_ast->name, .type=field_t, .default_val=field_ast->value, .next=fields); + REVERSE_LIST(fields); + type->__data.StructType.fields = fields; // populate placeholder + type->__data.StructType.opaque = false; } - REVERSE_LIST(fields); - type->__data.StructType.fields = fields; // populate placeholder - type->__data.StructType.opaque = false; for (ast_list_t *stmt = def->namespace ? Match(def->namespace, Block)->statements : NULL; stmt; stmt = stmt->next) bind_statement(ns_env, stmt->ast); @@ -382,6 +388,10 @@ void bind_statement(env_t *env, ast_t *statement) } if (non_opt_field_t == type) compiler_err(file, start, end, "This is a recursive enum that would be infinitely large. Maybe you meant to use an optional '@%T?' pointer instead?", type); + else if (non_opt_field_t->tag == StructType && Match(non_opt_field_t, StructType)->external) + compiler_err(file, start, end, "This is an opaque externally defined struct.\n" + "I can't use it as a member without knowing what its fields are.\n" + "Either specify its fields and remove the `opaque` qualifier, or use something like a @%T pointer.", non_opt_field_t); else compiler_err(file, start, end, "I'm still in the process of defining the fields of %T, so I don't know how to use it as a member." "\nTry using a @%T pointer for this field.", diff --git a/types.c b/types.c index eaecad2..fde7644 100644 --- a/types.c +++ b/types.c @@ -462,6 +462,8 @@ PUREFUNC bool is_packed_data(type_t *t) PUREFUNC size_t unpadded_struct_size(type_t *t) { + if (Match(t, StructType)->opaque) + compiler_err(NULL, NULL, NULL, "The struct type %s is opaque, so I can't get the size of it", Match(t, StructType)->name); arg_t *fields = Match(t, StructType)->fields; size_t size = 0; size_t bit_offset = 0; @@ -546,6 +548,8 @@ PUREFUNC size_t type_size(type_t *t) } } case StructType: { + if (Match(t, StructType)->opaque) + compiler_err(NULL, NULL, NULL, "The struct type %s is opaque, so I can't get the size of it", Match(t, StructType)->name); size_t size = unpadded_struct_size(t); size_t align = type_align(t); if (size > 0 && align > 0 && (size % align) > 0) @@ -625,6 +629,9 @@ PUREFUNC size_t type_align(type_t *t) } } case StructType: { + if (Match(t, StructType)->opaque) + compiler_err(NULL, NULL, NULL, "The struct type %s is opaque, so I can't get the alignment of it", + Match(t, StructType)->name); arg_t *fields = Match(t, StructType)->fields; size_t align = t->tag == StructType ? 0 : sizeof(void*); for (arg_t *field = fields; field; field = field->next) {