Semantic verification: Add compatibility check for parameter direction in function invocation.
authorMario de Sousa <msousa@fe.up.pt>
Sun, 19 Feb 2012 16:16:45 +0000
changeset 449 3c6225521059
parent 448 1bd18fc06911
child 450 eb1b28acec2e
Semantic verification: Add compatibility check for parameter direction in function invocation.
absyntax_utils/function_call_param_iterator.cc
absyntax_utils/function_call_param_iterator.hh
stage3/fill_candidate_datatypes.cc
stage3/print_datatypes_error.cc
--- a/absyntax_utils/function_call_param_iterator.cc	Sat Feb 18 21:03:01 2012 +0000
+++ b/absyntax_utils/function_call_param_iterator.cc	Sun Feb 19 16:16:45 2012 +0000
@@ -184,6 +184,7 @@
  */
 symbol_c *function_call_param_iterator_c::next_f(void) {
   current_value = NULL;
+  current_assign_direction = assign_none;
   param_count = 0;
   iterate_f_next_param++;
   current_operation = function_call_param_iterator_c::iterate_f_op;
@@ -201,6 +202,7 @@
  */
 symbol_c *function_call_param_iterator_c::next_nf(void) {
   current_value = NULL;
+  current_assign_direction = assign_none;
   param_count = 0;
   iterate_nf_next_param++;
   current_operation = function_call_param_iterator_c::iterate_nf_op;
@@ -212,6 +214,7 @@
 /* Search for the value passed to the parameter named <param_name>...  */
 symbol_c *function_call_param_iterator_c::search_f(symbol_c *param_name) {
   current_value = NULL;
+  current_assign_direction = assign_none;
   if (NULL == param_name) ERROR;
   search_param_name = dynamic_cast<identifier_c *>(param_name);
   if (NULL == search_param_name) ERROR;
@@ -233,6 +236,12 @@
   return current_value;
 }
 
+/* Returns the value being passed to the current parameter. */
+function_call_param_iterator_c::assign_direction_t function_call_param_iterator_c::get_assign_direction(void) {
+  return current_assign_direction;
+}
+
+
 /********************************/
 /* B 1.7 Configuration elements */
 /********************************/
@@ -339,6 +348,7 @@
   if (NULL == symb_var)
     ERROR;
 
+  current_assign_direction = assign_in;
   return handle_parameter_assignment(symb_var->var_name, symbol->prog_data_source);
 }
 
@@ -355,6 +365,7 @@
   if (NULL == symb_var)
     ERROR;
 
+  current_assign_direction = assign_out;
   return handle_parameter_assignment(symb_var->var_name, symbol->data_sink);
 }
 
@@ -459,6 +470,7 @@
   // since we do not yet support it, it is best to simply stop than to fail silently...
   if (NULL != symbol->simple_instr_list) ERROR;
 
+  current_assign_direction = assign_in;
   return handle_parameter_assignment((symbol_c *)symbol->il_assign_operator->accept(*this), symbol->il_operand);
 }
 
@@ -466,6 +478,7 @@
 // SYM_REF2(il_param_out_assignment_c, il_assign_out_operator, variable);
 void *function_call_param_iterator_c::visit(il_param_out_assignment_c *symbol) {
   TRACE("il_param_out_assignment_c");
+  current_assign_direction = assign_out;
   return handle_parameter_assignment((symbol_c *)symbol->il_assign_out_operator->accept(*this), symbol->variable);
 }
 
@@ -564,6 +577,7 @@
 // SYM_REF2(input_variable_param_assignment_c, variable_name, expression)
 void *function_call_param_iterator_c::visit(input_variable_param_assignment_c *symbol) {
   TRACE("input_variable_param_assignment_c");
+  current_assign_direction = assign_in;
   return handle_parameter_assignment(symbol->variable_name, symbol->expression);
 }
 
@@ -574,6 +588,7 @@
   // TODO : Handle not_param !!!
   if (NULL != symbol->not_param) ERROR;  // we do not yet support it, so it is best to simply stop than to fail silently...
 
+  current_assign_direction = assign_out;
   return handle_parameter_assignment(symbol->variable_name, symbol->variable);
 }
 
--- a/absyntax_utils/function_call_param_iterator.hh	Sat Feb 18 21:03:01 2012 +0000
+++ b/absyntax_utils/function_call_param_iterator.hh	Sun Feb 19 16:16:45 2012 +0000
@@ -51,6 +51,58 @@
 class function_call_param_iterator_c : public null_visitor_c {
 
   private:
+    void *search_list(list_c *list);
+    void *handle_parameter_assignment(symbol_c *variable_name, symbol_c *expression) ;
+
+
+  public:
+    /* start off at the first parameter once again... */
+    void reset(void);
+
+    /* initialise the iterator object.
+     * We must be given a reference to the function/program/function block call
+     * that will be analysed...
+     */
+    function_call_param_iterator_c(symbol_c *f_call);
+
+    /* Skip to the next formal parameter. After object creation,
+     * the object references on parameter _before_ the first, so
+     * this function must be called once to get the object to
+     * reference the first parameter...
+     *
+     * Returns the paramater name to which a value is being passed!
+     * You can determine the value being passed by calling 
+     * function_call_param_iterator_c::search_f()
+     */
+    symbol_c *next_f(void);
+
+    /* Skip to the next non-formal parameter. After object creation,
+     * the object references on parameter _before_ the first, so
+     * this function must be called once to get the object to
+     * reference the first parameter...
+     *
+     * Returns whatever is being passed to the parameter!
+     */
+    symbol_c *next_nf(void);
+
+    /* Search for the value passed to the parameter named <param_name>...  */
+    symbol_c *search_f(symbol_c *param_name);
+    symbol_c *search_f(const char *param_name);
+
+    /* Returns the value being passed to the current parameter. */
+    symbol_c *get_current_value(void);
+    
+    /* A type to specify how the current parameter was assigned.
+     * param_name := var   -> assign_in
+     * param_name => var   -> assign_out
+     *                     -> assign_none  (used when handling non formal calls, when no assignment type is used)
+     */
+    typedef enum {assign_in, assign_out, assign_none} assign_direction_t ;
+    /* Returns the assignment direction of the current parameter. */
+    assign_direction_t get_assign_direction(void);
+
+
+  private:
       /* a pointer to the function call
        * (or function block or program call!)
        */
@@ -58,6 +110,7 @@
     int iterate_f_next_param, iterate_nf_next_param, param_count;
     identifier_c *search_param_name;
     symbol_c *current_value;
+    assign_direction_t current_assign_direction;
 
     /* Which operation of the class was called:
      *  - iterate to the next non-formal parameter. 
@@ -67,49 +120,7 @@
     typedef enum {iterate_nf_op, iterate_f_op, search_f_op} operation_t;
     operation_t current_operation;
 
-  private:
-    void *search_list(list_c *list);
-    void *handle_parameter_assignment(symbol_c *variable_name, symbol_c *expression) ;
-
-
-  public:
-    /* start off at the first parameter once again... */
-    void reset(void);
-
-    /* initialise the iterator object.
-     * We must be given a reference to the function/program/function block call
-     * that will be analysed...
-     */
-    function_call_param_iterator_c(symbol_c *f_call);
-
-    /* Skip to the next formal parameter. After object creation,
-     * the object references on parameter _before_ the first, so
-     * this function must be called once to get the object to
-     * reference the first parameter...
-     *
-     * Returns the paramater name to which a value is being passed!
-     * You can determine the value being passed by calling 
-     * function_call_param_iterator_c::search_f()
-     */
-    symbol_c *next_f(void);
-
-    /* Skip to the next non-formal parameter. After object creation,
-     * the object references on parameter _before_ the first, so
-     * this function must be called once to get the object to
-     * reference the first parameter...
-     *
-     * Returns whatever is being passed to the parameter!
-     */
-    symbol_c *next_nf(void);
-
-    /* Search for the value passed to the parameter named <param_name>...  */
-    symbol_c *search_f(symbol_c *param_name);
-    symbol_c *search_f(const char *param_name);
-
-    /* Returns the value being passed to the current parameter. */
-    symbol_c *get_current_value(void);
-
-
+    
   private:
   /********************************/
   /* B 1.7 Configuration elements */
--- a/stage3/fill_candidate_datatypes.cc	Sat Feb 18 21:03:01 2012 +0000
+++ b/stage3/fill_candidate_datatypes.cc	Sun Feb 19 16:16:45 2012 +0000
@@ -73,7 +73,7 @@
  * beginning of the parameter list BEFORE calling handle_function_call().
  */
 bool fill_candidate_datatypes_c::match_nonformal_call(symbol_c *f_call, symbol_c *f_decl) {
-	symbol_c *call_param_value,  *param_type;
+	symbol_c *call_param_value,  *param_datatype;
 	identifier_c *param_name;
 	function_param_iterator_c       fp_iterator(f_decl);
 	function_call_param_iterator_c fcp_iterator(f_call);
@@ -92,10 +92,10 @@
 		} while ((strcmp(param_name->value, "EN") == 0) || (strcmp(param_name->value, "ENO") == 0));
 
 		/* Get the parameter type */
-		param_type = base_type(fp_iterator.param_type());
+		param_datatype = 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 */
-		if (search_in_candidate_datatype_list(param_type, call_param_value->candidate_datatypes) < 0)
+		if (search_in_candidate_datatype_list(param_datatype, call_param_value->candidate_datatypes) < 0)
 			return false; /* return false if param_type not in the list! */
 	}
 	/* call is compatible! */
@@ -107,7 +107,7 @@
 /* returns true if compatible function/FB invocation, otherwise returns false */
 /* Assumes that the candidate_datatype lists of all the parameters being passed haved already been filled in */
 bool fill_candidate_datatypes_c::match_formal_call(symbol_c *f_call, symbol_c *f_decl) {
-	symbol_c *call_param_value, *call_param_name, *param_type;
+	symbol_c *call_param_value, *call_param_name, *param_datatype;
 	symbol_c *verify_duplicate_param;
 	identifier_c *param_name;
 	function_param_iterator_c       fp_iterator(f_decl);
@@ -118,12 +118,14 @@
 
 	/* Iterating through the formal parameters of the function call */
 	while((call_param_name = fcp_iterator.next_f()) != NULL) {
-/* TODO: check whether direction (IN, OUT, IN_OUT) and assignment types (:= , =>) are compatible !!! */
 		/* Obtaining the value being passed in the function call */
 		call_param_value = fcp_iterator.get_current_value();
 		/* the following should never occur. If it does, then we have a bug in our code... */
 		if (NULL == call_param_value) ERROR;
 
+		/* Obtaining the assignment direction:  := (assign_in) or => (assign_out) */
+		function_call_param_iterator_c::assign_direction_t call_param_dir = fcp_iterator.get_assign_direction();
+
 		/* Checking if there are duplicated parameter values */
 		verify_duplicate_param = fcp_iterator.search_f(call_param_name);
 		if(verify_duplicate_param != call_param_value)
@@ -135,10 +137,23 @@
 		/* Find the corresponding parameter in function declaration */
 		param_name = fp_iterator.search(call_param_name);
 		if(param_name == NULL) return false;
-		/* Get the parameter type */
-		param_type = base_type(fp_iterator.param_type());
+		/* Get the parameter data type */
+		param_datatype = base_type(fp_iterator.param_type());
+		/* Get the parameter direction: IN, OUT, IN_OUT */
+		function_param_iterator_c::param_direction_t param_dir = fp_iterator.param_direction();
+
+		/* check whether direction (IN, OUT, IN_OUT) and assignment types (:= , =>) are compatible !!! */
+		if          (function_call_param_iterator_c::assign_in  == call_param_dir) {
+			if ((function_param_iterator_c::direction_in    != param_dir) &&
+			    (function_param_iterator_c::direction_inout != param_dir))
+				return false;
+		} else if   (function_call_param_iterator_c::assign_out == call_param_dir) {
+			if ((function_param_iterator_c::direction_out   != param_dir))
+				return false;
+		} else ERROR;
+		
 		/* check whether one of the candidate_data_types of the value being passed is the same as the param_type */
-		if (search_in_candidate_datatype_list(param_type, call_param_types) < 0)
+		if (search_in_candidate_datatype_list(param_datatype, call_param_types) < 0)
 			return false; /* return false if param_type not in the list! */
 	}
 	/* call is compatible! */
--- a/stage3/print_datatypes_error.cc	Sat Feb 18 21:03:01 2012 +0000
+++ b/stage3/print_datatypes_error.cc	Sun Feb 19 16:16:45 2012 +0000
@@ -124,7 +124,6 @@
 		ERROR;
 	}
 	if (NULL == f_decl) {
-		STAGE3_ERROR(0, fcall, fcall, "Unable to resolve which overloaded %s '%s' is being invoked.", POU_str, ((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;
@@ -136,22 +135,46 @@
 		if (NULL != f_decl) {
 			function_param_iterator_c fp_iterator(f_decl);
 			while ((param_name = fcp_iterator.next_f()) != NULL) {
-#if 0
-/* TODO: check whether direction (IN, OUT, IN_OUT) and assignment types (:= , =>) are compatible !!! */
-
-
-/* TODO: Check if there are duplicat parameter values */
-		verify_duplicate_param = fcp_iterator.search_f(call_param_name);
-		if(verify_duplicate_param != call_param_value)
-			return false;
-#endif
 				param_value = fcp_iterator.get_current_value();
+
+				/* Check if there are duplicate parameter values */
+				if(fcp_iterator.search_f(param_name) != param_value) {
+					function_invocation_error = true;
+					STAGE3_ERROR(0, param_name, param_name, "Duplicate parameter '%s' when invoking %s '%s'", ((identifier_c *)param_name)->value, POU_str, ((identifier_c *)fcall_data.function_name)->value);
+					continue; /* jump to next parameter */
+				}
+
 				/* Find the corresponding parameter in function declaration */
 				if (NULL == fp_iterator.search(param_name)) {
-					STAGE3_ERROR(0, param_name, param_name, "Invalid parameter '%s' when invoking %s '%s'", ((identifier_c *)param_name)->value, POU_str, ((identifier_c *)fcall_data.function_name)->value);		  
-				} else if (NULL == param_value->datatype) {
+					function_invocation_error = true;
+					STAGE3_ERROR(0, param_name, param_name, "Invalid parameter '%s' when invoking %s '%s'", ((identifier_c *)param_name)->value, POU_str, ((identifier_c *)fcall_data.function_name)->value);
+					continue; /* jump to next parameter */
+				} 
+
+				/* check whether direction (IN, OUT, IN_OUT) and assignment types (:= , =>) are compatible !!! */
+				/* Obtaining the assignment direction:  := (assign_in) or => (assign_out) */
+				function_call_param_iterator_c::assign_direction_t call_param_dir = fcp_iterator.get_assign_direction();
+				/* Get the parameter direction: IN, OUT, IN_OUT */
+				function_param_iterator_c::param_direction_t param_dir = fp_iterator.param_direction();
+				if          (function_call_param_iterator_c::assign_in  == call_param_dir) {
+					if ((function_param_iterator_c::direction_in    != param_dir) &&
+					    (function_param_iterator_c::direction_inout != param_dir)) {
+						function_invocation_error = true;
+						STAGE3_ERROR(0, param_name, param_name, "Invalid assignment syntax ':=' used for parameter '%s', when invoking %s '%s'", ((identifier_c *)param_name)->value, POU_str, ((identifier_c *)fcall_data.function_name)->value);
+						continue; /* jump to next parameter */
+					}
+				} else if   (function_call_param_iterator_c::assign_out == call_param_dir) {
+					if ((function_param_iterator_c::direction_out   != param_dir)) {
+						function_invocation_error = true;
+						STAGE3_ERROR(0, param_name, param_name, "Invalid assignment syntax '=>' used for parameter '%s', when invoking %s '%s'", ((identifier_c *)param_name)->value, POU_str, ((identifier_c *)fcall_data.function_name)->value);
+						continue; /* jump to next parameter */
+					}
+				} else ERROR;
+
+				if (NULL == param_value->datatype) {
 					function_invocation_error = true;
 					STAGE3_ERROR(0, param_value, param_value, "Data type incompatibility between parameter '%s' and value being passed, when invoking %s '%s'", ((identifier_c *)param_name)->value, POU_str, ((identifier_c *)fcall_data.function_name)->value);
+					continue; /* jump to next parameter */
 				}
 			}
 		}
@@ -167,6 +190,11 @@
 			}
 	}
 
+	if (NULL == fcall_data.called_function_declaration) {
+		function_invocation_error = true;
+		STAGE3_ERROR(0, fcall, fcall, "Unable to resolve which overloaded %s '%s' is being invoked.", POU_str, ((identifier_c *)fcall_data.function_name)->value);
+	}
+
 	if (function_invocation_error) {
 		/* No compatible function exists */
 		STAGE3_ERROR(2, fcall, fcall, "Invalid parameters when invoking %s '%s'", POU_str, ((identifier_c *)fcall_data.function_name)->value);