aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2025-03-16 13:35:58 -0400
committerBruce Hill <bruce@bruce-hill.com>2025-03-16 13:35:58 -0400
commit469b1e067961e021e7860f70919b574c142d1f40 (patch)
treecd696be4b66550669c883075fd41a6bbd2a26eaa
parentf51acef40e8297d7bd41b774413aa8331ca946ed (diff)
Fixes for opaque external structs
-rw-r--r--ast.h2
-rw-r--r--compile.c41
-rw-r--r--enums.c2
-rw-r--r--parse.c16
-rw-r--r--stdlib/arrays.h20
-rw-r--r--stdlib/structs.c2
-rw-r--r--stdlib/types.h2
-rw-r--r--structs.c38
-rw-r--r--structs.h2
-rw-r--r--typecheck.c56
-rw-r--r--types.c7
11 files changed, 109 insertions, 79 deletions
diff --git a/ast.h b/ast.h
index 0b8e7865..c9db9061 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 c753fc6d..d40ab487 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 811b42b7..06af0df3 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 bbe8a70f..10fa97dc 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 0e75f0e5..332a1db0 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 b3b26bde..0b1385ee 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 f74048cc..c7b938a0 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 fe57f472..c5f45c59 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 b189b0db..328972cd 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 11b2d6dc..0d0690bb 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)) {
-
- 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 (!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;
+ }
+ 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 eaecad2f..fde76440 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) {