diff --git a/.gitignore b/.gitignore index 30c8e0e58..48360bf80 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ *.ko *.obj *.elf +*.ll # Linker output *.ilk diff --git a/README.md b/README.md index a80605b9c..064726b6c 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ C3 tries to be an alternative in the the C/C++ niche: fast and close to the meta - Learning C3 should be easy for a C programmer. - Data is inert. - Avoid "big ideas" & the "more is better" fallacy. -- Dare introducing some conveniences not "close to metal" if the value is great. +- Introduce some higher level conveniences where the value is great. ### In what ways do C3 differ from C? @@ -42,29 +42,69 @@ developer of Judge0. Design work is still being done in the design draft here: https://c3lang.github.io/c3docs/. If you have suggestions, send a mail to [christoffer@aegik.com](mailto:christoffer@aegik.com), [file an issue](https://github.com/c3lang/c3c/issues) or discuss C3 on the r/ProgrammingLanguages Discord server: https://discord.gg/cfu4wdk -#### What's currently missing +#### Todo / done -- `asm` sections. -- bitstructs -- array range initializers e.g. `{ [1..2] = 2 }` -- `assert` - with compiler hint -- `$switch` `$for` - compile time iteration / switch -- Pre/post conditions -- `generic` - explicit overloading -- `malloc` / `free` -- `string` not fully implemented -- vararrays, e.g. `int[*]` not working -- `unreachable` for compiler hinting -- Generic modules -- Stdlib not linked. - -Also see: https://github.com/c3lang/c3c/issues - -#### What's working? - -- Lexing/parsing/semantic analysis/codegen. -- "Regular code" should mostly work. -- You can use any C function by declaring it as a normal C3 function with external +- [x] For/while/do +- [x] `if`/ternary +- [x] Structs +- [x] Union +- [x] Enums +- [x] Value methods +- [x] Compound literals +- [x] Designated initalizers +- [x] Slicing syntax +- [x] Arrays and subarrays +- [x] Modules +- [x] `$unreachable` +- [x] Compile time assert with `$assert` +- [x] Compiler guiding `assert` +- [x] C code calling by declaring methods `extern` +- [x] Compile time variables +- [x] Basic macros +- [x] 4cc, 8cc, 2cc +- [x] Enum type inference in switch/assignment +- [x] Integer type inference +- [x] Error type +- [x] Failable error handling +- [x] `try` for conditional execution +- [x] `catch` for error handling +- [x] Implicit unwrap after `catch` +- [x] `sizeof` +- [x] `typeof` +- [x] 2s complement wrapping operators +- [x] Labelled break / continue +- [x] `next` statement +- [x] Expression blocks +- [x] Do-without-while +- [ ] Foreach statement +- [ ] All attributes +- [ ] Associative array literals +- [ ] CT type constants +- [ ] Reflection methods +- [ ] Anonymous structs +- [ ] Distinct types +- [ ] LTO/ThinLTO setup +- [ ] Built-in linking +- [ ] `global` / `shared` for globals +- [ ] Complex macros +- [ ] CT only macros evaluating to constants +- [ ] Escape macros +- [ ] Implicit capturing macros +- [ ] Trailing body macros +- [ ] Subarray initializers +- [ ] slice initializers e.g. `{ [1..2] = 2 }` +- [ ] Bitstructs +- [ ] `asm` section +- [ ] `$switch` +- [ ] `$for` +- [ ] Pre-post conditions +- [ ] Stdlib inclusion +- [ ] Generic modules +- [ ] String functions +- [ ] Vararrays e.g. `int[*]` +- [ ] Compile time incremental arrays +- [ ] Complete C ABI conformance +- [ ] Generic functions #### What can you help with? diff --git a/resources/testfragments/struct_ref.c3 b/resources/testfragments/struct_ref.c3 new file mode 100644 index 000000000..0976f701c --- /dev/null +++ b/resources/testfragments/struct_ref.c3 @@ -0,0 +1,51 @@ +struct Foo +{ + int x; + struct b + { + int y; + int z; + } + struct + { + int g; + } + union + { + usize x1; + int z2; + } +} + +func void Foo.xy(Foo* f, int y) +{ + f.b.y = y; +} + +extern func void printf(char*c , ...); + +struct SimpleStruct +{ + int a; + double b; +} +func void main() +{ + Foo z; + z.x = 2; + z.b.z = 3; + z.g = 4; + printf("z.x = %d\n", z.x); + printf("z.b.z = %d\n", z.b.z); + printf("z.g = %d\n", z.g); + z.xy(100); + printf("z.b.y = %d\n", z.b.y); + SimpleStruct s = { a = 3, b = 33.3 }; + int[2] fe = { 1, 2 }; + //Foo f = { x = 2, b.z = 3, z2 = -2 }; + Foo f = { x = 2, b.z = 3, z2 = -2, b = { 1, 4 } }; + int[3] fo = { [1] = 2 }; + + + //byte[7] fo = { [1..4] = 3 }; +} \ No newline at end of file diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 80e2d2005..9354e690e 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -166,7 +166,9 @@ typedef enum typedef struct _DesignatedPath { - DesignatedPathKind kind; + DesignatedPathKind kind : 3; + bool constant : 1; + bool pure : 1; struct _DesignatedPath *sub_path; Type *type; union @@ -623,7 +625,7 @@ typedef struct typedef struct { DesignatedPath *path; - Expr* value; + Expr *value; } ExprDesignatedInit; typedef struct @@ -1585,4 +1587,11 @@ static inline void advance_and_verify(Context *context, TokenType token_type) assert(context->tok.type == token_type); advance(context); } + +#define TRY_AST_OR(_ast_stmt, _res) ({ Ast* _ast = (_ast_stmt); if (!ast_ok(_ast)) return _res; _ast; }) +#define TRY_EXPR_OR(_expr_stmt, _res) ({ Expr* _expr = (_expr_stmt); if (!expr_ok(_expr)) return _res; _expr; }) +#define TRY_TYPE_OR(_type_stmt, _res) ({ TypeInfo* _type = (_type_stmt); if (!type_info_ok(_type)) return _res; _type; }) +#define TRY_TYPE_REAL_OR(_type_stmt, _res) ({ Type* _type = (_type_stmt); if (!type_ok(_type)) return _res; _type; }) +#define TRY_DECL_OR(_decl_stmt, _res) ({ Decl* _decl = (_decl_stmt); if (!decl_ok(_decl)) return _res; _decl; }) + #pragma clang diagnostic pop \ No newline at end of file diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index e0cdfadb4..743f6fa08 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -369,8 +369,141 @@ static inline LLVMValueRef gencontext_emit_cast_expr(GenContext *context, Expr * return gencontext_emit_cast(context, expr->cast_expr.kind, rhs, expr->type->canonical, expr->cast_expr.expr->type->canonical); } +static LLVMValueRef gencontext_emit_initializer_list_expr_const(GenContext *context, Expr *expr); +static LLVMValueRef gencontext_construct_const_value(GenContext *context, Expr *expr) +{ + NESTED_RETRY: + if (expr->expr_kind == EXPR_COMPOUND_LITERAL) + { + expr = expr->expr_compound_literal.initializer; + goto NESTED_RETRY; + } + if (expr->expr_kind == EXPR_INITIALIZER_LIST) + { + return gencontext_emit_initializer_list_expr_const(context, expr); + } + return gencontext_emit_expr(context, expr); +} +static LLVMValueRef gencontext_construct_const_union_struct(GenContext *context, Type *canonical, Expr *value) +{ + LLVMValueRef values[2]; + values[0] = gencontext_construct_const_value(context, value); + unsigned size_diff = type_size(canonical) - type_size(value->type); + if (size_diff > 0) + { + LLVMTypeRef size = LLVMArrayType(llvm_type(type_char), size_diff); + values[1] = LLVMConstNull(size); + } + return LLVMConstStructInContext(context->context, values, size_diff > 0 ? 2 : 1, false); +} +static LLVMValueRef gencontext_recursive_set_const_value(GenContext *context, DesignatedPath *path, LLVMValueRef parent, Type *parent_type, Expr *value) +{ + switch (path->kind) + { + case DESIGNATED_IDENT: + if (!path->sub_path) + { + if (parent_type->canonical->type_kind == TYPE_UNION) + { + return gencontext_construct_const_union_struct(context, parent_type, value); + } + return LLVMConstInsertValue(parent, gencontext_construct_const_value(context, value), &path->index, 1); + } + else + { + parent_type = path->type; + return LLVMConstInsertValue(parent, + gencontext_recursive_set_const_value(context, + path->sub_path, + LLVMConstExtractValue(parent, + &path->index, + 1), + parent_type, + value), &path->index, 1); + } + case DESIGNATED_SUBSCRIPT: + { + // TODO range, more arrays + assert(path->index_expr->expr_kind == EXPR_CONST); + unsigned int index = (unsigned int)bigint_as_unsigned(&path->index_expr->const_expr.i); + if (!path->sub_path) + { + LLVMValueRef res = gencontext_construct_const_value(context, value); + return LLVMConstInsertValue(parent, res, &index, 1); + } + parent_type = path->type; + return LLVMConstInsertValue(parent, + gencontext_recursive_set_const_value(context, + path->sub_path, + LLVMConstExtractValue(parent, &index, 1), + parent_type, + value), &index, 1); + } + default: + UNREACHABLE; + + } + +} + +static LLVMValueRef gencontext_emit_initializer_list_expr_const(GenContext *context, Expr *expr) +{ + Type *canonical = expr->type->canonical; + LLVMTypeRef type = llvm_type(canonical); + + if (expr->expr_initializer.init_type == INITIALIZER_ZERO) + { + return LLVMConstNull(type); + } + + bool is_error = expr->type->canonical->type_kind == TYPE_ERRTYPE; + + if (is_error) + { + TODO + } + + Expr **elements = expr->expr_initializer.initializer_expr; + + bool is_union = expr->type->canonical->type_kind == TYPE_UNION; + + if (expr->expr_initializer.init_type == INITIALIZER_NORMAL && is_union) + { + assert(vec_size(elements) == 1); + return gencontext_construct_const_union_struct(context, canonical, elements[0]); + } + + if (expr->expr_initializer.init_type == INITIALIZER_NORMAL) + { + LLVMValueRef value = LLVMGetUndef(type); + VECEACH(elements, i) + { + Expr *element = elements[i]; + if (element->expr_kind == EXPR_CONST) + { + + } + value = LLVMConstInsertValue(value, gencontext_emit_expr(context, element), &i, 1); + } + return value; + } + + LLVMValueRef value = LLVMConstNull(type); + VECEACH(elements, i) + { + Expr *element = elements[i]; + DesignatedPath *path = element->designated_init_expr.path; + Type *parent_type = expr->type->canonical; + value = gencontext_recursive_set_const_value(context, + path, + value, + parent_type, + element->designated_init_expr.value); + } + return value; +} /** * Emit a Foo { .... } literal. @@ -380,6 +513,19 @@ static inline LLVMValueRef gencontext_emit_cast_expr(GenContext *context, Expr * */ static inline LLVMValueRef gencontext_emit_initializer_list_expr_addr(GenContext *context, Expr *expr, LLVMValueRef optional_ref) { + if (expr->constant && type_size(expr->type) <= type_size(type_voidptr) * 4) + { + LLVMTypeRef type = llvm_type(expr->type); + LLVMValueRef val = gencontext_emit_initializer_list_expr_const(context, expr); + LLVMValueRef ref = LLVMAddGlobal(context->module, type, ""); + LLVMSetInitializer(ref, val); + LLVMSetGlobalConstant(ref, true); + if (optional_ref) + { + LLVMBuildMemCpy(context->builder, optional_ref, LLVMGetAlignment(optional_ref), ref, LLVMGetAlignment(ref), LLVMSizeOf(type)); + } + return ref; + } LLVMTypeRef type = llvm_type(expr->type); LLVMValueRef ref = optional_ref ?: gencontext_emit_alloca(context, type, "literal"); @@ -426,6 +572,7 @@ static inline LLVMValueRef gencontext_emit_initializer_list_expr_addr(GenContext return ref; } + VECEACH(elements, i) { if (is_error) TODO diff --git a/src/compiler/parser_internal.h b/src/compiler/parser_internal.h index cdf95a13e..1ec4a1db2 100644 --- a/src/compiler/parser_internal.h +++ b/src/compiler/parser_internal.h @@ -15,11 +15,7 @@ #define TRY_CONSUME_EOS() TRY_CONSUME_EOS_OR(poisoned_ast) #define RETURN_AFTER_EOS(_ast) extend_ast_with_prev_token(context, ast); TRY_CONSUME_EOS_OR(poisoned_ast); return _ast -#define TRY_AST_OR(_ast_stmt, _res) ({ Ast* _ast = (_ast_stmt); if (!ast_ok(_ast)) return _res; _ast; }) #define TRY_AST(_ast_stmt) TRY_AST_OR(_ast_stmt, poisoned_ast) -#define TRY_EXPR_OR(_expr_stmt, _res) ({ Expr* _expr = (_expr_stmt); if (!expr_ok(_expr)) return _res; _expr; }) -#define TRY_TYPE_OR(_type_stmt, _res) ({ TypeInfo* _type = (_type_stmt); if (!type_info_ok(_type)) return _res; _type; }) -#define TRY_DECL_OR(_decl_stmt, _res) ({ Decl* _decl = (_decl_stmt); if (!decl_ok(_decl)) return _res; _decl; }) #define CHECK_EXPR(_expr) do { if (!expr_ok(_expr)) return _expr; } while(0) diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index a3793a505..1ae323ad9 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -937,54 +937,6 @@ static inline void insert_access_deref(Expr *expr) expr->access_expr.parent = deref; } -static inline bool sema_expr_analyse_method(Context *context, Expr *expr, Decl *decl, bool is_pointer) -{ - const char *name = TOKSTR(expr->access_expr.sub_element); - VECEACH(decl->methods, i) - { - Decl *function = decl->methods[i]; - if (function->name == name) - { - if (is_pointer) - { - insert_access_deref(expr); - } - expr->access_expr.ref = function; - expr->type = function->type; - return true; - } - } - - if (decl_is_struct_type(decl)) - { - SEMA_ERROR(expr, "There is no element or method '%s.%s'.", decl->name, name); - } - else - { - SEMA_ERROR(expr, "Cannot find method '%s.%s'", decl->name, name); - } - return false; -} - - -static Decl *strukt_recursive_search_member(Decl *strukt, const char *name) -{ - VECEACH(strukt->strukt.members, i) - { - Decl *member = strukt->strukt.members[i]; - if (member->name == name) return member; - if (decl_is_struct_type(member) && !member->name) - { - Decl *result = strukt_recursive_search_member(member, name); - if (result) - { - return result; - } - } - } - return NULL; -} - static inline bool sema_expr_analyse_group(Context *context, Type *to, Expr *expr) { if (!sema_analyse_expr(context, NULL, expr->group_expr)) return false; @@ -1162,6 +1114,29 @@ static inline bool sema_expr_analyse_member_access(Context *context, Expr *expr) */ +static void add_members_to_context(Context *context, Decl *decl) +{ + if (decl_is_struct_type(decl)) + { + Decl **members = decl->strukt.members; + VECEACH(members, i) + { + Decl *member = members[i]; + if (member->name == NULL) + { + add_members_to_context(context, member); + continue; + } + sema_add_member(context, member); + } + } + VECEACH(decl->methods, i) + { + Decl *func = decl->methods[i]; + sema_add_member(context, func); + } +} + static inline bool sema_expr_analyse_access(Context *context, Expr *expr) { Expr *parent = expr->access_expr.parent; @@ -1211,7 +1186,6 @@ static inline bool sema_expr_analyse_access(Context *context, Expr *expr) } goto NO_MATCH; case TYPE_ENUM: - return sema_expr_analyse_method(context, expr, type->decl, is_pointer); case TYPE_ERRTYPE: case TYPE_STRUCT: case TYPE_UNION: @@ -1222,10 +1196,14 @@ static inline bool sema_expr_analyse_access(Context *context, Expr *expr) return false; } Decl *decl = type->decl; - Decl *member = strukt_recursive_search_member(decl, TOKSTR(expr->access_expr.sub_element)); + context_push_scope(context); + add_members_to_context(context, decl); + Decl *member = sema_resolve_symbol_in_current_dynamic_scope(context, kw); + + context_pop_scope(context); if (!member) { - return sema_expr_analyse_method(context, expr, decl, is_pointer); + SEMA_ERROR(expr, "There is no element or method '%s.%s'.", decl->name, kw); return false; } if (is_pointer) @@ -1270,6 +1248,8 @@ static DesignatedPath *sema_analyse_init_identifier_string(Context *context, Des sub_path->kind = DESIGNATED_IDENT; sub_path->index = i; parent_path->sub_path = sub_path; + sub_path->pure = true; + sub_path->constant = true; *has_found_match = true; return sub_path; } @@ -1284,6 +1264,11 @@ static DesignatedPath *sema_analyse_init_access(Context *context, DesignatedPath if (!last_path) return NULL; DesignatedPath *path = sema_analyse_init_identifier_string(context, last_path, TOKSTR(access_expr->access_expr.sub_element), has_found_match, has_reported_error); + if (path) + { + path->pure = true; + path->constant = true; + } if (!path && has_found_match && !has_reported_error) { SEMA_TOKID_ERROR(access_expr->access_expr.sub_element, "'%s' is not a valid sub member.", TOKSTR(access_expr->access_expr.sub_element)); @@ -1341,6 +1326,11 @@ static DesignatedPath *sema_analyse_init_subscript(Context *context, DesignatedP DesignatedPath *sub_path = CALLOCS(DesignatedPath); path->sub_path = sub_path; + sub_path->pure = index->pure; + sub_path->constant = index->constant; + path->constant &= index->pure; + path->pure &= index->pure; + sub_path->type = inner_type; sub_path->kind = DESIGNATED_SUBSCRIPT; sub_path->index_expr = index; @@ -1370,9 +1360,8 @@ static bool sema_expr_analyse_designated_initializer(Context *context, Type *ass Expr **init_expressions = initializer->expr_initializer.initializer_expr; bool is_structlike = type_is_structlike(assigned->canonical); - // TODO purity const - initializer->pure = false; - initializer->constant = false; + initializer->pure = true; + initializer->constant = true; VECEACH(init_expressions, i) { Expr *expr = init_expressions[i]; @@ -1391,6 +1380,8 @@ static bool sema_expr_analyse_designated_initializer(Context *context, Type *ass } Expr *init_expr = expr->binary_expr.left; DesignatedPath path = { .type = assigned }; + path.pure = true; + path.constant = true; bool has_reported_error = false; bool has_found_match = false; DesignatedPath *last_path = sema_analyse_init_path(context, &path, init_expr, &has_found_match, &has_reported_error); @@ -1401,11 +1392,15 @@ static bool sema_expr_analyse_designated_initializer(Context *context, Type *ass } Expr *value = expr->binary_expr.right; if (!sema_analyse_expr_of_required_type(context, last_path->type, value, true)) return false; + expr->pure = value->pure & last_path->pure; + expr->constant = value->constant & last_path->constant; expr->expr_kind = EXPR_DESIGNATED_INITIALIZER; expr->designated_init_expr.path = path.sub_path; expr->designated_init_expr.value = value; expr->failable |= value->failable; expr->resolve_status = RESOLVE_DONE; + initializer->pure &= expr->pure; + initializer->constant &= expr->constant; } initializer->expr_initializer.init_type = INITIALIZER_DESIGNATED; return true; @@ -1417,9 +1412,8 @@ static bool sema_expr_analyse_designated_initializer(Context *context, Type *ass */ static inline bool sema_expr_analyse_struct_plain_initializer(Context *context, Decl *assigned, Expr *initializer) { - // TODO purity const - initializer->pure = false; - initializer->constant = false; + initializer->pure = true; + initializer->constant = true; Expr **elements = initializer->expr_initializer.initializer_expr; Decl **members = assigned->strukt.members; @@ -1448,14 +1442,17 @@ static inline bool sema_expr_analyse_struct_plain_initializer(Context *context, // 4. Check if we exceeded the list of elements in the struct/union. // This way we can check the other elements which might help the // user pinpoint where they put the double elements. + Expr *element = elements[i]; if (i >= expected_members) { - SEMA_ERROR(elements[i], "Too many elements in initializer, expected only %d.", expected_members); + SEMA_ERROR(element, "Too many elements in initializer, expected only %d.", expected_members); return false; } // 5. We know the required type, so resolve the expression. if (!sema_analyse_expr_of_required_type(context, members[i]->type, elements[i], 0)) return false; - initializer->failable = elements[i]->failable; + initializer->pure &= element->pure; + initializer->constant &= element->constant; + initializer->failable |= element->failable; } // 6. There's the case of too few values as well. Mark the last element as wrong. @@ -1476,9 +1473,8 @@ static inline bool sema_expr_analyse_struct_plain_initializer(Context *context, */ static inline bool sema_expr_analyse_array_plain_initializer(Context *context, Type *assigned, Expr *initializer) { - // TODO purity const - initializer->pure = false; - initializer->constant = false; + initializer->pure = true; + initializer->constant = true; Expr **elements = initializer->expr_initializer.initializer_expr; @@ -1502,13 +1498,16 @@ static inline bool sema_expr_analyse_array_plain_initializer(Context *context, T VECEACH(elements, i) { + Expr *element = elements[i]; if (i >= expected_members) { - SEMA_ERROR(elements[i], "Too many elements in initializer, expected only %d.", expected_members); + SEMA_ERROR(element, "Too many elements in initializer, expected only %d.", expected_members); return false; } - if (!sema_analyse_expr_of_required_type(context, inner_type, elements[i], true)) return false; - initializer->failable |= elements[i]->failable; + if (!sema_analyse_expr_of_required_type(context, inner_type, element, true)) return false; + initializer->failable |= element->failable; + initializer->pure &= element->pure; + initializer->constant &= element->constant; } if (expected_members > size) @@ -1546,10 +1545,8 @@ static inline bool sema_expr_analyse_initializer(Context *context, Type *assigne { return sema_expr_analyse_array_plain_initializer(context, assigned, expr); } - else - { - return sema_expr_analyse_struct_plain_initializer(context, assigned->decl, expr); - } + + return sema_expr_analyse_struct_plain_initializer(context, assigned->decl, expr); } static inline bool sema_expr_analyse_initializer_list(Context *context, Type *to, Expr *expr)