summaryrefslogtreecommitdiff
path: root/scope.c
blob: 0a05baea2cfc663a2ef6dcd5ad0bd6cce12cc288 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
/*
  Copyright (C) 2019, 2020 Leo Tenenbaum.
  This file is part of toc. toc is distributed under version 3 of the GNU General Public License, without any warranty whatsoever.
  You should have received a copy of the GNU General Public License along with toc. If not, see <https://www.gnu.org/licenses/>.
*/
enum {
	  SCOPE_CHECK_REDECL = 0x01,
};


static void val_free(Value *v, Type *t);

static bool DEBUG_UNDERSCORE(add_ident_decls)(SOURCE_LOCATION_PARAMS Block *b, Declaration *d, U16 flags) {
	bool ret = true;
	arr_foreach(d->idents, Identifier, ident) {
		IdentDecl *decls = (*ident)->decls;
		if ((flags & SCOPE_CHECK_REDECL) && arr_len(decls)) {
			/* check that it hasn't been declared in this block */
			IdentDecl *prev = arr_last(decls);
			if (prev->scope == b) {
				err_print(d->where, "Re-declaration of identifier in the same block.");
				info_print(prev->decl->where, "Previous declaration was here.");
#ifdef TOC_DEBUG
				err_fprint(d->where.file->ctx, "First declaration was done by %s:%d, second was done by %s:%d.\n", prev->src_file, prev->src_line, src_file, src_line);
#endif				
				ret = false;
				continue;
			}
		}
		
		IdentDecl *idecl = ident_add_decl(*ident, d, b);
#ifdef TOC_DEBUG
		idecl->src_file = src_file;
		idecl->src_line = src_line;
#else
		(void)idecl;
#endif
	}
	return ret;
}

static void remove_ident_decls(Block *b, Declaration *d) {
	U64 i = 0;
	bool is_tuple = d->type.kind == TYPE_TUPLE;
	arr_foreach(d->idents, Identifier, ident) {
		IdentSlot *id_info = *ident;
		IdentDecl **decls = &id_info->decls;
		IdentDecl *last_decl = arr_last(*decls);
		if (last_decl && last_decl->scope == b) {
			if ((last_decl->flags & IDECL_HAS_VAL)
				/* don't free const vals (there's only one per decl) */
				&& !(last_decl->decl->flags & DECL_IS_CONST)) {
				val_free(&last_decl->val, is_tuple ? &d->type.tuple[i++] : &d->type);
			}
			arr_remove_last(decls); /* remove that declaration */
		}
	}
}

/* pass NULL for block for global scope */
static bool block_enter(Block *b, Statement *stmts, U16 flags) {
	bool ret = true;
	arr_foreach(stmts, Statement, stmt) {
		if (stmt->kind == STMT_DECL) {
			Declaration *decl = &stmt->decl;
			if (!add_ident_decls(b, decl, flags))
				ret = false;
		}
	}
	return ret;
}

static void block_exit(Block *b, Statement *stmts) {
	arr_foreach(stmts, Statement, stmt) {
		if (stmt->kind == STMT_DECL) {
			Declaration *decl = &stmt->decl;
			remove_ident_decls(b, decl);
		}
	}
}

/* does NOT enter function's block body */
static bool fn_enter(FnExpr *f, U16 flags) {
	arr_foreach(f->params, Declaration, decl)
		if (!add_ident_decls(&f->body, decl, flags))
			return false;
	arr_foreach(f->ret_decls, Declaration, decl)
		if (!add_ident_decls(&f->body, decl, flags))
			return false;
	return true;
}

static void fn_exit(FnExpr *f) {
	arr_foreach(f->params, Declaration, decl)
		remove_ident_decls(&f->body, decl);
	arr_foreach(f->ret_decls, Declaration, decl)
		remove_ident_decls(&f->body, decl);
}

static bool for_enter(Expression *e) {
	assert(e->kind == EXPR_FOR);
	ForExpr *fo = e->for_;
	if (fo->index && fo->index == fo->value) {
		err_print(e->where, "The identifier for the index of a for loop must be different from the identifier for the value.");
		return false;
	}
	if (fo->index) {
		IdentDecl *id = arr_add(&fo->index->decls);
		id->flags = 0;
		id->kind = IDECL_EXPR;
		id->scope = &fo->body;
		id->expr = e;
	}
	if (fo->value) {
		IdentDecl *id = arr_add(&fo->value->decls);
		id->flags = 0;
		id->kind = IDECL_EXPR;
		id->scope = &fo->body;
		id->expr = e;
	}
	return true;
}

static void for_exit(Expression *e) {
	assert(e->kind == EXPR_FOR);
	ForExpr *fo = e->for_;
	if (fo->index) {
		arr_remove_last(&fo->index->decls);
	}
	if (fo->value) {
		arr_remove_last(&fo->value->decls);
	}
}