From 563b1e2b285e788338aecfa9f3d6536fb9516fd0 Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Thu, 19 Jan 2017 19:02:44 +0000 Subject: [PATCH] `immutable` field prefix --- code/demo.odin | 7 +++ src/check_decl.c | 2 + src/check_expr.c | 3 ++ src/check_stmt.c | 9 ++-- src/entity.c | 4 +- src/parser.c | 130 ++++++++++++++++++++++++++--------------------- src/tokenizer.c | 1 + 7 files changed, 89 insertions(+), 67 deletions(-) diff --git a/code/demo.odin b/code/demo.odin index 4b4781323..272ef0ec2 100644 --- a/code/demo.odin +++ b/code/demo.odin @@ -10,6 +10,13 @@ #import "utf8.odin"; 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) { // fmt.println("^int"); // return 123, int(x^); diff --git a/src/check_decl.c b/src/check_decl.c index dc7c6e120..5b6d9d230 100644 --- a/src/check_decl.c +++ b/src/check_decl.c @@ -538,6 +538,7 @@ void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNod if (!(e->flags & EntityFlag_Anonymous)) { continue; } + bool is_immutable = e->Variable.is_immutable; String name = e->token.string; Type *t = base_type(type_deref(e->type)); 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; if (f->kind == Entity_Variable) { 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); if (prev != NULL) { error(e->token, "Namespace collision while `using` `%.*s` of: %.*s", LIT(name), LIT(prev->token.string)); diff --git a/src/check_expr.c b/src/check_expr.c index 06ba49d9d..8f0a26dfb 100644 --- a/src/check_expr.c +++ b/src/check_expr.c @@ -782,6 +782,9 @@ Type *check_get_params(Checker *c, Scope *scope, AstNodeArray params, bool *is_v if (p->flags&FieldFlag_no_alias) { param->flags |= EntityFlag_NoAlias; } + if (p->flags&FieldFlag_immutable) { + param->Variable.is_immutable = true; + } add_entity(c, scope, name, param); variables[variable_index++] = param; } diff --git a/src/check_stmt.c b/src/check_stmt.c index f9315c321..c2a09070f 100644 --- a/src/check_stmt.c +++ b/src/check_stmt.c @@ -270,13 +270,10 @@ Type *check_assignment_variable(Checker *c, Operand *op_a, AstNode *lhs) { } gbString str = expr_to_string(op_b.expr); - switch (op_b.mode) { - case Addressing_Value: + if (e != NULL && e->kind == Entity_Variable && e->Variable.is_immutable) { + error_node(op_b.expr, "Cannot assign to an immutable: `%s`", str); + } else { 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); } break; diff --git a/src/entity.c b/src/entity.c index 449c25eeb..70c117d21 100644 --- a/src/entity.c +++ b/src/entity.c @@ -34,8 +34,8 @@ typedef enum EntityFlag { EntityFlag_Anonymous = 1<<2, EntityFlag_Field = 1<<3, EntityFlag_Param = 1<<4, - EntityFlag_Ellipsis = 1<<5, - EntityFlag_VectorElem = 1<<6, + EntityFlag_VectorElem = 1<<5, + EntityFlag_Ellipsis = 1<<6, EntityFlag_NoAlias = 1<<7, } EntityFlag; diff --git a/src/parser.c b/src/parser.c index f083c2c8f..4d799b375 100644 --- a/src/parser.c +++ b/src/parser.c @@ -93,9 +93,12 @@ typedef enum StmtStateFlag { } StmtStateFlag; typedef enum FieldFlag { - FieldFlag_using = 1<<0, - FieldFlag_no_alias = 1<<1, - FieldFlag_ellipsis = 1<<2, + FieldFlag_ellipsis = 1<<0, + FieldFlag_using = 1<<1, + FieldFlag_no_alias = 1<<2, + FieldFlag_immutable = 1<<3, + + FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_immutable, } FieldListTag; 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); } -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) { if (separator == Token_Semicolon) { expect_semicolon(f, param); @@ -2305,41 +2288,75 @@ AstNode *parse_var_type(AstFile *f, bool allow_ellipsis) { 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) { - syntax_error(f->curr_token, "Cannot apply `using` to more than one of the same type"); - *using_count = 0; + +u32 parse_field_prefixes(AstFile *f) { + i32 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; - if (using_count > 0) field_flags |= FieldFlag_using; - if (no_alias_count > 0) field_flags |= FieldFlag_no_alias; + if (using_count > 0) field_flags |= FieldFlag_using; + if (no_alias_count > 0) field_flags |= FieldFlag_no_alias; + if (immutable_count > 0) field_flags |= FieldFlag_immutable; 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) { AstNodeArray params = make_ast_node_array(f); AstNodeArray list = make_ast_node_array(f); 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 - i32 using_count = 0; - i32 no_alias_count = 0; - parse_field_prefixes(f, flags, &using_count, &no_alias_count); + u32 set_flags = parse_field_prefixes(f); while (f->curr_token.kind != follow && f->curr_token.kind != Token_Colon && 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) { 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; expect_token_after(f, Token_Colon, "field list"); 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); parse_expect_separator(f, separator, type); while (f->curr_token.kind != follow && f->curr_token.kind != Token_EOF) { - i32 using_count = 0; - i32 no_alias_count = 0; - parse_field_prefixes(f, flags, &using_count, &no_alias_count); - + u32 set_flags = parse_field_prefixes(f); AstNodeArray names = parse_ident_list(f); if (names.count == 0) { syntax_error(f->curr_token, "Empty field declaration"); 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; expect_token_after(f, Token_Colon, "field list"); 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); if (parse_expect_separator(f, separator, param)) { @@ -2396,7 +2408,7 @@ AstNodeArray parse_field_list(AstFile *f, isize *name_count_, u32 flags, 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) { AstNodeArray names = {0}; 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; 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); } @@ -2601,7 +2613,7 @@ void parse_proc_signature(AstFile *f, AstNodeArray *params, AstNodeArray *results) { 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"); *results = parse_results(f); } diff --git a/src/tokenizer.c b/src/tokenizer.c index f4dbb9fff..74a0b4529 100644 --- a/src/tokenizer.c +++ b/src/tokenizer.c @@ -106,6 +106,7 @@ TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \ TOKEN_KIND(Token_vector, "vector"), \ TOKEN_KIND(Token_using, "using"), \ TOKEN_KIND(Token_no_alias, "no_alias"), \ + TOKEN_KIND(Token_immutable, "immutable"), \ TOKEN_KIND(Token_asm, "asm"), \ TOKEN_KIND(Token_push_allocator, "push_allocator"), \ TOKEN_KIND(Token_push_context, "push_context"), \