diff mbox series

[v4] diffcore-break: use a goto instead of a redundant if statement

Message ID 20190929204322.1244907-1-alexhenrie24@gmail.com (mailing list archive)
State New, archived
Headers show
Series [v4] diffcore-break: use a goto instead of a redundant if statement | expand

Commit Message

Alex Henrie Sept. 29, 2019, 8:43 p.m. UTC
The condition "if (q->nr <= j)" checks whether the loop exited normally
or via a break statement. This check can be avoided by replacing the
jump out of the inner loop with a jump to the end of the outer loop.

With the break replaced by a goto, the two diff_q calls then can be
replaced with a single diff_q call outside of the outer if statement.

Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>
---
 diffcore-break.c | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

Comments

Junio C Hamano Sept. 30, 2019, 1:36 a.m. UTC | #1
Alex Henrie <alexhenrie24@gmail.com> writes:

> The condition "if (q->nr <= j)" checks whether the loop exited normally
> or via a break statement. This check can be avoided by replacing the
> jump out of the inner loop with a jump to the end of the outer loop.
>
> With the break replaced by a goto, the two diff_q calls then can be
> replaced with a single diff_q call outside of the outer if statement.

I doubt that it is a good idea to do these two things.  Especially I
do not see why the latter makes the resulting code better.

> Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>
> ---
>  diffcore-break.c | 14 ++++++--------
>  1 file changed, 6 insertions(+), 8 deletions(-)
>
> diff --git a/diffcore-break.c b/diffcore-break.c
> index 875aefd3fe..ee7519d959 100644
> --- a/diffcore-break.c
> +++ b/diffcore-break.c
> @@ -286,17 +286,15 @@ void diffcore_merge_broken(void)
>  					/* Peer survived.  Merge them */
>  					merge_broken(p, pp, &outq);
>  					q->queue[j] = NULL;
> -					break;
> +					goto next;
>  				}
>  			}
> -			if (q->nr <= j)
> -				/* The peer did not survive, so we keep
> -				 * it in the output.
> -				 */
> -				diff_q(&outq, p);
> +			/* The peer did not survive, so we keep
> +			 * it in the output.
> +			 */
>  		}
> -		else
> -			diff_q(&outq, p);
> +		diff_q(&outq, p);
> +next:;
>  	}
>  	free(q->queue);
>  	*q = outq;
Alex Henrie Sept. 30, 2019, 7:45 a.m. UTC | #2
On Sun, Sep 29, 2019 at 7:36 PM Junio C Hamano <gitster@pobox.com> wrote:
>
> Alex Henrie <alexhenrie24@gmail.com> writes:
>
> > The condition "if (q->nr <= j)" checks whether the loop exited normally
> > or via a break statement. This check can be avoided by replacing the
> > jump out of the inner loop with a jump to the end of the outer loop.
> >
> > With the break replaced by a goto, the two diff_q calls then can be
> > replaced with a single diff_q call outside of the outer if statement.
>
> I doubt that it is a good idea to do these two things.  Especially I
> do not see why the latter makes the resulting code better.

Well, I admit that code clarity is somewhat subjective. To me it's not
obvious that "if (q->nr <= j)" means "if the loop exited normally",
but a goto does make it obvious. (And it's definitely more clear to
scan-build, which complains about a possible memory leak when an if
statement is used but does not complain when the if statement is
replaced with a goto.)

As far as the diff_q calls, I think that having one call instead of
two is slightly more readable, but I don't care very much about it.
I'd be happy to drop that change from the next version of the patch.

-Alex
Junio C Hamano Sept. 30, 2019, 9:48 a.m. UTC | #3
Alex Henrie <alexhenrie24@gmail.com> writes:

> Well, I admit that code clarity is somewhat subjective. To me it's not
> obvious that "if (q->nr <= j)" means "if the loop exited normally",

I actually do not have too much problem with this side of the
equation.  I however do see problem with squashing the two diff_q
calls that _happens_ to be textually the same but is made from two
logically separate codepaths (cases B.1 and C, in the message you
are responding to but omitted from quote).  After all, you did not
even realize you introduced a new bug by doing so before CB pointed
it out, no?  A rewrite like that that easily allows a new bug to
slip in hardly qualifies as making code "more clear and readable".

> but a goto does make it obvious. (And it's definitely more clear to
> scan-build, which complains about a possible memory leak when an if
> statement is used but does not complain when the if statement is
> replaced with a goto.)
>
> As far as the diff_q calls, I think that having one call instead of
> two is slightly more readable, but I don't care very much about it.
> I'd be happy to drop that change from the next version of the patch.
>
> -Alex
Alex Henrie Sept. 30, 2019, 5:36 p.m. UTC | #4
On Mon, Sep 30, 2019 at 3:48 AM Junio C Hamano <gitster@pobox.com> wrote:
>
> Alex Henrie <alexhenrie24@gmail.com> writes:
>
> > Well, I admit that code clarity is somewhat subjective. To me it's not
> > obvious that "if (q->nr <= j)" means "if the loop exited normally",
>
> I actually do not have too much problem with this side of the
> equation.  I however do see problem with squashing the two diff_q
> calls that _happens_ to be textually the same but is made from two
> logically separate codepaths (cases B.1 and C, in the message you
> are responding to but omitted from quote).  After all, you did not
> even realize you introduced a new bug by doing so before CB pointed
> it out, no?  A rewrite like that that easily allows a new bug to
> slip in hardly qualifies as making code "more clear and readable".

Sure, let's keep the two diff_q calls. I'll send a new patch.

To me at least it was not clear what code is executed if the peer
survives. The fact that I put the goto label in the wrong place the
first time only underscores the difficulty of understanding this
function. But with a goto (and with its label in the correct place),
the execution path is obvious.

Thank you for being willing to look at this code with me, and thank
you for your feedback!

-Alex
Junio C Hamano Oct. 2, 2019, 5:55 a.m. UTC | #5
Alex Henrie <alexhenrie24@gmail.com> writes:

> On Mon, Sep 30, 2019 at 3:48 AM Junio C Hamano <gitster@pobox.com> wrote:
>>
>> Alex Henrie <alexhenrie24@gmail.com> writes:
>>
>> > Well, I admit that code clarity is somewhat subjective. To me it's not
>> > obvious that "if (q->nr <= j)" means "if the loop exited normally",

I agree that it is subjective, but "if counter hasn't run beyond the
limit" immediately after a loop is a pretty-well established idiom
to see if we broke out prematurely.

> To me at least it was not clear what code is executed if the peer
> survives. The fact that I put the goto label in the wrong place the
> first time only underscores the difficulty of understanding this
> function. But with a goto (and with its label in the correct place),
> the execution path is obvious.

Well, the fact that the version with goto jumping to a wrong place
was so easy to spot as buggy might be an indication that goto made
it obvious to CB, but clearly use of goto did not make logic obvious
at least to the older version of you who wrote the buggy code.

Well I am being a bit mean in the above ;-) A more fair statement
would be that your bug did not come from goto; I think what
contributed more to the logic flaw in your earlier round was the
merging of two diff_q() calls from unrelated codepath.  Without that
change, and with the knowledge of "did the loop terminate early"
idiom, the version without 'goto' is obvious, and with only 'goto'
change, that original obviousness would not diminish.
diff mbox series

Patch

diff --git a/diffcore-break.c b/diffcore-break.c
index 875aefd3fe..ee7519d959 100644
--- a/diffcore-break.c
+++ b/diffcore-break.c
@@ -286,17 +286,15 @@  void diffcore_merge_broken(void)
 					/* Peer survived.  Merge them */
 					merge_broken(p, pp, &outq);
 					q->queue[j] = NULL;
-					break;
+					goto next;
 				}
 			}
-			if (q->nr <= j)
-				/* The peer did not survive, so we keep
-				 * it in the output.
-				 */
-				diff_q(&outq, p);
+			/* The peer did not survive, so we keep
+			 * it in the output.
+			 */
 		}
-		else
-			diff_q(&outq, p);
+		diff_q(&outq, p);
+next:;
 	}
 	free(q->queue);
 	*q = outq;