3

I'm getting a spurious "warning: left shift count >= width of type" when compiling some template code with gcc:

template <int N> class bitval {
unsigned long  val;
#pragma GCC diagnostic ignored "-Wall"
    const bitval &check() {
        if (N < sizeof(unsigned long) * CHAR_BIT && val >= (1UL << N)) {
            std::clog << val << " out of range for " << N << " bits in " << std::endl;
            val &= (1UL << N) - 1; }
        return *this; }
#pragma GCC diagnostic pop
};

The problem being that when it is instantiated with N == 64, it gives the warning.

Now the warning is completely spurious, as the code checks for N being large enough that no check/mask is needed, so when N is 64, the shifts will never happen. But gcc warns anyways. So I'm trying to disable the warning, but I can't figure out the needed #pragma magic to shut it up...

Chris Dodd
  • 119,907
  • 13
  • 134
  • 226
  • 2
    Clearly it's not spurious, the compiler knows more about the instantiation than you do. G++ is being nice and letting you know there is undefined behavior in your code. I would suggest using an `enable_if` to disable this template if `N` is greater than `63` – Mgetz Feb 02 '15 at 15:40
  • So you're just assuming that `unsigned long` is a 64-bit type? OK. – unwind Feb 02 '15 at 15:44
  • 1
    No, I don't think OP is assuming that (see `sizeof(unsigned long) * CHAR_BIT` but I'm guessing the compiler doesn't realise that this means the `(1UL << N)` later in the `if` is "safe". – TripeHound Feb 02 '15 at 15:48
  • OP is not assuming anything of the sort. Anyway, I think the `val >= (1UL << N)` check is superfluous; the transformation will be a nop if `val < (1UL << N)`. – Wintermute Feb 02 '15 at 15:49
  • 1
    @Mgetz: But there's no undefined behaviour. The shifts are only evaluated if the first check for `N` succeeds, in which case they are in range and well defined - as the question describes. – Mike Seymour Feb 02 '15 at 15:50
  • @MikeSeymour clearly g++ has found a way.... so either it's a g++ bug... or the code has undefined behavior. – Mgetz Feb 02 '15 at 15:57
  • 2
    @Mgetz: No, it's just a spurious warning, as the question says. The code presumably isn't analysed deeply enough to figure out that the undefined behaviour can't be triggered. – Mike Seymour Feb 02 '15 at 16:02

1 Answers1

3

Tag dispatching to the rescue:

const bitval& check() {
  constexpr bool enough_bits = (N < sizeof(unsigned long) * CHAR_BIT);
  using enough_bits_t = std::integral_constant<bool, enough_bits>;
  return check( enough_bits_t{} );
}
const bitval& check(std::false_type /*enough bits*/) {
  return *this;
} 
const bitval& check(std::true_type /*enough bits*/) {
  if (val >= (1UL << N)) {
    std::clog << val << " out of range for " << N << " bits in \n";
    val &= (1UL << N) - 1;
  }
  return *this;
}

The decision (are there enough bits) is a compile-time constant, so I calculate it as one.

I then build a type (std::true_type or std::false_type) based off that decision, and dispatch to one of two different overloads.

So the code where N is too big to be shifted that far is never instantiated, and hence no warning. If there is a logic error in the decision, and the code is instantiated, I get a compiler warning instead of silence (had you managed to disable the warning, and you had a logic error, you'd get silent UB).

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524