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