Reputation: 10222
Writing a simple evaluate I came across a funny issue.
Given the code:
enum node_type {LEAF, NODE};
struct tree_elm_t {
enum node_type type;
union {
struct tree_node_t node;
struct tree_leaf_t leaf;
} datum;
};
int parse_leaf(struct tree_leaf_t leaf);
int parse_node( struct tree_node_t node );
int parse_tree( struct tree_elm_t* tree );
....
int parse_tree( struct tree_elm_t* tree ) {
switch( tree->type ) {
case NODE: return parse_node(tree->datum.node);
case LEAF: return parse_leaf(tree->datum.leaf);
}
}
I was surprised to see that gcc is complaining about a missing control flow option :
example.c: In function 'parse_tree':
example.c:54: warning: control reaches end of non-void function
the flow problem can be solved by storing the return value, in a variable like so:
int parse_tree( struct tree_elm_t* tree ) {
int sum;
switch( tree->type ) {
case NODE: sum = parse_node(tree->datum.node); break;
case LEAF: sum = parse_leaf(tree->datum.leaf); break;
}
return sum;
}
I do however find the original code alot cleaner, is there a way of making gcc accept the original code - (I want to static analysis to realize that my code is valid, and clean).
EDIT:
I might have been a bit unclear.
lets say I compile the following code :
int parse_tree( struct tree_elm_t* tree ) {
int sum;
switch( tree->type ) {
case NODE: sum = parse_node(tree->datum.node); break;
// case LEAF: sum = parse_leaf(tree->datum.leaf); break;
}
return sum;
}
gcc will give me a warning:
example.c: In function 'parse_tree':
example.c:51: warning: enumeration value 'LEAF' not handled in switch
meaning that gcc has a sense of the options for values in the switch, and the fact that I hav commented out the LEAF case. This would imply that gcc also knows that when going though the switch every case is being examined. so why the statement:
control reaches end of non-void function
is it lack a lacking static analysis system in gcc - or a language feature?
Upvotes: 8
Views: 1623
Reputation: 2192
To prevent the missing return warning with GCC but still get warnings if you are actually missing an enum
case
, stop the control flow after the switch
statement but before the end of the function.
In C you can use exit(int)
, quick_exit(int)
, _Exit(int)
or abort()
. (reference)
In C++ there is the throw
expression – with or without a proper exception as argument. (reference)
This also has the benefit of defined behavior in case the function gets called incorrectly.
Clang does not warn about the missing return
btw.
Upvotes: 2
Reputation: 78943
The ambiguity come from the fact that in C enum
types can store other values than the ones given in the type declaration, and that in any context, including your switch
statement, an enumeration typed object evaluates to an int
. You could avoid this warning with
switch( tree->type ) {
case NODE: return parse_node(tree->datum.node);
default: return parse_leaf(tree->datum.leaf);
}
if you think that in further developement you'll add other cases. If not, you'd better go with a bool
isNode
or something like that.
Upvotes: 1
Reputation: 42103
Your compiler is complaining because all paths in your function's logic should return a value (as the prototype of this function prescribes):
int parse_tree( struct tree_elm_t* tree ) {
switch( tree->type ) {
case NODE: return parse_node(tree->datum.node);
case LEAF: return parse_leaf(tree->datum.leaf);
default: return 0; // <-- problem solved
}
}
Compiler (like me in this answer) focuses rather on the syntax than semantics of your code.
And although you have defined enum node_type {LEAF, NODE}
, your compiler doesn't want to rely on this constraint and accepts the possibility of type
in tree->type
statement having a different value from just NODE
or LEAF
anyway.
EDIT: I have tried this code:
enum node_type {LEAF, NODE};
struct node { enum node_type type; };
int parse_tree( struct node* n ) {
switch( n->type ) {
case NODE: return 1;
case LEAF: return 2;
}
}
int main() {
struct node n;
printf("%d", parse_tree(&n));
return 0;
}
on ideone and the result is following:
(gcc-4.8.1, compiled as "C") ~ http://ideone.com/b0wdSk : code is valid, outputs 2
(gcc-4.8.1, compiled as "C++") ~ http://ideone.com/OPH5Ar : same as "C"
(gcc-4.8.1, compiled as "C99 strict") ~ http://ideone.com/ou71fe : invalid because of:
error: control reaches end of non-void function [-Werror=return-type]
And to support Martin Kristiansen's point about assigning any integral value to enum being valid, I have tried struct node n; n.type = 7;
with the same code and with "C" but also with "C99 strict" the compiler doesn't complain at all. However "C++" gives:
error: invalid conversion from ‘int’ to ‘node_type’ [-fpermissive]
Upvotes: 9
Reputation: 2389
Is there some reason this won't work? (added declarations):
void parse_leaf(struct tree_leaf_t leaf);
void parse_node( struct tree_node_t node );
void parse_tree( struct tree_elm_t* tree );
void parse_tree( struct tree_elm_t* tree ) {
switch( tree->type ) {
case NODE: parse_node(tree->datum.node); break;
case LEAF: parse_leaf(tree->datum.leaf); break;
}
}
Upvotes: -2
Reputation: 18880
Add in a default case.
switch( tree->type )
{
case NODE: return parse_node(tree->datum.node); break;
case LEAF: return parse_leaf(tree->datum.leaf); break;
default :
//perhaps throw exception here
return 0;
}
Note that this situation probably doesn't need a switch statement, if/else if, is probably what you want.
Upvotes: 0