Skip to content

Commit

Permalink
Report error when missing return statement in BCS
Browse files Browse the repository at this point in the history
  • Loading branch information
positively-charged committed Nov 4, 2016
1 parent e8ca0e7 commit 45be2a4
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 25 deletions.
4 changes: 0 additions & 4 deletions src/codegen/dec.c
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,6 @@ void write_func( struct codegen* codegen, struct func* func ) {
alloc_param_indexes( &record, func->params );
alloc_funcscopevars_indexes( &record, &impl->funcscope_vars );
c_write_block( codegen, impl->body );
c_pcd( codegen, PCD_RETURNVOID );
impl->size = record.size;
codegen->func = NULL;
if ( impl->nested_funcs ) {
Expand Down Expand Up @@ -553,9 +552,6 @@ void write_one_nestedfunc( struct codegen* codegen,
// Body:
// -----------------------------------------------------------------------
c_write_block( codegen, impl->body );
if ( func->return_spec != SPEC_VOID ) {
c_pcd( codegen, PCD_PUSHNUMBER, 0 );
}
impl->size = record.size;
// Prologue (part #2):
// -----------------------------------------------------------------------
Expand Down
14 changes: 1 addition & 13 deletions src/semantic/dec.c
Original file line number Diff line number Diff line change
Expand Up @@ -2181,26 +2181,14 @@ void s_test_func_body( struct semantic* semantic, struct func* func ) {
semantic->topfunc_test = &test;
}
semantic->func_test = &test;
s_test_top_block( semantic, impl->body );
s_test_func_block( semantic, func, impl->body );
semantic->func_test = test.parent;
impl->returns = test.returns;
if ( ! impl->nested ) {
impl->nested_funcs = test.nested_funcs;
semantic->topfunc_test = NULL;
}
s_pop_scope( semantic );
if ( semantic->lang == LANG_ACS ) {
bool return_stmt_at_end = false;
if ( list_size( &impl->body->stmts ) > 0 ) {
struct node* node = list_tail( &impl->body->stmts );
return_stmt_at_end = ( node->type == NODE_RETURN );
}
if ( func->return_spec != SPEC_VOID && ! return_stmt_at_end ) {
s_diag( semantic, DIAG_POS_ERR, &func->object.pos,
"function missing return statement at end of body" );
s_bail( semantic );
}
}
// If `auto` is still the specifier, it means no return statements were
// encountered. The return type is then `void`.
if ( func->return_spec == SPEC_AUTO ) {
Expand Down
9 changes: 9 additions & 0 deletions src/semantic/phase.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,16 @@ struct stmt_test {
struct jump* jump_break;
struct jump* jump_continue;
struct type_info cond_type;
enum {
FLOW_GOING,
FLOW_BREAKING,
FLOW_DEAD,
FLOW_RESET,
} flow;
bool in_loop;
bool manual_scope;
bool case_allowed;
bool found_folded_case;
};

struct expr_test {
Expand Down Expand Up @@ -139,6 +146,8 @@ void s_test_expr_type( struct semantic* semantic, struct expr_test* test,
void s_test_bool_expr( struct semantic* semantic, struct expr* expr );
void s_init_stmt_test( struct stmt_test*, struct stmt_test* );
void s_test_top_block( struct semantic* semantic, struct block* block );
void s_test_func_block( struct semantic* semantic, struct func* func,
struct block* block );
void s_add_scope( struct semantic* semantic, bool func_scope );
void s_pop_scope( struct semantic* semantic );
void s_test_script( struct semantic* semantic, struct script* script );
Expand Down
156 changes: 148 additions & 8 deletions src/semantic/stmt.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,11 @@ void s_init_stmt_test( struct stmt_test* test, struct stmt_test* parent ) {
test->switch_stmt = NULL;
test->jump_break = NULL;
test->jump_continue = NULL;
test->flow = FLOW_GOING;
test->in_loop = false;
test->manual_scope = false;
test->case_allowed = false;
test->found_folded_case = false;
}

void s_test_top_block( struct semantic* semantic, struct block* block ) {
Expand All @@ -70,6 +72,34 @@ void s_test_top_block( struct semantic* semantic, struct block* block ) {
check_dup_label( semantic );
}

void s_test_func_block( struct semantic* semantic, struct func* func,
struct block* block ) {
struct stmt_test test;
s_init_stmt_test( &test, NULL );
test.manual_scope = true;
test_block( semantic, &test, block );
check_dup_label( semantic );
if ( semantic->lang == LANG_BCS ) {
if ( func->return_spec != SPEC_VOID && test.flow != FLOW_DEAD ) {
s_diag( semantic, DIAG_POS_ERR, &func->object.pos,
"function missing return statement" );
s_bail( semantic );
}
}
else if ( semantic->lang == LANG_ACS ) {
bool return_stmt_at_end = false;
if ( list_size( &block->stmts ) > 0 ) {
struct node* node = list_tail( &block->stmts );
return_stmt_at_end = ( node->type == NODE_RETURN );
}
if ( func->return_spec != SPEC_VOID && ! return_stmt_at_end ) {
s_diag( semantic, DIAG_POS_ERR, &func->object.pos,
"function missing return statement at end of body" );
s_bail( semantic );
}
}
}

void test_block( struct semantic* semantic, struct stmt_test* test,
struct block* block ) {
if ( ! test->manual_scope ) {
Expand All @@ -78,9 +108,7 @@ void test_block( struct semantic* semantic, struct stmt_test* test,
list_iter_t i;
list_iter_init( &i, &block->stmts );
while ( ! list_end( &i ) ) {
struct stmt_test nested;
s_init_stmt_test( &nested, test );
test_block_item( semantic, &nested, list_data( &i ) );
test_block_item( semantic, test, list_data( &i ) );
list_next( &i );
}
if ( ! test->manual_scope ) {
Expand All @@ -90,6 +118,8 @@ void test_block( struct semantic* semantic, struct stmt_test* test,

void test_block_item( struct semantic* semantic, struct stmt_test* test,
struct node* node ) {
struct stmt_test nested_test;
s_init_stmt_test( &nested_test, test );
switch ( node->type ) {
case NODE_ENUMERATION:
s_test_enumeration( semantic,
Expand All @@ -108,11 +138,11 @@ void test_block_item( struct semantic* semantic, struct stmt_test* test,
( struct func* ) node );
break;
case NODE_CASE:
test_case( semantic, test,
test_case( semantic, &nested_test,
( struct case_label* ) node );
break;
case NODE_CASE_DEFAULT:
test_default_case( semantic, test,
test_default_case( semantic, &nested_test,
( struct case_label* ) node );
break;
case NODE_ASSERT:
Expand All @@ -128,7 +158,21 @@ void test_block_item( struct semantic* semantic, struct stmt_test* test,
( struct type_alias* ) node );
break;
default:
test_stmt( semantic, test, node );
test_stmt( semantic, &nested_test, node );
}
// Flow.
if ( (
node->type == NODE_CASE ||
node->type == NODE_CASE_DEFAULT ) ) {
if ( ( nested_test.flow == FLOW_GOING && test->flow != FLOW_BREAKING ) ||
nested_test.flow == FLOW_RESET ) {
test->flow = FLOW_GOING;
}
}
else {
if ( test->flow == FLOW_GOING ) {
test->flow = nested_test.flow;
}
}
}

Expand Down Expand Up @@ -187,6 +231,18 @@ void test_case( struct semantic* semantic, struct stmt_test* test,
label->next = switch_test->switch_stmt->case_head;
switch_test->switch_stmt->case_head = label;
}
// Flow.
if ( switch_test->switch_stmt->cond.expr &&
switch_test->switch_stmt->cond.expr->folded ) {
if ( label->number->value ==
switch_test->switch_stmt->cond.expr->value ) {
switch_test->found_folded_case = true;
test->flow = FLOW_RESET;
}
else {
test->flow = FLOW_DEAD;
}
}
}

void test_default_case( struct semantic* semantic, struct stmt_test* test,
Expand Down Expand Up @@ -214,6 +270,16 @@ void test_default_case( struct semantic* semantic, struct stmt_test* test,
s_bail( semantic );
}
switch_test->switch_stmt->case_default = label;
// Flow.
if ( switch_test->switch_stmt->cond.expr &&
switch_test->switch_stmt->cond.expr->folded ) {
if ( switch_test->found_folded_case ) {
test->flow = FLOW_DEAD;
}
else {
test->flow = FLOW_RESET;
}
}
}

void test_assert( struct semantic* semantic, struct assert* assert ) {
Expand Down Expand Up @@ -336,9 +402,33 @@ void test_if( struct semantic* semantic, struct stmt_test* test,
struct stmt_test body;
s_init_stmt_test( &body, test );
test_stmt( semantic, &body, stmt->body );
struct stmt_test else_body;
s_init_stmt_test( &else_body, test );
if ( stmt->else_body ) {
s_init_stmt_test( &body, test );
test_stmt( semantic, &body, stmt->else_body );
test_stmt( semantic, &else_body, stmt->else_body );
}
// Flow.
// Constant condition:
if ( stmt->cond.expr && stmt->cond.expr->folded ) {
if ( stmt->cond.expr->value != 0 ) {
test->flow = body.flow;
}
else {
if ( stmt->else_body ) {
test->flow = else_body.flow;
}
}
}
// Runtime condition:
else {
if ( body.flow == FLOW_BREAKING ||
( stmt->else_body && else_body.flow == FLOW_BREAKING ) ) {
test->flow = FLOW_BREAKING;
}
else if ( body.flow == FLOW_DEAD &&
( stmt->else_body && else_body.flow == FLOW_DEAD ) ) {
test->flow = FLOW_DEAD;
}
}
s_pop_scope( semantic );
}
Expand Down Expand Up @@ -402,6 +492,11 @@ void test_switch( struct semantic* semantic, struct stmt_test* test,
if ( stmt->case_head || stmt->case_default ) {
warn_switch_skipped_init( semantic, ( struct block* ) stmt->body );
}
// Flow.
if ( ( test->found_folded_case || stmt->case_default ) &&
body.flow == FLOW_DEAD ) {
test->flow = FLOW_DEAD;
}
s_pop_scope( semantic );
}

Expand Down Expand Up @@ -462,6 +557,41 @@ void test_while( struct semantic* semantic, struct stmt_test* test,
if ( stmt->type == WHILE_DO_WHILE || stmt->type == WHILE_DO_UNTIL ) {
test_cond( semantic, &stmt->cond );
}
// Flow.
if ( stmt->type == WHILE_WHILE || stmt->type == WHILE_UNTIL ) {
if ( stmt->cond.u.node->type == NODE_EXPR &&
stmt->cond.u.expr->folded ) {
if ( ( stmt->type == WHILE_WHILE && stmt->cond.u.expr->value != 0 ) ||
( stmt->type == WHILE_UNTIL && stmt->cond.u.expr->value == 0 ) ) {
if ( body.flow != FLOW_BREAKING ) {
test->flow = FLOW_DEAD;
}
}
}
}
else {
if ( stmt->cond.u.node->type == NODE_EXPR &&
stmt->cond.u.expr->folded ) {
if ( ( stmt->type == WHILE_DO_WHILE &&
stmt->cond.u.expr->value != 0 ) ||
( stmt->type == WHILE_DO_UNTIL &&
stmt->cond.u.expr->value == 0 ) ) {
if ( body.flow != FLOW_BREAKING ) {
test->flow = FLOW_DEAD;
}
}
else {
if ( body.flow == FLOW_DEAD ) {
test->flow = FLOW_DEAD;
}
}
}
else {
if ( body.flow == FLOW_DEAD ) {
test->flow = FLOW_DEAD;
}
}
}
s_pop_scope( semantic );
}

Expand Down Expand Up @@ -510,6 +640,13 @@ void test_for( struct semantic* semantic, struct stmt_test* test,
test_stmt( semantic, &body, stmt->body );
stmt->jump_break = test->jump_break;
stmt->jump_continue = test->jump_continue;
// Flow.
if ( ! stmt->cond.u.node || ( stmt->cond.u.node->type == NODE_EXPR &&
stmt->cond.u.expr->value != 0 ) ) {
if ( body.flow != FLOW_BREAKING ) {
test->flow = FLOW_DEAD;
}
}
s_pop_scope( semantic );
}

Expand Down Expand Up @@ -599,6 +736,7 @@ void test_break( struct semantic* semantic, struct stmt_test* test,
}
stmt->next = target->jump_break;
target->jump_break = stmt;
test->flow = FLOW_BREAKING;
}

void test_continue( struct semantic* semantic, struct stmt_test* test,
Expand All @@ -614,6 +752,7 @@ void test_continue( struct semantic* semantic, struct stmt_test* test,
}
stmt->next = target->jump_continue;
target->jump_continue = stmt;
test->flow = FLOW_DEAD;
}

void test_script_jump( struct semantic* semantic, struct stmt_test* test,
Expand Down Expand Up @@ -650,6 +789,7 @@ void test_return( struct semantic* semantic, struct stmt_test* test,
}
stmt->next = semantic->func_test->returns;
semantic->func_test->returns = stmt;
test->flow = FLOW_DEAD;
}

void test_return_value( struct semantic* semantic, struct stmt_test* test,
Expand Down

0 comments on commit 45be2a4

Please sign in to comment.