Eric Blake wrote:
On 05/14/2014 04:45 PM, Eric Blake wrote:
> For internal structs, we might as well be type-safe and let the
> compiler help us with less typing required on our part (getting
> rid of casts is always nice). In trying to use enums directly,
> I noticed two problems in virstoragefile.h that can't be fixed
> without more invasive refactoring: virStorageSource.format is
> used as more of a union of multiple enums in storage volume
> code (so it has to remain an int), and virStorageSourcePoolDef
> refers to pooltype whose enum is declared in src/conf, but where
> src/util can't pull in headers from src/conf.
>
I'm probably going to have to revert this patch :(
There is a subtle bug here, where the set of machines where the bug
surfaces depends on the whims of the compiler.
CC conf/libvirt_conf_la-domain_conf.lo
conf/domain_conf.c: In function 'virDomainDiskSourceParse':
conf/domain_conf.c:4992:9: error: comparison of unsigned expression < 0
is always false [-Werror=type-limits]
Basically, compilers are allowed to choose whether an enum value with no
negative constants behaves as signed or unsigned; if the compiler
chooses unsigned, but we then do 'if ((value = virBlahFromString(str)) <
0)', we cause the compiler warning; if the compiler chooses signed, then
everything just works. Worse, if we do 'if ((value =
virBlahFromString(str)) <= 0)', the compiler silently does the wrong
thing (-1 returned from virBlahFromString is promoted to unsigned, so
the condition is false, and we are stuck using an out-of-range value
without warning).
On IRC, I talked about several workarounds that could keep us with an
enum type; each looks worse than what this change would cure, which is
why I think reverting is the only sane option.
1. Force the comparison in int (yucky because it adds a cast, but my
stated intention of this commit was to avoid casts, and requires a code
audit):
if ((int)(value = virBlahFromString(str)) <= 0)
2. Use temporaries (no ugly casts, but requires a code audit):
int tmp;
if ((tmp = virBlahFromString(str)) < 0)
...
value = tmp;
I have a patch along these lines to fix the build failures, but agree
that it does not sit well with your intentions of this patch.
3. Force the enum to be signed (yucky because we have to inject an
otherwise unused -1 element in each enum):
typedef enum {
VIR_BLAH__FORCE_SIGNED = -1,
VIR_BLAH_A,
VIR_BLAH_B,
VIR_BLAH_LAST
} virBlah;
4. Enhance VIR_ENUM_DECL/VIR_ENUM_IMPL to provide yet another conversion
function. The existing virBlahFromString(str) returns -1 on failure or
enum values on success; the new virBlahEnumFromString(str) returns -1 on
failure or 0 on success with the correct value stored into the address.
(yucky because you now have to know which of the two FromString variants
to call based on what type you are assigning into, because 'int*' and
'enum*' are not compatible types):
enum value;
int other;
if (virBlahEnumFromString(str, &value) < 0)
...
if ((other = virBlahFromString(str)) < 0)
,,,
5. Alter VIR_ENUM_DECL/VIR_ENUM_IMPL to make the FromString variant have
new semantics - the _LAST value is now hard-coded as the failure value
rather than -1, and the return signature is an enum (ugly because it
requires a full tree audit, and because checking for ==_LAST is not as
nice as checking for < 0; and it gets worse when also excluding 0)
if ((value = virBlahFromString(str)) == VIR_BLAH_LAST)
...error about unknown value
value = virBlahFromString(str);
if (!value || value == VIR_BLAH_LAST)
...error about bad value
Heh, hard to argue that any of these options _improve_ the code base.
Any other opinions, or should I just go ahead with the revert? This
is
breaking the build for some people, depending on their gcc version.
I'm torn. This intent of the patch is a nice improvement, but with
diminishing returns when adjusting it to work with some versions of gcc
and clang.
Regards,
Jim