diff --git a/releasenotes.md b/releasenotes.md index b5a695f24..ca93c2a0b 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -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. diff --git a/src/build/build_options.c b/src/build/build_options.c index 01985dd1d..d5fd58b32 100644 --- a/src/build/build_options.c +++ b/src/build/build_options.c @@ -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) { diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 27e4a93e1..55e8202f3 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -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); diff --git a/src/compiler/headers.c b/src/compiler/headers.c index 597d55c8e..a6b48ded3 100644 --- a/src/compiler/headers.c +++ b/src/compiler/headers.c @@ -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 \n"); OUT(file_types, "#include \n"); + OUT(file_types, "#include \n"); OUT(file_types, "#ifndef __c3__\n"); OUT(file_types, "#define __c3__\n\n"); OUT(file_types, "typedef void* c3typeid_t;\n"); diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 6b0c218c0..07a5983c8 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -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) { diff --git a/src/compiler/types.c b/src/compiler/types.c index c598c00b0..856c3d3b3 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -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) { diff --git a/test/test_suite/debug_symbols/defer_macro.c3t b/test/test_suite/debug_symbols/defer_macro.c3t index 60beb41c8..15854e693 100644 --- a/test/test_suite/debug_symbols/defer_macro.c3t +++ b/test/test_suite/debug_symbols/defer_macro.c3t @@ -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) diff --git a/test/test_suite/visibility/export_property.c3 b/test/test_suite/visibility/export_property.c3 new file mode 100644 index 000000000..80d9f7d60 --- /dev/null +++ b/test/test_suite/visibility/export_property.c3 @@ -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 + } +} \ No newline at end of file