Consider this situation:
uint64_t add(uint32_t a, uint32_t b)
{
return a + b; // programmer neglected (uint64_t) a + b.
}
How do we get
Since the code I'm working with compiles as C or C++, and the types in question are all typedefs (which are easily retargeted to classes), it occurs to me that a C++ solution is possible. The following code sample hints at the idea:
#include
template class arith {
public:
underlying val;
arith(underlying v) : val(v) { }
explicit operator underlying () const { return val; }
outer operator +(const inner &rhs) { return val + rhs.val; }
};
struct narrow;
struct narrow_result : public arith {
narrow_result(uint32_t v) : arith(v) { }
narrow_result(const narrow &v);
};
struct narrow : public arith {
narrow(uint32_t v) : arith(v) { }
narrow(const narrow_result &v) : arith(v.val) { }
};
inline narrow_result::narrow_result(const narrow &v)
: arith(v.val)
{
}
struct wide {
uint64_t val;
wide(uint64_t v) : val(v) { }
wide(const narrow &v) : val(v) { }
operator uint64_t () const { return val; }
wide operator +(const wide &rhs) { return val + rhs.val; }
};
int main()
{
narrow a = 42;
narrow b = 9;
wide c = wide(a) + b;
wide d = a + b; // line 43
narrow e = a + b;
wide f = a; // line 45
narrow g = a + b + b; // line 46
return 0;
}
Here, GNU C++ diagnoses only line 43:
overflow.cc: In function ‘int main()’:
overflow.cc:43:16: error: conversion from ‘narrow_result’ to non-scalar type ‘wide’ requested
Note that a narrow
to wide
implicit conversion is still allowed, as seen in line 45, simply because wide
has a conversion constructor targeting narrow
directly. It just lacks one for narrow_result
.
Line 46 shows that we can compound the arithmetic operations. This is possible because narrow
implicitly converts to narrow_result
and vice versa. However, this implicit conversion doesn't kick in on line 45; the narrow_result
of the addition doesn't convert to narrow
so that this could then convert to wide
.
This can all be wrapped with #ifdef __cplusplus
and the presence of a conditional debug macro, that same macro also enabling alternative definitions of the types as typedefs for narrow
and wide
. Of course, numerous other arithmetic operations must be supported in the arith
template base.