diff --git a/CMakeLists.txt b/CMakeLists.txt index f4e9b02d5..094f19aa9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,10 +1,24 @@ cmake_minimum_required(VERSION 3.15) -project(c3c) + +# Grab the version +file(READ "src/version.h" ver) +if (NOT ${ver} MATCHES "COMPILER_VERSION \"([0-9]+.[0-9]+.[0-9]+)\"") + message(FATAL_ERROR "Compiler version could not be parsed from version.h") +endif() + +# Set the project and version +project(c3c VERSION ${CMAKE_MATCH_1}) +message("C3C version: ${CMAKE_PROJECT_VERSION}") + +# Enable fetching (for Windows) include(FetchContent) include(FeatureSummary) + set(CMAKE_FIND_PACKAGE_SORT_ORDER NATURAL) set(CMAKE_FIND_PACKAGE_SORT_DIRECTION DEC) + +# We use C11 and C++17 set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 17) diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index c6ad586a2..b4b2287de 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -1237,17 +1237,25 @@ static inline bool sema_call_check_contract_param_match(SemaContext *context, De SEMA_ERROR(expr, "You may not pass null to a '&' parameter."); return false; } + if (expr->expr_kind == EXPR_UNARY && expr->unary_expr.expr->expr_kind == EXPR_IDENTIFIER) + { + if (expr->unary_expr.expr->identifier_expr.decl->var.kind == VARDECL_CONST && param->var.out_param) + { + SEMA_ERROR(expr, "A const parameter may not be passed into a function or macro as an 'out' argument."); + return false; + } + } if (expr->expr_kind != EXPR_IDENTIFIER) return true; Decl *ident = expr->identifier_expr.decl; if (ident->decl_kind != DECL_VAR) return true; if (ident->var.out_param && param->var.in_param) { - SEMA_ERROR(expr, "It's not allowed to pass an 'out' parameter into a function or macro as an 'in' argument."); + SEMA_ERROR(expr, "An 'out' parameter may not be passed into a function or macro as an 'in' argument."); return false; } if (ident->var.in_param && param->var.out_param) { - SEMA_ERROR(expr, "It's not allowed to pass an 'in' parameter into a function or macro as an 'out' argument."); + SEMA_ERROR(expr, "An 'in' parameter may not be passed into a function or macro as an 'out' argument."); return false; } return true; diff --git a/src/version.h b/src/version.h index da6ec54c4..eee4ac948 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.4.100" \ No newline at end of file +#define COMPILER_VERSION "0.4.101" \ No newline at end of file diff --git a/test/test_suite/contracts/constant_out.c3 b/test/test_suite/contracts/constant_out.c3 new file mode 100644 index 000000000..9f98a7081 --- /dev/null +++ b/test/test_suite/contracts/constant_out.c3 @@ -0,0 +1,36 @@ +module test; + + +struct Abc +{ + int a; +} + +fn void Abc.update(Abc* a, int ab) +{ + a.a = ab; +} + +/** + * @param [out] a + **/ +fn void Abc.update_fail(Abc* a, int ab) +{ + a.a = ab; +} + +const Abc X = { 2 }; + + +fn int main() +{ + const Abc Y = { 3 }; + int* z = &X.a; + *z = 31; + X.update(1); + X.update_fail(2); // #error: const parameter may not + X.a = 32; // #error: cannot assign to a constant + Y.a = 34; // #error: cannot assign to a constant + return 0; +} + diff --git a/test/test_suite/contracts/in_out.c3 b/test/test_suite/contracts/in_out.c3 index 359d4949a..97ca78f7a 100644 --- a/test/test_suite/contracts/in_out.c3 +++ b/test/test_suite/contracts/in_out.c3 @@ -12,7 +12,7 @@ fn void bar(Foo* f) **/ fn void foo(Foo* f) { - bar(f); // #error: It's not allowed to pass an 'in' + bar(f); // #error: macro as an 'out' argument } /** @@ -27,5 +27,5 @@ fn void foo2(Foo* f) **/ fn void baz(Foo *f) { - foo2(f); // #error: It's not allowed to pass an 'out' + foo2(f); // #error: may not be passed into a function } \ No newline at end of file