+static bool expression_returns(expression_t const *const expr)
+{
+ switch (expr->kind) {
+ case EXPR_CALL: {
+ expression_t const *const func = expr->call.function;
+ if (func->kind == EXPR_REFERENCE) {
+ entity_t *entity = func->reference.entity;
+ if (entity->kind == ENTITY_FUNCTION
+ && entity->declaration.modifiers & DM_NORETURN)
+ return false;
+ }
+
+ if (!expression_returns(func))
+ return false;
+
+ for (call_argument_t const* arg = expr->call.arguments; arg != NULL; arg = arg->next) {
+ if (!expression_returns(arg->expression))
+ return false;
+ }
+
+ return true;
+ }
+
+ case EXPR_REFERENCE:
+ case EXPR_REFERENCE_ENUM_VALUE:
+ case EXPR_CONST:
+ case EXPR_CHARACTER_CONSTANT:
+ case EXPR_WIDE_CHARACTER_CONSTANT:
+ case EXPR_STRING_LITERAL:
+ case EXPR_WIDE_STRING_LITERAL:
+ case EXPR_COMPOUND_LITERAL: // TODO descend into initialisers
+ case EXPR_LABEL_ADDRESS:
+ case EXPR_CLASSIFY_TYPE:
+ case EXPR_SIZEOF: // TODO handle obscure VLA case
+ case EXPR_ALIGNOF:
+ case EXPR_FUNCNAME:
+ case EXPR_BUILTIN_SYMBOL:
+ case EXPR_BUILTIN_CONSTANT_P:
+ case EXPR_BUILTIN_PREFETCH:
+ case EXPR_OFFSETOF:
+ case EXPR_INVALID:
+ case EXPR_STATEMENT: // TODO implement
+ return true;
+
+ case EXPR_CONDITIONAL:
+ // TODO handle constant expression
+ return
+ expression_returns(expr->conditional.condition) && (
+ expression_returns(expr->conditional.true_expression) ||
+ expression_returns(expr->conditional.false_expression)
+ );
+
+ case EXPR_SELECT:
+ return expression_returns(expr->select.compound);
+
+ case EXPR_ARRAY_ACCESS:
+ return
+ expression_returns(expr->array_access.array_ref) &&
+ expression_returns(expr->array_access.index);
+
+ case EXPR_VA_START:
+ return expression_returns(expr->va_starte.ap);
+
+ 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");
+}
+