constant string as controlling expression in _Generic gives error

  • Thread starter Prathamesh Kulkarni
  • Start date
P

Prathamesh Kulkarni

I was reading this article (http://www.robertgamble.net/2012/01/c11-generic-selections.html), on _Generic.
The author shows a printnl() macro, which fails to compile if the controlling expression is a constant string, and the generic association is char *

Here's a simplified test-case:

int
main(void)
{
int a = _Generic("hello", char *: 1);
}

I used clang-3.2 to compile.

f.c:5:20: error: controlling expression type 'char [6]' not compatible withany generic association type
int a = _Generic("hello", char *: 1);

A possible work-around mentioned in the article:
int a = _Generic((char *) "hello", char *: 1);

Another work-around (http://stackoverflow.com/questions/18857056/c11-generic-how-to-deal-with-string-literals):

int a = _Generic((0, "hello"), char *: 1);

I am not able to understand why does the code compile to fail without the
workarounds. n1570 p 6.3.2, 3rd point (page 54) states:
Except when it is the operand of the sizeof operator, the _Alignof operator, or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue. If the array object has register storage class, the behavior is undefined.

This clause has no mention for _Generic, so shouldn't the type of
"hello" be converted from char[6], to char * when it's used as a controlling expression ?
(the author of the above mentioned article raises the same issue but didn'tpursue it further).

Thanks and Regards,
Prathamesh
 
P

Prathamesh Kulkarni

I apologize for the bad formatting, I will
make sure this does not get repeated again.

Regards,
Prathamesh
 
K

Keith Thompson

Prathamesh Kulkarni said:
I was reading this article
(http://www.robertgamble.net/2012/01/c11-generic-selections.html), on
_Generic. The author shows a printnl() macro, which fails to compile
if the controlling expression is a constant string, and the generic
association is char *

Here's a simplified test-case:

int
main(void)
{
int a = _Generic("hello", char *: 1);
}

I used clang-3.2 to compile.

f.c:5:20: error: controlling expression type 'char [6]' not compatible with any generic association type
int a = _Generic("hello", char *: 1);

A possible work-around mentioned in the article:
int a = _Generic((char *) "hello", char *: 1);

Another work-around
(http://stackoverflow.com/questions/18857056/c11-generic-how-to-deal-with-string-literals):

int a = _Generic((0, "hello"), char *: 1);

I am not able to understand why does the code compile to fail without
the workarounds. n1570 p 6.3.2, 3rd point (page 54) states: Except
when it is the operand of the sizeof operator, the _Alignof operator,
or the unary & operator, or is a string literal used to initialize an
array, an expression that has type ‘‘array of type’’ is converted to
an expression with type ‘‘pointer to type’’ that points to the initial
element of the array object and is not an lvalue. If the array object
has register storage class, the behavior is undefined.

This clause has no mention for _Generic, so shouldn't the type of
"hello" be converted from char[6], to char * when it's used as a
controlling expression ? (the author of the above mentioned article
raises the same issue but didn't pursue it further).

The relevant requirement for _Generic is 6.5.1.1p3:

If a generic selection has a generic association with a type name
that is compatible with the type of the controlling expression,
then the result expression of the generic selection is the
expression in that generic association.

So the question is, what is "the type of the controlling expression"
when the controlling expression is a string literal?

Obviously the interpretation is that the type of the string literal
is `char[N]`, where N is the length plus one, and that the type
used for the generic selection is the array type before any implicit
conversion.

The other interpretation, that the type of the expression "hello"
*in that context* is `char*`, is also reasonable, and it's what I
would have expected (i.e., I think clang 3.2 got this wrong).

I just tried to track down in N1570 just what the relevant type is,
but it's surprisingly difficult. 6.4.5 (String literals) doesn't
even *explicitly* say what the value or type of a string literal
is in general.

The preceding section, 6.4.4, describes various kinds of constants
(integer, floating, enumeration, and character), and carefully
describes the type and value of each. But in the description of
string literals, the closest it comes is (6.4.5p5):

The multibyte character sequence is then used to initialize an
array of static storage duration and length just sufficient
to contain the sequence. For character string literals, the
array elements have type char, and are initialized with the
individual bytes of the multibyte character sequence. [...]

The sequence is used to initialize an array -- but it never actually
says that the value of the string literal is the value of the array,
or that its type is char[N].

It's reasonably obvious what the intent is, but without an explicit
statement it's not 100% clear that 6.3.2.1p3 even applies, since
there's no explicit statement that a string literal is an expression
of array type.

If 6.4.5 were more explicit about the type of a string literal,
it might provide some guidance regarding _Generic.

Note that the controlling expression of a generic selection is not
evaluated, and is used only for its type -- just like the operand
of sizeof. So it make sense for an array expression to keep its
array type in that context (which is clang's interpretation) --
but the standard doesn't say so.

(Unfortunately, I don't seem to have a compiler that supports _Generic.)
 
P

Prathamesh Kulkarni

Um, the issue was not really about string literals.
Consider:
char s[] = { 'h', 'e', 'l', 'l', 'o' };
int a = _Generic(s, char*: 1);

This still gives the same error:
f.c:4:20: error: controlling expression
type 'char [5]' not compatible with any
generic association type
int a = _Generic(s, char *: 1);

Here the type of controlling expression is
char[5]. Why shouldn't this be implicitly converted
to char * ?

However this works:
int a = _Generic( (0, s), char *: 1);
Note that the controlling expression of a generic selection is not
evaluated, and is used only for its type -- just like the operand
of sizeof. So it make sense for an array expression to keep its
array type in that context (which is clang's interpretation) --
but the standard doesn't say so.

I may be completely wrong, but this is what I feel:
int a = _Generic(s, char *: 1)
From 6.5.1.1:
generic-selection:
_Generic (assignment-expression, generic-assoc-list)

assignment-expression eventually derives primary-expression
(considering leftmost-derivation)
primary-expression:
identifier
where identifier "s" is matched.
Here type of s is char[5].
However since s is not an operand of sizeof or _Alignof
or & operator, it's type should be converted to char *,
according to $6.3.2.1 3rd point.
So char[5] should be implicitly converted to
char *, and then should be matched for a compatible type
with one of the generic associations.
(Unfortunately, I don't seem to have a compiler that supports _Generic.)
You can use clang-3.0 online at http://gcc.godbolt.org/
 
K

Keith Thompson

Prathamesh Kulkarni said:
Um, the issue was not really about string literals.
Consider:
char s[] = { 'h', 'e', 'l', 'l', 'o' };
int a = _Generic(s, char*: 1);

This still gives the same error:
f.c:4:20: error: controlling expression
type 'char [5]' not compatible with any
generic association type
int a = _Generic(s, char *: 1);

Here the type of controlling expression is
char[5]. Why shouldn't this be implicitly converted
to char * ?

However this works:
int a = _Generic( (0, s), char *: 1);

Probably because the developers of the compiler you're using
thought that made the most sense. I agree with them that it makes
sense for a controlling expression of array not *not* to decay to
a pointer -- but the standard doesn't say so. (You're right that
string literals are a side issue -- but I still find it interesting
that the standard neglects to tell us the type and value of string
literals in general.)

Out of curiosity, what happens if you try this?

char s[] = "hello";
int a = _Generic(s,
char[]: 1,
char*: 2);
Note that the controlling expression of a generic selection is not
evaluated, and is used only for its type -- just like the operand
of sizeof. So it make sense for an array expression to keep its
array type in that context (which is clang's interpretation) --
but the standard doesn't say so.

I may be completely wrong, but this is what I feel:
int a = _Generic(s, char *: 1)
From 6.5.1.1:
generic-selection:
_Generic (assignment-expression, generic-assoc-list)

assignment-expression eventually derives primary-expression
(considering leftmost-derivation)
primary-expression:
identifier
where identifier "s" is matched.
Here type of s is char[5].
However since s is not an operand of sizeof or _Alignof
or & operator, it's type should be converted to char *,
according to $6.3.2.1 3rd point.
So char[5] should be implicitly converted to
char *, and then should be matched for a compatible type
with one of the generic associations.

I agree.
You can use clang-3.0 online at http://gcc.godbolt.org/

I just tried it, but I can't get it to work. When I add "-std=c11",
it complains:

error: invalid argument '-std=c11' not allowed with 'C++/ObjC++'
Compilation failed

even when I save my source with a ".c" suffix.
 
P

Prathamesh Kulkarni

Prathamesh Kulkarni said:
Um, the issue was not really about string literals.
Consider:

char s[] = { 'h', 'e', 'l', 'l', 'o' };
int a = _Generic(s, char*: 1);

This still gives the same error:
f.c:4:20: error: controlling expression
type 'char [5]' not compatible with any
generic association type
int a = _Generic(s, char *: 1);

Here the type of controlling expression is
char[5]. Why shouldn't this be implicitly converted
to char * ?

However this works:
int a = _Generic( (0, s), char *: 1);



Probably because the developers of the compiler you're using

thought that made the most sense. I agree with them that it makes

sense for a controlling expression of array not *not* to decay to

a pointer -- but the standard doesn't say so. (You're right that

string literals are a side issue -- but I still find it interesting

that the standard neglects to tell us the type and value of string

literals in general.)

For arguments sake, let's assume that some
other compiler chooses to decay array into pointer,
and then check for type compatibility,
Would it still be conforming to the standard ?
Out of curiosity, what happens if you try this?



char s[] = "hello";

int a = _Generic(s,

char[]: 1,

char*: 2);
That's a compile-time error.
$6.5.1.1 2nd point under Constraints states:
The type name in a generic association shall
specify a complete object type other than a variably
modified type.

compiling with clang-3.2 gives the following error:
f.c:4:23: error: type 'char []' in
generic association incomplete
int a = _Generic(s, char[]: 1);
I may be completely wrong, but this is what I feel:
int a = _Generic(s, char *: 1)
From 6.5.1.1:

_Generic (assignment-expression, generic-assoc-list)

assignment-expression eventually derives primary-expression
(considering leftmost-derivation)


where identifier "s" is matched.
Here type of s is char[5].
However since s is not an operand of sizeof or _Alignof
or & operator, it's type should be converted to char *,
according to $6.3.2.1 3rd point.
So char[5] should be implicitly converted to
char *, and then should be matched for a compatible type
with one of the generic associations.



I agree.


You can use clang-3.0 online at http://gcc.godbolt.org/



I just tried it, but I can't get it to work. When I add "-std=c11",

it complains:



error: invalid argument '-std=c11' not allowed with 'C++/ObjC++'

Compilation failed

Did you choose the Ubuntu clang-3.0 option ?
It worked for me:
http://gcc.godbolt.org/#{%22version%22%3A3%2C%22filterAsm%22%3A{%22labels%22%3Atrue%2C%22directives%22%3Atrue%2C%22commentOnly%22%3Atrue}%2C%22compilers%22%3A[{%22source%22%3A%22%2F%2F%20Type%20your%20code%20here%2C%20or%20load%20an%20example.\nint%20main%28void%29\n{\n%20%20int%20x%20%3D%20_Generic%281%2C%20int%3A%200%29%3B\n}%22%2C%22compiler%22%3A%22%2Fusr%2Fbin%2Fclang%2B%2B%22%2C%22options%22%3A%22-O2%20-march%3Dnative%22}]}
 
K

Keith Thompson

Prathamesh Kulkarni said:
Here the type of controlling expression is
char[5]. Why shouldn't this be implicitly converted
to char * ?

However this works:
int a = _Generic( (0, s), char *: 1);

Probably because the developers of the compiler you're using
thought that made the most sense. I agree with them that it makes
sense for a controlling expression of array not *not* to decay to
a pointer -- but the standard doesn't say so. (You're right that
string literals are a side issue -- but I still find it interesting
that the standard neglects to tell us the type and value of string
literals in general.)

For arguments sake, let's assume that some
other compiler chooses to decay array into pointer,
and then check for type compatibility,
Would it still be conforming to the standard ?

Only one behavior or the other can be conforming. In my opinion, clang
gets this wrong. And now that I think about it further, clang's
interpretation is impractical; since incomplete types (particularly
char[]) are not permitted, there's no good way to match an array as the
controlling expression. You'd have to have cases for char[1], char[2],
char[3], etc.
Out of curiosity, what happens if you try this?

char s[] = "hello";
int a = _Generic(s,
char[]: 1,
char*: 2);
That's a compile-time error.
$6.5.1.1 2nd point under Constraints states:
The type name in a generic association shall
specify a complete object type other than a variably
modified type.

compiling with clang-3.2 gives the following error:
f.c:4:23: error: type 'char []' in
generic association incomplete
int a = _Generic(s, char[]: 1); [...]
I just tried it, but I can't get it to work. When I add "-std=c11",

it complains:

error: invalid argument '-std=c11' not allowed with 'C++/ObjC++'
Compilation failed

Did you choose the Ubuntu clang-3.0 option ?
It worked for me:
http://gcc.godbolt.org/#{%22version%22%3A3%2C%22filterAsm%22%3A{%22labels%22%3Atrue%2C%22directives%22%3Atrue%2C%22commentOnly%22%3Atrue}%2C%22compilers%22%3A[{%22source%22%3A%22%2F%2F%20Type%20your%20code%20here%2C%20or%20load%20an%20example.\nint%20main%28void%29\n{\n%20%20int%20x%20%3D%20_Generic%281%2C%20int%3A%200%29%3B\n}%22%2C%22compiler%22%3A%22%2Fusr%2Fbin%2Fclang%2B%2B%22%2C%22options%22%3A%22-O2%20-march%3Dnative%22}]}

I just tried that URL and got the same error.

My conclusion is that clang has a bug. You can work around it by
casting the controlling expression to a pointer.

Incidentally, I see you're using the badly broken Google Groups
web interface to Usenet. This results in quoted text being
rendered incorrectly, with spurious double- and quadruple-spacing
inserted. Consider using an actual NNTP server and client (I use
news.eternal-september.org, which is free). Mozilla Thunderbird
has a decent NNTP client. I use Gnus, which you'll like if and
only if you like Emacs. Or you can copy-and-paste your article
into a text editor, clean it up, and then copy-and-paste it back
into the web interface before posting.
 
P

Prathamesh Kulkarni

Prathamesh Kulkarni said:
Prathamesh Kulkarni <[email protected]> writes:
[...]
Here the type of controlling expression is
char[5]. Why shouldn't this be implicitly converted
to char * ?

However this works:
int a = _Generic( (0, s), char *: 1);

Probably because the developers of the compiler you're using
thought that made the most sense. I agree with them that it makes
sense for a controlling expression of array not *not* to decay to
a pointer -- but the standard doesn't say so. (You're right that
string literals are a side issue -- but I still find it interesting
that the standard neglects to tell us the type and value of string
literals in general.)
For arguments sake, let's assume that some
other compiler chooses to decay array into pointer,
and then check for type compatibility,
Would it still be conforming to the standard ?



Only one behavior or the other can be conforming. In my opinion, clang

gets this wrong. And now that I think about it further, clang's

interpretation is impractical; since incomplete types (particularly

char[]) are not permitted, there's no good way to match an array as the

controlling expression. You'd have to have cases for char[1], char[2],

char[3], etc.
This was a bug in clang, I found a bug-report filed regarding the
same issue: http://llvm.org/bugs/show_bug.cgi?id=16340

Now this opens an interesting possibility.
Consider:
char s[] = { 'h', 'e', 'l', 'l', 'o'};
int a = _Generic(s, char[5]: 1);

Should this be a compile-error now ?
because type of s, shall be converted from char[5] to char *
and there is no generic association having compatible type.
This compiles fine on clang-3.2
Out of curiosity, what happens if you try this?

char s[] = "hello";
int a = _Generic(s,
char[]: 1,
char*: 2);
That's a compile-time error.
$6.5.1.1 2nd point under Constraints states:
The type name in a generic association shall
specify a complete object type other than a variably
modified type.

compiling with clang-3.2 gives the following error:
f.c:4:23: error: type 'char []' in
generic association incomplete
int a = _Generic(s, char[]: 1);
[...]
You can use clang-3.0 online at http://gcc.godbolt.org/

I just tried it, but I can't get it to work. When I add "-std=c11",

it complains:

error: invalid argument '-std=c11' not allowed with 'C++/ObjC++'
Compilation failed
Did you choose the Ubuntu clang-3.0 option ?
It worked for me:
http://gcc.godbolt.org/#{%22version%22%3A3%2C%22filterAsm%22%3A{%22labels%22%3Atrue%2C%22directives%22%3Atrue%2C%22commentOnly%22%3Atrue}%2C%22compilers%22%3A[{%22source%22%3A%22%2F%2F%20Type%20your%20code%20here%2C%20or%20load%20an%20example.\nint%20main%28void%29\n{\n%20%20int%20x%20%3D%20_Generic%281%2C%20int%3A%200%29%3B\n}%22%2C%22compiler%22%3A%22%2Fusr%2Fbin%2Fclang%2B%2B%22%2C%22options%22%3A%22-O2%20-march%3Dnative%22}]}



I just tried that URL and got the same error.



My conclusion is that clang has a bug. You can work around it by

casting the controlling expression to a pointer.



Incidentally, I see you're using the badly broken Google Groups

web interface to Usenet. This results in quoted text being

rendered incorrectly, with spurious double- and quadruple-spacing

inserted. Consider using an actual NNTP server and client (I use

news.eternal-september.org, which is free). Mozilla Thunderbird

has a decent NNTP client. I use Gnus, which you'll like if and

only if you like Emacs. Or you can copy-and-paste your article

into a text editor, clean it up, and then copy-and-paste it back

into the web interface before posting.
Thankyou. I shall switch to a NNTP client.
 
K

Keith Thompson

Prathamesh Kulkarni said:
This was a bug in clang, I found a bug-report filed regarding the
same issue: http://llvm.org/bugs/show_bug.cgi?id=16340

Now this opens an interesting possibility.
Consider:
char s[] = { 'h', 'e', 'l', 'l', 'o'};
int a = _Generic(s, char[5]: 1);

Should this be a compile-error now ?
because type of s, shall be converted from char[5] to char *
and there is no generic association having compatible type.
This compiles fine on clang-3.2

Yes, that should be a compile-time error. clang's acceptance of
it is a symptom of the same bug.
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,535
Members
45,007
Latest member
obedient dusk

Latest Threads

Top