------- Comment #6 from matz at gcc dot gnu dot org 2008-02-02 16:08 ------- (written before richis comment, essentially the same info)
The compare routine doesn't need to be a template to show the bug. But it needs to take reference parameters. The difference is in the call. E.g. with this testcase: % cat x.cc enum EBorderStyle { bla = 1 }; inline bool compare_ref(const unsigned int &t, const EBorderStyle &u) { return t == u; } inline bool compare_val(const unsigned int t, const EBorderStyle u) { return t == u; } struct S { unsigned m_style : 4; }; void call_ref (S *s, EBorderStyle v) { if (!compare_ref(s->m_style, v)) s->m_style = v; } void call_val (S *s, EBorderStyle v) { if (!compare_val(s->m_style, v)) s->m_style = v; } The bodies of the two compare routines is sensible (after all nothing fancy happens as not bitfield types are involved there anymore): bool compare_ref(const unsigned int&, const EBorderStyle&) (t, u) { bool D.1655; unsigned int D.1656; EBorderStyle D.1657; D.1656 = *t; D.1657 = *u; D.1655 = D.1656 == D.1657; return D.1655; } bool compare_val(unsigned int, EBorderStyle) (t, u) { bool D.1662; D.1662 = t == u; return D.1662; } But the difference in the call setup is revealing: void call_ref(S*, EBorderStyle) (s, v) <unnamed-unsigned:4> D.1672; unsigned int D.1670; ... D.1672 = s->m_style; D.1670 = D.1672; D.1673 = compare_ref (&D.1670, &v); } void call_val(S*, EBorderStyle) (s, v) { ... <unnamed-unsigned:4> D.1682; unsigned int D.1683; ... D.1682 = s->m_style; D.1683 = (unsigned int) D.1682; D.1684 = compare_val (D.1683, v); } Note how the call with the reference parameter copies the value from the bitfield into an unsigned temp without a cast, and the call by value copies it into a unsigned temp with a cast. That latter cast will stay there until 130.final_cleanup, and hence the compare instruction will be expanded correctly. -- http://gcc.gnu.org/bugzilla/show_bug.cgi?id=35056