Undefined Behaviour in C99 related to adjusted parameters
Asked Answered
K

2

8

I don't understand the following undefined behaviour from C99 standard:

An adjusted parameter type in a function definition is not an object type (6.9.1)

From the Standard, parameters of a function shall be adjusted in two cases:

  • an array is adjusted to a pointer,
  • and a function is adjusted to a pointer to function.

In the second case the adjusted parameter of a function will indeed not be an object (as far as I know the standard distinguishes between object and function):

An identifier can denote an object; a function; a tag or a member of a structure, union...

Could you clarify the point and provide an example of such UB ?

Katinka answered 19/1, 2021 at 13:32 Comment(13)
You provided an incomplete quote. It sounds like :"— An adjusted parameter type in a function definition is not a complete object type (6.9.1)."Matelote
@VladfromMoscow No, the quote is accurate, from C99 J.2 Undefined behavior.Wigeon
A function pointer type is an object type.Decalcify
There seems to be precious little written on exactly what is meant by an adjusted parameter type. I cannot find an example of what constitutes this condition. Can you provide a code snippet to illustrate your question?Accusatory
@JensGustedt that's what I was wondering. But then how can the UB happen ?Katinka
@VladfromMoscow The text was corrected as you show it in C11 J.2.Wigeon
No idea, C99 has been corrected several times since. The current formulation is An adjusted parameter type in a function definition is not a complete object type.Decalcify
As a side note, language lawyer type questions make no sense for obsolete standards.Decalcify
I don't think C99 is obsolete, many tools and many software are still using it. For example (as far as I know) MISRA-C -which is a major coding rules framework in the industry- aplies to C90 and C99Katinka
C99 is obsolete as far as ISO WG14, C11 and C18 are concerned ("This [nth] edition cancels and replaces the [n-1th] edition", etc.), even if not as far as the industry is concerned. C11 is also obsolete in the same way.Wigeon
@JensGustedt - Although there can be debate, I do not believe there can be consensus on the meaning of Obsolescence as it would be applied to a programming language. The closest that can be hoped for is that there are varying opinions. (developer teams here are constrained to use C99 on certain work for the reason it has been proved fit for the purpose by years of development in a highly regulated industry.) Because this question is careful to point out it is specific to C99, it is completely viable, and it makes perfect sense in that context.Accusatory
@ryyker, I don't think so. It makes no sense to discuss a standard that has seen two revisions since, where the responsible ISO committee has already admitted that the text had to be changed (and changed it), and where most people that were on the committee when C99 was voted have already retired. Implementations that still use C99 as their reference do that on their own risk and own interpretation of the text, but language lawyering (as this question is tagged) makes no sense at all.Decalcify
@JensGustedt there is the theory and the real industrial world. When most major analysis tools and coding rules scheme support only C99, well... you are developping your software for C99 and choose this revision of the Standard as your bedside book (especially when some industry standards require to document unspecified and undefined behaviours). Of course it doesn't prevent to take into account the corrections done in the later versions of the Standard. but why notpicking about the choice of a tag on SO ?Katinka
M
5

The first quote from the C Standard is incorrect. It sounds like

— An adjusted parameter type in a function definition is not a complete object type (6.9.1)

That is you omitted the word complete.

For example in a function declaration that is not at the same type its definition you may specify an incomplete object type like

void f( size_t, size_t, int [][*] );

In this function declaration the declaration of the third parameter is not a complete object type because the size of the array elements is unknown.

Here is a demonstrative program

#include <stdio.h>

void f( size_t, size_t, int [][*] );

void f( size_t m, size_t n, int a[][n] )
{
    for ( size_t i = 0; i < m; i++ )
    {
        for ( size_t j = 0; j < n; j++ )
        {
            a[i][j] = n * i + j;
        }
    }
}

void g( size_t, size_t, int [][*] );

void g( size_t m, size_t n, int a[][n] )
{
    for ( size_t i = 0; i < m; i++ )
    {
        for ( size_t j = 0; j < n; j++ )
        {
            printf( "%d ", a[i][j] );
        }
        putchar( '\n' );
    }
}

int main(void) 
{
    size_t m = 2, n = 3;
    int a[m][n];
    
    f( m, n, a );
    g( m, n, a );
    
    return 0;
}

Its output is

0 1 2 
3 4 5 

Here in the program these two function declarations

void f( size_t, size_t, int [][*] );

and

void g( size_t, size_t, int [][*] );

have a parameter declaration with an incomplete object type.

You may not use such a declaration that at the same type is its definition like for example

void f( size_t m, size_t n, int a[][*] )
{
    // ...
}

Because the compiler is unable to determine the pointer type after adjusting the third parameter to pointer. That is the pointer will have an incomplete object type int ( * )[].

Matelote answered 19/1, 2021 at 14:4 Comment(10)
As discussed, there is no mistake in the quote from C99Katinka
@GuillaumePetitjean No there is a mistake because in any case the Standard means a complete object type.Matelote
I gotcha. but you write: "That is you omitted the word complete." No I didn't.Katinka
@GuillaumePetitjean It seems you are looking through an old C Standard Draft with a defect.Matelote
I don't think so. It is the official document from ISO/IEC. "ISO/IEC 9899:1999(E) Second edition 1999-12-01".Katinka
@GuillaumePetitjean It only means that the Standard you are referring has a defect. In Committee Draft — December 2, 2010 ISO/IEC 9899:201x N1548 and diff ✿✿✿✿✿✿ marks — November 6, 2018 ISO/IEC 9899:2017✿✿ 2x (E) N2310 this defect was updated and the word complete was included in the quote.Matelote
@GuillaumePetitjean Without the word complete the quote does not make a sense.Matelote
It would be better to say the C99 version omitted the word 'complete', rather than blaming OP.Wigeon
@IanAbbott I do not blame OP. I pointed out that the quote has a defect. Without the omitted word it does not make a sense. It is a very important remark.Matelote
The complaint is more to do with attribution of sources.Wigeon
W
4

As pointed out in the comments, the text in the standard was corrected in C11. It now reads (C11 J.2):

— An adjusted parameter type in a function definition is not a complete object type (6.9.1).

That makes more sense.

However, I cannot think of an example of the use of an incomplete object type in the parameter of a function definition that would compile without an error. All I can think of is that perhaps some compilers allow unused parameters to have incomplete object types.

As pointed out by @Lundin in the comments, Appendix J is informative, not a normative part of the standard. The correction was also made in the text of the referenced section 6.9.1 in the normative part of the standard. The final clause of the final sentence of 6.9.1/7 was changed from "the resulting type shall be an object type" to "the resulting type shall be a complete object type".

@Lundin also points out that in a function definition, an adjusted parameter type that is an incomplete type is a constraint violation by C11 6.7.6.3/4 (C99 6.7.5.3/4):

After adjustment, the parameters in a parameter type list in a function declarator that is part of a definition of that function shall not have incomplete type.

That is listed under "constraints" and so requires translation of the program to produce at least one diagnostic.

Wigeon answered 19/1, 2021 at 13:59 Comment(12)
"an incomplete object type in the parameter of a function definition that would compile without an error..." I think To some extent the paragraphs we are talking about here are targeting compiler writers as much or more than users (C programmers), i.e. requiring that syntactically correct parameter types, once adjusted must decay into a complete object type. So compiling correct syntax is one thing, but implementing a compiler to correctly adjust the parameter arguments is anotherAccusatory
@Accusatory I still find it strange that the standard is worded in such a way as to allow such code to be successfully translated without a diagnostic.Wigeon
The committee certainly seems to have struggled with the wording, but by listing this as UB, aren't they in essence saying the corollary: "an adjusted function parameter must resolve into a complete type"?Accusatory
@Accusatory It must have resolved into a complete object type only so far as not exhibiting UB is concerned. C11 5.1.1.3 and footnote 9 still allows the program to be successfully translated without a diagnostic, since it is neither a syntax rule violation nor a constraint violation as written in the standard. C11 6.9.1/7 is in a "semantics" section rather than a "constraints" section. That's what I find surprising!Wigeon
Again, annex J is just informative and one should look at the actual 6.9.1 which was also fixed with C11 (6.9.1/7) "the resulting type shall be an object type" -> "the resulting type shall be a complete object type."Sienese
@Accusatory I think your comments are useful.Wigeon
@Sienese Yes, indeed, I should include the relevant paragraphs from C99 and C11 6.9.1 in my answer too.Wigeon
@Ian Abbott C11 6.9.1/7 points at normative chapter 6.7.6.3 where 6.7.6.3/4 (constraints) says "After adjustment, the parameters in a parameter type list in a function declarator that is part of a definition of that function shall not have incomplete type." This text is also there in C99.Sienese
@Sienese I suppose that begs the questions why J.2 refers to 6.9.1 instead of 6.7.6.3?, and why J.2 needs to mention it at all!?Wigeon
@Sienese Oh I see why C11 J.2 refers to 6.9.1 instead of 6.7.6.3. It is because of old-style function definitions.Wigeon
@IanAbbott: I would not be surprised if some compilers would perfectly happily accept definitions with incomplete parameter types in cases where the specifics of the type would have no effect on the generated code, and if the authors of the Standard were aware that some code might exploit this. One of the purposes of UB is to identify areas of "conforming language extension", and the authors of the Standard might not have wanted to forbid implementations from extending the language in this fashion in cases where they could practically do so.Ohmmeter
@IanAbbott: Although there are many cases where I think the Standard should give implementations to either reject a program or process it in a specific fashion, but not to accept the program and process it in another fashion, the Standard has generally avoided doing that. Instead, situations where an implementation would be allowed but not required to process a construct meaningfully are generally classified as UB, even if there is only one commonplace behavior.Ohmmeter

© 2022 - 2024 — McMap. All rights reserved.