RE: [sv-ec] Multiple inheritance of Interface Classes in Mantis Item 1356

From: Tipp, Brandon P <brandon.p.tipp@intel.com>
Date: Wed Jun 01 2011 - 10:37:16 PDT

I don't think that we want to open up discussion at this point if a class inherits anything from an interface class. That rule is already clear in the proposal. As in your example, layered parameterized interface classes containing typedefs will be particularly complex, but it will not be a very common case. We can't focus on to simplifying complex, uncommon cases. Rather, we need to make sure that the rules are clear and consistent enough to be applied without any surprises or questions by vendors or users alike. Although it's complex and ugly, I think that the requirements for your example are clear.

That said, I want to stay focused on the discussion only of the inheritance behavior between interface classes and each other. Given the different proposals for handling your scenario, only option #1 would affect your code in any way, as follows:

   interface class A #(type T);
      typedef struct { T a,b; } S;
      pure virtual task myTask(input S);
   endinterface

   interface class B #(type T) extends implements A#(T);
      typedef A#(T) bT[1:0];
      pure virtual task myOtherTask(input bT);
   endinterface

   class Imp #(type T) implements B#(T);
         task myTask(input A#(T)::S); endtask;
         task myOtherTask(input B#(T)::bT); endtask
   endinterface

We can look at a different example translated and slightly modified from sun's website on Java:

interface class BaseColors {
                parameter RED = 1;
parameter GREEN = 2;
parameter BLUE = 4;
}
interface class RainbowColors extends BaseColors {
                parameter YELLOW = 3;
parameter ORANGE = 5;
                parameter INDIGO = 6;
parameter VIOLET = 7;
}
Interface class PrintColors extends BaseColors {
                parameter YELLOW = 8;
parameter CYAN = 16;
parameter MAGENTA = 32;
parameter VIOLET = 7; // added for illustrative purposes
}
Interface class LotsOfColors extends RainbowColors, PrintColors {
                parameter FUCHSIA = 17
parameter VERMILION = 43
parameter CHARTREUSE = RED+90;
}

For the different options, this is how code within LotsOfColors would have to refer to each color:

1) The class for all colors would have to be specified

2) YELLOW and VIOLET (and possibly RED, GREEN and BLUE) would need to be scoped, all other parameters would be accessible without a scope

a. VIOLET, RED, READ and BLUE could be accessed without a scope, but YELLOW would still require a scope because the 3 vs. 8 ambiguity

3) Different for each:

a. (assuming LotsOfColors extends RainbowColors implements PrintColors) CYAN and MAGENTA would require a scope, no other colors would. Un-scoped, YELLOW is 3.

b. Nothing would require a scope. Un-scoped, YELLOW is 3.

There are also implications for any class which implements LotsOfColors. Anything that must be scoped within LotsOfColors must be scoped with a super-interface class by the implementing class. Anything not scoped inside LotsOfColors could be scoped as LotsOfColors without digging up the hierarchy.

Option #2 and #2a have additional complexity introduced with the diamond inheritance case which I implied with the question regarding RED, GREEN and BLUE. Let me toss out an even more complicated example to show how messy variations of option #2 could get:

interface class boolIntf #(bit TRUE = 1);
                localparam FALSE = ~TRUE;
                typedef enum bit (TRUE, FALSE) bool_t;
endclass

interface class YesNoIntf extends boolIntf #(1);
                typedef enum bit (YES = TRUE, NO = FALSE) yes_no_t;
                typedef enum bit (YEA = 1, NEA = 0) yea_nea_t;
endclass

interface class SiNoIntf extends boolIntf #(0);
                typedef enum bit (SI = TRUE, NO = FALSE) si_no_t;
                typedef enum bit (YEA = 1, NEA = 0) yea_nea_t;
endclass

interface class PassFailIntf extends YesNoIntf, SiNoIntf;
                // Questions on inheritance here
endclass

Observations/questions:

1) TRUE, FALSE and NO are ambiguous inside PassFailIntf. Do we want to try to define a precedence for inheritance (option #3a or #3b)?

a. If SiNoIntf were declared as "interface class SiNoIntf extends boolIntf #(1);" instead then TRUE, FALSE and NO would have the same value no matter which path you look at. In that case, would you want to inherit them?

b. If the TRUE value were passed thru SiNoIntf and YesNoIntf instead of hard-coded, would that change the inheritance into PassFailIntf?

2) YES, SI, yes_no_t and si_no_t don't have conflicts but they are part of an enumerated type that has a partial conflict; do we want to inherit any/all of them into PassFailIntf?

3) bool_t comes from the same place two different ways, but the values for bool_t don't exactly match through those two ways. Do we want to inherit bool_t?

4) YEA, NEA and yea_nea_t have the exact same definition from both super interface classes of PassFailIntf. Do we want to inherit any of them into class PassFailIntf?

The above is a complicated (and poor) example, so I'm not too worried about how complicated the code would get for a user who does this. Rather, I want to make sure that the rules governing the behavior is completely clear for both the tool and user. I'm not saying that we should abandon option #2, but if we want to go with it then we need to define more clarifications.

-Brandon

From: Gordon Vreugdenhil [mailto:gordonv@model.com]
Sent: Tuesday, May 31, 2011 4:34 PM
To: Tipp, Brandon P
Cc: sv-ec@eda.org
Subject: Re: [sv-ec] Multiple inheritance of Interface Classes in Mantis Item 1356

This kind of issue impacts not only the interface classes themselves.
Consider the following:

   interface class A #(type T);
      typedef struct { T a,b; } S;
      pure virtual task myTask(input S);
   endinterface

   interface class B #(type T) extends A#(T);
      typedef A#(T) bT[1:0];
      pure virtual task myOtherTask(input bT);
   endinterface

   class Imp #(type T) implements B#(T);
         task myTask(input A#(T)::S); endtask;
         task myOtherTask(input B#(T)::bT); endtask
   endinterface

Since types are not inherited by an implementing class,
the class needs to know (potentially all) information
about where a type originates in order to define the
implementation of the methods.

As we consider some of these effects, I'd suggest that
we use examples such as the one I have above to make
sure that we all understand and agree with the compositional
effects of parameterized classes (and other constructs) with
the name visibility and resolution.

I am concerned that the composition of some of these
issues will lead to the kinds of thing that my example
is doing -- essentially requiring implementing classes to
have knowledge about the scope derivation of the types
which participate in the method signatures. This is
particularly true if the "conflict" rules end up resulting
in names of types becoming inaccessible via the most
derived interface class and yet the inherited methods
need to be defined. My example above is pretty trivial;
if the actual type used in B's extension of A is itself
a parameterized type (say "B extends A#(C#(T))") then
the resulting type complexity will become very high
quickly.

I'm not entirely sure whether there is a very good answer
to some of this. Any form of "diamond" equivalence
is going to pose problems that I raised very early on in
terms of confusion over type names and exactly how
one has to refer to types within the implementing class.
Given the interesting forms of parameterized class
specialization, we need to be very careful to make sure
that the rules end up resulting in as little surprise as
possible -- an interesting question for many of the non-trivial
situations is what happens when someone is implements an
interface class and wants to "cut-and-paste" the method
prototypes. That clearly won't work (with any of the
proposed directions) but what is the obligation of the
implementing class in terms of determining the origination
and reference syntax for all of the required types?

Gord.

On 5/31/2011 3:30 PM, Tipp, Brandon P wrote:
Hi,

I was asked to highlight on the reflector an issue with the Interface Class proposal (mantis item 1356). The proposal calls for allowing an interface class to extend (inherit from) multiple interface classes. Whenever there is multiple inheritance there is the possibility of name collisions and the Interface Class proposal doesn't discuss how to resolve any such conflicts. We talked about this briefly at the May 23 SV-EC meeting. These are the main options that we discussed:

Don't allow any inheritance at all. The BNF would change so that an interface class would implement one or more interface classes, rather than extend one or more interface classes. Any references to parameters or typedefs from a "super" interface class would have to be scoped.

Whenever there is a conflict, the conflicting symbol is not inherited. Any reference to a symbol that has a conflict would therefore result in an elaboration error that the symbol was never defined or not found. This option is similar to the behavior in Java (I've looked it up) where any un-scoped reference to a symbol that is present in multiple super interfaces would be "considered ambiguous".

For further discussion on this option, would we allow inheritance if the conflicting symbol can be statically determined to resolve to the exact same value/type for all super interface classes? (Java would still consider that to be ambiguous).

Define a precedence. This can be done in two ways:

Allow an Interface Class to extend (inherit from) only one other interface class, and "implements" one or more interface classes for which there would be no inheritance.

Precedence of inheritance would be given by the order by which interface classes are inherited.

My take on each of the above (none of which is ideal):

This adds verbosity to code for the most common cases and it's not consistent with Java interfaces that we are modeling which will be confusing to those familiar with Java. Since all symbols will have to be scoped, this would result in code that is the most difficult to break.

This is most consistent with Java's behavior and it's less verbose than #1. When an error occurs it could be difficult to figure out why a symbol isn't found. A similar error in Java would tell you that the symbol is ambiguous and suggest what you might be referring to which would make it easier to fix. By "not inheriting" the symbol, the compiler/elaborator would just tell you that the symbol was not defined or not found, which isn't helpful. Users would still be free to act like nothing is inherited and refer to everything with a scope if they want.

This would reduce the verbosity and errors further, but I'm not entirely convinced that the ROI is there considering the extra effort on the tool side. Also, you are more likely to get apparently random errors where one usage of a parameterized interface class will give you errors, but another will not.

There are different problems with each choice.

This syntax is more confusing than the other options. Users would be prone to get errors where they intend to extend a single interface class but instead us the "implements" key word. Also, if you want to inherit from multiple interface you'd have to pick just one to extend.

This can lead to difficult to debug compile-time and run-time errors if you think that you are inheriting a symbol ICA::foo, but instead are inheriting ICB::foo and they are not the same.

I don't think that there is an ideal option, but we need to pick one of the above. Are there any thoughts from the committee?

Thanks,
Brandon

--
This message has been scanned for viruses and
dangerous content by MailScanner<http://www.mailscanner.info/>, and is
believed to be clean.
--
--------------------------------------------------------------------
Gordon Vreugdenhil                                503-685-0808
Model Technology (Mentor Graphics)                gordonv@model.com<mailto:gordonv@model.com>
-- 
This message has been scanned for viruses and
dangerous content by MailScanner, and is
believed to be clean.
Received on Wed Jun 1 10:38:22 2011

This archive was generated by hypermail 2.1.8 : Wed Jun 01 2011 - 10:38:26 PDT