diff --git a/src/cc65/stmt.c b/src/cc65/stmt.c index abf8e183d..13399f6b2 100644 --- a/src/cc65/stmt.c +++ b/src/cc65/stmt.c @@ -229,8 +229,10 @@ static int IfStatement (void) g_defcodelabel (Label2); } - /* Done */ - return StmtFlags; + /* Done. We will never return "empty" for an if statement because of side + ** effects when evaluating the condition. + */ + return StmtFlags & ~SF_EMPTY; } @@ -299,8 +301,10 @@ static int DoStatement (void) } } - /* "break" and "continue" are not relevant for the following code */ - StmtFlags &= ~(SF_ANY_BREAK | SF_ANY_CONTINUE); + /* "break" and "continue" are not relevant for the following code. "empty" + ** is removed because of side effects when evaluating the condition. + */ + StmtFlags &= ~(SF_ANY_BREAK | SF_ANY_CONTINUE | SF_EMPTY); /* Done */ return StmtFlags; @@ -393,8 +397,10 @@ static int WhileStatement (void) */ StmtFlags &= ~SF_MASK_UNREACH; } - /* "break" and "continue" are not relevant for the following code */ - StmtFlags &= ~(SF_ANY_BREAK | SF_ANY_CONTINUE); + /* "break" and "continue" are not relevant for the following code. "empty" + ** is removed because of side effects when evaluating the condition. + */ + StmtFlags &= ~(SF_ANY_BREAK | SF_ANY_CONTINUE | SF_EMPTY); /* Done */ return StmtFlags; @@ -602,6 +608,11 @@ static int ForStatement (void) /* Skip the closing paren */ ConsumeRParen (); + /* Output a warning if the loop body is never executed */ + if (TestResult == TESTEXPR_FALSE) { + UnreachableCodeWarning (); + } + /* Loop body */ g_defcodelabel (BodyLabel); StmtFlags = AnyStatement (&PendingToken, 0); @@ -628,10 +639,30 @@ static int ForStatement (void) /* Remove the loop from the loop stack */ DelLoop (); - /* If the condition is always true, any special statements are always - ** executed. Otherwise we don't know. + /* Fix the flags for the loop. */ + if (TestResult == TESTEXPR_TRUE) { + /* If the loop condition is always true, and we do not have a + ** "break" statement, the loop won't terminate. So the only valid + ** "unreach" flag is that for the endless loop. Otherwise - if there + ** is a "break" statement, the code after the loop is reachable. + */ + StmtFlags &= ~SF_MASK_UNREACH; + if (!SF_Any_Break (StmtFlags)) { + StmtFlags |= (SF_OTHER | SF_ANY_OTHER); + } + } else { + /* If the loop condition is not always true, the code after the loop + ** is always reachable. + */ + StmtFlags &= ~SF_MASK_UNREACH; + } + /* "break" and "continue" are not relevant for the following code. "empty" + ** is removed because of side effects when evaluating the condition. */ - return (TestResult == TESTEXPR_TRUE)? StmtFlags : SF_NONE; + StmtFlags &= ~(SF_ANY_BREAK | SF_ANY_CONTINUE | SF_EMPTY); + + /* Done */ + return StmtFlags; } @@ -641,15 +672,23 @@ static int CompoundStatement (int* PendingToken, struct SwitchCtrl* Switch) ** function returns true if the last statement was a break or return. */ { + int OldStack; + unsigned OldBlockStackSize; int StmtFlags; - /* Remember the stack at block entry */ - int OldStack = StackPtr; - unsigned OldBlockStackSize = CollCount (&CurrentFunc->LocalsBlockStack); - /* Skip '{' */ NextToken (); + /* If the closing curly bracket follows we have an empty statement */ + if (CurTok.Tok == TOK_RCURLY) { + NextToken (); + return SF_EMPTY; + } + + /* Remember the stack at block entry */ + OldStack = StackPtr; + OldBlockStackSize = CollCount (&CurrentFunc->LocalsBlockStack); + /* Enter a new lexical level */ EnterBlockLevel (); @@ -742,36 +781,41 @@ int StatementBlock (struct SwitchCtrl* Switch) LineInfo* LI1 = UseLineInfo (GetDiagnosticLI ()); int StmtFlags1 = AnyStatement (0, Switch); int Unreachable = 0; /* True if code is unreachable */ - int UnreachableWarning = 0; /* True if warning was output */ + int Warning = 0; /* True if warning was output */ while (CurTok.Tok != TOK_RCURLY && CurTok.Tok != TOK_CEOF) { LineInfo* LI2 = UseLineInfo (GetDiagnosticLI ()); int StmtFlags2 = AnyStatement (0, Switch); - if (!UnreachableWarning) { - /* If this statement is not already unreachable, check if the - ** previous statement made it unreachable. - */ - if (!Unreachable) { - Unreachable = SF_Unreach (StmtFlags1); - } - /* If the previous statement made this one unreachable, but - ** this one has a label, it is not unreachable. - */ - if (Unreachable && SF_Label (StmtFlags2)) { - Unreachable = 0; - } - /* If this statement is unreachable but not the empty - ** statement, output a warning. - */ - if (Unreachable && !SF_Empty (StmtFlags2)) { - LIUnreachableCodeWarning (LI2); - UnreachableWarning = 1; - } + + /* If this statement is not already unreachable, check if the + ** previous statement made it unreachable. + */ + if (!Unreachable) { + Unreachable = SF_Unreach (StmtFlags1); } + /* If the previous statement made this one unreachable, but this + ** one has a label, it is not unreachable. + */ + if (Unreachable && SF_Label (StmtFlags2)) { + Unreachable = 0; + } + /* If this statement is unreachable but not the empty statement, + ** and we didn't give a warning before, to that now + */ + if (Unreachable && !SF_Empty (StmtFlags2) && !Warning) { + LIUnreachableCodeWarning (LI2); + Warning = 1; + } + + /* If the current statement wasn't unreachable update the flags */ + if (!Unreachable) { + StmtFlags1 = SF_Any (StmtFlags1) | StmtFlags2; + } + + /* Prepare for the next round */ if (LI1) { ReleaseLineInfo (LI1); } LI1 = LI2; - StmtFlags1 = (StmtFlags1 & SF_MASK_ANY) | StmtFlags2; } if (LI1) { ReleaseLineInfo (LI1); diff --git a/test/misc/Makefile b/test/misc/Makefile index a595d82a9..b131fef2e 100644 --- a/test/misc/Makefile +++ b/test/misc/Makefile @@ -157,6 +157,11 @@ $(WORKDIR)/flow-do-01.$1.$2.prg: flow-do-01.c $(ISEQUAL) | $(WORKDIR) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLOUT) 2>$(WORKDIR)/flow-do-01.$1.$2.out $(ISEQUAL) $(WORKDIR)/flow-do-01.$1.$2.out flow-do-01.ref +$(WORKDIR)/flow-for-01.$1.$2.prg: flow-for-01.c $(ISEQUAL) | $(WORKDIR) + $(if $(QUIET),echo misc/flow-for-01.$1.$2.prg) + $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLOUT) 2>$(WORKDIR)/flow-for-01.$1.$2.out + $(ISEQUAL) $(WORKDIR)/flow-for-01.$1.$2.out flow-for-01.ref + $(WORKDIR)/flow-if-01.$1.$2.prg: flow-if-01.c $(ISEQUAL) | $(WORKDIR) $(if $(QUIET),echo misc/flow-if-01.$1.$2.prg) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLOUT) 2>$(WORKDIR)/flow-if-01.$1.$2.out diff --git a/test/misc/flow-for-01.c b/test/misc/flow-for-01.c new file mode 100644 index 000000000..c64dcf6b1 --- /dev/null +++ b/test/misc/flow-for-01.c @@ -0,0 +1,90 @@ +int a; + +static int f1(void) +{ + for (;;) { + ++a; + } + /* Unreachable */ + a = 2; + return a; +} + +static int f2(void) +{ + for (;1;) { + ++a; + } + /* Unreachable */ + a = 2; + return a; +} + +static int f3(void) +{ + for (;;) { + ++a; + if (a == 5) break; + } + /* Reachable */ + a = 2; + return a; +} + +static int f4(void) +{ + for (;;) { + ++a; + return a; + } + /* Unreachable */ + a = 2; +} + +static int f5(void) +{ + for (;0;) { + /* Unreachable */ + ++a; + return a; + } + /* Reachable */ + a = 2; + return 0; +} + +static int f6(void) +{ + for (;;) { + ++a; + if (a == 4) goto L; + return a; + } + /* Reachable via L */ +L: a = 2; +} + +static int f7(void) +{ + for (;0;) { + /* Unreachable but no warning */ + } + a = 2; + return a; +} + +static int f8(void) +{ + for (;a;) { + return a; + } + /* Reachable */ + a = 2; + return a; +} + +int main(void) +{ + return f1() + f2() + f3() + f4() + f5() + f6() + f7() + f8(); +} + diff --git a/test/misc/flow-for-01.ref b/test/misc/flow-for-01.ref new file mode 100644 index 000000000..4425066ac --- /dev/null +++ b/test/misc/flow-for-01.ref @@ -0,0 +1,4 @@ +flow-for-01.c:9: Warning: Unreachable code +flow-for-01.c:19: Warning: Unreachable code +flow-for-01.c:41: Warning: Unreachable code +flow-for-01.c:48: Warning: Unreachable code diff --git a/test/misc/flow-while-01.c b/test/misc/flow-while-01.c index 91d1454a9..3c354942c 100644 --- a/test/misc/flow-while-01.c +++ b/test/misc/flow-while-01.c @@ -63,8 +63,18 @@ static int f6(void) return a; } -int main(void) +static int f7(void) { - return f1() + f2() + f3() + f4() + f5() + f6(); + while (a) { + return a; + } + /* Reachable */ + a = 2; + return a; +} + +int main(void) +{ + return f1() + f2() + f3() + f4() + f5() + f6() + f7(); } diff --git a/test/misc/flow-while-02.c b/test/misc/flow-while-02.c index 11ea2c0c4..17f835005 100644 --- a/test/misc/flow-while-02.c +++ b/test/misc/flow-while-02.c @@ -22,7 +22,7 @@ static int f2(void) /* Unreachable */ break; } - /* Unreachable but no warning */ + /* Unreachable */ a = 2; return a; } diff --git a/test/misc/flow-while-02.ref b/test/misc/flow-while-02.ref index 2609f395a..ad5587257 100644 --- a/test/misc/flow-while-02.ref +++ b/test/misc/flow-while-02.ref @@ -1,6 +1,7 @@ flow-while-02.c:8: Warning: Unreachable code flow-while-02.c:12: Warning: Unreachable code flow-while-02.c:23: Warning: Unreachable code +flow-while-02.c:26: Warning: Unreachable code flow-while-02.c:38: Warning: Unreachable code flow-while-02.c:50: Warning: Unreachable code flow-while-02.c:66: Warning: Unreachable code