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...

7
  • 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 Commented Feb 2, 2015 at 15:40
  • So you're just assuming that unsigned long is a 64-bit type? OK. Commented Feb 2, 2015 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". Commented Feb 2, 2015 at 15:48
  • 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. Commented Feb 2, 2015 at 15:50
  • 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. Commented Feb 2, 2015 at 16:02

1 Answer 1

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).

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.