From 571728a42e9139dd6c34b1cd95b64ebd20d8e9a9 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Fri, 12 Aug 2022 00:13:44 +0200 Subject: [PATCH] Bump version to 0.3.14. Better non-lvalue errors. Dereferenced optional pointers are not lvalues. --- src/compiler/ast.c | 79 +++++++------- src/compiler/compiler_internal.h | 1 + src/compiler/sema_expr.c | 101 +++++++++++++++--- src/compiler/sema_internal.h | 1 - src/version.h | 2 +- .../errors/try_with_chained_unwrap_errors.c3 | 6 +- .../expressions/assign_deref_opt.c3 | 9 ++ test/test_suite/symbols/various.c3 | 2 +- .../errors/try_with_chained_unwrap_errors.c3 | 6 +- .../expressions/assign_deref_opt.c3 | 9 ++ test/test_suite2/symbols/various.c3 | 5 +- 11 files changed, 155 insertions(+), 66 deletions(-) create mode 100644 test/test_suite/expressions/assign_deref_opt.c3 create mode 100644 test/test_suite2/expressions/assign_deref_opt.c3 diff --git a/src/compiler/ast.c b/src/compiler/ast.c index c48fe0f2a..364c02b7d 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -97,58 +97,65 @@ Decl *decl_new_with_type(const char *name, SourceSpan loc, DeclKind decl_type, V } const char *decl_to_name(Decl *decl) +{ + const char *name = decl_to_a_name(decl); + if (name[1] == 'n') return &name[3]; + return &name[2]; +} + +const char *decl_to_a_name(Decl *decl) { switch (decl->decl_kind) { case DECL_BODYPARAM: - return "bodyparam"; + return "a bodyparam"; case DECL_DECLARRAY: - return "declarray"; + return "a declarray"; case DECL_BITSTRUCT: - return "bitstruct"; + return "a bitstruct"; case DECL_POISONED: - return "poisoned decl"; + return "a poisoned decl"; case DECL_CT_ASSERT: - return "compile time assert"; + return "a compile time assert"; case DECL_CT_CASE: - return "compile time case"; + return "a compile time case"; case DECL_CT_ELIF: - return "compile time else if"; + return "a compile time else if"; case DECL_CT_ELSE: - return "compile time else"; + return "a compile time else"; case DECL_CT_IF: - return "compile time if"; + return "a compile time if"; case DECL_CT_SWITCH: - return "compile time switch"; + return "a compile time switch"; case DECL_IMPORT: - return "import"; + return "an import"; case DECL_LABEL: - return "label"; + return "a label"; case DECL_ATTRIBUTE: - return "attribute"; + return "an attribute"; case DECL_DEFINE: case DECL_TYPEDEF: - return "define"; + return "a define"; case DECL_DISTINCT: - return "distinct type"; + return "a distinct type"; case DECL_ENUM: - return "enum"; + return "an enum"; case DECL_ENUM_CONSTANT: - return "enum value"; + return "an enum value"; case DECL_FAULTVALUE: - return "err value"; + return "a fault value"; case DECL_FAULT: - return "fault"; + return "a fault"; case DECL_FUNC: - return "function"; + return "a function"; case DECL_GENERIC: - return "generic"; + return "a generic"; case DECL_MACRO: - return "macro"; + return "a macro"; case DECL_STRUCT: - return "struct"; + return "a struct"; case DECL_UNION: - return "union"; + return "a union"; case DECL_VAR: switch (decl->var.kind) { @@ -156,30 +163,30 @@ const char *decl_to_name(Decl *decl) case VARDECL_REWRAPPED: UNREACHABLE case VARDECL_CONST: - return "constant"; + return "a constant"; case VARDECL_GLOBAL: - return "global variable"; + return "a global variable"; case VARDECL_LOCAL: - return "variable"; + return "a variable"; case VARDECL_PARAM: - return "parameter"; + return "a parameter"; case VARDECL_MEMBER: case VARDECL_BITMEMBER: - return "member"; + return "a member"; case VARDECL_PARAM_CT: - return "compile time parameter"; + return "a compile time parameter"; case VARDECL_PARAM_CT_TYPE: - return "compile time type parameter"; + return "a compile time type parameter"; case VARDECL_PARAM_REF: - return "ref parameter"; + return "a ref parameter"; case VARDECL_PARAM_EXPR: - return "expression parameter"; + return "a expression parameter"; case VARDECL_LOCAL_CT: - return "compile time variable"; + return "a compile time variable"; case VARDECL_LOCAL_CT_TYPE: - return "compile time type variable"; + return "a compile time type variable"; case VARDECL_UNWRAPPED: - return "unwrapped"; + return "an unwrapped variable"; } UNREACHABLE } diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 3d445d94d..587cd558d 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -1907,6 +1907,7 @@ Decl *decl_new_var(const char *name, SourceSpan span, TypeInfo *type, VarDeclKin Decl *decl_new_generated_var(Type *type, VarDeclKind kind, SourceSpan span); void decl_set_external_name(Decl *decl); const char *decl_to_name(Decl *decl); +const char *decl_to_a_name(Decl *decl); INLINE bool decl_ok(Decl *decl); INLINE bool decl_poison(Decl *decl); diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 9460cfc43..7361b1c78 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -558,7 +558,7 @@ int sema_check_comp_time_bool(SemaContext *context, Expr *expr) } -bool expr_is_lvalue(Expr *expr) +static bool sema_check_expr_lvalue(Expr *top_expr, Expr *expr) { switch (expr->expr_kind) { @@ -566,9 +566,17 @@ bool expr_is_lvalue(Expr *expr) return true; case EXPR_IDENTIFIER: { - if (expr->identifier_expr.is_const) return false; + if (expr->identifier_expr.is_const) + { + SEMA_ERROR(top_expr, "You cannot assign to a constant."); + return false; + } Decl *decl = expr->identifier_expr.decl; - if (decl->decl_kind != DECL_VAR) return false; + if (decl->decl_kind != DECL_VAR) + { + SEMA_ERROR(top_expr, "You cannot assign a value to %s.", decl_to_a_name(decl)); + return false; + } decl = decl_raw(decl); switch (decl->var.kind) { @@ -578,33 +586,96 @@ bool expr_is_lvalue(Expr *expr) case VARDECL_GLOBAL: case VARDECL_PARAM: case VARDECL_PARAM_REF: - return true; - case VARDECL_CONST: - case VARDECL_MEMBER: - case VARDECL_BITMEMBER: case VARDECL_PARAM_CT: case VARDECL_PARAM_CT_TYPE: + return true; + case VARDECL_CONST: case VARDECL_PARAM_EXPR: - return false; + UNREACHABLE + case VARDECL_MEMBER: + case VARDECL_BITMEMBER: + goto ERR; case VARDECL_UNWRAPPED: case VARDECL_ERASE: case VARDECL_REWRAPPED: UNREACHABLE } + UNREACHABLE } case EXPR_UNARY: - return expr->unary_expr.operator == UNARYOP_DEREF; + if (expr->unary_expr.operator != UNARYOP_DEREF) goto ERR; + if (IS_OPTIONAL(expr)) + { + SEMA_ERROR(top_expr, "You cannot assign to a dereferenced optional."); + return false; + } + return true; case EXPR_BITACCESS: case EXPR_ACCESS: - return expr_is_lvalue(expr->access_expr.parent); + return sema_check_expr_lvalue(top_expr, expr->access_expr.parent); case EXPR_GROUP: - return expr_is_lvalue(expr->inner_expr); + return sema_check_expr_lvalue(top_expr, expr->inner_expr); case EXPR_SUBSCRIPT: case EXPR_SLICE: + case EXPR_SUBSCRIPT_ADDR: + if (IS_OPTIONAL(expr)) + { + SEMA_ERROR(top_expr, "You cannot assign to an optional value."); + return false; + } return true; - default: + case EXPR_HASH_IDENT: + SEMA_ERROR(top_expr, "You cannot assign to an unevaluated expression."); return false; + case EXPR_POISONED: + case EXPR_BITASSIGN: + case EXPR_BINARY: + case EXPR_BUILTIN: + case EXPR_COMPILER_CONST: + case EXPR_MACRO_BODY_EXPANSION: + case EXPR_CALL: + case EXPR_CAST: + case EXPR_CATCH: + case EXPR_CATCH_UNWRAP: + case EXPR_COMPOUND_LITERAL: + case EXPR_CONST: + case EXPR_CT_CALL: + case EXPR_CT_CONV: + case EXPR_CT_EVAL: + case EXPR_COND: + case EXPR_DECL: + case EXPR_DESIGNATOR: + case EXPR_EXPR_BLOCK: + case EXPR_EXPRESSION_LIST: + case EXPR_FAILABLE: + case EXPR_RETHROW: + case EXPR_FORCE_UNWRAP: + case EXPR_MACRO_BLOCK: + case EXPR_RETVAL: + case EXPR_FLATPATH: + case EXPR_INITIALIZER_LIST: + case EXPR_DESIGNATED_INITIALIZER_LIST: + case EXPR_POST_UNARY: + case EXPR_SLICE_ASSIGN: + case EXPR_STRINGIFY: + case EXPR_ARGV_TO_SUBARRAY: + case EXPR_TERNARY: + case EXPR_TRY: + case EXPR_TRY_UNWRAP: + case EXPR_TRY_UNWRAP_CHAIN: + case EXPR_TYPEID: + case EXPR_TYPEINFO: + case EXPR_VARIANTSWITCH: + case EXPR_NOP: + case EXPR_TYPEID_INFO: + case EXPR_VARIANT: + case EXPR_BUILTIN_ACCESS: + goto ERR; } + UNREACHABLE +ERR: + SEMA_ERROR(top_expr, "An assignable expression, like a variable, was expected here."); + return false; } @@ -703,11 +774,7 @@ bool expr_may_addr(Expr *expr) bool sema_expr_check_assign(SemaContext *c, Expr *expr) { - if (!expr_is_lvalue(expr)) - { - SEMA_ERROR(expr, "An assignable expression, like a variable, was expected here."); - return false; - } + if (!sema_check_expr_lvalue(expr, expr)) return false; if (expr->expr_kind == EXPR_BITACCESS || expr->expr_kind == EXPR_ACCESS) expr = expr->access_expr.parent; if (expr->expr_kind == EXPR_IDENTIFIER) { diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h index 9f8ff6d7b..8febd1f77 100644 --- a/src/compiler/sema_internal.h +++ b/src/compiler/sema_internal.h @@ -56,7 +56,6 @@ bool splitpathref(const char *string, ArraySize len, Path **path_ref, const char #define IS_CONST(_x) ((_x)->expr_kind == EXPR_CONST) -bool expr_is_lvalue(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); diff --git a/src/version.h b/src/version.h index df162ec13..8256bc222 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.3.13" \ No newline at end of file +#define COMPILER_VERSION "0.3.14" \ No newline at end of file diff --git a/test/test_suite/errors/try_with_chained_unwrap_errors.c3 b/test/test_suite/errors/try_with_chained_unwrap_errors.c3 index 272b46519..02da2efe0 100644 --- a/test/test_suite/errors/try_with_chained_unwrap_errors.c3 +++ b/test/test_suite/errors/try_with_chained_unwrap_errors.c3 @@ -9,7 +9,7 @@ fn void test2() int! a; int b; if (try b = a) {} - if (try test2 = a) {} // #error: An assignable expression + if (try test2 = a) {} // #error: You cannot assign a value to a function } const int BAZ = 1; @@ -19,7 +19,7 @@ fn void test3() int! a; int b; - if (try BAZ = a) {} // #error: An assignable expression + if (try BAZ = a) {} // #error: You cannot assign to a constant } @@ -45,7 +45,7 @@ fn void test6() int! a; int b; int*! x; - if (try *x = a) {} // #error: This is a failable expression, it can't go on the left hand side of a 'try'. + if (try *x = a) {} // #error: You cannot assign to a dereferenced optional. } diff --git a/test/test_suite/expressions/assign_deref_opt.c3 b/test/test_suite/expressions/assign_deref_opt.c3 new file mode 100644 index 000000000..5f4584533 --- /dev/null +++ b/test/test_suite/expressions/assign_deref_opt.c3 @@ -0,0 +1,9 @@ +module test; + +fn void main() +{ + int! a; + int*! b; + *b = a; // #error: You cannot assign to a dereferenced optional +} + diff --git a/test/test_suite/symbols/various.c3 b/test/test_suite/symbols/various.c3 index b6b381c6f..1ab385fa5 100644 --- a/test/test_suite/symbols/various.c3 +++ b/test/test_suite/symbols/various.c3 @@ -62,7 +62,7 @@ fn void test9() { const char A = 1; char b = A; - A = b; // #error: An assignable expression + A = b; // #error: You cannot assign to a constant } fn void test10() diff --git a/test/test_suite2/errors/try_with_chained_unwrap_errors.c3 b/test/test_suite2/errors/try_with_chained_unwrap_errors.c3 index 272b46519..02da2efe0 100644 --- a/test/test_suite2/errors/try_with_chained_unwrap_errors.c3 +++ b/test/test_suite2/errors/try_with_chained_unwrap_errors.c3 @@ -9,7 +9,7 @@ fn void test2() int! a; int b; if (try b = a) {} - if (try test2 = a) {} // #error: An assignable expression + if (try test2 = a) {} // #error: You cannot assign a value to a function } const int BAZ = 1; @@ -19,7 +19,7 @@ fn void test3() int! a; int b; - if (try BAZ = a) {} // #error: An assignable expression + if (try BAZ = a) {} // #error: You cannot assign to a constant } @@ -45,7 +45,7 @@ fn void test6() int! a; int b; int*! x; - if (try *x = a) {} // #error: This is a failable expression, it can't go on the left hand side of a 'try'. + if (try *x = a) {} // #error: You cannot assign to a dereferenced optional. } diff --git a/test/test_suite2/expressions/assign_deref_opt.c3 b/test/test_suite2/expressions/assign_deref_opt.c3 new file mode 100644 index 000000000..5f4584533 --- /dev/null +++ b/test/test_suite2/expressions/assign_deref_opt.c3 @@ -0,0 +1,9 @@ +module test; + +fn void main() +{ + int! a; + int*! b; + *b = a; // #error: You cannot assign to a dereferenced optional +} + diff --git a/test/test_suite2/symbols/various.c3 b/test/test_suite2/symbols/various.c3 index b6b381c6f..f45cd0c93 100644 --- a/test/test_suite2/symbols/various.c3 +++ b/test/test_suite2/symbols/various.c3 @@ -62,7 +62,7 @@ fn void test9() { const char A = 1; char b = A; - A = b; // #error: An assignable expression + A = b; // #error: You cannot assign to a constant } fn void test10() @@ -184,6 +184,3 @@ fn void test24() { int a = b123; // #error: 'int[2][3]' into 'int' } - - -