ETSI's Bug Tracker - Ext Pack: Object-oriented features (ES 203 790) |
View Issue Details |
|
ID | Project | Category | View Status | Date Submitted | Last Update |
0007862 | Ext Pack: Object-oriented features (ES 203 790) | [All Projects] General | public | 26-08-2019 12:58 | 28-12-2020 13:04 |
|
Reporter | Jacob Wieland - Spirent | |
Assigned To | Jens Grabowski | |
Priority | normal | Severity | minor | Reproducibility | have not tried |
Status | closed | Resolution | fixed | |
Platform | | OS | | OS Version | |
Product Version | V1.2.1 (published 2020-05) | |
Target Version | V1.3.1 (ongoing) | Fixed in Version | | |
|
Summary | 0007862: Allow trait classes and multiple inheritance |
Description | A trait class is a form of abstract class that can not be created but only extended. It can only extend other trait classes.
A normal class should be allowed to extend a list of trait classes beside its superclass. It inherits all members from all the trait classes it extends.
If a class extends two classes which both provide a member with the same name, that member shall be inherited from the same *defining* class (the most concrete extended class defining or overriding that member). If a trait and a class both need to access the same field, that field shall be inherited from the same defining trait class.
Fields of trait classes shall not have an initializer. Trait classes also do not have a constructor.
Example:
external function newGlobalId() return charstring;
type class @trait Identifiable {
private var charstring id;
public function setId(charstring id) {
this.id := id;
}
public function getId() return charstring {
return id;
}
}
type class MyIdentifiableClass extends Identifiable {
create(charstring id) {
setId(id);
}
}
var Identifiable v_idObj := MyIdentifiableClass.create("1");
var charstring v_id := v_idObj.getId();
Example 2: parallel inheritance
type class @trait A {
function @abstract f();
}
type class @trait B {
function @abstract f();
}
type class C extends A, B {
// illegal, as it inherits A.f() and B.f()
}
type class @trait B2 extends A {}
type class C2 extends A, B2 { // legal, as it inherits only A.f()
function f() { ... }
}
type class C3 extends A {
function f() { ... }
}
type class D extends C2, C3 {
// illegal, as it inherits C2.f() and C3.f()
} |
Steps To Reproduce | |
Additional Information | |
Tags | No tags attached. |
Relationships | |
Attached Files | CR7862.docx (146,209) 28-08-2019 12:10 http://oldforge.etsi.org/mantis/file_download.php?file_id=3862&type=bug CR7862-2.docx (167,362) 28-08-2019 13:51 http://oldforge.etsi.org/mantis/file_download.php?file_id=3863&type=bug CR7862-3.docx (148,469) 28-08-2019 14:17 http://oldforge.etsi.org/mantis/file_download.php?file_id=3864&type=bug CR7862-4.docx (148,365) 07-10-2020 12:26 http://oldforge.etsi.org/mantis/file_download.php?file_id=3957&type=bug CR7862-5.docx (148,418) 07-12-2020 13:18 http://oldforge.etsi.org/mantis/file_download.php?file_id=3966&type=bug CR7862-6.docx (149,147) 09-12-2020 19:11 http://oldforge.etsi.org/mantis/file_download.php?file_id=3983&type=bug |
|
Issue History |
Date Modified | Username | Field | Change |
26-08-2019 12:58 | Jacob Wieland - Spirent | New Issue | |
26-08-2019 12:58 | Jacob Wieland - Spirent | Status | new => assigned |
26-08-2019 12:58 | Jacob Wieland - Spirent | Assigned To | => Jacob Wieland - Spirent |
26-08-2019 14:28 | Jacob Wieland - Spirent | Status | assigned => new |
27-08-2019 09:41 | Kristóf Szabados | Note Added: 0015466 | |
27-08-2019 11:59 | Jacob Wieland - Spirent | Note Added: 0015469 | |
27-08-2019 12:03 | Jacob Wieland - Spirent | Note Added: 0015470 | |
27-08-2019 14:50 | Jacob Wieland - Spirent | Note Added: 0015477 | |
27-08-2019 14:50 | Jacob Wieland - Spirent | Status | new => assigned |
28-08-2019 12:10 | Jacob Wieland - Spirent | File Added: CR7862.docx | |
28-08-2019 12:20 | Jacob Wieland - Spirent | Note Added: 0015482 | |
28-08-2019 12:29 | Kristóf Szabados | Note Added: 0015484 | |
28-08-2019 12:37 | Jacob Wieland - Spirent | Note Added: 0015485 | |
28-08-2019 12:37 | Jacob Wieland - Spirent | Assigned To | Jacob Wieland - Spirent => Tomas Urban |
28-08-2019 12:37 | Jacob Wieland - Spirent | Status | assigned => confirmed |
28-08-2019 13:32 | Kristóf Szabados | Note Added: 0015487 | |
28-08-2019 13:51 | Tomas Urban | File Added: CR7862-2.docx | |
28-08-2019 13:53 | Tomas Urban | Note Added: 0015488 | |
28-08-2019 13:53 | Tomas Urban | Assigned To | Tomas Urban => Kristof.Szabados |
28-08-2019 14:17 | Kristóf Szabados | File Added: CR7862-3.docx | |
28-08-2019 14:20 | Kristóf Szabados | Note Added: 0015489 | |
28-08-2019 14:20 | Kristóf Szabados | Assigned To | Kristof.Szabados => Tomas Urban |
28-08-2019 14:20 | Kristóf Szabados | Status | confirmed => assigned |
28-08-2019 14:21 | Kristóf Szabados | Status | assigned => confirmed |
28-08-2019 14:40 | Tomas Urban | Note Added: 0015490 | |
28-08-2019 14:40 | Tomas Urban | Assigned To | Tomas Urban => Jens Grabowski |
17-12-2019 11:22 | Jens Grabowski | Assigned To | Jens Grabowski => Kristóf Szabados |
17-12-2019 11:22 | Jens Grabowski | Status | confirmed => assigned |
18-12-2019 07:41 | Kristóf Szabados | Note Added: 0015565 | |
18-12-2019 11:10 | Jacob Wieland - Spirent | Note Added: 0015578 | |
18-12-2019 11:35 | Jens Grabowski | Note Added: 0015580 | |
18-12-2019 11:35 | Jens Grabowski | Assigned To | Kristóf Szabados => Jens Grabowski |
14-08-2020 11:26 | Jens Grabowski | Assigned To | Jens Grabowski => Kristóf Szabados |
06-10-2020 13:53 | Jens Grabowski | Assigned To | Kristóf Szabados => Jacob Wieland - Spirent |
06-10-2020 13:54 | Jens Grabowski | Note Added: 0015761 | |
07-10-2020 12:26 | Jacob Wieland - Spirent | File Added: CR7862-4.docx | |
07-10-2020 12:27 | Jacob Wieland - Spirent | Note Added: 0015774 | |
07-10-2020 12:27 | Jacob Wieland - Spirent | Assigned To | Jacob Wieland - Spirent => Kristof.Szabados |
07-10-2020 12:27 | Jacob Wieland - Spirent | Status | assigned => confirmed |
08-10-2020 13:28 | Kristóf Szabados | Note Added: 0015780 | |
08-10-2020 13:29 | Kristóf Szabados | Assigned To | Kristof.Szabados => Jacob Wieland - Spirent |
08-10-2020 13:29 | Kristóf Szabados | Status | confirmed => assigned |
08-10-2020 13:29 | Kristóf Szabados | Status | assigned => confirmed |
08-10-2020 15:50 | Jacob Wieland - Spirent | Note Added: 0015784 | |
08-10-2020 15:55 | Jacob Wieland - Spirent | Note Added: 0015785 | |
07-12-2020 13:18 | Jacob Wieland - Spirent | File Added: CR7862-5.docx | |
07-12-2020 13:19 | Jacob Wieland - Spirent | Status | confirmed => new |
07-12-2020 13:20 | Jacob Wieland - Spirent | Note Added: 0015797 | |
07-12-2020 13:20 | Jacob Wieland - Spirent | Assigned To | Jacob Wieland - Spirent => Tomas Urban |
07-12-2020 13:20 | Jacob Wieland - Spirent | Status | new => confirmed |
09-12-2020 08:02 | Tomas Urban | Note Added: 0015819 | |
09-12-2020 08:02 | Tomas Urban | Assigned To | Tomas Urban => Kristóf Szabados |
09-12-2020 19:11 | Kristóf Szabados | File Added: CR7862-6.docx | |
09-12-2020 19:16 | Kristóf Szabados | Note Added: 0015828 | |
09-12-2020 19:17 | Kristóf Szabados | Note Added: 0015829 | |
09-12-2020 19:17 | Kristóf Szabados | Assigned To | Kristóf Szabados => Tomas Urban |
09-12-2020 19:17 | Kristóf Szabados | Status | confirmed => assigned |
10-12-2020 07:33 | Tomas Urban | Note Added: 0015832 | |
10-12-2020 07:33 | Tomas Urban | Assigned To | Tomas Urban => Jacob Wieland - Spirent |
10-12-2020 07:33 | Tomas Urban | Status | assigned => confirmed |
10-12-2020 09:21 | Jens Grabowski | Note Added: 0015839 | |
10-12-2020 09:22 | Jens Grabowski | Status | confirmed => resolved |
10-12-2020 09:22 | Jens Grabowski | Resolution | open => fixed |
10-12-2020 09:22 | Jens Grabowski | Assigned To | Jacob Wieland - Spirent => Gyorgy Rethy |
17-12-2020 16:18 | Gyorgy Rethy | Assigned To | Gyorgy Rethy => Jens Grabowski |
17-12-2020 16:18 | Gyorgy Rethy | Product Version | => V1.2.1 (published 2020-05) |
17-12-2020 16:18 | Gyorgy Rethy | Target Version | => V1.3.1 (ongoing) |
28-12-2020 13:04 | Jens Grabowski | Status | resolved => closed |
Notes |
|
|
so multiple extension is only allowed for extending trait classes, and this extension behaves as a copy-paste of the trait's content, right? |
|
|
|
I would consider it more as a sort of flattening where first you flatten the definition of each extended class (keeping track of the original class of each member) and then merge the resulting class bodies of the flattened classes into one effective superclass while removing duplicates that come from the same (non-flattened) class body.
type class @trait A {
var integer a;
}
type class @trait B {
var integer b;
}
type class @trait C {
var integer c;
}
type class @trait D extends B, C {
}
==> D is the same (ignoring subtyping) as:
type class @trait D {
var integer a; // a is inherited via B and C, but only added once!
var integer b;
var integer c;
} |
|
|
|
flattened B:
type class @trait B {
var integer a; [from A]
var integer b; [from B]
}
flattened C:
type class @trait C {
var integer a; [from A]
var integer c; [from C]
}
If we now add a class C2:
type class @trait C2 {
var integer c;
}
then
type class @trait D2 extends C, C2 {
}
would result in a flattened class:
type class @trait D2 {
var integer a; [from A]
var integer c; [from C]
var integer c; [from C2]
}
which is illegal (it has c from C and C2) |
|
|
|
STF discussions: maybe not allow fields in trait classes |
|
|
|
I have uploaded a proposal.
I have kept the possibility of fields (as the same problem already occurs with components and the same solution can be used).
I also relaxed the parallel inheritance restriction somewhat insofar that if you inherit from one class A function A.f() and from another class you inherit B.f() which overrides A.f() (directly or indirectly) through extension, then this shall also be allowed.
Consider the following rather typical scenario.
type class @trait HasX {
function @abstract getX() return integer;
}
type class @abstract A extends HasX {
// implementation using getX()
}
type class @trait HasXImpl {
var integer x;
function getX() return integer {
return x;
}
}
type class AImpl extends A, HasXImpl {
// inherits only HasXImpl.getX() because it overrides HasX.getX()
create(integer x) {
this.x := x;
}
}
So, one version of an implementation can only 'win' over another if there is a clear overriding relationship between them (which can be established via topological sorting). |
|
|
|
Shouldn't HasXImpl extend HasX for this to work?
Now they are just 2 classes that happen to have functions with the same name. |
|
|
|
|
|
|
The wording is a bit strange now.
in 5.1.1.0 the new J restriction sounds like saying no 2 (even unrelated) classes can have fields with the same name (the same extending class would have to extend both classes for this to be problem, even then it would not be a problem with the fields, but the extension).
restriction k sounds like implying a direct extension tree.
restriction m is saying that each class can be extended only once (which not be very useful) |
|
|
|
I made changes to the revisions j, k and m. Please review. |
|
|
|
Added a note on the not so obvious way of making a backward incompatible change, please check.
The rest of the text is fine, but maybe we should sleep on it to see if we can come up with a wording, that is not suggesting multiple inheritance support this much. |
|
|
|
The note added by Kristof is fine. The proposal can be added to the specification, but since Kristof suggests postponing, I won't mark it as resolved yet. |
|
|
|
To me the current text still sounds very much like a normal multiple inheritance feature, which we aggreed to not have as a general guideline in the beginning.
Ok it is cumbersome for a multiple-inheritance, since one has to write his own initializer functions, and create some "dummy" class on the top of the hierarchy to be able to instantiate it ... yet it still is multiple-inheritance.
Or I might miss some part that forbids this kind of understanding and emphasizes flattening. |
|
|
|
Yes, it is multiple inheritence but much more restrictive than C++ multiple inheritance, avoiding diamond problems and clashing definitions.
Also, the general guidelines in the beginning were (as far as I remember) meant for the first version of the OO feature where we explicitly kept the possibility of multiple inheritance open, so I don't understand why this is relevant for this additional OO feature. |
|
|
|
STF discussion: It was agreed that some sort of multiple inheritance should be supported. The concrete implementation, e.g., interfaces or trait classes or something else, need to be discussed in 2020. |
|
|
|
STF decision: Only JAVA-like interfaces without defaults. |
|
|
|
updated with the agreed-on restrictions, please review |
|
|
|
Some notes:
1)
"adding a new overriding function to an existing normal or trait class is semantically valid in its own context"
What is the purpose of a trait class overriding a function of an other trait class?
As neither can have bodies, this sounds questionable.
2)
Other than the previous question why would it be non-backward compatible if a normal class extends two independend trait classes, that are added new overriding functions?
As these functions can not have bodies, and have the same signature, if this repetition/oberriding is allowed, it just repeats that the extending normal classes have to have a function with that signature.
3)
"type class C2 extends A, B2 { // legal, as it inherits only B2.f()"
and
"type class E extends A, C2 {
// legal, it does not inherit A.f() as this is overriden by C2.f()
"
It is actually not trivial. While I could agree it is not trivial why inheriting via 2 different inheritance trees of trait classes is allowed. As far as I understand the reason, is that both inheritance trees in the end define that there must be a function with that signature in the extending normal class, and none of the functions in the trait classes has bodies. As such it might be better to say, that it is legal since both traits being extended require the presence of a function with the exact same signature and there is no normal class extended. |
|
|
|
1) is for future expansion of the trait concept, if both traits add the same function, this shall be a clash, so one needs to override the other.
But, of course, we could remove that restriction for now, would also need to be done in the example.
I don't understand question 2.
3) is in essence also to avoid confusion, if you inherit two functions of the same name from different sources, you would have to check whether they have the same parameters. With this, you can only inherit it from one source, so the question of comparison side-by-side inherited things becomes moot.
Again, we can also remove this, it will have to be re-added once we allow trait-members with bodies, though. |
|
|
|
Adding functions to traits is potentially non-backward compatible as it cannot be assured that all classes inheriting the trait directly or indirectly already implement that new function. Even if you implement it in all classes that are under your control, some other source can have used a class implementing the former version of the trait and not implement the new function. Thereby, you are potentially breaking the using code.
Same is true for adding any members in any class (even private), as you can never know if a subclass already has a member of that name and thus would be broken by your change. This is normal, except for private members. |
|
|
|
I have removed the trait-related restrictions not necessary for only-interface-traits and changed the examples accordingly. Please review. |
|
|
|
It looks fine by me. It is quite restrictive at moment, but it is possible to add more complicated features (like non-abstract functions on the trait level) in the future.
I have only two technical comments:
1. Since the restrictions j and k were added in this proposal, I would not mark them as "Void", but remove completely so that we won't accidentally add them to the standard.
2. The note for the restriction k should be moved to the semantic rules.
Kristof, you revised the previous versions of the proposed changes. Could you please check this one as well? |
|
|
|
For the first point I have removed restriction j and k (that were changed to void.
For the second point I have just removed the note as the situation described in it is not possible.
The note said: ... "if they also extend an other (possibly independent) trait that also overrides the same function but with different formal parameters or return clause"
This is not possible as section 5.1.1.7 declares:
- "A method inherited from a superclass can be overridden by the subclass by redefining a function of the same name and with the same formal parameter list."
- "The return type of an overriding function shall be the same as the return type of the overridden function with the same template restrictions and modifiers" |
|
|
|
Please check the new version. |
|
|
|
Well, I think that what Jacob meant is that developers have to be careful when adding new methods to classes and traits if these are extended in other projects that are not under their control. You are right that it would produce an error. However, it is true only if the developer can compile the whole code and that's not always the case.
The question is if we need this kind of note or not. From my point of view, it is not necessary because (as Kristof pointed out) there are clear rules for parameters and return values of methods whose name is present on a superclass or supertrait level. Jacob, could you please comment the note issue? If you don't insist on having it, I think it is possible to resolve the CR. |
|
|
|
STF discussion: Note should be added in a more prominent place. This CR is resolved with version 6 of the resolution. |
|