An assert
does not count as a piece of control flow logic. It's just a "documenting" contract check. In release builds it doesn't even happen! It's a debug tool, only.
So, you're left with a function that omits a return
statement if the scalar type width is not 16, 32 or 64. This means your program has undefined behaviour. The warning is telling you that you need to return something in all cases, even if you think the other cases won't happen at runtime.
In this case I'd probably throw an exception if you get past the switch
without returning — this may then be handled like any other exceptional case. An alternative if you don't want exceptions is to call std::terminate
yourself (which is what the assertion will ultimately do in a debug build).
However, terminating is a bit of a nuclear option and you don't really want your program terminating in production; you want it kicking out a proper diagnostic message via your existing exception handling channels, so that when your customer reports a bug, they can say "apparently makeFpConstant got a value it didn't expect" and you know what to do. And if you don't want to leak function names to customers then you could at least pick some "secret" failure code that only your team/business knows (and document it internally!).
Terminating in a debug build is often fine, though, so leave that assert
in too! Or just rely on the exception that you now have, which will result in a termination anyway if you don't catch it.
Here's how I'd probably write the function:
Id Builder::makeFpConstant(
const Id type,
const double d,
const bool specConstant
)
{
assert(isFloatType(type));
const auto width = getScalarTypeWidth(type);
switch (width) {
case 16: return makeFloat16Constant(d, specConstant);
case 32: return makeFloatConstant(d, specConstant);
case 64: return makeDoubleConstant(d, specConstant);
}
// Shouldn't get here!
throw std::logic_error(
"Unexpected scalar type width "
+ std::to_string(width)
+ " in Builder::makeFpConstant"
);
}