diff --git a/resources/grammar.y b/resources/grammar.y index 7ed4cf47e..0beb9a846 100644 --- a/resources/grammar.y +++ b/resources/grammar.y @@ -257,7 +257,6 @@ base_type type_expression : base_type | type_expression '*' - | type_expression '&' | type_expression '[' constant_expression ']' | type_expression '[' ']' | type_expression '[' '+' ']' diff --git a/resources/testfragments/bigint.c3 b/resources/testfragments/bigint.c3 new file mode 100644 index 000000000..28fabcff2 --- /dev/null +++ b/resources/testfragments/bigint.c3 @@ -0,0 +1,273 @@ +module bigint; + +macro @max(a, b) +{ + return a > b ? a : b; +} + +// Horribly bad implementation of BigInt with add/sub. +public struct BigInt @opaque +{ + byte& number; + uint length; + char sign; +} + +public func void BigInt.init(BigInt& bigInt) +{ + bigInt.number = malloc(1); + bigInt.number[0] = 0; + bigInt.length = 1; + bigInt.sign = 1; +} + +public func void BigInt.initFromString(BigInt& bigInt, char& str) +{ + uint size = strlen(str); + bigInt.sign = 1; + switch (str[0]) + { + case '-': + bigInt.sign = -1; + size--; + str++; + case '+': + size--; + str++; + default: + break; + } + char* res = malloc(size); + for (uint i = 0; i < size; i++) + { + res[i] = str[size - i - 1] - '0'; + } + bigInt.number = res; + bigInt.length = size; +} + +public func BigInt& BigInt.newFromString(char& str) +{ + BigInt& bigInt = malloc(sizeof(BigInt)); + bigInt.initFromString(str); + return bigInt; +} + +public func void BigInt.copyTo(BigInt& source, BigInt& target) +{ + target.number = realloc(target.number, source.length); + target.sign = source.sign; + target.length = source.length; + for (uint i = 0; i < target.length; i++) target.number[i] = source.number[i]; +} + +public func void BigInt.destroy(BigInt& bigInt) +{ + free(bigInt.number); +} + +func void BigInt.addIgnoreSign(BigInt& a, BigInt& b, BigInt& result) +{ + uint length = @max(a.length, b.length) + 1; + byte* res = malloc(length); + char carry = 0; + BigInt* x; + BigInt* y; + if (a.length > b.length) + { + x = a; + y = b; + } + else + { + x = b; + y = a; + } + for (uint i = 0; i < length; i++) + { + if (i >= y.length) + { + res[i] = carry + (i >= x.length ? 0 : x.number[i]); + } + else + { + res[i] = x.number[i] + y.number[i] + carry; + } + carry = 0; + if (res[i] > 9) + { + carry = 1; + res[i] -= 10; + } + } + result.destroy(); + result.number = res; + result.length = length; +} + +public func void BigInt.getMaxVal(BigInt &bigInt, uint& pos, int& val) +{ + for (uint i = bigInt.length; i > 0; i++) + { + if (bigInt.number[i] != 0) + { + *pos = i; + *val = bigInt.number[i]; + return; + } + } + *pos = 0; + *val = 0; +} + +public func char BigInt.compare(BigInt& a, BigInt& b) +{ + if (a.sign != b.sign) return a.sign; + byte aMax; + uint aMaxPos; + a.getMaxVal(&aMaxPos, &aMax); + if (aMaxPos >= b.length) return a.sign; + byte bMax; + uint bMaxPos; + b.getMaxVal(&bMaxPos, &bMax); + if (aMaxPos > bMaxPos) return a.sign; + if (aMaxPos < bMaxPos) return -a.sign; + if (aMax > bMax) return a.sign; + if (aMax < bMax) return -a.sign; + return 0; +} + +public func char BigInt.compareNoSign(BigInt& a, BigInt& b) +{ + byte aMax; + uint aMaxPos; + a.getMaxVal(&aMaxPos, &aMax); + if (aMaxPos >= b.length) return 1; + byte bMax; + uint bMaxPos; + b.getMaxVal(&bMaxPos, &bMax); + if (aMaxPos > bMaxPos) return 1; + if (aMaxPos < bMaxPos) return -1; + if (aMax > bMax) return 1; + if (aMax < bMax) return -1; + return 0; +} + +func void BigInt.subIgnoreSign(BigInt& a, BigInt& b, BigInt& result) +{ + uint length = @max(a.length, b.length); + byte& res = malloc(length); + + BigInt& x; + BigInt& y; + if (a.compareNoSign(b) < 0) + { + result.sign = -1; + x = b; + y = a; + } + else + { + result.sign = 1; + x = a; + y = b; + } + + byte borrow = 0; + for (uint i = 0; i < length; i++) + { + byte aValue = i >= x.length ? 0 : x.number[i]; + byte bValue = borrow + (i >= y.length ? 0 : y.number[i]); + if (bValue > aValue) + { + borrow = 1; + aValue += 10; + } + else + { + borrow = 0; + } + res[i] = aValue - bValue; + } + result.destroy(); + result.number = res; + result.length = length; +} + +public func void BigInt.add(BigInt& a, BigInt& b, BigInt& result) +{ + if (a.sign == b.sign) + { + a.addIgnoreSign(b, result); + result.sign = a.sign; + return; + } + if (a.sign < 0) + { + b.subIgnoreSign(a, result); + } + else + { + a.subIgnoreSign(a, result); + } +} + +public func char* BigInt.toCharArray(BigInt* bigInt) +{ + uint charLen = bigInt.length + 1 + (bigInt.sign < 0 ? 1 : 0); + byte* out = malloc(charLen); + out[charLen - 1] = '\0'; + byte* start = out; + if (bigInt.sign < 0) + { + out[0] = '-'; + start++; + } + bool nonZeroFound = false; + for (uint i = bigInt.length; i > 0; i--) + { + byte digit = bigInt.number[i - 1]; + if (i > 1 && !nonZeroFound && digit == 0) continue; + nonZeroFound = true; + *(start++) = digit + '0'; + } + return out; +} + +public func void BigInt.print(BigInt& bigInt) +{ + char* chars = bigInt.toCharArray(); + puts(chars); + free(chars); +} + +/* +public func void BigInt.fprint(BigInt* bigInt, FILE* file) +{ + char* chars = bigInt.toCharArray(); + fputs(chars, file); + putc('\n', file); + free(chars); +}*/ + + +public func int main(int size, char*& args) +{ + BigInt minus2; + BigInt minus1; + BigInt current; + minus2.initFromString("0"); + minus1.initFromString("1"); + current.initFromString("0"); + + + for (int i = 1; i <= 500; i++) + { + minus1.add(&minus2, ¤t); + printf("%d : ", i); + current.print(); + minus1.copyTo(&minus2); + current.copyTo(&minus1); + } + return 0; +} diff --git a/resources/testfragments/simple_test1.c3 b/resources/testfragments/simple_test1.c3 index 88d40698c..266ede4cf 100644 --- a/resources/testfragments/simple_test1.c3 +++ b/resources/testfragments/simple_test1.c3 @@ -1,7 +1,19 @@ module foo; +public struct Foo +{ + int i; +} + +public func int Foo.test(Foo *foo) +{ + if (!foo) return 0; + return foo.i; +} + public func void gonk() { + Foo.test(nil); printf("Bob\n"); } func void test() diff --git a/resources/testfragments/simple_test2.c3 b/resources/testfragments/simple_test2.c3 index 2c7817f2a..7ab2beac0 100644 --- a/resources/testfragments/simple_test2.c3 +++ b/resources/testfragments/simple_test2.c3 @@ -8,6 +8,6 @@ func void test() func void main() { - gronk(); + gonk(); printf("Helo\n"); } \ No newline at end of file diff --git a/src/compiler/ast.c b/src/compiler/ast.c index e98544ad9..3295cc640 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -7,8 +7,7 @@ Decl *decl_new(DeclKind decl_kind, Token name, Visibility visibility) { assert(name.string); - Decl *decl = malloc_arena(sizeof(Decl)); - memset(decl, 0, sizeof(Decl)); + Decl *decl = CALLOCS(Decl); decl->decl_kind = decl_kind; decl->name = name; decl->visibility = visibility; @@ -318,9 +317,9 @@ void fprint_expr_recursive(FILE *file, Expr *expr, int indent) fprintf(file, "(postunary %s\n", token_type_to_string(expr->post_expr.operator)); fprint_expr_recursive(file, expr->post_expr.expr, indent + 1); break; - case EXPR_METHOD_REF: - fprintf(file, "(methodref .%s\n", expr->method_ref_expr.method.string); - fprint_type_recursive(file, expr->method_ref_expr.type, indent + 1); + case EXPR_TYPE_ACCESS: + fprintf(file, "(typeaccess .%s\n", expr->type_access.name.string); + fprint_type_recursive(file, expr->type_access.type, indent + 1); break; case EXPR_STRUCT_VALUE: fprintf(file, "(structvalue\n"); @@ -434,7 +433,7 @@ void fprint_decl_recursive(FILE *file, Decl *decl, int indent) break; case DECL_FUNC: fprintf(file, "(func %s\n", decl->name.string); - fprint_type_recursive(file, decl->func.struct_parent, indent + 1); + fprint_type_recursive(file, decl->func.type_parent, indent + 1); fprint_func_signature(file, &decl->func.function_signature, indent + 1); fprint_ast_recursive(file, decl->func.body, indent + 1); break; diff --git a/src/compiler/casts.c b/src/compiler/casts.c index 9dd77b4e3..0ed7dbb5f 100644 --- a/src/compiler/casts.c +++ b/src/compiler/casts.c @@ -84,11 +84,9 @@ static inline bool may_implicitly_cast_ptr_to_ptr(Type *current_type, Type *targ assert(target_type->canonical == target_type); // Neither is void* or have matching bases: - // TODO recursively check for ?* or? if (target_type->base != type_void && current_type->base != type_void && target_type->base != current_type->base) return false; - // Current is not null, or target is nullable? Ok! - return target_type->nullable || !current_type->nullable; + return true; } bool ptpt(Expr* left, Type *canonical, Type *type, CastType cast_type) @@ -446,11 +444,8 @@ bool usus(Expr* left, Type *canonical, Type *type, CastType cast_type) { if (type_is_subtype(left_canonical->base, canonical->base)) { - if (!left_canonical->base->nullable || canonical->base->nullable) - { - insert_cast(left, CAST_PTRPTR, canonical); - return true; - } + insert_cast(left, CAST_PTRPTR, canonical); + return true; } sema_type_mismatch(left, type, cast_type); return false; diff --git a/src/compiler/codegen.c b/src/compiler/codegen.c index cee405cd9..964f871e2 100644 --- a/src/compiler/codegen.c +++ b/src/compiler/codegen.c @@ -379,6 +379,16 @@ static int codegen_emit_initializer_list(Context *context, Expr *expr, int inden return index; } +static int codegen_emit_access(Context *context, Expr *expr, int indent) +{ + int left = codegen_emit_expr(context, expr->access_expr.parent, indent); + int index = ++context->unique_index; + INDENT(); + PRINTTYPE(expr->type); + PRINTF(" _%d = _%d.%s;\n", index, left, expr->access_expr.sub_element.string); + return index; +} + static int codegen_emit_unary_expr(Context *context, Expr *expr, int indent) { int index = ++context->unique_index; @@ -452,6 +462,8 @@ static int codegen_emit_expr(Context *context, Expr *expr, int indent) return codegen_emit_unary_expr(context, expr, indent); case EXPR_INITIALIZER_LIST: return codegen_emit_initializer_list(context, expr, indent); + case EXPR_ACCESS: + return codegen_emit_access(context, expr, indent); default: TODO } diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index 9ae49ed81..915876567 100644 --- a/src/compiler/compiler.c +++ b/src/compiler/compiler.c @@ -10,7 +10,7 @@ Compiler compiler; void compiler_init(void) { stable_init(&compiler.modules, 64); - compiler.module_list = NULL; + stable_init(&compiler.global_symbols, 0x1000); } static void compiler_lex() @@ -63,14 +63,33 @@ void compiler_compile() vec_add(contexts, context); parse_file(context); } + const char *printf = "printf"; + TokenType t_type = TOKEN_IDENT; + const char *interned = symtab_add(printf, (uint32_t) 6, fnv1a(printf, (uint32_t)6), &t_type); + Decl *decl = decl_new(DECL_FUNC, wrap(interned), VISIBLE_PUBLIC); + Type *type = type_new(TYPE_POINTER); + type->base = type_char; + sema_resolve_type(contexts[0], type); + Decl *param = decl_new_var(wrap("str"), type, VARDECL_PARAM, VISIBLE_LOCAL); + vec_add(decl->func.function_signature.params, param); + decl->func.function_signature.rtype = type_void; + decl->resolve_status = RESOLVE_DONE; + context_register_global_decl(contexts[0], decl); + + VECEACH(contexts, i) + { + sema_analysis_pass_conditional_compilation(contexts[i]); + } + VECEACH(contexts, i) + { + sema_analysis_pass_decls(contexts[i]); + } + if (diagnostics.errors > 0) exit(EXIT_FAILURE); VECEACH(contexts, i) { Context *context = contexts[i]; - sema_analysis(context); - if (diagnostics.errors > 0) exit(EXIT_FAILURE); char buffer[255]; sprintf(buffer, "%s_test.c", context->module_name.string); - printf("%s\n", buffer); FILE *f = fopen(buffer,"w"); fprintf(f, "#include \n#include \n"); context->codegen_output = f; @@ -103,22 +122,7 @@ void compile_file() Decl *compiler_find_symbol(Token token) { - Decl *candidate = NULL; - VECEACH(compiler.module_list, i) - { - Module *module = compiler.module_list[i]; - Decl *decl = module_find_symbol(module, token.string); - if (decl && candidate) - { - const char *previous = candidate->module->name; - const char *current = decl->module->name; - SEMA_ERROR(token, "Ambiguous use of '%s', matches both %s::%s and %s::%s.", token.string, - previous, token.string, current, token.string); - return &poisoned_decl; - } - candidate = decl; - } - return candidate; + return stable_get(&compiler.global_symbols, token.string); } Module *compiler_find_or_create_module(const char *module_name) @@ -129,6 +133,21 @@ Module *compiler_find_or_create_module(const char *module_name) module->name = module_name; stable_init(&module->symbols, 0x10000); stable_set(&compiler.modules, module_name, module); - vec_add(compiler.module_list, module); return module; } + +void compiler_register_public_symbol(Decl *decl) +{ + Decl *prev = stable_get(&compiler.global_symbols, decl->name.string); + // If the previous symbol was already declared globally, remove it. + stable_set(&compiler.global_symbols, decl->name.string, prev ? &poisoned_decl : decl); + STable *sub_module_space = stable_get(&compiler.qualified_symbols, decl->module->name); + if (!sub_module_space) + { + sub_module_space = malloc_arena(sizeof(*sub_module_space)); + stable_init(sub_module_space, 0x100); + stable_set(&compiler.qualified_symbols, decl->module->name, sub_module_space); + } + prev = stable_get(sub_module_space, decl->name.string); + stable_set(sub_module_space, decl->name.string, prev ? &poisoned_decl : decl); +} diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 3b53bfc7d..385ef7615 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -76,8 +76,8 @@ typedef struct typedef struct { - Token package; Token module; + Token sub_module; } Path; struct _Type @@ -107,7 +107,6 @@ struct _Type { Expr *unresolved_len; size_t len; - bool nullable; }; }; }; @@ -139,7 +138,6 @@ typedef struct typedef struct { Decl **members; - Decl **method_functions; } StructDecl; @@ -195,7 +193,7 @@ typedef struct typedef struct { const char *full_name; - Type *struct_parent; + Type *type_parent; FunctionSignature function_signature; Ast *body; FuncAnnotations *annotations; @@ -251,16 +249,23 @@ typedef struct _Decl }; uint32_t size;*/ Type *self_type; - struct _Module *module; + Module *module; Attr** attributes; union { - ErrorDecl error; + struct + { + Decl** method_functions; + union + { + ErrorDecl error; + StructDecl strukt; + EnumDecl enums; + }; + }; ErrorConstantDecl error_constant; ImportDecl import; - StructDecl strukt; VarDecl var; - EnumDecl enums; EnumConstantDecl enum_constant; FuncDecl func; AttrDecl attr; @@ -284,8 +289,12 @@ typedef struct typedef struct { Type *type; - Token method; -} ExprMethodRef; + union + { + Token name; + Decl *method; + }; +} ExprTypeRef; typedef struct { @@ -353,7 +362,11 @@ typedef struct typedef struct { Expr *parent; - Token sub_element; + union + { + Token sub_element; + Decl *ref; + }; } ExprAccess; typedef struct @@ -393,7 +406,7 @@ struct _Expr ExprCast expr_cast; ExprConst const_expr; ExprStructValue struct_value_expr; - ExprMethodRef method_ref_expr; + ExprTypeRef type_access; ExprTry try_expr; ExprBinary binary_expr; ExprAssign assign_expr; @@ -600,14 +613,8 @@ typedef struct _Ast } Ast; -typedef struct _Package -{ - const char *name; -} Package; - typedef struct _Module { - Package *package; const char *name; bool is_external; @@ -646,6 +653,7 @@ typedef struct _Context Decl **enums; Decl **types; Decl **functions; + Decl **struct_functions; Decl **vars; Decl **ct_ifs; Ast **defers; @@ -667,7 +675,8 @@ typedef struct _Context typedef struct { STable modules; - Module **module_list; + STable global_symbols; + STable qualified_symbols; } Compiler; extern Context *current_context; @@ -682,7 +691,7 @@ extern Diagnostics diagnostics; extern Token next_tok; extern Token tok; -extern Type *type_bool, *type_void, *type_string; +extern Type *type_bool, *type_void, *type_string, *type_voidptr, *type_voidref; extern Type *type_float, *type_double; extern Type *type_char, *type_short, *type_int, *type_long, *type_isize; extern Type *type_byte, *type_ushort, *type_uint, *type_ulong, *type_usize; @@ -696,6 +705,7 @@ extern Type t_f32, t_f64, t_fxx; extern Type t_u0, t_str; extern Type t_cus, t_cui, t_cul, t_cull; extern Type t_cs, t_ci, t_cl, t_cll; +extern Type t_voidstar; #define AST_NEW(_kind, _token) new_ast(_kind, _token) @@ -772,6 +782,7 @@ bool sema_analyse_expr(Context *context, Expr *expr); Decl *compiler_find_symbol(Token token); Module *compiler_find_or_create_module(const char *module_name); +void compiler_register_public_symbol(Decl *decl); Context *context_create(File *file); void context_push(Context *context); @@ -851,7 +862,9 @@ void parse_file(Context *context); #define SEMA_ERROR(_tok, ...) sema_error_range(_tok.span, __VA_ARGS__) void sema_init(File *file); -void sema_analysis(Context *context); +void sema_analysis_pass_conditional_compilation(Context *context); +void sema_analysis_pass_decls(Context *context); +void sema_analysis_pass_3(Context *context); bool sema_analyse_statement(Context *context, Ast *statement); bool sema_resolve_type(Context *context, Type *type); @@ -900,6 +913,7 @@ static inline bool type_is_signed(Type *type) { return type->type_kind >= TYPE_I static inline bool type_is_unsigned(Type *type) { return type->type_kind >= TYPE_U8 && type->type_kind <= TYPE_UXX; } static inline bool type_ok(Type *type) { return !type || type->type_kind != TYPE_POISONED; } static inline void type_poison(Type *type) { type->type_kind = TYPE_POISONED; type->resolve_status = RESOLVE_DONE; } +bool type_may_have_method_functions(Type *type); static inline bool type_is_integer(Type *type) { assert(type == type->canonical); diff --git a/src/compiler/context.c b/src/compiler/context.c index ca1aa2b30..e6ee17722 100644 --- a/src/compiler/context.c +++ b/src/compiler/context.c @@ -114,7 +114,15 @@ void context_register_global_decl(Context *context, Decl *decl) case DECL_GENERIC: break; case DECL_FUNC: - vec_add(context->functions, decl); + if (decl->func.type_parent) + { + vec_add(context->struct_functions, decl); + return; + } + else + { + vec_add(context->functions, decl); + } break; case DECL_VAR: vec_add(context->vars, decl); @@ -123,6 +131,7 @@ void context_register_global_decl(Context *context, Decl *decl) case DECL_UNION: case DECL_TYPEDEF: vec_add(context->types, decl); + break; case DECL_ENUM: vec_add(context->enums, decl); @@ -140,7 +149,6 @@ void context_register_global_decl(Context *context, Decl *decl) case DECL_ATTRIBUTE: UNREACHABLE break; - case DECL_CT_IF: vec_add(context->ct_ifs, decl); return; @@ -154,6 +162,7 @@ void context_register_global_decl(Context *context, Decl *decl) } if (!old && decl->visibility == VISIBLE_PUBLIC) { + compiler_register_public_symbol(decl); old = stable_set(&context->module->public_symbols, decl->name.string, decl); } if (old != NULL) diff --git a/src/compiler/enums.h b/src/compiler/enums.h index ecb127033..22c27dbf5 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -188,7 +188,7 @@ typedef enum EXPR_POST_UNARY, EXPR_TYPE, EXPR_IDENTIFIER, - EXPR_METHOD_REF, + EXPR_TYPE_ACCESS, EXPR_CALL, EXPR_SIZEOF, EXPR_SUBSCRIPT, diff --git a/src/compiler/expr_analysis.c b/src/compiler/expr_analysis.c index d076c3e0b..59e226a9d 100644 --- a/src/compiler/expr_analysis.c +++ b/src/compiler/expr_analysis.c @@ -65,7 +65,7 @@ static inline bool sema_expr_analyse_identifier(Context *context, Expr *expr) static inline bool sema_expr_analyse_var_call(Context *context, Expr *expr) { TODO } static inline bool sema_expr_analyse_macro_call(Context *context, Expr *expr, Decl *macro) { - Ast macro_parent; + Ast *macro_parent; // TODO handle loops Decl *stored_macro = context->evaluating_macro; Type *stored_rtype = context->rtype; @@ -104,11 +104,18 @@ static inline bool sema_expr_analyse_call(Context *context, Expr *expr) { Expr *func_expr = expr->call_expr.function; if (!sema_analyse_expr(context, func_expr)) return false; - if (func_expr->expr_kind != EXPR_IDENTIFIER) + Decl *decl; + switch (func_expr->expr_kind) { - TODO + case EXPR_TYPE_ACCESS: + decl = func_expr->type_access.method; + break; + case EXPR_IDENTIFIER: + decl = func_expr->identifier_expr.decl; + break; + default: + TODO } - Decl *decl = func_expr->identifier_expr.decl; switch (decl->decl_kind) { case DECL_VAR: @@ -141,14 +148,166 @@ static inline bool sema_expr_analyse_subscript(Context *context, Expr *expr) TODO } -static inline bool sema_expr_analyse_access(Context *context, Expr *expr) +static inline bool sema_expr_analyse_method_function(Context *context, Expr *expr, Decl *decl, bool is_pointer) { - TODO + const char *name = expr->access_expr.sub_element.string; + VECEACH(decl->method_functions, i) + { + Decl *function = decl->method_functions[i]; + if (function->name.string == name) + { + // TODO + return true; + } + } + SEMA_ERROR(expr->loc, "Cannot find method function '%s.%s'", decl->name.string, name); + return false; } -static inline bool sema_expr_analyse_method_ref(Context *context, Expr *expr) +static inline bool sema_expr_analyse_enum_constant(Context *context, Expr *expr, Decl *decl) { - TODO + const char *name = expr->type_access.name.string; + VECEACH(decl->enums.values, i) + { + Decl *enum_constant = decl->enums.values[i]; + if (enum_constant->name.string == name) + { + assert(enum_constant->resolve_status == RESOLVE_DONE); + expr_replace(expr, decl->enum_constant.expr); + return true; + } + } + SEMA_ERROR(expr->loc, "'%s' has no enumeration value '%s'.", decl->name.string, name); + return false; +} + +static inline bool sema_expr_analyse_error_constant(Context *context, Expr *expr, Decl *decl) +{ + const char *name = expr->type_access.name.string; + VECEACH(decl->error.error_constants, i) + { + Decl *error_constant = decl->error.error_constants[i]; + if (error_constant->name.string == name) + { + assert(error_constant->resolve_status == RESOLVE_DONE); + expr->type = decl->self_type; + expr->expr_kind = EXPR_CONST; + expr->const_expr.type = CONST_INT; + expr->const_expr.i = decl->error_constant.value; + return true; + } + } + SEMA_ERROR(expr->loc, "'%s' has no error type '%s'.", decl->name.string, 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.string == name) return member; + if (!member->name.string && decl_is_struct_type(member)) + { + Decl *result = strukt_recursive_search_member(member, name); + if (result) return result; + } + } + return NULL; +} + +static inline bool sema_expr_analyse_access(Context *context, Expr *expr) +{ + if (!sema_analyse_expr(context, expr->access_expr.parent)) return false; + Type *parent_type = expr->access_expr.parent->type; + + Type *type = parent_type->canonical; + bool is_pointer = type->type_kind == TYPE_POINTER; + if (is_pointer) + { + type = type->base; + } + if (!type_may_have_method_functions(type)) + { + SEMA_ERROR(expr->loc, "Cannot access '%s' on '%s'", expr->access_expr.sub_element.string, type_to_error_string(parent_type)); + return false; + } + Decl *decl = type->decl; + switch (decl->decl_kind) + { + case DECL_ENUM: + case DECL_ERROR: + return sema_expr_analyse_method_function(context, expr, decl, is_pointer); + case DECL_STRUCT: + case DECL_UNION: + break; + default: + UNREACHABLE + } + Decl *member = strukt_recursive_search_member(decl, expr->access_expr.sub_element.string); + if (!member) + { + SEMA_ERROR(expr->access_expr.sub_element, "There is no element '%s.%s'.", decl->name.string, expr->access_expr.sub_element.string); + return false; + } + if (is_pointer) + { + Expr *deref = expr_new(EXPR_UNARY, expr->loc); + deref->unary_expr.operator = TOKEN_STAR; + deref->unary_expr.expr = expr->access_expr.parent; + deref->resolve_status = RESOLVE_DONE; + deref->type = type; + expr->access_expr.parent = deref; + } + if (member->decl_kind == DECL_VAR) + { + expr->type = member->var.type; + } + else + { + expr->type = member->self_type; + } + return true; +} + +static inline bool sema_expr_analyse_type_access(Context *context, Expr *expr) +{ + Type *type = expr->type_access.type; + if (!sema_resolve_type(context, type)) return false; + if (!type_may_have_method_functions(type)) + { + SEMA_ERROR(expr->loc, "'%s' does not have method functions.", type_to_error_string(type)); + return false; + } + Decl *decl = type->decl; + // TODO add more constants that can be inspected? + // e.g. SomeEnum.values, MyUnion.x.offset etc? + switch (decl->decl_kind) + { + case DECL_ENUM: + if (expr->type_access.name.type == TOKEN_CONST_IDENT) return sema_expr_analyse_enum_constant(context, expr, decl); + break; + case DECL_ERROR: + if (expr->type_access.name.type == TOKEN_CONST_IDENT) return sema_expr_analyse_error_constant(context, expr, decl); + break; + case DECL_UNION: + case DECL_STRUCT: + break; + default: + UNREACHABLE + } + VECEACH(type->decl->method_functions, i) + { + Decl *function = type->decl->method_functions[i]; + if (expr->type_access.name.string == function->name.string) + { + expr->type_access.method = function; + expr->type = function->func.function_signature.rtype; + return true; + } + } + SEMA_ERROR(expr->loc, "No function '%s.%s' found.", type_to_error_string(type), expr->type_access.name.string); + return false; } static inline Decl *decl_find_by_name(Decl** decls, const char *name) @@ -631,11 +790,6 @@ static bool sema_expr_analyse_deref(Context *context, Expr *expr, Expr *inner) SEMA_ERROR(inner->loc, "Dereferencing nil is not allowed."); return false; } - if (canonical->nullable) - { - SEMA_ERROR(inner->loc, "Dereferencing a nullable pointer is not allowed."); - return false; - } Type *deref_type = inner->type->type_kind != TYPE_POINTER ? inner->type : canonical; expr->type = deref_type->base; return true; @@ -651,7 +805,6 @@ static bool sema_expr_analyse_addr(Context *context, Expr *expr, Expr *inner) Type *type = type_new(TYPE_POINTER); type->name_loc = inner->type->name_loc; type->base = inner->type; - type->nullable = false; type->resolve_status = RESOLVE_DONE; type->canonical = type_get_canonical_ptr(type); assert(type->resolve_status == RESOLVE_DONE); @@ -912,7 +1065,7 @@ static ExprAnalysis EXPR_ANALYSIS[EXPR_CAST + 1] = { [EXPR_POST_UNARY] = &sema_expr_analyse_postunary, [EXPR_TYPE] = &sema_expr_analyse_type, [EXPR_IDENTIFIER] = &sema_expr_analyse_identifier, - [EXPR_METHOD_REF] = &sema_expr_analyse_method_ref, + [EXPR_TYPE_ACCESS] = &sema_expr_analyse_type_access, [EXPR_CALL] = &sema_expr_analyse_call, [EXPR_SIZEOF] = &sema_expr_analyse_sizeof, [EXPR_SUBSCRIPT] = &sema_expr_analyse_subscript, diff --git a/src/compiler/parser.c b/src/compiler/parser.c index ce7550236..f8b1753a0 100644 --- a/src/compiler/parser.c +++ b/src/compiler/parser.c @@ -207,14 +207,14 @@ static Path *parse_path(void) Path *path = malloc_arena(sizeof(Path)); memset(path, 0, sizeof(Path)); - path->package = tok; + path->sub_module = tok; if (tok.type == TOKEN_IDENT && next_tok.type == TOKEN_SCOPE) { advance(); advance(); - path->module = tok; - path->package = tok; + path->module = path->sub_module; + path->sub_module = tok; } return path; @@ -409,33 +409,8 @@ static Type *parse_type_expression(void) Type *ptr_type = type_new(TYPE_POINTER); assert(type); ptr_type->base = type; - ptr_type->nullable = true; type = ptr_type; } - break; - case TOKEN_AND: - advance(); - { - Type *ptr_type = type_new(TYPE_POINTER); - assert(type); - ptr_type->base = type; - ptr_type->nullable = false; - type = ptr_type; - ptr_type = type_new(TYPE_POINTER); - ptr_type->base = type; - ptr_type->nullable = false; - type = ptr_type; - break; - } - case TOKEN_AMP: - advance(); - { - Type *ptr_type = type_new(TYPE_POINTER); - assert(type); - ptr_type->base = type; - ptr_type->nullable = false; - type = ptr_type; - } break; default: return type; @@ -1901,8 +1876,6 @@ static inline Decl *parse_struct_declaration(Visibility visibility) if (!consume_type_name(type_name)) return &poisoned_decl; Decl *decl = decl_new_user_defined_type(name, decl_from_token(type), visibility); - decl->strukt.method_functions = NULL; - if (!parse_attributes(decl)) { return &poisoned_decl; @@ -2344,7 +2317,7 @@ static inline Decl *parse_func_definition(Visibility visibility, bool is_interfa Type *type = type_new(TYPE_USER_DEFINED); type->unresolved.path = path; type->name_loc = tok; - func->func.struct_parent = type; + func->func.type_parent = type; advance_and_verify(TOKEN_TYPE_IDENT); TRY_CONSUME_OR(TOKEN_DOT, "Expected '.' after the type in a method function.", false); @@ -3029,7 +3002,7 @@ static Expr *parse_nil(Expr *left) assert(!left && "Had left hand side"); Expr *number = EXPR_NEW_TOKEN(EXPR_CONST, tok); number->const_expr.type = CONST_NIL; - number->type = type_get_canonical_ptr(type_void); + number->type = type_voidptr; number->resolve_status = RESOLVE_DONE; advance(); return number; @@ -3089,13 +3062,13 @@ static Expr *parse_initializer(void) * @param type * @return Expr */ -static Expr *parse_method_ref(Type *type) +static Expr *parse_type_access(Type *type) { - Expr *expr = EXPR_NEW_TOKEN(EXPR_METHOD_REF, tok); - expr->method_ref_expr.type = type; + Expr *expr = EXPR_NEW_TOKEN(EXPR_TYPE_ACCESS, tok); + expr->type_access.type = type; advance_and_verify(TOKEN_DOT); - expr->method_ref_expr.method = tok; + expr->type_access.name = tok; TRY_CONSUME_OR(TOKEN_IDENT, "Expected a function name or value", &poisoned_expr); @@ -3141,7 +3114,7 @@ static Expr *parse_type_identifier_with_path(Path *path) return expr; } EXPECT_OR(TOKEN_DOT, &poisoned_expr); - return parse_method_ref(type); + return parse_type_access(type); } /** diff --git a/src/compiler/semantic_analyser.c b/src/compiler/semantic_analyser.c index 90fb315fe..8776e1f26 100644 --- a/src/compiler/semantic_analyser.c +++ b/src/compiler/semantic_analyser.c @@ -20,6 +20,7 @@ void sema_shadow_error(Decl *decl, Decl *old) } + Decl *context_find_ident(Context *context, const char *symbol) { Decl **first = &context->locals[0]; @@ -185,7 +186,6 @@ static inline bool sema_analyse_struct_union(Context *context, Decl *decl) static inline bool sema_analyse_function_param(Context *context, Decl *param, bool is_function) { - if (!decl_ok(param)) return false; assert(param->decl_kind == DECL_VAR); assert(param->var.kind == VARDECL_PARAM); if (!sema_resolve_type(context, param->var.type)) @@ -218,11 +218,16 @@ static inline bool sema_analyse_function_signature(Context *context, FunctionSig // TODO check parameter name appearing more than once. VECEACH(signature->params, i) { - if (!sema_analyse_function_param(context, signature->params[i], is_function)) + Decl *param = signature->params[i]; + assert(param->resolve_status == RESOLVE_NOT_DONE); + param->resolve_status = RESOLVE_RUNNING; + if (!sema_analyse_function_param(context, param, is_function)) { - decl_poison(signature->params[i]); + decl_poison(param); all_ok = false; + continue; } + param->resolve_status = RESOLVE_DONE; } VECEACH(signature->throws, i) { @@ -939,13 +944,40 @@ static inline bool sema_analyse_function_body(Context *context, Decl *func) context_pop_scope(context); return true; } + + +static inline bool sema_analyse_method_function(Context *context, Decl *decl) +{ + Type *parent_type = decl->func.type_parent; + if (!sema_resolve_type(context, parent_type)) return false; + if (!type_may_have_method_functions(parent_type)) + { + SEMA_ERROR(decl->name, "Method functions can not be associated with '%s'", type_to_error_string(decl->func.type_parent)); + return false; + } + Decl *parent = parent_type->decl; + VECEACH(parent->method_functions, i) + { + Decl *function = parent->method_functions[i]; + if (function->name.string == decl->name.string) + { + SEMA_ERROR(decl->name, "Duplicate name '%s' for method function.", function->name); + sema_prev_at_range(function->name.span, "Previous definition here."); + return false; + } + } + DEBUG_LOG("Method function '%s.%s' analysed.", parent->name.string, decl->name.string); + vec_add(parent->method_functions, decl); + return true; +} + static inline bool sema_analyse_func(Context *context, Decl *decl) { DEBUG_LOG("Analysing function %s", decl->name.string); bool all_ok = sema_analyse_function_signature(context, &decl->func.function_signature, true); - if (decl->func.struct_parent) + if (decl->func.type_parent) { - all_ok = sema_resolve_type(context, decl->func.struct_parent) && all_ok; + all_ok = all_ok && sema_analyse_method_function(context, decl); } all_ok = all_ok && sema_analyse_function_body(context, decl); if (!all_ok) decl_poison(decl); @@ -1128,26 +1160,19 @@ static inline void sema_process_imports(Context *context) { // TODO } -void sema_analysis(Context *context) -{ - const char *printf = "printf"; - TokenType t_type = TOKEN_IDENT; - const char *interned = symtab_add(printf, (uint32_t) 6, fnv1a(printf, (uint32_t)6), &t_type); - Decl *decl = decl_new(DECL_FUNC, wrap(interned), VISIBLE_PUBLIC); - Type *type = type_new(TYPE_POINTER); - type->base = type_char; - sema_resolve_type(context, type); - Decl *param = decl_new_var(wrap("str"), type, VARDECL_PARAM, VISIBLE_LOCAL); - vec_add(decl->func.function_signature.params, param); - decl->func.function_signature.rtype = type_void; - decl->resolve_status = RESOLVE_DONE; - context_register_global_decl(context, decl); - sema_process_imports(context); +void sema_analysis_pass_conditional_compilation(Context *context) +{ + DEBUG_LOG("Pass 1 - analyse: %s", context->file->name); VECEACH(context->ct_ifs, i) { sema_analyse_top_level_if(context, context->ct_ifs[i]); } +} + +void sema_analysis_pass_decls(Context *context) +{ + DEBUG_LOG("Pass 2 - analyse: %s", context->file->name); VECEACH(context->enums, i) { sema_analyse_decl(context, context->enums[i]); @@ -1156,6 +1181,10 @@ void sema_analysis(Context *context) { sema_analyse_decl(context, context->types[i]); } + VECEACH(context->struct_functions, i) + { + sema_analyse_decl(context, context->struct_functions[i]); + } VECEACH(context->vars, i) { sema_analyse_decl(context, context->vars[i]); @@ -1175,7 +1204,7 @@ bool sema_resolve_type_shallow(Context *context, Type *type) if (type->resolve_status == RESOLVE_RUNNING) { - SEMA_ERROR(type->name_loc, "Circular dependency resolving type '%s'.", type->name_loc); + SEMA_ERROR(type->name_loc, "Circular dependency resolving type '%s'.", type->name_loc.string); type_poison(type); return false; } diff --git a/src/compiler/symtab.c b/src/compiler/symtab.c index 90a52bdc2..499533fff 100644 --- a/src/compiler/symtab.c +++ b/src/compiler/symtab.c @@ -59,7 +59,7 @@ void symtab_init(uint32_t capacity) uint32_t len = (uint32_t)strlen(name); TokenType type = (TokenType)i; const char* interned = symtab_add(name, (uint32_t)strlen(name), fnv1a(name, len), &type); - assert(type == i); + assert(type == (TokenType)i); assert(symtab_add(name, (uint32_t)strlen(name), fnv1a(name, len), &type) == interned); } diff --git a/src/compiler/types.c b/src/compiler/types.c index f73117c5a..3bb912d70 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -4,7 +4,7 @@ #include "compiler_internal.h" -Type *type_bool, *type_void, *type_string; +Type *type_bool, *type_void, *type_string, *type_voidptr; Type *type_float, *type_double; Type *type_char, *type_short, *type_int, *type_long, *type_isize; Type *type_byte, *type_ushort, *type_uint, *type_ulong, *type_usize; @@ -19,7 +19,7 @@ Type t_f32, t_f64, t_fxx; Type t_usz, t_isz; Type t_cus, t_cui, t_cul, t_cull; Type t_cs, t_ci, t_cl, t_cll; - +Type t_voidstar; Type *type_signed_int_by_size(int bitsize) { @@ -70,7 +70,7 @@ const char *type_to_error_string(Type *type) case TYPE_FXX: return type->name_loc.string; case TYPE_POINTER: - asprintf(&buffer, "%s%s", type_to_error_string(type->base), type->nullable ? "*" : "?"); + asprintf(&buffer, "%s*", type_to_error_string(type->base)); return buffer; case TYPE_STRING: return "string"; @@ -147,7 +147,6 @@ static inline void create_ptr_live_canonical(Type *canonical_type) assert(canonical_type->canonical == canonical_type); canonical_type->ptr_like_canonical = VECADD(canonical_type->ptr_like_canonical, NULL); canonical_type->ptr_like_canonical = VECADD(canonical_type->ptr_like_canonical, NULL); - canonical_type->ptr_like_canonical = VECADD(canonical_type->ptr_like_canonical, NULL); } Type *type_get_canonical_ptr(Type *ptr_type) @@ -161,7 +160,7 @@ Type *type_get_canonical_ptr(Type *ptr_type) create_ptr_live_canonical(canonical_base); } - Type *canonical_ptr = canonical_base->ptr_like_canonical[(int)ptr_type->nullable]; + Type *canonical_ptr = canonical_base->ptr_like_canonical[0]; if (canonical_ptr == NULL) { canonical_ptr = malloc_arena(sizeof(Type)); @@ -169,7 +168,7 @@ Type *type_get_canonical_ptr(Type *ptr_type) canonical_ptr->base = canonical_base; canonical_ptr->canonical = canonical_ptr; canonical_ptr->resolve_status = RESOLVE_DONE; - canonical_base->ptr_like_canonical[(int)ptr_type->nullable] = canonical_ptr; + canonical_base->ptr_like_canonical[0] = canonical_ptr; canonical_base->resolve_status = RESOLVE_DONE; } @@ -187,20 +186,20 @@ Type *type_get_canonical_array(Type *arr_type) // Dynamic array if (arr_type->len == 0) { - Type *canonical = canonical_base->ptr_like_canonical[2]; + Type *canonical = canonical_base->ptr_like_canonical[1]; if (canonical == NULL) { canonical = malloc_arena(sizeof(Type)); *canonical = *arr_type; canonical->canonical = canonical_base; - canonical_base->ptr_like_canonical[2] = canonical; + canonical_base->ptr_like_canonical[1] = canonical; canonical->resolve_status = RESOLVE_DONE; } return canonical; } int entries = (int)vec_size(canonical_base->ptr_like_canonical); - for (int i = 3; i < entries; i++) + for (int i = 1; i < entries; i++) { Type *ptr = canonical_base->ptr_like_canonical[i]; if (ptr->len == arr_type->len) @@ -239,6 +238,10 @@ void builtin_setup() { create_type("void", &t_u0, &type_void, TYPE_VOID, 1, 8); create_type("string", &t_str, &type_string, TYPE_STRING, build_options.pointer_size, build_options.pointer_size * 8); + create_ptr_live_canonical(type_void); + type_void->ptr_like_canonical[0] = &t_voidstar; + create_type("void*", &t_voidstar, &type_voidptr, TYPE_POINTER, 0, 0); + t_voidstar.base = type_void; /*TODO * decl_string = (Decl) { .decl_kind = DECL_BUILTIN, .name.string = "string" }; @@ -308,3 +311,19 @@ bool type_is_subtype(Type *type, Type *possible_subtype) } +bool type_may_have_method_functions(Type *type) +{ + // An alias is not ok. + if (type->type_kind != TYPE_USER_DEFINED) return false; + Decl *decl = type->decl; + switch (decl->decl_kind) + { + case DECL_UNION: + case DECL_STRUCT: + case DECL_ERROR: + case DECL_ENUM: + return true; + default: + return false; + } +} \ No newline at end of file