Check VAR_EXTERN variables declared in FBs instantiated in a Program (and recursively too - FBs in FBs).
authormjsousa
Tue, 20 May 2014 08:25:59 +0100
changeset 892 54d5d185d6e2
parent 891 58109ca87903
child 894 39086e324665
child 895 f824bf8e1579
Check VAR_EXTERN variables declared in FBs instantiated in a Program (and recursively too - FBs in FBs).
stage3/declaration_check.cc
--- a/stage3/declaration_check.cc	Tue May 20 08:24:25 2014 +0100
+++ b/stage3/declaration_check.cc	Tue May 20 08:25:59 2014 +0100
@@ -70,6 +70,162 @@
 }
 
 
+
+
+
+
+
+
+
+#include <set>
+class check_extern_c: public iterator_visitor_c {
+  public:
+  
+  private:
+    int current_display_error_level;
+    symbol_c *current_pou_decl;
+    symbol_c *current_resource_decl;
+    static std::set<symbol_c *> checked_decl; // A set with all the declarations that have already been checked, so we don't recheck it again!
+  
+  
+  public:
+    static int error_count;
+    
+    check_extern_c(symbol_c *current_pou, symbol_c *current_resource) {
+      current_display_error_level = 0;
+      current_pou_decl      = current_pou;
+      current_resource_decl = current_resource;
+    }
+        
+    ~check_extern_c(void) {}
+    
+    
+    
+    void check_global_decl(symbol_c *p_decl) {
+      if (NULL == current_pou_decl) ERROR;
+      
+      if (checked_decl.find(p_decl) != checked_decl.end()) return; // has already been checked!
+      checked_decl.insert(p_decl);
+
+      search_var_instance_decl_c search_var_instance_pou_glo_decl(current_pou_decl);
+      search_var_instance_decl_c search_var_instance_res_glo_decl(current_resource_decl);
+      search_var_instance_decl_c search_var_instance_ext_decl(p_decl);
+      function_param_iterator_c  fpi(p_decl);
+      
+      symbol_c *var_name;
+      while((var_name = fpi.next()) != NULL) {            
+        if (fpi.param_direction() == function_param_iterator_c::direction_extref) {
+          /* found an external reference parameter. */
+          symbol_c *ext_decl = search_var_instance_ext_decl.get_decl(var_name);
+          
+          // NOTE: Must check the POU first, and RESOURCE second!
+          symbol_c                                       *glo_decl =  search_var_instance_res_glo_decl.get_decl(var_name);           
+              search_var_instance_decl_c *search_var_instance_glo_decl = &search_var_instance_res_glo_decl;
+          if (NULL == glo_decl) {
+            glo_decl                     =  search_var_instance_pou_glo_decl.get_decl(var_name);
+            search_var_instance_glo_decl = &search_var_instance_pou_glo_decl;
+          }
+          
+          if (NULL == glo_decl) {
+            STAGE3_ERROR(0, ext_decl, ext_decl, "Declaration error. The external variable does not match with any global variable.");
+            continue;
+          }
+    
+          /* Check whether variable's constness (CONSTANT) is compatible.
+           *        VAR_GLOBAL is contant     => VAR_EXTERNAL must also be CONSTANT
+           *        VAR_GLOBAL is not contant => VAR_EXTERNAL may be CONSTANT, or not!
+           */
+          search_var_instance_decl_c::opt_t ext_opt = search_var_instance_ext_decl. get_option(var_name);
+          search_var_instance_decl_c::opt_t glo_opt = search_var_instance_glo_decl->get_option(var_name);
+          if ((glo_opt == search_var_instance_decl_c::constant_opt) && (ext_opt != search_var_instance_decl_c::constant_opt))
+            STAGE3_ERROR(0, glo_decl, glo_decl, "Declaration error. The external variable must be declared as constant, as it maps to a constant global variable.");
+    
+          /* TODO: Check redefinition data type.
+           *       We need a new class (like search_base_type class) to get type id by variable declaration.
+           *  symbol_c *glo_type = ????;
+           *  symbol_c *ext_type = fpi.param_type();
+           */
+          /* For the moment, we will just use search_base_type_c instead... */
+          symbol_c *glo_type = search_base_type_c::get_basetype_decl(glo_decl);
+          symbol_c *ext_type = search_base_type_c::get_basetype_decl(ext_decl);
+          if (! get_datatype_info_c::is_type_equal(glo_type, ext_type))
+            STAGE3_ERROR(0, ext_decl, ext_decl, "Declaration error.  Data type mismatch between external and global variable declarations.");
+        }
+      }
+    }
+    
+    
+    
+    /********************************/
+    /* B 1.3.3 - Derived data types */
+    /********************************/
+    /*  function_block_type_name ASSIGN structure_initialization */
+    /* structure_initialization -> may be NULL ! */
+    //SYM_REF2(fb_spec_init_c, function_block_type_name, structure_initialization)
+    /* NOTE: This class is only used when declaring FB variables, as in:
+     * name_list ':' function_block_type_name ASSIGN structure_initialization
+     *               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+     *                      fb_spec_init_c                                   
+     * Here, fb_spec_init_c is used whether it is an external, local, or global variable!
+     * Note too that we must also check the datatypes of external and global variables!! 
+     */
+    void *visit(fb_spec_init_c *symbol) {
+      symbol_c *fb_decl = function_block_type_symtable.find_value(symbol->function_block_type_name);
+      /* stage1_2 guarantees that we are sure to find a declaration in FB or Program symtable. */
+      if (fb_decl == function_block_type_symtable.end_value())
+        ERROR;
+      fb_decl->accept(*this);
+      return NULL;
+    }
+    
+    /*****************************/
+    /* B 1.5.2 - Function Blocks */
+    /*****************************/
+    /*  FUNCTION_BLOCK derived_function_block_name io_OR_other_var_declarations function_block_body END_FUNCTION_BLOCK */
+    // SYM_REF3(function_block_declaration_c, fblock_name, var_declarations, fblock_body)
+    void *visit(function_block_declaration_c *symbol) {
+      check_global_decl(symbol);
+      /* The following two lines of code are only valid for v3 of IEC 61131-3, that allows VAR_GLOBAL declarations inside FBs!
+       * current_pou_decl = symbol;  
+       * current_resource_decl = NULL;  
+       */
+      /* check if any FB declared as a VAR has any incompatible VAR_EXTERNAL declarations */
+      if (NULL != symbol->var_declarations)
+        symbol->var_declarations->accept(*this);  // This will eventually call the visit(fb_spec_init_c *) method, if any FB are declared here!
+      return NULL;
+    }
+    
+    /******************************************/
+    /* B 1.5.3 - Declaration & Initialisation */
+    /******************************************/
+    /*  PROGRAM program_type_name program_var_declarations_list function_block_body END_PROGRAM */
+    // SYM_REF3(program_declaration_c, program_type_name, var_declarations, function_block_body)
+    void *visit(program_declaration_c *symbol) {
+      check_global_decl(symbol);
+      /* The following two lines of code are only valid for v3 of IEC 61131-3, that allows VAR_GLOBAL declarations inside PROGRAMs!
+       * current_pou_decl = symbol;  
+       * current_resource_decl = NULL;  
+       */
+      /* check if any FB declared as a VAR has any incompatible VAR_EXTERNAL declarations */
+      if (NULL != symbol->var_declarations)
+        symbol->var_declarations->accept(*this);  // This will eventually call the visit(fb_spec_init_c *) method, if any FB are declared here!
+      return NULL;
+    }
+    
+};
+
+int check_extern_c::error_count = 0;
+std::set<symbol_c *> check_extern_c::checked_decl;
+
+
+
+
+
+
+
+
+
+
 declaration_check_c::declaration_check_c(symbol_c *ignore) {
   current_display_error_level = 0;
   current_pou_decl = NULL;
@@ -82,92 +238,26 @@
 }
 
 int declaration_check_c::get_error_count() {
-  return error_count;
-}
-
-void declaration_check_c::check_global_decl(symbol_c *p_decl) {
-  if (NULL == current_pou_decl) ERROR;
-  
-  search_var_instance_decl_c search_var_instance_pou_glo_decl(current_pou_decl);
-  search_var_instance_decl_c search_var_instance_res_glo_decl(current_resource_decl);
-  search_var_instance_decl_c search_var_instance_ext_decl(p_decl);
-  function_param_iterator_c  fpi(p_decl);
-  
-  symbol_c *var_name;
-  while((var_name = fpi.next()) != NULL) {
-    if (fpi.param_direction() == function_param_iterator_c::direction_extref) {
-      /* found an external reference parameter. */
-      symbol_c *ext_decl = search_var_instance_ext_decl.get_decl(var_name);
-      
-      // NOTE: Must check the POU first, and RESOURCE second!
-      symbol_c                                       *glo_decl =  search_var_instance_res_glo_decl.get_decl(var_name);           
-      search_var_instance_decl_c *search_var_instance_glo_decl = &search_var_instance_res_glo_decl;
-      if (NULL == glo_decl) {
-        glo_decl                     =  search_var_instance_pou_glo_decl.get_decl(var_name);
-        search_var_instance_glo_decl = &search_var_instance_pou_glo_decl;
-      }
-      
-      if (NULL == glo_decl) {
-        STAGE3_ERROR(0, ext_decl, ext_decl, "Declaration error. The external variable does not match with any global variable.");
-        continue;
-      }
-      
-      /* Check whether variable's constness (CONSTANT) is compatible.
-       *        VAR_GLOBAL is contant     => VAR_EXTERNAL must also be CONSTANT
-       *        VAR_GLOBAL is not contant => VAR_EXTERNAL may be CONSTANT, or not!
-       */
-      search_var_instance_decl_c::opt_t ext_opt = search_var_instance_ext_decl. get_option(var_name);
-      search_var_instance_decl_c::opt_t glo_opt = search_var_instance_glo_decl->get_option(var_name);
-      if ((glo_opt == search_var_instance_decl_c::constant_opt) && (ext_opt != search_var_instance_decl_c::constant_opt))
-        STAGE3_ERROR(0, glo_decl, glo_decl, "Declaration error. The external variable must be declared as constant, as it maps to a constant global variable.");
-
-      /* TODO: Check redefinition data type.
-       *       We need a new class (like search_base_type class) to get type id by variable declaration.
-       *  symbol_c *glo_type = ????;
-       *  symbol_c *ext_type = fpi.param_type();
-       */
-      /* For the moment, we will just use search_base_type_c instead... */
-      symbol_c *glo_type = search_base_type_c::get_basetype_decl(glo_decl);
-      symbol_c *ext_type = search_base_type_c::get_basetype_decl(ext_decl);
-      if (! get_datatype_info_c::is_type_equal(glo_type, ext_type))
-        STAGE3_ERROR(0, ext_decl, ext_decl, "Declaration error.  Data type mismatch between external and global variable declarations.");
-    }
-  }
-}
-
-
+  return check_extern_c::error_count;
+}
 
 /*****************************/
 /* B 1.5.2 - Function Blocks */
 /*****************************/
 /*  FUNCTION_BLOCK derived_function_block_name io_OR_other_var_declarations function_block_body END_FUNCTION_BLOCK */
 // SYM_REF3(function_block_declaration_c, fblock_name, var_declarations, fblock_body)
-void *declaration_check_c::visit(function_block_declaration_c *symbol) {
-  /* The following two lines of code are only valid for v3 of IEC 61131-3, that allows VAR_GLOBAL declarations inside FBs!
-   * current_pou_decl = symbol;  
-   * current_resource_decl = NULL;  
-   */
-  /* check if any FB declared as a VAR has any incompatible VAR_EXTERNAL declarations */
-  if (NULL != symbol->var_declarations)
-    symbol->var_declarations->accept(*this);
-  return NULL;
-}
+void *declaration_check_c::visit(function_block_declaration_c *symbol)
+  {return NULL;} // We only check the declarations that are used to instantiate variables. This is done in the configuration!
 
 /******************************************/
 /* B 1.5.3 - Declaration & Initialisation */
 /******************************************/
 /*  PROGRAM program_type_name program_var_declarations_list function_block_body END_PROGRAM */
 // SYM_REF3(program_declaration_c, program_type_name, var_declarations, function_block_body)
-void *declaration_check_c::visit(program_declaration_c *symbol) {
-  /* The following two lines of code are only valid for v3 of IEC 61131-3, that allows VAR_GLOBAL declarations inside PROGRAMs!
-   * current_pou_decl = symbol;  
-   * current_resource_decl = NULL;  
-   */
-  /* check if any FB declared as a VAR has any incompatible VAR_EXTERNAL declarations */
-  if (NULL != symbol->var_declarations)
-    symbol->var_declarations->accept(*this);
-  return NULL;
-}
+void *declaration_check_c::visit(program_declaration_c *symbol) 
+  {return NULL;} // We only check the declarations that are used to instantiate variables. This is done in the configuration!
+
+
 
 /********************************/
 /* B 1.7 Configuration elements */
@@ -199,8 +289,11 @@
 /* enumvalue_symtable is filled in by enum_declaration_check_c, during stage3 semantic verification, with a list of all enumerated constants declared inside this POU */
 // SYM_REF4(resource_declaration_c, resource_name, resource_type_name, global_var_declarations, resource_declaration, enumvalue_symtable_t enumvalue_symtable;)
 void *declaration_check_c::visit(resource_declaration_c *symbol) {
+  // check if any FB instantiated inside this resource (in a VAR_GLOBAL) has any VAR_EXTERNAL declarations incompatible with the configuration's VAR_GLOBALs
+  check_extern_c check_extern(current_pou_decl, current_resource_decl);
+  symbol->global_var_declarations->accept(check_extern);   
+  // Now check the Programs instantiated in this resource
   current_resource_decl = symbol;
-  /* check if any FB declared as a VAR has any incompatible VAR_EXTERNAL declarations */
   symbol->resource_declaration->accept(*this);
   current_resource_decl = NULL;  
   return NULL;
@@ -211,10 +304,11 @@
   symbol_c *p_decl = program_type_symtable.find_value(symbol->program_type_name);
   if (p_decl == program_type_symtable.end_value())
     p_decl = function_block_type_symtable.find_value(symbol->program_type_name);
-  /* stage1_2 guarantees that we are sure to find a declaration in FB or Program symtable. */
   if (p_decl == function_block_type_symtable.end_value())
-    ERROR;
-  check_global_decl(p_decl);
+    ERROR;  // Should never occur! stage1_2 guarantees that we are sure to find a declaration in FB or Program symtable.
+  
+  check_extern_c check_extern(current_pou_decl, current_resource_decl);
+  p_decl->accept(check_extern);
   return NULL;
 }