Fixing check for assignment to output variables.
authorMario de Sousa <msousa@fe.up.pt>
Mon, 16 Apr 2012 14:41:07 +0100
changeset 510 9317e04c1dde
parent 509 35d391c38a30
child 511 b22ae67d8003
Fixing check for assignment to output variables.
stage3/lvalue_check.cc
stage3/lvalue_check.hh
stage3/print_datatypes_error.cc
stage3/print_datatypes_error.hh
stage3/stage3.cc
--- a/stage3/lvalue_check.cc	Sun Apr 15 19:56:33 2012 +0100
+++ b/stage3/lvalue_check.cc	Mon Apr 16 14:41:07 2012 +0100
@@ -44,7 +44,7 @@
                                                  LAST_(symbol1,symbol2) ->last_line,  LAST_(symbol1,symbol2) ->last_column);\
     fprintf(stderr, __VA_ARGS__);                                                                                           \
     fprintf(stderr, "\n");                                                                                                  \
-    error_found = true;                                                                                                     \
+    error_count++;                                                                                                     \
   }                                                                                                                         \
 }
 
@@ -60,14 +60,14 @@
 
 
 lvalue_check_c::lvalue_check_c(symbol_c *ignore) {
-	error_found = false;
+	error_count = 0;
 }
 
 lvalue_check_c::~lvalue_check_c(void) {
 }
 
-int lvalue_check_c::get_error_found() {
-	return error_found;
+int lvalue_check_c::get_error_count() {
+	return error_count;
 }
 
 
@@ -84,18 +84,47 @@
 }
 
 
-/* fb_instance.var := ...  is not valid if var is output (not input ??) variable */
-void lvalue_check_c::check_assignment_to_output(symbol_c * lvalue) {
-	symbol_c *type_id = search_varfb_instance_type->get_basetype_id(lvalue);
-	if (NULL != type_id) {
-		function_block_declaration_c *fb_decl = function_block_type_symtable.find_value(type_id);
-		if (function_block_type_symtable.end_value() != fb_decl) {
-			search_var_instance_decl_c   search_var_instance_decl(fb_decl);
-			structured_variable_c * str_var = (structured_variable_c *)lvalue;
-			unsigned int vartype = search_var_instance_decl.get_vartype(str_var->field_selector);
-			if (vartype == search_var_instance_decl_c::output_vt)
-				STAGE3_ERROR(0, lvalue, lvalue, "Assignment to FB output field variable is not be allowed.");
+/* fb_instance.var := ...  is not valid if var is output variable */
+/* NOTE, if a fb_instance1.fb_instance2.fb_instance3.var is used, we must iteratively check that none of the 
+ *       FB records are declared as OUTPUT variables!!  
+ *       This is the reason why we have the while() loop in this function!
+ */
+void lvalue_check_c::check_assignment_to_output(symbol_c *lvalue) {
+	decompose_var_instance_name_c decompose_lvalue(lvalue);
+	search_base_type_c            search_base_type;
+
+	symbol_c *struct_elem = decompose_lvalue.next_part();
+	symbol_c *type_decl   = search_var_instance_decl->get_decl(struct_elem);
+	// symbol_c *type_id  = spec_init_sperator_c::get_spec(type_decl); /* this is not required! search_base_type_c can handle spec_init symbols! */
+	symbol_c *basetype_id = search_base_type.get_basetype_id(/*type_id*/ type_decl);
+	/* If we can not determine the data type of the element, then the code must have a data type semantic error.
+	 * This will have been caught by the data type semantic verifier, so we do not bother with this anymore!
+	 */
+	if (NULL == basetype_id) return;
+
+	/* Determine if the record/structure element is of a FB type. */
+	/* NOTE: If the structure element is not a FB type, then we can quit this check.
+	 *       Remember that the standard does not allow a STRUCT data type to have elements that are FB instances!
+	 *       Similarly, arrays of FB instances is also not allowed.
+	 *       So, as soon as we find one record/structure element that is not a FB, no other record/structure element
+	 *       will be of FB type, which means we can quit this check!
+	 */
+	function_block_declaration_c *fb_decl = function_block_type_symtable.find_value(basetype_id);
+	if (function_block_type_symtable.end_value() == fb_decl) return;
+
+	while (NULL != (struct_elem = decompose_lvalue.next_part())) {
+		search_var_instance_decl_c   fb_search_var_instance_decl(fb_decl);
+		if (search_var_instance_decl_c::output_vt == fb_search_var_instance_decl.get_vartype(struct_elem)) {
+			STAGE3_ERROR(0, struct_elem, struct_elem, "Assignment to FB output variable is not allowed.");
+			return; /* no need to carry on checking once the first error is found! */
 		}
+
+		/* prepare for any possible further record/structure elements */
+		type_decl   = fb_search_var_instance_decl.get_decl(struct_elem);
+		basetype_id = search_base_type.get_basetype_id(type_decl);
+		if (NULL == basetype_id) return; /* same comment as above... */
+		fb_decl = function_block_type_symtable.find_value(basetype_id);
+		if (function_block_type_symtable.end_value() == fb_decl) return; /* same comment as above... */
 	}
 }
 
@@ -113,17 +142,17 @@
 void lvalue_check_c::check_assignment_to_expression(symbol_c *lvalue) {
 	/* TODO: check whether the lvalue is an expresion! */
 	/* This may occur in function invocations, when passing values (possibly an expression) to one 
-	 * of the function's OUTPUT parameters.
+	 * of the function's OUTPUT or IN_OUT parameters.
 	 */
 }
 
 
 
 void lvalue_check_c::verify_is_lvalue(symbol_c *lvalue) {
+	check_assignment_to_expression(lvalue);
 	check_assignment_to_controlvar(lvalue);
 	check_assignment_to_output(lvalue);
 	check_assignment_to_constant(lvalue);
-	check_assignment_to_expression(lvalue);
 }
 
 
@@ -161,8 +190,7 @@
 		/* We only check if 'call_param_value' is a valid lvalue if the value is being passed
 		 * to a valid paramater of the function being called, and that parameter is either OUT or IN_OUT.
 		 */
-		if ((param_name != NULL) && 
-		    ((function_param_iterator_c::direction_out == param_direction) || (function_param_iterator_c::direction_inout == param_direction))) {
+		if ((param_name != NULL) && ((function_param_iterator_c::direction_out == param_direction) || (function_param_iterator_c::direction_inout == param_direction))) {
 			verify_is_lvalue(call_param_value);
 		}
 	}
@@ -227,10 +255,8 @@
 /***********************/
 // 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 *lvalue_check_c::visit(function_invocation_c *symbol) {
-	if (NULL != symbol->formal_param_list)
-		check_formal_call(symbol, symbol->called_function_declaration);
-	if (NULL != symbol->nonformal_param_list)
-		check_nonformal_call(symbol, symbol->called_function_declaration);
+	if (NULL != symbol->formal_param_list   )  check_formal_call   (symbol, symbol->called_function_declaration);
+	if (NULL != symbol->nonformal_param_list)  check_nonformal_call(symbol, symbol->called_function_declaration);
 	return NULL;
 }
 
--- a/stage3/lvalue_check.hh	Sun Apr 15 19:56:33 2012 +0100
+++ b/stage3/lvalue_check.hh	Mon Apr 16 14:41:07 2012 +0100
@@ -47,7 +47,7 @@
     search_varfb_instance_type_c *search_varfb_instance_type;
     search_var_instance_decl_c *search_var_instance_decl;
     search_base_type_c search_base_type;
-    int error_found;
+    int error_count;
     int current_display_error_level;
     std::vector <symbol_c *> control_variables;
 
@@ -63,7 +63,7 @@
   public:
     lvalue_check_c(symbol_c *ignore);
     virtual ~lvalue_check_c(void);
-    int get_error_found();
+    int get_error_count();
 
     /**************************************/
     /* B 1.5 - Program organisation units */
--- a/stage3/print_datatypes_error.cc	Sun Apr 15 19:56:33 2012 +0100
+++ b/stage3/print_datatypes_error.cc	Mon Apr 16 14:41:07 2012 +0100
@@ -61,7 +61,7 @@
     fprintf(stderr, __VA_ARGS__);                                                                                           \
     fprintf(stderr, "\n");                                                                                                  \
     il_error = true;                                                                                                        \
-    error_found = true;                                                                                                     \
+    error_count++;                                                                                                     \
   }                                                                                                                         \
 }  
 
@@ -80,7 +80,7 @@
 static int debug = 0;
 
 print_datatypes_error_c::print_datatypes_error_c(symbol_c *ignore) {
-	error_found = false;
+	error_count = 0;
 	warning_found = false;
 	current_display_error_level = error_level_default;
 }
@@ -88,8 +88,8 @@
 print_datatypes_error_c::~print_datatypes_error_c(void) {
 }
 
-int print_datatypes_error_c::get_error_found() {
-	return error_found;
+int print_datatypes_error_c::get_error_count() {
+	return error_count;
 }
 
 
--- a/stage3/print_datatypes_error.hh	Sun Apr 15 19:56:33 2012 +0100
+++ b/stage3/print_datatypes_error.hh	Mon Apr 16 14:41:07 2012 +0100
@@ -73,7 +73,7 @@
      */
     int  il_parenthesis_level;
     bool il_error;
-    bool error_found;
+    int  error_count;
     bool warning_found;
 
     /* the current data type of the data stored in the IL stack, i.e. the default variable */
@@ -99,7 +99,7 @@
   public:
     print_datatypes_error_c(symbol_c *ignore);
     virtual ~print_datatypes_error_c(void);
-    int get_error_found();
+    int get_error_count();
 
 
     /*********************/
--- a/stage3/stage3.cc	Sun Apr 15 19:56:33 2012 +0100
+++ b/stage3/stage3.cc	Mon Apr 16 14:41:07 2012 +0100
@@ -56,18 +56,29 @@
 	tree_root->accept(narrow_candidate_datatypes);
 	print_datatypes_error_c print_datatypes_error(tree_root);
 	tree_root->accept(print_datatypes_error);
-	if (print_datatypes_error.get_error_found())
-		return -1;
-	lvalue_check_c lvalue_check(tree_root);
-	tree_root->accept(lvalue_check);
-	if (lvalue_check.get_error_found())
-		return -1;
-	return 0;
+	return print_datatypes_error.get_error_count();
 }
 
 
+/* Type safety analysis assumes that flow control analysis has already been completed,
+ * so be sure to call flow_control_analysis() before calling this function
+ */
+static int lvalue_check(symbol_c *tree_root){
+	lvalue_check_c lvalue_check(tree_root);
+	tree_root->accept(lvalue_check);
+	return lvalue_check.get_error_count();
+}
+
 
 int stage3(symbol_c *tree_root){
-	flow_control_analysis(tree_root);
-	return type_safety(tree_root);
+	int error_count = 0;
+	error_count += flow_control_analysis(tree_root);
+	error_count += type_safety(tree_root);
+	error_count += lvalue_check(tree_root);
+	
+	if (error_count > 0) {
+		fprintf(stderr, "%d errors found. Bailing out!\n", error_count); 
+		return -1;
+	}
+	return 0;
 }