diff --git a/compiler2/AST.cc b/compiler2/AST.cc index d571bf866e8943a60504787e51f5c89ebc726d3b..df2c754a45b85b7a46effb9b9040214d3d5580d9 100644 --- a/compiler2/AST.cc +++ b/compiler2/AST.cc @@ -1923,6 +1923,24 @@ namespace Common { return false; } + bool Assignment::is_abstract() const + { + FATAL_ERROR("Common::Assignment::is_abstract()"); + return false; + } + + bool Assignment::is_final() const + { + FATAL_ERROR("Common::Assignment::is_final()"); + return false; + } + + Ttcn::PropertyBody* Assignment::get_property_body() + { + FATAL_ERROR("Common::Assignment::get_property_body()"); + return 0; + } + Setting *Assignment::get_Setting() { FATAL_ERROR("Common::Assignment::get_Setting()"); diff --git a/compiler2/AST.hh b/compiler2/AST.hh index 0f772219e86575b97e5866cb903a2e0ceefc54b1..198e2584a29b54d986213a5174e2461214e73c0d 100644 --- a/compiler2/AST.hh +++ b/compiler2/AST.hh @@ -51,6 +51,7 @@ namespace Ttcn { class ArrayDimensions; class Group; class Module; + class PropertyBody; } // namespace Ttcn // not defined here @@ -580,6 +581,9 @@ namespace Common { * Only relevant for variable and template variable definitions. */ virtual bool is_property() const { return false; } + virtual bool is_abstract() const; + virtual bool is_final() const; + virtual Ttcn::PropertyBody* get_property_body(); /** @name Need to be overridden and implemented in derived classes. * Calling these methods causes a FATAL_ERROR. * diff --git a/compiler2/Setting.hh b/compiler2/Setting.hh index 1f68d200395f5882c70da54cd06a00b87c748562..afa7495eb696d86e5542d7d1ff60efc889d2028b 100644 --- a/compiler2/Setting.hh +++ b/compiler2/Setting.hh @@ -758,6 +758,12 @@ public: REF_THIS, // reference to the current class object REF_VALUE // reference to the value being matched in a dynamic template }; + enum refside_t { + REF_SIDE_ERROR = 0, // indicates that an error related to the reference side or visibility has already been reported + RHS_REF = 1, // the reference is on the right-hand-side (i.e. the referenced assignment is read) + LHS_REF = 2, // the reference is on the left-hand-side (i.e. the referenced assignment is written) + BOTH_SIDE_REF = 3 // the reference is on both sides (i.e. the referenced assignment is read and written) + }; protected: // Derived classes need access /** Points to the referred assignment. Used for caching. */ Assignment *refd_ass; @@ -775,6 +781,8 @@ public: /** Creates a display-name for the reference. */ virtual reftype_t get_reftype() const { return REF_BASIC; } virtual void set_reftype(reftype_t) { FATAL_ERROR("Ref_simple::set_reftype"); } + virtual refside_t get_ref_side() const { FATAL_ERROR("Ref_simple::get_ref_side"); return RHS_REF; } + virtual void set_ref_side(refside_t) { FATAL_ERROR("Ref_simple::set_ref_side"); } virtual string get_dispname(); virtual Setting* get_refd_setting(); /** \param check_parlist is ignored */ diff --git a/compiler2/Type.cc b/compiler2/Type.cc index 1a403127e4647ec8e1d1b4fd376eb4df14586204..e0ef1c2f3296730c5c3e914d08bd251c99865d56 100644 --- a/compiler2/Type.cc +++ b/compiler2/Type.cc @@ -1745,7 +1745,7 @@ namespace Common { return 0; } Assignment* ass = class_->get_local_ass_byId(id); - if (!class_->chk_visibility(ass, ref, subrefs->get_my_scope())) { + if (!class_->chk_visibility(ass, subrefs->get_my_ref(), ref)) { // the member is not visible (the error has already been reported) return 0; } @@ -1837,7 +1837,7 @@ namespace Common { } if (!is_object_method) { Assignment* ass = class_->get_local_ass_byId(id); - if (!class_->chk_visibility(ass, ref, subrefs->get_my_scope())) { + if (!class_->chk_visibility(ass, subrefs->get_my_ref(), ref)) { // the method is not visible (the error has already been reported) return 0; } diff --git a/compiler2/Value.cc b/compiler2/Value.cc index 13a646a1d07ff638317cfae899cad82d6fe5672c..5f3b6a9d174a7966e1c104fe6de9fc54fff3b881 100644 --- a/compiler2/Value.cc +++ b/compiler2/Value.cc @@ -6692,6 +6692,7 @@ void Value::chk_expr_operand_execute_refd(Value *v1, { Error_Context cntxt(this, "In the parameters of %s()", opname); Ttcn::Reference* ref = u.expr.r1; + ref->set_ref_side(Ref_simple::BOTH_SIDE_REF); Ttcn::FieldOrArrayRefs* t_subrefs = ref->get_subrefs(); Type* t_type_last = 0; Assignment* t_ass = ref->get_refd_assignment(); @@ -6774,6 +6775,7 @@ void Value::chk_expr_operand_execute_refd(Value *v1, } ref = u.expr.r2; + ref->set_ref_side(Ref_simple::LHS_REF); t_subrefs = ref->get_subrefs(); t_ass = ref->get_refd_assignment(); @@ -15425,8 +15427,8 @@ void Value::chk_expr_operand_execute_refd(Value *v1, expression_struct expr1, expr2; Code::init_expr(&expr1); Code::init_expr(&expr2); - u.expr.r1->generate_code(&expr1); - u.expr.r2->generate_code(&expr2); + u.expr.r1->generate_code_actualpar(&expr1); + u.expr.r2->generate_code_actualpar(&expr2); Type* _type = u.expr.r2->get_refd_assignment()->get_Type()-> get_field_type(u.expr.r2->get_subrefs(), Type::EXPECTED_DYNAMIC_VALUE); @@ -15504,12 +15506,6 @@ void Value::chk_expr_operand_execute_refd(Value *v1, expr->postamble = mputprintf(expr->postamble, "%s", expr2.postamble); } else { // new codec handling - if (expr1.preamble != NULL) { - expr->preamble = mputstr(expr->preamble, expr1.preamble); - } - if (expr2.preamble != NULL) { - expr->preamble = mputstr(expr->preamble, expr2.preamble); - } expression_struct expr3; Code::init_expr(&expr3); if (u.expr.v4 != NULL) { @@ -15681,8 +15677,8 @@ void Value::chk_expr_operand_execute_refd(Value *v1, expression_struct expr1, expr2; Code::init_expr(&expr1); Code::init_expr(&expr2); - u.expr.r1->generate_code(&expr1); - u.expr.r2->generate_code(&expr2); + u.expr.r1->generate_code_actualpar(&expr1); + u.expr.r2->generate_code_actualpar(&expr2); Type* _type = u.expr.r2->get_refd_assignment()->get_Type()-> get_field_type(u.expr.r2->get_subrefs(), Type::EXPECTED_DYNAMIC_VALUE); @@ -15779,12 +15775,6 @@ void Value::chk_expr_operand_execute_refd(Value *v1, expr->postamble = mputprintf(expr->postamble, "%s", expr2.postamble); } else { // new codec handling - if (expr1.preamble != NULL) { - expr->preamble = mputstr(expr->preamble, expr1.preamble); - } - if (expr2.preamble != NULL) { - expr->preamble = mputstr(expr->preamble, expr2.preamble); - } expression_struct expr3; Code::init_expr(&expr3); if (u.expr.v5 != NULL) { diff --git a/compiler2/ttcn3/AST_ttcn3.cc b/compiler2/ttcn3/AST_ttcn3.cc index 128721ffb29e90ea37d616384ce3b95341e28b09..58a062cb0b7a60b8b74050fa1216fec38de5e70a 100644 --- a/compiler2/ttcn3/AST_ttcn3.cc +++ b/compiler2/ttcn3/AST_ttcn3.cc @@ -465,8 +465,9 @@ namespace Ttcn { // 'x.set_y(z)' instead of 'x.y = z' string tmp_id = ref_scope->get_scope_mod_gen()->get_temporary_id(); expr->preamble = mputprintf(expr->preamble, "%s %s;\n", - is_template ? ass->get_Type()->get_genname_template(ref_scope).c_str() : - ass->get_Type()->get_genname_value(ref_scope).c_str(), + ass->get_asstype() == Definition::A_VAR ? + ass->get_Type()->get_genname_value(ref_scope).c_str() : + ass->get_Type()->get_genname_template(ref_scope).c_str(), tmp_id.c_str()); expr->postamble = mputprintf(expr->postamble, "%s->set_%s(%s);\n", expr->expr, id.get_name().c_str(), tmp_id.c_str()); @@ -744,40 +745,53 @@ namespace Ttcn { // ================================= Reference::Reference(const Reference& p) - : Ref_base(p), reftype(p.reftype), gen_const_prefix(false), - expr_cache(NULL) + : Ref_base(p), reftype(p.reftype), ref_side(RHS_REF), + gen_const_prefix(false), expr_cache(NULL), + gen_class_defpar_prefix(false), gen_class_base_call_postfix(false) { params = p.params != NULL ? p.params->clone() : NULL; parlist = p.parlist != NULL ? p.parlist->clone() : NULL; + subrefs.set_my_ref(this); } Reference::Reference(Identifier *p_id) - : Ref_base(), reftype(REF_BASIC), parlist(NULL), params(NULL), - gen_const_prefix(false), expr_cache(NULL), + : Ref_base(), reftype(REF_BASIC), ref_side(RHS_REF), + parlist(NULL), params(NULL), gen_const_prefix(false), expr_cache(NULL), gen_class_defpar_prefix(false), gen_class_base_call_postfix(false) { subrefs.add(new FieldOrArrayRef(p_id)); + subrefs.set_my_ref(this); } Reference::Reference(reftype_t p_reftype) - : Ref_base(), reftype(p_reftype), parlist(NULL), params(NULL), - gen_const_prefix(false), expr_cache(NULL), + : Ref_base(), reftype(p_reftype), ref_side(RHS_REF), + parlist(NULL), params(NULL), gen_const_prefix(false), expr_cache(NULL), gen_class_defpar_prefix(false), gen_class_base_call_postfix(false) { if (reftype != REF_THIS && reftype != REF_VALUE) { FATAL_ERROR("Ttcn::Reference(): basic or 'super' reference with no ID"); } + subrefs.set_my_ref(this); } + Reference::Reference(Identifier *p_modid, Identifier *p_id, reftype_t p_reftype) + : Ref_base(p_modid, p_id), reftype(p_reftype), ref_side(RHS_REF), + parlist(NULL), params(NULL), gen_const_prefix(false), expr_cache(NULL), + gen_class_defpar_prefix(false), gen_class_base_call_postfix(false) + { + subrefs.set_my_ref(this); + } + Reference::Reference(Identifier *p_modid, Identifier *p_id, ParsedActualParameters *p_params, reftype_t p_reftype) - : Ref_base(p_modid, p_id), reftype(p_reftype), parlist(NULL), params(p_params), - gen_const_prefix(false), expr_cache(NULL), + : Ref_base(p_modid, p_id), reftype(p_reftype), ref_side(RHS_REF), + parlist(NULL), params(p_params), gen_const_prefix(false), expr_cache(NULL), gen_class_defpar_prefix(false), gen_class_base_call_postfix(false) { if (p_params == NULL) { FATAL_ERROR("Ttcn::Reference::Reference(): NULL parameter"); } + subrefs.set_my_ref(this); } Reference::~Reference() @@ -996,6 +1010,12 @@ namespace Ttcn { ass = type->get_class_type_body()-> get_local_ass_byId(*subref->get_id()); type = ass->get_Type(); + if (i < subrefs.get_nof_refs() - 1 && ass->is_property() && (ref_side & LHS_REF)) { + error("Left-hand-side reference to property `%s' cannot have index or dot notation " + "applied to it", ass->get_fullname().c_str()); + ref_side = REF_SIDE_ERROR; + return NULL; + } } else { type = type->get_comp_byName(*subref->get_id())->get_type(); @@ -1235,6 +1255,9 @@ namespace Ttcn { return false; } } + if ((ref_side & LHS_REF) && get_refd_assignment_last()->is_property()) { + return false; // property setter is not a single expression + } if (parlist != NULL) { const FormalParList* fplist = (ass != NULL) ? ass->get_FormalParList() : NULL; for (size_t i = 0; i < parlist->get_nof_pars(); i++) { @@ -1385,7 +1408,10 @@ namespace Ttcn { get_class_base_call_postfix() + exception_postfix).c_str()); } } - if (subrefs.get_nof_refs() > 0) subrefs.generate_code(expr, ass, my_scope); + if (subrefs.get_nof_refs() > 0) { + subrefs.generate_code(expr, ass, my_scope, + get_refd_assignment_last()->is_property() ? ref_side == RHS_REF : false); + } } void Reference::generate_code_const_ref(expression_struct_t *expr) @@ -1523,6 +1549,37 @@ namespace Ttcn { if (subrefs.get_nof_refs() > 0) subrefs.generate_code(expr, ass, my_scope); } + void Reference::generate_code_actualpar(expression_struct_t* expr) + { + Common::Assignment* ass = get_refd_assignment_last(); + if (ass->is_property() && (ref_side & LHS_REF)) { + expression_struct_t setter_expr; + Code::init_expr(&setter_expr); + generate_code(&setter_expr); + if (ref_side & RHS_REF) { + expression_struct_t getter_expr; + Code::init_expr(&getter_expr); + generate_code_const_ref(&getter_expr); + expr->preamble = mputstr(expr->preamble, getter_expr.preamble); + // the last 2 characters of the setter's preamble should be ";\n", + // an initializer using the getter is inserted instead of these + expr->preamble = mputprintf(expr->preamble, "%.*s(%s);\n", + (int)(mstrlen(setter_expr.preamble) - 2), setter_expr.preamble, getter_expr.expr); + expr->postamble = mputstr(expr->postamble, getter_expr.postamble); + Code::free_expr(&getter_expr); + } + else { + expr->preamble = mputstr(expr->preamble, setter_expr.preamble); + } + expr->expr = mputstr(expr->expr, setter_expr.expr); + expr->postamble = mputstr(expr->postamble, setter_expr.postamble); + Code::free_expr(&setter_expr); + } + else { + generate_code(expr); + } + } + //FIXME quick hack void Reference::generate_code_ispresentboundchosen(expression_struct_t *expr, bool is_template, const Value::operationtype_t optype, const char* field) @@ -4358,6 +4415,10 @@ namespace Ttcn { t->get_typename().c_str()); break; } + if (my_scope->is_class_scope() && my_scope->get_scope_class()->is_trait()) { + error("Trait class type `%s' cannot have constant members", + my_scope->get_scope_class()->get_my_def()->get_Type()->get_typename().c_str()); + } if (value != NULL) { value_under_check = true; namedbool class_member_init = my_scope->is_class_scope() ? @@ -5120,6 +5181,11 @@ namespace Ttcn { chk_modified(); chk_recursive_derivation(); + if (my_scope->is_class_scope() && my_scope->get_scope_class()->is_trait()) { + error("Trait class type `%s' cannot have template members", + my_scope->get_scope_class()->get_my_def()->get_Type()->get_typename().c_str()); + } + if (body != NULL) { namedbool class_member_init = my_scope->is_class_scope() ? CLASS_MEMBER_INIT : NOT_CLASS_MEMBER_INIT; @@ -5739,27 +5805,32 @@ namespace Ttcn { } // else fall through default: - if (initial_value) { - initial_value->set_my_governor(type); - type->chk_this_value_ref(initial_value); - namedbool class_member_init = my_scope->is_class_scope() ? - CLASS_MEMBER_INIT : NOT_CLASS_MEMBER_INIT; - type->chk_this_value(initial_value, this, is_local() ? - Type::EXPECTED_DYNAMIC_VALUE : Type::EXPECTED_STATIC_VALUE, - INCOMPLETE_ALLOWED, OMIT_NOT_ALLOWED, SUB_CHK, NOT_IMPLICIT_OMIT, - NOT_STR_ELEM, class_member_init); - if (!semantic_check_only) { - initial_value->set_genname_recursive(get_genname()); - initial_value->set_code_section(my_scope->is_class_scope() ? - GovernedSimple::CS_INIT_CLASS : GovernedSimple::CS_INLINE); - if (my_scope->is_class_scope()) { - initial_value->chk_class_member(my_scope->get_scope_class()); + if (initial_value) { + initial_value->set_my_governor(type); + type->chk_this_value_ref(initial_value); + namedbool class_member_init = my_scope->is_class_scope() ? + CLASS_MEMBER_INIT : NOT_CLASS_MEMBER_INIT; + type->chk_this_value(initial_value, this, is_local() ? + Type::EXPECTED_DYNAMIC_VALUE : Type::EXPECTED_STATIC_VALUE, + INCOMPLETE_ALLOWED, OMIT_NOT_ALLOWED, SUB_CHK, NOT_IMPLICIT_OMIT, + NOT_STR_ELEM, class_member_init); + if (!semantic_check_only) { + initial_value->set_genname_recursive(get_genname()); + initial_value->set_code_section(my_scope->is_class_scope() ? + GovernedSimple::CS_INIT_CLASS : GovernedSimple::CS_INLINE); + if (my_scope->is_class_scope()) { + initial_value->chk_class_member(my_scope->get_scope_class()); + } } } - } break; } + if (my_scope->is_class_scope() && my_scope->get_scope_class()->is_trait()) { + error("Trait class type `%s' cannot have variable members", + my_scope->get_scope_class()->get_my_def()->get_Type()->get_typename().c_str()); + } + if (w_attrib_path) { w_attrib_path->chk_global_attrib(); w_attrib_path->chk_no_qualif(); @@ -5930,8 +6001,10 @@ namespace Ttcn { // ================================= Def_Property::Def_Property(Identifier* p_id, Type* p_type, Value* p_initial_value, - PropertyBody* p_body) - : Def_Var(p_id, p_type, p_initial_value), body(p_body) + PropertyBody* p_body, bool p_abstract, + bool p_final, bool p_deterministic) + : Def_Var(p_id, p_type, p_initial_value), abstract(p_abstract), final(p_final), + deterministic(p_deterministic), body(p_body) { } @@ -5954,16 +6027,13 @@ namespace Ttcn { return; } Def_Var::chk(); + Error_Context cntxt(this, "In %sproperty definition `%s'", + body == NULL && !abstract ? "automatic " : "", id->get_dispname().c_str()); if (body != NULL) { - body->chk(this); + body->chk(this, abstract, final, deterministic); } - } - - void Def_Property::use_as_lvalue(const Location& p_loc) - { - if (body != NULL && body->get_setter() == NULL) { - p_loc.error("Cannot modify property `%s' because it doesn't have a setter", - id->get_dispname().c_str()); + else { + PropertyBody::chk_automatic(this, abstract); } } @@ -6085,6 +6155,11 @@ namespace Ttcn { t->get_typename().c_str()); } + if (my_scope->is_class_scope() && my_scope->get_scope_class()->is_trait()) { + error("Trait class type `%s' cannot have template variable members", + my_scope->get_scope_class()->get_my_def()->get_Type()->get_typename().c_str()); + } + if (initial_value) { initial_value->set_my_governor(type); initial_value->flatten(false); @@ -6283,8 +6358,9 @@ namespace Ttcn { Def_Property_Template::Def_Property_Template(Identifier* p_id, Type* p_type, Template* p_initial_value, template_restriction_t p_template_restriction, - PropertyBody* p_body) - : Def_Var_Template(p_id, p_type, p_initial_value, p_template_restriction), body(p_body) + PropertyBody* p_body, bool p_abstract, bool p_final, bool p_deterministic) + : Def_Var_Template(p_id, p_type, p_initial_value, p_template_restriction), abstract(p_abstract), + final(p_final), deterministic(p_deterministic), body(p_body) { } @@ -6293,6 +6369,69 @@ namespace Ttcn { delete body; } + void Def_Property_Template::set_fullname(const string& p_fullname) + { + Def_Var_Template::set_fullname(p_fullname); + if (body != NULL) { + body->set_fullname(p_fullname); + } + } + + void Def_Property_Template::chk() + { + if (checked) { + return; + } + Def_Var_Template::chk(); + Error_Context cntxt(this, "In %sproperty template definition `%s'", + body == NULL && !abstract ? "automatic " : "", id->get_dispname().c_str()); + if (body != NULL) { + body->chk(this, abstract, final, deterministic); + } + else { + PropertyBody::chk_automatic(this, abstract); + } + } + + void Def_Property_Template::generate_code(output_struct* target, bool clean_up) + { + type->generate_code(target); + if (body != NULL) { + body->generate_code(target); + } + else { + // the automatic property generates a member variable with the property name, + // that stores the current value of a property; + // the automatic getter and setter gets and sets this member (just like with record fields); + // this member is always private, while the getter and setter use the property's visibility + target->header.class_defs = mputprintf(target->header.class_defs, + "\nprivate:\n"); + const string& name = get_genname(); + const_def cdef; + Code::init_cdef(&cdef); + type->generate_code_object(&cdef, my_scope, name, NULL, true, false, true); + Code::merge_cdef(target, &cdef, true); + Code::free_cdef(&cdef); + target->header.class_defs = mputprintf(target->header.class_defs, + "\n%s:\n", ClassTypeBody::get_visibility_str(visibilitytype)); + const string& type_str = type->get_genname_template(my_scope); + const string& class_name = my_scope->get_scope_class()->get_id()->get_name(); + Getter::generate_code_automatic(target, type_str, name, class_name); + Setter::generate_code_automatic(target, type_str, name, class_name); + } + if (initial_value != NULL) { + char*& init = target->temp.constructor_preamble; + string tmp_id = my_scope->get_scope_mod_gen()->get_temporary_id(); + init = mputprintf(init, "%s %s;\n", type->get_genname_template(my_scope).c_str(), tmp_id.c_str()); + init = initial_value->generate_code_init(init, tmp_id.c_str()); + if (template_restriction != TR_NONE && gen_restriction_check) { + init = Template::generate_restriction_check_code(init, + tmp_id.c_str(), template_restriction); + } + init = mputprintf(init, "set_%s(%s);\n", get_genname().c_str(), tmp_id.c_str()); + } + } + // ================================= // ===== Def_Timer // ================================= @@ -10007,6 +10146,15 @@ namespace Ttcn { } } + void FormalPar::replace_defval(TemplateInstance* p_new_defval) + { + if (checked) { + FATAL_ERROR("FormalPar::replace_defval"); + } + delete defval.ti; + defval.ti = p_new_defval; + } + ActualPar *FormalPar::chk_actual_par(TemplateInstance *actual_par, Type::expected_value_t exp_val) { @@ -10272,6 +10420,7 @@ namespace Ttcn { ActualPar *FormalPar::chk_actual_par_by_ref(TemplateInstance *actual_par, bool is_template, Type::expected_value_t exp_val) { + bool inout_par = get_asstype() == A_PAR_VAL_INOUT || get_asstype() == A_PAR_TEMPL_INOUT; Type *ap_type = actual_par->get_Type(); if (ap_type) { ap_type->warning("Explicit type specification is useless for an %s", @@ -10291,6 +10440,7 @@ namespace Ttcn { Template *ap_template = actual_par->get_Template(); if (ap_template->is_Ref()) { Reference *ref = ap_template->get_Ref(); + ref->set_ref_side(inout_par ? Reference::BOTH_SIDE_REF : Reference::LHS_REF); Common::Assignment *ass = ref->get_refd_assignment_last(); if (!ass) { delete ref; @@ -10300,7 +10450,7 @@ namespace Ttcn { switch (ass->get_asstype()) { case A_PAR_VAL_IN: ass->use_as_lvalue(*ref); - if (get_asstype() == A_PAR_VAL_OUT || get_asstype() == A_PAR_TEMPL_OUT) { + if (!inout_par) { ass->warning("Passing an `in' parameter as another function's `out' parameter"); } // no break @@ -10312,7 +10462,7 @@ namespace Ttcn { break; case A_PAR_TEMPL_IN: ass->use_as_lvalue(*ref); - if (get_asstype() == A_PAR_VAL_OUT || get_asstype() == A_PAR_TEMPL_OUT) { + if (!inout_par) { ass->warning("Passing an `in' parameter as another function's `out' parameter"); } // no break @@ -10446,6 +10596,7 @@ namespace Ttcn { Template *ap_template = actual_par->get_Template(); if (ap_template->is_Ref()) { Reference *ref = ap_template->get_Ref(); + ref->set_ref_side(Reference::BOTH_SIDE_REF); Common::Assignment *ass = ref->get_refd_assignment(); if (!ass) { delete ref; @@ -10493,6 +10644,7 @@ namespace Ttcn { Template *ap_template = actual_par->get_Template(); if (ap_template->is_Ref()) { Reference *ref = ap_template->get_Ref(); + ref->set_ref_side(Reference::BOTH_SIDE_REF); Common::Assignment *ass = ref->get_refd_assignment(); if (!ass) { delete ref; @@ -10617,9 +10769,6 @@ namespace Ttcn { if (!defval.ap || defval_generated) return str; defval_generated = true; bool defpar_wrapper = has_defpar_wrapper(); - if (!usage_found && defpar_wrapper) { - return str; - } switch (defval.ap->get_selection()) { case ActualPar::AP_VALUE: { Value *val = defval.ap->get_Value(); @@ -12220,7 +12369,7 @@ namespace Ttcn { const char *tmp_id_str = tmp_id.c_str(); expression_struct ref_expr; Code::init_expr(&ref_expr); - ref->generate_code(&ref_expr); + ref->generate_code_actualpar(&ref_expr); ref_expr.preamble = mputprintf(ref_expr.preamble, "%s& %s = %s;\n", 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); @@ -12264,7 +12413,7 @@ namespace Ttcn { expr->postamble = mputstr(expr->postamble, ref_expr.postamble); Code::free_expr(&ref_expr); } else { - ref->generate_code(expr); + ref->generate_code_actualpar(expr); } break; } case AP_DEFAULT: diff --git a/compiler2/ttcn3/AST_ttcn3.hh b/compiler2/ttcn3/AST_ttcn3.hh index be8365e0bcec503bade19544e6cf93bf51c32f57..79aa32dac4aaa0be530d5b7e24928e64ede4583e 100644 --- a/compiler2/ttcn3/AST_ttcn3.hh +++ b/compiler2/ttcn3/AST_ttcn3.hh @@ -274,8 +274,9 @@ namespace Ttcn { bool refs_str_element; Common::Scope* my_scope; ///< %Scope. Not owned bool checked; // set by Type::get_field_type + Reference* my_ref; // the reference these subrefs belong to (not owned and may be null) public: - FieldOrArrayRefs() : Node(), refs(), refs_str_element(false), my_scope(NULL), checked(false) { } + FieldOrArrayRefs() : Node(), refs(), refs_str_element(false), my_scope(NULL), checked(false), my_ref(NULL) { } FieldOrArrayRefs(const FieldOrArrayRefs& p); ~FieldOrArrayRefs(); FieldOrArrayRefs *clone() const; @@ -285,6 +286,8 @@ namespace Ttcn { void add(FieldOrArrayRef *p_ref) { refs.add(p_ref); } size_t get_nof_refs() const { return refs.size(); } FieldOrArrayRef* get_ref(size_t i) const { return refs[i]; } + Reference* get_my_ref() const { return my_ref; } + void set_my_ref(Reference* p_ref) { my_ref = p_ref; } bool has_unfoldable_index() const; bool is_checked() const { return checked; } void set_checked() { checked = true; } @@ -372,6 +375,7 @@ namespace Ttcn { */ class Reference : public Ref_base { reftype_t reftype; + refside_t ref_side; /** "Processed" parameter list, after the semantic check. */ ActualParList* parlist; /** "Raw" parameter list, before the semantic check. */ @@ -388,10 +392,7 @@ namespace Ttcn { public: Reference(Identifier *p_id); Reference(reftype_t p_reftype); - Reference(Identifier *p_modid, Identifier *p_id, reftype_t p_reftype = REF_BASIC) - : Ref_base(p_modid, p_id), reftype(p_reftype), parlist(NULL), params(NULL), - gen_const_prefix(false), expr_cache(NULL), - gen_class_defpar_prefix(false), gen_class_base_call_postfix(false) { } + Reference(Identifier *p_modid, Identifier *p_id, reftype_t p_reftype = REF_BASIC); Reference(Identifier *p_modid, Identifier *p_id, ParsedActualParameters *p_params, reftype_t p_reftype = REF_BASIC); ~Reference(); @@ -404,6 +405,8 @@ namespace Ttcn { virtual Common::Assignment *get_refd_assignment_last(bool check_parlist = true); virtual reftype_t get_reftype() const { return reftype; } virtual void set_reftype(reftype_t p_reftype) { reftype = p_reftype; } + virtual refside_t get_ref_side() const { return ref_side; } + virtual void set_ref_side(refside_t p_ref_side) { ref_side = p_ref_side; } virtual const Identifier* get_modid(); virtual const Identifier* get_id(); virtual ActualParList *get_parlist(); @@ -427,6 +430,7 @@ namespace Ttcn { * Argument \a p_scope shall point to the scope of the statement. */ void generate_code_portref(expression_struct_t *expr, Scope *p_scope); virtual void generate_code_const_ref(expression_struct_t *expr); + void generate_code_actualpar(expression_struct_t* expr); /** * Generates code for checking if the reference * and the referred objects are bound or not.*/ @@ -1244,6 +1248,9 @@ namespace Ttcn { }; class Def_Property : public Def_Var { + bool abstract; + bool final; + bool deterministic; PropertyBody* body; /// Copy constructor disabled @@ -1253,14 +1260,14 @@ namespace Ttcn { public: Def_Property(Identifier* p_id, Type* p_type, Value* p_initial_value, - PropertyBody* p_body); + PropertyBody* p_body, bool p_abstract, bool p_final, bool p_deterministic); virtual ~Def_Property(); virtual bool is_property() const { return true; } - bool is_automatic() const { return body == NULL; } + virtual bool is_abstract() const { return abstract; } + virtual bool is_final() const { return final; } + virtual PropertyBody* get_property_body() { return body; } virtual void set_fullname(const string& p_fullname); virtual void chk(); - virtual void use_as_lvalue(const Location& p_loc); - // todo: use as rvalue virtual void generate_code(output_struct* target, bool clean_up = false); }; @@ -1269,7 +1276,7 @@ namespace Ttcn { * definition. */ class Def_Var_Template : public Definition { - private: + protected: Type *type; /** the initial value: optional and maybe incomplete */ Template *initial_value; @@ -1306,6 +1313,9 @@ namespace Ttcn { }; class Def_Property_Template : public Def_Var_Template { + bool abstract; + bool final; + bool deterministic; PropertyBody* body; /// Copy constructor disabled @@ -1315,10 +1325,16 @@ namespace Ttcn { public: Def_Property_Template(Identifier* p_id, Type* p_type, Template* p_initial_value, - template_restriction_t p_template_restriction, PropertyBody* p_body); + template_restriction_t p_template_restriction, PropertyBody* p_body, + bool p_abstract, bool p_final, bool p_deterministic); virtual ~Def_Property_Template(); virtual bool is_property() const { return true; } - bool is_automatic() const { return body == NULL; } + virtual bool is_abstract() const { return abstract; } + virtual bool is_final() const { return final; } + virtual PropertyBody* get_property_body() { return body; } + virtual void set_fullname(const string& p_fullname); + virtual void chk(); + virtual void generate_code(output_struct* target, bool clean_up = false); }; /** @@ -1956,6 +1972,7 @@ namespace Ttcn { * \pre chk() has been called (checked==true) */ ActualPar *get_defval() const; void set_defval(ActualPar *defpar); + void replace_defval(TemplateInstance* p_new_defval); void set_my_parlist(FormalParList *p_parlist) { my_parlist = p_parlist; } FormalParList *get_my_parlist() const { return my_parlist; } ActualPar *chk_actual_par(TemplateInstance *actual_par, diff --git a/compiler2/ttcn3/Statement.cc b/compiler2/ttcn3/Statement.cc index 0797dd4be4d9c5c3d178af6ac04937e5d446d6c2..781ec52faeedce6550f271142361134d3245e8b6 100644 --- a/compiler2/ttcn3/Statement.cc +++ b/compiler2/ttcn3/Statement.cc @@ -1473,6 +1473,9 @@ namespace Ttcn { comp_op.compref = p_val; comp_op.any_from = p_any_from; comp_op.index_redirect = p_index_redirect; + if (comp_op.index_redirect != NULL) { + comp_op.index_redirect->set_ref_side(Reference::LHS_REF); + } break; default: FATAL_ERROR("Statement::Statement()"); @@ -1503,6 +1506,9 @@ namespace Ttcn { port_op.s.sendpar=p_templinst; port_op.s.toclause=p_val; port_op.s.timestampredirect = p_timestampredirect; + if (port_op.s.timestampredirect != NULL) { + port_op.s.timestampredirect->set_ref_side(Reference::LHS_REF); + } break; default: FATAL_ERROR("Statement::Statement()"); @@ -1526,6 +1532,9 @@ namespace Ttcn { port_op.s.toclause=p_toclause; port_op.s.call.body=p_callbody; port_op.s.timestampredirect = p_timestampredirect; + if (port_op.s.timestampredirect != NULL) { + port_op.s.timestampredirect->set_ref_side(Reference::LHS_REF); + } break; default: FATAL_ERROR("Statement::Statement()"); @@ -1546,6 +1555,9 @@ namespace Ttcn { port_op.s.replyval=p_replyval; port_op.s.toclause=p_toclause; port_op.s.timestampredirect = p_timestampredirect; + if (port_op.s.timestampredirect != NULL) { + port_op.s.timestampredirect->set_ref_side(Reference::LHS_REF); + } break; default: FATAL_ERROR("Statement::Statement()"); @@ -1567,6 +1579,9 @@ namespace Ttcn { port_op.s.sendpar=p_templinst; port_op.s.toclause=p_toclause; port_op.s.timestampredirect = p_timestampredirect; + if (port_op.s.timestampredirect != NULL) { + port_op.s.timestampredirect->set_ref_side(Reference::LHS_REF); + } break; default: FATAL_ERROR("Statement::Statement()"); @@ -1595,6 +1610,15 @@ namespace Ttcn { port_op.r.redirect.sender=p_redirectsender; port_op.r.redirect.index = p_redirectindex; port_op.r.redirect.timestamp = p_timestamp_redirect; + if (port_op.r.redirect.sender != NULL) { + port_op.r.redirect.sender->set_ref_side(Reference::LHS_REF); + } + if (port_op.r.redirect.index != NULL) { + port_op.r.redirect.index->set_ref_side(Reference::LHS_REF); + } + if (port_op.r.redirect.timestamp != NULL) { + port_op.r.redirect.timestamp->set_ref_side(Reference::LHS_REF); + } break; default: FATAL_ERROR("Statement::Statement()"); @@ -1622,6 +1646,15 @@ namespace Ttcn { port_op.r.redirect.sender=p_redirectsender; port_op.r.redirect.index = p_redirectindex; port_op.r.redirect.timestamp = p_timestamp_redirect; + if (port_op.r.redirect.sender != NULL) { + port_op.r.redirect.sender->set_ref_side(Reference::LHS_REF); + } + if (port_op.r.redirect.index != NULL) { + port_op.r.redirect.index->set_ref_side(Reference::LHS_REF); + } + if (port_op.r.redirect.timestamp != NULL) { + port_op.r.redirect.timestamp->set_ref_side(Reference::LHS_REF); + } break; default: FATAL_ERROR("Statement::Statement()"); @@ -1651,6 +1684,15 @@ namespace Ttcn { port_op.r.redirect.sender=p_redirectsender; port_op.r.redirect.index = p_redirectindex; port_op.r.redirect.timestamp = p_timestamp_redirect; + if (port_op.r.redirect.sender != NULL) { + port_op.r.redirect.sender->set_ref_side(Reference::LHS_REF); + } + if (port_op.r.redirect.index != NULL) { + port_op.r.redirect.index->set_ref_side(Reference::LHS_REF); + } + if (port_op.r.redirect.timestamp != NULL) { + port_op.r.redirect.timestamp->set_ref_side(Reference::LHS_REF); + } break; default: FATAL_ERROR("Statement::Statement()"); @@ -1684,6 +1726,15 @@ namespace Ttcn { port_op.r.redirect.sender=p_redirectsender; port_op.r.redirect.index = p_redirectindex; port_op.r.redirect.timestamp = p_timestamp_redirect; + if (port_op.r.redirect.sender != NULL) { + port_op.r.redirect.sender->set_ref_side(Reference::LHS_REF); + } + if (port_op.r.redirect.index != NULL) { + port_op.r.redirect.index->set_ref_side(Reference::LHS_REF); + } + if (port_op.r.redirect.timestamp != NULL) { + port_op.r.redirect.timestamp->set_ref_side(Reference::LHS_REF); + } break; default: FATAL_ERROR("Statement::Statement()"); @@ -1706,6 +1757,15 @@ namespace Ttcn { port_op.r.redirect.sender=p_redirectsender; port_op.r.redirect.index = p_redirectindex; port_op.r.redirect.timestamp = p_timestamp_redirect; + if (port_op.r.redirect.sender != NULL) { + port_op.r.redirect.sender->set_ref_side(Reference::LHS_REF); + } + if (port_op.r.redirect.index != NULL) { + port_op.r.redirect.index->set_ref_side(Reference::LHS_REF); + } + if (port_op.r.redirect.timestamp != NULL) { + port_op.r.redirect.timestamp->set_ref_side(Reference::LHS_REF); + } break; default: FATAL_ERROR("Statement::Statement()"); @@ -1722,6 +1782,9 @@ namespace Ttcn { timer_op.timerref = p_ref; timer_op.any_from = p_any_from; timer_op.index_redirect = p_redirectindex; + if (timer_op.index_redirect != NULL) { + timer_op.index_redirect->set_ref_side(Reference::LHS_REF); + } } Statement::Statement(statementtype_t p_st, Value *p_compref, @@ -1754,6 +1817,9 @@ namespace Ttcn { comp_op.donereturn.redirect = p_redirect; comp_op.index_redirect = p_index_redirect; comp_op.any_from = p_any_from; + if (comp_op.index_redirect != NULL) { + comp_op.index_redirect->set_ref_side(Reference::LHS_REF); + } break; default: FATAL_ERROR("Statement::Statement()"); @@ -1773,6 +1839,9 @@ namespace Ttcn { comp_op.any_or_all = p_anyall; comp_op.any_from = false; comp_op.index_redirect = NULL; + if (comp_op.index_redirect != NULL) { + comp_op.index_redirect->set_ref_side(Reference::LHS_REF); + } break; default: FATAL_ERROR("Statement::Statement()"); @@ -3350,7 +3419,8 @@ namespace Ttcn { Error_Context cntxt(this, "In string2ttcn() statement"); convert_op.val->chk_expr_type(Type::T_CSTR, "charstring", Type::EXPECTED_DYNAMIC_VALUE); /// - Common::Assignment* refd_ass = convert_op.ref->get_refd_assignment(); + convert_op.ref->set_ref_side(Reference::LHS_REF); + Common::Assignment* refd_ass = convert_op.ref->get_refd_assignment_last(); if (refd_ass==NULL) { error("Could not determine the assignment for second parameter"); goto error; @@ -3387,7 +3457,8 @@ namespace Ttcn { Error_Context cntxt(this, "In int2enum() statement"); convert_op.val->chk_expr_type(Type::T_INT, "integer", Type::EXPECTED_DYNAMIC_VALUE); /// - Common::Assignment* refd_ass = convert_op.ref->get_refd_assignment(); + convert_op.ref->set_ref_side(Reference::LHS_REF); + Common::Assignment* refd_ass = convert_op.ref->get_refd_assignment_last(); if (refd_ass==NULL) { error("Could not determine the assignment for second parameter"); goto error; @@ -4076,6 +4147,10 @@ error: return; } } + else if (my_sb->is_in_setter_scope()) { + error("Property setter's statement block cannot have a return statement"); + goto error; + } else { // control part error("Return statement cannot be used in the control part. " "It is allowed only in functions and altsteps"); @@ -5752,7 +5827,7 @@ error: void Statement::chk_setverdict() { Error_Context cntxt(this, "In setverdict statement"); - if(!my_sb->get_my_def()) + if(!my_sb->get_my_def() && !my_sb->is_in_getter_scope() && !my_sb->is_in_setter_scope()) error("Setverdict statement is not allowed in the control part"); setverdict.verdictval->chk_expr_verdict(Type::EXPECTED_DYNAMIC_VALUE); Value *t_val = setverdict.verdictval->get_value_refd_last(); @@ -5870,7 +5945,7 @@ error: Type *Statement::chk_port_ref(Reference *p_ref, bool p_any_from) { - if (!my_sb->get_my_def()) + if (!my_sb->get_my_def() && !my_sb->is_in_getter_scope() && !my_sb->is_in_setter_scope()) error("Port operation is not allowed in the control part"); if (!p_ref) return 0; Common::Assignment *t_ass = p_ref->get_refd_assignment(); @@ -6362,7 +6437,7 @@ error: Type *Statement::chk_comp_ref(Value *p_val, bool allow_mtc, bool allow_system, bool p_any_from) { - if (!my_sb->get_my_def()) + if (!my_sb->get_my_def() && !my_sb->is_in_getter_scope() && !my_sb->is_in_setter_scope()) error("Component operation is not allowed in the control part"); if (!p_val) return 0; Value *v = p_val->get_value_refd_last(); @@ -6636,7 +6711,7 @@ error: error("The compiler option `-k' must be activated to use object-oriented features such as raise statements."); } Definition *my_def = my_sb->get_my_def(); - if (!my_def) { + if (!my_def && !my_sb->is_in_getter_scope() && !my_sb->is_in_setter_scope()) { error("Raise statement cannot be used in the control part. " "It is allowed only in functions, altsteps and testcases."); } @@ -7246,30 +7321,12 @@ error: expression_struct ref_expr; Code::init_expr(&ref_expr); convert_op.ref->generate_code(&ref_expr); - - // check if the reference is an optional field - bool is_optional = false; - FieldOrArrayRefs* subrefs = convert_op.ref->get_subrefs(); - if (NULL != subrefs) { - Type* ref_type = convert_op.ref->get_refd_assignment()->get_Type()->get_type_refd_last(); - for (size_t i = 0; i < subrefs->get_nof_refs(); ++i) { - FieldOrArrayRef* subref = subrefs->get_ref(i); - if (FieldOrArrayRef::ARRAY_REF == subref->get_type()) { - ref_type = ref_type->get_ofType()->get_type_refd_last(); - } - else { // FIELD_REF - CompField* cf = ref_type->get_comp_byName(*subref->get_id()); - if (i == subrefs->get_nof_refs() - 1 && cf->get_is_optional()) { - is_optional = true; - } - ref_type = cf->get_type()->get_type_refd_last(); - } - } - } str = mputstr(str, val_expr.preamble); str = mputstr(str, ref_expr.preamble); + bool is_optional = convert_op.ref->get_refd_assignment()->get_Type()-> + field_is_optional(convert_op.ref->get_subrefs()); str = mputprintf(str, "%s%s.int2enum(%s);\n", ref_expr.expr, is_optional ? "()" : "", val_expr.expr); @@ -8259,9 +8316,9 @@ error: if (debugger_active) { // the debugger's return value storing macro requires a temporary, // so the returned expression isn't evaluated twice - string tmp_id = my_def->get_my_scope()->get_scope_mod_gen()->get_temporary_id(); + string tmp_id = my_sb->get_scope_mod_gen()->get_temporary_id(); expr.preamble = mputprintf(expr.preamble, "%s %s;\n", - returnexpr.v->get_expr_governor_last()->get_genname_value(my_def->get_my_scope()).c_str(), + returnexpr.v->get_expr_governor_last()->get_genname_value(my_sb).c_str(), tmp_id.c_str()); expr.expr = mputprintf(expr.expr, "DEBUGGER_STORE_RETURN_VALUE(%s, ", tmp_id.c_str()); } @@ -8271,19 +8328,17 @@ error: } } else if (returnexpr.t) { expr.expr = mputc(expr.expr, ' '); - if (!my_def) FATAL_ERROR("Statement::generate_code_return()"); if (debugger_active) { // the debugger's return value storing macro requires a temporary, // so the returned expression isn't evaluated twice - string tmp_id = my_def->get_my_scope()->get_scope_mod_gen()->get_temporary_id(); + string tmp_id = my_sb->get_scope_mod_gen()->get_temporary_id(); expr.preamble = mputprintf(expr.preamble, "%s_template %s;\n", - returnexpr.t->get_my_governor()->get_genname_value(my_def->get_my_scope()).c_str(), + returnexpr.t->get_my_governor()->get_genname_value(my_sb).c_str(), tmp_id.c_str()); expr.expr = mputprintf(expr.expr, "DEBUGGER_STORE_RETURN_VALUE(%s, ", tmp_id.c_str()); } - Def_Function_Base* dfb = dynamic_cast(my_def); - if (!dfb) FATAL_ERROR("Statement::generate_code_return()"); - if (dfb->get_template_restriction() != TR_NONE && + Def_Function_Base* dfb = my_def != NULL ? dynamic_cast(my_def) : NULL; + if (dfb != NULL && dfb->get_template_restriction() != TR_NONE && returnexpr.gen_restriction_check) { returnexpr.t->generate_code_expr(&expr, dfb->get_template_restriction()); @@ -9282,7 +9337,7 @@ error: { if (port_op.r.redirect.sender) { expr->expr = mputstr(expr->expr, "&("); - port_op.r.redirect.sender->generate_code(expr); + port_op.r.redirect.sender->generate_code_actualpar(expr); expr->expr = mputc(expr->expr, ')'); } else expr->expr = mputstr(expr->expr, "NULL"); } @@ -9294,7 +9349,7 @@ error: port_op.r.redirect.timestamp; if (tr != NULL) { expr->expr = mputstr(expr->expr, "&("); - tr->generate_code(expr); + tr->generate_code_actualpar(expr); expr->expr = mputstr(expr->expr, ")"); } else { @@ -9343,7 +9398,7 @@ error: // the expression proper (expr->expr). expression_struct ref_expr; Code::init_expr(&ref_expr); - p_ref->generate_code(&ref_expr); + p_ref->generate_code_actualpar(&ref_expr); if (ref_expr.preamble != NULL) { expr->preamble = mputstr(expr->preamble, ref_expr.preamble); } @@ -9382,7 +9437,7 @@ error: "Index_Redirect_%s %s(&(%s));\n", tmp_id_class.c_str(), tmp_id_var.c_str(),ref_expr.expr); if (ref_expr.postamble != NULL) { - expr->preamble = mputstr(expr->preamble, ref_expr.postamble); + expr->postamble = mputstr(expr->postamble, ref_expr.postamble); } Code::free_expr(&ref_expr); expr->expr = mputprintf(expr->expr, "&%s", tmp_id_var.c_str()); @@ -9840,6 +9895,7 @@ error: void Assignment::chk() { + ref->set_ref_side(Reference::LHS_REF); switch(asstype) { case ASS_UNKNOWN: chk_unknown_ass(); @@ -9962,7 +10018,6 @@ error: str = mputprintf(str, "%s& %s = %s; /* 7388 */\n", type_genname_str, tmp_id_str, expr.expr); } - str = mputstr(str, expr.postamble); // We now have a reference to the LHS. Generate the actual assignment if (rhs_copied) { str = mputprintf(str, "%s = %s;\n", tmp_id_str, rhs_copy.c_str()); @@ -9972,6 +10027,7 @@ error: str = TypeConv::gen_conv_code_refd(str, tmp_id_str, val); else str = val->generate_code_init(str, tmp_id_str); } + str = mputstr(str, expr.postamble); Code::free_expr(&expr); str = mputstr(str, "}\n"); } @@ -10159,6 +10215,7 @@ error: if (!id || !ref || (!decoded && str_enc)) { FATAL_ERROR("Ttcn::ParamAssignment::ParamAssignment()"); } + ref->set_ref_side(Reference::LHS_REF); } ParamAssignment::~ParamAssignment() @@ -10263,6 +10320,7 @@ error: : Node(), Location(), ref(p_ref), decoded(false), str_enc(NULL), dec_type(NULL) { if(!ref) FATAL_ERROR("VariableEntry::VariableEntry()"); + ref->set_ref_side(Reference::LHS_REF); } VariableEntry::VariableEntry(Reference* p_ref, bool p_decoded, @@ -10271,6 +10329,7 @@ error: , dec_type(p_dec_type) { if(!ref) FATAL_ERROR("VariableEntry::VariableEntry()"); + ref->set_ref_side(Reference::LHS_REF); } VariableEntry::~VariableEntry() @@ -10647,7 +10706,7 @@ error: if (ref) { // the variable reference is present expr->expr = mputstr(expr->expr, "&("); - ref->generate_code(expr); + ref->generate_code_actualpar(expr); expr->expr = mputc(expr->expr, ')'); if (str_enc != NULL) { expr->expr = mputstr(expr->expr, ", "); @@ -11177,6 +11236,7 @@ error: if (var_ref == NULL) { FATAL_ERROR("SingleValueRedirect::SingleValueRedirect"); } + var_ref->set_ref_side(Reference::LHS_REF); } SingleValueRedirect::SingleValueRedirect(Reference* p_var_ref, @@ -11188,6 +11248,7 @@ error: if (var_ref == NULL || subrefs == NULL || (!decoded && str_enc != NULL)) { FATAL_ERROR("SingleValueRedirect::SingleValueRedirect"); } + var_ref->set_ref_side(Reference::LHS_REF); } SingleValueRedirect::~SingleValueRedirect() @@ -11492,7 +11553,7 @@ error: Code::init_expr(&var_ref_expr); inst_params_str = mputstr(inst_params_str, "&("); Reference* ref = v[i]->get_var_ref(); - ref->generate_code(&var_ref_expr); + ref->generate_code_actualpar(&var_ref_expr); inst_params_str = mputstr(inst_params_str, var_ref_expr.expr); if (ref->get_refd_assignment()->get_Type()->get_type_refd_last()-> get_field_type(ref->get_subrefs(), @@ -12039,7 +12100,7 @@ error: else { // RT1 or verdict only // in this case only the address of the one variable needs to be generated expr->expr = mputstr(expr->expr, "&("); - v[0]->get_var_ref()->generate_code(expr); + v[0]->get_var_ref()->generate_code_actualpar(expr); expr->expr = mputc(expr->expr, ')'); } } diff --git a/compiler2/ttcn3/TtcnTemplate.cc b/compiler2/ttcn3/TtcnTemplate.cc index 3c32db5d26a770530b64b6a8d8e7823920099fac..ea3b4dd1776a988102e0dc58e69f658137bb92fe 100644 --- a/compiler2/ttcn3/TtcnTemplate.cc +++ b/compiler2/ttcn3/TtcnTemplate.cc @@ -6149,7 +6149,7 @@ compile_time: // convert the reference to a single expression expression_struct expr; Code::init_expr(&expr); - u.ref.ref->generate_code(&expr); + u.ref.ref->generate_code_const_ref(&expr); if (expr.preamble || expr.postamble) FATAL_ERROR("Template::get_single_expr()"); ret_val = expr.expr; diff --git a/compiler2/ttcn3/Ttcnstuff.cc b/compiler2/ttcn3/Ttcnstuff.cc index fc02edc729ff1fceaf51a5601fcb23f49c413403..e95fb4141ee5f337492dc5bb5286392df31961de 100644 --- a/compiler2/ttcn3/Ttcnstuff.cc +++ b/compiler2/ttcn3/Ttcnstuff.cc @@ -3150,6 +3150,8 @@ namespace Ttcn { delete constructor; } abstract_functions.clear(); + abstract_getters.clear(); + abstract_setters.clear(); for (size_t i = 0; i < defpar_list.size(); ++i) { Free(defpar_list.get_nth_elem(i)); } @@ -3390,18 +3392,67 @@ namespace Ttcn { } bool ClassTypeBody::chk_visibility(Common::Assignment* ass, - Common::Location* usage_loc, - Common::Scope* usage_scope) + Common::Ref_simple* p_ref, + Common::Location* usage_loc) { if (built_in) { FATAL_ERROR("ClassTypeBody::chk_visibility"); } - if (ass->get_visibility() == PUBLIC) { + if (p_ref->get_ref_side() == Reference::REF_SIDE_ERROR) { + return false; // already checked + } + visibility_t visibility1 = ass->get_visibility(); // visibility for getting (RHS) + visibility_t visibility2 = PUBLIC; // visibility for setting (LHS) + // if the assignment is not a property, then visibility2 remains 'public', + // which means only visibility1 is really checked + if (ass->is_property()) { + // the visibility of the getter/setter overrides the assignment's visibility; + if (p_ref->get_ref_side() & Reference::RHS_REF) { + property_function_container_t getter = + PropertyBody::get_property_function(ass, true); + if (getter.is_automatic) { + visibility1 = getter.def->get_visibility(); + } + else if (getter.func != NULL) { + visibility1 = getter.func->get_visibility(); + } + else { + usage_loc->error("Property `%s' with no getter cannot be referenced " + "on the right-hand-side of an assignment", ass->get_fullname().c_str()); + p_ref->set_ref_side(Reference::REF_SIDE_ERROR); + return false; + } + } + else { + // the reference is not on the right-hand-side, so ignore visibility1 in the checks + visibility1 = PUBLIC; + } + if (p_ref->get_ref_side() & Reference::LHS_REF) { + property_function_container_t setter = + PropertyBody::get_property_function(ass, false); + if (setter.is_automatic) { + visibility1 = setter.def->get_visibility(); + } + else if (setter.func != NULL) { + visibility1 = setter.func->get_visibility(); + } + else { + usage_loc->error("Property `%s' with no setter cannot be referenced " + "on the left-hand-side of an assignment", ass->get_fullname().c_str()); + p_ref->set_ref_side(Reference::REF_SIDE_ERROR); + return false; + } + } + // otherwise the reference is not on the left-hand-side, + // and visibility2 is already set to 'public', so it'll be ignored in the checks + } + + if (visibility1 == PUBLIC && visibility2 == PUBLIC) { // it's public, so it doesn't matter where it's accessed from return true; } - ClassTypeBody* ref_scope_class = usage_scope->get_scope_class(); + ClassTypeBody* ref_scope_class = p_ref->get_my_scope()->get_scope_class(); ClassTypeBody* ass_scope_class = ass->get_my_scope()->get_scope_class(); if (ass_scope_class == NULL) { FATAL_ERROR("ClassTypeBody::chk_visibility()"); @@ -3412,14 +3463,16 @@ namespace Ttcn { } if (ref_scope_class != NULL && - ass->get_visibility() == NOCHANGE && // i.e. protected + visibility1 != PRIVATE && visibility2 != PRIVATE && ref_scope_class->is_parent_class(ass_scope_class)) { + // if the reference is in a subclass, then both visibilities must be protected or public return true; } usage_loc->error("The %s definition `%s' in class type `%s' is not visible " "in this scope", ass->get_FormalParList() != NULL ? "method" : "member", ass->get_id().get_dispname().c_str(), class_id->get_dispname().c_str()); + p_ref->set_ref_side(Reference::REF_SIDE_ERROR); return false; } @@ -3461,7 +3514,7 @@ namespace Ttcn { FATAL_ERROR("ClassTypeBody::get_ass_bySRef()"); } - if (chk_visibility(ass, p_ref, p_ref->get_my_scope())) { + if (chk_visibility(ass, p_ref, p_ref)) { return ass; } else { @@ -3555,11 +3608,20 @@ namespace Ttcn { } break; default: - def1->error("%s shadows inherited %s `%s'", - def1->get_description().c_str(), - dynamic_cast(def2) != NULL ? "method" : "member", - def2->get_fullname().c_str()); - name_clash = true; + if (def1->is_property() && def2->is_property()) { + if (!def1->get_Type()->is_identical(def2->get_Type())) { + def1->error("The type of property `%s' is not identical " + "to that of inherited property `%s'", + id1.get_dispname().c_str(), def2->get_fullname().c_str()); + } + } + else { + def1->error("%s shadows inherited %s `%s'", + def1->get_description().c_str(), + dynamic_cast(def2) != NULL ? "method" : "member", + def2->get_fullname().c_str()); + name_clash = true; + } break; } } @@ -3862,14 +3924,14 @@ namespace Ttcn { } else if (fp_list->get_nof_fps() == 0) { inres = Def_Function_Base::RES_IDENTICAL; } - switch (def->get_asstype()) { + switch (def->get_asstype()) { case Common::Assignment::A_FUNCTION_RVAL: case Common::Assignment::A_EXT_FUNCTION_RVAL: if (def->get_visibility() == PUBLIC && inres != Def_Function_Base::RES_DIFFERS) { Def_Function_Base* def_func = dynamic_cast(def); - Def_AbsFunction* def_func_abs = dynamic_cast(def_func); + Def_AbsFunction* def_func_abs = dynamic_cast(def_func); if (def_func_abs == NULL && - def_func->get_return_type()->is_identical(get_object_method_return_type(id.get_name()))) { + def_func->get_return_type()->is_identical(get_object_method_return_type(id.get_name()))) { if (inres == Def_Function_Base::RES_NAME_DIFFERS) { def->warning("One or more parameter names differ from previous definition"); } @@ -3889,9 +3951,9 @@ namespace Ttcn { default: def->error("%s shadows a method inherited from the 'object' class", def->get_description().c_str()); - name_clash = true; - break; - } + name_clash = true; + break; + } } } @@ -3964,45 +4026,79 @@ namespace Ttcn { } is_template = true; break; - case Common::Assignment::A_VAR: + case Common::Assignment::A_VAR: { + FormalPar* automatic_property_formalpar = NULL; + Value* initial_value = member->get_Value(); if (member->is_property()) { - Def_Property* property_member = dynamic_cast(member); - if (property_member == NULL) { - FATAL_ERROR("ClassTypeBody::chk - Def_Property cast"); - } - if (!property_member->is_automatic()) { + if (member->is_abstract() || member->get_property_body() != NULL) { continue; // only automatic properties get a constructor parameter } + // if it inherits an automatic property, then the first automatic property's + // constructor is responsible for its initialization + if (PropertyBody::inherits_automatic_property(member)) { + if (initial_value == NULL) { + continue; + } + automatic_property_formalpar = fp_list->get_fp_byName(member->get_id()); + if (automatic_property_formalpar == NULL) { + continue; + } + // otherwise delay exiting the loop until later + } } - if (member->get_Value() != NULL) { + if (initial_value != NULL) { // set the variable's initial value as the constructor parameter's default value Def_Var* var_member = dynamic_cast(member); if (var_member == NULL) { FATAL_ERROR("ClassTypeBody::chk - Def_Var cast"); } def_val = new TemplateInstance(NULL, NULL, new Template(var_member->steal_Value())); + if (automatic_property_formalpar != NULL) { + // if an automatic property with an initial value inherits another + // automatic property, then use the stolen initial value as the + // default value for the parameter matching the inherited automatic property + automatic_property_formalpar->replace_defval(def_val); + continue; + } } - break; - case Common::Assignment::A_VAR_TEMPLATE: + break; } + case Common::Assignment::A_VAR_TEMPLATE: { + FormalPar* automatic_property_formalpar = NULL; + Template* initial_value = member->get_Template(); if (member->is_property()) { - Def_Property_Template* property_temp_member = dynamic_cast(member); - if (property_temp_member == NULL) { - FATAL_ERROR("ClassTypeBody::chk - Def_Property_Template cast"); - } - if (!property_temp_member->is_automatic()) { + if (member->is_abstract() || member->get_property_body() != NULL) { continue; // only automatic properties get a constructor parameter } + // if it inherits an automatic property, then the first automatic property's + // constructor is responsible for its initialization + if (PropertyBody::inherits_automatic_property(member)) { + if (initial_value == NULL) { + continue; + } + automatic_property_formalpar = fp_list->get_fp_byName(member->get_id()); + if (automatic_property_formalpar == NULL) { + continue; + } + // otherwise delay exiting the loop until later + } } is_template = true; - if (member->get_Template() != NULL) { + if (initial_value != NULL) { // set the template variable's initial value as the constructor parameter's default value Def_Var_Template* var_temp_member = dynamic_cast(member); if (var_temp_member == NULL) { FATAL_ERROR("ClassTypeBody::chk - Def_Var_Template cast"); } def_val = new TemplateInstance(NULL, NULL, var_temp_member->steal_Template()); + if (automatic_property_formalpar != NULL) { + // if an automatic property with an initial value inherits another + // automatic property, then use the stolen initial value as the + // default value for the parameter matching the inherited automatic property + automatic_property_formalpar->replace_defval(def_val); + continue; + } } - break; + break; } default: continue; break; @@ -4068,6 +4164,14 @@ namespace Ttcn { abstract_functions.add(base_class->abstract_functions.get_nth_key(i), base_class->abstract_functions.get_nth_elem(i)); } + for (size_t i = 0; i < base_class->abstract_getters.size(); ++i) { + abstract_getters.add(base_class->abstract_getters.get_nth_key(i), + base_class->abstract_getters.get_nth_elem(i)); + } + for (size_t i = 0; i < base_class->abstract_setters.size(); ++i) { + abstract_setters.add(base_class->abstract_setters.get_nth_key(i), + base_class->abstract_setters.get_nth_elem(i)); + } } if (base_traits != NULL) { for (size_t i = 0; i < base_traits->get_nof_types(); ++i) { @@ -4086,18 +4190,54 @@ namespace Ttcn { } for (size_t i = 0; i < members->get_nof_asss(); ++i) { Common::Assignment* ass = members->get_ass_byIndex(i, false); + const string& def_name = ass->get_id().get_name(); switch (ass->get_asstype()) { case Common::Assignment::A_FUNCTION: case Common::Assignment::A_FUNCTION_RVAL: case Common::Assignment::A_FUNCTION_RTEMP: { Def_AbsFunction* def_abs_func = dynamic_cast(ass); if (def_abs_func != NULL) { - const string& def_name = def_abs_func->get_id().get_name(); if (!abstract_functions.has_key(def_name)) { abstract_functions.add(def_name, def_abs_func); } } break; } + case Common::Assignment::A_VAR: + case Common::Assignment::A_VAR_TEMPLATE: + if (ass->is_property()) { + PropertyBody* property_body = ass->get_property_body(); + if (property_body != NULL) { + Getter* getter = property_body->get_getter(); + if (getter != NULL) { + bool already_in_list = abstract_getters.has_key(def_name); + if (getter->is_abstract() && !already_in_list) { + abstract_getters.add(def_name, getter); + } + else if (!getter->is_abstract() && already_in_list) { + abstract_getters.erase(def_name); + } + } + Setter* setter = property_body->get_setter(); + if (setter != NULL) { + bool already_in_list = abstract_setters.has_key(def_name); + if (setter->is_abstract() && !already_in_list) { + abstract_setters.add(def_name, setter); + } + else if (!setter->is_abstract() && already_in_list) { + abstract_setters.erase(def_name); + } + } + } + else if (!ass->is_abstract()) { // automatic property + if (abstract_getters.has_key(def_name)) { + abstract_getters.erase(def_name); + } + if (abstract_setters.has_key(def_name)) { + abstract_setters.erase(def_name); + } + } + } + break; default: break; } @@ -4105,7 +4245,8 @@ namespace Ttcn { } if (!abstract && !trait) { - // all abstract methods from the base class and base traits have to be implemented in this class + // all abstract methods, getters and setters from the base class and base traits + // have to be implemented in this class if (base_class != NULL && base_class->abstract) { for (size_t i = 0; i < base_class->abstract_functions.size(); ++i) { Def_AbsFunction* def_abs_func = base_class->abstract_functions.get_nth_elem(i); @@ -4127,6 +4268,48 @@ namespace Ttcn { break; } } + for (size_t i = 0; i < base_class->abstract_getters.size(); ++i) { + Getter* getter = base_class->abstract_getters.get_nth_elem(i); + Common::Assignment* ass = get_local_ass_byId(getter->get_my_def()->get_id()); + switch (ass->get_asstype()) { + case Common::Assignment::A_VAR: + case Common::Assignment::A_VAR_TEMPLATE: + if (ass->is_property()) { + PropertyBody* property_body = ass->get_property_body(); + if (property_body != NULL && + (property_body->get_getter() == NULL || + property_body->get_getter()->is_abstract())) { + error("Missing implementation of abstract getter from property `%s'", + getter->get_my_def()->get_fullname().c_str()); + } + } + // other cases have been handled by the property's semantic check + break; + default: + break; + } + } + for (size_t i = 0; i < base_class->abstract_setters.size(); ++i) { + Setter* setter = base_class->abstract_setters.get_nth_elem(i); + Common::Assignment* ass = get_local_ass_byId(setter->get_my_def()->get_id()); + switch (ass->get_asstype()) { + case Common::Assignment::A_VAR: + case Common::Assignment::A_VAR_TEMPLATE: + if (ass->is_property()) { + PropertyBody* property_body = ass->get_property_body(); + if (property_body != NULL && + (property_body->get_setter() == NULL || + property_body->get_setter()->is_abstract())) { + error("Missing implementation of abstract setter from property `%s'", + setter->get_my_def()->get_fullname().c_str()); + } + } + // other cases have been handled by the property's semantic check + break; + default: + break; + } + } } if (base_traits != NULL) { for (size_t k = 0; k < base_traits->get_nof_types(); ++k) { @@ -4696,17 +4879,69 @@ namespace Ttcn { return parent_scope->get_ass_bySRef(p_ref); } - void Property_Function::chk(Definition* p_def) + void Property_Function::chk(Definition* p_def, bool p_abstract, bool p_final, bool p_deterministic) { if (p_def == NULL) { FATAL_ERROR("Property_Function::chk"); } my_def = p_def; parent_scope = my_def->get_my_scope(); + + // inherit visibility and modifiers from the property, if these are not set locally if (visibility == NOCHANGE) { visibility = my_def->get_visibility(); } - // todo: visibility, abstract & final checks + if (!abstract) { + abstract = p_abstract; + } + if (!final) { + final = p_final; + } + if (!deterministic) { + deterministic = p_deterministic; + } + + const char* function_name = is_getter() ? "getter" : "setter"; + ClassTypeBody* parent_class = parent_scope->get_scope_class(); + if (parent_class == NULL) { + FATAL_ERROR("Property_Function::chk"); + } + if (abstract && !parent_class->is_abstract() && !parent_class->is_trait()) { + error("Concrete class type `%s' cannot have abstract %s", + parent_class->get_my_def()->get_Type()->get_typename().c_str(), function_name); + } + property_function_container_t pf = + PropertyBody::get_inherited_property_function(my_def, is_getter()); + if (!pf.is_automatic && pf.func != NULL && pf.func->final) { + error("Cannot inherit %s with the @final modifier", function_name); + pf.func->note("Inherited %s is here", function_name); + } + else if (pf.is_automatic && pf.def->is_final()) { + error("Cannot inherit %s with the @final modifier", function_name); + pf.def->note("Inherited automatic property is here"); + } + chk_visibility(visibility, this, function_name, pf); + } + + void Property_Function::chk_visibility(visibility_t new_vis, Location* new_loc, const char* pf_name, + property_function_container_t inherited_pf) + { + if (!inherited_pf.is_automatic && inherited_pf.func == NULL) { + return; // no inherited getter/setter + } + visibility_t inherited_vis = inherited_pf.is_automatic ? + inherited_pf.def->get_visibility() : inherited_pf.func->get_visibility(); + const string& inherited_name = inherited_pf.is_automatic ? + inherited_pf.def->get_fullname() : inherited_pf.func->get_my_def()->get_fullname(); + if (inherited_vis == PUBLIC && new_vis != PUBLIC) { + new_loc->error("Public %s of property `%s' can only be inherited by a public %s", + pf_name, inherited_name.c_str(), pf_name); + } + else if (inherited_vis == NOCHANGE && + (new_vis != PUBLIC && new_vis != NOCHANGE)) { + new_loc->error("Protected %s of property `%s' can only be inherited by a public or protected %s", + pf_name, inherited_name.c_str(), pf_name); + } } // ================================= @@ -4726,62 +4961,55 @@ namespace Ttcn { : Property_Function(false, p_final, p_deterministic), short_form(false), is_template(false), block(p_block) { - if (p_block == NULL) { - FATAL_ERROR("Getter::Getter"); - } } - Getter::Getter(bool p_deterministic) - : Property_Function(true, false, p_deterministic), short_form(false), + Getter::Getter(bool p_abstract, bool p_deterministic) + : Property_Function(p_abstract, false, p_deterministic), short_form(false), is_template(false), block(NULL) { } Getter::~Getter() { - if (!abstract) { - if (short_form) { - if (is_template) { - delete temp; - } - else { - delete val; - } + if (short_form) { + if (is_template) { + delete temp; } else { - delete block; + delete val; } } + else { + delete block; + } } void Getter::set_fullname(const string& p_fullname) { Property_Function::set_fullname(p_fullname); - if (!abstract) { - if (short_form) { - if (is_template) { - temp->set_fullname(p_fullname + ".