From 1addd611e1632b5a8d66a42b1154a7c5c09cb61c Mon Sep 17 00:00:00 2001
From: Botond Baranyi <botond.baranyi@ericsson.com>
Date: Mon, 3 Oct 2016 16:21:11 +0200
Subject: [PATCH] enabled type compatibility for 'out' and 'inout' parameters
 in Runtime2 (artf785695)

Change-Id: I2126f4aeac13172fdf2a18b279093abacf2719db
Signed-off-by: Botond Baranyi <botond.baranyi@ericsson.com>
---
 common/UnicharPattern.cc               |   1 +
 compiler2/TypeCompat.cc                |   4 +-
 compiler2/ttcn3/AST_ttcn3.cc           | 110 +++++++++++++++++++------
 compiler2/ttcn3/AST_ttcn3.hh           |   2 +-
 core/Charstring.cc                     |  25 +++---
 regression_test/typeCompat/Compat.ttcn |  57 +++++++++++++
 6 files changed, 164 insertions(+), 35 deletions(-)

diff --git a/common/UnicharPattern.cc b/common/UnicharPattern.cc
index 8ba957e39..9908411d6 100644
--- a/common/UnicharPattern.cc
+++ b/common/UnicharPattern.cc
@@ -98,6 +98,7 @@ UnicharPattern::UnicharPattern() : mappings_head(NULL)
       "Case-insensitive universal charstring patterns are disabled.\n", mappings_file);
     return;
   }
+  Free(mappings_file);
   
   // this always points to the last element of the list
   mapping_t* mappings_tail = NULL;
diff --git a/compiler2/TypeCompat.cc b/compiler2/TypeCompat.cc
index faba1c859..ce669c906 100644
--- a/compiler2/TypeCompat.cc
+++ b/compiler2/TypeCompat.cc
@@ -482,7 +482,9 @@ void TypeConv::gen_conv_func_array_record_of(char **p_bodies, Module *p_mod)
   else if (m_to_tt == Type::T_SEQOF)
     *p_bodies = mputprintf(*p_bodies, "p_to_v.set_size(%lu);\n",
       (long unsigned)m_from->get_dimension()->get_size());
-  for (size_t i = 0; i < m_from->get_dimension()->get_size(); i++) {
+  size_t dim = m_from_tt == Type::T_ARRAY ? m_from->get_dimension()->get_size() :
+    m_to->get_dimension()->get_size();
+  for (size_t i = 0; i < dim; i++) {
     if (!p_mod->needs_type_conv(of_type_from, of_type_to)) {
       *p_bodies = mputprintf(*p_bodies,
         "if (p_from_v[%lu].is_bound()) p_to_v[%lu] = p_from_v[%lu];\n",
diff --git a/compiler2/ttcn3/AST_ttcn3.cc b/compiler2/ttcn3/AST_ttcn3.cc
index 209262e79..708582a6f 100644
--- a/compiler2/ttcn3/AST_ttcn3.cc
+++ b/compiler2/ttcn3/AST_ttcn3.cc
@@ -8257,17 +8257,33 @@ namespace Ttcn {
         FieldOrArrayRefs *t_subrefs = ref->get_subrefs();
         Type *ref_type = ass->get_Type()->get_field_type(t_subrefs, exp_val);
         if (ref_type) {
-          if (!type->is_identical(ref_type)) {
-            ref->error("Type mismatch: Reference to a %s of type "
-              "`%s' was expected instead of `%s'", expected_string,
+          TypeCompatInfo info(my_scope->get_scope_mod(), type, ref_type, true,
+            false, is_template);
+          TypeChain l_chain_base;
+          TypeChain r_chain_base;
+          if (!type->is_compatible(ref_type, &info, &l_chain_base, &r_chain_base)) {
+            if (info.is_subtype_error()) {
+              ref->error("%s", info.get_subtype_error().c_str());
+            }
+            else if (!info.is_erroneous()) {
+              ref->error("Type mismatch: Reference to a %s of type "
+                "`%s' was expected instead of `%s'", expected_string,
               type->get_typename().c_str(), ref_type->get_typename().c_str());
-          } else if (type->get_sub_type() && ref_type->get_sub_type() &&
-      (type->get_sub_type()->get_subtypetype()==ref_type->get_sub_type()->get_subtypetype()) &&
-      (!type->get_sub_type()->is_compatible(ref_type->get_sub_type()))) {
-        ref->error("Subtype mismatch: subtype %s has no common value with subtype %s",
-                   type->get_sub_type()->to_string().c_str(),
-                   ref_type->get_sub_type()->to_string().c_str());
-    }
+            }
+            else {
+              // Always use the format string.
+              ref->error("%s", info.get_error_str_str().c_str());
+            }
+          }
+          else if ((asstype == A_PAR_VAL_OUT || asstype == A_PAR_VAL_INOUT ||
+                   asstype == A_PAR_TEMPL_OUT || asstype == A_PAR_TEMPL_INOUT) &&
+                   !ref_type->is_compatible(type, &info, &l_chain_base, &r_chain_base)) {
+            // run the type compatibility check in the reverse order, too, for 
+            // 'out' and 'inout' parameters (they need to be converted back after
+            // the function call)
+            // this should never fail if the first type compatibility succeeded
+            FATAL_ERROR("FormalPar::chk_actual_par_by_ref");
+          }
           if (t_subrefs && t_subrefs->refers_to_string_element()) {
             ref->error("Reference to a string element of type `%s' cannot be "
               "used in this context", ref_type->get_typename().c_str());
@@ -9563,8 +9579,11 @@ namespace Ttcn {
     }
   }
 
-  void ActualPar::generate_code(expression_struct *expr, bool copy_needed, bool lazy_param, bool used_as_lvalue) const
+  void ActualPar::generate_code(expression_struct *expr, bool copy_needed,
+                                FormalPar* formal_par) const
   {
+    bool lazy_param = formal_par != NULL ? formal_par->get_lazy_eval() : false;
+    bool used_as_lvalue = formal_par != NULL ? formal_par->get_used_as_lvalue() : false;
     switch (selection) {
     case AP_VALUE:
       if (lazy_param) { // copy_needed doesn't matter in this case
@@ -9631,39 +9650,84 @@ namespace Ttcn {
         if (copy_needed) expr->expr = mputc(expr->expr, ')');
       }
       break;
-    case AP_REF:
+    case AP_REF: {
       if (lazy_param) FATAL_ERROR("ActualPar::generate_code()"); // syntax error should have already happened
       if (copy_needed) FATAL_ERROR("ActualPar::generate_code()");
-      if (gen_restriction_check != TR_NONE ||
-          gen_post_restriction_check != TR_NONE) {
-        // generate runtime check for restricted templates
-        // code for reference + restriction check
-        Common::Assignment *ass = ref->get_refd_assignment();
+      bool is_restricted_template = gen_restriction_check != TR_NONE ||
+        gen_post_restriction_check != TR_NONE;
+      Common::Assignment *ass = ref->get_refd_assignment();
+      bool is_template_par = false;
+      Type* actual_par_type = NULL;
+      Type* formal_par_type = NULL;
+      bool needs_conversion = false;
+      if (formal_par != NULL &&
+          formal_par->get_asstype() != Common::Assignment::A_PAR_TIMER &&
+          formal_par->get_asstype() != Common::Assignment::A_PAR_PORT) {
+        if (formal_par->get_asstype() == Common::Assignment::A_PAR_TEMPL_INOUT ||
+            formal_par->get_asstype() == Common::Assignment::A_PAR_TEMPL_OUT) {
+          is_template_par = true;
+        }
+        actual_par_type = ass->get_Type()->get_field_type(ref->get_subrefs(),
+          is_template_par ? Type::EXPECTED_TEMPLATE : Type::EXPECTED_DYNAMIC_VALUE)->
+          get_type_refd_last();
+        formal_par_type = formal_par->get_Type()->get_type_refd_last();
+        needs_conversion = use_runtime_2 && my_scope->get_scope_mod()->
+          needs_type_conv(actual_par_type, formal_par_type);
+      }
+      if (is_restricted_template || needs_conversion) {
+        // generate runtime check for restricted templates and/or generate
+        // type conversion to the formal parameter's type and back
         const string& tmp_id= my_scope->get_scope_mod_gen()->get_temporary_id();
         const char *tmp_id_str = tmp_id.c_str();
         expression_struct ref_expr;
         Code::init_expr(&ref_expr);
         ref->generate_code_const_ref(&ref_expr);
         ref_expr.preamble = mputprintf(ref_expr.preamble, "%s& %s = %s;\n",
-          ass->get_Type()->get_genname_template(ref->get_my_scope()).c_str(),
-          tmp_id_str, ref_expr.expr);
+          is_template_par ? actual_par_type->get_genname_template(my_scope).c_str() :
+          actual_par_type->get_genname_value(my_scope).c_str(), tmp_id_str, ref_expr.expr);
         if (gen_restriction_check != TR_NONE) {
           ref_expr.preamble = Template::generate_restriction_check_code(
             ref_expr.preamble, tmp_id_str, gen_restriction_check);
         }
+        if (needs_conversion) {
+          // create another temporary, this time of the formal parameter's type,
+          // containing the converted parameter
+          const string& tmp_id2 = my_scope->get_scope_mod_gen()->get_temporary_id();
+          const char *tmp_id2_str = tmp_id2.c_str();
+          ref_expr.preamble = mputprintf(ref_expr.preamble,
+            "%s %s;\n"
+            "if (%s.is_bound() && !%s(%s, %s)) TTCN_error(\"Values or templates "
+            "of types `%s' and `%s' are not compatible at run-time\");\n",
+            is_template_par ? formal_par_type->get_genname_template(my_scope).c_str() :
+            formal_par_type->get_genname_value(my_scope).c_str(), tmp_id2_str,
+            tmp_id_str, TypeConv::get_conv_func(actual_par_type, formal_par_type,
+            my_scope->get_scope_mod()).c_str(), tmp_id2_str, tmp_id_str,
+            actual_par_type->get_typename().c_str(), formal_par_type->get_typename().c_str());
+          // pass the new temporary to the function instead of the original reference
+          expr->expr = mputprintf(expr->expr, "%s", tmp_id2_str);
+          // convert the temporary's new value back to the actual parameter's type
+          ref_expr.postamble = mputprintf(ref_expr.postamble,
+            "if (%s.is_bound() && !%s(%s, %s)) TTCN_error(\"Values or templates "
+            "of types `%s' and `%s' are not compatible at run-time\");\n",
+            tmp_id2_str, TypeConv::get_conv_func(formal_par_type, actual_par_type, 
+            my_scope->get_scope_mod()).c_str(), tmp_id_str, tmp_id2_str,
+            formal_par_type->get_typename().c_str(), actual_par_type->get_typename().c_str());
+        }
+        else { // is_restricted_template
+          expr->expr = mputprintf(expr->expr, "%s", tmp_id_str);
+        }
         if (gen_post_restriction_check != TR_NONE) {
           ref_expr.postamble = Template::generate_restriction_check_code(
             ref_expr.postamble, tmp_id_str, gen_post_restriction_check);
         }
         // copy content of ref_expr to expr
         expr->preamble = mputstr(expr->preamble, ref_expr.preamble);
-        expr->expr = mputprintf(expr->expr, "%s", tmp_id_str);
         expr->postamble = mputstr(expr->postamble, ref_expr.postamble);
         Code::free_expr(&ref_expr);
       } else {
         ref->generate_code(expr);
       }
-      break;
+      break; }
     case AP_DEFAULT:
       if (copy_needed) FATAL_ERROR("ActualPar::generate_code()");
       switch (act->selection) {
@@ -9838,7 +9902,7 @@ namespace Ttcn {
     size_t nof_pars = params.size();
     for (size_t i = 0; i < nof_pars; i++) {
       if (i > 0) expr->expr = mputstr(expr->expr, ", ");
-      params[i]->generate_code(expr, false, p_fpl && p_fpl->get_fp_byIndex(i)->get_lazy_eval(), p_fpl && p_fpl->get_fp_byIndex(i)->get_used_as_lvalue());
+      params[i]->generate_code(expr, false, p_fpl != NULL ? p_fpl->get_fp_byIndex(i) : NULL);
     }
   }
 
@@ -10009,7 +10073,7 @@ namespace Ttcn {
         } // if (subrefs != NULL)
       } // if (ActualPar::AP_REF == par->get_selection())
       
-      par->generate_code(expr, copy_needed, p_fpl && p_fpl->get_fp_byIndex(i)->get_lazy_eval(), p_fpl && p_fpl->get_fp_byIndex(i)->get_used_as_lvalue());
+      par->generate_code(expr, copy_needed, p_fpl != NULL ? p_fpl->get_fp_byIndex(i) : NULL);
     }
     value_refs.clear();
     template_refs.clear();
diff --git a/compiler2/ttcn3/AST_ttcn3.hh b/compiler2/ttcn3/AST_ttcn3.hh
index 03e87938b..757834325 100644
--- a/compiler2/ttcn3/AST_ttcn3.hh
+++ b/compiler2/ttcn3/AST_ttcn3.hh
@@ -129,7 +129,7 @@ namespace Ttcn {
      * Flag \a copy_needed indicates whether to add an extra copy constructor
      * call if \a this contains a referenced value or template to avoid
      * aliasing problems with other out/inout parameters. */
-    void generate_code(expression_struct *expr, bool copy_needed, bool lazy_param=false, bool used_as_lvalue=false) const;
+    void generate_code(expression_struct *expr, bool copy_needed, FormalPar* formal_par) const;
     /** Appends the initialization sequence of all (directly or indirectly)
      * referred non-parameterized templates and the default values of all
      *  parameterized templates to \a str and returns the resulting string. 
diff --git a/core/Charstring.cc b/core/Charstring.cc
index 49924ddab..575f81ba5 100644
--- a/core/Charstring.cc
+++ b/core/Charstring.cc
@@ -208,17 +208,22 @@ CHARSTRING& CHARSTRING::operator=(const UNIVERSAL_CHARSTRING& other_value)
 {
   other_value.must_bound("Assignment of an unbound universal charstring to "
                          "a charstring.");
-  clean_up();
-  int n_chars = other_value.val_ptr->n_uchars;
-  init_struct(n_chars);
-  for (int i = 0; i < n_chars; ++i) {
-    const universal_char& uc = other_value.val_ptr->uchars_ptr[i];
-    if (uc.uc_group != 0 || uc.uc_plane != 0 || uc.uc_row != 0) {
-      TTCN_error("Multiple-byte characters cannot be assigned to a charstring, "
-        "invalid character char(%u, %u, %u, %u) at index %d.", 
-        uc.uc_group, uc.uc_plane, uc.uc_row, uc.uc_cell, i);
+  if (other_value.charstring) {
+    *this = other_value.cstr;
+  }
+  else {
+    clean_up();
+    int n_chars = other_value.val_ptr->n_uchars;
+    init_struct(n_chars);
+    for (int i = 0; i < n_chars; ++i) {
+      const universal_char& uc = other_value.val_ptr->uchars_ptr[i];
+      if (uc.uc_group != 0 || uc.uc_plane != 0 || uc.uc_row != 0) {
+        TTCN_error("Multiple-byte characters cannot be assigned to a charstring, "
+          "invalid character char(%u, %u, %u, %u) at index %d.", 
+          uc.uc_group, uc.uc_plane, uc.uc_row, uc.uc_cell, i);
+      }
+      val_ptr->chars_ptr[i] = other_value.val_ptr->uchars_ptr[i].uc_cell;
     }
-    val_ptr->chars_ptr[i] = other_value.val_ptr->uchars_ptr[i].uc_cell;
   }
   return *this;
 }
diff --git a/regression_test/typeCompat/Compat.ttcn b/regression_test/typeCompat/Compat.ttcn
index 96480b464..1669b7612 100644
--- a/regression_test/typeCompat/Compat.ttcn
+++ b/regression_test/typeCompat/Compat.ttcn
@@ -153,6 +153,15 @@ function f3(in template myrec1 p1) return template myrec2 {
   return p1
 }
 function f4(in template intlist4 p1) { }
+function f5(inout myrec1 p1, out intlist4 p2) {
+  p1.f1 := p1.f1 + 1;
+  p2 := { p1.f2, p1.f1 };
+}
+function f6(inout template intlist4 p1, out template myrec2 p2) {
+  p1 := { p1[1], p1[0] };
+  p2 := p1;
+}
+
 // A very nice use of type compatibility!  Return a different type on each
 // branch.  Both f2() and ReturnMeAll() returns "{ 2, 3 }".
 function ReturnMeAll() return myrec1 {
@@ -373,6 +382,53 @@ testcase tc_Params() runs on Empty {
   f4(myrec1 : { 1, 2 })
 }
 
+// testing type compatibility for 'out' and 'inout' parameters
+testcase tc_Params2() runs on Empty {
+  // testing value parameters
+  var myrec1 v_rec1 := { 1, 4 };
+  var myrec2 v_rec2;
+  var intlist1 v_il1 := { 1, 4 };
+  var intlist4 v_il4;
+  
+  // only the 2nd parameter needs conversion
+  f5(v_rec1, v_rec2);
+  if (v_rec1 == { 2, 4 }) { setverdict(pass); } else { setverdict(fail, 1); }
+  if (v_rec2 == { 4, 2 }) { setverdict(pass); } else { setverdict(fail, v_rec2); }
+  
+  // only the 1st parameter needs conversion
+  f5(v_il1, v_il4);
+  if (v_il1 == { 2, 4 }) { setverdict(pass); } else { setverdict(fail, 3); }
+  if (v_il4 == { 4, 2 }) { setverdict(pass); } else { setverdict(fail, 4); }
+  
+  // both parameters need conversion
+  v_rec2 := { 1, 4 };
+  f5(v_rec2, v_il1);
+  if (v_rec2 == { 2, 4 }) { setverdict(pass); } else { setverdict(fail, 5); }
+  if (v_il1 == { 4, 2 }) { setverdict(pass); } else { setverdict(fail, 6); }
+  
+  // testing template parameters
+  var template myrec1 vt_rec1;
+  var template (value) myrec2 vt_rec2;
+  var template intlist1 vt_il1 := { 1, 3 };
+  var template intlist4 vt_il4 := { 1, 3 };
+  
+  // only the 2nd parameter needs conversion
+  f6(vt_il4, vt_rec1);
+  if (isvalue(vt_il4) and valueof(vt_il4) == { 3, 1 }) { setverdict(pass); } else { setverdict(fail, 7); }
+  if (isvalue(vt_rec1) and valueof(vt_rec1) == { 3, 1 }) { setverdict(pass); } else { setverdict(fail, 8); }
+  
+  // only the 1st parameter needs conversion
+  f6(vt_il1, vt_rec2);
+  if (isvalue(vt_il1) and valueof(vt_il1) == { 3, 1 }) { setverdict(pass); } else { setverdict(fail, 9); }
+  if (isvalue(vt_il4) and valueof(vt_il4) == { 3, 1 }) { setverdict(pass); } else { setverdict(fail, 10); }
+  
+  // both parameters need conversion
+  vt_rec2 := { 1, 3 };
+  f6(vt_rec2, vt_il4);
+  if (isvalue(vt_rec2) and valueof(vt_rec2) == { 3, 1 }) { setverdict(pass); } else { setverdict(fail, 11); }
+  if (isvalue(vt_il4) and valueof(vt_il4) == { 3, 1 }) { setverdict(pass); } else { setverdict(fail, 12); }
+}
+
 testcase tc_EqNeAss() runs on Empty {
   var myrec1 rec1 := { 1, 2 }
   var myrec1Ext rece1 := { 1, 2 }
@@ -523,6 +579,7 @@ control {
   execute(tc_ProcPortOps())
   execute(tc_EqNeAss())
   execute(tc_Params())
+  execute(tc_Params2())
   execute(tc_DefPar(rec2, rec1))
   execute(tc_Exprs())
   execute(tc_Templates())
-- 
GitLab