2

This is a language-lawyer question about ISO C.

I'm trying to understand how declaration is defined in the Standard. I use N1570. Consider the following cases:

Case 1.

int a;
extern int a; //compatible types, external linkage, well-defined behavior

Case 2.

extern int a;
int a; //well-defined behavior, external linkage, well-defined behavior

Case 3.

int a;
static int a; //6.2.2/6.9.2UB, linkage-disagreement

Case 4.

static int a;
extern int a; //6̶.̶2̶.̶2̶/̶6̶.̶9̶.̶2̶ ̶U̶B̶,̶ ̶l̶i̶n̶k̶a̶g̶e̶-̶d̶i̶s̶a̶g̶r̶e̶e̶m̶e̶n̶t̶
              //as @EricPostpischil mentioned in the comment
              //it is well-defined in 6.2.2

Case 5.

int a;
long a; //6.7.2 UB incompatible types

Case 6.

int a;
const int a; //6.7.2/6.7.3 incompatible types, different qualifiers

Case 7.

enum{
    a
};

enum{
    a //UB, why?
};

Case 8.

 enum {
     a
 };

 const unsigned char a; //UB, why?

Case 1-4

The explanation is clear and well-defined in the Standard.

6.2.2(p7):

If, within a translation unit, the same identifier appears with both internal and external linkage, the behavior is undefined.

Case 5

It is explained in the sections 6.2.7(p1):

If, within a translation unit, the same identifier appears with both internal and external linkage, the behavior is undefined.

and 6.7(p4):

All declarations in the same scope that refer to the same object or function shall specify compatible types.

Case 6

It is explained by 6.7.3(p10):

For two qualified types to be compatible, both shall have the identically qualified version of a compatible type

Case 7-8.

Unclear. I did not find any formal references in the Standard related to them. 6.2.7(p1) states:

For two enumerations, corresponding members shall have the same values.

case 7 satisfies that requirements.

so I don't see any problems.

I did not find anything related to case 8 explicitly so as if it is not defined in the Standard it should be UB.

Can you help to find an explanation in the Standard of the Case 7 and Case 8?

Some Name
  • 8,555
  • 5
  • 27
  • 77
  • Regarding cases 7 and 8, where did you find those examples? Which source said those were UB? – Some programmer dude Jan 29 '19 at 11:45
  • @Someprogrammerdude No source says this. This is just my conclusion which I'm not sure about and asking clarification. – Some Name Jan 29 '19 at 11:47
  • 2
    Regarding 7, both enumerations are equal, but it's a redefinition if both are in the same translation unit. – Some programmer dude Jan 29 '19 at 11:47
  • 2
    Regarding 8, an enumeration creates a global symbolic constant for each symbol in the enumeration. So that's why the second declaration is invalid, as it's redefining an already existing symbol. – Some programmer dude Jan 29 '19 at 11:48
  • @Someprogrammerdude Accoridng to ISO C is redefinition UB? – Some Name Jan 29 '19 at 11:49
  • 2
    UB is a run-time thing. Redefinition is a build-time thing (either at compilation or at linking). – Some programmer dude Jan 29 '19 at 11:50
  • 1
    @SomeName Queston: Did you even try to compile the programs? Did (or did not) you get any errors/ warnings/ compilation failures? – Sourav Ghosh Jan 29 '19 at 11:51
  • @Someprogrammerdude Makes sense to me. Since `6.7.(p5)` states: `A definition of an identifier is a declaration for that identifier that: for an enumeration constant, is the (only) declaration of the identifier` – Some Name Jan 29 '19 at 11:51
  • @SouravGhosh I know that in C++ Standard ODR is explicitly defined. Is it true for ISO C? Did not find anything similar to that? – Some Name Jan 29 '19 at 11:53
  • 1
    @SomeName See this one: [Does C have One Definition Rule like C++?](https://stackoverflow.com/q/34986335/2173917) – Sourav Ghosh Jan 29 '19 at 12:12
  • @SouravGhosh Good point, but I'm not sure `annex §J.5.11` can be applied here since enumerator are not objects. Am I wrong? – Some Name Jan 29 '19 at 12:27
  • @P__J__ Please note that I tagged the question as language-lawyer so please provide a reference to prove that _If we have same symbols in different compilation units the linker will complain instead_. Linkage is a well-defined concept in the Standard. Moreover as you said [before](https://stackoverflow.com/a/54398220/8990329) "You are not getting to the stage when linkage matters" so you just contradicted to yourself. – Some Name Jan 29 '19 at 12:28
  • file 1: int a;, file b: int a; - try to link. – 0___________ Jan 29 '19 at 12:30
  • @P__J__ - That has to be the worst abuse of the custom close vote reason I have ever seen. – StoryTeller - Unslander Monica Jan 29 '19 at 12:30
  • @P__J__ Please read the question more carefully. I did not ask about such a trivial example you provided (Which I found an explanation to by myself and attached it to the question). – Some Name Jan 29 '19 at 12:31
  • @StoryTeller I think it is pointless to answer the same question asked by the same author, only because OP does not want to listen. – 0___________ Jan 29 '19 at 12:32
  • @P__J__ - Who are *you* to judge they don't want to listen? If this is a duplicate, vote as such. If you just want to antagonize the OP then no wonder no one listens to you. – StoryTeller - Unslander Monica Jan 29 '19 at 12:33
  • @EricPostpischil Agree, fixed. Thanks. – Some Name Jan 29 '19 at 13:13

2 Answers2

3

Case 7

It is explained by 6.7.2.3 paragraph 1, 4 and 5 (page 137) (emphasis is mine)

1 A specific type shall have its content defined at most once.

4 All declarations of structure, union, or enumerated types that have the same scope and use the same tag declare the same type. Irrespective of whether there is a tag or what other declarations of the type are in the same translation unit, the type is incomplete [footnote 129)] until immediately after the closing brace of the list defining the content, and complete thereafter.

5 Two declarations of structure, union, or enumerated types which are in different scopes or use different tags declare distinct types. Each declaration of a structure, union, or enumerated type which does not include a tag declares a distinct type.

So an example of identical types of enums [if not for paragraph 1] would be like

enum TagNameA
{
    a
};
enum TagNameA
{
   a
};

Case 8 It is explained by 6.7.2.2 paragraph 3 (page 136) (emphasis is mine)

The identifiers in an enumerator list are declared as constants that have type int and may appear wherever such are permitted [footnote: 127)]

...

[footnote 127)] Thus, the identifiers of enumeration constants declared in the same scope shall all be distinct from each other and from other identifiers declared in ordinary declarators.

where in Case 8

const unsigned char a;

is an ordinary declarator for a that is not distinct from the enumeration constant identifier a.

Gunther Schulz
  • 575
  • 3
  • 18
0

case 8

 enum {
     a
 };

 const unsigned char a; //UB, why?

It is not UB. It is semantic error. UB may happen only runtime.

duplicate identifier 'a'. Consider

int b = a;  //which a? 

case 7

enum{
    a
};

enum{
    a //UB, why?
};

It is not UB. It is semantic error. UB may happen only runtime.

duplicate identifier 'a'. Consider

int b = a;  //which a? 
0___________
  • 60,014
  • 4
  • 34
  • 74
  • 4
    I disagree, [undefined behavior is undefined](https://stackoverflow.com/q/11546193/8746648). That includes compile time. – asynts Jan 29 '19 at 12:51
  • @asynts program which does not compile or link cant show any behavior including the undefined one. Source code which does not compile in not the program and all behavior considerations are void – 0___________ Jan 29 '19 at 12:56
  • The behavior described by the C standard includes both behavior of an executing program and behavior of a C implementation. When the compiler prints, or does not print, a diagnostic, that is behavior. – Eric Postpischil Jan 29 '19 at 12:59
  • @EricPostpischil I have bot found on page 557 anything about the syntax errors. – 0___________ Jan 29 '19 at 13:03
  • @asynts *Undefined means that the compiler can accept it, creating an invalid binary.* Compiler and linker always create **valid** binaries. The behaviour of the code when executing those valid binaries can be undefined (but valid). If compiler/linker does not create a valid binary it is just the compiler/linker bug – 0___________ Jan 29 '19 at 13:10
  • 1
    Please use clause and paragraph numbers to refer to the standard, not page numbers. Annex J is merely informative, not normative—it is merely a summary for convenience, not a binding part of the standard. “Behavior” is defined in 3.4 1, and its definition is broadly “external appearance or action,” with no restriction to program execution alone. – Eric Postpischil Jan 29 '19 at 13:10
  • @EricPostpischil If annex J is not normative where is it defined that _if the definitions disagree, or more than one is initialized, the behavior is undefined_? (J.5.11) – Some Name Jan 29 '19 at 13:17
  • 2
    @SomeName: C 2018 6.7 3: “If an identifier has no linkage, there shall be no more than one declaration of the identifier (in a declarator or type specifier) with the same scope and in the same name space, except that: — a typedef name may be redefined to denote the same type as it currently does, provided that type is not a variably modified type; — tags may be redeclared as specified in 6.7.2.3.” – Eric Postpischil Jan 29 '19 at 14:23
  • @EricPostpischil The linkage concept seems a bit blurred. 6.2.2 Describes "Linkages of ***identifiers***". Does the linkage concept apply to identifier denoting something else except objects or functions? Can we say that tags, typenames, enumeration constants are identifier with no linkage? Currently I don't see anything in N1570 preventing us from saying so... – Some Name Jan 30 '19 at 06:49
  • 1
    @SomeName: Only objects and functions can have external or internal linkage. The standard is a bit imprecise about its wording regarding no linkage. 6.2.2 1 says there are three kinds of linkage, “external, internal, and none.” Taken literally, that means an identifier could have linkage, and its kind would be none. But the next paragraph refers to an identifier with “no linkage” and apparently means an identifier with linkage of kind none. So, I think in spite of the grammatical oddities, having no linkage and having linkage of kind none are the same thing. – Eric Postpischil Jan 30 '19 at 20:24
  • 1
    @SomeName: Given that, I think identifiers for things other than objects or functions have no linkage. Meaning, sure, the concept has meaning—would could consider whether the name of a type or a structure member in one module is linked to the same name in another translation unit—but the answer is their linkage is always *none*, and any compatibilities or other issues between the same identifiers in different units are resolved by rules other than linkage. – Eric Postpischil Jan 30 '19 at 20:26