13

The following code is accepted by clang 6.0.0 but rejected by gcc 8.2

enum class E {
  Good, Bad,
};

struct S {
  E e : 2;
  int dummy;
};

S f() {
  return {E::Good, 100};
}

Live godbolt example

The GCC complains

error: could not convert '{Good, 100}' from '<brace-enclosed initializer list>' to 'S'

Which one is correct? Where in the standard talks about this situation?

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
Kan Li
  • 8,557
  • 8
  • 53
  • 93
  • It is interesting that GCC gives a spurious warning `10:9: warning: 'S::e' is too small to hold all values of 'enum class E'` for any bit field size except 32, and clang doesn't warn about this even when the enum range really won't fit. perhaps this special handling of bitfields is partially why GCC can't perform the aggregate initialisation? – Gem Taylor Sep 06 '18 at 17:44

2 Answers2

4

return {E::Good, 100}; performs copy list initialization of the return value. The effect of this list initialization is an aggregate initialization.

So is S an aggregate? The description of an aggregate varies depending on which version of C++ you're using, but in all cases S should be an aggregate so this should compile. Clang (and MSVC) have the correct behavior.

The fix is simple, though. Change your return statement to return the correctly typed object:

return S{E::Good, 100};
1201ProgramAlarm
  • 32,384
  • 7
  • 42
  • 56
  • 2
    instead of "From the description of list initialization used in a return statement" it would be good to provide Standard quotes, and in particular point out why aggregate initialization should not apply here – M.M Sep 06 '18 at 02:13
  • 2
    @1201ProgramAlarm I think you're right, but I also think that gcc is trying to do some narrowing conversation on the scoped enum(with `int` as it underlaying type) to the bit field, which is prohibited in a list-initialization that is not direct, like in the case of the return, i do think this because if you turn the enum into an unscoped one(for which implicit conversion to `int` is allowed) or increase the bit field to 32 so that perfect match happens, the error disappear even leaving the initialization as copy-list-initialzation – Jans Sep 06 '18 at 02:16
  • @xhamr After further investigation I've changed my verdict and rewrote the answer. – 1201ProgramAlarm Sep 06 '18 at 02:31
3

This should be well-formed and so this is a gcc bug.

We end up at aggregate initialization via [stmt.return]p2 which says:

… A return statement with a braced-init-list initializes the object or reference to be returned from the function by copy-list-initialization ([dcl.init.list]) from the specified initializer list. …

then [dcl.init.list]p3.2 says:

Otherwise, if T is an aggregate, aggregate initialization is performed ([dcl.init.aggr]). …

At this point we may wonder if this is a narrowing conversion and therefore ill-formed but [dcl.init.list]p7 does not have any clauses that cover this case and no other cases in [dcl.init.list] apply to make this ill-formed.

We can see with a similar example which removes enum from the equation but keeps the bit-field shows neither gcc nor clang gives us a narrowing conversion diagnostic, which we expect to be the case, although this similar problem is covered by [dcl.init.list]p7.4 although not ill-formed:

struct S2 {
    int e : 2 ;
    int dummy ;
} ;

S2 foo( int x ) {
   return {x, 100} ;
}

see it live in godbolt

As observed gcc does not seem to have a problem in other contexts i.e.

S f(E e1, int x) {  
  S s {e1,100} ;
  return s;
}

So you do have work-arounds available.

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740