From 7e0a29ef4031c1e1ddd33bbeb7bbdb733c821663 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Sat, 23 Jul 2022 23:08:27 +0200 Subject: [PATCH] Fix constant typeid comparisons. Allow methods to use & and * and constants. Improved error messages. Updated String type with generic append. --- lib/std/core/string.c3 | 63 +++++++++++++++++++++----------- lib/std/math.matrix.c3 | 6 +-- src/compiler/compiler_internal.h | 1 + src/compiler/number.c | 12 +++++- src/compiler/parse_stmt.c | 6 +-- src/compiler/parser_internal.h | 1 + src/compiler/sema_decls.c | 41 +++++++++++++++++---- src/compiler/sema_expr.c | 20 ++++++++-- src/compiler/sema_passes.c | 6 +-- src/compiler/sema_stmts.c | 9 ++--- src/version.h | 2 +- 11 files changed, 117 insertions(+), 50 deletions(-) diff --git a/lib/std/core/string.c3 b/lib/std/core/string.c3 index 60b0d7fb1..1eb9c95be 100644 --- a/lib/std/core/string.c3 +++ b/lib/std/core/string.c3 @@ -41,14 +41,12 @@ fn String new(char[] c) return (String)data; } -fn ZString String.zstr(String* str) +fn ZString String.zstr(String str) { - if (!str) return (ZString)""; StringData* data = str.data(); if (!data) return (ZString)""; if (data.capacity == data.len) { - libc::printf("feofk\n"); str.reserve(1); data.chars[data.len] = 0; } @@ -59,24 +57,24 @@ fn ZString String.zstr(String* str) return (ZString)&data.chars[0]; } -fn usize String.len(String* this) +fn usize String.len(String this) { - if (!*this) return 0; + if (!this) return 0; return this.data().len; } /** * @require new_size <= this.len() */ -fn void String.chop(String* this, usize new_size) +fn void String.chop(String this, usize new_size) { - if (!*this) return; + if (!this) return; this.data().len = new_size; } -fn char[] String.str(String* str) +fn char[] String.str(String str) { - StringData* data = (StringData*)*str; + StringData* data = (StringData*)str; return data.chars[0..data.len - 1]; } @@ -92,7 +90,7 @@ fn void String.append_utf32(String* str, Char32[] chars) /** * @require index < str.len() **/ -fn void String.set(String* str, usize index, char c) +fn void String.set(String str, usize index, char c) { str.data().chars[index] = c; } @@ -147,7 +145,7 @@ fn void String.append_char32(String* str, Char32 c) fn String String.copy(String* str, Allocator allocator = { null, null }) { - if (!*str) + if (!str) { if (allocator.function) return new_with_capacity(0, allocator); return (String)null; @@ -175,7 +173,7 @@ fn ZString String.copy_zstr(String* str, Allocator allocator = { null, null }) return (ZString)zstr; } -fn bool String.equals(String* str, String other_string) +fn bool String.equals(String str, String other_string) { StringData *str1 = str.data(); StringData *str2 = other_string.data(); @@ -200,7 +198,7 @@ fn void String.destroy(String* str) *str = (String)null; } -fn bool String.less(String* str, String other_string) +fn bool String.less(String str, String other_string) { StringData* str1 = str.data(); StringData* str2 = other_string.data(); @@ -217,7 +215,7 @@ fn bool String.less(String* str, String other_string) return true; } -fn void String.append(String* this, char[] str) +fn void String.append_chars(String* this, char[] str) { usize other_len = str.len; if (!other_len) return; @@ -255,29 +253,50 @@ fn void String.append_char(String* str, char c) data.chars[data.len++] = c; } -macro void String.@append(String &str, value) +macro bool is_pointer_char_array($Type) { - $switch ($typeof(value)): + var $Type2 = $Type; + $if ($Type.typeid.kind != TypeKind.POINTER): + return false; + $elif ($Type.typeid.inner.kind != TypeKind.ARRAY): + return false; + $elif ($Type.typeid.inner.inner != char.typeid): + return false; + $else: + return true; + $endif; +} + +macro void String.append(String* str, value) +{ + var $Type = $typeof(value); + $switch ($Type): $case char: + $case ichar: str.append_char(value); $case String: str.append_string(value); $case char[]: - str.append(value); - $case char32: + str.append_chars(value); + $case Char32: str.append_char32(value); $default: - $assert("Unsupported type for appending"); + $if ($Type.typeid.kind == TypeKind.SIGNED_INT || $Type.typeid.kind == TypeKind.UNSIGNED_INT): + str.append_char32((Char32)value); + $elif (is_pointer_char_array($Type)): + str.append_chars(value); + $else: + $assert("Unsupported type for appending"); + $endif; $endswitch; } -private fn StringData* String.data(String* str) @inline +private fn StringData* String.data(String str) @inline { - return (StringData*)*str; + return (StringData*)str; } - private fn void String.reserve(String* str, usize addition) { StringData* data = str.data(); diff --git a/lib/std/math.matrix.c3 b/lib/std/math.matrix.c3 index cd3dacb25..8ef11f8b6 100644 --- a/lib/std/math.matrix.c3 +++ b/lib/std/math.matrix.c3 @@ -1,4 +1,4 @@ -module std::math; +module std::math::matrix; fault MatrixError { MATRIX_INVERSE_DOESNT_EXIST, @@ -360,7 +360,7 @@ fn Matrix4x4 Matrix4x4.scale(Matrix4x4* m, float[<3>] v) { } -fn Matrix4x4 Matrix4x4.ortho(float left, float right, float top, float bottom, float near, float far) { +fn Matrix4x4 ortho(float left, float right, float top, float bottom, float near, float far) { float width = right - left; float height = top - bottom; float depth = far - near; @@ -375,7 +375,7 @@ fn Matrix4x4 Matrix4x4.ortho(float left, float right, float top, float bottom, f } // fov in radians -fn Matrix4x4 Matrix4x4.perspective(float fov, float aspect_ratio, float near, float far) { +fn Matrix4x4 perspective(float fov, float aspect_ratio, float near, float far) { float top = ((float)math::sin(fov / 2) / (float)math::cos(fov / 2)) * near; float right = top * aspect_ratio; float depth = far - near; diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index a84739bc6..e9a102014 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -1913,6 +1913,7 @@ void expr_const_set_float(ExprConst *expr, Real d, TypeKind kind); void expr_const_set_bool(ExprConst *expr, bool b); void expr_const_set_null(ExprConst *expr); +bool expr_const_in_range(const ExprConst *left, const ExprConst *right, const ExprConst *right_to); bool expr_const_compare(const ExprConst *left, const ExprConst *right, BinaryOp op); bool expr_const_will_overflow(const ExprConst *expr, TypeKind kind); diff --git a/src/compiler/number.c b/src/compiler/number.c index ad6aeb9ce..5cb201eee 100644 --- a/src/compiler/number.c +++ b/src/compiler/number.c @@ -102,7 +102,8 @@ bool expr_const_compare(const ExprConst *left, const ExprConst *right, BinaryOp is_eq = !strncmp(left->string.chars, right->string.chars, left->string.len); break; case CONST_TYPEID: - return left->typeid == right->typeid; + is_eq = left->typeid == right->typeid; + break; case CONST_ERR: case CONST_ENUM: { @@ -151,6 +152,15 @@ bool expr_const_compare(const ExprConst *left, const ExprConst *right, BinaryOp return (op == BINARYOP_EQ) && is_eq; } +bool expr_const_in_range(const ExprConst *left, const ExprConst *right, const ExprConst *right_to) +{ + if (right == right_to) + { + return expr_const_compare(left, right, BINARYOP_EQ); + } + return expr_const_compare(left, right, BINARYOP_GE) && expr_const_compare(left, right_to, BINARYOP_LE); +} + bool float_const_fits_type(const ExprConst *expr_const, TypeKind kind) { Real hi_limit; diff --git a/src/compiler/parse_stmt.c b/src/compiler/parse_stmt.c index f0b6a39c5..89e40e9e3 100644 --- a/src/compiler/parse_stmt.c +++ b/src/compiler/parse_stmt.c @@ -585,7 +585,7 @@ static inline Ast* parse_ct_else_stmt(ParseContext *c) { Ast *ast = new_ast(AST_CT_ELSE_STMT, c->span); advance_and_verify(c, TOKEN_CT_ELSE); - TRY_CONSUME(TOKEN_COLON, "$else needs a ':', did you forget it?"); + TRY_CONSUME_AFTER(TOKEN_COLON, "$else needs a ':', did you forget it?", poisoned_ast); if (!parse_ct_compound_stmt(c, &ast->ct_else_stmt)) return poisoned_ast; return ast; } @@ -604,11 +604,11 @@ static inline Ast* parse_ct_if_stmt(ParseContext *c, bool is_elif) ASSIGN_EXPR_OR_RET(ast->ct_if_stmt.expr, parse_const_paren_expr(c), poisoned_ast); if (is_elif) { - TRY_CONSUME(TOKEN_COLON, "$elif needs a ':' after the expression, did you forget it?"); + TRY_CONSUME_AFTER(TOKEN_COLON, "$elif needs a ':' after the expression, did you forget it?", poisoned_ast); } else { - TRY_CONSUME(TOKEN_COLON, "$if needs a ':' after the expression, did you forget it?"); + TRY_CONSUME_AFTER(TOKEN_COLON, "$if needs a ':' after the expression, did you forget it?", poisoned_ast); } if (!parse_ct_compound_stmt(c, &ast->ct_if_stmt.then)) return poisoned_ast; diff --git a/src/compiler/parser_internal.h b/src/compiler/parser_internal.h index 32fd46e42..77a479dde 100644 --- a/src/compiler/parser_internal.h +++ b/src/compiler/parser_internal.h @@ -13,6 +13,7 @@ #define TRY_EXPECT_OR_RET(_tok, _message, _type) do { if (!tok_is(c, _tok)) { SEMA_ERROR_HERE(_message); return _type; } } while(0) #define TRY_CONSUME_OR_RET(_tok, _message, _type) do { if (!consume(c, _tok, _message)) return _type; } while(0) #define TRY_CONSUME(_tok, _message) TRY_CONSUME_OR_RET(_tok, _message, poisoned_ast) +#define TRY_CONSUME_AFTER(_tok, _message, _type) do { if (!try_consume(c, _tok)) { sema_error_at_after(c->prev_span, _message); return _type; } } while(0) #define CHECK_EXPR_OR_RET(_expr) do { if (!expr_ok(_expr)) return _expr; } while(0) Decl *parse_top_level_statement(ParseContext *c); diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index a706d1b8d..d5120944b 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -5,6 +5,7 @@ #include "sema_internal.h" +static inline bool sema_is_valid_method_param(SemaContext *context, Decl *param, Type *parent_type); static bool sema_analyse_struct_union(SemaContext *context, Decl *decl); static bool sema_analyse_attributes(SemaContext *context, Decl *decl, Attr** attrs, AttributeDomain domain); static bool sema_analyse_attributes_for_var(SemaContext *context, Decl *decl); @@ -1116,6 +1117,14 @@ static inline bool sema_analyse_method(SemaContext *context, Decl *decl) type_to_error_string(parent_type->type)); return false; } + Decl **params = decl->func_decl.function_signature.params; + if (!vec_size(params)) + { + SEMA_ERROR(decl, "A method must start with a parameter of type %s or %s.", + type_quoted_error_string(parent_type->type), type_quoted_error_string(type_get_ptr(parent_type->type))); + return false; + } + if (!sema_is_valid_method_param(context, params[0], type)) return false; return unit_add_method_like(context->unit, type, decl); } @@ -1755,6 +1764,24 @@ static inline bool sema_analyse_func(SemaContext *context, Decl *decl) return true; } +static inline bool sema_is_valid_method_param(SemaContext *context, Decl *param, Type *parent_type) +{ + assert(parent_type->canonical == parent_type && "Expected already the canonical version."); + Type *param_type = param->type; + + if (!param_type) goto ERROR; + param_type = param_type->canonical; + // 1. Same type ok! + if (param_type == parent_type) return true; + // 2. A pointer is ok! + if (param_type->type_kind == TYPE_POINTER && param_type->pointer == parent_type) return true; + +ERROR: + SEMA_ERROR(param, "The first parameter must be of type %s or %s.", type_quoted_error_string(parent_type), + type_quoted_error_string(type_get_ptr(parent_type))); + return false; +} + static bool sema_analyse_macro_method(SemaContext *context, Decl *decl) { TypeInfo *parent_type_info = type_infoptr(decl->macro_decl.type_parent); @@ -1769,18 +1796,16 @@ static bool sema_analyse_macro_method(SemaContext *context, Decl *decl) } if (!vec_size(decl->macro_decl.parameters)) { - SEMA_ERROR(decl, "Expected at least one parameter - of type &%s.", type_to_error_string(parent_type)); + SEMA_ERROR(decl, "Expected at least one parameter - of type %s.", type_to_error_string(parent_type)); return false; } Decl *first_param = decl->macro_decl.parameters[0]; - if (!first_param->type || first_param->type->canonical != parent_type->canonical) + + if (!sema_is_valid_method_param(context, first_param, parent_type->canonical)) return false; + + if (first_param->var.kind != VARDECL_PARAM_REF && first_param->var.kind != VARDECL_PARAM) { - SEMA_ERROR(first_param, "The first parameter must be &%s.", type_to_error_string(parent_type)); - return false; - } - if (first_param->var.kind != VARDECL_PARAM_REF) - { - SEMA_ERROR(first_param, "The first parameter must be a ref (&) parameter."); + SEMA_ERROR(first_param, "The first parameter must be a regular or ref (&) type."); return false; } return unit_add_method_like(context->unit, parent_type, decl); diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 156cb8972..41dea472a 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -2488,10 +2488,24 @@ static inline bool sema_expr_analyse_call(SemaContext *context, Expr *expr) break; case EXPR_ACCESS: decl = func_expr->access_expr.ref; - if (decl->decl_kind == DECL_FUNC || decl->decl_kind == DECL_MACRO) + switch (decl->decl_kind) { - if (decl->decl_kind != DECL_MACRO) expr_insert_addr(func_expr->access_expr.parent); - struct_var = func_expr->access_expr.parent; + case DECL_MACRO: + if (decl->macro_decl.parameters[0]->type->type_kind == TYPE_POINTER) + { + expr_insert_addr(func_expr->access_expr.parent); + } + struct_var = func_expr->access_expr.parent; + break; + case DECL_FUNC: + if (decl->func_decl.function_signature.params[0]->type->type_kind == TYPE_POINTER) + { + expr_insert_addr(func_expr->access_expr.parent); + } + struct_var = func_expr->access_expr.parent; + break; + default: + break; } break; case EXPR_TYPEINFO: diff --git a/src/compiler/sema_passes.c b/src/compiler/sema_passes.c index 435723ac7..a9501a28a 100644 --- a/src/compiler/sema_passes.c +++ b/src/compiler/sema_passes.c @@ -242,16 +242,14 @@ static inline bool sema_analyse_top_level_switch(SemaContext *context, Decl *ct_ ExprConst *other_const = &other_case->ct_case_decl.expr->const_expr; ExprConst *other_const_to = other_case->ct_case_decl.to_expr ? &other_case->ct_case_decl.to_expr->const_expr : other_const; - if (expr_const_compare(const_expr, other_const_to, BINARYOP_LE) && - expr_const_compare(const_to_expr, other_const, BINARYOP_GE)) + if (expr_const_in_range(const_expr, other_const, other_const_to)) { SEMA_ERROR(kase, "'%s' appears more than once.", expr_const_to_error_string(const_expr)); SEMA_PREV(cases[j]->ct_case_decl.expr, "The previous $case was here."); return false; } } - if (expr_const_compare(switch_expr_const, const_expr, BINARYOP_GE) && - expr_const_compare(switch_expr_const, const_to_expr, BINARYOP_LE)) + if (expr_const_in_range(switch_expr_const, const_expr, const_to_expr)) { matched_case = (int)i; } diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 7fb1ae248..de6babd18 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -1531,8 +1531,7 @@ static bool sema_analyse_nextcase_stmt(SemaContext *context, Ast *statement) } ExprConst *const_expr = &case_stmt->case_stmt.expr->const_expr; ExprConst *to_const_expr = case_stmt->case_stmt.to_expr ? &case_stmt->case_stmt.to_expr->const_expr : const_expr; - if (expr_const_compare(&target->const_expr, const_expr, BINARYOP_GE) && - expr_const_compare(&target->const_expr, to_const_expr, BINARYOP_LE)) + if (expr_const_in_range(&target->const_expr, const_expr, to_const_expr)) { statement->nextcase_stmt.case_switch_stmt = astid(case_stmt); return true; @@ -1753,7 +1752,7 @@ static inline bool sema_check_value_case(SemaContext *context, Type *switch_type if (other->ast_kind != AST_CASE_STMT) continue; ExprConst *other_const = &other->case_stmt.expr->const_expr; ExprConst *other_to_const = other->case_stmt.to_expr ? &other->case_stmt.to_expr->const_expr : other_const; - if (expr_const_compare(const_expr, other_to_const, BINARYOP_LE) && expr_const_compare(to_const_expr, other_const, BINARYOP_GE)) + if (expr_const_in_range(const_expr, other_const, other_to_const)) { SEMA_ERROR(case_stmt, "The same case value appears more than once."); SEMA_PREV(other, "Here is the previous use of that value."); @@ -1937,14 +1936,14 @@ static bool sema_analyse_ct_switch_body(SemaContext *context, Ast *statement) if (other_stmt->ast_kind == AST_DEFAULT_STMT) continue; ExprConst *other_const = &other_stmt->case_stmt.expr->const_expr; ExprConst *other_const_to = other_stmt->case_stmt.to_expr ? &other_stmt->case_stmt.to_expr->const_expr : other_const; - if (expr_const_compare(const_expr, other_const_to, BINARYOP_LE) && expr_const_compare(const_to_expr, other_const, BINARYOP_GE)) + if (expr_const_in_range(const_expr, other_const, other_const_to)) { SEMA_ERROR(stmt, "'%s' appears more than once.", expr_const_to_error_string(const_expr)); SEMA_PREV(cases[j]->case_stmt.expr, "The previous $case was here."); return false; } } - if (expr_const_compare(switch_expr_const, const_expr, BINARYOP_GE) && expr_const_compare(switch_expr_const, const_to_expr, BINARYOP_LE)) + if (expr_const_in_range(switch_expr_const, const_expr, const_to_expr)) { matched_case = (int) i; } diff --git a/src/version.h b/src/version.h index 2a35980c6..23edb169b 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.2.21" \ No newline at end of file +#define COMPILER_VERSION "0.2.22" \ No newline at end of file