+ case EXPR_VA_ARG:
+ return expression_returns(expr->va_arge.ap);
+
+ EXPR_UNARY_CASES_MANDATORY
+ return expression_returns(expr->unary.value);
+
+ case EXPR_UNARY_THROW:
+ return false;
+
+ EXPR_BINARY_CASES
+ // TODO handle constant lhs of && and ||
+ return
+ expression_returns(expr->binary.left) &&
+ expression_returns(expr->binary.right);
+
+ case EXPR_UNKNOWN:
+ break;
+ }
+
+ panic("unhandled expression");
+}
+
+static bool noreturn_candidate;
+
+static void check_reachable(statement_t *const stmt)
+{
+ if (stmt->base.reachable)
+ return;
+ if (stmt->kind != STATEMENT_DO_WHILE)
+ stmt->base.reachable = true;
+
+ statement_t *last = stmt;
+ statement_t *next;
+ switch (stmt->kind) {
+ case STATEMENT_INVALID:
+ case STATEMENT_EMPTY:
+ case STATEMENT_DECLARATION:
+ case STATEMENT_LOCAL_LABEL:
+ case STATEMENT_ASM:
+ next = stmt->base.next;
+ break;
+
+ case STATEMENT_COMPOUND:
+ next = stmt->compound.statements;
+ break;
+
+ case STATEMENT_RETURN:
+ noreturn_candidate = false;
+ return;
+
+ case STATEMENT_IF: {
+ if_statement_t const* const ifs = &stmt->ifs;
+ int const val = determine_truth(ifs->condition);
+
+ if (val >= 0)
+ check_reachable(ifs->true_statement);
+
+ if (val > 0)
+ return;
+
+ if (ifs->false_statement != NULL) {
+ check_reachable(ifs->false_statement);
+ return;
+ }
+
+ next = stmt->base.next;
+ break;
+ }
+
+ case STATEMENT_SWITCH: {
+ switch_statement_t const *const switchs = &stmt->switchs;
+ expression_t const *const expr = switchs->expression;
+
+ if (is_constant_expression(expr)) {
+ long const val = fold_constant(expr);
+ case_label_statement_t * defaults = NULL;
+ for (case_label_statement_t *i = switchs->first_case; i != NULL; i = i->next) {
+ if (i->expression == NULL) {
+ defaults = i;
+ continue;
+ }
+
+ if (i->first_case <= val && val <= i->last_case) {
+ check_reachable((statement_t*)i);
+ return;
+ }
+ }
+
+ if (defaults != NULL) {
+ check_reachable((statement_t*)defaults);
+ return;
+ }
+ } else {
+ bool has_default = false;
+ for (case_label_statement_t *i = switchs->first_case; i != NULL; i = i->next) {
+ if (i->expression == NULL)
+ has_default = true;
+
+ check_reachable((statement_t*)i);
+ }
+
+ if (has_default)
+ return;
+ }
+
+ next = stmt->base.next;
+ break;
+ }
+
+ case STATEMENT_EXPRESSION: {
+ /* Check for noreturn function call */
+ expression_t const *const expr = stmt->expression.expression;
+ if (!expression_returns(expr))
+ return;
+
+ next = stmt->base.next;
+ break;
+ }
+
+ case STATEMENT_CONTINUE: {
+ statement_t *parent = stmt;
+ for (;;) {
+ parent = parent->base.parent;
+ if (parent == NULL) /* continue not within loop */
+ return;
+
+ next = parent;
+ switch (parent->kind) {
+ case STATEMENT_WHILE: goto continue_while;
+ case STATEMENT_DO_WHILE: goto continue_do_while;
+ case STATEMENT_FOR: goto continue_for;
+
+ default: break;
+ }
+ }
+ }
+
+ case STATEMENT_BREAK: {
+ statement_t *parent = stmt;
+ for (;;) {
+ parent = parent->base.parent;
+ if (parent == NULL) /* break not within loop/switch */
+ return;
+
+ switch (parent->kind) {
+ case STATEMENT_SWITCH:
+ case STATEMENT_WHILE:
+ case STATEMENT_DO_WHILE:
+ case STATEMENT_FOR:
+ last = parent;
+ next = parent->base.next;
+ goto found_break_parent;
+
+ default: break;
+ }
+ }
+found_break_parent:
+ break;
+ }
+
+ case STATEMENT_GOTO:
+ if (stmt->gotos.expression) {
+ statement_t *parent = stmt->base.parent;
+ if (parent == NULL) /* top level goto */
+ return;
+ next = parent;
+ } else {
+ next = stmt->gotos.label->statement;
+ if (next == NULL) /* missing label */
+ return;
+ }
+ break;
+
+ case STATEMENT_LABEL:
+ next = stmt->label.statement;
+ break;
+
+ case STATEMENT_CASE_LABEL:
+ next = stmt->case_label.statement;
+ break;
+
+ case STATEMENT_WHILE: {
+ while_statement_t const *const whiles = &stmt->whiles;
+ int const val = determine_truth(whiles->condition);
+
+ if (val >= 0)
+ check_reachable(whiles->body);
+
+ if (val > 0)
+ return;
+
+ next = stmt->base.next;
+ break;
+ }
+
+ case STATEMENT_DO_WHILE:
+ next = stmt->do_while.body;
+ break;
+
+ case STATEMENT_FOR: {
+ for_statement_t *const fors = &stmt->fors;
+
+ if (fors->condition_reachable)
+ return;
+ fors->condition_reachable = true;
+
+ expression_t const *const cond = fors->condition;
+ int const val =
+ cond == NULL ? 1 : determine_truth(cond);
+
+ if (val >= 0)
+ check_reachable(fors->body);
+
+ if (val > 0)
+ return;
+
+ next = stmt->base.next;
+ break;
+ }
+
+ case STATEMENT_MS_TRY: {
+ ms_try_statement_t const *const ms_try = &stmt->ms_try;
+ check_reachable(ms_try->try_statement);
+ next = ms_try->final_statement;
+ break;
+ }
+
+ case STATEMENT_LEAVE: {
+ statement_t *parent = stmt;
+ for (;;) {
+ parent = parent->base.parent;
+ if (parent == NULL) /* __leave not within __try */
+ return;
+
+ if (parent->kind == STATEMENT_MS_TRY) {
+ last = parent;
+ next = parent->ms_try.final_statement;
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ while (next == NULL) {
+ next = last->base.parent;
+ if (next == NULL) {
+ noreturn_candidate = false;
+
+ type_t *const type = current_function->base.type;
+ assert(is_type_function(type));
+ type_t *const ret = skip_typeref(type->function.return_type);
+ if (warning.return_type &&
+ !is_type_atomic(ret, ATOMIC_TYPE_VOID) &&
+ is_type_valid(ret) &&
+ !is_sym_main(current_function->base.base.symbol)) {
+ warningf(&stmt->base.source_position,
+ "control reaches end of non-void function");
+ }
+ return;
+ }
+
+ switch (next->kind) {
+ case STATEMENT_INVALID:
+ case STATEMENT_EMPTY:
+ case STATEMENT_DECLARATION:
+ case STATEMENT_LOCAL_LABEL:
+ case STATEMENT_EXPRESSION:
+ case STATEMENT_ASM:
+ case STATEMENT_RETURN:
+ case STATEMENT_CONTINUE:
+ case STATEMENT_BREAK:
+ case STATEMENT_GOTO:
+ case STATEMENT_LEAVE:
+ panic("invalid control flow in function");
+
+ case STATEMENT_COMPOUND:
+ case STATEMENT_IF:
+ case STATEMENT_SWITCH:
+ case STATEMENT_LABEL:
+ case STATEMENT_CASE_LABEL:
+ last = next;
+ next = next->base.next;
+ break;
+
+ case STATEMENT_WHILE: {
+continue_while:
+ if (next->base.reachable)
+ return;
+ next->base.reachable = true;
+
+ while_statement_t const *const whiles = &next->whiles;
+ int const val = determine_truth(whiles->condition);
+
+ if (val >= 0)
+ check_reachable(whiles->body);
+
+ if (val > 0)
+ return;
+
+ last = next;
+ next = next->base.next;
+ break;
+ }
+
+ case STATEMENT_DO_WHILE: {
+continue_do_while:
+ if (next->base.reachable)
+ return;
+ next->base.reachable = true;
+
+ do_while_statement_t const *const dw = &next->do_while;
+ int const val = determine_truth(dw->condition);
+
+ if (val >= 0)
+ check_reachable(dw->body);
+
+ if (val > 0)
+ return;
+
+ last = next;
+ next = next->base.next;
+ break;
+ }
+
+ case STATEMENT_FOR: {
+continue_for:;
+ for_statement_t *const fors = &next->fors;
+
+ fors->step_reachable = true;
+
+ if (fors->condition_reachable)
+ return;
+ fors->condition_reachable = true;
+
+ expression_t const *const cond = fors->condition;
+ int const val =
+ cond == NULL ? 1 : determine_truth(cond);
+
+ if (val >= 0)
+ check_reachable(fors->body);
+
+ if (val > 0)
+ return;
+
+ last = next;
+ next = next->base.next;
+ break;
+ }
+
+ case STATEMENT_MS_TRY:
+ last = next;
+ next = next->ms_try.final_statement;
+ break;
+ }
+ }
+
+ check_reachable(next);
+}
+
+static void check_unreachable(statement_t* const stmt, void *const env)
+{
+ (void)env;
+
+ switch (stmt->kind) {
+ case STATEMENT_DO_WHILE:
+ if (!stmt->base.reachable) {
+ expression_t const *const cond = stmt->do_while.condition;
+ if (determine_truth(cond) >= 0) {
+ warningf(&cond->base.source_position,
+ "condition of do-while-loop is unreachable");
+ }
+ }
+ return;
+
+ case STATEMENT_FOR: {
+ for_statement_t const* const fors = &stmt->fors;
+
+ // if init and step are unreachable, cond is unreachable, too
+ if (!stmt->base.reachable && !fors->step_reachable) {
+ warningf(&stmt->base.source_position, "statement is unreachable");
+ } else {
+ if (!stmt->base.reachable && fors->initialisation != NULL) {
+ warningf(&fors->initialisation->base.source_position,
+ "initialisation of for-statement is unreachable");
+ }
+
+ if (!fors->condition_reachable && fors->condition != NULL) {
+ warningf(&fors->condition->base.source_position,
+ "condition of for-statement is unreachable");
+ }
+
+ if (!fors->step_reachable && fors->step != NULL) {
+ warningf(&fors->step->base.source_position,
+ "step of for-statement is unreachable");
+ }
+ }
+ return;
+ }
+
+ case STATEMENT_COMPOUND:
+ if (stmt->compound.statements != NULL)
+ return;
+ /* FALLTHROUGH*/
+
+ default:
+ if (!stmt->base.reachable)
+ warningf(&stmt->base.source_position, "statement is unreachable");
+ return;
+ }
+}
+
+static void parse_external_declaration(void)
+{
+ /* function-definitions and declarations both start with declaration
+ * specifiers */
+ declaration_specifiers_t specifiers;
+ memset(&specifiers, 0, sizeof(specifiers));
+
+ add_anchor_token(';');
+ parse_declaration_specifiers(&specifiers);
+ rem_anchor_token(';');
+
+ /* must be a declaration */
+ if (token.type == ';') {
+ parse_anonymous_declaration_rest(&specifiers);
+ return;
+ }
+
+ add_anchor_token(',');
+ add_anchor_token('=');
+ add_anchor_token(';');
+ add_anchor_token('{');
+
+ /* declarator is common to both function-definitions and declarations */
+ entity_t *ndeclaration = parse_declarator(&specifiers, /*may_be_abstract=*/false, false);
+
+ rem_anchor_token('{');
+ rem_anchor_token(';');
+ rem_anchor_token('=');
+ rem_anchor_token(',');
+
+ /* must be a declaration */
+ switch (token.type) {
+ case ',':
+ case ';':
+ case '=':
+ parse_declaration_rest(ndeclaration, &specifiers, record_entity);
+ return;
+ }
+
+ /* must be a function definition */
+ parse_kr_declaration_list(ndeclaration);
+
+ if (token.type != '{') {
+ parse_error_expected("while parsing function definition", '{', NULL);
+ eat_until_matching_token(';');
+ return;
+ }
+
+ assert(is_declaration(ndeclaration));
+ type_t *type = skip_typeref(ndeclaration->declaration.type);
+
+ if (!is_type_function(type)) {
+ if (is_type_valid(type)) {