merge
authorMario de Sousa <msousa@fe.up.pt>
Wed, 13 Feb 2019 14:16:26 +0000
changeset 1086 ee66a14e38f7
parent 1085 9766936990db (diff)
parent 1073 24ef30a9bcee (current diff)
child 1087 d9e47e018320
merge
lib/C/iec_std_functions.h
--- a/lib/C/iec_std_functions.h	Sun Oct 14 20:14:13 2018 +0300
+++ b/lib/C/iec_std_functions.h	Wed Feb 13 14:16:26 2019 +0000
@@ -1481,46 +1481,100 @@
   return __time_add(IN1, IN2);
 }
 
+/* overloaded version of ADD(TIME, TIME) */
+static inline TIME ADD__TIME__TIME__TIME(EN_ENO_PARAMS TIME IN1, TIME IN2){
+  TEST_EN(TIME)
+  return __time_add(IN1, IN2);
+}
+
 static inline TOD ADD_TOD_TIME(EN_ENO_PARAMS TOD IN1, TIME IN2){
   TEST_EN(TOD)
   return __time_add(IN1, IN2);
 }
 
+/* overloaded version of ADD(TOD, TIME) */
+static inline TOD ADD__TOD__TOD__TIME(EN_ENO_PARAMS TOD IN1, TIME IN2){
+  TEST_EN(TIME)
+  return __time_add(IN1, IN2);
+}
+
 static inline DT ADD_DT_TIME(EN_ENO_PARAMS DT IN1, TIME IN2){
   TEST_EN(DT)
   return __time_add(IN1, IN2);
 }
 
+/* overloaded version of ADD(DT, TIME) */
+static inline DT ADD__DT__DT__TIME(EN_ENO_PARAMS DT IN1, TIME IN2){
+  TEST_EN(TIME)
+  return __time_add(IN1, IN2);
+}
+
 static inline TIME SUB_TIME(EN_ENO_PARAMS TIME IN1, TIME IN2){
   TEST_EN(TIME)
   return __time_sub(IN1, IN2);
 }
 
+/* overloaded version of SUB(TIME, TIME) */
+static inline TIME SUB__TIME__TIME__TIME(EN_ENO_PARAMS TIME IN1, TIME IN2){
+  TEST_EN(TIME)
+  return __time_sub(IN1, IN2);
+}
+
 static inline TIME SUB_DATE_DATE(EN_ENO_PARAMS DATE IN1, DATE IN2){
   TEST_EN(TIME)
   return __time_sub(IN1, IN2);
 }
 
+/* overloaded version of SUB(DATE, DATE) */
+static inline TIME SUB__TIME__DATE__DATE(EN_ENO_PARAMS DATE IN1, DATE IN2){
+  TEST_EN(TIME)
+  return __time_sub(IN1, IN2);
+}
+
 static inline TOD SUB_TOD_TIME(EN_ENO_PARAMS TOD IN1, TIME IN2){
   TEST_EN(TOD)
   return __time_sub(IN1, IN2);
 }
 
+/* overloaded version of SUB(TOD, TIME) */
+static inline TOD SUB__TOD__TOD__TIME(EN_ENO_PARAMS TOD IN1, TIME IN2){
+  TEST_EN(TOD)
+  return __time_sub(IN1, IN2);
+}
+
 static inline TIME SUB_TOD_TOD(EN_ENO_PARAMS TOD IN1, TOD IN2){
   TEST_EN(TIME)
   return __time_sub(IN1, IN2);
 }
 
+/* overloaded version of SUB(TOD, TOD) */
+static inline TIME SUB__TIME__TOD__TOD(EN_ENO_PARAMS TOD IN1, TOD IN2){
+  TEST_EN(TIME)
+  return __time_sub(IN1, IN2);
+}
+
 static inline DT SUB_DT_TIME(EN_ENO_PARAMS DT IN1, TIME IN2){
   TEST_EN(DT)
   return __time_sub(IN1, IN2);
 }
 
+/* overloaded version of SUB(DT, TIME) */
+static inline DT SUB__DT__DT__TIME(EN_ENO_PARAMS DT IN1, TIME IN2){
+  TEST_EN(DT)
+  return __time_sub(IN1, IN2);
+}
+
 static inline TIME SUB_DT_DT(EN_ENO_PARAMS DT IN1, DT IN2){
   TEST_EN(TIME)
   return __time_sub(IN1, IN2);
 }
 
+/* overloaded version of SUB(DT, DT) */
+static inline TIME SUB__TIME__DT__DT(EN_ENO_PARAMS DT IN1, DT IN2){
+  TEST_EN(TIME)
+  return __time_sub(IN1, IN2);
+}
+
 
 /***  MULTIME  ***/
 #define __iec_(TYPENAME)\
@@ -1531,6 +1585,15 @@
 __ANY_NUM(__iec_)
 #undef __iec_
 
+/***  MULTIME_TYPENAME  ***/
+#define __iec_(TYPENAME)\
+static inline TIME MULTIME_##TYPENAME(EN_ENO_PARAMS TIME IN1, TYPENAME IN2){\
+  TEST_EN(TIME)\
+  return __time_mul(IN1, (LREAL)IN2);\
+}
+__ANY_NUM(__iec_)
+#undef __iec_
+
 /***  MUL  ***/
 #define __iec_(TYPENAME)\
 static inline TIME MUL__TIME__TIME__##TYPENAME(EN_ENO_PARAMS TIME IN1, TYPENAME IN2){\
@@ -1549,6 +1612,15 @@
 __ANY_NUM(__iec_)
 #undef __iec_
 
+/***  DIVTIME_TYPENAME  ***/
+#define __iec_(TYPENAME)\
+static inline TIME DIVTIME_##TYPENAME(EN_ENO_PARAMS TIME IN1, TYPENAME IN2){\
+  TEST_EN(TIME)\
+  return __time_div(IN1, (LREAL)IN2);\
+}
+__ANY_NUM(__iec_)
+#undef __iec_
+
 /***  DIV  ***/
 #define __iec_(TYPENAME)\
 static inline TIME DIV__TIME__TIME__##TYPENAME(EN_ENO_PARAMS TIME IN1, TYPENAME IN2){\
--- a/lib/create_standard_function_txt.sh	Sun Oct 14 20:14:13 2018 +0300
+++ b/lib/create_standard_function_txt.sh	Wed Feb 13 14:16:26 2019 +0000
@@ -836,16 +836,25 @@
 /***  Functions of time data types  ***/
 /**************************************/
 
-__function_2p(ADD_TIME, TIME, IN1, TIME, IN2, TIME)
-__function_2p(ADD_TOD_TIME, TOD, IN1, TOD, IN2, TIME)
-__function_2p(ADD_DT_TIME, DT, IN1, DT, IN2, TIME)
-
-__function_2p(SUB_TIME, TIME, IN1, TIME, IN2, TIME)
+__function_2p(ADD_TIME,      TIME, IN1, TIME, IN2, TIME)
+__function_2p(ADD,           TIME, IN1, TIME, IN2, TIME)       /* overloaded function */
+__function_2p(ADD_TOD_TIME,  TOD,  IN1, TOD,  IN2, TIME)
+__function_2p(ADD,           TOD,  IN1, TOD,  IN2, TIME)       /* overloaded function */
+__function_2p(ADD_DT_TIME,   DT,   IN1, DT,   IN2, TIME)
+__function_2p(ADD,           DT,   IN1, DT,   IN2, TIME)       /* overloaded function */
+
+__function_2p(SUB_TIME,      TIME, IN1, TIME, IN2, TIME)
+__function_2p(SUB,           TIME, IN1, TIME, IN2, TIME)       /* overloaded function */
 __function_2p(SUB_DATE_DATE, TIME, IN1, DATE, IN2, DATE)
-__function_2p(SUB_TOD_TIME, TOD, IN1, TOD, IN2, TIME)
-__function_2p(SUB_TOD_TOD, TIME, IN1, TOD, IN2, TOD)
-__function_2p(SUB_DT_TIME, DT, IN1, DT, IN2, TIME)
-__function_2p(SUB_DT_DT, TIME, IN1, DT, IN2, DT)
+__function_2p(SUB,           TIME, IN1, DATE, IN2, DATE)       /* overloaded function */
+__function_2p(SUB_TOD_TIME,  TOD,  IN1, TOD,  IN2, TIME)
+__function_2p(SUB,           TOD,  IN1, TOD,  IN2, TIME)       /* overloaded function */
+__function_2p(SUB_TOD_TOD,   TIME, IN1, TOD,  IN2, TOD )
+__function_2p(SUB,           TIME, IN1, TOD,  IN2, TOD )       /* overloaded function */
+__function_2p(SUB_DT_TIME,   DT,   IN1, DT,   IN2, TIME)
+__function_2p(SUB,           DT,   IN1, DT,   IN2, TIME)       /* overloaded function */
+__function_2p(SUB_DT_DT,     TIME, IN1, DT,   IN2, DT  )
+__function_2p(SUB,           TIME, IN1, DT,   IN2, DT  )       /* overloaded function */
 
 
 /***  MULTIME  ***/
--- a/lib/standard_functions.txt	Sun Oct 14 20:14:13 2018 +0300
+++ b/lib/standard_functions.txt	Wed Feb 13 14:16:26 2019 +0000
@@ -1763,15 +1763,24 @@
 FUNCTION FIND : UDINT VAR_INPUT IN1 : STRING; IN2 : STRING; END_VAR RETURN; END_FUNCTION
 FUNCTION FIND : ULINT VAR_INPUT IN1 : STRING; IN2 : STRING; END_VAR RETURN; END_FUNCTION
 FUNCTION ADD_TIME : TIME VAR_INPUT IN1 : TIME; IN2 : TIME; END_VAR RETURN; END_FUNCTION
+FUNCTION ADD : TIME VAR_INPUT IN1 : TIME; IN2 : TIME; END_VAR RETURN; END_FUNCTION
 FUNCTION ADD_TOD_TIME : TOD VAR_INPUT IN1 : TOD; IN2 : TIME; END_VAR RETURN; END_FUNCTION
+FUNCTION ADD : TOD VAR_INPUT IN1 : TOD; IN2 : TIME; END_VAR RETURN; END_FUNCTION
 FUNCTION ADD_DT_TIME : DT VAR_INPUT IN1 : DT; IN2 : TIME; END_VAR RETURN; END_FUNCTION
+FUNCTION ADD : DT VAR_INPUT IN1 : DT; IN2 : TIME; END_VAR RETURN; END_FUNCTION
 
 FUNCTION SUB_TIME : TIME VAR_INPUT IN1 : TIME; IN2 : TIME; END_VAR RETURN; END_FUNCTION
+FUNCTION SUB : TIME VAR_INPUT IN1 : TIME; IN2 : TIME; END_VAR RETURN; END_FUNCTION
 FUNCTION SUB_DATE_DATE : TIME VAR_INPUT IN1 : DATE; IN2 : DATE; END_VAR RETURN; END_FUNCTION
+FUNCTION SUB : TIME VAR_INPUT IN1 : DATE; IN2 : DATE; END_VAR RETURN; END_FUNCTION
 FUNCTION SUB_TOD_TIME : TOD VAR_INPUT IN1 : TOD; IN2 : TIME; END_VAR RETURN; END_FUNCTION
+FUNCTION SUB : TOD VAR_INPUT IN1 : TOD; IN2 : TIME; END_VAR RETURN; END_FUNCTION
 FUNCTION SUB_TOD_TOD : TIME VAR_INPUT IN1 : TOD; IN2 : TOD; END_VAR RETURN; END_FUNCTION
+FUNCTION SUB : TIME VAR_INPUT IN1 : TOD; IN2 : TOD; END_VAR RETURN; END_FUNCTION
 FUNCTION SUB_DT_TIME : DT VAR_INPUT IN1 : DT; IN2 : TIME; END_VAR RETURN; END_FUNCTION
+FUNCTION SUB : DT VAR_INPUT IN1 : DT; IN2 : TIME; END_VAR RETURN; END_FUNCTION
 FUNCTION SUB_DT_DT : TIME VAR_INPUT IN1 : DT; IN2 : DT; END_VAR RETURN; END_FUNCTION
+FUNCTION SUB : TIME VAR_INPUT IN1 : DT; IN2 : DT; END_VAR RETURN; END_FUNCTION
 
 
 
--- a/stage1_2/iec_flex.ll	Sun Oct 14 20:14:13 2018 +0300
+++ b/stage1_2/iec_flex.ll	Wed Feb 13 14:16:26 2019 +0000
@@ -247,6 +247,8 @@
 void  append_bodystate_buffer(const char *text, int is_whitespace = 0);
 void   unput_bodystate_buffer(void);
 int  isempty_bodystate_buffer(void);
+void     del_bodystate_buffer(void);
+
 
 int GetNextChar(char *b, int maxBuffer);
 %}
@@ -1231,12 +1233,12 @@
 				 append_bodystate_buffer(yytext, 1 /* is whitespace */); 
 				}
 	/* 'INITIAL_STEP' always used in beginning of SFCs !! */
-INITIAL_STEP			{ if (isempty_bodystate_buffer())	{unput_text(0); BEGIN(sfc_state);}
+INITIAL_STEP			{ if (isempty_bodystate_buffer())	{unput_text(0); del_bodystate_buffer(); BEGIN(sfc_state);}
 				  else					{append_bodystate_buffer(yytext);}
 				}
  
 	/* ':=', at the very beginning of a 'body', occurs only in transitions and not Function, FB, or Program bodies! */
-:=				{ if (isempty_bodystate_buffer())	{unput_text(0); BEGIN(st_state);} /* We do _not_ return a start_ST_body_token here, as bison does not expect it! */
+:=				{ if (isempty_bodystate_buffer())	{unput_text(0); del_bodystate_buffer(); BEGIN(st_state);} /* We do _not_ return a start_ST_body_token here, as bison does not expect it! */
 				  else				 	{append_bodystate_buffer(yytext);}
 				}
  
@@ -1257,7 +1259,7 @@
 FOR				|
 WHILE				|
 EXIT				|
-REPEAT				{ if (isempty_bodystate_buffer())	{unput_text(0); BEGIN(st_state); return start_ST_body_token;}
+REPEAT				{ if (isempty_bodystate_buffer())	{unput_text(0); del_bodystate_buffer(); BEGIN(st_state); return start_ST_body_token;}
 				  else				 	{append_bodystate_buffer(yytext);}
 				}
 
@@ -2183,6 +2185,16 @@
 }
 
 
+/* Delete all data in bodystate. */
+/* Will be used to delete ST whitespace when not needed. If not deleted this whitespace 
+ * will be prepended to the next text block of code being appended to bodystate_buffer,
+ * which may cause trouble if it is IL code
+ */
+void  del_bodystate_buffer(void) {
+  free(bodystate_buffer);
+  bodystate_buffer        = NULL;
+  bodystate_is_whitespace = 1;  
+}
 
 
 /* Called by flex when it reaches the end-of-file */
--- a/stage3/fill_candidate_datatypes.cc	Sun Oct 14 20:14:13 2018 +0300
+++ b/stage3/fill_candidate_datatypes.cc	Wed Feb 13 14:16:26 2019 +0000
@@ -2011,7 +2011,7 @@
 	 *       We do not need to generate an error message. This error will be caught somewhere else!
 	 */
 	if (NULL == prev_il_instruction) return NULL;
-	if (NULL == il_operand)          return NULL;
+	if (NULL != il_operand)          return NULL;
 	for (unsigned int i = 0; i < prev_il_instruction->candidate_datatypes.size(); i++) {
 		if (get_datatype_info_c::is_ANY_BIT_compatible(prev_il_instruction->candidate_datatypes[i]))
 			add_datatype_to_candidate_list(symbol, prev_il_instruction->candidate_datatypes[i]);
--- a/stage4/generate_c/generate_c.cc	Sun Oct 14 20:14:13 2018 +0300
+++ b/stage4/generate_c/generate_c.cc	Wed Feb 13 14:16:26 2019 +0000
@@ -544,9 +544,27 @@
 /***********************************************************************/
 /***********************************************************************/
 
-#define MILLISECOND 1000000
+#define MILLISECOND ((unsigned long long)1000000)
 #define SECOND 1000 * MILLISECOND
 
+#define ULL_MAX std::numeric_limits<unsigned long long>::max()
+
+/* unsigned long long -> multiply and add : time_var += interval * multiplier  */
+/*  note: multiplier must be <> 0 due to overflow test                         */
+#define ULL_MUL_ADD(time_var, interval, multiplier, overflow_flag) {                       \
+    /* Test overflow on MUL by pre-condition: If (ULL_MAX / a) < b => overflow! */         \
+    overflow_flag |= ((ULL_MAX / (multiplier)) < GET_CVALUE(uint64, interval));            \
+    /* Test overflow on ADD by pre-condition: If (ULL_MAX - a) < b => overflow! */         \
+    overflow_flag |= ((ULL_MAX - (GET_CVALUE(uint64, interval) * multiplier)) < time_var); \
+    time_var += GET_CVALUE(uint64, interval) * (multiplier);                               \
+}
+
+/* long double -> multiply and add : time_var += interval * multiplier  */
+#define LDB_MUL_ADD(time_var, interval, multiplier) {  \
+    time_var += GET_CVALUE(real64, interval) * (multiplier);               \
+}
+
+
 unsigned long long calculate_time(symbol_c *symbol) {
   if (NULL == symbol) return 0;
   
@@ -567,52 +585,54 @@
     /* SYM_REF5(interval_c, days, hours, minutes, seconds, milliseconds) */
       unsigned long long int time_ull = 0; 
       long double            time_ld  = 0;
-      /*
-      const unsigned long long int MILLISECOND = 1000000;
-      const unsigned long long int      SECOND = 1000 * MILLISECOND
-      */
+      bool                   ovflow   = false;
       
       if (NULL != interval->milliseconds) {
         if      (VALID_CVALUE( int64, interval->milliseconds) &&           GET_CVALUE( int64, interval->milliseconds) < 0) ERROR; // interval elements should always be positive!
-        if      (VALID_CVALUE( int64, interval->milliseconds)) time_ull += GET_CVALUE( int64, interval->milliseconds) * MILLISECOND;
-        else if (VALID_CVALUE(uint64, interval->milliseconds)) time_ull += GET_CVALUE(uint64, interval->milliseconds) * MILLISECOND;
-        else if (VALID_CVALUE(real64, interval->milliseconds)) time_ld  += GET_CVALUE(real64, interval->milliseconds) * MILLISECOND;
+        if      (VALID_CVALUE(uint64, interval->milliseconds)) ULL_MUL_ADD(time_ull, interval->milliseconds,  MILLISECOND, ovflow)
+        else if (VALID_CVALUE(real64, interval->milliseconds)) LDB_MUL_ADD(time_ld , interval->milliseconds,  MILLISECOND)
         else ERROR; // if (NULL != interval->milliseconds) is true, then it must have a valid constant value!
       }
    
       if (NULL != interval->seconds     ) {
         if      (VALID_CVALUE( int64, interval->seconds     ) &&           GET_CVALUE( int64, interval->seconds     ) < 0) ERROR; // interval elements should always be positive!
-        if      (VALID_CVALUE( int64, interval->seconds     )) time_ull += GET_CVALUE( int64, interval->seconds     ) * SECOND;
-        else if (VALID_CVALUE(uint64, interval->seconds     )) time_ull += GET_CVALUE(uint64, interval->seconds     ) * SECOND;
-        else if (VALID_CVALUE(real64, interval->seconds     )) time_ld  += GET_CVALUE(real64, interval->seconds     ) * SECOND;
+        if      (VALID_CVALUE(uint64, interval->seconds     )) ULL_MUL_ADD(time_ull, interval->seconds,       SECOND, ovflow)
+        else if (VALID_CVALUE(real64, interval->seconds     )) LDB_MUL_ADD(time_ld , interval->seconds,       SECOND)
         else ERROR; // if (NULL != interval->seconds) is true, then it must have a valid constant value!
       }
 
       if (NULL != interval->minutes     ) {
         if      (VALID_CVALUE( int64, interval->minutes     ) &&           GET_CVALUE( int64, interval->minutes     ) < 0) ERROR; // interval elements should always be positive!
-        if      (VALID_CVALUE( int64, interval->minutes     )) time_ull += GET_CVALUE( int64, interval->minutes     ) * SECOND * 60;
-        else if (VALID_CVALUE(uint64, interval->minutes     )) time_ull += GET_CVALUE(uint64, interval->minutes     ) * SECOND * 60;
-        else if (VALID_CVALUE(real64, interval->minutes     )) time_ld  += GET_CVALUE(real64, interval->minutes     ) * SECOND * 60;
+        if      (VALID_CVALUE(uint64, interval->minutes     )) ULL_MUL_ADD(time_ull, interval->minutes,       SECOND * 60, ovflow)
+        else if (VALID_CVALUE(real64, interval->minutes     )) LDB_MUL_ADD(time_ld , interval->minutes,       SECOND * 60)
         else ERROR; // if (NULL != interval->minutes) is true, then it must have a valid constant value!
       }
 
       if (NULL != interval->hours       ) {
         if      (VALID_CVALUE( int64, interval->hours       ) &&           GET_CVALUE( int64, interval->hours       ) < 0) ERROR; // interval elements should always be positive!
-        if      (VALID_CVALUE( int64, interval->hours       )) time_ull += GET_CVALUE( int64, interval->hours       ) * SECOND * 60 * 60;
-        else if (VALID_CVALUE(uint64, interval->hours       )) time_ull += GET_CVALUE(uint64, interval->hours       ) * SECOND * 60 * 60;
-        else if (VALID_CVALUE(real64, interval->hours       )) time_ld  += GET_CVALUE(real64, interval->hours       ) * SECOND * 60 * 60;
+        if      (VALID_CVALUE(uint64, interval->hours       )) ULL_MUL_ADD(time_ull, interval->hours,         SECOND * 60 * 60, ovflow)
+        else if (VALID_CVALUE(real64, interval->hours       )) LDB_MUL_ADD(time_ld , interval->hours,         SECOND * 60 * 60)
         else ERROR; // if (NULL != interval->hours) is true, then it must have a valid constant value!
       }
 
       if (NULL != interval->days        ) {
         if      (VALID_CVALUE( int64, interval->days        ) &&           GET_CVALUE( int64, interval->days        ) < 0) ERROR; // interval elements should always be positive!
-        if      (VALID_CVALUE( int64, interval->days        )) time_ull += GET_CVALUE( int64, interval->days        ) * SECOND * 60 * 60 * 24;
-        else if (VALID_CVALUE(uint64, interval->days        )) time_ull += GET_CVALUE(uint64, interval->days        ) * SECOND * 60 * 60 * 24;
-        else if (VALID_CVALUE(real64, interval->days        )) time_ld  += GET_CVALUE(real64, interval->days        ) * SECOND * 60 * 60 * 24;
+        if      (VALID_CVALUE(uint64, interval->days        )) ULL_MUL_ADD(time_ull, interval->days,          SECOND * 60 * 60 * 24, ovflow)
+        else if (VALID_CVALUE(real64, interval->days        )) LDB_MUL_ADD(time_ld , interval->days,          SECOND * 60 * 60 * 24)
         else ERROR; // if (NULL != interval->days) is true, then it must have a valid constant value!
       }
 
+      /* Test overflow on ADD by pre-condition: If (ULL_MAX - a) < b => overflow! */
+      ovflow |= ((ULL_MAX - time_ull) < (unsigned long long)time_ld);
       time_ull += time_ld;
+      
+      if (ovflow) {
+        /* time is being stored in ns resolution (MILLISECOND #define is set to 1000000)    */
+        /* time is being stored in unsigned long long (ISO C99 guarantees at least 64 bits) */
+        /* 2⁶64ns works out to around 584.5 years, assuming 365.25 days per year            */
+        STAGE4_ERROR(symbol, symbol, "Internal overflow calculating task interval (must be < 584 years).");
+      }
+
       return time_ull;
   };
   ERROR; // should never reach this point!
@@ -642,8 +662,10 @@
       else
         return euclide(b, c);
     }
-    
-    void update_ticktime(unsigned long long time) {
+
+    bool update_ticktime(unsigned long long time) {
+      bool overflow = false;
+      
       if (common_ticktime == 0)
         common_ticktime = time;
       else if (time > common_ticktime)
@@ -652,10 +674,14 @@
         common_ticktime = euclide(common_ticktime, time);
       if (least_common_ticktime == 0)
         least_common_ticktime = time;
-      else
-        least_common_ticktime = (least_common_ticktime * time) / common_ticktime;
-    }
-
+      else {
+        /* Test overflow on MUL by pre-condition: If (ULL_MAX / a) < b => overflow! */
+        overflow = ((ULL_MAX / least_common_ticktime) < (time / common_ticktime));
+        least_common_ticktime = least_common_ticktime * (time / common_ticktime);
+      }
+      return !overflow;
+    }
+    
     unsigned long long get_common_ticktime(void) {
       return common_ticktime;
     }
@@ -671,9 +697,12 @@
 //SYM_REF2(task_configuration_c, task_name, task_initialization)  
     void *visit(task_initialization_c *symbol) {
       if (symbol->interval_data_source != NULL) {
-    	  unsigned long long time = calculate_time(symbol->interval_data_source);
-    	  if (time < 0)  ERROR;
-    	  else           update_ticktime(time);
+        unsigned long long time = calculate_time(symbol->interval_data_source);
+        if (!update_ticktime(time))
+          /* time is being stored in ns resolution (MILLISECOND #define is set to 1000000)    */
+          /* time is being stored in unsigned long long (ISO C99 guarantees at least 64 bits) */
+          /* 2⁶64ns works out to around 584.5 years, assuming 365.25 days per year            */
+          STAGE4_ERROR(symbol, symbol, "Internal overflow calculating least common multiple of task intervals (must be < 584 years).");
       }
       return NULL;
     }
@@ -2670,6 +2699,8 @@
 
         config_s4o.print("unsigned long long common_ticktime__ = ");
         config_s4o.print_long_long_integer(common_ticktime);
+        config_s4o.print(" * ");
+        config_s4o.print_long_long_integer(1000000 / MILLISECOND);
         config_s4o.print("; /*ns*/\n");
         config_s4o.print("unsigned long greatest_tick_count__ = (unsigned long)");
         config_s4o.print_long_integer(calculate_common_ticktime.get_greatest_tick_count());
--- a/stage4/generate_c/generate_c_st.cc	Sun Oct 14 20:14:13 2018 +0300
+++ b/stage4/generate_c/generate_c_st.cc	Wed Feb 13 14:16:26 2019 +0000
@@ -937,7 +937,7 @@
 /* B 3.2.1 Assignment Statements */
 /*********************************/
 void *visit(assignment_statement_c *symbol) {
-  symbol_c *left_type = search_varfb_instance_type->get_type_id(symbol->l_exp);
+  symbol_c *left_type = symbol->l_exp->datatype;
   
   if (this->is_variable_prefix_null()) {
     symbol->l_exp->accept(*this);