This is an archive of the discontinued LLVM Phabricator instance.

[C11] Diagnose unreachable generic selection associations
ClosedPublic

Authored by aaron.ballman on May 9 2022, 12:18 PM.

Details

Summary

The controlling expression of a _Generic selection expression undergoes lvalue conversion, array conversion, and function conversion before picking the association. This means that array types, function types, and qualified types are all unreachable code if they're used as an association. I've been caught by this twice in the past few months and I figure that if a WG14 member can't seem to remember this rule, users are also likely to struggle with it. So this adds an on-by-default unreachable code diagnostic for generic selection expression associations.

Note, we don't have to worry about function types as those are already a constraint violation which generates an error.

Diff Detail

Event Timeline

aaron.ballman created this revision.May 9 2022, 12:18 PM
Herald added a project: Restricted Project. · View Herald TranscriptMay 9 2022, 12:18 PM
aaron.ballman requested review of this revision.May 9 2022, 12:18 PM
Herald added a project: Restricted Project. · View Herald TranscriptMay 9 2022, 12:18 PM
erichkeane accepted this revision.May 9 2022, 12:44 PM
erichkeane added a subscriber: erichkeane.

One minor preference, otherwise LGTM.

clang/lib/Sema/SemaExpr.cpp
1702

Splitting this up into the separate line for only 2 items seems like a waste, would it look better to just do the diag in each 'if'?

This revision is now accepted and ready to land.May 9 2022, 12:44 PM
tahonermann accepted this revision.May 9 2022, 12:46 PM

Looks good to me!

This revision was landed with ongoing or failed builds.May 10 2022, 8:16 AM
This revision was automatically updated to reflect the committed changes.
aaron.ballman added inline comments.May 10 2022, 8:19 AM
clang/lib/Sema/SemaExpr.cpp
1702

I tried it both ways and I think using only one call to Diag is better (it makes it easier if we want to update the diagnostic text in the future, say to add more information to it like a source range, etc). However, I don't feel super strongly -- if you'd like to see it changed still, I can do so.

erichkeane added inline comments.May 10 2022, 8:26 AM
clang/lib/Sema/SemaExpr.cpp
1702

I don't feel super strongly either. It just seemed to be 4 lines of rigamarole to 'save' 1 line of copy/paste :D So the judgement comes down to 'how much do we expect the message format/reasons to emit it' will change vs the extra mental 'overhead'.

Since you already committed it, I'd say it is not worth a cleanup patch.

$ cat /tmp/a.cc
typedef struct Test {
} Test;

void f() {
  const Test constVal;
  _Generic(constVal, const Test : 0);
}
$ ./build/rel/bin/clang -fsyntax-only  -x c++ /tmp/a.cc -Wall
/tmp/a.cc:6:28: warning: due to lvalue conversion of the controlling expression, association of type 'const Test' will never be selected because it is qualified [-Wunreachable-code-generic-assoc]
  _Generic(constVal, const Test : 0);
                           ^
/tmp/a.cc:6:35: warning: expression result unused [-Wunused-value]
  _Generic(constVal, const Test : 0);
                                  ^
2 warnings generated.
$ ./build/rel/bin/clang -fsyntax-only  -x c /tmp/a.cc -Wall
/tmp/a.cc:6:28: warning: due to lvalue conversion of the controlling expression, association of type 'const Test' (aka 'const struct Test') will never be selected because it is qualified [-Wunreachable-code-generic-assoc]
  _Generic(constVal, const Test : 0);
                           ^
/tmp/a.cc:6:12: error: controlling expression type 'Test' (aka 'struct Test') not compatible with any generic association type
  _Generic(constVal, const Test : 0);
           ^~~~~~~~
1 warning and 1 error generated.

the C++ case looks wrong, it's warning that const Test can't be selected then selects it

$ cat /tmp/a.cc
typedef struct Test {
} Test;

void f() {
  const Test constVal;
  _Generic(constVal, const Test : 0);
}
$ ./build/rel/bin/clang -fsyntax-only  -x c++ /tmp/a.cc -Wall
/tmp/a.cc:6:28: warning: due to lvalue conversion of the controlling expression, association of type 'const Test' will never be selected because it is qualified [-Wunreachable-code-generic-assoc]
  _Generic(constVal, const Test : 0);
                           ^
/tmp/a.cc:6:35: warning: expression result unused [-Wunused-value]
  _Generic(constVal, const Test : 0);
                                  ^
2 warnings generated.
$ ./build/rel/bin/clang -fsyntax-only  -x c /tmp/a.cc -Wall
/tmp/a.cc:6:28: warning: due to lvalue conversion of the controlling expression, association of type 'const Test' (aka 'const struct Test') will never be selected because it is qualified [-Wunreachable-code-generic-assoc]
  _Generic(constVal, const Test : 0);
                           ^
/tmp/a.cc:6:12: error: controlling expression type 'Test' (aka 'struct Test') not compatible with any generic association type
  _Generic(constVal, const Test : 0);
           ^~~~~~~~
1 warning and 1 error generated.

the C++ case looks wrong, it's warning that const Test can't be selected then selects it

Oh wow, good catch! I'll correct this.

Oh wow, good catch! I'll correct this.

Oof, the plot thickens... the diagnostic is correct for some types, but is incorrect for others: https://godbolt.org/z/fahzx53W6. I also discovered a parsing issue in C++ where we get confused by elaborated type specifiers: https://godbolt.org/z/1ejxqd9ss.

I'm questioning the benefit to supporting _Generic in C++ and starting to wonder if perhaps we should pull this extension back. C++ already has a rich type system that can accomplish the same thing and we're not following the C semantics so there's not really an argument to be made for the extension letting people write code in both languages. @rsmith, do you have thoughts about that?

I'm questioning the benefit to supporting _Generic in C++ and starting to wonder if perhaps we should pull this extension back.

I imagine it may be used in shared system headers. E.g., implementations of tgmath.h that don't use the builtins.

I'm questioning the benefit to supporting _Generic in C++ and starting to wonder if perhaps we should pull this extension back.

I imagine it may be used in shared system headers. E.g., implementations of tgmath.h that don't use the builtins.

That's a good thought! I'm not seeing much evidence of that, at least from my code searches: https://sourcegraph.com/search?q=context:global+file:tgmath%5C.h+_Generic+AND+%28%23ifdef+__cplusplus+OR+%23if+defined%5C%28__cplusplus%5C%29%29&patternType=literal&case=yes

You can definitely find cases of #ifndef __cplusplus in the same code search though (which I've seen protecting uses of _Generic in the file). Also, our own tgmath.h implementation is not using _Generic (we use __attribute__((overloadable)) instead).

Oh wow, good catch! I'll correct this.

Oof, the plot thickens... the diagnostic is correct for some types, but is incorrect for others: https://godbolt.org/z/fahzx53W6. I also discovered a parsing issue in C++ where we get confused by elaborated type specifiers: https://godbolt.org/z/1ejxqd9ss.

https://reviews.llvm.org/D125882 addresses the diagnostic behavior for the unreachable warning.

Oh wow, good catch! I'll correct this.

Oof, the plot thickens... the diagnostic is correct for some types, but is incorrect for others: https://godbolt.org/z/fahzx53W6. I also discovered a parsing issue in C++ where we get confused by elaborated type specifiers: https://godbolt.org/z/1ejxqd9ss.

https://reviews.llvm.org/D125882 addresses the diagnostic behavior for the unreachable warning.

I landed that fix in 47b8424a533d5c02fd8b3517047cf93e533f00d0, thank you for pointing it out @aeubanks!