From e91f6e268efa0f45d9d192fc931f48b86cc5c7cf Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Fri, 8 Dec 2023 19:25:03 +0100 Subject: [PATCH] 0.5.2: Allow trailing comma in calls and parameter declarations #1092. Fixes issue where single character filenames like 'a.c3' would be rejected. Improve error messages for incorrect user defined foreach. Fix bug with generics in generics. Fix to error with modified vector parameters. Crash with lhs vector inference. Fixes to priority queue. --- lib/std/collections/priorityqueue.c3 | 43 ++++++----- lib/std/core/array.c3 | 77 +++++++++++++++++++ lib/std/core/string.c3 | 48 ++++++------ releasenotes.md | 20 ++++- resources/grammar/grammar.y | 37 +++++---- src/compiler/ast.c | 6 ++ src/compiler/compiler.c | 5 +- src/compiler/compiler_internal.h | 2 + src/compiler/parse_expr.c | 17 +--- src/compiler/parse_global.c | 10 +-- src/compiler/parser_internal.h | 3 +- src/compiler/sema_decls.c | 24 ++++-- src/compiler/sema_expr.c | 19 +++-- src/compiler/sema_internal.h | 2 + src/compiler/sema_stmts.c | 20 ++++- src/compiler/semantic_analyser.c | 9 +++ src/utils/file_utils.c | 1 + src/version.h | 2 +- test/test_suite/contracts/in_array.c3 | 12 +++ .../functions/param_with_comma_at_end.c3 | 2 +- test/test_suite/vector/vector_param.c3t | 34 ++++++++ test/unit/stdlib/core/string.c3 | 6 ++ 22 files changed, 294 insertions(+), 105 deletions(-) create mode 100644 test/test_suite/contracts/in_array.c3 create mode 100644 test/test_suite/vector/vector_param.c3t diff --git a/lib/std/collections/priorityqueue.c3 b/lib/std/collections/priorityqueue.c3 index a6410a119..40856a28b 100644 --- a/lib/std/collections/priorityqueue.c3 +++ b/lib/std/collections/priorityqueue.c3 @@ -74,29 +74,32 @@ fn Type! PrivatePriorityQueue.pop(&self) usz i = 0; usz len = self.heap.len(); if (!len) return IteratorResult.NO_MORE_ELEMENT?; - usz newCount = len - 1; - self.heap.swap(0, newCount); - while ((2 * i + 1) < newCount) + usz new_count = len - 1; + self.heap.swap(0, new_count); + while OUTER: ((2 * i + 1) < new_count) { usz j = 2 * i + 1; - Type itemj = self.heap[j]; - if ((j + 1) < newCount) - { - Type nextj = self.heap[j + 1]; - $if MAX: - bool ok = greater(nextj, itemj); - $else - bool ok = less(nextj, itemj); - $endif - if (ok) j++; - } + Type left = self.heap[j]; Type item = self.heap[i]; - $if MAX: - bool ok = less(item, itemj); - $else - bool ok = greater(item, itemj); - $endif - if (!ok) break; + switch + { + case j + 1 < new_count: + Type right = self.heap[j + 1]; + $if MAX: + if (!greater(right, left)) nextcase; + if (!greater(right, item)) break OUTER; + $else + if (!greater(left, right)) nextcase; + if (!greater(item, right)) break OUTER; + $endif + j++; + default: + $if MAX: + if (!greater(left, item)) break OUTER; + $else + if (!greater(item, left)) break OUTER; + $endif + } self.heap.swap(i, j); i = j; } diff --git a/lib/std/core/array.c3 b/lib/std/core/array.c3 index c4adcf67e..6f1021e5b 100644 --- a/lib/std/core/array.c3 +++ b/lib/std/core/array.c3 @@ -1,4 +1,5 @@ module std::core::array; +import std::core::array::slice; /** * @param [in] array @@ -15,6 +16,19 @@ macro index_of(array, element) return SearchResult.MISSING?; } +/** + * @require @typekind(array) == VECTOR || @typekind(array) == ARRAY + * @require @typekind(array[0]) == VECTOR || @typekind(array[0]) == ARRAY + **/ +macro slice2d(array, x = 0, xlen = 0, y = 0, ylen = 0) +{ + if (xlen < 1) xlen = $typeof(array[0]).len + xlen; + if (ylen < 1) ylen = $typeof(array).len + ylen; + var $ElementType = $typeof(array[0][0]); + return Slice2d(<$ElementType>) { ($ElementType*)&array, $typeof(array[0]).len, y, ylen, x, xlen }; +} + + /** * @param [in] array * @param [in] element @@ -68,3 +82,66 @@ macro concat_new(arr1, arr2, Allocator* allocator = mem::heap()) * @ensure result.len == arr1.len + arr2.len **/ macro tconcat(arr1, arr2) => concat(arr1, arr2, mem::temp()); + +module std::core::array::slice(); + +struct Slice2d +{ + Type* ptr; + usz inner_len; + usz ystart; + usz ylen; + usz xstart; + usz xlen; +} + +fn usz Slice2d.len(&self) @operator(len) +{ + return self.ylen; +} + +fn usz Slice2d.count(&self) +{ + return self.ylen * self.xlen; +} + +macro void Slice2d.@each(&self; @body(usz[<2>], Type)) +{ + foreach (y, line : *self) + { + foreach (x, val : line) + { + @body({ x, y }, val); + } + } +} + +macro void Slice2d.@each_ref(&self; @body(usz[<2>], Type*)) +{ + foreach (y, line : *self) + { + foreach (x, &val : line) + { + @body({ x, y }, val); + } + } +} + +/** + * @require idy >= 0 && idy < self.ylen + **/ +macro Type[] Slice2d.get(self, usz idy) @operator([]) +{ + return (self.ptr + self.inner_len * (idy + self.ystart))[self.xstart:self.xlen]; +} + +/** + * @require y >= 0 && y < self.ylen + * @require x >= 0 && x < self.xlen + **/ +fn Slice2d Slice2d.slice(&self, isz x = 0, isz xlen = 0, isz y = 0, isz ylen = 0) +{ + if (xlen < 1) xlen = self.xlen + xlen; + if (ylen < 1) ylen = self.ylen + ylen; + return { self.ptr, self.inner_len, y + self.ystart, ylen, x + self.xstart, xlen }; +} diff --git a/lib/std/core/string.c3 b/lib/std/core/string.c3 index 33130f9c2..2525b1013 100644 --- a/lib/std/core/string.c3 +++ b/lib/std/core/string.c3 @@ -495,7 +495,11 @@ fn usz String.utf8_codepoints(s) return len; } -macro String.to_integer(string, $Type) + +/** + * @require (base <= 10 && base > 1) || base == 16 : "Unsupported base" + **/ +macro String.to_integer(string, $Type, int base = 10) { usz len = string.len; usz index = 0; @@ -515,8 +519,8 @@ macro String.to_integer(string, $Type) break; } if (len == index) return NumberConversion.MALFORMED_INTEGER?; - $Type base = 10; - if (string[index] == '0') + $Type base_used = ($Type)base; + if (string[index] == '0' && base == 10) { index++; if (index == len) return ($Type)0; @@ -524,15 +528,15 @@ macro String.to_integer(string, $Type) { case 'x': case 'X': - base = 16; + base_used = 16; index++; case 'b': case 'B': - base = 2; + base_used = 2; index++; case 'o': case 'O': - base = 8; + base_used = 8; index++; default: break; @@ -544,21 +548,21 @@ macro String.to_integer(string, $Type) { char c = {| char ch = string[index++]; - if (base != 16 || ch < 'A') return (char)(ch - '0'); - if (ch <= 'F') return (char)(ch - 'A'); + if (base_used != 16 || ch < 'A') return (char)(ch - '0'); + if (ch <= 'F') return (char)(ch - 'A' + 10); if (ch < 'a') return NumberConversion.MALFORMED_INTEGER?; if (ch > 'f') return NumberConversion.MALFORMED_INTEGER?; - return (char)(ch - 'a'); + return (char)(ch - 'a' + 10); |}!; - if (c >= base) return NumberConversion.MALFORMED_INTEGER?; + if (c >= base_used) return NumberConversion.MALFORMED_INTEGER?; value = {| if (is_negative) { - $Type new_value = value * base - c; + $Type new_value = value * base_used - c; if (new_value > value) return NumberConversion.INTEGER_OVERFLOW?; return new_value; } - $Type new_value = value * base + c; + $Type new_value = value * base_used + c; if (new_value < value) return NumberConversion.INTEGER_OVERFLOW?; return new_value; |}!; @@ -566,17 +570,17 @@ macro String.to_integer(string, $Type) return value; } -fn int128! String.to_int128(s) => s.to_integer(int128); -fn long! String.to_long(s) => s.to_integer(long); -fn int! String.to_int(s) => s.to_integer(int); -fn short! String.to_short(s) => s.to_integer(short); -fn ichar! String.to_ichar(s) => s.to_integer(ichar); +fn int128! String.to_int128(s, int base = 10) => s.to_integer(int128, base); +fn long! String.to_long(s, int base = 10) => s.to_integer(long, base); +fn int! String.to_int(s, int base = 10) => s.to_integer(int, base); +fn short! String.to_short(s, int base = 10) => s.to_integer(short, base); +fn ichar! String.to_ichar(s, int base = 10) => s.to_integer(ichar, base); -fn uint128! String.to_uint128(s) => s.to_integer(uint128); -fn ulong! String.to_ulong(s) => s.to_integer(ulong); -fn uint! String.to_uint(s) => s.to_integer(uint); -fn ushort! String.to_ushort(s) => s.to_integer(ushort); -fn char! String.to_uchar(s) => s.to_integer(char); +fn uint128! String.to_uint128(s, int base = 10) => s.to_integer(uint128, base); +fn ulong! String.to_ulong(s, int base = 10) => s.to_integer(ulong, base); +fn uint! String.to_uint(s, int base = 10) => s.to_integer(uint, base); +fn ushort! String.to_ushort(s, int base = 10) => s.to_integer(ushort, base); +fn char! String.to_uchar(s, int base = 10) => s.to_integer(char, base); fn double! String.to_double(s) => s.to_real(double); fn float! String.to_float(s) => s.to_real(float); diff --git a/releasenotes.md b/releasenotes.md index 99a0a882f..781f5f2e4 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -1,5 +1,23 @@ # C3C Release Notes +## 0.5.2 Change list + +### Changes / improvements +- Allow trailing comma in calls and parameters #1092. + +### Fixes +- Fixes issue where single character filenames like 'a.c3' would be rejected. +- Better errors when index type doesn't match len() when doing user defined foreach. +- Fixes to `to_int` for hexadecimal strings. +- Fixed issue when using a generic type from a generic type. +- Bug with vector parameters when the size > 2 and modified. +- Missing error on assigning to in-parameters through subscripting. +- Inference of a vector on the lhs of a binary expression would cause a crash. +- Fixes to PriorityQueue + +### Stdlib changes +- Allow `to_int` family functions take a base, parsing base 2-10 and 16. + ## 0.5.1 Change list ### Changes / improvements @@ -7,7 +25,7 @@ - Do not link with debug libraries unless using static libraries. - Add 'print-linking' build option. - System linker may be used even if the target arch is different from current. -- Slice -> array/vector works for constant slice lenghts. +- Slice -> array/vector works for constant slice lengths. ### Fixes - On Aarch64 use the correct frame pointer type. diff --git a/resources/grammar/grammar.y b/resources/grammar/grammar.y index a9a7a52c9..b8119f2e0 100644 --- a/resources/grammar/grammar.y +++ b/resources/grammar/grammar.y @@ -443,22 +443,20 @@ arg : param_path '=' expr arg_list : arg | arg_list ',' arg + | arg_list ',' + ; + +opt_arg_list + : arg_list + | empty ; call_arg_list - : arg_list - | arg_list ';' - | arg_list ';' parameters - | ';' - | ';' parameters - | empty + : opt_arg_list + | opt_arg_list ';' + | opt_arg_list ';' parameters ; -opt_arg_list_trailing - : arg_list - | arg_list ',' - | empty - ; interfaces : TYPE_IDENT opt_generic_parameters @@ -483,7 +481,6 @@ enum_list enum_constant : CONST_IDENT opt_attributes | CONST_IDENT '(' arg_list ')' opt_attributes - | CONST_IDENT '(' arg_list ',' ')' opt_attributes ; identifier_list @@ -573,7 +570,7 @@ var_decl ; initializer_list - : '{' opt_arg_list_trailing '}' + : '{' opt_arg_list '}' ; ct_case_stmt @@ -1070,11 +1067,15 @@ fn_parameter_list | '(' ')' ; +parameter_default + : parameter + | parameter '=' expr + ; + parameters - : parameter '=' expr - | parameter - | parameters ',' parameter - | parameters ',' parameter '=' expr + : parameter_default + | parameters ',' parameter_default + | parameters ',' ; parameter @@ -1178,8 +1179,6 @@ opt_generic_parameters | empty ; - - define_ident : IDENT '=' path_ident opt_generic_parameters | CONST_IDENT '=' path_const opt_generic_parameters diff --git a/src/compiler/ast.c b/src/compiler/ast.c index fe287e673..1348215b1 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -89,6 +89,12 @@ Decl *decl_new_with_type(const char *name, SourceSpan loc, DeclKind decl_type) return decl; } +const char *decl_safe_name(Decl *decl) +{ + if (!decl) return ""; + if (decl->name) return decl->name; + return decl_to_name(decl); +} const char *decl_to_name(Decl *decl) { const char *name = decl_to_a_name(decl); diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index 2e124d3cc..5a149493c 100644 --- a/src/compiler/compiler.c +++ b/src/compiler/compiler.c @@ -604,8 +604,7 @@ static const char **target_expand_source_names(const char** dirs, const char **s } goto INVALID_NAME; } - if (name_len < 4) goto INVALID_NAME; - if (name_len < 5 || !file_has_suffix_in_list(name, name_len, suffix_list, suffix_count)) goto INVALID_NAME; + if (!file_has_suffix_in_list(name, name_len, suffix_list, suffix_count)) goto INVALID_NAME; vec_add(files, name); continue; INVALID_NAME: @@ -615,7 +614,7 @@ static const char **target_expand_source_names(const char** dirs, const char **s continue; } if (!error_on_mismatch) continue; - error_exit("File names must end with %s or they cannot be compiled: '%s' is invalid.", suffix_list[0], name); + error_exit("File names must be a non-empty name followed by %s or they cannot be compiled: '%s' is invalid.", suffix_list[0], name); } return files; } diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 9b1108024..4fc40af1b 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -1508,6 +1508,7 @@ typedef struct Module_ bool is_c_library : 1; bool is_exported : 1; bool is_generic : 1; + bool is_from_generic : 1; bool no_extprefix : 1; AnalysisStage stage : 6; @@ -2163,6 +2164,7 @@ Decl *decl_new_with_type(const char *name, SourceSpan span, DeclKind decl_type); Decl *decl_new_var(const char *name, SourceSpan span, TypeInfo *type, VarDeclKind kind); Decl *decl_new_generated_var(Type *type, VarDeclKind kind, SourceSpan span); void decl_set_external_name(Decl *decl); +const char *decl_safe_name(Decl *decl); const char *decl_to_name(Decl *decl); const char *decl_to_a_name(Decl *decl); int decl_count_elements(Decl *structlike); diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index 8a8d31742..f53972a57 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -441,8 +441,7 @@ Expr *parse_vasplat(ParseContext *c) * * parameter ::= (param_path '=')? expr */ -bool parse_arg_list(ParseContext *c, Expr ***result, TokenType param_end, bool *splat, bool vasplat, - bool allow_trailing_comma) +bool parse_arg_list(ParseContext *c, Expr ***result, TokenType param_end, bool *splat, bool vasplat) { *result = NULL; if (splat) *splat = false; @@ -483,15 +482,7 @@ bool parse_arg_list(ParseContext *c, Expr ***result, TokenType param_end, bool * { return true; } - if (tok_is(c, param_end)) - { - if (!allow_trailing_comma) - { - sema_error_at(c->prev_span, "Trailing commas are not allowed."); - return false; - } - return true; - } + if (tok_is(c, param_end)) return true; if (splat && *splat) { SEMA_ERROR_HERE("'...' is only allowed on the last argument in a call."); @@ -756,7 +747,7 @@ Expr *parse_initializer_list(ParseContext *c, Expr *left) if (!try_consume(c, TOKEN_RBRACE)) { Expr **exprs = NULL; - if (!parse_arg_list(c, &exprs, TOKEN_RBRACE, NULL, true, true)) return poisoned_expr; + if (!parse_arg_list(c, &exprs, TOKEN_RBRACE, NULL, true)) return poisoned_expr; int designated = -1; VECEACH(exprs, i) { @@ -854,7 +845,7 @@ static Expr *parse_call_expr(ParseContext *c, Expr *left) { // Pick a modest guess. params = VECNEW(Expr*, 4); - if (!parse_arg_list(c, ¶ms, TOKEN_RPAREN, &splat, true, false)) return poisoned_expr; + if (!parse_arg_list(c, ¶ms, TOKEN_RPAREN, &splat, true)) return poisoned_expr; } if (try_consume(c, TOKEN_EOS)) { diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index 7dcf9c745..b84e15ca0 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -1191,10 +1191,8 @@ bool parse_parameters(ParseContext *c, Decl ***params_ref, Decl **body_params, { Decl** params = NULL; bool var_arg_found = false; - bool last_is_comma = false; while (!is_end_of_param_list(c)) { - last_is_comma = false; bool ellipsis = try_consume(c, TOKEN_ELLIPSIS); // Check for "raw" variadic arguments. This is allowed on C functions and macros. @@ -1392,12 +1390,6 @@ bool parse_parameters(ParseContext *c, Decl ***params_ref, Decl **body_params, } vec_add(params, param); if (!try_consume(c, TOKEN_COMMA)) break; - last_is_comma = true; - } - if (last_is_comma) - { - sema_error_at_after(c->prev_span, "Expected another parameter after ','."); - return false; } *params_ref = params; return true; @@ -2242,7 +2234,7 @@ static inline Decl *parse_enum_declaration(ParseContext *c) if (try_consume(c, TOKEN_LPAREN)) { Expr **result = NULL; - if (!parse_arg_list(c, &result, TOKEN_RPAREN, NULL, false, true)) return poisoned_decl; + if (!parse_arg_list(c, &result, TOKEN_RPAREN, NULL, false)) return poisoned_decl; enum_const->enum_constant.args = result; CONSUME_OR_RET(TOKEN_RPAREN, poisoned_decl); } diff --git a/src/compiler/parser_internal.h b/src/compiler/parser_internal.h index d6bdb7d28..9593d8945 100644 --- a/src/compiler/parser_internal.h +++ b/src/compiler/parser_internal.h @@ -58,8 +58,7 @@ bool parse_current_is_expr(ParseContext *c); bool parse_parameters(ParseContext *c, Decl ***params_ref, Decl **body_params, Variadic *variadic, int *vararg_index_ref, ParameterParseKind parse_kind); -bool parse_arg_list(ParseContext *c, Expr ***result, TokenType param_end, bool *splat, bool vasplat, - bool allow_trailing_comma); +bool parse_arg_list(ParseContext *c, Expr ***result, TokenType param_end, bool *splat, bool vasplat); Expr *parse_type_compound_literal_expr_after_type(ParseContext *c, TypeInfo *type_info); INLINE void add_decl_to_list(Decl ***list, Decl *decl) diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index d82d9fa97..c835304a7 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -2789,7 +2789,7 @@ static inline bool sema_analyse_func_macro(SemaContext *context, Decl *decl, Att static inline bool sema_analyse_func(SemaContext *context, Decl *decl, bool *erase_decl) { - DEBUG_LOG("----Analysing function %s", decl->name); + DEBUG_LOG(">>> Analyse function [%s] in %s", decl_safe_name(decl), context->unit->file->full_path); bool is_interface_method = decl->func_decl.attr_interface_method; if (!sema_analyse_func_macro(context, decl, is_interface_method ? ATTR_INTERFACE_METHOD : ATTR_FUNC, erase_decl)) return false; @@ -2892,7 +2892,7 @@ static inline bool sema_analyse_func(SemaContext *context, Decl *decl, bool *era if (!sema_analyse_doc_header(decl->func_decl.docs, decl->func_decl.signature.params, NULL, &pure)) return decl_poison(decl); decl->func_decl.signature.attrs.is_pure = pure; if (!sema_set_alloca_alignment(context, decl->type, &decl->alignment)) return false; - DEBUG_LOG("Function analysis done."); + DEBUG_LOG("<<< Function analysis of [%s] successful.", decl_safe_name(decl)); return true; } @@ -3396,9 +3396,10 @@ static Module *module_instantiate_generic(SemaContext *context, Module *module, SEMA_ERROR(param, "Expected a value, not a type."); return NULL; } + TypeInfo *type_info = param->type_expr; + if (!sema_resolve_type_info(context, type_info, RESOLVE_TYPE_DEFAULT)) return false; Decl *decl = decl_new_with_type(param_name, params[i]->span, DECL_TYPEDEF); decl->resolve_status = RESOLVE_DONE; - TypeInfo *type_info = param->type_expr; assert(type_info->resolve_status == RESOLVE_DONE); decl->typedef_decl.type_info = type_info; decl->type->name = decl->name; @@ -3406,9 +3407,9 @@ static Module *module_instantiate_generic(SemaContext *context, Module *module, params_decls[decls++] = decl; } - Module *new_module = compiler_find_or_create_module(path, NULL); new_module->is_generic = false; + new_module->is_from_generic = true; CompilationUnit **units = module->units; VECEACH(units, i) { @@ -3633,7 +3634,14 @@ Decl *sema_analyse_parameterized_identifier(SemaContext *c, Path *decl_path, con if (!sema_append_generate_parameterized_name(c, module, params, false)) return poisoned_decl; if (!instantiated_module) return poisoned_decl; instantiated_module->generic_suffix = scratch_buffer_copy(); - sema_analyze_stage(instantiated_module, c->unit->module->stage - 1); + if (c->unit->module->is_from_generic) + { + sema_analyze_stage(instantiated_module, c->unit->module->stage); + } + else + { + sema_analyze_stage(instantiated_module, c->unit->module->stage - 1); + } } if (global_context.errors_found) return poisoned_decl; Decl *symbol = module_find_symbol(instantiated_module, name); @@ -3770,10 +3778,11 @@ bool sema_analyse_decl(SemaContext *context, Decl *decl) { if (decl->resolve_status == RESOLVE_DONE) return decl_ok(decl); + DEBUG_LOG(">>> Analyse declaration [%s] in %s.", decl_safe_name(decl), context_filename(context)); + SemaContext temp_context; context = context_transform_for_eval(context, &temp_context, decl->unit); - DEBUG_LOG(">>> Analysing %s.", decl->name ? decl->name : ".anon"); if (decl->resolve_status == RESOLVE_RUNNING) { SEMA_ERROR(decl, decl->name @@ -3859,9 +3868,12 @@ bool sema_analyse_decl(SemaContext *context, Decl *decl) decl->resolve_status = RESOLVE_DONE; sema_context_destroy(&temp_context); + DEBUG_LOG("<<< Analysis of [%s] successful.", decl_safe_name(decl)); + return true; FAILED: sema_context_destroy(&temp_context); + DEBUG_LOG("<<< Analysis of [%s] failed.", decl_safe_name(decl)); return decl_poison(decl); } diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 5171d6877..7df28fd68 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -648,15 +648,22 @@ static bool expr_may_ref(Expr *expr) bool sema_expr_check_assign(SemaContext *c, Expr *expr) { + Expr *inner; if (!sema_binary_is_expr_lvalue(expr, expr)) return false; - if (expr->expr_kind == EXPR_SUBSCRIPT_ASSIGN) return true; + if (expr->expr_kind == EXPR_SUBSCRIPT) + { + inner = exprptr(expr->subscript_expr.expr); + if (inner->expr_kind == EXPR_IDENTIFIER) inner->identifier_expr.decl->var.is_written = true; + goto CHECK_INNER; + } if (expr->expr_kind == EXPR_BITACCESS || expr->expr_kind == EXPR_ACCESS) expr = expr->access_expr.parent; if (expr->expr_kind == EXPR_IDENTIFIER) { expr->identifier_expr.decl->var.is_written = true; } if (expr->expr_kind != EXPR_UNARY) return true; - Expr *inner = expr->inner_expr; + inner = expr->inner_expr; +CHECK_INNER: if (inner->expr_kind != EXPR_IDENTIFIER) return true; Decl *decl = inner->identifier_expr.decl; if (decl->decl_kind != DECL_VAR) return true; @@ -1067,7 +1074,7 @@ static inline bool sema_binary_analyse_subexpr(SemaContext *context, Expr *binar if (!sema_analyse_expr(context, right)) return false; if (type_kind_is_any_vector(type_flatten(right->type)->type_kind)) { - return sema_analyse_inferred_expr(context, right->type, right); + return sema_analyse_inferred_expr(context, right->type, left); } return sema_analyse_expr(context, left); } @@ -2536,6 +2543,7 @@ static inline bool sema_expr_analyse_subscript(SemaContext *context, Expr *expr, // 1. Evaluate the expression to index. Expr *subscripted = exprptr(expr->subscript_expr.expr); if (!sema_analyse_expr_lvalue_fold_const(context, subscripted)) return false; + if (eval_type == SUBSCRIPT_EVAL_ASSIGN && !sema_expr_check_assign(context, expr)) return false; // 2. Evaluate the index. Expr *index = exprptr(expr->subscript_expr.range.start); @@ -2547,7 +2555,7 @@ static inline bool sema_expr_analyse_subscript(SemaContext *context, Expr *expr, Type *underlying_type = type_flatten(subscripted->type); Type *current_type = underlying_type; - + assert(current_type == current_type->canonical); int64_t index_value = -1; bool start_from_end = expr->subscript_expr.range.start_from_end; if (start_from_end && (underlying_type->type_kind == TYPE_POINTER || underlying_type->type_kind == TYPE_FLEXIBLE_ARRAY)) @@ -4732,9 +4740,6 @@ static bool sema_expr_analyse_assign(SemaContext *context, Expr *expr, Expr *lef { if (!sema_analyse_expr_lvalue(context, left)) return false; } - - bool is_subscript_assign = left->expr_kind == EXPR_SUBSCRIPT_ASSIGN; - // 2. Check assignability if (!sema_expr_check_assign(context, left)) return false; diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h index 8f86f97a4..896591058 100644 --- a/src/compiler/sema_internal.h +++ b/src/compiler/sema_internal.h @@ -31,6 +31,8 @@ extern const char *ct_eval_error; Decl **global_context_acquire_locals_list(void); void generic_context_release_locals_list(Decl **); +const char *context_filename(SemaContext *context); + AstId context_get_defers(SemaContext *context, AstId defer_top, AstId defer_bottom, bool is_success); void context_pop_defers(SemaContext *context, AstId *next); void context_pop_defers_and_replace_ast(SemaContext *context, Ast *ast); diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 8d3f96f98..145aa2154 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -1426,6 +1426,7 @@ static inline bool sema_analyse_foreach_stmt(SemaContext *context, Ast *statemen SEMA_ERROR(enumerator, "%s does not support 'foreach' with the value by reference.", type_quoted_error_string(enumerator->type)); return false; } + if (!decl_ok(len) || !decl_ok(by_val) || !decl_ok(by_ref)) return false; index_macro = value_by_ref ? by_ref : by_val; assert(index_macro); index_type = index_macro->func_decl.signature.params[1]->type; @@ -1559,7 +1560,24 @@ static inline bool sema_analyse_foreach_stmt(SemaContext *context, Ast *statemen if (len_call) { len_decl = decl_new_generated_var(index_type, VARDECL_LOCAL, enumerator->span); - if (!cast_implicit(context, len_call, index_type)) return false; + if (!cast_implicit_silent(context, len_call, index_type)) + { + SEMA_ERROR(enumerator, + "'foreach' is not supported, as the length %s cannot " + "be cast implicitly cast to %s - please update your definition.", + type_quoted_error_string(len_call->type), type_quoted_error_string(index_type)); + if (len) + { + SEMA_NOTE(len, "The definition of 'len()' is here."); + decl_poison(len); + } + if (index_macro) + { + SEMA_NOTE(index_macro, "The index definition is here."); + decl_poison(index_macro); + } + return false; + } vec_add(expressions, expr_generate_decl(len_decl, len_call)); } Expr *idx_init = expr_new_const_int(idx_decl->span, index_type, 0); diff --git a/src/compiler/semantic_analyser.c b/src/compiler/semantic_analyser.c index 3b7df3cc7..06ff9aa62 100644 --- a/src/compiler/semantic_analyser.c +++ b/src/compiler/semantic_analyser.c @@ -51,6 +51,15 @@ void context_change_scope_with_flags(SemaContext *context, ScopeFlags flags) } } +const char *context_filename(SemaContext *context) +{ + CompilationUnit *unit = context->unit; + if (!unit) return ""; + File *file = unit->file; + if (!file || !file->full_path) return ""; + return file->full_path; +} + void context_change_scope_for_label(SemaContext *context, DeclId label_id) { context_change_scope_with_flags(context, SCOPE_NONE); diff --git a/src/utils/file_utils.c b/src/utils/file_utils.c index d626e6c12..e2d9fd506 100644 --- a/src/utils/file_utils.c +++ b/src/utils/file_utils.c @@ -444,6 +444,7 @@ bool file_has_suffix_in_list(const char *file_name, int name_len, const char **s { const char *suffix = suffix_list[i]; int len = (int)strlen(suffix); + if (name_len < len + 1) continue; if (strncmp(&file_name[name_len - len], suffix, len) == 0) return true; } return false; diff --git a/src/version.h b/src/version.h index 0e47f0154..161ae756a 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.5.1" +#define COMPILER_VERSION "0.5.2" diff --git a/test/test_suite/contracts/in_array.c3 b/test/test_suite/contracts/in_array.c3 new file mode 100644 index 000000000..e5b88cb59 --- /dev/null +++ b/test/test_suite/contracts/in_array.c3 @@ -0,0 +1,12 @@ +/** + * @param [in] x + **/ +fn void test(int* x) +{ + x[1] = 123; // #error: 'in' parameters may not be assigned to +} +fn int main() +{ + test(&&int[3]{ 1, 2, 1 }); + return 0; +} \ No newline at end of file diff --git a/test/test_suite/functions/param_with_comma_at_end.c3 b/test/test_suite/functions/param_with_comma_at_end.c3 index 40991ebe7..a83cde8b3 100644 --- a/test/test_suite/functions/param_with_comma_at_end.c3 +++ b/test/test_suite/functions/param_with_comma_at_end.c3 @@ -1,6 +1,6 @@ import std::io; -fn void foo(int a,) // #error: Expected another parameter +fn void foo(int a,) { } \ No newline at end of file diff --git a/test/test_suite/vector/vector_param.c3t b/test/test_suite/vector/vector_param.c3t new file mode 100644 index 000000000..2cb143223 --- /dev/null +++ b/test/test_suite/vector/vector_param.c3t @@ -0,0 +1,34 @@ +// #target: macos-x64 +module test; + +fn void test(int[<4>] x) +{ + x[1] = 123; + int y = x[1]; + assert(y == 123); +} + +fn int main() +{ + test({ 1, 2, 1, 4 }); + return 0; +} + +/* #expect: test.ll + +define void @test.test(<4 x i32> %0) #0 { +entry: + %x = alloca <4 x i32>, align 16 + %y = alloca i32, align 4 + store <4 x i32> %0, ptr %x, align 16 + %1 = load <4 x i32>, ptr %x, align 16 + %elemset = insertelement <4 x i32> %1, i32 123, i64 1 + store <4 x i32> %elemset, ptr %x, align 16 + %2 = load <4 x i32>, ptr %x, align 16 + %3 = extractelement <4 x i32> %2, i64 1 + store i32 %3, ptr %y, align 4 + %4 = load i32, ptr %y, align 4 + %eq = icmp eq i32 %4, 123 + call void @llvm.assume(i1 %eq) + ret void +} \ No newline at end of file diff --git a/test/unit/stdlib/core/string.c3 b/test/unit/stdlib/core/string.c3 index 93becec99..51aa80888 100644 --- a/test/unit/stdlib/core/string.c3 +++ b/test/unit/stdlib/core/string.c3 @@ -111,4 +111,10 @@ fn void! test_rindex_of_char() assert(test.rindex_of_char('l')! == 15); assert(test.rindex_of_char('h')! == 12); assert(@catch(test.index_of_char('x'))); +} + +fn void! test_hex_conversion() +{ + assert("0x123aCd".to_long()! == 0x123acd); + assert("123acD".to_long(16)! == 0x123acd); } \ No newline at end of file