diff --git a/releasenotes.md b/releasenotes.md index 49d595969..6b9e10fc0 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -73,6 +73,14 @@ - Defining an extern const without a type would crash rather than print an error. #2771 - Typedef followed by brace would trigger an assert. #2771 - Union with too big member would trigger an assert. #2771 +- Bitstruct with unevaluated user-defined type would cause a crash. #2771 +- Using named parameters with builtins would cause a crash. #2771 +- In some cases, using missing identifiers with builtins would cause a crash. #2771 +- Using `$defined` with function call missing arguments would cause a crash. #2771 +- Adding @nostrip to a test function would crash. #2771 +- Mixing struct splat, non-named params and named params would crash rather than to print an error. #2771 +- Creating a char vector from bytes would crash. #2771 +- Using $$wstr16 with an illegal argument would crash instead of printing an error. #2771 ### Stdlib changes - Add `ThreadPool` join function to wait for all threads to finish in the pool without destroying the threads. diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index 41530a5ed..629416ba2 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -884,6 +884,7 @@ static Expr *parse_initializer_list(ParseContext *c, Expr *left, SourceSpan lhs_ if (i == 0 && expr->expr_kind == EXPR_SPLAT) { splat = expr; + continue; } if (expr->expr_kind == EXPR_DESIGNATOR) { @@ -892,12 +893,10 @@ static Expr *parse_initializer_list(ParseContext *c, Expr *left, SourceSpan lhs_ designated = expr->designator_expr.path[0]->kind == DESIGNATOR_FIELD ? 1 : 2; goto ERROR; } - designated = expr->designator_expr.path[0]->kind == DESIGNATOR_FIELD ? 1 : 2; continue; } if (designated > 0) goto ERROR; - if (designated == -1 && splat) continue; designated = 0; continue; ERROR:; diff --git a/src/compiler/sema_builtins.c b/src/compiler/sema_builtins.c index ae4388dac..ed9201f06 100644 --- a/src/compiler/sema_builtins.c +++ b/src/compiler/sema_builtins.c @@ -395,7 +395,7 @@ bool sema_expr_analyse_str_find(SemaContext *context, Expr *expr) bool sema_expr_analyse_str_conv(SemaContext *context, Expr *expr, BuiltinFunction func) { Expr *inner = expr->call_expr.arguments[0]; - if (!sema_analyse_expr_rvalue(context, inner)) return true; + if (!sema_analyse_expr_rvalue(context, inner)) return false; if (!expr_is_const_string(inner)) { RETURN_SEMA_ERROR(inner, "You need a compile time constant string to take convert."); @@ -550,7 +550,7 @@ bool sema_expr_analyse_str_wide(SemaContext *context, Expr *expr, BuiltinFunctio zero_terminate = zero_term->const_expr.b; } if (!sema_analyse_expr_rvalue(context, inner)) return false; - if (!sema_cast_const(inner) && !expr_is_const_string(inner)) + if (!sema_cast_const(inner) || !expr_is_const_string(inner)) { RETURN_SEMA_ERROR(inner, "You need a compile time constant string to convert to a wide string."); } @@ -640,6 +640,14 @@ bool sema_expr_analyse_builtin_call(SemaContext *context, Expr *expr) RETURN_SEMA_ERROR(args[expected_args], "Too many arguments."); } + for (unsigned i = 0; i < arg_count; i++) + { + if (args[i]->expr_kind == EXPR_NAMED_ARGUMENT) + { + RETURN_SEMA_ERROR(args[i], "Named arguments are not allowed in builtin calls."); + } + } + switch (func) { case BUILTIN_SPRINTF: diff --git a/src/compiler/sema_casts.c b/src/compiler/sema_casts.c index d12f1864d..ef98728b6 100644 --- a/src/compiler/sema_casts.c +++ b/src/compiler/sema_casts.c @@ -2426,10 +2426,17 @@ static void cast_arr_to_vec(Expr *expr, Type *to_type) if (sema_cast_const(expr)) { // For the array -> vector this is always a simple rewrite of type. - ASSERT(expr->const_expr.const_kind == CONST_INITIALIZER); - ConstInitializer *list = expr->const_expr.initializer; - list->type = type_flatten(to_temp); - expr->type = to_temp; + if (expr->const_expr.const_kind == CONST_BYTES || expr->const_expr.const_kind == CONST_STRING) + { + expr->type = to_temp; + } + else + { + ASSERT(expr->const_expr.const_kind == CONST_INITIALIZER); + ConstInitializer *list = expr->const_expr.initializer; + list->type = type_flatten(to_temp); + expr->type = to_temp; + } } else { diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 27688704c..13a9401d0 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -1092,6 +1092,7 @@ static bool sema_analyse_bitstruct(SemaContext *context, Decl *decl, bool *erase DEBUG_LOG("Beginning analysis of %s.", decl->name ? decl->name : ".anon"); if (!sema_resolve_type_info(context, decl->strukt.container_type, RESOLVE_TYPE_DEFAULT)) return false; Type *type = type_flatten(decl->strukt.container_type->type->canonical); + if (!sema_resolve_type_decl(context, type)) return false; if (type_size(type) == 1) { decl->strukt.big_endian = false; diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 1aabca649..674574b4f 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -2289,11 +2289,7 @@ SPLAT_NORMAL:; Decl *param = params[i]; if (param->var.no_init) continue; // Macro empty args // Argument missing, that's bad. - if (no_match_ref) - { - *no_match_ref = true; - return true; - } + if (no_match_ref) return *no_match_ref = true, false; if (!has_named || !param->name) { int missing = 1; @@ -2583,7 +2579,6 @@ static inline bool sema_call_analyse_func_invocation(SemaContext *context, Decl if (sig->attrs.noreturn) expr->call_expr.no_return = true; if (!sema_call_evaluate_arguments(context, &callee, expr, &optional, no_match_ref)) return false; - if (expr->call_expr.is_dynamic_dispatch) { Expr *any_val = expr->call_expr.arguments[0]; diff --git a/src/compiler/sema_initializers.c b/src/compiler/sema_initializers.c index b8804f6f8..5b8cbcb6e 100644 --- a/src/compiler/sema_initializers.c +++ b/src/compiler/sema_initializers.c @@ -1262,6 +1262,7 @@ static inline void sema_update_const_initializer_with_designator( static Type *sema_expr_analyse_designator(SemaContext *context, Type *current, Expr *expr, ArrayIndex *max_index, Decl **member_ptr) { + ASSERT(expr->expr_kind == EXPR_DESIGNATOR); DesignatorElement **path = expr->designator_expr.path; // Walk down into this path diff --git a/src/compiler/sema_liveness.c b/src/compiler/sema_liveness.c index 6a7a09eeb..de5ff3bfd 100644 --- a/src/compiler/sema_liveness.c +++ b/src/compiler/sema_liveness.c @@ -530,10 +530,12 @@ void sema_trace_liveness(void) { FOREACH(Decl *, function, unit->functions) { + if (function->func_decl.attr_test && !keep_tests) continue; + if (function->func_decl.attr_benchmark && !keep_benchmarks) continue; if (function->is_export || function->no_strip || function->func_decl.attr_finalizer || function->func_decl.attr_init || - (function->func_decl.attr_test && keep_tests) || - (function->func_decl.attr_benchmark && keep_benchmarks)) + (function->func_decl.attr_test) || + (function->func_decl.attr_benchmark)) sema_trace_decl_liveness(function); } FOREACH(Decl *, method, unit->methods) diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index cdd80be24..a02b73c3b 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -1527,7 +1527,15 @@ static inline bool sema_analyse_foreach_stmt(SemaContext *context, Ast *statemen // Check the type if needed TypeInfo *variable_type_info = vartype(var); if (variable_type_info && !sema_resolve_type_info(context, variable_type_info, RESOLVE_TYPE_DEFAULT)) return false; - + if (variable_type_info) + { + switch (sema_resolve_storage_type(context, variable_type_info->type)) + { + case STORAGE_ERROR: return false; + case STORAGE_NORMAL: break; + default: RETURN_SEMA_ERROR(var, "%s is not a valid type for '%s', only runtime types with a known size may be used.", type_quoted_error_string(variable_type_info->type), var->name); + } + } // Conditional scope start SCOPE_START diff --git a/test/test_suite/bitstruct/bitstruct_on_unresolved_decl.c3 b/test/test_suite/bitstruct/bitstruct_on_unresolved_decl.c3 new file mode 100644 index 000000000..46d282af8 --- /dev/null +++ b/test/test_suite/bitstruct/bitstruct_on_unresolved_decl.c3 @@ -0,0 +1,3 @@ +bitstruct Foo : FixedBlockPool // #error: The type of the bitstruct cannot be 'FixedBlockPool' but must be an integer or an array of integers +{ +} \ No newline at end of file diff --git a/test/test_suite/builtins/builtin_named.c3 b/test/test_suite/builtins/builtin_named.c3 new file mode 100644 index 000000000..bff25955c --- /dev/null +++ b/test/test_suite/builtins/builtin_named.c3 @@ -0,0 +1,7 @@ +module test; +fn void main() +{ + ichar x = 1; + ichar z2; + bool success2 = $$overflow_add(x, file: filename, &z2); // #error: Named arguments are not allowed in builtin calls. +} \ No newline at end of file diff --git a/test/test_suite/builtins/str_pascalcase_invalid_arg.c3 b/test/test_suite/builtins/str_pascalcase_invalid_arg.c3 new file mode 100644 index 000000000..21f22c757 --- /dev/null +++ b/test/test_suite/builtins/str_pascalcase_invalid_arg.c3 @@ -0,0 +1 @@ +fn int main() => $$str_pascalcase(foo); // #error: 'foo' could not be found, did you spell it right? \ No newline at end of file diff --git a/test/test_suite/builtins/wide_string_not_string.c3 b/test/test_suite/builtins/wide_string_not_string.c3 new file mode 100644 index 000000000..1d2c42f92 --- /dev/null +++ b/test/test_suite/builtins/wide_string_not_string.c3 @@ -0,0 +1 @@ +uint b = $$wstr16(0xfe); // #error: You need a compile time constant string to convert to a wide string \ No newline at end of file diff --git a/test/test_suite/expressions/call_defined_missing_args.c3 b/test/test_suite/expressions/call_defined_missing_args.c3 new file mode 100644 index 000000000..4398d0bb4 --- /dev/null +++ b/test/test_suite/expressions/call_defined_missing_args.c3 @@ -0,0 +1,6 @@ +import std; +HashMap {String, int} val; +const V = $defined(val.init()); +fn void main() +{ +} \ No newline at end of file diff --git a/test/test_suite/functions/nostrip_test.c3t b/test/test_suite/functions/nostrip_test.c3t new file mode 100644 index 000000000..82fd87ab6 --- /dev/null +++ b/test/test_suite/functions/nostrip_test.c3t @@ -0,0 +1,8 @@ +module std::io::fileinfo @test; +fn void test_native_is_file() @nostrip +{ + os::native_is_file; +} +module other; +fn void main() +{} \ No newline at end of file diff --git a/test/test_suite/functions/struct_splat_mixed.c3 b/test/test_suite/functions/struct_splat_mixed.c3 new file mode 100644 index 000000000..476e18649 --- /dev/null +++ b/test/test_suite/functions/struct_splat_mixed.c3 @@ -0,0 +1,4 @@ +struct ParentType { int a; } +struct ChildType { ParentType b;} +const ChildType ABC2 = { }; +float[] asd = { ...ABC2, 0x828dabe3efd81cfa, .c = 8 }; // #error: You’re using both named and unnamed values in the same initializer. \ No newline at end of file diff --git a/test/test_suite/statements/foreach_with_void.c3 b/test/test_suite/statements/foreach_with_void.c3 new file mode 100644 index 000000000..00ec873ea --- /dev/null +++ b/test/test_suite/statements/foreach_with_void.c3 @@ -0,0 +1,6 @@ +fn void main() +{ + foreach_r (void a : foo) // #error: 'void' is not a valid type for 'a', only runtime types with a known size may be used + { + } +} \ No newline at end of file diff --git a/test/test_suite/vector/vector_from_hex.c3t b/test/test_suite/vector/vector_from_hex.c3t new file mode 100644 index 000000000..1353f0698 --- /dev/null +++ b/test/test_suite/vector/vector_from_hex.c3t @@ -0,0 +1,33 @@ +// #target: macos-x64 +module test; +char[<*>] fooy = x'dead'; +char[<*>] fooy2 = { 0xde, 0xad }; +fn void main() +{ + char x = fooy[0]; + char x2 = fooy2[0]; +} + +/* #expect: test.ll + +; ModuleID = 'test' +source_filename = "test" +target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-macosx10.13.0" + +@test.fooy = local_unnamed_addr global [2 x i8] c"\DE\AD", align 2 +@test.fooy2 = local_unnamed_addr global <2 x i8> , align 2 + +define void @test.main() #0 { +entry: + %x = alloca i8, align 1 + %x2 = alloca i8, align 1 + %0 = load <2 x i8>, ptr @test.fooy, align 2 + %1 = extractelement <2 x i8> %0, i64 0 + store i8 %1, ptr %x, align 1 + %2 = load <2 x i8>, ptr @test.fooy2, align 2 + %3 = extractelement <2 x i8> %2, i64 0 + store i8 %3, ptr %x2, align 1 + ret void +} +