diff --git a/compiler2/ttcn3/AST_ttcn3.cc b/compiler2/ttcn3/AST_ttcn3.cc index 29b8dac0c857454a92248f840feb42994dbea368..723cba45e9394879bef0de188ffec925501122c7 100644 --- a/compiler2/ttcn3/AST_ttcn3.cc +++ b/compiler2/ttcn3/AST_ttcn3.cc @@ -7032,6 +7032,8 @@ namespace Ttcn { " in clause 16.1.4 of the TTCN-3 core language standard (ES 201 873-1)", id->get_dispname().c_str()); } + ClassTypeBody* my_class = my_scope->get_scope_class(); + // `runs on' clause and `port' clause are mutually exclusive if (runs_on_ref && port_ref) { runs_on_ref->error("A `runs on' and a `port' clause cannot be present at the same time."); @@ -7039,6 +7041,10 @@ namespace Ttcn { if (my_scope->is_class_scope()) { // class methods inherit `runs on', `mtc' and `system' clauses from the class ClassTypeBody* class_ = my_scope->get_scope_class(); + if (class_->is_trait()) { + error("Trait class type `%s' cannot have non-abstract methods", + class_->get_my_def()->get_Type()->get_typename().c_str()); + } runs_on_type = class_->get_RunsOnType(); mtc_type = class_->get_MtcType(); system_type = class_->get_SystemType(); @@ -7783,9 +7789,14 @@ namespace Ttcn { checked = true; Error_Context cntxt(this, "In external function definition `%s'", id->get_dispname().c_str()); - if (!ext_keyword && !my_scope->get_scope_class()->is_external()) { + ClassTypeBody* my_class = my_scope->get_scope_class(); + if (!ext_keyword && !my_class->is_external()) { error("Missing function body or `external' keyword"); } + if (my_class != NULL && my_class->is_trait()) { + error("Trait class type `%s' cannot have non-abstract methods", + my_class->get_my_def()->get_Type()->get_typename().c_str()); + } fp_list->chk(asstype); if (return_type) { Error_Context cntxt2(return_type, "In return type"); @@ -8401,7 +8412,7 @@ namespace Ttcn { if (my_class == NULL) { FATAL_ERROR("Def_AbsFunction::chk"); } - if (!my_class->is_abstract()) { + if (!my_class->is_abstract() && !my_class->is_trait()) { error("Concrete class type `%s' cannot have abstract methods", my_class->get_my_def()->get_Type()->get_typename().c_str()); } diff --git a/compiler2/ttcn3/Ttcnstuff.cc b/compiler2/ttcn3/Ttcnstuff.cc index 92d2d6d0aece807ec9badb01c80edccd423b1818..acf2ed00277388d5021587486569772e002a31d8 100644 --- a/compiler2/ttcn3/Ttcnstuff.cc +++ b/compiler2/ttcn3/Ttcnstuff.cc @@ -3067,11 +3067,11 @@ namespace Ttcn { // ================================= ClassTypeBody::ClassTypeBody(Common::Identifier* p_class_id, boolean p_external, boolean p_final, - boolean p_abstract, Common::Type* p_base_type, + boolean p_abstract, boolean p_trait, Types* p_base_types, Reference* p_runs_on_ref, Reference* p_mtc_ref, Reference* p_system_ref, Definitions* p_members, StatementBlock* p_finally_block) : Scope(), Location(), class_id(p_class_id), my_def(NULL), external(p_external), final(p_final), - abstract(p_abstract), built_in(FALSE), base_type(p_base_type), base_class(NULL), + abstract(p_abstract), trait(p_trait), built_in(FALSE), base_type(NULL), base_traits(p_base_types), base_class(NULL), runs_on_ref(p_runs_on_ref), runs_on_type(NULL), mtc_ref(p_mtc_ref), mtc_type(NULL), system_ref(p_system_ref), system_type(NULL), members(p_members), finally_block(p_finally_block), constructor(NULL), checked(false), @@ -3088,8 +3088,8 @@ namespace Ttcn { ClassTypeBody::ClassTypeBody() : Scope(), Location(), class_id(NULL), my_def(NULL), external(FALSE), final(FALSE), - abstract(TRUE), built_in(TRUE), base_type(NULL), base_class(NULL), - runs_on_ref(NULL), runs_on_type(NULL), mtc_ref(NULL), + abstract(TRUE), trait(FALSE), built_in(TRUE), base_type(NULL), base_class(NULL), + base_traits(NULL), runs_on_ref(NULL), runs_on_type(NULL), mtc_ref(NULL), mtc_type(NULL), system_ref(NULL), system_type(NULL), members(NULL), finally_block(NULL), constructor(NULL), checked(false), default_constructor(false) @@ -3105,11 +3105,16 @@ namespace Ttcn { external = p.external; final = p.final; abstract = p.abstract; + trait = p.trait; base_type = p.base_type != NULL ? p.base_type->clone() : NULL; base_class = p.base_class; + base_traits = p.base_traits != NULL ? p.base_traits->clone() : NULL; runs_on_ref = p.runs_on_ref != NULL ? p.runs_on_ref->clone() : NULL; + runs_on_type = p.runs_on_type; mtc_ref = p.mtc_ref != NULL ? p.mtc_ref->clone() : NULL; + mtc_type = p.mtc_type; system_ref = p.system_ref != NULL ? p.system_ref->clone() : NULL; + system_type = p.system_type; members = p.members != NULL ? p.members->clone() : NULL; finally_block = p.finally_block != NULL ? p.finally_block->clone() : NULL; default_constructor = p.default_constructor; @@ -3128,6 +3133,7 @@ namespace Ttcn { return; } delete base_type; + delete base_traits; delete finally_block; delete members; delete mtc_ref; @@ -3156,6 +3162,9 @@ namespace Ttcn { if (base_type != NULL) { base_type->set_fullname(p_fullname + ".<superclass>"); } + if (base_traits != NULL) { + base_traits->set_fullname(p_fullname + ".<supertraits>"); + } if (runs_on_ref != NULL) { runs_on_ref->set_fullname(p_fullname + ".<runs_on_type>"); } @@ -3180,6 +3189,9 @@ namespace Ttcn { if (base_type != NULL) { base_type->set_my_scope(p_scope); } + if (base_traits != NULL) { + base_traits->set_my_scope(p_scope); + } if (runs_on_ref != NULL) { runs_on_ref->set_my_scope(p_scope); } @@ -3203,8 +3215,18 @@ namespace Ttcn { } DEBUG(level, "Modifiers:%s%s%s", external ? "external " : "", final ? " @final " : "", abstract ? " @abstract" : ""); - DEBUG(level, "Base class: %s", base_type != NULL ? - base_type->get_typename().c_str() : ""); + DEBUG(level, "Base classes:"); + if (base_type != NULL) { + DEBUG(level + 1, base_type->get_typename().c_str()); + } + if (base_traits != NULL) { + for (size_t i = 0; i < base_traits->get_nof_types(); ++i) { + Type* base_trait = base_traits->get_type_byIndex(i); + if (base_trait != NULL) { + DEBUG(level + 1, base_trait->get_typename().c_str()); + } + } + } if (runs_on_ref != NULL) { DEBUG(level, "Runs on clause:"); runs_on_ref->dump(level + 1); @@ -3268,13 +3290,22 @@ namespace Ttcn { if (!checked) { chk(); } - if (this == p_class || p_class->built_in) { + if (this == p_class || (!trait && p_class->built_in)) { return true; } - if (base_class == NULL) { - return false; + if (base_class != NULL && base_class->is_parent_class(p_class)) { + return true; + } + if (base_traits != NULL) { + for (size_t i = 0; i < base_traits->get_nof_types(); ++i) { + Type* base_trait = base_traits->get_type_byIndex(i); + if (base_trait != NULL && + base_trait->get_type_refd_last()->get_class_type_body()->is_parent_class(p_class)) { + return true; + } + } } - return base_class->is_parent_class(p_class); + return false; } bool ClassTypeBody::has_local_ass_withId(const Identifier& p_id) @@ -3288,12 +3319,19 @@ namespace Ttcn { if (members->has_local_ass_withId(p_id)) { return true; } - if (base_class != NULL) { - return base_class->has_local_ass_withId(p_id); + if (base_class != NULL && base_class->has_local_ass_withId(p_id)) { + return true; } - else { - return false; + if (base_traits) { + for (size_t i = 0; i < base_traits->get_nof_types(); ++i) { + Type* base_trait = base_traits->get_type_byIndex(i); + if (base_trait != NULL && + base_trait->get_type_refd_last()->get_class_type_body()->has_local_ass_withId(p_id)) { + return true; + } + } } + return false; } Common::Assignment* ClassTypeBody::get_local_ass_byId(const Identifier& p_id) @@ -3311,6 +3349,17 @@ namespace Ttcn { if (ass == NULL && base_class != NULL) { ass = base_class->get_local_ass_byId(p_id); } + if (ass == NULL && base_traits != NULL) { + for (size_t i = 0; i < base_traits->get_nof_types(); ++i) { + Type* base_trait = base_traits->get_type_byIndex(i); + if (base_trait != NULL) { + ass = base_trait->get_type_refd_last()->get_class_type_body()->get_local_ass_byId(p_id); + if (ass != NULL) { + break; + } + } + } + } return ass; } @@ -3347,7 +3396,7 @@ namespace Ttcn { ass->get_id().get_dispname().c_str(), class_id->get_dispname().c_str()); return false; } - + Common::Assignment* ClassTypeBody::get_ass_bySRef(Common::Ref_simple* p_ref) { if (built_in) { @@ -3379,13 +3428,13 @@ namespace Ttcn { // nothing special is needed for 'this.field' or 'this.method' // (it's already been handled at the lower scopes) } - + if (id != NULL && has_local_ass_withId(*id)) { Common::Assignment* ass = get_local_ass_byId(*id); if (ass == NULL) { FATAL_ERROR("ClassTypeBody::get_ass_bySRef()"); } - + if (chk_visibility(ass, p_ref, p_ref->get_my_scope())) { return ass; } @@ -3397,39 +3446,178 @@ namespace Ttcn { return parent_scope->get_ass_bySRef(p_ref); } + bool ClassTypeBody::compare_members(ClassTypeBody* c1, ClassTypeBody* c2, Location* subclass_loc) + { + // if subclass_loc is NULL, then c1 is the subclass and c2 is the base class or a base trait + // otherwise both c1 and c2 are inherited by the subclass + if (subclass_loc != NULL && (c1->is_parent_class(c2) || c2->is_parent_class(c1))) { + return false; + } + bool name_clash = false; + for (size_t i = 0; i < c1->members->get_nof_asss(); ++i) { + Common::Assignment* def1 = c1->members->get_ass_byIndex(i, false); + if (def1->get_asstype() == Common::Assignment::A_CONSTRUCTOR) { + continue; + } + const Common::Identifier& id1 = def1->get_id(); + if (c2->has_local_ass_withId(id1)) { + Common::Assignment* def2 = c2->get_local_ass_byId(id1); + ClassTypeBody* def2_class = def2->get_my_scope()->get_scope_class(); + if (subclass_loc != NULL && + def2_class != c2 && def2_class != c1 && c1->is_parent_class(def2_class)) { + // def2 is defined in a trait class that is inherited by both c1 and c2, + // ignore this comparison + continue; + } + switch (def1->get_asstype()) { + case Common::Assignment::A_FUNCTION: + case Common::Assignment::A_FUNCTION_RVAL: + case Common::Assignment::A_FUNCTION_RTEMP: + case Common::Assignment::A_EXT_FUNCTION: + case Common::Assignment::A_EXT_FUNCTION_RVAL: + case Common::Assignment::A_EXT_FUNCTION_RTEMP: + switch (def2->get_asstype()) { + case Common::Assignment::A_FUNCTION: + case Common::Assignment::A_FUNCTION_RVAL: + case Common::Assignment::A_FUNCTION_RTEMP: + case Common::Assignment::A_EXT_FUNCTION: + case Common::Assignment::A_EXT_FUNCTION_RVAL: + case Common::Assignment::A_EXT_FUNCTION_RTEMP: { + Def_Function_Base* func1 = dynamic_cast<Def_Function_Base*>(def1); + Def_Function_Base* func2 = dynamic_cast<Def_Function_Base*>(def2); + bool func1_is_abstract = dynamic_cast<Def_AbsFunction*>(func1) != NULL; + bool func2_is_abstract = dynamic_cast<Def_AbsFunction*>(func2) != NULL; + bool functions_are_identical = func1->is_identical(func2); + if (func2->get_visibility() != PRIVATE && + (!functions_are_identical || func1_is_abstract != func2_is_abstract)) { + if (subclass_loc == NULL && !functions_are_identical) { + def1->error("The prototype of method `%s' is not identical " + "to that of inherited method `%s'", + id1.get_dispname().c_str(), def2->get_fullname().c_str()); + } + else if (subclass_loc != NULL && func1->get_visibility() != PRIVATE) { + subclass_loc->error("The prototypes of methods `%s' inherited from " + "classes `%s' and `%s' are not identical", + id1.get_dispname().c_str(), c1->get_id()->get_dispname().c_str(), + c2->get_id()->get_dispname().c_str()); + } + } + else if (subclass_loc == NULL && func2->is_final()) { + def1->error("Cannot override final method `%s'", + def2->get_fullname().c_str()); + } + else if (subclass_loc == NULL && func1->is_identical(func2)) { + if (func2->get_visibility() == PUBLIC && func1->get_visibility() != PUBLIC) { + def1->error("Public methods can be only overridden by public methods `%s'", + id1.get_dispname().c_str()); + } + else if (func2->get_visibility() == NOCHANGE && + func1->get_visibility() != PUBLIC && func1->get_visibility() != NOCHANGE) { + def1->error("Protected methods can be only overridden by " + "public or protected methods `%s'", id1.get_dispname().c_str()); + } + } + break; } + default: + def1->error("%s shadows inherited member `%s'", + def1->get_description().c_str(), def2->get_fullname().c_str()); + name_clash = true; + break; + } + break; + default: + def1->error("%s shadows inherited %s `%s'", + def1->get_description().c_str(), + dynamic_cast<Def_Function_Base*>(def2) != NULL ? "method" : "member", + def2->get_fullname().c_str()); + name_clash = true; + break; + } + } + } + if (subclass_loc != NULL) { + // the code above only goes through the local members of c1, + // comparing them to local or inherited members of c2; + // the members in the superclass and supertraits of c1 must also be compared to c2 + if (c1->base_class != NULL) { + name_clash |= compare_members(c1->base_class, c2, subclass_loc); + } + if (c1->base_traits != NULL) { + for (size_t i = 0; i < c1->base_traits->get_nof_types(); ++i) { + Type* base_trait = c1->base_traits->get_type_byIndex(i); + if (base_trait != NULL) { + ClassTypeBody* base_trait_class = base_trait->get_type_refd_last()->get_class_type_body(); + name_clash |= compare_members(base_trait_class, c2, subclass_loc); + } + } + } + } + return name_clash; + } + void ClassTypeBody::chk() { if (checked || built_in) { return; } checked = true; - if (final && abstract) { - error("Final classes cannot be abstract"); + if ((final && abstract) || (final && trait) || (abstract && trait)) { + error("A classes cannot have more than one of the @final, @abstract and @trait modifiers"); } - if (external && abstract) { + if (external && abstract) { // todo error("External classes cannot be abstract"); } - if (base_type != NULL) { - Error_Context cntxt(base_type, "In superclass definition"); - base_type->chk(); - if (base_type->get_type_refd_last()->get_typetype() != Common::Type::T_CLASS) { - if (base_type->get_typetype() != Common::Type::T_ERROR) { - base_type->error("Class type expected instead of `%s'", - base_type->get_typename().c_str()); - } - delete base_type; - base_type = NULL; - } - else { - base_class = base_type->get_type_refd_last()->get_class_type_body(); - if (base_class->final) { - base_type->error("The superclass cannot be final"); + if (base_traits != NULL) { + for (size_t i = 0; i < base_traits->get_nof_types(); ++i) { + Type* t = base_traits->get_type_byIndex(i); + Error_Context cntxt(t, "In superclass or supertrait definition"); + t->chk(); + if (t->get_type_refd_last()->get_typetype() != Common::Type::T_CLASS) { + if (t->get_typetype() != Common::Type::T_ERROR) { + t->error("Class type expected instead of `%s'", t->get_typename().c_str()); + } + base_traits->extract_type_byIndex(i); + delete t; } - if (external && !base_class->external) { - base_type->error("An external class cannot extend an internal class"); + else { + ClassTypeBody* t_class = t->get_type_refd_last()->get_class_type_body(); + if (!t_class->trait) { + if (trait) { + t->error("A trait class cannot extend a non-trait class"); + } + else if (base_type != NULL) { + t->error("A class cannot extend more than one non-trait class"); + base_type->note("Previous extended non-trait class is here"); + } + else { + base_traits->extract_type_byIndex(i); + base_type = t; + base_class = t_class; + if (base_class->final) { + base_type->error("The superclass cannot be final"); + } + if (external && !base_class->external) { + base_type->error("An external class cannot extend an internal class"); + } + } + } + for (size_t j = 0; j < i; ++j) { + Type* t2 = base_traits->get_type_byIndex(j); + if (t2 != NULL && t2->get_typename() == t->get_typename()) { + t->error("Duplicate class type in list of classes being extended"); + t2->note("Class type `%s' is already given here", t->get_typename().c_str()); + } + } } } } + if (base_class != NULL && base_class->built_in) { + // if the base class is 'object', then just delete it and set it to NULL, + // so it functions the same way as not specifying a base class + delete base_type; + base_type = NULL; + base_class = NULL; + } if (runs_on_ref != NULL) { Error_Context cntxt(runs_on_ref, "In `runs on' clause"); @@ -3489,6 +3677,64 @@ namespace Ttcn { system_type = base_class->get_SystemType(); } + if (base_traits != NULL) { + for (size_t i = 0; i < base_traits->get_nof_types(); ++i) { + Type* base_trait = base_traits->get_type_byIndex(i); + if (base_trait != NULL) { + ClassTypeBody* base_trait_class = base_trait->get_type_refd_last()-> + get_class_type_body(); + Type* base_runs_on_type = base_trait_class->get_RunsOnType(); + if (base_runs_on_type != NULL) { + if (runs_on_type == NULL) { + error("Supertrait `%s' has a `runs on` component type, " + "but the subclass and its superclass does not", + base_trait_class->class_id->get_dispname().c_str()); + } + else if(!base_runs_on_type->is_compatible(runs_on_type, NULL, NULL)) { + runs_on_ref->error("The `runs on' component type of the subclass " + "(`%s') is not compatible with the `runs on' component type of " + "supertrait `%s' (`%s')", + runs_on_type->get_typename().c_str(), + base_trait_class->class_id->get_dispname().c_str(), + base_runs_on_type->get_typename().c_str()); + } + } + Type* base_mtc_type = base_trait_class->get_MtcType(); + if (base_mtc_type != NULL) { + if (mtc_type == NULL) { + error("Supertrait `%s' has an `mtc` component type, " + "but the subclass and its superclass does not", + base_trait_class->class_id->get_dispname().c_str()); + } + else if(!base_mtc_type->is_compatible(mtc_type, NULL, NULL)) { + mtc_ref->error("The `mtc' component type of the subclass " + "(`%s') is not compatible with the `mtc' component type of " + "supertrait `%s' (`%s')", + mtc_type->get_typename().c_str(), + base_trait_class->class_id->get_dispname().c_str(), + base_mtc_type->get_typename().c_str()); + } + } + Type* base_system_type = base_trait_class->get_SystemType(); + if (base_system_type != NULL) { + if (system_type == NULL) { + error("Supertrait `%s' has an `system` component type, " + "but the subclass and its superclass does not", + base_trait_class->class_id->get_dispname().c_str()); + } + else if(!base_system_type->is_compatible(system_type, NULL, NULL)) { + system_ref->error("The `system' component type of the subclass " + "(`%s') is not compatible with the `system' component type of " + "supertrait `%s' (`%s')", + system_type->get_typename().c_str(), + base_trait_class->class_id->get_dispname().c_str(), + base_system_type->get_typename().c_str()); + } + } + } + } + } + for (size_t i = 0; i < members->get_nof_asss(); ++i) { Common::Assignment* ass = members->get_ass_byIndex(i, false); if (ass->get_asstype() == Common::Assignment::A_CONSTRUCTOR) { @@ -3519,68 +3765,10 @@ namespace Ttcn { } } - bool name_clash = false; - if (base_class != NULL || runs_on_type != NULL || mtc_type != NULL || system_type != NULL) { + if (runs_on_type != NULL || mtc_type != NULL || system_type != NULL) { for (size_t i = 0; i < members->get_nof_asss(); ++i) { Common::Assignment* local_def = members->get_ass_byIndex(i, false); const Common::Identifier& local_id = local_def->get_id(); - if (base_class != NULL && - local_def->get_asstype() != Common::Assignment::A_CONSTRUCTOR && - base_class->has_local_ass_withId(local_id)) { - Common::Assignment* base_def = base_class->get_local_ass_byId(local_id); - switch (local_def->get_asstype()) { - case Common::Assignment::A_FUNCTION: - case Common::Assignment::A_FUNCTION_RVAL: - case Common::Assignment::A_FUNCTION_RTEMP: - case Common::Assignment::A_EXT_FUNCTION: - case Common::Assignment::A_EXT_FUNCTION_RVAL: - case Common::Assignment::A_EXT_FUNCTION_RTEMP: - switch (base_def->get_asstype()) { - case Common::Assignment::A_FUNCTION: - case Common::Assignment::A_FUNCTION_RVAL: - case Common::Assignment::A_FUNCTION_RTEMP: - case Common::Assignment::A_EXT_FUNCTION: - case Common::Assignment::A_EXT_FUNCTION_RVAL: - case Common::Assignment::A_EXT_FUNCTION_RTEMP: { - Def_Function_Base* local_func = dynamic_cast<Def_Function_Base*>(local_def); - Def_Function_Base* base_func = dynamic_cast<Def_Function_Base*>(base_def); - if (base_func->get_visibility() != PRIVATE && !local_func->is_identical(base_func)) { - local_def->error("The prototype of method `%s' is not identical " - "to that of inherited method `%s'", - local_id.get_dispname().c_str(), base_def->get_fullname().c_str()); - } - else if (base_func->is_final()) { - local_def->error("Cannot override final method `%s'", - base_def->get_fullname().c_str()); - } - else if (local_func->is_identical(base_func)) { - if (base_func->get_visibility() == PUBLIC && local_func->get_visibility() != PUBLIC) { - local_def->error("Public methods can be only overridden by public methods `%s'", - local_id.get_dispname().c_str()); - } - else if (base_func->get_visibility() == NOCHANGE && - local_func->get_visibility() != PUBLIC && local_func->get_visibility() != NOCHANGE) { - local_def->error("Protected methods can be only overridden by " - "public or protected methods `%s'", local_id.get_dispname().c_str()); - } - } - break; } - default: - local_def->error("%s shadows inherited member `%s'", - local_def->get_description().c_str(), base_def->get_fullname().c_str()); - name_clash = true; - break; - } - break; - default: - local_def->error("%s shadows inherited %s `%s'", - local_def->get_description().c_str(), - dynamic_cast<Def_Function_Base*>(base_def) != NULL ? "method" : "member", - base_def->get_fullname().c_str()); - name_clash = true; - break; - } - } if (runs_on_type != NULL && runs_on_type->get_CompBody()->has_local_ass_withId(local_id)) { local_def->error("%s shadows a definition in runs-on component type `%s'", local_def->get_description().c_str(), runs_on_type->get_typename().c_str()); @@ -3596,7 +3784,37 @@ namespace Ttcn { } } - if (constructor == NULL && !name_clash) { + bool name_clash = false; + if (base_class != NULL) { + name_clash = compare_members(this, base_class); + } + if (base_traits != NULL) { + for (size_t i = 0; i < base_traits->get_nof_types(); ++i) { + Type* base_trait = base_traits->get_type_byIndex(i); + if (base_trait != NULL) { + ClassTypeBody* base_trait_class = base_trait->get_type_refd_last()-> + get_class_type_body(); + name_clash |= compare_members(this, base_trait_class); + if (base_class != NULL) { + name_clash |= compare_members(base_class, base_trait_class, this); + } + for (size_t j = 0; j < i; ++j) { + Type* base_trait2 = base_traits->get_type_byIndex(j); + if (base_trait2 != NULL) { + ClassTypeBody* base_trait_class2 = base_trait2->get_type_refd_last()-> + get_class_type_body(); + name_clash |= compare_members(base_trait_class, base_trait_class2, this); + } + } + } + } + } + + if (constructor != NULL && trait) { + constructor->error("Trait class type `%s' cannot have a constructor", + my_def->get_Type()->get_typename().c_str()); + } + if (constructor == NULL && !name_clash && !trait) { // create a default constructor Reference* base_call = NULL; FormalParList* fp_list = NULL; @@ -3668,6 +3886,10 @@ namespace Ttcn { if (finally_block != NULL) { Error_Context cntxt(finally_block, "In class destructor"); finally_block->chk(); + if (trait) { + finally_block->error("Trait class type `%s' cannot have a destructor", + my_def->get_Type()->get_typename().c_str()); + } } if (external) { @@ -3690,7 +3912,7 @@ namespace Ttcn { } } - if (abstract) { + if (abstract || trait) { // create a map of all abstract functions (including inherited ones) if (base_class != NULL && base_class->abstract) { for (size_t i = 0; i < base_class->abstract_functions.size(); ++i) { @@ -3698,6 +3920,21 @@ namespace Ttcn { base_class->abstract_functions.get_nth_elem(i)); } } + if (base_traits != NULL) { + for (size_t i = 0; i < base_traits->get_nof_types(); ++i) { + Type* base_trait = base_traits->get_type_byIndex(i); + if (base_trait != NULL) { + ClassTypeBody* base_trait_class = base_trait->get_type_refd_last()-> + get_class_type_body(); + for (size_t j = 0; j < base_trait_class->abstract_functions.size(); ++j) { + const string& key = base_trait_class->abstract_functions.get_nth_key(j); + if (!abstract_functions.has_key(key)) { + abstract_functions.add(key, base_trait_class->abstract_functions.get_nth_elem(j)); + } + } + } + } + } for (size_t i = 0; i < members->get_nof_asss(); ++i) { Common::Assignment* ass = members->get_ass_byIndex(i, false); switch (ass->get_asstype()) { @@ -3718,26 +3955,57 @@ namespace Ttcn { } } - if (!abstract && base_class != NULL && base_class->abstract) { - // all abstract methods from the base class have to be implemented in this class - 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); - Common::Assignment* ass = get_local_ass_byId(def_abs_func->get_id()); - switch (ass->get_asstype()) { - case Common::Assignment::A_FUNCTION: - case Common::Assignment::A_FUNCTION_RVAL: - case Common::Assignment::A_FUNCTION_RTEMP: { - if (dynamic_cast<Def_AbsFunction*>(ass) != NULL) { - error("Missing implementation of abstract method `%s'", - def_abs_func->get_fullname().c_str()); + if (!abstract && !trait) { + // all abstract methods 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); + Common::Assignment* ass = get_local_ass_byId(def_abs_func->get_id()); + switch (ass->get_asstype()) { + case Common::Assignment::A_FUNCTION: + case Common::Assignment::A_FUNCTION_RVAL: + case Common::Assignment::A_FUNCTION_RTEMP: { + if (dynamic_cast<Def_AbsFunction*>(ass) != NULL) { + error("Missing implementation of abstract method `%s'", + def_abs_func->get_fullname().c_str()); + } + // whether the new function is identical to the abstract one has + // already been checked + break; } + default: + // it's either an external function (which is OK), or + // it's shadowed by a member (error has already been reported) + break; + } + } + } + if (base_traits != NULL) { + for (size_t k = 0; k < base_traits->get_nof_types(); ++k) { + Type* base_trait = base_traits->get_type_byIndex(k); + if (base_trait != NULL) { + ClassTypeBody* base_trait_class = base_trait->get_type_refd_last()-> + get_class_type_body(); + for (size_t i = 0; i < base_trait_class->abstract_functions.size(); ++i) { + Def_AbsFunction* def_abs_func = base_trait_class->abstract_functions.get_nth_elem(i); + Common::Assignment* ass = get_local_ass_byId(def_abs_func->get_id()); + switch (ass->get_asstype()) { + case Common::Assignment::A_FUNCTION: + case Common::Assignment::A_FUNCTION_RVAL: + case Common::Assignment::A_FUNCTION_RTEMP: { + if (dynamic_cast<Def_AbsFunction*>(ass) != NULL) { + error("Missing implementation of abstract method `%s'", + def_abs_func->get_fullname().c_str()); + } + // whether the new function is identical to the abstract one has + // already been checked + break; } + default: + // it's either an external function (which is OK), or + // it's shadowed by a member (error has already been reported) + break; + } + } } - // whether the new function is identical to the abstract one has - // already been checked - break; } - default: - // it's either an external function (which is OK), or - // it's shadowed by a member (error has already been reported) - break; } } } @@ -3749,7 +4017,19 @@ namespace Ttcn { return; } if (base_type != NULL) { + refch.mark_state(); base_type->chk_recursions(refch); + refch.prev_state(); + } + if (base_traits != NULL) { + for (size_t i = 0; i < base_traits->get_nof_types(); ++i) { + Type* base_trait = base_traits->get_type_byIndex(i); + if (base_trait != NULL) { + refch.mark_state(); + base_trait->chk_recursions(refch); + refch.prev_state(); + } + } } for (size_t i = 0; i < members->get_nof_asss(); ++i) { @@ -3763,7 +4043,9 @@ namespace Ttcn { case Common::Assignment::A_CONST: case Common::Assignment::A_TEMPLATE: case Common::Assignment::A_VAR_TEMPLATE: + refch.mark_state(); def->get_Type()->chk_recursions(refch); + refch.prev_state(); break; default: break; @@ -3780,7 +4062,8 @@ namespace Ttcn { "class %s;\n", class_id->get_name().c_str()); if (!external || generate_skeleton) { string base_type_name = base_type != NULL ? - base_type->get_type_refd_last()->get_genname_own(this) : string("OBJECT"); + base_type->get_type_refd_last()->get_genname_own(this) : + trait ? string("CLASS_BASE") : string("OBJECT"); output_struct* local_struct; if (external) { local_struct = new output_struct; @@ -3791,8 +4074,27 @@ namespace Ttcn { } local_struct->header.class_defs = mputprintf(local_struct->header.class_defs, - "class %s : public %s {\n", - class_id->get_name().c_str(), base_type_name.c_str()); + "class %s : ", + class_id->get_name().c_str()); + bool has_base = false; + if (base_traits != NULL) { + for (size_t i = 0; i < base_traits->get_nof_types(); ++i) { + Type* base_trait = base_traits->get_type_byIndex(i); + if (base_trait != NULL) { + local_struct->header.class_defs = mputprintf(local_struct->header.class_defs, + "%spublic %s", has_base ? ", " : "", + base_trait->get_type_refd_last()->get_genname_own(this).c_str()); + has_base = true; + } + } + } + if (!trait || !has_base) { + local_struct->header.class_defs = mputprintf(local_struct->header.class_defs, + "%s public %s", has_base ? ", " : "", base_type_name.c_str()); + has_base |= base_type != NULL; + } + local_struct->header.class_defs = mputstr(local_struct->header.class_defs, + " {\n"); // class name local_struct->header.class_defs = mputprintf(local_struct->header.class_defs, @@ -3809,58 +4111,60 @@ namespace Ttcn { } // constructor - char* formal_par_list_str = NULL; - expression_struct_t base_call_expr; - Code::init_expr(&base_call_expr); - Reference* base_call = constructor->get_base_call(); - if (base_call != NULL) { - base_call->generate_code(&base_call_expr); - } - // generate code for the base call first, so the formal parameter list - // knows which parameters are used and which aren't - formal_par_list_str = constructor->get_FormalParList()->generate_code( - memptystr(), external ? constructor->get_FormalParList()->get_nof_fps() : 0); - if (base_call_expr.expr == NULL) { - base_call_expr.expr = mprintf("%s()", base_type_name.c_str()); - } - local_struct->header.class_defs = mputprintf(local_struct->header.class_defs, - "\npublic:\n%s" - "%s(%s);\n\n", - default_constructor ? "/* default constructor */\n" : "", - class_id->get_name().c_str(), - formal_par_list_str != NULL ? formal_par_list_str : ""); - local_struct->source.methods = mputprintf(local_struct->source.methods, - "%s::%s(%s)\n" - ": %s", - class_id->get_name().c_str(), class_id->get_name().c_str(), - formal_par_list_str != NULL ? formal_par_list_str : "", - base_call_expr.expr); - Free(formal_par_list_str); - Code::free_expr(&base_call_expr); - if (local_struct->temp.constructor_init != NULL) { - local_struct->source.methods = mputstr(local_struct->source.methods, - local_struct->temp.constructor_init); - Free(local_struct->temp.constructor_init); - local_struct->temp.constructor_init = NULL; - } - local_struct->source.methods = mputstr(local_struct->source.methods, "\n{\n"); - if (local_struct->temp.constructor_preamble != NULL || - local_struct->temp.constructor_block != NULL) { - local_struct->source.methods = create_location_object( - local_struct->source.methods, "FUNCTION", class_id->get_name().c_str()); - local_struct->source.methods = mputstr(local_struct->source.methods, - local_struct->temp.constructor_preamble); - local_struct->source.methods = mputstr(local_struct->source.methods, - local_struct->temp.constructor_block); - Free(local_struct->temp.constructor_preamble); - Free(local_struct->temp.constructor_block); - local_struct->temp.constructor_preamble = NULL; - local_struct->temp.constructor_block = NULL; - } - else if (external) { - local_struct->source.methods = mputc(local_struct->source.methods, '\n'); + if (constructor != NULL) { + char* formal_par_list_str = NULL; + expression_struct_t base_call_expr; + Code::init_expr(&base_call_expr); + Reference* base_call = constructor->get_base_call(); + if (base_call != NULL) { + base_call->generate_code(&base_call_expr); + } + // generate code for the base call first, so the formal parameter list + // knows which parameters are used and which aren't + formal_par_list_str = constructor->get_FormalParList()->generate_code( + memptystr(), external ? constructor->get_FormalParList()->get_nof_fps() : 0); + if (base_call_expr.expr == NULL) { + base_call_expr.expr = mprintf("%s()", base_type_name.c_str()); + } + local_struct->header.class_defs = mputprintf(local_struct->header.class_defs, + "\npublic:\n%s" + "%s(%s);\n\n", + default_constructor ? "/* default constructor */\n" : "", + class_id->get_name().c_str(), + formal_par_list_str != NULL ? formal_par_list_str : ""); + local_struct->source.methods = mputprintf(local_struct->source.methods, + "%s::%s(%s)\n" + ": %s", + class_id->get_name().c_str(), class_id->get_name().c_str(), + formal_par_list_str != NULL ? formal_par_list_str : "", + base_call_expr.expr); + Free(formal_par_list_str); + Code::free_expr(&base_call_expr); + if (local_struct->temp.constructor_init != NULL) { + local_struct->source.methods = mputstr(local_struct->source.methods, + local_struct->temp.constructor_init); + Free(local_struct->temp.constructor_init); + local_struct->temp.constructor_init = NULL; + } + local_struct->source.methods = mputstr(local_struct->source.methods, "\n{\n"); + if (local_struct->temp.constructor_preamble != NULL || + local_struct->temp.constructor_block != NULL) { + local_struct->source.methods = create_location_object( + local_struct->source.methods, "FUNCTION", class_id->get_name().c_str()); + local_struct->source.methods = mputstr(local_struct->source.methods, + local_struct->temp.constructor_preamble); + local_struct->source.methods = mputstr(local_struct->source.methods, + local_struct->temp.constructor_block); + Free(local_struct->temp.constructor_preamble); + Free(local_struct->temp.constructor_block); + local_struct->temp.constructor_preamble = NULL; + local_struct->temp.constructor_block = NULL; + } + else if (external) { + local_struct->source.methods = mputc(local_struct->source.methods, '\n'); + } + local_struct->source.methods = mputstr(local_struct->source.methods, "}\n\n"); } - local_struct->source.methods = mputstr(local_struct->source.methods, "}\n\n"); // destructor bool desctructor_body = finally_block != NULL || external; @@ -3906,12 +4210,27 @@ namespace Ttcn { "void %s::log() const\n" "{\n" "TTCN_Logger::log_event_str(\"%s%s\");\n", - class_id->get_name().c_str(), class_id->get_dispname().c_str(), base_type != NULL ? " ( " : ""); + class_id->get_name().c_str(), class_id->get_dispname().c_str(), has_base ? " ( " : ""); if (base_type != NULL) { local_struct->source.methods = mputprintf(local_struct->source.methods, - "%s::log();\n" - "TTCN_Logger::log_event_str(\" )\");\n", - base_type_name.c_str()); + "%s::log();\n", base_type_name.c_str()); + } + if (base_traits != NULL) { + for (size_t i = 0; i < base_traits->get_nof_types(); ++i) { + Type* base_trait = base_traits->get_type_byIndex(i); + if (base_trait != NULL) { + if (i > 0 || base_type != NULL) { + local_struct->source.methods = mputstr(local_struct->source.methods, + "TTCN_Logger::log_event_str(\", \");\n"); + } + local_struct->source.methods = mputprintf(local_struct->source.methods, + "%s::log();\n", base_trait->get_type_refd_last()->get_genname_own(this).c_str()); + } + } + } + if (has_base) { + local_struct->source.methods = mputstr(local_struct->source.methods, + "TTCN_Logger::log_event_str(\" )\");\n"); } local_struct->source.methods = mputstr(local_struct->source.methods, "}\n\n"); diff --git a/compiler2/ttcn3/Ttcnstuff.hh b/compiler2/ttcn3/Ttcnstuff.hh index 78a4bdeba6ed28544f42568c8cb35abd2078acd9..0e44d94ca329f20536f8e483f54382aa98ccc7c2 100644 --- a/compiler2/ttcn3/Ttcnstuff.hh +++ b/compiler2/ttcn3/Ttcnstuff.hh @@ -285,7 +285,7 @@ public: }; /** - * Type list, used in port types + * Type list, used in port and class types */ class Types : public Common::Node, public Common::Location { private: @@ -763,9 +763,11 @@ class ClassTypeBody : public Common::Scope, public Common::Location { boolean external; boolean final; boolean abstract; + boolean trait; boolean built_in; Common::Type* base_type; ClassTypeBody* base_class; // not owned + Types* base_traits; Reference* runs_on_ref; Type* runs_on_type; Reference* mtc_ref; @@ -783,7 +785,7 @@ class ClassTypeBody : public Common::Scope, public Common::Location { public: ClassTypeBody(Common::Identifier* p_class_id, boolean p_external, boolean p_final, - boolean p_abstract, Common::Type* p_base_type, + boolean p_abstract, boolean p_trait, Types* p_base_types, Ttcn::Reference* p_runs_on_ref, Reference* p_mtc_ref, Reference* p_system_ref, Definitions* p_members, StatementBlock* p_finally_block); ClassTypeBody(); @@ -794,6 +796,7 @@ public: void set_my_def(Def_Type* p_def); Def_Type* get_my_def() { return my_def; } boolean is_abstract() const { return abstract; } + boolean is_trait() const { return trait; } boolean is_external() const { return external; } void set_fullname(const string& p_fullname); @@ -817,6 +820,7 @@ public: Common::Assignment* get_local_ass_byId(const Identifier& p_id); Common::Assignment* get_ass_bySRef(Common::Ref_simple* p_ref); + static bool compare_members(ClassTypeBody* c1, ClassTypeBody* c2, Location* subclass_loc = NULL); bool chk_visibility(Common::Assignment* ass, Common::Location* usage_loc, Common::Scope* usage_scope); void chk(); diff --git a/compiler2/ttcn3/compiler.l b/compiler2/ttcn3/compiler.l index f66d30924bbb6258a5cd8ad928b701d2f5a83cbf..1f2a5a75a8ef87842382dbbd4716bea523d3d94e 100644 --- a/compiler2/ttcn3/compiler.l +++ b/compiler2/ttcn3/compiler.l @@ -640,6 +640,7 @@ object { "@abstract" RETURN(AbstractKeyword); "@default" RETURN(DefaultModifier); "@dynamic" RETURN(DynamicModifier); +"@trait" RETURN(TraitKeyword); /* special TITAN specific keywords */ diff --git a/compiler2/ttcn3/compiler.y b/compiler2/ttcn3/compiler.y index 833f066b54ae80a7100124b086bb042e96abc12e..4719104158dd8befd6b65aef49ba677a19423d52 100644 --- a/compiler2/ttcn3/compiler.y +++ b/compiler2/ttcn3/compiler.y @@ -847,6 +847,7 @@ static const string anyname("anytype"); %token AbstractKeyword %token DefaultModifier %token DynamicModifier +%token TraitKeyword /* TITAN specific keywords */ %token TitanSpecificTryKeyword @@ -984,6 +985,7 @@ static const string anyname("anytype"); %type <bool_val> optAliveKeyword optOptionalKeyword optErrValueRaw optAllKeyword optDeterministicModifier optRealtimeClause optExtKeyword optFinalModifier optAbstractModifier optDefaultModifier + optTraitModifier %type <str> FreeText optLanguageSpec PatternChunk PatternChunkList %type <uchar_val> Group Plane Row Cell %type <id> FieldReference GlobalModuleId @@ -1105,7 +1107,8 @@ static const string anyname("anytype"); %type <type> NestedEnumDef NestedRecordDef NestedRecordOfDef NestedSetDef NestedSetOfDef NestedTypeDef NestedUnionDef PortDefAttribs ReferencedType Type TypeOrNestedTypeDef NestedFunctionTypeDef NestedAltstepTypeDef - NestedTestcaseTypeDef optExtendsClassDef + NestedTestcaseTypeDef +%type <types> optExtendsClassDef BaseClassList %type <types_with_mapping> TypeList AllOrTypeList AllOrTypeListWithFrom AllOrTypeListWithTo TypeListWithFrom TypeListWithTo %type <value> AddressValue AliveOp AllPortsSpec AltGuardChar ArrayBounds @@ -3680,16 +3683,16 @@ PortElement: // 86 ; ClassDef: - optExtKeyword ClassKeyword optFinalModifier optAbstractModifier IDentifier - optExtendsClassDef optRunsOnSpec optMtcSpec optSystemSpec '{' + optExtKeyword ClassKeyword optFinalModifier optAbstractModifier optTraitModifier + IDentifier optExtendsClassDef optRunsOnSpec optMtcSpec optSystemSpec '{' optClassMemberList '}' optFinallyDef { - ClassTypeBody* class_ = new ClassTypeBody($5, $1, $3, $4, $6, $7, $8, $9, - $11, $13); + ClassTypeBody* class_ = new ClassTypeBody($6, $1, $3, $4, $5, $7, $8, $9, $10, + $12, $14); class_->set_location(infile, @$); Type* type = new Type(Type::T_CLASS, class_); type->set_location(infile, @$); - $$ = new Def_Type($5, type); + $$ = new Def_Type($6, type); $$->set_location(infile, @$); class_->set_my_def($$); } @@ -3710,10 +3713,41 @@ optAbstractModifier: | AbstractKeyword { $$ = true; } ; +optTraitModifier: + /* empty */ { $$ = false; } +| TraitKeyword { $$ = true; } +; + optExtendsClassDef: - /* empty */ { $$ = NULL; } -| ExtendsKeyword ReferencedType { $$ = $2; } -| ExtendsKeyword ObjectKeyword { $$ = NULL; } + /* empty */ { $$ = NULL; } +| ExtendsKeyword BaseClassList { $$ = $2; } +; + +BaseClassList: + ReferencedType + { + $$ = new Types; + $$->add_type($1); + } +| ObjectKeyword + { + $$ = new Types; + Type* type = new Type(Type::T_CLASS); + type->set_location(infile, @$); + $$->add_type(type); + } +| BaseClassList ',' ReferencedType + { + $$ = $1; + $$->add_type($3); + } +| BaseClassList ',' ObjectKeyword + { + $$ = $1; + Type* type = new Type(Type::T_CLASS); + type->set_location(infile, @3); + $$->add_type(type); + } ; optFinallyDef: diff --git a/core/OOP.hh b/core/OOP.hh index 607d4098fab01273f859f954b99f3bc3bebca776..60795da9a042be42934c8009c04b2152bf6a8001 100644 --- a/core/OOP.hh +++ b/core/OOP.hh @@ -20,18 +20,17 @@ // OBJECT // ------ -class OBJECT { -private: +class CLASS_BASE { size_t ref_count; boolean destructor; // true, if the destructor is currently running; // also makes sure the object is not deleted again when inside the destructor - - OBJECT(const OBJECT&); // copy disabled - OBJECT operator=(const OBJECT&); // assignment disabled - boolean operator==(const OBJECT&); // equality operator disabled + + CLASS_BASE(const CLASS_BASE&); // copy disabled + CLASS_BASE operator=(const CLASS_BASE&); // assignment disabled + boolean operator==(const CLASS_BASE&); // equality operator disabled public: - OBJECT(): ref_count(0), destructor(FALSE) {} - virtual ~OBJECT() { + CLASS_BASE(): ref_count(0), destructor(FALSE) {} + virtual ~CLASS_BASE() { if (ref_count != 0) { TTCN_error("Internal error: deleting an object with %lu reference(s) left.", ref_count); } @@ -45,6 +44,16 @@ public: destructor = ref_count == 0; return destructor; } +}; + +class OBJECT : public CLASS_BASE { +private: + OBJECT(const OBJECT&); // copy disabled + OBJECT operator=(const OBJECT&); // assignment disabled + boolean operator==(const OBJECT&); // equality operator disabled +public: + OBJECT(): CLASS_BASE() { } + virtual ~OBJECT() { } virtual void log() const { TTCN_Logger::log_event_str("object: { }"); } diff --git a/function_test/Semantic_Analyser/oop/oop_SE.ttcn b/function_test/Semantic_Analyser/oop/oop_SE.ttcn index 21c0007bcd809b5b276b1e91911cac129f8838bf..00ea89bff2ac11534860bf878b821aa4e33d706c 100644 --- a/function_test/Semantic_Analyser/oop/oop_SE.ttcn +++ b/function_test/Semantic_Analyser/oop/oop_SE.ttcn @@ -49,11 +49,11 @@ template integer t := 4; type class C0 { } -type class C1 extends Nonexistent { } //^In type definition// //^In superclass definition// //There is no local or imported definition with name `Nonexistent'// +type class C1 extends Nonexistent { } //^In type definition// //^In superclass or supertrait definition// //There is no local or imported definition with name `Nonexistent'// -type class C2 extends c { } //^In type definition// //^In superclass definition// //`c' is not a reference to a type// +type class C2 extends c { } //^In type definition// //^In superclass or supertrait definition// //`c' is not a reference to a type// -type class C3 extends Rec { } //^In type definition// //^In superclass definition// //Class type expected instead of `@oop_SE.Rec'// +type class C3 extends Rec { } //^In type definition// //^In superclass or supertrait definition// //Class type expected instead of `@oop_SE.Rec'// type class C4 { //^In type definition// @@ -130,11 +130,11 @@ type class C6 { //^In type definition// } -type class C7 extends C9 { } //^In type definition// //^In superclass definition// +type class C7 extends C9 { } //^In type definition// //^In superclass or supertrait definition// -type class C8 extends C7 { } //^In type definition// //While checking embedded recursions\: Circular reference\: `@oop_SE.C8' -> `@oop_SE.C8.<superclass>' -> `@oop_SE.C7' -> `@oop_SE.C7.<superclass>' -> `@oop_SE.C9' -> `@oop_SE.C9.<superclass>' -> `@oop_SE.C8'// +type class C8 extends C7 { } //^In type definition// //While checking embedded recursions\: Circular reference\: `@oop_SE.C8' -> `@oop_SE.C8.<supertraits>.<type1\>' -> `@oop_SE.C7' -> `@oop_SE.C7.<supertraits>.<type1\>' -> `@oop_SE.C9' -> `@oop_SE.C9.<supertraits>.<type1\>' -> `@oop_SE.C8'// -type class C9 extends C8 { } //^In type definition// //^In superclass definition// +type class C9 extends C8 { } //^In type definition// //^In superclass or supertrait definition// external const C0 ec_c0; //^In external constant definition// //External constant cannot be defined for class type `@oop_SE.C0'// external const object ec_obj; //^In external constant definition// //External constant cannot be defined for class type `object'// @@ -346,11 +346,11 @@ function f_create() { //^In function definition// } -type class @final @abstract C21 { } //^In type definition// //Final classes cannot be abstract// +type class @final @abstract C21 { } //^In type definition// //A classes cannot have more than one of the \@final, \@abstract and \@trait modifiers// type class @final C22 { } -type class C23 extends C22 { } //^In type definition// //^In superclass definition// //The superclass cannot be final// +type class C23 extends C22 { } //^In type definition// //^In superclass or supertrait definition// //The superclass cannot be final// type class @abstract C24 { //^In type definition// public function @abstract f1(in Nonexistent a); //^In abstract function definition// //^In formal parameter list// //^In parameter// //There is no local or imported definition with name `Nonexistent'// @@ -476,7 +476,7 @@ type external class @abstract C42 { //^In type definition// //External classes c function @abstract f(); //An external class cannot contain a function// } -type external class C43 extends C39 { } //^In type definition// //^In superclass definition// //An external class cannot extend an internal class// +type external class C43 extends C39 { } //^In type definition// //^In superclass or supertrait definition// //An external class cannot extend an internal class// type class C44 extends C40 { //^In type definition// external function f(); @@ -663,6 +663,92 @@ finally { //^In class destructor// } +type class @trait T1 { //^In type definition// + function @abstract f(); + create() {} //Trait class type `@oop_SE.T1' cannot have a constructor// + function f2() { } //^In function definition// //Trait class type `@oop_SE.T1' cannot have non-abstract methods// +} + +type class @trait T2 { //^In type definition// + function @abstract f(); + external function f3(); //^In external function definition// //Trait class type `@oop_SE.T2' cannot have non-abstract method// +} +finally { //^In class destructor// //Trait class type `@oop_SE.T2' cannot have a destructor// +} + +type class C52 extends T1, T2 { } //^In type definition// //Missing implementation of abstract method `@oop_SE.T1.f'// //Missing implementation of abstract method `@oop_SE.T2.f'// + +type class C53 extends T1, T2 { + function f() { } +} + +type class @trait T3 extends T1 { + function @abstract f2(); +} + +type class C54 extends C53, T3 { + function f2() { } +} + +type class C55 extends T1, //^In type definition// + T2, //Class type `@oop_SE.T2' is already given here// + C54, + T2 { } //^In superclass or supertrait definition// //Duplicate class type in list of classes being extended// + +type class @trait T4 { + public function @abstract f1() return integer; +} + +type class @trait T5 extends T4 { } + +type class @trait T6 { + public function @abstract f1(inout integer p); + public function @abstract f2(inout integer p); +} + +type class C56 { + private function f1() { } + public function f2(inout integer p) { p := p + 1; } +} + +type class C57 extends C56, T6, T5 { //^In type definition// //The prototypes of methods `f2' inherited from classes `C56' and `T6' are not identical// //The prototypes of methods `f1' inherited from classes `T4' and `T6' are not identical// + public function f1() return integer { return 2; } //The prototype of method `f1' is not identical to that of inherited method `@oop_SE.T6.f1'// +} + +type class @trait T7 runs on CT_RunsOn { + public function @abstract f(in integer p) return integer; +} +type class @trait T8 mtc CT_Mtc { } +type class @trait T9 system CT_System { } + +type class C58 extends T7, T8, T9 //^In type definition// + runs on Comp //The `runs on' component type of the subclass \(`@oop_SE.Comp'\) is not compatible with the `runs on' component type of supertrait `T7' \(`@oop_SE.CT_RunsOn'\)// + mtc Comp //The `mtc' component type of the subclass \(`@oop_SE.Comp'\) is not compatible with the `mtc' component type of supertrait `T8' \(`@oop_SE.CT_Mtc'\)// + system Comp //The `system' component type of the subclass \(`@oop_SE.Comp'\) is not compatible with the `system' component type of supertrait `T9' \(`@oop_SE.CT_System'\)// +{ + public function f(in integer p) return integer { return p + 1; } +} + +type class C59 extends T7, T8, T9 + runs on CT_RunsOn2 + mtc CT_Mtc2 + system CT_System2 +{ + public function f(in integer p) return integer { return p + 1; } +} + +function f_traits_bad() { //^In function definition// + var T7 v_t7 := C59.create; //^In variable definition// //A definition without `runs on' clause cannot create a value of class type `@oop_SE.C59', which runs on component type `@oop_SE.CT_RunsOn2'// + var T4 v_t4 := C54.create; //^In variable definition// //Incompatible class types: operation `create' should refer to `@oop_SE.T4' instead of `@oop_SE.C54'// + var object v_obj := v_t7; //^In variable definition// //Type mismatch\: a value of type `object' was expected instead of `@oop_SE.T7'// +} + +function f_traits_good() runs on CT_RunsOn2 { + var T7 v_t7 := C59.create; + log(v_t7.f(3)); +} + + control { //^In control part// var C11 x := C11.create; //^In variable definition// //A definition without `runs on' clause cannot create a value of class type `@oop_SE.C11', which runs on component type `@oop_SE.CT_RunsOn'// //Cannot create value of class type `@oop_SE.C11', which has an `mtc' clause, in the control part.// //Cannot create value of class type `@oop_SE.C11', which has a `system' clause, in the control part.// raise "abc"; //^In raise statement// //Raise statement cannot be used in the control part. It is allowed only in functions, altsteps and testcases.// diff --git a/regression_test/oop/oop.ttcn b/regression_test/oop/oop.ttcn index f545093133c63dcd35b6e8fd1d7d31d090d7513f..75f21adb6c86e66fa76e63713b861b7280aa9bbf 100644 --- a/regression_test/oop/oop.ttcn +++ b/regression_test/oop/oop.ttcn @@ -202,7 +202,11 @@ type class @abstract AbstractClass { } } -type class ConcreteClass extends AbstractClass { +type class @trait TraitClass { + public function @abstract f_trait(in integer x) return integer; +} + +type class ConcreteClass extends AbstractClass, TraitClass { create(octetstring p_const := 'AB'O) { // for compilation only, to make sure there are no name clashes between parameter default values log(p_const); @@ -211,6 +215,9 @@ type class ConcreteClass extends AbstractClass { x := x + 1; return x > 0; } + public function f_trait(in integer x) return integer { + return x + 1; + } } @@ -237,14 +244,17 @@ testcase tc_logging() runs on CT { var template float tf := (0.0 .. 10.0); var BaseClass v_sub := SubClass.create(4, il, "a", 'FF'O, tf); var BaseClass v_final := FinalClass.create(4, il, "a", 'FF'O, tf, 8, "x", -1.5, *); + var TraitClass v_conc := ConcreteClass.create; log(v_base); log(v_sub); log(v_final); + log(v_conc); var charstring v_base_str := "BaseClass"; var charstring v_sub_str := "SubClass ( BaseClass )"; var charstring v_final_str := "FinalClass ( SubClass ( BaseClass ) )"; + var charstring v_conc_str := "ConcreteClass ( AbstractClass, TraitClass )"; if (log2str(v_base) != v_base_str) { setverdict(fail, "v_base: ", v_base); @@ -255,6 +265,9 @@ testcase tc_logging() runs on CT { if (log2str(v_final) != v_final_str) { setverdict(fail, "v_final: ", v_final); } + if (log2str(v_conc) != v_conc_str) { + setverdict(fail, "v_conc: ", v_conc); + } setverdict(pass); } @@ -538,6 +551,16 @@ testcase tc_abstract() runs on CT { } } +testcase tc_trait() runs on CT { + var TraitClass v := ConcreteClass.create; + if (v.f_trait(2) == 3) { + setverdict(pass); + } + else { + setverdict(fail); + } +} + type class Destructor { } finally { setverdict(pass); @@ -722,6 +745,7 @@ control { execute(tc_function_pars_in()); execute(tc_object()); execute(tc_abstract()); + execute(tc_trait()); execute(tc_destructor()); execute(tc_of_operator()); execute(tc_select_class()); diff --git a/usrguide/referenceguide/4-ttcn3_language_extensions.adoc b/usrguide/referenceguide/4-ttcn3_language_extensions.adoc index 13a42acf4d56ba63527cf3039061f0e07e141888..b099ab99e8bc514009028366b1897a0da410c595 100644 --- a/usrguide/referenceguide/4-ttcn3_language_extensions.adoc +++ b/usrguide/referenceguide/4-ttcn3_language_extensions.adoc @@ -9208,24 +9208,27 @@ type class MyClass { ---- * The constructor of a subclass must call the superclass' constructor, if the super-constructor has at least one formal parameter without a default value. (This is in accordance with V1.2.1 of the standard extension, not V1.1.1.) -* The result of logging an instance of a class contains the class name and the logging result of the superclass in round brackets, if there is a superclass. +* The result of logging an instance of a class contains the class name and the logging result of the superclass (if there is one) and supertraits (if there are any) in round brackets, separated by commas. Logging example: [source] ---- type class MyClass2 { - private var integer num; - private var charstring str; + ... +} + +type class @trait MyTraitClass { + ... } -type class MyClass3 extends MyClass2 { - private var template octetstring temp; +type class MyClass3 extends MyClass2, MyTraitClass { + ... } ... -var MyClass3 my_object := MyClass3.create(3, "abc", ?); -log(my_object); // result: MyClass3 ( MyClass2 ) +var MyClass3 my_object := MyClass3.create(...); +log(my_object); // result: MyClass3 ( MyClass2, MyTraitClass ) ---- NOTE: Even though every class automatically extends the built-in class `object`, if it has no superclass specified, this class does not appear in the log. @@ -9235,9 +9238,9 @@ NOTE: Even though every class automatically extends the built-in class `object`, Equality example: [source] ---- -var MyClass3 my_object1 := MyClass3.create(3, "abc", ?); +var MyClass3 my_object1 := MyClass3.create(...); var MyClass3 my_object2 := my_object1; -var MyClass3 my_object3 := MyClass3.create(3, "abc", ?); +var MyClass3 my_object3 := MyClass3.create(...); log(my_object1 == my_object2); // true log(my_object1 == my_object3); // false ---- @@ -9297,6 +9300,8 @@ catch(RoI[-] x) {} // catches only the element type of RoI and no other types module <name> "TTCN-3:2018 Object-Oriented" { ... } ---- +* Trait classes do not extend the built-in class `object`, only normal classes do. + == Default alternatives of union types TITAN supports the default alternatives of union types (i.e. the `@default` modifier) described in the TTCN-3 core language standard with the clarifications and limitations described in this section.