diff --git a/engine/qclib/qcc.h b/engine/qclib/qcc.h index 42d8f79a0..3c9257fd5 100644 --- a/engine/qclib/qcc.h +++ b/engine/qclib/qcc.h @@ -567,6 +567,7 @@ enum { WARN_EXTRAPRECACHE, WARN_NOTPRECACHED, WARN_DEADCODE, + WARN_UNREACHABLECODE, WARN_NOTSTANDARDBEHAVIOUR, WARN_INEFFICIENTPLUSPLUS, WARN_DUPLICATEPRECOMPILER, diff --git a/engine/qclib/qcc_pr_comp.c b/engine/qclib/qcc_pr_comp.c index be9a84b48..fab56cfc5 100644 --- a/engine/qclib/qcc_pr_comp.c +++ b/engine/qclib/qcc_pr_comp.c @@ -5231,6 +5231,87 @@ void QCC_CompoundJumps(int first, int last) } } #endif + +void QCC_CheckForDeadAndMissingReturns(int first, int last, int rettype) +{ + int st, st2; + + if (statements[last-1].op == OP_DONE) + last--; //don't want the done + + if (rettype != ev_void) + if (statements[last-1].op != OP_RETURN) + { + QCC_PR_ParseWarning(WARN_MISSINGRETURN, "%s: not all control paths return a value", pr_scope->name ); + return; + } + + for (st = first; st < last; st++) + { + if (statements[st].op == OP_RETURN) + { + st++; + if (st == last) + continue; //erm... end of function doesn't count as unreachable. + + if (statements[st].op == OP_GOTO) //inefficient compiler, we can ignore this. + continue; + + + //make sure something goes to just after this return. + for (st2 = first; st2 < last; st2++) + { + if (pr_opcodes[statements[st2].op].type_a == NULL) + { + if (st2 + (signed)statements[st2].a == st) + break; + } + if (pr_opcodes[statements[st2].op].type_b == NULL) + { + if (st2 + (signed)statements[st2].b == st) + break; + } + if (pr_opcodes[statements[st2].op].type_c == NULL) + { + if (st2 + (signed)statements[st2].c == st) + break; + } + } + if (st2 == last) + { + QCC_PR_ParseWarning(WARN_UNREACHABLECODE, "%s: contains unreachable code", pr_scope->name ); + } + continue; + } + if (rettype != ev_void) + { + if (pr_opcodes[statements[st].op].type_a == NULL) + { + if (st + (signed)statements[st].a == last) + { + QCC_PR_ParseWarning(WARN_MISSINGRETURN, "%s: not all control paths return a value", pr_scope->name ); + return; + } + } + if (pr_opcodes[statements[st].op].type_b == NULL) + { + if (st + (signed)statements[st].b == last) + { + QCC_PR_ParseWarning(WARN_MISSINGRETURN, "%s: not all control paths return a value", pr_scope->name ); + return; + } + } + if (pr_opcodes[statements[st].op].type_c == NULL) + { + if (st + (signed)statements[st].c == last) + { + QCC_PR_ParseWarning(WARN_MISSINGRETURN, "%s: not all control paths return a value", pr_scope->name ); + return; + } + } + } + } +} /* //goes through statements, if it sees a matching statement earlier, it'll strim out the current. void QCC_CommonSubExpressionRemoval(int first, int last) @@ -5584,9 +5665,9 @@ QCC_function_t *QCC_PR_ParseImmediateStatements (QCC_type_t *type) QCC_FreeTemps(); // this is cheap - if (type->aux_type->type) - if (statements[numstatements - 1].op != OP_RETURN) - QCC_PR_ParseWarning(WARN_MISSINGRETURN, "%s: not all control paths return a value", pr_scope->name ); +// if (type->aux_type->type) +// if (statements[numstatements - 1].op != OP_RETURN) +// QCC_PR_ParseWarning(WARN_MISSINGRETURN, "%s: not all control paths return a value", pr_scope->name ); if (f->code == numstatements) needsdone = true; @@ -5639,6 +5720,8 @@ QCC_function_t *QCC_PR_ParseImmediateStatements (QCC_type_t *type) // if (opt_comexprremoval) // QCC_CommonSubExpressionRemoval(f->code, numstatements); + QCC_CheckForDeadAndMissingReturns(f->code, numstatements, type->aux_type->type); + QCC_RemapLockedTemps(f->code, numstatements); locals_end = numpr_globals;