Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

after defining case for all enum values, compiler still says: "control reaches end of non-void function"

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?

like image 385
Martin Kristiansen Avatar asked Sep 08 '13 04:09

Martin Kristiansen


2 Answers

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]

like image 91
LihO Avatar answered Sep 23 '22 05:09

LihO


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.

like image 34
Darklighter Avatar answered Sep 19 '22 05:09

Darklighter