- Bitstructs, unions and flexible arrays now correctly emitted in headers.

- Require `@export` functions to have `@export` types.
This commit is contained in:
Christoffer Lerno
2024-06-23 23:39:58 +02:00
parent b0b976ee52
commit 201b1b7fbc
8 changed files with 160 additions and 25 deletions

View File

@@ -8,6 +8,7 @@
- Improved error notes when call expressions have errors.
- Trailing body arguments may now be `&ref`, `#hash`, `$const` and `$Type` arguments.
- "panic-msg" setting to suppress panic message output.
- Require `@export` functions to have `@export` types.
### Fixes
- Error with unsigned compare in `@ensure` when early returning 0 #1207.
@@ -18,6 +19,7 @@
- No longer possible to dereference a function pointer.
- Fix bug with @jump miscompile.
- Bit negate does implicit integer promotion.
- Bitstructs, unions and flexible arrays now correctly emitted in headers.
### Stdlib changes
- Added `remove_first_item` `remove_last_item` and `remove_item` as aliases for the `match` functions.

View File

@@ -49,7 +49,7 @@ const char *trust_level[3] = {
#define EOUTPUT(string, ...) fprintf(stderr, string "\n", ##__VA_ARGS__)
#define PRINTF(string, ...) fprintf(stdout, string "\n", ##__VA_ARGS__) // NOLINT
#define FAIL_WITH_ERR(string, ...) do { fprintf(stderr, "Error: " string "\n\n", ##__VA_ARGS__); usage(); exit_compiler(EXIT_FAILURE); } while (0)
#define FAIL_WITH_ERR(string, ...) do { fprintf(stderr, "Error: " string "\n\n", ##__VA_ARGS__); usage(); exit_compiler(EXIT_FAILURE); } while (0) /* NOLINT */
static void usage(void)
{

View File

@@ -2471,6 +2471,7 @@ Type *type_find_parent_type(Type *type);
bool type_is_subtype(Type *type, Type *possible_subtype);
bool type_is_abi_aggregate(Type *type);
bool type_is_int128(Type *type);
Decl * type_no_export(Type *type);
Type *type_from_token(TokenType type);
bool type_is_user_defined(Type *type);

View File

@@ -4,9 +4,9 @@
#include "compiler_internal.h"
#define PRINTF(x, ...) fprintf(file, x, ## __VA_ARGS__)
#define PRINTF(x, ...) fprintf(file, x, ## __VA_ARGS__) /* NOLINT */
#define INDENT() indent_line(file, indent)
#define OUT(file, x, ...) fprintf(file, x, ## __VA_ARGS__)
#define OUT(file, x, ...) fprintf(file, x, ## __VA_ARGS__) /* NOLINT */
static void header_gen_struct_union(FILE *file, int indent, Decl *decl);
static void header_gen_maybe_generate_type(FILE *file, HTable *table, Type *type);
@@ -125,11 +125,8 @@ static void header_print_type(FILE *file, Type *type)
return;
case TYPE_DISTINCT:
case TYPE_TYPEDEF:
UNREACHABLE
case TYPE_FLEXIBLE_ARRAY:
header_print_type(file, type->array.base);
PRINTF("[]");
return;
UNREACHABLE
case TYPE_ARRAY:
PRINTF("struct { ");
header_print_type(file, type->array.base);
@@ -203,6 +200,7 @@ static void header_gen_function_ptr(FILE *file, HTable *table, Type *type)
static void header_gen_function(FILE *file, FILE *file_types, HTable *table, Decl *decl)
{
if (!decl->is_export) return;
if (decl->name[0] == '_' && decl->name[1] == '_') return;
Signature *sig = &decl->func_decl.signature;
PRINTF("extern ");
Type *rtype = typeget(sig->rtype);
@@ -240,23 +238,41 @@ static void header_gen_members(FILE *file, int indent, Decl **members)
VECEACH(members, i)
{
Decl *member = members[i];
Type *type = type_flatten(member->type);
switch (member->decl_kind)
{
case DECL_VAR:
INDENT();
if (member->type->canonical->type_kind == TYPE_ARRAY)
switch (type->type_kind)
{
header_print_type(file, member->type->canonical->array.base);
PRINTF(" %s[%d];\n", member->name, member->type->canonical->array.len);
break;
case TYPE_ARRAY:
header_print_type(file, type->array.base);
PRINTF(" %s[%d];\n", member->name, type->array.len);
break;
case TYPE_FLEXIBLE_ARRAY:
header_print_type(file, type->array.base);
PRINTF(" %s[];\n", member->name);
break;
default:
header_print_type(file, member->type);
PRINTF(" %s;\n", member->name);
break;
}
header_print_type(file, member->type);
PRINTF(" %s;\n", member->name);
break;
case DECL_STRUCT:
case DECL_UNION:
header_gen_struct_union(file, indent, member);
break;
case DECL_BITSTRUCT:
INDENT();
header_print_type(file, member->bitstruct.base_type->type->canonical);
if (member->name)
{
PRINTF(" %s;\n", member->name);
break;
}
PRINTF(" __bits%d;\n", i);
break;
default:
UNREACHABLE
}
@@ -266,7 +282,7 @@ static void header_gen_struct_union(FILE *file, int indent, Decl *decl)
{
if (!indent)
{
PRINTF("typedef struct %s__ %s;\n", decl->extname, decl->extname);
PRINTF("typedef %s %s__ %s;\n", struct_union_str(decl), decl->extname, decl->extname);
}
INDENT();
if (decl->name)
@@ -343,6 +359,8 @@ void header_ensure_member_types_exist(FILE *file, HTable *table, Decl **members)
case DECL_UNION:
header_ensure_member_types_exist(file, table, member->strukt.members);
break;
case DECL_BITSTRUCT:
break;
default:
UNREACHABLE
}
@@ -419,7 +437,7 @@ RETRY:
if (htable_get(table, type)) return;
{
Decl *decl = type->decl;
PRINTF("typedef struct %s__ %s;\n", decl_get_extname(decl), decl_get_extname(decl));
PRINTF("typedef %s %s__ %s;\n", struct_union_str(decl), decl_get_extname(decl), decl_get_extname(decl));
htable_set(table, type, type);
header_ensure_member_types_exist(file, table, decl->strukt.members);
PRINTF("%s %s__\n", struct_union_str(decl), decl->extname);
@@ -544,6 +562,7 @@ void header_gen(Module **modules, unsigned module_count)
FILE *file_types = fopen(filename_types, "w");
OUT(file_types, "#include <stdint.h>\n");
OUT(file_types, "#include <stddef.h>\n");
OUT(file_types, "#include <stdbool.h>\n");
OUT(file_types, "#ifndef __c3__\n");
OUT(file_types, "#define __c3__\n\n");
OUT(file_types, "typedef void* c3typeid_t;\n");

View File

@@ -11,7 +11,7 @@
static inline bool sema_analyse_func_macro(SemaContext *context, Decl *decl, AttributeDomain domain, bool *erase_decl);
static inline bool sema_analyse_func(SemaContext *context, Decl *decl, bool *erase_decl);
static inline bool sema_analyse_macro(SemaContext *context, Decl *decl, bool *erase_decl);
static inline bool sema_analyse_signature(SemaContext *context, Signature *sig, TypeInfoId type_parent);
static inline bool sema_analyse_signature(SemaContext *context, Signature *sig, TypeInfoId type_parent, bool is_export);
static inline bool sema_analyse_main_function(SemaContext *context, Decl *decl);
static inline bool sema_check_param_uniqueness_and_type(SemaContext *context, Decl **decls, Decl *current,
unsigned current_index, unsigned count);
@@ -27,6 +27,7 @@ static inline Decl *operator_in_module(SemaContext *c, Module *module, OperatorO
static inline bool sema_analyse_operator_element_at(SemaContext *context, Decl *method);
static inline bool sema_analyse_operator_element_set(SemaContext *context, Decl *method);
static inline bool sema_analyse_operator_len(Decl *method, SemaContext *context);
static bool sema_require_export_type(SemaContext *context, TypeInfo *type_info);
static bool sema_check_operator_method_validity(SemaContext *context, Decl *method);
static inline const char *method_name_by_decl(Decl *method_like);
@@ -229,6 +230,7 @@ static inline bool sema_analyse_struct_member(SemaContext *context, Decl *parent
sema_decl_stack_push(decl);
}
bool is_export = parent->is_export;
// Analysis depends on the underlying type.
switch (decl->decl_kind)
{
@@ -240,6 +242,7 @@ static inline bool sema_analyse_struct_member(SemaContext *context, Decl *parent
assert(type_infoptrzero(decl->var.type_info));
TypeInfo *type_info = type_infoptr(decl->var.type_info);
if (!sema_resolve_type_info(context, type_info, RESOLVE_TYPE_ALLOW_FLEXIBLE)) return decl_poison(decl);
if (is_export && !sema_require_export_type(context, type_info)) return decl_poison(decl);
Type *type = type_info->type;
switch (type_storage_type(type))
{
@@ -259,6 +262,7 @@ static inline bool sema_analyse_struct_member(SemaContext *context, Decl *parent
case DECL_STRUCT:
case DECL_UNION:
case DECL_BITSTRUCT:
decl->is_export = is_export;
if (!sema_analyse_decl(context, decl)) return false;
return true;
default:
@@ -945,7 +949,17 @@ ERROR:
return decl_poison(decl);
}
static inline bool sema_analyse_signature(SemaContext *context, Signature *sig, TypeInfoId type_parent)
static bool sema_require_export_type(SemaContext *context, TypeInfo *type_info)
{
Decl *decl = type_no_export(type_info->type);
if (!decl) return true;
SEMA_ERROR(type_info, "%s must also be an exported type, to make this work '%s' needs to be marked '@export'",
type_quoted_error_string(type_info->type), decl->name);
SEMA_NOTE(decl, "The definition is here.");
return false;
}
static inline bool sema_analyse_signature(SemaContext *context, Signature *sig, TypeInfoId type_parent, bool is_export)
{
Variadic variadic_type = sig->variadic;
Decl **params = sig->params;
@@ -963,6 +977,7 @@ static inline bool sema_analyse_signature(SemaContext *context, Signature *sig,
is_macro ? RESOLVE_TYPE_ALLOW_INFER
: RESOLVE_TYPE_DEFAULT)) return false;
rtype = rtype_info->type;
if (is_export && !sema_require_export_type(context, rtype_info)) return false;
if (sig->attrs.nodiscard)
{
if (type_is_void(rtype))
@@ -993,10 +1008,12 @@ static inline bool sema_analyse_signature(SemaContext *context, Signature *sig,
return false;
}
TypeInfo *method_parent = type_infoptrzero(type_parent);
if (is_export && method_parent && !sema_require_export_type(context, method_parent)) return false;
// Fill in the type if the first parameter is lacking a type.
if (type_parent && params && params[0] && !params[0]->var.type_info)
if (method_parent && params && params[0] && !params[0]->var.type_info)
{
TypeInfo *method_parent = type_infoptr(type_parent);
if (!sema_resolve_type_info(context, method_parent,
is_macro ? RESOLVE_TYPE_MACRO_METHOD : RESOLVE_TYPE_FUNC_METHOD)) return false;
Decl *param = params[0];
@@ -1067,6 +1084,7 @@ static inline bool sema_analyse_signature(SemaContext *context, Signature *sig,
if (!sema_resolve_type_info(context, type_info,
is_macro ? RESOLVE_TYPE_ALLOW_INFER
: RESOLVE_TYPE_DEFAULT)) return decl_poison(param);
if (is_export && !sema_require_export_type(context, type_info)) return false;
param->type = type_info->type;
}
switch (var_kind)
@@ -1181,7 +1199,7 @@ bool sema_analyse_function_signature(SemaContext *context, Decl *func_decl, Call
// Get param count and variadic type
Decl **params = signature->params;
if (!sema_analyse_signature(context, signature, func_decl->func_decl.type_parent)) return false;
if (!sema_analyse_signature(context, signature, func_decl->func_decl.type_parent, func_decl->is_export)) return false;
Variadic variadic_type = signature->variadic;
// Remove the last empty value.
@@ -3273,7 +3291,8 @@ static inline bool sema_analyse_macro(SemaContext *context, Decl *decl, bool *er
if (!sema_analyse_func_macro(context, decl, ATTR_MACRO, erase_decl)) return false;
if (*erase_decl) return true;
if (!sema_analyse_signature(context, &decl->func_decl.signature, decl->func_decl.type_parent)) return decl_poison(decl);
if (!sema_analyse_signature(context, &decl->func_decl.signature, decl->func_decl.type_parent,
false)) return decl_poison(decl);
if (!decl->func_decl.signature.is_at_macro && decl->func_decl.body_param && !decl->func_decl.signature.is_safemacro)
{

View File

@@ -384,6 +384,59 @@ bool type_flat_is_boolintlike(Type *type)
return kind == TYPE_BOOL || (kind >= TYPE_INTEGER_FIRST && kind <= TYPE_INTEGER_LAST);
}
Decl *type_no_export(Type *type)
{
type = type->canonical;
RETRY:
switch (type->type_kind)
{
case TYPE_POISONED:
case TYPE_UNTYPED_LIST:
case TYPE_WILDCARD:
case TYPE_MEMBER:
case TYPE_TYPEINFO:
return NULL;
case TYPE_VOID:
case TYPE_BOOL:
case ALL_INTS:
case ALL_FLOATS:
case TYPE_ANY:
case TYPE_INTERFACE:
case TYPE_ANYFAULT:
case TYPE_TYPEID:
return NULL;
case TYPE_POINTER:
type = type->pointer;
goto RETRY;
case TYPE_FUNC_PTR:
type = type->pointer;
FALLTHROUGH;
case TYPE_FUNC_RAW:
case TYPE_ENUM:
case TYPE_STRUCT:
case TYPE_UNION:
case TYPE_BITSTRUCT:
case TYPE_FAULTTYPE:
case TYPE_DISTINCT:
if (type->decl->is_export) return NULL;
return type->decl;
case TYPE_SLICE:
case TYPE_TYPEDEF:
case TYPE_ARRAY:
case TYPE_FLEXIBLE_ARRAY:
case TYPE_INFERRED_ARRAY:
type = type->array.base;
goto RETRY;
case TYPE_VECTOR:
case TYPE_INFERRED_VECTOR:
return NULL;
case TYPE_OPTIONAL:
type = type->optional;
goto RETRY;
}
UNREACHABLE
}
bool type_is_int128(Type *type)
{

View File

@@ -83,11 +83,11 @@ distinct Color = float[<4>];
const Color BLACK = {0, 0, 0, 1};
const Color WHITE = {1, 1, 1, 1};
struct Arena {
struct Arena @export {
usz cursor;
}
struct Arena_Cursor {
struct Arena_Cursor @export {
Arena* arena;
usz cursor;
}
@@ -690,11 +690,11 @@ no_match: ; preds = %compare
!106 = !{!12}
!107 = !DILocalVariable(name: "scratch", scope: !108, file: !5, line: 110, type: !109, align: 8)
!108 = distinct !DISubprogram(name: "@scratch", linkageName: "@scratch", scope: !5, file: !5, line: 109, scopeLine: 109, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit:
!109 = !DICompositeType(tag: DW_TAG_structure_type, name: "Arena_Cursor", scope: !5, file: !5, line: 88, size: 128, align: 64, elements: !110, identifier: "foo.Arena_Cursor")
!109 = !DICompositeType(tag: DW_TAG_structure_type, name: "Arena_Cursor", scope: !5, file: !5, line: 88, size: 128, align: 64, elements: !110, identifier: "foo_Arena_Cursor")
!110 = !{!111, !116}
!111 = !DIDerivedType(tag: DW_TAG_member, name: "arena", scope: !109, file: !5, line: 89, baseType: !112, size: 64, align: 64)
!112 = !DIDerivedType(tag: DW_TAG_pointer_type, name: "Arena*", baseType: !113, size: 64, align: 64, dwarfAddressSpace: 0)
!113 = !DICompositeType(tag: DW_TAG_structure_type, name: "Arena", scope: !5, file: !5, line: 84, size: 64, align: 64, elements: !114, identifier: "foo.Arena")
!113 = !DICompositeType(tag: DW_TAG_structure_type, name: "Arena", scope: !5, file: !5, line: 84, size: 64, align: 64, elements: !114, identifier: "foo_Arena")
!114 = !{!115}
!115 = !DIDerivedType(tag: DW_TAG_member, name: "cursor", scope: !113, file: !5, line: 85, baseType: !44, size: 64, align: 64)
!116 = !DIDerivedType(tag: DW_TAG_member, name: "cursor", scope: !109, file: !5, line: 90, baseType: !44, size: 64, align: 64, offset: 64)

View File

@@ -0,0 +1,41 @@
fn Abcd test(Test a) @export
{
return Abcd.ABC;
}
fn void test2(Abc a) @export // #error: must also be an exported type
{
}
fault Abcd @export
{
ABC,
EFG
}
enum Abc
{
ABC,
DEF
}
struct Test @export
{
struct
{
int a;
int b;
}
bitstruct : int
{
int fa : 1..6;
}
int[*] x;
}
struct Test2 @export
{
struct
{
Abc d; // #error: must also be an exported type
}
}