From 398e19d727cee274957323fb959c9f38a6188a03 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Fri, 5 Aug 2022 00:42:22 +0200 Subject: [PATCH] Corrected default alignment on temp alloc. Added str_index_of. Added simple getline. Added a simple calculator. Allow [1..] to create a zero length slice. Added some initial macro contracts. Fix accessing enum functions. Support for @checked. Bump to 0.3.4 --- lib/std/core/builtin_comparison.c3 | 10 ++++---- lib/std/core/mem.c3 | 6 ++--- lib/std/core/str.c3 | 26 +++++++++++++++++++ lib/std/core/types.c3 | 2 +- lib/std/io.c3 | 16 ++++++++++++ lib/std/libc.c3 | 1 + resources/examples/plus_minus.c3 | 41 ++++++++++++++++++++++++++++++ src/compiler/compiler_internal.h | 3 +-- src/compiler/diagnostics.c | 11 ++------ src/compiler/llvm_codegen_expr.c | 2 +- src/compiler/parse_global.c | 5 ++-- src/compiler/sema_expr.c | 10 ++++++-- src/compiler/sema_internal.h | 3 ++- src/compiler/sema_stmts.c | 26 +++++++++++++------ src/version.h | 2 +- 15 files changed, 129 insertions(+), 35 deletions(-) create mode 100644 resources/examples/plus_minus.c3 diff --git a/lib/std/core/builtin_comparison.c3 b/lib/std/core/builtin_comparison.c3 index 0ea6e9824..079b1d937 100644 --- a/lib/std/core/builtin_comparison.c3 +++ b/lib/std/core/builtin_comparison.c3 @@ -4,7 +4,7 @@ module std::core::builtin; /** - * @require is_comparable_value(a) && is_comparable_value(b) + * @require types::is_comparable_value(a) && types::is_comparable_value(b) **/ macro less(a, b) @builtin { @@ -18,7 +18,7 @@ macro less(a, b) @builtin } /** - * @require is_comparable_value(a) && is_comparable_value(b) + * @require types::is_comparable_value(a) && types::is_comparable_value(b) **/ macro less_eq(a, b) @builtin { @@ -32,7 +32,7 @@ macro less_eq(a, b) @builtin } /** - * @require is_comparable_value(a) && is_comparable_value(b) + * @require types::is_comparable_value(a) && types::is_comparable_value(b) **/ macro greater(a, b) @builtin { @@ -46,7 +46,7 @@ macro greater(a, b) @builtin } /** - * @require is_comparable_value(a) && is_comparable_value(b) + * @require types::is_comparable_value(a) && types::is_comparable_value(b) **/ macro greater_eq(a, b) @builtin { @@ -60,7 +60,7 @@ macro greater_eq(a, b) @builtin } /** - * @require is_equatable_value(a) && is_equatable_value(b) `values must be equatable` + * @require types::is_equatable_value(a) && types::is_equatable_value(b) `values must be equatable` **/ macro bool equals(a, b) @builtin { diff --git a/lib/std/core/mem.c3 b/lib/std/core/mem.c3 index 3b683ce82..bd352d73f 100644 --- a/lib/std/core/mem.c3 +++ b/lib/std/core/mem.c3 @@ -166,17 +166,17 @@ macro talloc($Type) @builtin return temp_allocator().alloc_aligned($Type.sizeof, $alignof($Type))!!; } -fn void* tmalloc(usize size, usize alignment = 0) @builtin @inline +fn void* tmalloc(usize size, usize alignment = allocator::DEFAULT_MEM_ALIGNMENT) @builtin @inline { return temp_allocator().alloc_aligned(size, alignment)!!; } -fn void* tcalloc(usize size, usize alignment = 0) @builtin @inline +fn void* tcalloc(usize size, usize alignment = allocator::DEFAULT_MEM_ALIGNMENT) @builtin @inline { return temp_allocator().calloc_aligned(size, alignment)!!; } -fn void* trealloc(void* ptr, usize size, usize alignment = 0) @builtin @inline +fn void* trealloc(void* ptr, usize size, usize alignment = allocator::DEFAULT_MEM_ALIGNMENT) @builtin @inline { return temp_allocator().realloc_aligned(ptr, size, alignment)!!; } diff --git a/lib/std/core/str.c3 b/lib/std/core/str.c3 index 257717110..374233df7 100644 --- a/lib/std/core/str.c3 +++ b/lib/std/core/str.c3 @@ -29,6 +29,32 @@ fn String join(char[][] s, char[] joiner) return res; } +fn usize! str_index_of(char[] s, char[] needle) +{ + usize match = 0; + usize needed = needle.len; + if (!needed) return SearchResult.MISSING!; + usize index_start = 0; + char search = needle[0]; + foreach (usize i, char c : s) + { + if (c == search) + { + if (!match) index_start = i; + match++; + if (match == needed) return i; + search = needle[match]; + continue; + } + if (match) + { + match = 0; + search = needle[0]; + } + } + return SearchResult.MISSING!; +} + fn ZString copy_zstring(char[] s) { usize len = s.len; diff --git a/lib/std/core/types.c3 b/lib/std/core/types.c3 index 0eca10561..20cdb0527 100644 --- a/lib/std/core/types.c3 +++ b/lib/std/core/types.c3 @@ -8,7 +8,7 @@ fault ConversionResult VALUE_OUT_OF_UNSIGNED_RANGE, } /** - * @require type.kind == SIGNED_INT || type.kind == UNSIGNED_INT || type.kind == ENUM, "Argument was not an integer" + * @require $Type.kind.is_int() || $Type.kind == TypeKind.ENUM "Argument was not an integer" **/ macro variant_to_int(variant v, $Type) { diff --git a/lib/std/io.c3 b/lib/std/io.c3 index eaad62423..c1b714adb 100644 --- a/lib/std/io.c3 +++ b/lib/std/io.c3 @@ -169,6 +169,22 @@ fn usize! File.println(File* file, char[] string) return len + 1; } +/** + * @param [&in] file + * @require file.file `File must be initialized` + */ +fn String File.getline(File* file, Allocator* allocator = mem::current_allocator()) +{ + String s = string::new_with_capacity(120, allocator); + while (!file.eof()) + { + int c = libc::fgetc(file.file); + if (c == '\n') break; + s.append_char((char)c); + } + return s; +} + /** * @param [&in] file * @require file.file `File must be initialized` diff --git a/lib/std/libc.c3 b/lib/std/libc.c3 index 9ca089bc6..3ecfe1778 100644 --- a/lib/std/libc.c3 +++ b/lib/std/libc.c3 @@ -186,6 +186,7 @@ extern fn int putchar(int c); extern fn int puts(char* str); extern fn int ungetc(int c, CFile stream); extern fn void perror(char* str); +extern fn isize getline(char** linep, usize* linecapp, CFile stream); // vsprintf vprintf not supported diff --git a/resources/examples/plus_minus.c3 b/resources/examples/plus_minus.c3 new file mode 100644 index 000000000..58b5431e9 --- /dev/null +++ b/resources/examples/plus_minus.c3 @@ -0,0 +1,41 @@ +module test; +import std::io; +import libc; + +fault TokenResult +{ + NO_MORE_TOKENS +} + +fn char[]! read_next(char[]* remaining) +{ + // Skip spaces. + while (remaining.len > 0 && (*remaining)[0] == ' ') *remaining = (*remaining)[1..]; + char* ptr_start = remaining.ptr; + usize len = 0; + while (remaining.len > 0 && (*remaining)[0] != ' ') + { + len++; + *remaining = (*remaining)[1..]; + } + if (!len) return TokenResult.NO_MORE_TOKENS!; + return ptr_start[:len]; +} + +fn void main(char[][] args) +{ + String s = io::stdin().getline(); + defer s.destroy(); + char[] numbers = s.str(); + int val = 0; + bool add = true; + while (try char[] token = read_next(&numbers)) + { + int i = libc::atoi(token.ptr); + val = add ? val + i : val - i; + char[]! op = read_next(&numbers); + if (catch op) break; + add = op[0] == '+'; + } + io::printfln("%d", val); +} \ No newline at end of file diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index b035499e5..6077200d1 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -1482,6 +1482,7 @@ typedef struct bool in_test_mode : 1; unsigned errors_found; unsigned warnings_found; + bool suppress_errors; Decl ***locals_list; HTable compiler_defines; Module std_module; @@ -1907,8 +1908,6 @@ static inline Decl *decl_flatten(Decl *decl) // --- Diag functions -void diag_verror_range(SourceSpan location, const char *message, va_list args); - #define EXPR_NEW_EXPR(kind_, expr_) expr_new(kind_, (expr_)->span) #define EXPR_NEW_TOKEN(kind_) expr_new(kind_, c->span) diff --git a/src/compiler/diagnostics.c b/src/compiler/diagnostics.c index 60fbb30c3..aa04c9e24 100644 --- a/src/compiler/diagnostics.c +++ b/src/compiler/diagnostics.c @@ -152,17 +152,9 @@ static void vprint_error(SourceSpan location, const char *message, va_list args) } -void diag_verror_range(SourceSpan location, const char *message, va_list args) -{ - if (global_context.in_panic_mode) return; - global_context.in_panic_mode = true; - vprint_error(location, message, args); - global_context.errors_found++; -} - - void sema_verror_range(SourceSpan location, const char *message, va_list args) { + if (global_context.suppress_errors) return; vprint_error(location, message, args); global_context.errors_found++; } @@ -208,6 +200,7 @@ void sema_error_prev_at(SourceSpan loc, const char *message, ...) void sema_error(ParseContext *context, const char *message, ...) { + if (global_context.suppress_errors) return; global_context.errors_found++; File *file = context->unit->file; va_list list; diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index a4b8b382c..3c80285f4 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -2342,7 +2342,7 @@ static void llvm_emit_slice_values(GenContext *c, Expr *slice, BEValue *parent_r { assert(len.value); BEValue exceeds_size; - llvm_emit_int_comparison(c, &exceeds_size, &start_index, &len, BINARYOP_GE); + llvm_emit_int_comparison(c, &exceeds_size, &start_index, &len, BINARYOP_GT); llvm_emit_panic_if_true(c, &exceeds_size, "Index exceeds array length.", slice->span); } diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index e744f8735..b5774ba9e 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -1778,7 +1778,7 @@ static inline bool parse_func_macro_header(ParseContext *c, bool is_macro, /** * macro ::= macro_header '(' macro_params ')' compound_statement */ -static inline Decl *parse_macro_declaration(ParseContext *c, Visibility visibility) +static inline Decl *parse_macro_declaration(ParseContext *c, Visibility visibility, AstId docs) { DeclKind kind = try_consume(c, TOKEN_MACRO) ? DECL_MACRO : DECL_GENERIC; if (kind == DECL_GENERIC) advance_and_verify(c, TOKEN_GENERIC); @@ -1786,6 +1786,7 @@ static inline Decl *parse_macro_declaration(ParseContext *c, Visibility visibili Decl *decl = decl_calloc(); decl->decl_kind = kind; decl->visibility = visibility; + decl->macro_decl.docs = docs; TypeInfoId *rtype_ref = &decl->macro_decl.rtype; TypeInfoId *method_type_ref = &decl->macro_decl.type_parent; if (!parse_func_macro_header(c, true, rtype_ref, method_type_ref, &decl->name, &decl->span)) return poisoned_decl; @@ -2426,7 +2427,7 @@ Decl *parse_top_level_statement(ParseContext *c) case TOKEN_GENERIC: case TOKEN_MACRO: { - ASSIGN_DECL_OR_RET(decl, parse_macro_declaration(c, visibility), poisoned_decl); + ASSIGN_DECL_OR_RET(decl, parse_macro_declaration(c, visibility, docs), poisoned_decl); break; } case TOKEN_ENUM: diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 2330acd56..9de58bf3f 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -1420,7 +1420,7 @@ FAIL_MISSING: static inline bool sema_expr_analyse_call_invocation(SemaContext *context, Expr *call, CalledDecl callee, bool *failable) { - // 1. Check body arguments. + // 1. Check body arguments (for macro calls, or possibly broken ) if (!sema_check_invalid_body_arguments(context, call, &callee)) return false; // 2. Pick out all the arguments and parameters. @@ -1945,6 +1945,12 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s if (!sema_add_local(¯o_context, param)) goto EXIT_FAIL; } + AstId assert_first = 0; + AstId* next = &assert_first; + + if (!sema_analyse_contracts(¯o_context, decl->macro_decl.docs, &next)) return false; + sema_append_contract_asserts(assert_first, body); + if (!sema_analyse_statement(¯o_context, body)) goto EXIT_FAIL; bool is_no_return = decl->macro_decl.attr_noreturn; @@ -3786,7 +3792,7 @@ CHECK_DEEPER: member = sema_resolve_symbol_in_current_dynamic_scope(context, kw); SCOPE_END; - if (member && decl_is_enum_kind(decl) && parent->expr_kind == EXPR_CONST) + if (member && decl_is_enum_kind(decl) && member->decl_kind == DECL_VAR && parent->expr_kind == EXPR_CONST) { assert(parent->const_expr.const_kind == CONST_ENUM); Expr *copy_init = expr_macro_copy(current_parent->const_expr.enum_val->enum_constant.args[member->var.index]); diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h index 4ccf4c2d8..b4663d84f 100644 --- a/src/compiler/sema_internal.h +++ b/src/compiler/sema_internal.h @@ -58,7 +58,8 @@ bool splitpathref(const char *string, ArraySize len, Path **path_ref, const char bool expr_is_ltype(Expr *expr); bool sema_expr_check_assign(SemaContext *c, Expr *expr); - +bool sema_analyse_contracts(SemaContext *context, AstId doc, AstId **asserts); +void sema_append_contract_asserts(AstId assert_first, Ast* compound_stmt); void sema_context_init(SemaContext *context, CompilationUnit *unit); void sema_context_destroy(SemaContext *context); Decl **global_context_acquire_locals_list(void); diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index fa41e51d3..c71ffd0d7 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -2446,15 +2446,20 @@ static bool sema_analyse_checked(SemaContext *context, Ast *directive, AstId **a { Expr *declexpr = directive->doc_stmt.contract.decl_exprs; bool success = true; + bool suppress_error = global_context.suppress_errors; + global_context.suppress_errors = true; SCOPE_START VECEACH(declexpr->cond_expr, j) { Expr *expr = declexpr->cond_expr[j]; + + const char *comment = directive->doc_stmt.contract.comment; + global_context.suppress_errors = comment != NULL; if (!sema_analyse_cond_expr(context, expr)) { - const char *comment = directive->doc_stmt.contract.comment; if (comment) { + global_context.suppress_errors = false; SEMA_ERROR(expr, comment); } success = false; @@ -2463,10 +2468,20 @@ static bool sema_analyse_checked(SemaContext *context, Ast *directive, AstId **a } END: SCOPE_END; + global_context.suppress_errors = suppress_error; return success; } -static bool sema_analyse_contracts(SemaContext *context, AstId doc, AstId **asserts) +void sema_append_contract_asserts(AstId assert_first, Ast* compound_stmt) +{ + assert(compound_stmt->ast_kind == AST_COMPOUND_STMT); + if (!assert_first) return; + Ast *ast = new_ast(AST_COMPOUND_STMT, compound_stmt->span); + ast->compound_stmt.first_stmt = assert_first; + ast_prepend(&compound_stmt->compound_stmt.first_stmt, ast); +} + +bool sema_analyse_contracts(SemaContext *context, AstId doc, AstId **asserts) { while (doc) { @@ -2548,12 +2563,7 @@ bool sema_analyse_function_body(SemaContext *context, Decl *func) } else { - if (assert_first) - { - Ast *ast = new_ast(AST_COMPOUND_STMT, body->span); - ast->compound_stmt.first_stmt = assert_first; - ast_prepend(&body->compound_stmt.first_stmt, ast); - } + sema_append_contract_asserts(assert_first, body); Type *canonical_rtype = type_no_fail(prototype->rtype)->canonical; // Insert an implicit return if (canonical_rtype == type_void) diff --git a/src/version.h b/src/version.h index 898827b36..35b182c8b 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.3.3" \ No newline at end of file +#define COMPILER_VERSION "0.3.4" \ No newline at end of file