0

Why does the 2nd and 3rd assignments in f2 produces this and the first doesn't?

Clang-Tidy: Narrowing conversion from 'int' to signed type 'char' is implementation-defined

AFAIK, according to C language specification the type of the ternary operator result is determined by the type of the first return clause, ergo, the type of X ? Y : Z is the type of Y, so the type of test() ? f1() : 'b' and test() ? 'b' : f1() should be char.

However, the third line doesn't produce this warning, so apparently clang-tidy thinks that the type of test() ? f1() : 'b' is int...

int a;
int test() { return a; }
char f1() { return 'x'; }

void f2() {
    char x = f1(); // fine
    char y = test() ? f1() : 'b'; // clang-tidy "narrowing conversion"
    char z = test() ? 'b' : f1(); // clang-tidy "narrowing conversion"
    char w = (char) (test() ? f1() : 'b'); // fine
}

P.S. please don't refer me to this post. There is no relation.

traveh
  • 2,700
  • 3
  • 27
  • 44
  • 3
    Please be aware that character constant `'b'` is of type `int`. – Weather Vane Apr 21 '23 at 18:57
  • Please try `printf("%zu", sizeof 'b');` – Weather Vane Apr 21 '23 at 19:06
  • @traveh: In C, the type of a plain character constant is `int`, not `char`, per C 1999 6.4.4.4 10 and C 2018 6.4.4.4 10. – Eric Postpischil Apr 21 '23 at 19:08
  • So this was changed from C90? I remember a character literal used to be of type `char`. – traveh Apr 21 '23 at 19:22
  • 1
    @WeatherVane My apologies ... I guess I'm quite outdated... – traveh Apr 21 '23 at 19:29
  • 1
    C89/90 section **6.1.3.4 Character constants** says *"An integer character constant has type `int`"* and *"If an integer character constant contains a single character or escape sequence. its value is the one that results when an object with type `char` whose value is that of the single character or escape sequence is converted to type `int`."* – Weather Vane Apr 21 '23 at 19:32
  • 2
    @traveh - You might have looked at C++ rules, where in fact `'b'` has type `char` (to allow for overloaded functions). – BoP Apr 21 '23 at 20:14
  • @BoP you might be right. I remember it being *somewhere* :o – traveh Apr 23 '23 at 06:25

2 Answers2

1

AFAIK, according to C language specification the type of the ternary operator result is determined by the type of the first return clause…

It is not. C 1999 6.5.15 5 says:

If both the second and third operands have arithmetic type, the result type that would be determined by the usual arithmetic conversions, were they applied to those two operands, is the type of the result…

In ordinary C implementations, the common type of the usual arithmetic conversions applied to char and int is int.

However, the third line doesn't produce this warning, so apparently clang-tidy thinks that the type of test() ? f1() : 'b' is int...

The result type of all the conditional operator expressions shown is int. The reason clang-tidy does not warn about it is not because of the types involved but because that statement contains a cast. Code analyzers commonly take a cast as a deliberate conversion the author has thought about, and they do not warn about deliberate conversions as much as with implicit conversions such as occur in an assignment.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • Note that I was asking about C99 (although it might not change the answer, the quote is wrong). – traveh Apr 21 '23 at 19:03
  • @traveh: The text is exactly the same in C 2018, including the clause and paragraph numbers. – Eric Postpischil Apr 21 '23 at 19:09
  • Got it. Strange though that `(char)'A' + (char)'A')` produces an `int`... – traveh Apr 21 '23 at 19:28
  • 1
    @traveh: `+` uses the usual arithmetic conversions too, and the usual arithmetic conversions for `char` and `char` result in `int`, because integer types narrower than `int` are automatically promoted to `int` (with some technicalities). Most integer arithmetic in C is done as `int` or wider. – Eric Postpischil Apr 21 '23 at 19:30
1

The following have no narrowing as it is char_object = some_char_value;

char x = f1(); // fine
char w = (char) (test() ? f1() : 'b'); // fine

With :?, the return type is a common type between char and int, which in OP's implementation is an int. Note that 'b', a character constant, has type int.

char y = test() ? f1() : 'b'; // clang-tidy "narrowing conversion"
char z = test() ? 'b' : f1(); // clang-tidy "narrowing conversion"

This is like char_object = some_int_value;. As the int range exceeds char, there is, in general, a narrowing potential.


Consider a 5th case: char_object = a_particular_int_value;. Here the compiler knows the int value is in range of a char and does not emit a warning.

char v = 'b';

With more advanced compiler analysis, a compiler could recognize that char y = test() ? f1() : 'b' also does not attempt to assign an out-of-range char value and so not warn.


I remember a character literal used to be of type char.

C defines 'b' as a character-constant of type int, not as a character literal.

C defines only 2 literals: string literals and compound literals.

I suspect OP is thinking of C++ which does use character literal and there 'b' is type char.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256