Commit 1addd611 authored by Botond Baranyi's avatar Botond Baranyi
Browse files

enabled type compatibility for 'out' and 'inout' parameters in Runtime2 (artf785695)



Change-Id: I2126f4aeac13172fdf2a18b279093abacf2719db
Signed-off-by: Botond Baranyi's avatarBotond Baranyi <botond.baranyi@ericsson.com>
parent 92ec5512
......@@ -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;
......
......@@ -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",
......
......@@ -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();
......
......@@ -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.
......
......@@ -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;
}
......
......@@ -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())
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment