Add support for semantic verification of function calls in IL.
authorMario de Sousa <msousa@fe.up.pt>
Fri, 10 Feb 2012 19:04:31 +0000
changeset 438 744b125d911e
parent 437 0e09a8840c92
child 439 cf7d6862033d
Add support for semantic verification of function calls in IL.
absyntax/absyntax.cc
absyntax/absyntax.def
absyntax/absyntax.hh
stage3/datatype_functions.hh
stage3/fill_candidate_datatypes.cc
stage3/fill_candidate_datatypes.hh
stage3/narrow_candidate_datatypes.cc
stage3/narrow_candidate_datatypes.hh
stage3/print_datatypes_error.cc
stage3/print_datatypes_error.hh
--- a/absyntax/absyntax.cc	Wed Feb 08 18:33:01 2012 +0000
+++ b/absyntax/absyntax.cc	Fri Feb 10 19:04:31 2012 +0000
@@ -131,18 +131,27 @@
 /* To insert into the begining of list, call with pos=0  */
 /* To insert into the end of list, call with pos=list->n */
 void list_c::insert_element(symbol_c *elem, int pos) {
-  int i;
   if (pos > n) ERROR;
   
   /* add new element to end of list. Basically alocate required memory... */
   /* will also increment n by 1 ! */
   add_element(elem);
   /* if not inserting into end position, shift all elements up one position, to open up a slot in pos for new element */
-  if (pos < (n-1)) for (i = n-2; i >= pos; i--) elements[i+1] = elements[i];
+  if (pos < (n-1)) for (int i = n-2; i >= pos; i--) elements[i+1] = elements[i];
   elements[pos] = elem;
 }
 
 
+/* remove element at position pos. */
+void list_c::remove_element(int pos) {
+  if (pos > n) ERROR;
+  
+  /* Shift all elements down one position, starting at the entry to delete. */
+  for (int i = pos; i < n-1; i++) elements[i] = elements[i+1];
+  /* corrent the new size, and free unused memory */
+  n--;
+  elements = (symbol_c **)realloc(elements, n * sizeof(symbol_c *));
+}
 
 #define SYM_LIST(class_name_c, ...)								\
 class_name_c::class_name_c(									\
--- a/absyntax/absyntax.def	Wed Feb 08 18:33:01 2012 +0000
+++ b/absyntax/absyntax.def	Fri Feb 10 19:04:31 2012 +0000
@@ -920,10 +920,10 @@
 SYM_REF2(il_simple_operation_c, il_simple_operator, il_operand)
 
 /* | function_name [il_operand_list] */
-/* NOTE: The parameter 'called_function_declaration' is used to pass data between the stage 3 and stage 4.
+/* NOTE: The parameter 'called_function_declaration', 'extensible_param_count' and 'candidate_functions' are used to pass data between the stage 3 and stage 4.
  *       See the comment above function_invocation_c for more details 
  */
-SYM_REF2(il_function_call_c, function_name, il_operand_list, symbol_c *called_function_declaration; int extensible_param_count;)
+SYM_REF2(il_function_call_c, function_name, il_operand_list, symbol_c *called_function_declaration; int extensible_param_count; std::vector <symbol_c *> candidate_functions;)
 
 
 /* | il_expr_operator '(' [il_operand] eol_list [simple_instr_list] ')' */
@@ -942,10 +942,10 @@
 
 
 /* | function_name '(' eol_list [il_param_list] ')' */
-/* NOTE: The parameter 'called_function_declaration' is used to pass data between the stage 3 and stage 4.
- *       See the comment above function_invocation_c for more details 
- */
-SYM_REF2(il_formal_funct_call_c, function_name, il_param_list, symbol_c *called_function_declaration; int extensible_param_count;)
+/* NOTE: The parameter 'called_function_declaration', 'extensible_param_count' and 'candidate_functions' are used to pass data between the stage 3 and stage 4.
+ *       See the comment above function_invocation_c for more details. 
+ */
+SYM_REF2(il_formal_funct_call_c, function_name, il_param_list, symbol_c *called_function_declaration; int extensible_param_count; std::vector <symbol_c *> candidate_functions;)
 
 /* | il_operand_list ',' il_operand */
 SYM_LIST(il_operand_list_c)
--- a/absyntax/absyntax.hh	Wed Feb 08 18:33:01 2012 +0000
+++ b/absyntax/absyntax.hh	Fri Feb 10 19:04:31 2012 +0000
@@ -129,6 +129,8 @@
      /* To insert into the begining of list, call with pos=0  */
      /* To insert into the end of list, call with pos=list->n */
     virtual void insert_element(symbol_c *elem, int pos = 0);
+     /* remove element at position pos. */
+    virtual void remove_element(int pos = 0);
 };
 
 
--- a/stage3/datatype_functions.hh	Wed Feb 08 18:33:01 2012 +0000
+++ b/stage3/datatype_functions.hh	Fri Feb 10 19:04:31 2012 +0000
@@ -35,6 +35,21 @@
 
 
 
+
+typedef struct {
+  symbol_c *function_name;
+  symbol_c *nonformal_operand_list;
+  symbol_c *   formal_operand_list;
+
+//symbol_c &*datatype;
+//std::vector <symbol_c *> &candidate_datatypes;
+  std::vector <symbol_c *> &candidate_functions;
+  symbol_c *&called_function_declaration;
+  int      &extensible_param_count;
+} generic_function_call_t;
+
+
+
 /* A small helper class, to transform elementary data type to string.
  * this allows us to generate more relevant error messages...
  */
--- a/stage3/fill_candidate_datatypes.cc	Wed Feb 08 18:33:01 2012 +0000
+++ b/stage3/fill_candidate_datatypes.cc	Fri Feb 10 19:04:31 2012 +0000
@@ -91,6 +91,10 @@
 		param_type = base_type(fp_iterator.param_type());
 		
 		/* check whether one of the candidate_data_types of the value being passed is the same as the param_type */
+			/* TODO
+			 * call  int search_in_datatype_list(symbol_c *datatype, std::vector <symbol_c *> candidate_datatypes);
+			 * instead of using for loop!
+			 */
 		for(i = 0; i < call_param_value->candidate_datatypes.size(); i++) {
 			/* If found (correct data type being passed), then stop the search */
 			if(is_type_equal(param_type, call_param_value->candidate_datatypes[i])) break;
@@ -138,6 +142,10 @@
 		/* Get the parameter type */
 		param_type = base_type(fp_iterator.param_type());
 		/* check whether one of the candidate_data_types of the value being passed is the same as the param_type */
+			/* TODO
+			 * call  int search_in_datatype_list(symbol_c *datatype, std::vector <symbol_c *> candidate_datatypes);
+			 * instead of using for loop!
+			 */
 		for (i = 0; i < call_param_types.size(); i++) {
 			/* If found (correct data type being passed), then stop the search */
 			if(is_type_equal(param_type, call_param_types[i])) break;
@@ -153,6 +161,98 @@
 
 
 
+/* Handle a generic function call!
+ * Assumes that the parameter_list containing the values being passed in this function invocation
+ * has already had all the candidate_datatype lists filled in!
+ *
+ * All parameters being passed to the called function MUST be in the parameter list to which f_call points to!
+ * This means that, for non formal function calls in IL, de current (default value) must be artificially added to the
+ * beginning of the parameter list BEFORE calling handle_function_call().
+ */
+/*
+typedef struct {
+  symbol_c *function_name,
+  symbol_c *nonformal_operand_list,
+  symbol_c *   formal_operand_list,
+
+  std::vector <symbol_c *> &candidate_functions,  
+  symbol_c &*called_function_declaration,
+  int      &extensible_param_count
+} generic_function_call_t;
+*/
+/*
+void narrow_candidate_datatypes_c::narrow_function_invocation(symbol_c *fcall, generic_function_call_t fcall_data) {
+void *fill_candidate_datatypes_c::handle_function_call(symbol_c *f_call, symbol_c *function_name, invocation_type_t invocation_type,
+                                                       std::vector <symbol_c *> *candidate_datatypes,
+                                                       std::vector <symbol_c *> *candidate_functions) {
+  */
+void fill_candidate_datatypes_c::handle_function_call(symbol_c *fcall, generic_function_call_t fcall_data) {
+	function_declaration_c *f_decl;
+	list_c *parameter_list;
+	list_c *parameter_candidate_datatypes;
+	symbol_c *returned_parameter_type;
+
+	if (debug) std::cout << "function()\n";
+
+	function_symtable_t::iterator lower = function_symtable.lower_bound(fcall_data.function_name);
+	function_symtable_t::iterator upper = function_symtable.upper_bound(fcall_data.function_name);
+	/* If the name of the function being called is not found in the function symbol table, then this is an invalid call */
+	/* Since the lexical parser already checks for this, then if this occurs then we have an internal compiler error. */
+	if (lower == function_symtable.end()) ERROR;
+	
+	/* Look for all compatible function declarations, and add their return datatypes 
+	 * to the candidate_datatype list of this function invocation. 
+	 *
+	 * If only one function exists, we add its return datatype to the candidate_datatype list,
+	 * even if the parameters passed to it are invalid.
+	 * This guarantees that the remainder of the expression in which the function call is inserted
+	 * is treated as if the function call returns correctly, and therefore does not generate
+	 * spurious error messages.
+	 * Even if the parameters to the function call are invalid, doing this is still safe, as the 
+	 * expressions inside the function call will themselves have erros and will  guarantee that 
+	 * compilation is aborted in stage3 (in print_datatypes_error_c).
+	 */
+	if (function_symtable.multiplicity(fcall_data.function_name) == 1) {
+		f_decl = function_symtable.get_value(lower);
+		returned_parameter_type = base_type(f_decl->type_name);
+		fcall_data.candidate_functions.push_back(f_decl);
+		fcall->    candidate_datatypes.push_back(returned_parameter_type);
+	}
+	for(; lower != upper; lower++) {
+		bool compatible = false;
+		
+		f_decl = function_symtable.get_value(lower);
+		/* Check if function declaration in symbol_table is compatible with parameters */
+		if (NULL != fcall_data.nonformal_operand_list) compatible=match_nonformal_call(fcall, f_decl);
+		if (NULL != fcall_data.   formal_operand_list) compatible=   match_formal_call(fcall, f_decl);
+		if (compatible) {
+			/* Add the data type returned by the called functions. 
+			 * However, only do this if this data type is not already present in the candidate_datatypes list_c
+			 */
+			/* TODO
+			 * call  int search_in_datatype_list(symbol_c *datatype, std::vector <symbol_c *> candidate_datatypes);
+			 * instead of using for loop!
+			 */
+			unsigned int k;
+			returned_parameter_type = base_type(f_decl->type_name);
+			for(k = 0; k < fcall->candidate_datatypes.size(); k++) {
+				if (is_type_equal(returned_parameter_type, fcall->candidate_datatypes[k]))
+					break;
+			}
+			if (k >= fcall->candidate_datatypes.size()) {
+				fcall->    candidate_datatypes.push_back(returned_parameter_type);
+				fcall_data.candidate_functions.push_back(f_decl);
+			}
+		}
+	}
+	if (debug) std::cout << "end_function() [" << fcall->candidate_datatypes.size() << "] result.\n";
+	return;
+}
+
+
+
+
+
 /* a helper function... */
 symbol_c *fill_candidate_datatypes_c::base_type(symbol_c *symbol) {
 	/* NOTE: symbol == NULL is valid. It will occur when, for e.g., an undefined/undeclared symbolic_variable is used
@@ -667,15 +767,42 @@
 	return NULL;
 }
 
+
+/* | function_name [il_operand_list] */
+/* NOTE: The parameters 'called_function_declaration' and 'extensible_param_count' are used to pass data between the stage 3 and stage 4. */
+// SYM_REF2(il_function_call_c, function_name, il_operand_list, symbol_c *called_function_declaration; int extensible_param_count;)
 void *fill_candidate_datatypes_c::visit(il_function_call_c *symbol) {
-}
-
-/* MJS: Manuele, could you please not delete the following 2 lines of comments. They help me understand where this class is used
- *     and when it is created by bison - syntax parse, and how it can show up in the abstract syntax tree.
- *
- *       Actually, it could be helpful if we could have all the similar comments already present in visit_expression_type_c
- *       in the 3 new classes fill/narrow/print candidate datatype 
- */
+	/* The first parameter of a non formal function call in IL will be the 'current value' (i.e. the prev_il_instruction)
+	 * In order to be able to handle this without coding special cases, we will simply prepend that symbol
+	 * to the il_operand_list, and remove it later (in the print_datatypes_error_c).
+	 *
+	 * However, if no further paramters are given, then il_operand_list will be NULL, and we will
+	 * need to create a new object to hold the pointer to prev_il_instruction.
+	 * This change will also be undone later in print_datatypes_error_c.
+	 */
+	if (NULL == symbol->il_operand_list)  symbol->il_operand_list = new il_operand_list_c;
+	if (NULL == symbol->il_operand_list)  ERROR;
+
+	symbol->il_operand_list->accept(*this);
+
+	if (NULL == prev_il_instruction)      return NULL;
+	((list_c *)symbol->il_operand_list)->insert_element(prev_il_instruction, 0);	
+
+	generic_function_call_t fcall_param = {
+	/* fcall_param.function_name               = */ symbol->function_name,
+	/* fcall_param.nonformal_operand_list      = */ symbol->il_operand_list,
+	/* fcall_param.formal_operand_list         = */ NULL,
+	/* fcall_param.candidate_functions         = */ symbol->candidate_functions,
+	/* fcall_param.called_function_declaration = */ symbol->called_function_declaration,
+	/* fcall_param.extensible_param_count      = */ symbol->extensible_param_count
+	};
+	handle_function_call(symbol, fcall_param);
+
+	if (debug) std::cout << "il_function_call_c [" << symbol->candidate_datatypes.size() << "] result.\n";
+	return NULL;
+}
+
+
 /* | il_expr_operator '(' [il_operand] eol_list [simple_instr_list] ')' */
 // SYM_REF3(il_expression_c, il_expr_operator, il_operand, simple_instr_list);
 void *fill_candidate_datatypes_c::visit(il_expression_c *symbol) {
@@ -717,8 +844,24 @@
 void *fill_candidate_datatypes_c::visit(il_fb_call_c *symbol) {
 }
 
+/* | function_name '(' eol_list [il_param_list] ')' */
+/* NOTE: The parameter 'called_function_declaration' is used to pass data between the stage 3 and stage 4. */
+// SYM_REF2(il_formal_funct_call_c, function_name, il_param_list, symbol_c *called_function_declaration; int extensible_param_count;)
 void *fill_candidate_datatypes_c::visit(il_formal_funct_call_c *symbol) {
-
+	symbol->il_param_list->accept(*this);
+
+	generic_function_call_t fcall_param = {
+	/* fcall_param.function_name               = */ symbol->function_name,
+	/* fcall_param.nonformal_operand_list      = */ NULL,
+	/* fcall_param.formal_operand_list         = */ symbol->il_param_list,
+	/* fcall_param.candidate_functions         = */ symbol->candidate_functions,
+	/* fcall_param.called_function_declaration = */ symbol->called_function_declaration,
+	/* fcall_param.extensible_param_count      = */ symbol->extensible_param_count
+	};
+	handle_function_call(symbol, fcall_param);
+
+	if (debug) std::cout << "il_formal_funct_call_c [" << symbol->candidate_datatypes.size() << "] result.\n";
+	return NULL;
 }
 
 /*
@@ -1711,70 +1854,21 @@
 
 
 void *fill_candidate_datatypes_c::visit(function_invocation_c *symbol) {
-	function_declaration_c *f_decl;
-	list_c *parameter_list;
-	list_c *parameter_candidate_datatypes;
-	symbol_c *returned_parameter_type;
-
-	if (debug) std::cout << "function()\n";
-
-	function_symtable_t::iterator lower = function_symtable.lower_bound(symbol->function_name);
-	function_symtable_t::iterator upper = function_symtable.upper_bound(symbol->function_name);
-	/* If the name of the function being called is not found in the function symbol table, then this is an invalid call */
-	/* Since the lexical parser already checks for this, then if this occurs then we have an internal compiler error. */
-	if (lower == function_symtable.end()) ERROR;
-	
-	if (NULL != symbol->formal_param_list)
-		parameter_list = (list_c *)symbol->formal_param_list;
-	else if (NULL != symbol->nonformal_param_list)
-		parameter_list = (list_c *)symbol->nonformal_param_list;
+	if      (NULL != symbol->formal_param_list)        symbol->   formal_param_list->accept(*this);
+	else if (NULL != symbol->nonformal_param_list)     symbol->nonformal_param_list->accept(*this);
 	else ERROR;
-	
-	/* Fill in the candidate_datatypes lists of all the expressions used in the function call parameters */
-	parameter_list->accept(*this);
-
-	/* Look for all compatible function declarations, and add their return datatypes 
-	 * to the candidate_datatype list of this function invocation. 
-	 *
-	 * If only one function exists, we add its return datatype to the candidate_datatype list,
-	 * even if the parameters passed to it are invalid.
-	 * This guarantees that the remainder of the expression in which the function call is inserted
-	 * is treated as if the function call returns correctly, and therefore does not generate
-	 * spurious error messages.
-	 * Even if the parameters to the function call are invalid, doing this is still safe, as the 
-	 * expressions inside the function call will themselves have erros and will  guarantee that 
-	 * compilation is aborted in stage3 (in print_datatypes_error_c).
-	 */
-	if (function_symtable.multiplicity(symbol->function_name) == 1) {
-		f_decl = function_symtable.get_value(lower);
-		returned_parameter_type = base_type(f_decl->type_name);
-		symbol->candidate_functions.push_back(f_decl);
-		symbol->candidate_datatypes.push_back(returned_parameter_type);
-	}
-	for(; lower != upper; lower++) {
-		bool compatible = false;
-		
-		f_decl = function_symtable.get_value(lower);
-		/* Check if function declaration in symbol_table is compatible with parameters */
-		if (NULL != symbol->nonformal_param_list)  compatible=match_nonformal_call(symbol, f_decl);
-		if (NULL != symbol->   formal_param_list)  compatible=   match_formal_call(symbol, f_decl);
-		if (compatible) {
-			/* Add the data type returned by the called functions. 
-			 * However, only do this if this data type is not already present in the candidate_datatypes list_c
-			 */
-			unsigned int k;
-			returned_parameter_type = base_type(f_decl->type_name);
-			for(k = 0; k < symbol->candidate_datatypes.size(); k++) {
-				if (is_type_equal(returned_parameter_type, symbol->candidate_datatypes[k]))
-					break;
-			}
-			if (k >= symbol->candidate_datatypes.size()) {
-				symbol->candidate_datatypes.push_back(returned_parameter_type);
-				symbol->candidate_functions.push_back(f_decl);
-			}
-		}
-	}
-	if (debug) std::cout << "end_function() [" << symbol->candidate_datatypes.size() << "] result.\n";
+
+	generic_function_call_t fcall_param = {
+	/* fcall_param.function_name               = */ symbol->function_name,
+	/* fcall_param.nonformal_operand_list      = */ symbol->nonformal_param_list,
+	/* fcall_param.formal_operand_list         = */ symbol->formal_param_list,
+	/* fcall_param.candidate_functions         = */ symbol->candidate_functions,
+	/* fcall_param.called_function_declaration = */ symbol->called_function_declaration,
+	/* fcall_param.extensible_param_count      = */ symbol->extensible_param_count
+	};
+	handle_function_call(symbol, fcall_param);
+
+	if (debug) std::cout << "function_invocation_c [" << symbol->candidate_datatypes.size() << "] result.\n";
 	return NULL;
 }
 
--- a/stage3/fill_candidate_datatypes.hh	Wed Feb 08 18:33:01 2012 +0000
+++ b/stage3/fill_candidate_datatypes.hh	Fri Feb 10 19:04:31 2012 +0000
@@ -34,6 +34,7 @@
 
 
 #include "../absyntax_utils/absyntax_utils.hh"
+#include "datatype_functions.hh"
 
 class fill_candidate_datatypes_c: public iterator_visitor_c {
 
@@ -80,20 +81,20 @@
     symbol_c *il_operand;
     symbol_c *widening_conversion(symbol_c *left_type, symbol_c *right_type, const struct widen_entry widen_table[]);
 
-  public:
-    fill_candidate_datatypes_c(symbol_c *ignore);
-    virtual ~fill_candidate_datatypes_c(void);
-
     /* Match a function declaration with a function call through their parameters.*/
     /* returns true if compatible function/FB invocation, otherwise returns false */
     bool match_nonformal_call(symbol_c *f_call, symbol_c *f_decl);
     bool match_formal_call   (symbol_c *f_call, symbol_c *f_decl);
-
-    void *compute_standard_function_default(function_invocation_c *st_symbol, il_formal_funct_call_c *il_symbol);
-    void *compute_standard_function_il(il_function_call_c *symbol, symbol_c *param_data_type);
+    void handle_function_call(symbol_c *fcall, generic_function_call_t fcall_data);
 
     /* a helper function... */
     symbol_c *base_type(symbol_c *symbol);
+    
+    
+  public:
+    fill_candidate_datatypes_c(symbol_c *ignore);
+    virtual ~fill_candidate_datatypes_c(void);
+
 
     /*********************/
     /* B 1.2 - Constants */
--- a/stage3/narrow_candidate_datatypes.cc	Wed Feb 08 18:33:01 2012 +0000
+++ b/stage3/narrow_candidate_datatypes.cc	Fri Feb 10 19:04:31 2012 +0000
@@ -182,6 +182,59 @@
 }
 
 
+/*
+typedef struct {
+  symbol_c *function_name,
+  symbol_c *nonformal_operand_list,
+  symbol_c *   formal_operand_list,
+
+  std::vector <symbol_c *> &candidate_functions,  
+  symbol_c &*called_function_declaration,
+  int      &extensible_param_count
+} generic_function_call_t;
+*/
+void narrow_candidate_datatypes_c::narrow_function_invocation(symbol_c *fcall, generic_function_call_t fcall_data) {
+	/* set the called_function_declaration. */
+	fcall_data.called_function_declaration = NULL;
+
+	/* set the called_function_declaration taking into account the datatype that we need to return */
+	for(unsigned int i = 0; i < fcall->candidate_datatypes.size(); i++) {
+		if (is_type_equal(fcall->candidate_datatypes[i], fcall->datatype)) {
+			fcall_data.called_function_declaration = fcall_data.candidate_functions[i];
+			break;
+		}
+	}
+
+	/* NOTE: If we can't figure out the declaration of the function being called, this is not 
+	 *       necessarily an internal compiler error. It could be because the symbol->datatype is NULL
+	 *       (because the ST code being analysed has an error _before_ this function invocation).
+	 *       However, we don't just give, up, we carry on recursivly analysing the code, so as to be
+	 *       able to print out any error messages related to the parameters being passed in this function 
+	 *       invocation.
+	 */
+	/* if (NULL == symbol->called_function_declaration) ERROR; */
+	if (fcall->candidate_datatypes.size() == 1) {
+		/* If only one function declaration, then we use that (even if symbol->datatypes == NULL)
+		 * so we can check for errors in the expressions used to pass parameters in this
+		 * function invocation.
+		 */
+		fcall_data.called_function_declaration = fcall_data.candidate_functions[0];
+	}
+
+	/* If an overloaded function is being invoked, and we cannot determine which version to use,
+	 * then we can not meaningfully verify the expressions used inside that function invocation.
+	 * We simply give up!
+	 */
+	if (NULL == fcall_data.called_function_declaration)
+		return;
+
+	if (NULL != fcall_data.nonformal_operand_list)  narrow_nonformal_call(fcall, fcall_data.called_function_declaration, &(fcall_data.extensible_param_count));
+	if (NULL != fcall_data.   formal_operand_list)     narrow_formal_call(fcall, fcall_data.called_function_declaration, &(fcall_data.extensible_param_count));
+
+	return;
+}
+
+
 
 /* a helper function... */
 symbol_c *narrow_candidate_datatypes_c::base_type(symbol_c *symbol) {
@@ -324,7 +377,27 @@
 	return NULL;
 }
 
+/* | function_name [il_operand_list] */
+/* NOTE: The parameters 'called_function_declaration' and 'extensible_param_count' are used to pass data between the stage 3 and stage 4. */
+// SYM_REF2(il_function_call_c, function_name, il_operand_list, symbol_c *called_function_declaration; int extensible_param_count;)
 void *narrow_candidate_datatypes_c::visit(il_function_call_c *symbol) {
+	generic_function_call_t fcall_param = {
+	/* fcall_param.function_name               = */ symbol->function_name,
+	/* fcall_param.nonformal_operand_list      = */ symbol->il_operand_list,
+	/* fcall_param.formal_operand_list         = */ NULL,
+	/* fcall_param.candidate_functions         = */ symbol->candidate_functions,
+	/* fcall_param.called_function_declaration = */ symbol->called_function_declaration,
+	/* fcall_param.extensible_param_count      = */ symbol->extensible_param_count
+	};
+
+	/* The first parameter of a non formal function call in IL will be the 'current value' (i.e. the prev_il_instruction)
+	 * In order to be able to handle this without coding special cases, we simply prepend that symbol
+	 * to the il_operand_list (done in fill_candidate_datatypes_c), and remove it later (in the print_datatypes_error_c).
+	 *
+	 * Since this class is executed after fill_candidate_datatypes_c, and before print_datatypes_error_c,
+	 * the following code is actually correct!
+	 */
+	narrow_function_invocation(symbol, fcall_param);
 	return NULL;
 }
 
@@ -345,7 +418,20 @@
 	return NULL;
 }
 
+/* | function_name '(' eol_list [il_param_list] ')' */
+/* NOTE: The parameter 'called_function_declaration' is used to pass data between the stage 3 and stage 4. */
+// SYM_REF2(il_formal_funct_call_c, function_name, il_param_list, symbol_c *called_function_declaration; int extensible_param_count;)
 void *narrow_candidate_datatypes_c::visit(il_formal_funct_call_c *symbol) {
+	generic_function_call_t fcall_param = {
+	/* fcall_param.function_name               = */ symbol->function_name,
+	/* fcall_param.nonformal_operand_list      = */ NULL,
+	/* fcall_param.formal_operand_list         = */ symbol->il_param_list,
+	/* fcall_param.candidate_functions         = */ symbol->candidate_functions,
+	/* fcall_param.called_function_declaration = */ symbol->called_function_declaration,
+	/* fcall_param.extensible_param_count      = */ symbol->extensible_param_count
+	};
+  
+	narrow_function_invocation(symbol, fcall_param);
 	return NULL;
 }
 
@@ -1020,47 +1106,22 @@
 }
 
 
+
+/* NOTE: The parameter 'called_function_declaration', 'extensible_param_count' and 'candidate_functions' are used to pass data between the stage 3 and stage 4. */
+/*    formal_param_list -> may be NULL ! */
+/* nonformal_param_list -> may be NULL ! */
+// SYM_REF3(function_invocation_c, function_name, formal_param_list, nonformal_param_list, symbol_c *called_function_declaration; int extensible_param_count; std::vector <symbol_c *> candidate_functions;)
 void *narrow_candidate_datatypes_c::visit(function_invocation_c *symbol) {
-	int  ext_parm_count;
-
-	/* set the called_function_declaration. */
-	symbol->called_function_declaration = NULL;
-
-	/* set the called_function_declaration taking into account the datatype that we need to return */
-	for(unsigned int i = 0; i < symbol->candidate_datatypes.size(); i++) {
-		if (is_type_equal(symbol->candidate_datatypes[i], symbol->datatype)) {
-			symbol->called_function_declaration = symbol->candidate_functions[i];
-			break;
-		}
-	}
-
-	/* NOTE: If we can't figure out the declaration of the function being called, this is not 
-	 *       necessarily an internal compiler error. It could be because the symbol->datatype is NULL
-	 *       (because the ST code being analysed has an error _before_ this function invocation).
-	 *       However, we don't just give, up, we carry on recursivly analysing the code, so as to be
-	 *       able to print out any error messages related to the parameters being passed in this function 
-	 *       invocation.
-	 */
-	/* if (NULL == symbol->called_function_declaration) ERROR; */
-	if (symbol->candidate_datatypes.size() == 1) {
-		/* If only one function declaration, then we use that (even if symbol->datatypes == NULL)
-		 * so we can check for errors in the expressions used to pass parameters in this
-		 * function invocation.
-		 */
-		symbol->called_function_declaration = symbol->candidate_functions[0];
-	}
-
-	/* If an overloaded function is being invoked, and we cannot determine which version to use,
-	 * then we can not meaningfully verify the expressions used inside that function invocation.
-	 * We simply give up!
-	 */
-	if (NULL == symbol->called_function_declaration)
-		return NULL;
-
-	if (NULL != symbol->nonformal_param_list)  narrow_nonformal_call(symbol, symbol->called_function_declaration, &ext_parm_count);
-	if (NULL != symbol->   formal_param_list)     narrow_formal_call(symbol, symbol->called_function_declaration, &ext_parm_count);
-	symbol->extensible_param_count = ext_parm_count;
-
+	generic_function_call_t fcall_param = {
+	/* fcall_param.function_name               = */ symbol->function_name,
+	/* fcall_param.nonformal_operand_list      = */ symbol->nonformal_param_list,
+	/* fcall_param.formal_operand_list         = */ symbol->formal_param_list,
+	/* fcall_param.candidate_functions         = */ symbol->candidate_functions,
+	/* fcall_param.called_function_declaration = */ symbol->called_function_declaration,
+	/* fcall_param.extensible_param_count      = */ symbol->extensible_param_count
+	};
+  
+	narrow_function_invocation(symbol, fcall_param);
 	return NULL;
 }
 
--- a/stage3/narrow_candidate_datatypes.hh	Wed Feb 08 18:33:01 2012 +0000
+++ b/stage3/narrow_candidate_datatypes.hh	Fri Feb 10 19:04:31 2012 +0000
@@ -34,6 +34,7 @@
 
 
 #include "../absyntax_utils/absyntax_utils.hh"
+#include "datatype_functions.hh"
 
 class narrow_candidate_datatypes_c: public iterator_visitor_c {
 
@@ -47,11 +48,13 @@
 
     bool is_widening_compatible(symbol_c *left_type, symbol_c *right_type, symbol_c *result_type, const struct widen_entry widen_table[]);
 
+    void narrow_function_invocation(symbol_c *f_call, generic_function_call_t fcall_data);
+    void narrow_nonformal_call(symbol_c *f_call, symbol_c *f_decl, int *ext_parm_count = NULL);
+    void narrow_formal_call(symbol_c *f_call, symbol_c *f_decl, int *ext_parm_count = NULL);
+
   public:
     narrow_candidate_datatypes_c(symbol_c *ignore);
     virtual ~narrow_candidate_datatypes_c(void);
-    void narrow_nonformal_call(symbol_c *f_call, symbol_c *f_decl, int *ext_parm_count = NULL);
-    void narrow_formal_call(symbol_c *f_call, symbol_c *f_decl, int *ext_parm_count = NULL);
 
     symbol_c *base_type(symbol_c *symbol);
 
--- a/stage3/print_datatypes_error.cc	Wed Feb 08 18:33:01 2012 +0000
+++ b/stage3/print_datatypes_error.cc	Fri Feb 10 19:04:31 2012 +0000
@@ -90,6 +90,76 @@
 	return (symbol_c *)symbol->accept(search_base_type);
 }
 
+
+
+
+/*
+typedef struct {
+  symbol_c *function_name,
+  symbol_c *nonformal_operand_list,
+  symbol_c *   formal_operand_list,
+
+  std::vector <symbol_c *> &candidate_functions,  
+  symbol_c &*called_function_declaration,
+  int      &extensible_param_count
+} generic_function_call_t;
+*/
+void print_datatypes_error_c::handle_function_invocation(symbol_c *fcall, generic_function_call_t fcall_data) {
+	symbol_c *param_value, *param_name;
+	function_call_param_iterator_c fcp_iterator(fcall);
+	bool function_invocation_error = false;
+
+	if ((NULL != fcall_data.formal_operand_list) && (NULL != fcall_data.nonformal_operand_list)) 
+		ERROR;
+
+	symbol_c *f_decl = fcall_data.called_function_declaration;
+	if (NULL == f_decl) {
+		STAGE3_ERROR(0, fcall, fcall, "Unable to resolve which overloaded function '%s' is being invoked.", ((identifier_c *)fcall_data.function_name)->value);
+		/* we now try to find any function declaration with the same name, just so we can provide some relevant error messages */
+		function_symtable_t::iterator lower = function_symtable.lower_bound(fcall_data.function_name);
+		if (lower == function_symtable.end()) ERROR;
+		f_decl = function_symtable.get_value(lower);
+	}
+
+	if (NULL != fcall_data.formal_operand_list) {
+		fcall_data.formal_operand_list->accept(*this);
+		if (NULL != f_decl) {
+			function_param_iterator_c fp_iterator(f_decl);
+			while ((param_name = fcp_iterator.next_f()) != NULL) {
+				param_value = fcp_iterator.get_current_value();
+				/* Find the corresponding parameter in function declaration */
+				if (NULL == fp_iterator.search(param_name)) {
+					STAGE3_ERROR(0, fcall, fcall, "Invalid parameter '%s' when invoking function '%s'", ((identifier_c *)param_name)->value, ((identifier_c *)fcall_data.function_name)->value);		  
+				} else if (NULL == param_value->datatype) {
+					function_invocation_error = true;
+					STAGE3_ERROR(0, fcall, fcall, "Data type incompatibility between parameter '%s' and value being passed, when invoking function '%s'", ((identifier_c *)param_name)->value, ((identifier_c *)fcall_data.function_name)->value);
+				}
+			}
+		}
+	}
+	if (NULL != fcall_data.nonformal_operand_list) {
+		fcall_data.nonformal_operand_list->accept(*this);
+		if (f_decl)
+			for (int i = 1; (param_value = fcp_iterator.next_nf()) != NULL; i++) {
+				if (NULL == param_value->datatype) {
+					function_invocation_error = true;
+					STAGE3_ERROR(0, fcall, fcall, "Data type incompatibility for value passed in position %d when invoking function '%s'", i, ((identifier_c *)fcall_data.function_name)->value);
+				}
+			}
+	}
+
+	if (function_invocation_error) {
+		/* No compatible function exists */
+		STAGE3_ERROR(2, fcall, fcall, "Invalid parameters when invoking function '%s'", ((identifier_c *)fcall_data.function_name)->value);
+	} 
+
+	return;
+}
+
+
+
+
+
 /*********************/
 /* B 1.2 - Constants */
 /*********************/
@@ -444,7 +514,33 @@
 	return NULL;
 }
 
+/* | function_name [il_operand_list] */
+/* NOTE: The parameters 'called_function_declaration' and 'extensible_param_count' are used to pass data between the stage 3 and stage 4. */
+// SYM_REF2(il_function_call_c, function_name, il_operand_list, symbol_c *called_function_declaration; int extensible_param_count;)
 void *print_datatypes_error_c::visit(il_function_call_c *symbol) {
+	generic_function_call_t fcall_param = {
+	/* fcall_param.function_name               = */ symbol->function_name,
+	/* fcall_param.nonformal_operand_list      = */ symbol->il_operand_list,
+	/* fcall_param.formal_operand_list         = */ NULL,
+	/* fcall_param.candidate_functions         = */ symbol->candidate_functions,
+	/* fcall_param.called_function_declaration = */ symbol->called_function_declaration,
+	/* fcall_param.extensible_param_count      = */ symbol->extensible_param_count
+	};
+
+	handle_function_invocation(symbol, fcall_param);
+	
+	/* The first parameter of a non formal function call in IL will be the 'current value' (i.e. the prev_il_instruction)
+	 * In order to be able to handle this without coding special cases, we will simply prepend that symbol
+	 * to the il_operand_list. This is done in fill_candidate_datatypes_c.
+	 * We now undo those changes!
+	 */  
+	((list_c *)symbol->il_operand_list)->remove_element(0);
+	if (((list_c *)symbol->il_operand_list)->n == 0) {
+		/* if the list becomes empty, then that means that it did not exist before we made these changes, so we delete it! */
+		delete 	symbol->il_operand_list;
+		symbol->il_operand_list = NULL;
+	}
+
 	return NULL;
 }
 
@@ -456,7 +552,20 @@
 	return NULL;
 }
 
+/* | function_name '(' eol_list [il_param_list] ')' */
+/* NOTE: The parameter 'called_function_declaration' is used to pass data between the stage 3 and stage 4. */
+// SYM_REF2(il_formal_funct_call_c, function_name, il_param_list, symbol_c *called_function_declaration; int extensible_param_count;)
 void *print_datatypes_error_c::visit(il_formal_funct_call_c *symbol) {
+	generic_function_call_t fcall_param = {
+	/* fcall_param.function_name               = */ symbol->function_name,
+	/* fcall_param.nonformal_operand_list      = */ NULL,
+	/* fcall_param.formal_operand_list         = */ symbol->il_param_list,
+	/* fcall_param.candidate_functions         = */ symbol->candidate_functions,
+	/* fcall_param.called_function_declaration = */ symbol->called_function_declaration,
+	/* fcall_param.extensible_param_count      = */ symbol->extensible_param_count
+	};
+  
+	handle_function_invocation(symbol, fcall_param);
 	return NULL;
 }
 
--- a/stage3/print_datatypes_error.hh	Wed Feb 08 18:33:01 2012 +0000
+++ b/stage3/print_datatypes_error.hh	Fri Feb 10 19:04:31 2012 +0000
@@ -34,6 +34,8 @@
 
 
 #include "../absyntax_utils/absyntax_utils.hh"
+#include "datatype_functions.hh"
+
 
 class print_datatypes_error_c: public iterator_visitor_c {
 
@@ -78,14 +80,15 @@
     symbol_c *il_operand_type;
     symbol_c *il_operand;
 
+    /* some helper functions... */
+    symbol_c *base_type(symbol_c *symbol);
+    void handle_function_invocation(symbol_c *fcall, generic_function_call_t fcall_data);
 
   public:
     print_datatypes_error_c(symbol_c *ignore);
     virtual ~print_datatypes_error_c(void);
     int get_error_found();
 
-    /* a helper function... */
-    symbol_c *base_type(symbol_c *symbol);
 
     /*********************/
     /* B 1.2 - Constants */