diff options
| author | Kimplul <kimi.h.kuparinen@gmail.com> | 2025-01-04 01:25:31 +0200 | 
|---|---|---|
| committer | Kimplul <kimi.h.kuparinen@gmail.com> | 2025-01-04 01:25:31 +0200 | 
| commit | aec19e55ca32f68536a550f100d3f058b8a93c02 (patch) | |
| tree | c876d205fa9867aca8b7bb8c072635e2cf876205 /src | |
| parent | 718784ca20b8cb49aec438daecc846f273971793 (diff) | |
| download | fwd-aec19e55ca32f68536a550f100d3f058b8a93c02.tar.gz fwd-aec19e55ca32f68536a550f100d3f058b8a93c02.zip | |
initial move checking
+ Missing implementations for most things, but it already highlights
  an oversight in my initial plan, namely that currently, a function
  might call multiple of its closures, meaning that a closure wouldn't
  be allowed to move a value. I'm debating whether to check that only
  one closure from a parameter list is called at a time or if I should
  do what Hylo does and add in some kind of 'subscript' that's like a
  function but has slightly different rules?
Diffstat (limited to 'src')
| -rw-r--r-- | src/analyze.c | 2 | ||||
| -rw-r--r-- | src/ast.c | 44 | ||||
| -rw-r--r-- | src/compiler.c | 4 | ||||
| -rw-r--r-- | src/debug.c | 6 | ||||
| -rw-r--r-- | src/move.c | 149 | 
5 files changed, 204 insertions, 1 deletions
| diff --git a/src/analyze.c b/src/analyze.c index 37b2e65..077e7d8 100644 --- a/src/analyze.c +++ b/src/analyze.c @@ -418,7 +418,7 @@ static int analyze(struct state *state, struct scope *scope, struct ast *node)  	case AST_CONST_STR:     ret = analyze_str       (state, scope, node);  		break;  	default: -		internal_error("missing ast analysis"); +		internal_error("missing ast analysis for %s", ast_str(node->k));  		return -1;  	} @@ -604,3 +604,47 @@ bool type_lists_match(struct type *al, struct type *bl)  	}  	return true;  } + +const char *ast_str(enum ast_kind k) +{ +#define CASE(x) case x: return #x; +	switch (k) { +	CASE(AST_CLOSURE); +	CASE(AST_IF); +	CASE(AST_LET); +	CASE(AST_INIT); +	CASE(AST_CALL); +	CASE(AST_PROC_DEF); +	CASE(AST_VAR_DEF); +	CASE(AST_DOT); +	CASE(AST_BLOCK); +	CASE(AST_ID); +	CASE(AST_EMPTY); +	CASE(AST_ADD); +	CASE(AST_SUB); +	CASE(AST_MUL); +	CASE(AST_DIV); +	CASE(AST_REM); +	CASE(AST_LAND); +	CASE(AST_LOR); +	CASE(AST_LSHIFT); +	CASE(AST_RSHIFT); +	CASE(AST_LT); +	CASE(AST_GT); +	CASE(AST_LE); +	CASE(AST_GE); +	CASE(AST_NE); +	CASE(AST_EQ); +	CASE(AST_NEG); +	CASE(AST_LNOT); +	CASE(AST_NOT); +	CASE(AST_CONST_INT); +	CASE(AST_CONST_CHAR); +	CASE(AST_CONST_BOOL); +	CASE(AST_CONST_FLOAT); +	CASE(AST_CONST_STR); +	} +#undef CASE + +	return "UNKNOWN"; +} diff --git a/src/compiler.c b/src/compiler.c index d18e767..9f564d8 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -21,6 +21,7 @@  #include <fwd/debug.h>  #include <fwd/scope.h>  #include <fwd/lower.h> +#include <fwd/move.h>  /**   * Read whole file into a buffer and return pointer to buffer. @@ -112,6 +113,9 @@ static int process(struct scope **parent, const char *file)  	if (analyze_root(scope, tree))  		return -1; +	if (mvcheck_root(tree)) +		return -1; +  	return 0;  } diff --git a/src/debug.c b/src/debug.c index cd0b8eb..f72fe9b 100644 --- a/src/debug.c +++ b/src/debug.c @@ -144,6 +144,12 @@ void type_mismatch(struct scope *scope, struct ast *node,  	free((void *)rs);  } +void move_error(struct ast *new_use, struct ast *prev_use) +{ +	semantic_error(new_use->scope, new_use, "using moved value"); +	semantic_info(prev_use->scope, prev_use, "previously moved here"); +} +  static void _type_str(FILE *f, struct type *t);  static void _type_list_str(FILE *f, struct type *types) diff --git a/src/move.c b/src/move.c new file mode 100644 index 0000000..b09f745 --- /dev/null +++ b/src/move.c @@ -0,0 +1,149 @@ +#include <fwd/move.h> + +struct ast_pair { +	struct ast *def; +	struct ast *use; +}; + +#define SPTREE_TYPE struct ast_pair +#define SPTREE_CMP(a, b) ((a).def - (b).def) +#define SPTREE_NAME moved +#include <fwd/sptree.h> + +struct state { +	struct moved moved; +	struct state *parent; +}; + +static struct state create_state(struct state *parent) +{ +	struct state state = {}; +	state.parent = parent; +	state.moved = moved_create(); +	return state; +} + +static void destroy_state(struct state *state) +{ +	moved_destroy(&state->moved); +} + +static struct ast_pair *find_move(struct state *state, struct ast *def) +{ +	if (!state) +		return NULL; + +	struct ast_pair search = {.def = def}; +	struct ast_pair *found = moved_find(&state->moved, search); +	if (found) +		return found; + +	return find_move(state->parent, def); +} + +static void insert_move(struct state *state, struct ast *def, struct ast *use) +{ +	struct ast_pair pair =  {.def = def, .use = use}; +	moved_insert(&state->moved, pair); +} + +static void push_up(struct state *state) +{ +	struct state *parent = state->parent; +	if (!parent) +		return; + +	if (moved_len(&state->moved) == 0) +		return; + +	foreach(moved, n, &state->moved) { +		moved_insert(&parent->moved, *n); +	} +} + +static int mvcheck(struct state *state, struct ast *node); +static int mvcheck_list(struct state *state, struct ast *nodes); + +static int mvcheck_proc(struct state *state, struct ast *node) +{ +	/* extern, can't really do anything so just say it's fine */ +	if (!proc_body(node)) +		return 0; + +	struct state new_state = create_state(state); +	/* we don't need to merge things into the parent state */ +	int ret = mvcheck(&new_state, proc_body(node)); +	destroy_state(&new_state); +	return ret; +} + +static int mvcheck_block(struct state *state, struct ast *node) +{ +	return mvcheck_list(state, block_body(node)); +} + +static int mvcheck_list(struct state *state, struct ast *nodes) +{ +	foreach_node(node, nodes) { +		if (mvcheck(state, node)) +			return -1; +	} + +	return 0; +} + +static int mvcheck_call(struct state *state, struct ast *node) +{ +	return mvcheck_list(state, call_args(node)); +} + +static int mvcheck_closure(struct state *state, struct ast *node) +{ +	struct state new_state = create_state(state); +	int ret = mvcheck(&new_state, closure_body(node)); +	push_up(&new_state); + +	destroy_state(&new_state); +	return ret; +} + +static int mvcheck_id(struct state *state, struct ast *node) +{ +	struct ast *def = file_scope_find_symbol(node->scope, id_str(node)); +	assert(def); + +	struct ast_pair *prev = find_move(state, def); +	if (prev) { +		move_error(node, prev->use); +		return -1; +	} + +	insert_move(state, def, node); +	return 0; +} + +static int mvcheck(struct state *state, struct ast *node) +{ +	switch (node->k) { +	case AST_PROC_DEF:	return mvcheck_proc	(state, node); +	case AST_BLOCK:		return mvcheck_block	(state, node); +	case AST_CALL:		return mvcheck_call	(state, node); +	case AST_CLOSURE:	return mvcheck_closure	(state, node); +	case AST_ID:		return mvcheck_id	(state, node); +	default: break; +	} + +	internal_error("missing move check for %s", ast_str(node->k)); +	return -1; +} + +int mvcheck_root(struct ast *root) +{ +	foreach_node(node, root) { +		struct state state = create_state(NULL); +		if (mvcheck(&state, node)) +			return -1; +	} + +	return 0; +} | 
