mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-04 14:48:47 +00:00
immutable field prefix
This commit is contained in:
@@ -10,6 +10,13 @@
|
|||||||
#import "utf8.odin";
|
#import "utf8.odin";
|
||||||
|
|
||||||
main :: proc() {
|
main :: proc() {
|
||||||
|
T :: struct { x, y: int }
|
||||||
|
foo :: proc(using immutable t: T) {
|
||||||
|
a0 := t.x;
|
||||||
|
a1 := x;
|
||||||
|
x = 123; // Error: Cannot assign to an immutable: `x`
|
||||||
|
}
|
||||||
|
|
||||||
// foo :: proc(x: ^i32) -> (int, int) {
|
// foo :: proc(x: ^i32) -> (int, int) {
|
||||||
// fmt.println("^int");
|
// fmt.println("^int");
|
||||||
// return 123, int(x^);
|
// return 123, int(x^);
|
||||||
|
|||||||
@@ -538,6 +538,7 @@ void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNod
|
|||||||
if (!(e->flags & EntityFlag_Anonymous)) {
|
if (!(e->flags & EntityFlag_Anonymous)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
bool is_immutable = e->Variable.is_immutable;
|
||||||
String name = e->token.string;
|
String name = e->token.string;
|
||||||
Type *t = base_type(type_deref(e->type));
|
Type *t = base_type(type_deref(e->type));
|
||||||
if (is_type_struct(t) || is_type_raw_union(t)) {
|
if (is_type_struct(t) || is_type_raw_union(t)) {
|
||||||
@@ -547,6 +548,7 @@ void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNod
|
|||||||
Entity *f = (*found)->elements.entries.e[i].value;
|
Entity *f = (*found)->elements.entries.e[i].value;
|
||||||
if (f->kind == Entity_Variable) {
|
if (f->kind == Entity_Variable) {
|
||||||
Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type);
|
Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type);
|
||||||
|
uvar->Variable.is_immutable = is_immutable;
|
||||||
Entity *prev = scope_insert_entity(c->context.scope, uvar);
|
Entity *prev = scope_insert_entity(c->context.scope, uvar);
|
||||||
if (prev != NULL) {
|
if (prev != NULL) {
|
||||||
error(e->token, "Namespace collision while `using` `%.*s` of: %.*s", LIT(name), LIT(prev->token.string));
|
error(e->token, "Namespace collision while `using` `%.*s` of: %.*s", LIT(name), LIT(prev->token.string));
|
||||||
|
|||||||
@@ -782,6 +782,9 @@ Type *check_get_params(Checker *c, Scope *scope, AstNodeArray params, bool *is_v
|
|||||||
if (p->flags&FieldFlag_no_alias) {
|
if (p->flags&FieldFlag_no_alias) {
|
||||||
param->flags |= EntityFlag_NoAlias;
|
param->flags |= EntityFlag_NoAlias;
|
||||||
}
|
}
|
||||||
|
if (p->flags&FieldFlag_immutable) {
|
||||||
|
param->Variable.is_immutable = true;
|
||||||
|
}
|
||||||
add_entity(c, scope, name, param);
|
add_entity(c, scope, name, param);
|
||||||
variables[variable_index++] = param;
|
variables[variable_index++] = param;
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-6
@@ -270,13 +270,10 @@ Type *check_assignment_variable(Checker *c, Operand *op_a, AstNode *lhs) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
gbString str = expr_to_string(op_b.expr);
|
gbString str = expr_to_string(op_b.expr);
|
||||||
switch (op_b.mode) {
|
if (e != NULL && e->kind == Entity_Variable && e->Variable.is_immutable) {
|
||||||
case Addressing_Value:
|
error_node(op_b.expr, "Cannot assign to an immutable: `%s`", str);
|
||||||
|
} else {
|
||||||
error_node(op_b.expr, "Cannot assign to `%s`", str);
|
error_node(op_b.expr, "Cannot assign to `%s`", str);
|
||||||
break;
|
|
||||||
default:
|
|
||||||
error_node(op_b.expr, "Cannot assign to `%s`", str);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
gb_string_free(str);
|
gb_string_free(str);
|
||||||
} break;
|
} break;
|
||||||
|
|||||||
+2
-2
@@ -34,8 +34,8 @@ typedef enum EntityFlag {
|
|||||||
EntityFlag_Anonymous = 1<<2,
|
EntityFlag_Anonymous = 1<<2,
|
||||||
EntityFlag_Field = 1<<3,
|
EntityFlag_Field = 1<<3,
|
||||||
EntityFlag_Param = 1<<4,
|
EntityFlag_Param = 1<<4,
|
||||||
EntityFlag_Ellipsis = 1<<5,
|
EntityFlag_VectorElem = 1<<5,
|
||||||
EntityFlag_VectorElem = 1<<6,
|
EntityFlag_Ellipsis = 1<<6,
|
||||||
EntityFlag_NoAlias = 1<<7,
|
EntityFlag_NoAlias = 1<<7,
|
||||||
} EntityFlag;
|
} EntityFlag;
|
||||||
|
|
||||||
|
|||||||
+71
-59
@@ -93,9 +93,12 @@ typedef enum StmtStateFlag {
|
|||||||
} StmtStateFlag;
|
} StmtStateFlag;
|
||||||
|
|
||||||
typedef enum FieldFlag {
|
typedef enum FieldFlag {
|
||||||
FieldFlag_using = 1<<0,
|
FieldFlag_ellipsis = 1<<0,
|
||||||
FieldFlag_no_alias = 1<<1,
|
FieldFlag_using = 1<<1,
|
||||||
FieldFlag_ellipsis = 1<<2,
|
FieldFlag_no_alias = 1<<2,
|
||||||
|
FieldFlag_immutable = 1<<3,
|
||||||
|
|
||||||
|
FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_immutable,
|
||||||
} FieldListTag;
|
} FieldListTag;
|
||||||
|
|
||||||
AstNodeArray make_ast_node_array(AstFile *f) {
|
AstNodeArray make_ast_node_array(AstFile *f) {
|
||||||
@@ -2234,26 +2237,6 @@ AstNode *parse_proc_type(AstFile *f, String *foreign_name_, String *link_name_)
|
|||||||
return make_proc_type(f, proc_token, params, results, tags, cc);
|
return make_proc_type(f, proc_token, params, results, tags, cc);
|
||||||
}
|
}
|
||||||
|
|
||||||
void parse_field_prefixes(AstFile *f, u32 flags, i32 *using_count, i32 *no_alias_count) {
|
|
||||||
while (f->curr_token.kind == Token_using ||
|
|
||||||
f->curr_token.kind == Token_no_alias) {
|
|
||||||
if (allow_token(f, Token_using)) {
|
|
||||||
*using_count += 1;
|
|
||||||
}
|
|
||||||
if (allow_token(f, Token_no_alias)) {
|
|
||||||
*no_alias_count += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (*using_count > 1) {
|
|
||||||
syntax_error(f->curr_token, "Multiple `using` in this field list");
|
|
||||||
*using_count = 1;
|
|
||||||
}
|
|
||||||
if (*no_alias_count > 1) {
|
|
||||||
syntax_error(f->curr_token, "Multiple `no_alias` in this field list");
|
|
||||||
*no_alias_count = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool parse_expect_separator(AstFile *f, TokenKind separator, AstNode *param) {
|
bool parse_expect_separator(AstFile *f, TokenKind separator, AstNode *param) {
|
||||||
if (separator == Token_Semicolon) {
|
if (separator == Token_Semicolon) {
|
||||||
expect_semicolon(f, param);
|
expect_semicolon(f, param);
|
||||||
@@ -2305,41 +2288,75 @@ AstNode *parse_var_type(AstFile *f, bool allow_ellipsis) {
|
|||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
void check_field_prefixes(AstFile *f, AstNodeArray names, u32 flags, i32 *using_count, i32 *no_alias_count) {
|
|
||||||
if (names.count > 1 && *using_count > 0) {
|
u32 parse_field_prefixes(AstFile *f) {
|
||||||
syntax_error(f->curr_token, "Cannot apply `using` to more than one of the same type");
|
i32 using_count = 0;
|
||||||
*using_count = 0;
|
i32 no_alias_count = 0;
|
||||||
|
i32 immutable_count = 0;
|
||||||
|
|
||||||
|
while (f->curr_token.kind == Token_using ||
|
||||||
|
f->curr_token.kind == Token_no_alias ||
|
||||||
|
f->curr_token.kind == Token_immutable) {
|
||||||
|
if (allow_token(f, Token_using)) {
|
||||||
|
using_count += 1;
|
||||||
|
}
|
||||||
|
if (allow_token(f, Token_no_alias)) {
|
||||||
|
no_alias_count += 1;
|
||||||
|
}
|
||||||
|
if (allow_token(f, Token_immutable)) {
|
||||||
|
immutable_count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (using_count > 1) {
|
||||||
|
syntax_error(f->curr_token, "Multiple `using` in this field list");
|
||||||
|
using_count = 1;
|
||||||
|
}
|
||||||
|
if (no_alias_count > 1) {
|
||||||
|
syntax_error(f->curr_token, "Multiple `no_alias` in this field list");
|
||||||
|
no_alias_count = 1;
|
||||||
|
}
|
||||||
|
if (immutable_count > 1) {
|
||||||
|
syntax_error(f->curr_token, "Multiple `immutable` in this field list");
|
||||||
|
immutable_count = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((flags&FieldFlag_using) == 0 && *using_count > 0) {
|
|
||||||
syntax_error(f->curr_token, "`using` is not allowed within this field list");
|
|
||||||
*using_count = 0;
|
|
||||||
}
|
|
||||||
if ((flags&FieldFlag_no_alias) == 0 && *no_alias_count > 0) {
|
|
||||||
syntax_error(f->curr_token, "`no_alias` is not allowed within this field list");
|
|
||||||
*no_alias_count = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 field_prefixes_to_flags(i32 using_count, i32 no_alias_count) {
|
|
||||||
u32 field_flags = 0;
|
u32 field_flags = 0;
|
||||||
if (using_count > 0) field_flags |= FieldFlag_using;
|
if (using_count > 0) field_flags |= FieldFlag_using;
|
||||||
if (no_alias_count > 0) field_flags |= FieldFlag_no_alias;
|
if (no_alias_count > 0) field_flags |= FieldFlag_no_alias;
|
||||||
|
if (immutable_count > 0) field_flags |= FieldFlag_immutable;
|
||||||
return field_flags;
|
return field_flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
AstNodeArray parse_field_list(AstFile *f, isize *name_count_, u32 flags,
|
u32 check_field_prefixes(AstFile *f, AstNodeArray names, u32 allowed_flags, u32 set_flags) {
|
||||||
|
if (names.count > 1 && (set_flags&FieldFlag_using)) {
|
||||||
|
syntax_error(f->curr_token, "Cannot apply `using` to more than one of the same type");
|
||||||
|
set_flags &= ~FieldFlag_using;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((allowed_flags&FieldFlag_using) == 0 && (set_flags&FieldFlag_using)) {
|
||||||
|
syntax_error(f->curr_token, "`using` is not allowed within this field list");
|
||||||
|
set_flags &= ~FieldFlag_using;
|
||||||
|
}
|
||||||
|
if ((allowed_flags&FieldFlag_no_alias) == 0 && (set_flags&FieldFlag_no_alias)) {
|
||||||
|
syntax_error(f->curr_token, "`no_alias` is not allowed within this field list");
|
||||||
|
set_flags &= ~FieldFlag_no_alias;
|
||||||
|
}
|
||||||
|
if ((allowed_flags&FieldFlag_immutable) == 0 && (set_flags&FieldFlag_immutable)) {
|
||||||
|
syntax_error(f->curr_token, "`immutable` is not allowed within this field list");
|
||||||
|
set_flags &= ~FieldFlag_immutable;
|
||||||
|
}
|
||||||
|
return set_flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
AstNodeArray parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags,
|
||||||
TokenKind separator, TokenKind follow) {
|
TokenKind separator, TokenKind follow) {
|
||||||
AstNodeArray params = make_ast_node_array(f);
|
AstNodeArray params = make_ast_node_array(f);
|
||||||
AstNodeArray list = make_ast_node_array(f);
|
AstNodeArray list = make_ast_node_array(f);
|
||||||
isize name_count = 0;
|
isize name_count = 0;
|
||||||
bool allow_ellipsis = flags&FieldFlag_ellipsis;
|
bool allow_ellipsis = allowed_flags&FieldFlag_ellipsis;
|
||||||
|
|
||||||
// TODO(bill): Allow for just a list of types
|
u32 set_flags = parse_field_prefixes(f);
|
||||||
i32 using_count = 0;
|
|
||||||
i32 no_alias_count = 0;
|
|
||||||
parse_field_prefixes(f, flags, &using_count, &no_alias_count);
|
|
||||||
while (f->curr_token.kind != follow &&
|
while (f->curr_token.kind != follow &&
|
||||||
f->curr_token.kind != Token_Colon &&
|
f->curr_token.kind != Token_Colon &&
|
||||||
f->curr_token.kind != Token_EOF) {
|
f->curr_token.kind != Token_EOF) {
|
||||||
@@ -2356,35 +2373,30 @@ AstNodeArray parse_field_list(AstFile *f, isize *name_count_, u32 flags,
|
|||||||
if (names.count == 0) {
|
if (names.count == 0) {
|
||||||
syntax_error(f->curr_token, "Empty field declaration");
|
syntax_error(f->curr_token, "Empty field declaration");
|
||||||
}
|
}
|
||||||
check_field_prefixes(f, names, flags, &using_count, &no_alias_count);
|
set_flags = check_field_prefixes(f, names, allowed_flags, set_flags);
|
||||||
|
|
||||||
name_count += names.count;
|
name_count += names.count;
|
||||||
|
|
||||||
expect_token_after(f, Token_Colon, "field list");
|
expect_token_after(f, Token_Colon, "field list");
|
||||||
AstNode *type = parse_var_type(f, allow_ellipsis);
|
AstNode *type = parse_var_type(f, allow_ellipsis);
|
||||||
AstNode *param = make_field(f, names, type, field_prefixes_to_flags(using_count, no_alias_count));
|
AstNode *param = make_field(f, names, type, set_flags);
|
||||||
array_add(¶ms, param);
|
array_add(¶ms, param);
|
||||||
|
|
||||||
parse_expect_separator(f, separator, type);
|
parse_expect_separator(f, separator, type);
|
||||||
|
|
||||||
while (f->curr_token.kind != follow &&
|
while (f->curr_token.kind != follow &&
|
||||||
f->curr_token.kind != Token_EOF) {
|
f->curr_token.kind != Token_EOF) {
|
||||||
i32 using_count = 0;
|
u32 set_flags = parse_field_prefixes(f);
|
||||||
i32 no_alias_count = 0;
|
|
||||||
parse_field_prefixes(f, flags, &using_count, &no_alias_count);
|
|
||||||
|
|
||||||
AstNodeArray names = parse_ident_list(f);
|
AstNodeArray names = parse_ident_list(f);
|
||||||
if (names.count == 0) {
|
if (names.count == 0) {
|
||||||
syntax_error(f->curr_token, "Empty field declaration");
|
syntax_error(f->curr_token, "Empty field declaration");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
check_field_prefixes(f, names, flags, &using_count, &no_alias_count);
|
set_flags = check_field_prefixes(f, names, allowed_flags, set_flags);
|
||||||
name_count += names.count;
|
name_count += names.count;
|
||||||
|
|
||||||
expect_token_after(f, Token_Colon, "field list");
|
expect_token_after(f, Token_Colon, "field list");
|
||||||
AstNode *type = parse_var_type(f, allow_ellipsis);
|
AstNode *type = parse_var_type(f, allow_ellipsis);
|
||||||
|
AstNode *param = make_field(f, names, type, set_flags);
|
||||||
AstNode *param = make_field(f, names, type, field_prefixes_to_flags(using_count, no_alias_count));
|
|
||||||
array_add(¶ms, param);
|
array_add(¶ms, param);
|
||||||
|
|
||||||
if (parse_expect_separator(f, separator, param)) {
|
if (parse_expect_separator(f, separator, param)) {
|
||||||
@@ -2396,7 +2408,7 @@ AstNodeArray parse_field_list(AstFile *f, isize *name_count_, u32 flags,
|
|||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
check_field_prefixes(f, list, flags, &using_count, &no_alias_count);
|
set_flags = check_field_prefixes(f, list, allowed_flags, set_flags);
|
||||||
for_array(i, list) {
|
for_array(i, list) {
|
||||||
AstNodeArray names = {0};
|
AstNodeArray names = {0};
|
||||||
AstNode *type = list.e[i];
|
AstNode *type = list.e[i];
|
||||||
@@ -2406,7 +2418,7 @@ AstNodeArray parse_field_list(AstFile *f, isize *name_count_, u32 flags,
|
|||||||
token.pos = ast_node_token(type).pos;
|
token.pos = ast_node_token(type).pos;
|
||||||
names.e[0] = make_ident(f, token);
|
names.e[0] = make_ident(f, token);
|
||||||
|
|
||||||
AstNode *param = make_field(f, names, list.e[i], field_prefixes_to_flags(using_count, no_alias_count));
|
AstNode *param = make_field(f, names, list.e[i], set_flags);
|
||||||
array_add(¶ms, param);
|
array_add(¶ms, param);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2601,7 +2613,7 @@ void parse_proc_signature(AstFile *f,
|
|||||||
AstNodeArray *params,
|
AstNodeArray *params,
|
||||||
AstNodeArray *results) {
|
AstNodeArray *results) {
|
||||||
expect_token(f, Token_OpenParen);
|
expect_token(f, Token_OpenParen);
|
||||||
*params = parse_field_list(f, NULL, FieldFlag_using|FieldFlag_no_alias|FieldFlag_ellipsis, Token_Comma, Token_CloseParen);
|
*params = parse_field_list(f, NULL, FieldFlag_Signature, Token_Comma, Token_CloseParen);
|
||||||
expect_token_after(f, Token_CloseParen, "parameter list");
|
expect_token_after(f, Token_CloseParen, "parameter list");
|
||||||
*results = parse_results(f);
|
*results = parse_results(f);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -106,6 +106,7 @@ TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \
|
|||||||
TOKEN_KIND(Token_vector, "vector"), \
|
TOKEN_KIND(Token_vector, "vector"), \
|
||||||
TOKEN_KIND(Token_using, "using"), \
|
TOKEN_KIND(Token_using, "using"), \
|
||||||
TOKEN_KIND(Token_no_alias, "no_alias"), \
|
TOKEN_KIND(Token_no_alias, "no_alias"), \
|
||||||
|
TOKEN_KIND(Token_immutable, "immutable"), \
|
||||||
TOKEN_KIND(Token_asm, "asm"), \
|
TOKEN_KIND(Token_asm, "asm"), \
|
||||||
TOKEN_KIND(Token_push_allocator, "push_allocator"), \
|
TOKEN_KIND(Token_push_allocator, "push_allocator"), \
|
||||||
TOKEN_KIND(Token_push_context, "push_context"), \
|
TOKEN_KIND(Token_push_context, "push_context"), \
|
||||||
|
|||||||
Reference in New Issue
Block a user