C90 conformance: return statements without expressions in non-void functions

I have been using the Plum Hall Validation Suite for C [1] to validate a
Clang/LLVM backend that I'm working on.

In the conformance-testing code of the suite is defined the following
function:

  // test2.c
  int iequals(int, int, int);
  
  #if !defined(CPLUSPLUS) && !defined(C99)
  yfunc(i, pfn, pi) /* no return type, default to int */
    int i;
    int pfn(); /* parm w fn type becomes ptr-to-fn */
    int *pi;
  #else
  int yfunc(int i, int pfn(int*), int* pi) /* make yfunc explicit int
for C++ */
      /* parm w fn type becomes ptr-to-fn */
  #endif /* CPLUSPLUS */
  {
    static int j = 3;

    iequals(__LINE__, i, j);
    if (--i == 0)
  #if !defined(CPLUSPLUS) && !defined(C99)
      return;
  #else
      return 0;
  #endif
    --j;
    yfunc(i, pfn, pi);
    iequals(__LINE__, i, j);
    ++j;
    pfn(pi); /* converts to (*pfn)() */
  #if !defined(CPLUSPLUS) && !defined(C99)
    /* no return expression, would be erroneous to use value */
  #else
    return 0; /* must return expr, in C++ */
  #endif /* CPLUSPLUS */
  }

The C99 macro is defined when testing against the C99 standard and is
undefined for C90.

This code fails to compile with Clang in the C90 case:

  > clang -S -x c -std=iso9899:1990 -o - test2.c
  test2.c:19:5: error: non-void function 'yfunc' should return a value
        [-Wreturn-type]
      return;
      ^
  1 error generated.

Section 6.6.4 of C90 reads, "If a return statement without an expression
is executed, and the value of the function call is used by the caller,
the behavior is undefined." The standard says nothing (at least that
I've found) about the case where the value of the function is _not_
used, which happens to be the case for yfunc() in the context of the
validation suite.

Can any language lawyers comment on whether yfunc() should compile in
C90 mode?

-Ken

[1] http://www.plumhall.com/stec.html

I don't know the C90 standard well, but from the statement you quote it seems that Clang is non-conformant.

That said, from a user's perspective, Clang is doing the right thing: writing a "return" statement with no expression, in a function that has a non-void return type, is asking for trouble, and we're doing users a favor by rejecting it outright. Plum Hall isn't helping anyone by checking this.

  - Doug

We're doing the right thing here. That error is actually a warning that is mapped to an error. If you want full c90 conformance, you should pass -Wno-return-type or -Wreturn-type. However, I could be convinced that the default mapping in -std=c89 mode (but not -std=gnu89 mode) should be to warning. If you're interested, please prepare a patch for that. Thanks Ken,

-Chris

On Tuesday, September 14, 2010 12:01 PM, Chris Lattner

>> The C99 macro is defined when testing
against the C99 standard and is
>> undefined for C90.
>>
>> This code fails to compile with Clang in
the C90 case:
>>
>>> clang -S -x c -std=iso9899:1990 -o -
test2.c
>> test2.c:19:5: error: non-void function
'yfunc' should return a value
>> [-Wreturn-type]
>> return;
>> ^
>> 1 error generated.
>>
>> Section 6.6.4 of C90 reads, "If a return
statement without an expression
>> is executed, and the value of the function
call is used by the caller,
>> the behavior is undefined." The standard
says nothing (at least that
>> I've found) about the case where the value
of the function is _not_
>> used, which happens to be the case for
yfunc() in the context of the
>> validation suite.
>>
>> Can any language lawyers comment on
whether yfunc() should compile in
>> C90 mode?
>
>
> I don't know the C90 standard well, but
from the statement you quote it seems that
Clang is non-conformant.
>
> That said, from a user's perspective, Clang
is doing the right thing: writing a "return"
statement with no expression, in a function
that has a non-void return type, is asking
for trouble, and we're doing users a favor by
rejecting it outright. Plum Hall isn't
helping anyone by checking this.

We're doing the right thing here. That error
is actually a warning that is mapped to an
error. If you want full c90 conformance, you
should pass -Wno-return-type or -Wreturn-
type.

That seems to disable more errors than are allowed by C90. The one
constraint C90 imposes in 6.6.6.4 is, "A return statement with an
expression shall not appear in a function whose return type is void."
With, -Wno-return-type or -Wreturn-type, Clang generates warnings
instead of errors for:

  void superfluous_return_expr(void)
  {
    return 0;
  }

However, I could be convinced that the
default mapping in -std=c89 mode (but not -
std=gnu89 mode) should be to warning. If
you're interested, please prepare a patch for
that.

I'm interested but might not get to it for a while. Shall I file a bug
in the meantime?

-Ken

Can any language lawyers comment on whether yfunc() should compile in
C90 mode?

#if !defined(CPLUSPLUS) && !defined(C99)
yfunc(i, pfn, pi) /* no return type, default to int */
   int i;
   int pfn(); /* parm w fn type becomes ptr-to-fn */
   int *pi;
#else
int yfunc(int i, int pfn(int*), int* pi) /* make yfunc explicit int
for C++ */
     /* parm w fn type becomes ptr-to-fn */
#endif /* CPLUSPLUS */
{
   static int j = 3;

   iequals(__LINE__, i, j);
   if (--i == 0)
#if !defined(CPLUSPLUS) && !defined(C99)
     return;
#else
     return 0;
#endif
   --j;
   yfunc(i, pfn, pi);
   iequals(__LINE__, i, j);
   ++j;
   pfn(pi); /* converts to (*pfn)() */
#if !defined(CPLUSPLUS) && !defined(C99)
   /* no return expression, would be erroneous to use value */
#else
   return 0; /* must return expr, in C++ */
#endif /* CPLUSPLUS */
}

I don't know the C90 standard well, but from the statement you quote it seems that Clang is non-conformant.

That said, from a user's perspective, Clang is doing the right thing: writing a "return" statement with no expression, in a function that has a non-void return type, is asking for trouble, and we're doing users a favor by rejecting it outright. Plum Hall isn't helping anyone by checking this.

As I read the test, the function is declared with no return type (i.e. implicit int). This style was commonly used pre-C90 to declare functions that are logically void, as many compilers did not support void, and the C90 standard permits this usage in order to support such code. How much of it is still around is an open question, probably not much, but I'm sure somebody stuck with maintaining such code would not consider it friendly to have the compiler reject it.

This is a good point; it would be totally reasonable for us to consider this a hard error unless the return type is implicit.

John.

Yes, I agree.

  - Doug