diff --git a/releasenotes.md b/releasenotes.md index 7c21e3bfd..e2983ba43 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -18,6 +18,7 @@ - Improve error message for missing `$endif`. - `foo[x][y] = b` now interpreted as `(*&foo[x])[y] = b` which allows overloads to do chained [] accesses. - Error if a stack allocated variable is too big (configurable with `--max-stack-object-size`). +- Add `@safeinfer` to allow `var` to be used locally. ### Fixes - List.remove_at would incorrectly trigger ASAN. diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 7d4549bc7..d316af04c 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -466,6 +466,7 @@ typedef struct VarDecl_ bool is_temp : 1; bool copy_const : 1; bool defaulted : 1; + bool safe_infer : 1; union { Expr *init_expr; diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 44e6b3aac..1141b3aa1 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -307,6 +307,7 @@ typedef enum ATTRIBUTE_PUBLIC, ATTRIBUTE_PURE, ATTRIBUTE_REFLECT, + ATTRIBUTE_SAFEINFER, ATTRIBUTE_SAFEMACRO, ATTRIBUTE_SECTION, ATTRIBUTE_STRUCTLIKE, diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index e568b3edc..9230ef234 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -857,7 +857,7 @@ Decl *parse_local_decl_after_type(ParseContext *c, TypeInfo *type) bool is_cond; if (!parse_attributes(c, &decl->attributes, NULL, NULL, &is_cond)) return poisoned_decl; - decl->is_cond = true; + decl->is_cond = is_cond; if (tok_is(c, TOKEN_EQ)) { if (!decl) @@ -949,6 +949,8 @@ Decl *parse_var_decl(ParseContext *c) // analyser. The runtime variables must have an initializer unlike the CT ones. advance_and_verify(c, TOKEN_VAR); Decl *decl; + bool is_cond; + SourceSpan span; switch (c->tok) { case TOKEN_CONST_IDENT: @@ -957,6 +959,8 @@ Decl *parse_var_decl(ParseContext *c) case TOKEN_IDENT: decl = decl_new_var_current(c, NULL, VARDECL_LOCAL); advance(c); + if (!parse_attributes(c, &decl->attributes, NULL, NULL, &is_cond)) return poisoned_decl; + decl->is_cond = is_cond; if (!tok_is(c, TOKEN_EQ)) { PRINT_ERROR_HERE("'var' must always have an initial value, or the type cannot be inferred."); @@ -966,16 +970,16 @@ Decl *parse_var_decl(ParseContext *c) ASSIGN_EXPR_OR_RET(decl->var.init_expr, parse_expr(c), poisoned_decl); break; case TOKEN_CT_IDENT: - decl = decl_new_var_current(c, NULL, VARDECL_LOCAL_CT); - advance(c); - if (try_consume(c, TOKEN_EQ)) - { - ASSIGN_EXPR_OR_RET(decl->var.init_expr, parse_expr(c), poisoned_decl); - } - break; case TOKEN_CT_TYPE_IDENT: - decl = decl_new_var_current(c, NULL, VARDECL_LOCAL_CT_TYPE); + decl = decl_new_var_current(c, NULL, c->tok == TOKEN_CT_IDENT ? VARDECL_LOCAL_CT : VARDECL_LOCAL_CT_TYPE); advance(c); + span = c->span; + if (!parse_attributes(c, &decl->attributes, NULL, NULL, &is_cond)) return poisoned_decl; + if (is_cond || decl->attributes) + { + print_error_at(span, "Attributes are not allowed on compile time variables."); + return poisoned_decl; + } if (try_consume(c, TOKEN_EQ)) { ASSIGN_EXPR_OR_RET(decl->var.init_expr, parse_expr(c), poisoned_decl); diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index c2291a1fe..be4eacacf 100755 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -3055,6 +3055,7 @@ static bool sema_analyse_attribute(SemaContext *context, ResolvedAttrData *attr_ [ATTRIBUTE_PURE] = ATTR_CALL, [ATTRIBUTE_REFLECT] = ATTR_FUNC | ATTR_GLOBAL | ATTR_CONST | USER_DEFINED_TYPES, [ATTRIBUTE_SAFEMACRO] = ATTR_MACRO, + [ATTRIBUTE_SAFEINFER] = ATTR_GLOBAL | ATTR_LOCAL, [ATTRIBUTE_SECTION] = ATTR_FUNC | ATTR_CONST | ATTR_GLOBAL, [ATTRIBUTE_STRUCTLIKE] = ATTR_DISTINCT, [ATTRIBUTE_TAG] = ATTR_BITSTRUCT_MEMBER | ATTR_MEMBER | USER_DEFINED_TYPES | CALLABLE_TYPE, @@ -3421,6 +3422,9 @@ static bool sema_analyse_attribute(SemaContext *context, ResolvedAttrData *attr_ decl->attr_nopadding = true; decl->attr_compact = true; break; + case ATTRIBUTE_SAFEINFER: + decl->var.safe_infer = true; + break; case ATTRIBUTE_NOINIT: decl->var.no_init = true; break; @@ -4651,9 +4655,9 @@ bool sema_analyse_var_decl(SemaContext *context, Decl *decl, bool local) return decl_poison(decl); } ASSERT(!decl->var.no_init); - if (kind == VARDECL_LOCAL && !context_is_macro(context) && init_expr->expr_kind != EXPR_LAMBDA) + if (kind == VARDECL_LOCAL && !context_is_macro(context) && init_expr->expr_kind != EXPR_LAMBDA && !decl->var.safe_infer) { - SEMA_ERROR(decl, "Defining a variable using 'var %s = ...' is only allowed inside a macro, or when defining a lambda.", decl->name); + SEMA_ERROR(decl, "Defining a variable using 'var %s = ...' is only allowed inside a macro, or when defining a lambda. You can override this by adding the attribute '@safeinfer' to the declaration.", decl->name); return decl_poison(decl); } if (!sema_analyse_expr(context, init_expr)) return decl_poison(decl); diff --git a/src/compiler/symtab.c b/src/compiler/symtab.c index 5f4f68e49..aa13abca3 100644 --- a/src/compiler/symtab.c +++ b/src/compiler/symtab.c @@ -372,6 +372,7 @@ void symtab_init(uint32_t capacity) attribute_list[ATTRIBUTE_PURE] = kw_at_pure; attribute_list[ATTRIBUTE_PUBLIC] = KW_DEF("@public"); attribute_list[ATTRIBUTE_REFLECT] = KW_DEF("@reflect"); + attribute_list[ATTRIBUTE_SAFEINFER] = KW_DEF("@safeinfer"); attribute_list[ATTRIBUTE_SAFEMACRO] = KW_DEF("@safemacro"); attribute_list[ATTRIBUTE_SECTION] = KW_DEF("@section"); attribute_list[ATTRIBUTE_STRUCTLIKE] = KW_DEF("@structlike"); diff --git a/test/test_suite/attributes/safe_infer.c3 b/test/test_suite/attributes/safe_infer.c3 new file mode 100644 index 000000000..32b4c4056 --- /dev/null +++ b/test/test_suite/attributes/safe_infer.c3 @@ -0,0 +1,5 @@ +fn int main(String[] args) +{ + var y @safeinfer = 1; + return 0; +} \ No newline at end of file diff --git a/test/test_suite/statements/big_locals.c3 b/test/test_suite/statements/big_locals.c3 index c3ebe594d..de8876b0e 100644 --- a/test/test_suite/statements/big_locals.c3 +++ b/test/test_suite/statements/big_locals.c3 @@ -1,6 +1,6 @@ import std; fn int main(String[] args) { - int[1024 * 1024] x; // #error: The size of this local variable (4096 Kb) exceeds the maximum allowed stack object size (128 Kb) + int[1024 * 1024] x; // #error: The size of this local variable (4096 Kb) exceeds the maximum allowed return 0; }