The object status of function call returns

Discussion in 'C Programming' started by Netocrat, Jul 20, 2005.

  1. Netocrat

    Netocrat Guest

    Any comments on the correctness of the statements 1, 2a, 2b, 3 and 4 in
    the code below?

    If they are correct, then the definition of an object as well as that of
    an lvalue is broken in C99 by the following reasoning:

    foo() does not return an object, so the return of foo() conceptually is
    not stored, yet we are able to obtain a pointer to one of its member
    elements, therefore it must be stored, therefore foo() does return an
    object, which is a contradiction.

    6.5.2.2#5 prevents us from storing and later using foo().ar anyhow, but
    C99 seems to be confused about whether conceptually a function returns
    something that is stored (Stan Tobias's concept of a temporary variable)
    or is just a value.

    Both gcc and como online compile this code in strict C99 mode, and both
    fault in C90 mode. Como online accepts foo().ar as an isolated statement
    but not as an argument to printf.

    #include <stdio.h>
    struct s {char ar[3];} foo(void)
    {
    struct s s = {"ab"};
    return s;
    }

    int main (void)
    {
    /* 1. C90/C99 prohibit this because foo()'s return is not an
    * object - 6.8.6.4#3 refers to "the value of the function call"
    * rather than "the object returned by the function call". However
    * by C99's broken definition it is unintentedly an lvalue */
    /* &foo(); */
    /* 2a. By C90 foo().ar is not an lvalue so we can't take its
    * address.
    * 2b. By C99's broken definition it is an lvalue but not an
    * object. 6.5.2.3#1 specifically allows foo().ar and
    * 6.5.2.3#3 confirms that it is an lvalue */
    /* &foo().ar; */
    /* 3. By C99, ar decays to char* because of a wording change in
    * 6.3.2.1#3 from "lvalue that has type" to "expression that has
    * type" so this is valid */
    foo().ar;
    /* 4. As for (3) */
    &foo().ar[0];
    /* With gcc in C99 mode this compiles, runs and prints the
    * 2 ptrs */
    printf("%p\n", (void *)&foo().ar[0]);
    printf("%p\n", (void *)foo().ar);

    return 0;
    }
    Netocrat, Jul 20, 2005
    #1
    1. Advertising

  2. Netocrat

    Jack Klein Guest

    On Tue, 19 Jul 2005 23:25:27 GMT, Netocrat <>
    wrote in comp.lang.c:

    > Any comments on the correctness of the statements 1, 2a, 2b, 3 and 4 in
    > the code below?
    >
    > If they are correct, then the definition of an object as well as that of
    > an lvalue is broken in C99 by the following reasoning:


    That the definition of lvalue is broken in C99 is an acknowledged
    fact. But the proper place to discuss this is really comp.std.c, not
    here.

    > foo() does not return an object, so the return of foo() conceptually is
    > not stored, yet we are able to obtain a pointer to one of its member
    > elements, therefore it must be stored, therefore foo() does return an
    > object, which is a contradiction.
    >
    > 6.5.2.2#5 prevents us from storing and later using foo().ar anyhow, but
    > C99 seems to be confused about whether conceptually a function returns
    > something that is stored (Stan Tobias's concept of a temporary variable)
    > or is just a value.
    >
    > Both gcc and como online compile this code in strict C99 mode, and both
    > fault in C90 mode. Como online accepts foo().ar as an isolated statement
    > but not as an argument to printf.
    >
    > #include <stdio.h>
    > struct s {char ar[3];} foo(void)
    > {
    > struct s s = {"ab"};
    > return s;
    > }
    >
    > int main (void)
    > {
    > /* 1. C90/C99 prohibit this because foo()'s return is not an
    > * object - 6.8.6.4#3 refers to "the value of the function call"
    > * rather than "the object returned by the function call". However
    > * by C99's broken definition it is unintentedly an lvalue */
    > /* &foo(); */
    > /* 2a. By C90 foo().ar is not an lvalue so we can't take its
    > * address.
    > * 2b. By C99's broken definition it is an lvalue but not an
    > * object. 6.5.2.3#1 specifically allows foo().ar and
    > * 6.5.2.3#3 confirms that it is an lvalue */
    > /* &foo().ar; */
    > /* 3. By C99, ar decays to char* because of a wording change in
    > * 6.3.2.1#3 from "lvalue that has type" to "expression that has
    > * type" so this is valid */
    > foo().ar;
    > /* 4. As for (3) */
    > &foo().ar[0];
    > /* With gcc in C99 mode this compiles, runs and prints the
    > * 2 ptrs */
    > printf("%p\n", (void *)&foo().ar[0]);
    > printf("%p\n", (void *)foo().ar);
    >
    > return 0;
    > }


    You are missing the (admittedly non-normative) section on undefined
    behavior in Annex J. Specifically:

    "— An attempt is made to modify the result of a function call, a
    conditional operator, an assignment operator, or a comma operator, or
    to access it after the next sequence point (6.5.2.2, 6.5.15, 6.5.16,
    6.5.17)."

    So your &foo().ar is quite legal, but attempting to use it later by
    retaining the pointer produces undefined behavior. Which
    implementations are not required to diagnose. In fact, there is
    nothing in the standard that requires this to be a valid pointer. It
    could be a null pointer, or a random value in a pointer.

    There have already been threads on this in comp.std.c, Google the
    group.

    --
    Jack Klein
    Home: http://JK-Technology.Com
    FAQs for
    comp.lang.c http://www.eskimo.com/~scs/C-faq/top.html
    comp.lang.c++ http://www.parashift.com/c -faq-lite/
    alt.comp.lang.learn.c-c++
    http://www.contrib.andrew.cmu.edu/~ajo/docs/FAQ-acllc.html
    Jack Klein, Jul 20, 2005
    #2
    1. Advertising

  3. Netocrat

    Netocrat Guest

    Jack Klein wrote:

    > [T]he proper place to discuss this is really comp.std.c, not
    > here.


    You are right. My judgement was out and the post was OT.

    > You are missing the (admittedly non-normative) section on undefined
    > behavior in Annex J. Specifically:
    >
    > "- An attempt is made to modify the result of a function call, a
    > conditional operator, an assignment operator, or a comma operator, or
    > to access it after the next sequence point (6.5.2.2, 6.5.15, 6.5.16,
    > 6.5.17)."


    Actually my reference to "6.5.2.2#5 prevent[ing] us from storing and
    later using foo().ar anyhow" was an attempt to include that part of the
    standard.

    > There have already been threads on this in comp.std.c, Google the
    > group.


    I must overcome my tendency to post before searching.
    Netocrat, Jul 21, 2005
    #3
  4. On Tue, 19 Jul 2005 23:25:27 +0000, Netocrat wrote:

    > Any comments on the correctness of the statements 1, 2a, 2b, 3 and 4 in
    > the code below?
    >
    > If they are correct, then the definition of an object as well as that of
    > an lvalue is broken in C99 by the following reasoning:
    >
    > foo() does not return an object, so the return of foo() conceptually is
    > not stored, yet we are able to obtain a pointer to one of its member
    > elements, therefore it must be stored, therefore foo() does return an
    > object, which is a contradiction.


    There is clearly a temporary object in the execution environment. See
    below.

    > 6.5.2.2#5 prevents us from storing and later using foo().ar anyhow, but
    > C99 seems to be confused about whether conceptually a function returns
    > something that is stored (Stan Tobias's concept of a temporary variable)
    > or is just a value.
    >
    > Both gcc and como online compile this code in strict C99 mode, and both
    > fault in C90 mode. Como online accepts foo().ar as an isolated statement
    > but not as an argument to printf.
    >
    > #include <stdio.h>
    > struct s {char ar[3];} foo(void)
    > {
    > struct s s = {"ab"};
    > return s;
    > }
    >
    > int main (void)
    > {
    > /* 1. C90/C99 prohibit this because foo()'s return is not an
    > * object - 6.8.6.4#3 refers to "the value of the function call"
    > * rather than "the object returned by the function call". However
    > * by C99's broken definition it is unintentedly an lvalue */


    Where does the standard say that the return value of a function is an
    lvalue? The only sensible way to read the definition of lvalue in
    6.3.2.1p1 is to say that it specifies the meaning of the term when it is
    used in the standard, it is *not* a statement of when an expression is to
    be considered an lvalue.

    > /* &foo(); */
    > /* 2a. By C90 foo().ar is not an lvalue so we can't take its
    > * address.
    > * 2b. By C99's broken definition it is an lvalue but not an
    > * object.


    lvalues exist in the translation environment, objects at in the
    execution environment. Since we're tlking about the translation
    environment it cannot be an object. However an lvalue in the translation
    environment can designate an object in the execution environment.

    From above. The standard never talks about the result of an operator being
    "an object" even if the result is an lvalue. It is important to
    distinguish these 2 concepts. The presence of an lvalue (and the absence
    of undefined behaviour) implies the existence of an object at runtime. In
    the case of foo().ar[0] the creation of an lvalue is deferred but when it
    does come into being it implies the existence of an object at runtime.

    > 6.5.2.3#1 specifically allows foo().ar and
    > * 6.5.2.3#3 confirms that it is an lvalue */


    The . operator doesn't require its left hand operand to be an lvalue. If
    it isn't the result isn't either. That is the case here i.e. no lvalue is
    involved.

    > /* &foo().ar; */
    > /* 3. By C99, ar decays to char* because of a wording change in
    > * 6.3.2.1#3 from "lvalue that has type" to "expression that has
    > * type" so this is valid */


    & requires its operand to be an lvalue or function designator (or some
    other things not relevant here); this is a constraint violation.

    > foo().ar;
    > /* 4. As for (3) */
    > &foo().ar[0];


    This is different because the [] operator creates an lvalue, and is also
    one of the "other things" specified as the operand of &.

    > /* With gcc in C99 mode this compiles, runs and prints the
    > * 2 ptrs */
    > printf("%p\n", (void *)&foo().ar[0]);
    > printf("%p\n", (void *)foo().ar);


    Those have undefined behaviour in C99 so gcc is at liberty to print the 2
    pointers if it wants to.

    > return 0;
    > }


    Lawrence
    Lawrence Kirby, Jul 21, 2005
    #4
  5. Netocrat

    Netocrat Guest

    On Thu, 21 Jul 2005 12:10:21 +0100, Lawrence Kirby wrote:

    > On Tue, 19 Jul 2005 23:25:27 +0000, Netocrat wrote:


    <snip>

    >> #include <stdio.h>
    >> struct s {char ar[3];} foo(void)
    >> {
    >> struct s s = {"ab"};
    >> return s;
    >> }
    >>
    >> int main (void)
    >> {
    >> /* 1. C90/C99 prohibit this because foo()'s return is not an
    >> * object - 6.8.6.4#3 refers to "the value of the function
    >> call" * rather than "the object returned by the function
    >> call". However * by C99's broken definition it is unintentedly
    >> an lvalue */

    >
    > Where does the standard say that the return value of a function is an
    > lvalue?


    The standard does not anywhere explicitly specify that a function is,
    or is not, an lvalue.

    > The only sensible way to read the definition of lvalue in 6.3.2.1p1 is
    > to say that it specifies the meaning of the term when it is used in the
    > standard,


    That may be sensible, but it leaves the lvalue-ness of some expressions
    undefined. I compiled a list of all the cases specifically mentioned
    by the standard:

    lvalues:
    6.5.1#2 an identifier declared as designating an object
    6.5.1#4 a string literal
    6.5.1#5 a parenthesized expr if the unparenthezised expr is an lvalue
    6.5.2.3#3 struct expression members accessed with "." if the struct
    expression is an lvalue
    6.5.2.3#4 struct expression members accessed with "->"
    6.5.2.5#5 a compound literal
    6.5.3.2#4 the result of "*" if the operand points to an object
    7.5#2 errno

    non-lvalues:
    6.5.3.2#3 the result of "&*"
    6.5.16#3 an assignment expression
    Non-normative footnotes: cast, comma and conditional expressions

    Amongst others, this list is missing function returns.

    So I used the reasoning apparently supported by Pete, Tim Rentsch, and
    Michael Mair in the recent thread "gcc: pointer to array": that since
    it is italicized, 6.3.2.1#1 contains the sole definition of an lvalue
    (I think it's reasonable to add "unless an expression's lvalue-ness is
    otherwise specified elsewhere in a normative part of the standard").

    Given that the lvalue-ness of function returns and constants is not
    elsewhere defined, they match the definition of 6.3.2.1#1 - worse, they
    match the definition of "modifiable lvalue".

    > it is *not* a statement of when an expression is to be considered an
    > lvalue.


    How would you construct such a statement in reference to the standard,
    or do you agree that the inability to do so in all cases is one reason
    that the C99 lvalue definition is broken?

    >> /* &foo(); */
    >> /* 2a. By C90 foo().ar is not an lvalue so we can't take its
    >> * address.
    >> * 2b. By C99's broken definition it is an lvalue but not an *
    >> object.

    >
    > lvalues exist in the translation environment, objects at in the
    > execution environment. Since we're tlking about the translation
    > environment it cannot be an object. However an lvalue in the translation
    > environment can designate an object in the execution environment.
    >
    > From above. The standard never talks about the result of an operator
    > being "an object" even if the result is an lvalue. It is important to
    > distinguish these 2 concepts. The presence of an lvalue (and the absence
    > of undefined behaviour) implies the existence of an object at runtime.
    > In the case of foo().ar[0] the creation of an lvalue is deferred but
    > when it does come into being it implies the existence of an object at
    > runtime.


    How then do you respond to my original claim that the wording of
    6.8.6.4#3 implies the opposite?

    <snip>

    > the [] operator creates an lvalue


    How would you support that statement using the standard?

    Given that you do not accept a function return as an lvalue in C99 and
    interpret that an array index always results in an lvalue, I accept the
    rest of your reasoning with one query:

    >> /* With gcc in C99 mode this compiles, runs and prints the
    >> * 2 ptrs */
    >> printf("%p\n", (void *)&foo().ar[0]); printf("%p\n", (void
    >> *)foo().ar);

    >
    > Those have undefined behaviour in C99 so gcc is at liberty to print the
    > 2 pointers if it wants to.


    What makes the first printf undefined?

    Finally, if you agree with Jack Klein that this is off-topic, please
    cross-post and set the follow-up to comp.std.c. It seems borderline
    and I am no longer sure either way.
    Netocrat, Jul 21, 2005
    #5
  6. Netocrat

    Netocrat Guest

    Netocrat wrote:
    > On Thu, 21 Jul 2005 12:10:21 +0100, Lawrence Kirby wrote:
    > > On Tue, 19 Jul 2005 23:25:27 +0000, Netocrat wrote:


    <snip>
    > >> /* With gcc in C99 mode this compiles, runs and prints the
    > >> * 2 ptrs */
    > >> printf("%p\n", (void *)&foo().ar[0]); printf("%p\n", (void
    > >> *)foo().ar);

    > >
    > > Those have undefined behaviour in C99 so gcc is at liberty to print the
    > > 2 pointers if it wants to.

    >
    > What makes the first printf undefined?


    I'll answer my own question: accessing the result of foo() after the
    next sequence point.
    Netocrat, Jul 21, 2005
    #6
  7. Netocrat

    pete Guest

    Netocrat wrote:

    > I compiled a list of all the cases specifically mentioned
    > by the standard:
    >
    > lvalues:
    > 6.5.1#2 an identifier declared as designating an object
    > 6.5.1#4 a string literal
    > 6.5.1#5
    > a parenthesized expr if the unparenthezised expr is an lvalue
    > 6.5.2.3#3 struct expression members accessed with "." if the struct
    > expression is an lvalue
    > 6.5.2.3#4 struct expression members accessed with "->"
    > 6.5.2.5#5 a compound literal
    > 6.5.3.2#4 the result of "*" if the operand points to an object


    What if the operand is a null pointer or an indeterminate pointer?

    > 7.5#2 errno


    --
    pete
    pete, Jul 22, 2005
    #7
  8. Netocrat

    Netocrat Guest

    pete wrote:
    > Netocrat wrote:
    >
    > > I compiled a list of all the cases specifically mentioned
    > > by the standard:
    > >
    > > lvalues:
    > > 6.5.1#2 an identifier declared as designating an object
    > > 6.5.1#4 a string literal
    > > 6.5.1#5
    > > a parenthesized expr if the unparenthezised expr is an lvalue
    > > 6.5.2.3#3 struct expression members accessed with "." if the struct
    > > expression is an lvalue
    > > 6.5.2.3#4 struct expression members accessed with "->"
    > > 6.5.2.5#5 a compound literal
    > > 6.5.3.2#4 the result of "*" if the operand points to an object

    >
    > What if the operand is a null pointer or an indeterminate pointer?


    Then it obviously doesn't point to an object, and 6.5.3.2#4 says
    nothing about the lvalue-ness of the result. By your reasoning as I
    understand it, in this case "the" definition of 6.3.2.1#1 must be
    considered, by which we still classify the result as an lvalue.

    By both 6.5.3.2#4 and 6.3.2.1#1 the behavior is undefined.

    > > 7.5#2 errno
    Netocrat, Jul 22, 2005
    #8
  9. Netocrat

    Tim Rentsch Guest

    "Netocrat" <> writes:

    > [snip]
    >
    > So I used the reasoning apparently supported by Pete, Tim Rentsch, and
    > Michael Mair in the recent thread "gcc: pointer to array": that since
    > it is italicized, 6.3.2.1#1 contains the sole definition of an lvalue
    > (I think it's reasonable to add "unless an expression's lvalue-ness is
    > otherwise specified elsewhere in a normative part of the standard").


    I feel obliged to jump in and say something here, since apparently I'm
    at least partly responsible for the confusion.

    What the standard *means* in 6.3.2.1 is something like "an lvalue is
    something that has the property that ..., and whether or not something
    is an lvalue is defined in other places in the standard." That isn't
    what it *says*, but that's what it means.

    I'm one of the people who believes that the C standard document should
    say what it means and mean what it says, and when it doesn't then that
    should be pointed out. Other people have different ideas, such as "it
    should have only one sensible interpretation", which I don't really
    disagree with, except that saying what it means and meaning what it
    says should be a pre-requisite, since otherwise whether there is only
    one sensible interpretation (not to mention what the interpretation
    is) seems too dependent on who is doing the interpreting.

    For these reasons I responded to the discussion about the definition
    of lvalue. Probably I should have posted that to comp.std.c; but,
    I'm usually reluctant to change newsgroups mid-thread since I'm never
    sure who read which newsgroup.

    Meanwhile, back in *this* newsgroup, an lvalue should be understood to
    mean only those expressional forms that are identified as lvalues in
    sections 6.5.x (and probably 6.7.something, but I'm not going to look
    up the reference). The definition in 6.3.2.1 should be read not as a
    definition but merely as a remark about what sorts of things are in
    the realm of the term "lvalue".

    And, in case any committee members are listening, please clean up the
    language in 6.3.2.1#1 to make it clear that the normative text that
    defines whether or not some particular thing is an lvalue is given in
    the various sections relating to expressions, etc. Similar deferrals
    of defining properties are done in other places in the standard (eg
    6.2.7#1 for compatible type); the definition of lvalue deserves no
    less.
    Tim Rentsch, Jul 22, 2005
    #9
  10. Netocrat

    pete Guest

    Netocrat wrote:
    >
    > pete wrote:
    > > Netocrat wrote:
    > >
    > > > I compiled a list of all the cases specifically mentioned
    > > > by the standard:
    > > >
    > > > lvalues:
    > > > 6.5.1#2 an identifier declared as designating an object
    > > > 6.5.1#4 a string literal
    > > > 6.5.1#5
    > > > a parenthesized expr if the unparenthezised expr is an lvalue
    > > > 6.5.2.3#3
    > > > struct expression members accessed with "." if the struct
    > > > expression is an lvalue
    > > > 6.5.2.3#4 struct expression members accessed with "->"
    > > > 6.5.2.5#5 a compound literal
    > > > 6.5.3.2#4 the result of "*" if the operand points to an object

    > >
    > > What if the operand is a null pointer or an indeterminate pointer?

    >
    > Then it obviously doesn't point to an object, and 6.5.3.2#4 says
    > nothing about the lvalue-ness of the result. By your reasoning as I
    > understand it, in this case "the" definition of 6.3.2.1#1 must be
    > considered, by which we still classify the result as an lvalue.


    That makes it easier for your compiler to tell you
    if you don't have an lvalue where you need one.

    > By both 6.5.3.2#4 and 6.3.2.1#1 the behavior is undefined.


    --
    pete
    pete, Jul 23, 2005
    #10
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. saha
    Replies:
    0
    Views:
    4,057
  2. Shug
    Replies:
    13
    Views:
    898
    Philipp
    Dec 15, 2006
  3. Shug
    Replies:
    13
    Views:
    952
    Philipp
    Dec 15, 2006
  4. harryos
    Replies:
    0
    Views:
    382
    harryos
    Oct 13, 2010
  5. Sven S.

    status info like rc.status

    Sven S., Dec 9, 2008, in forum: Ruby
    Replies:
    2
    Views:
    175
    Sven S.
    Dec 11, 2008
Loading...

Share This Page