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