diff mbox series

[2/3] reftable: remove unreachable "return" statements

Message ID patch-2.3-7a6a69314b5-20220111T163908Z-avarab@gmail.com (mailing list archive)
State New, archived
Headers show
Series Fix SunCC compiler complaints new in v2.35.0-rc0 | expand

Commit Message

Ævar Arnfjörð Bjarmason Jan. 11, 2022, 4:40 p.m. UTC
Remove unreachable return statements added in acb533440fc (reftable:
implement refname validation, 2021-10-07) and f14bd719349 (reftable:
write reftable files, 2021-10-07).

This avoids the following warnings on SunCC 12.5 on
gcc211.fsffrance.org:

    "reftable/refname.c", line 135: warning: statement not reached
    "reftable/refname.c", line 135: warning: statement not reached

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 reftable/refname.c | 1 -
 reftable/writer.c  | 1 -
 2 files changed, 2 deletions(-)

Comments

Taylor Blau Jan. 11, 2022, 7:16 p.m. UTC | #1
On Tue, Jan 11, 2022 at 05:40:22PM +0100, Ævar Arnfjörð Bjarmason wrote:
> Remove unreachable return statements added in acb533440fc (reftable:
> implement refname validation, 2021-10-07) and f14bd719349 (reftable:
> write reftable files, 2021-10-07).
>
> This avoids the following warnings on SunCC 12.5 on
> gcc211.fsffrance.org:
>
>     "reftable/refname.c", line 135: warning: statement not reached
>     "reftable/refname.c", line 135: warning: statement not reached

Interesting. From a cursory reading, I agree with the assessment of
at least my compiler that these return statements are both unnecessary,
but...

> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
> ---
>  reftable/refname.c | 1 -
>  reftable/writer.c  | 1 -
>  2 files changed, 2 deletions(-)
>
> diff --git a/reftable/refname.c b/reftable/refname.c
> index 95734969324..136001bc2c7 100644
> --- a/reftable/refname.c
> +++ b/reftable/refname.c
> @@ -132,7 +132,6 @@ static int validate_refname(const char *name)
>  			return REFTABLE_REFNAME_ERROR;
>  		name = next + 1;
>  	}
> -	return 0;
>  }

In this case the loop inside of validate_refname() should always
terminate the function within the loop body. But removing this return
statement here relies on the compiler to determine that fact.

I could well imagine on the other end of the spectrum there exists a
compiler which _doesn't_ make this inference pass, and would complain
about the opposite thing as you're reporting from SunCC (i.e., that this
function which returns something other than void does not have a return
statement outside of the loop).

So in that sense, I disagree with the guidance of SunCC's warning. In
other words: by quelching this warning under one compiler, are we
introducing a new warning under a different/less advanced compiler?

>  int validate_ref_record_addition(struct reftable_table tab,
> diff --git a/reftable/writer.c b/reftable/writer.c
> index 35c8649c9b7..70a7bf142a2 100644
> --- a/reftable/writer.c
> +++ b/reftable/writer.c
> @@ -39,7 +39,6 @@ writer_reftable_block_stats(struct reftable_writer *w, uint8_t typ)
>  		return &w->stats.log_stats;
>  	}
>  	abort();
> -	return NULL;
>  }

Here I'm less skeptical, since it's almost certain that any compiler
would recognize this call to abort() as terminating the whole program.
So it should be able to infer that anything after it is unreachable.

But even though I'm less skeptical, I'm not sure that I would make the
same bet (though in practice this one is probably fine since there are
likely plenty of functions which end in the more standard `die()` and do
not have a return path).

Can reftable call die()? Or is this the least-common denominator among
Git and libgit2 for terminating a running program?

Thanks,
Taylor
Ævar Arnfjörð Bjarmason Jan. 12, 2022, 12:47 p.m. UTC | #2
On Tue, Jan 11 2022, Taylor Blau wrote:

> On Tue, Jan 11, 2022 at 05:40:22PM +0100, Ævar Arnfjörð Bjarmason wrote:
>> Remove unreachable return statements added in acb533440fc (reftable:
>> implement refname validation, 2021-10-07) and f14bd719349 (reftable:
>> write reftable files, 2021-10-07).
>>
>> This avoids the following warnings on SunCC 12.5 on
>> gcc211.fsffrance.org:
>>
>>     "reftable/refname.c", line 135: warning: statement not reached
>>     "reftable/refname.c", line 135: warning: statement not reached
>
> Interesting. From a cursory reading, I agree with the assessment of
> at least my compiler that these return statements are both unnecessary,
> but...
>
>> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
>> ---
>>  reftable/refname.c | 1 -
>>  reftable/writer.c  | 1 -
>>  2 files changed, 2 deletions(-)
>>
>> diff --git a/reftable/refname.c b/reftable/refname.c
>> index 95734969324..136001bc2c7 100644
>> --- a/reftable/refname.c
>> +++ b/reftable/refname.c
>> @@ -132,7 +132,6 @@ static int validate_refname(const char *name)
>>  			return REFTABLE_REFNAME_ERROR;
>>  		name = next + 1;
>>  	}
>> -	return 0;
>>  }
>
> In this case the loop inside of validate_refname() should always
> terminate the function within the loop body. But removing this return
> statement here relies on the compiler to determine that fact.
>
> I could well imagine on the other end of the spectrum there exists a
> compiler which _doesn't_ make this inference pass, and would complain
> about the opposite thing as you're reporting from SunCC (i.e., that this
> function which returns something other than void does not have a return
> statement outside of the loop).
>
> So in that sense, I disagree with the guidance of SunCC's warning. In
> other words: by quelching this warning under one compiler, are we
> introducing a new warning under a different/less advanced compiler?

I'd think that any compiler who'd warn about this sort of thing at all
would be able to spot constructs like this one, which are basically:

    while (1) {
    	...
        if (x)
        	return;
	...
    }
    return; /* unreachable */

Where the elided code contains no "break", "goto" or other mechanism for
exiting the for-loop.

I.e. GCC and Clang don't bother to note the unreachable code, but I
don't think the reverse will be true, that a compiler will say that a
"return" is missing there. Having a function be just a loop body that
returns an some point is a common pattern.

>>  int validate_ref_record_addition(struct reftable_table tab,
>> diff --git a/reftable/writer.c b/reftable/writer.c
>> index 35c8649c9b7..70a7bf142a2 100644
>> --- a/reftable/writer.c
>> +++ b/reftable/writer.c
>> @@ -39,7 +39,6 @@ writer_reftable_block_stats(struct reftable_writer *w, uint8_t typ)
>>  		return &w->stats.log_stats;
>>  	}
>>  	abort();
>> -	return NULL;
>>  }
>
> Here I'm less skeptical, since it's almost certain that any compiler
> would recognize this call to abort() as terminating the whole program.
> So it should be able to infer that anything after it is unreachable.

That's also correct, but in terms of compiler implementations I'd think
you'd get basic loop flow analysis first, and the annotation of
unreturn-able functions like abort() or a custom die() later.
> ...
Taylor Blau Jan. 12, 2022, 7:19 p.m. UTC | #3
On Wed, Jan 12, 2022 at 01:47:40PM +0100, Ævar Arnfjörð Bjarmason wrote:
> >> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
> >> ---
> >>  reftable/refname.c | 1 -
> >>  reftable/writer.c  | 1 -
> >>  2 files changed, 2 deletions(-)
> >>
> >> diff --git a/reftable/refname.c b/reftable/refname.c
> >> index 95734969324..136001bc2c7 100644
> >> --- a/reftable/refname.c
> >> +++ b/reftable/refname.c
> >> @@ -132,7 +132,6 @@ static int validate_refname(const char *name)
> >>  			return REFTABLE_REFNAME_ERROR;
> >>  		name = next + 1;
> >>  	}
> >> -	return 0;
> >>  }
> >
> > In this case the loop inside of validate_refname() should always
> > terminate the function within the loop body. But removing this return
> > statement here relies on the compiler to determine that fact.
> >
> > I could well imagine on the other end of the spectrum there exists a
> > compiler which _doesn't_ make this inference pass, and would complain
> > about the opposite thing as you're reporting from SunCC (i.e., that this
> > function which returns something other than void does not have a return
> > statement outside of the loop).
> >
> > So in that sense, I disagree with the guidance of SunCC's warning. In
> > other words: by quelching this warning under one compiler, are we
> > introducing a new warning under a different/less advanced compiler?
>
> I'd think that any compiler who'd warn about this sort of thing at all
> would be able to spot constructs like this one, which are basically:
>
>     while (1) {
>     	...
>         if (x)
>         	return;
> 	...
>     }
>     return; /* unreachable */
>
> Where the elided code contains no "break", "goto" or other mechanism for
> exiting the for-loop.
>
> I.e. GCC and Clang don't bother to note the unreachable code, but I
> don't think the reverse will be true, that a compiler will say that a
> "return" is missing there. Having a function be just a loop body that
> returns an some point is a common pattern.

Right, but I'm more concerned about a less advanced compiler that would
complain about the absence of a `return` statement as the last
instruction in a non-void function.

This is probably all academic, anyway, since less advanced compilers
probably have other issues compiling Git as it stands today, but
fundamentally I think that SunCC's warnings here are at the very least
inconsiderate of less advanced compilers.

To me, the safest thing to do would be to leave the code as-is and drop
this patch.

Thanks,
Taylor
Ævar Arnfjörð Bjarmason Jan. 13, 2022, 10:29 a.m. UTC | #4
On Wed, Jan 12 2022, Taylor Blau wrote:

> On Wed, Jan 12, 2022 at 01:47:40PM +0100, Ævar Arnfjörð Bjarmason wrote:
>> >> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
>> >> ---
>> >>  reftable/refname.c | 1 -
>> >>  reftable/writer.c  | 1 -
>> >>  2 files changed, 2 deletions(-)
>> >>
>> >> diff --git a/reftable/refname.c b/reftable/refname.c
>> >> index 95734969324..136001bc2c7 100644
>> >> --- a/reftable/refname.c
>> >> +++ b/reftable/refname.c
>> >> @@ -132,7 +132,6 @@ static int validate_refname(const char *name)
>> >>  			return REFTABLE_REFNAME_ERROR;
>> >>  		name = next + 1;
>> >>  	}
>> >> -	return 0;
>> >>  }
>> >
>> > In this case the loop inside of validate_refname() should always
>> > terminate the function within the loop body. But removing this return
>> > statement here relies on the compiler to determine that fact.
>> >
>> > I could well imagine on the other end of the spectrum there exists a
>> > compiler which _doesn't_ make this inference pass, and would complain
>> > about the opposite thing as you're reporting from SunCC (i.e., that this
>> > function which returns something other than void does not have a return
>> > statement outside of the loop).
>> >
>> > So in that sense, I disagree with the guidance of SunCC's warning. In
>> > other words: by quelching this warning under one compiler, are we
>> > introducing a new warning under a different/less advanced compiler?
>>
>> I'd think that any compiler who'd warn about this sort of thing at all
>> would be able to spot constructs like this one, which are basically:
>>
>>     while (1) {
>>     	...
>>         if (x)
>>         	return;
>> 	...
>>     }
>>     return; /* unreachable */
>>
>> Where the elided code contains no "break", "goto" or other mechanism for
>> exiting the for-loop.
>>
>> I.e. GCC and Clang don't bother to note the unreachable code, but I
>> don't think the reverse will be true, that a compiler will say that a
>> "return" is missing there. Having a function be just a loop body that
>> returns an some point is a common pattern.
>
> Right, but I'm more concerned about a less advanced compiler that would
> complain about the absence of a `return` statement as the last
> instruction in a non-void function.
>
> This is probably all academic, anyway, since less advanced compilers
> probably have other issues compiling Git as it stands today, but
> fundamentally I think that SunCC's warnings here are at the very least
> inconsiderate of less advanced compilers.
>
> To me, the safest thing to do would be to leave the code as-is and drop
> this patch.

I really don't see that, sorry. We have an actual example of a compliler
that does emit a warning new in this rc on this code, but AFAICT your
concern is purely hypothetical.

Such a hypothetical compiler would already be emitting a firehose of
false-positive warnings in our or any non-trivial C codebase,
e.g. builtin/bisect--helper.c:bisect_run(), show-branch.c:version_cmp()
and fsck.c:count_leading_dotdots() would all warn (and I just picked
three examples from a quick grep, there's a lot more of them).

So I don't think we need to be concerned about such a hypothetical
compiler. I think anyone doing such flow analysis tries to do it well
enough to not make the warning entirely useless.

Aside: SunCC does get it wrong in some cases, but it's more obscure
code, mainly from jumping into a for-loop via "goto", and not
propagating understanding the implications of NORETURN in some cases (or
maybe we're just using the GCC-specific one in that case).
Johannes Schindelin Jan. 13, 2022, 3:39 p.m. UTC | #5
Hi Ævar,

On Thu, 13 Jan 2022, Ævar Arnfjörð Bjarmason wrote:

> On Wed, Jan 12 2022, Taylor Blau wrote:
>
> > On Wed, Jan 12, 2022 at 01:47:40PM +0100, Ævar Arnfjörð Bjarmason wrote:
> >> >> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
> >> >> ---
> >> >>  reftable/refname.c | 1 -
> >> >>  reftable/writer.c  | 1 -
> >> >>  2 files changed, 2 deletions(-)
> >> >>
> >> >> diff --git a/reftable/refname.c b/reftable/refname.c
> >> >> index 95734969324..136001bc2c7 100644
> >> >> --- a/reftable/refname.c
> >> >> +++ b/reftable/refname.c
> >> >> @@ -132,7 +132,6 @@ static int validate_refname(const char *name)
> >> >>  			return REFTABLE_REFNAME_ERROR;
> >> >>  		name = next + 1;
> >> >>  	}
> >> >> -	return 0;
> >> >>  }
> >> >
> >> > In this case the loop inside of validate_refname() should always
> >> > terminate the function within the loop body. But removing this return
> >> > statement here relies on the compiler to determine that fact.
> >> >
> >> > I could well imagine on the other end of the spectrum there exists a
> >> > compiler which _doesn't_ make this inference pass, and would complain
> >> > about the opposite thing as you're reporting from SunCC (i.e., that this
> >> > function which returns something other than void does not have a return
> >> > statement outside of the loop).
> >> >
> >> > So in that sense, I disagree with the guidance of SunCC's warning. In
> >> > other words: by quelching this warning under one compiler, are we
> >> > introducing a new warning under a different/less advanced compiler?
> >>
> >> I'd think that any compiler who'd warn about this sort of thing at all
> >> would be able to spot constructs like this one, which are basically:
> >>
> >>     while (1) {
> >>     	...
> >>         if (x)
> >>         	return;
> >> 	...
> >>     }
> >>     return; /* unreachable */
> >>
> >> Where the elided code contains no "break", "goto" or other mechanism for
> >> exiting the for-loop.
> >>
> >> I.e. GCC and Clang don't bother to note the unreachable code, but I
> >> don't think the reverse will be true, that a compiler will say that a
> >> "return" is missing there. Having a function be just a loop body that
> >> returns an some point is a common pattern.
> >
> > Right, but I'm more concerned about a less advanced compiler that would
> > complain about the absence of a `return` statement as the last
> > instruction in a non-void function.
> >
> > This is probably all academic, anyway, since less advanced compilers
> > probably have other issues compiling Git as it stands today, but
> > fundamentally I think that SunCC's warnings here are at the very least
> > inconsiderate of less advanced compilers.
> >
> > To me, the safest thing to do would be to leave the code as-is and drop
> > this patch.
>
> I really don't see that, sorry. We have an actual example of a compliler
> that does emit a warning new in this rc on this code, but AFAICT your
> concern is purely hypothetical.

It's just a warning.

So I concur with Taylor. This patch does not need to go into v2.35.0.

Ciao,
Johannes
Johannes Sixt Jan. 13, 2022, 8:17 p.m. UTC | #6
Am 12.01.22 um 13:47 schrieb Ævar Arnfjörð Bjarmason:
> 
> On Tue, Jan 11 2022, Taylor Blau wrote:
> 
>> On Tue, Jan 11, 2022 at 05:40:22PM +0100, Ævar Arnfjörð Bjarmason wrote:
>>> Remove unreachable return statements added in acb533440fc (reftable:
>>> implement refname validation, 2021-10-07) and f14bd719349 (reftable:
>>> write reftable files, 2021-10-07).
>>>
>>> This avoids the following warnings on SunCC 12.5 on
>>> gcc211.fsffrance.org:
>>>
>>>     "reftable/refname.c", line 135: warning: statement not reached
>>>     "reftable/refname.c", line 135: warning: statement not reached
>>
>> Interesting. From a cursory reading, I agree with the assessment of
>> at least my compiler that these return statements are both unnecessary,
>> but...
>>
>>> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
>>> ---
>>>  reftable/refname.c | 1 -
>>>  reftable/writer.c  | 1 -
>>>  2 files changed, 2 deletions(-)
>>>
>>> diff --git a/reftable/refname.c b/reftable/refname.c
>>> index 95734969324..136001bc2c7 100644
>>> --- a/reftable/refname.c
>>> +++ b/reftable/refname.c
>>> @@ -132,7 +132,6 @@ static int validate_refname(const char *name)
>>>  			return REFTABLE_REFNAME_ERROR;
>>>  		name = next + 1;
>>>  	}
>>> -	return 0;
>>>  }
>>
>> In this case the loop inside of validate_refname() should always
>> terminate the function within the loop body. But removing this return
>> statement here relies on the compiler to determine that fact.
>>
>> I could well imagine on the other end of the spectrum there exists a
>> compiler which _doesn't_ make this inference pass, and would complain
>> about the opposite thing as you're reporting from SunCC (i.e., that this
>> function which returns something other than void does not have a return
>> statement outside of the loop).
>>
>> So in that sense, I disagree with the guidance of SunCC's warning. In
>> other words: by quelching this warning under one compiler, are we
>> introducing a new warning under a different/less advanced compiler?
> 
> I'd think that any compiler who'd warn about this sort of thing at all
> would be able to spot constructs like this one, which are basically:
> 
>     while (1) {
>     	...
>         if (x)
>         	return;
> 	...
>     }
>     return; /* unreachable */
> 
> Where the elided code contains no "break", "goto" or other mechanism for
> exiting the for-loop.

Why not just sidestep the problematic case:

diff --git a/reftable/refname.c b/reftable/refname.c
index 9573496932..4f89956187 100644
--- a/reftable/refname.c
+++ b/reftable/refname.c
@@ -120,17 +120,17 @@ static int modification_has_ref_with_prefix(struct modification *mod,
 static int validate_refname(const char *name)
 {
 	while (1) {
 		char *next = strchr(name, '/');
 		if (!*name) {
 			return REFTABLE_REFNAME_ERROR;
 		}
 		if (!next) {
-			return 0;
+			break;
 		}
 		if (next - name == 0 || (next - name == 1 && *name == '.') ||
 		    (next - name == 2 && name[0] == '.' && name[1] == '.'))
 			return REFTABLE_REFNAME_ERROR;
 		name = next + 1;
 	}
 	return 0;
 }

Sure, there are returns in the loop, but they are clearly error cases.
The regular exit is now at the end of the function.

-- Hannes
Junio C Hamano Jan. 13, 2022, 9:37 p.m. UTC | #7
Johannes Sixt <j6t@kdbg.org> writes:

> Why not just sidestep the problematic case:
>
> diff --git a/reftable/refname.c b/reftable/refname.c
> index 9573496932..4f89956187 100644
> --- a/reftable/refname.c
> +++ b/reftable/refname.c
> @@ -120,17 +120,17 @@ static int modification_has_ref_with_prefix(struct modification *mod,
>  static int validate_refname(const char *name)
>  {
>  	while (1) {
>  		char *next = strchr(name, '/');
>  		if (!*name) {
>  			return REFTABLE_REFNAME_ERROR;
>  		}
>  		if (!next) {
> -			return 0;
> +			break;
>  		}
>  		if (next - name == 0 || (next - name == 1 && *name == '.') ||
>  		    (next - name == 2 && name[0] == '.' && name[1] == '.'))
>  			return REFTABLE_REFNAME_ERROR;
>  		name = next + 1;
>  	}
>  	return 0;
>  }
>
> Sure, there are returns in the loop, but they are clearly error cases.
> The regular exit is now at the end of the function.

;-)
diff mbox series

Patch

diff --git a/reftable/refname.c b/reftable/refname.c
index 95734969324..136001bc2c7 100644
--- a/reftable/refname.c
+++ b/reftable/refname.c
@@ -132,7 +132,6 @@  static int validate_refname(const char *name)
 			return REFTABLE_REFNAME_ERROR;
 		name = next + 1;
 	}
-	return 0;
 }
 
 int validate_ref_record_addition(struct reftable_table tab,
diff --git a/reftable/writer.c b/reftable/writer.c
index 35c8649c9b7..70a7bf142a2 100644
--- a/reftable/writer.c
+++ b/reftable/writer.c
@@ -39,7 +39,6 @@  writer_reftable_block_stats(struct reftable_writer *w, uint8_t typ)
 		return &w->stats.log_stats;
 	}
 	abort();
-	return NULL;
 }
 
 /* write data, queuing the padding for the next write. Returns negative for