Polymorphism

I have written a simple example to understand polymorphism better.

module poly_case4;

class BaseC;

  virtual function void func1;
    $display ("func1 in BaseC");
    func2;
  endfunction

  function void func2;
    $display ("func2 in BaseC");
  endfunction

endclass: BaseC

class DerivedC extends BaseC;

  function void func1;
    $display ("func1 in DerivedC");
    func2;
  endfunction

  function void func2;
    $display ("func2 in DerivedC");
                                            
endclass : DerivedC

BaseC    P1 ;
DerivedC P2 = new;

initial begin
  P1 = P2;
  P1.func1;
end

endmodule

Please note that, func2 is not defined as virtual.
So, P1.func1 is called, I expected late binding based for func1 and get resolved to DerivedC.func1
But since func2 is not defined as virtual, I expected func2 to be called from BaseC.func2
In reality, DerivedC.func2 is being called.

So, how does polymorphism work actually?
In the above case, there is no late binding as far as func2 is concerned and it applies only for func1?

In reply to verif_learner:

But the call to func2 inside DerivedC::func1 is bound at compile time to DerivedC::func2.

It might help to think of how the code looks if you move the functions outside the class

module poly_case4;
 
class BaseC;
extern virtual function void func1:
extern         function void func2:
endclass
function void BaseC::func1;
    $display ("func1 in BaseC");
    func2; // bound as BaseC::func2;
endfunction
 
function void BaseC::func2;
    $display ("func2 in BaseC");
endfunction
 
class DerivedC extends BaseC;
extern virtual function void func1:
extern         function void func2: 
endclass
function void DerivedC::func1;
    $display ("func1 in DerivedC");
    func2; // bound as DerivedC::func2;
  endfunction
 
function void DerivedC::func2;
    $display ("func2 in DerivedC");
endfunction
 
BaseC    P1 ;
DerivedC P2 = new;
 
initial begin
  P1 = P2;
  P1.func1;
end
 
endmodule

In reply to dave_59:

In reply to verif_learner:
But the call to func2 inside DerivedC::func1 is bound at compile time to DerivedC::func2.
It might help to think of how the code looks if you move the functions outside the class

module poly_case4;
class BaseC;
extern virtual function void func1:
extern         function void func2:
endclass
function void BaseC::func1;
$display ("func1 in BaseC");
func2; // bound as BaseC::func2;
endfunction
function void BaseC::func2;
$display ("func2 in BaseC");
endfunction
class DerivedC extends BaseC;
extern virtual function void func1:
extern         function void func2: 
endclass
function void DerivedC::func1;
$display ("func1 in DerivedC");
func2; // bound as DerivedC::func2;
endfunction
function void DerivedC::func2;
$display ("func2 in DerivedC");
endfunction
BaseC    P1 ;
DerivedC P2 = new;
initial begin
P1 = P2;
P1.func1;
end
endmodule

Dave,

I still don’t get it.
If it is compile time binding then Base::func2 should be called as P1 is BaseC type.

Is it the case that since func1 is bound at run time, func2 is also bound at run time.
Since func1 is called as DerivedC::func1, is automatically calls DerivedC::func2 which is local to DerivedC.

In reply to verif_learner:
No, the call to funct2 is bound at compile time. It does not matter how you got into DerivedC::func1. The call to func2 from derivedC:funct1 is bound to derivedC::func2.

In reply to dave_59:

Hi Dave,

Does that mean even if func2 is virtual or not - same result i.e. “func2 in DerivedC”?
and same means final output depends on if func1 (which is calling function) is virtual or not.Is that correct?

mann_verif

In reply to dave_59:

In reply to verif_learner:
No, the call to funct2 is bound at compile time. It does not matter how you got into DerivedC::func1. The call to func2 from derivedC:funct1 is bound to derivedC::func2.

Dave,

I think I am getting a hang of this. I guess this is to do with calling a function using object variable versus regular calls. I think regular calls are bound at compile time irrespective of whether they are virtual or not. I think mann_verif was also alluding to this point.

By the way, is there anything in LRM that talks about this?

In reply to verif_learner:

" I guess this is to do with calling a function using object variable versus regular calls."
Yes verif_learner, I tried replacing func1 and func2 , func1 being called from func2. Calling function - now func2, if ‘it is virtual or not’ and ‘assigning P1=P2’ decide the functionality.

In reply to verif_learner:

If by regular call, you mean a non-class method, that is not the case. It might help to think about the implicit this class variable. The call to
funct2
is really the same as
this.funct2
, where the type of
this
variable is the current class.

In reply to dave_59:

In reply to verif_learner:
If by regular call, you mean a non-class method, that is not the case. It might help to think about the implicit this class variable. The call to
funct2
is really the same as
this.funct2
, where the type of
this
variable is the current class.

Dave,

I meant calling a function directly object_handle.func versus object_handle.func calling another function. I think in the latter case, it is compile time binding

In reply to verif_learner:

class DerivedC extends baseC;
extern virtual function void func1;
extern         function void func2;
endclass

function void DerivedC::func1;
    $display ("func1 in DerivedC");
    func2;   //**here means this.func2, if we use super.func2 then the result will be different**
  endfunction
 
function void DerivedC::func2;
    $display ("func2 in DerivedC");
endfunction
 
BaseC    P1 ;
DerivedC P2 = new;
 
initial begin
  P1 = P2;
  P1.func1;
end

just as the dave said, here use the implicit this class variable, so the result is func2 in DerivedC. I try to use super.func2, the result is func2 in baseC.

thanks
best regards

In reply to Allan_mo:

Looks like I have to open this thread.

I have tried several cases and I am not clear how polymorphism works in the light of the statement made by dave about compile time binding of func2.

Please see the cases below.


Case	Base	Derived	Resolution
1	NV	ND	Base
	NV	ND	Base
2	NV	NV	Base
	NV	NV	Base
3	NV	V	Base
	NV	V	Base
4	V	NV	Derived
	NV	NV	Derived
5	NV	NV	Base
	V	NV	Derived
6	V	V	Derived
	NV	NV	Derived
7	NV	NV	Base
	V	V	Derived
8	V	NV	Derived
	V	NV	Derived
9	V	V	Derived
	V	NV	Derived
10	V	V	Derived
	V	V	Derived
11	V	ND	Base
	NV	NV	Base
12	V	ND	Base
	V	ND	Base

Please look at the table above.
The basic code template remains as I have put in the original post.
What varies is just whether func1, func2 are defined as virtual or non virtual.
In the above table, N - virtual; NV - non virtual; ND - not defined

For example,
case 1 means the following
In base class, func1/2 are non virtual
in derived class, func1/func2 are not defined
The last column, resolution, indicates that the call P1.func1 resolves to
In this case, it resolves to func1 and func2 in base class

Let us take another case, case 5
Here,
in base class, func1 is non virtual, func2 is virtual
in derived class, func1 is virtual and func2 is non virtual
it resolves to func1 in base class and func2 in derived class

Now, what I am confused is,
How to interpret case 5, where func1 is called from base class but func2 is called from derived class. It is based on the fact that func2 is a virtual function. So, wherever func2 is declared as a virtual class, it seems there is late binding. This seems to be all true.
So I am a bit confused when Dave says that func2 is based on compile time binding.

I need some clarifications here please …

The code base is here in case anyone wants to see the source code:

https://github.com/sharanbr/assorted_verification/tree/master/polymorphism

In reply to verif_learner:

I said that the call to func2 was bound at compile time because it was declared as a non-virtual method. Your case 5 has it declared as a virtual method. You are forgetting that once declared virtual, all overrides remain virtual.

In reply to verif_learner:

Hi,
Firstly,thank you for giving us so many case to reference. From your case, we can easily find a law. Once func1 or func2 was decleared as virtual, after p1.func1 has been done func1 or func2 in derived class was implemented.

Thanks
Best regard

In reply to dave_59:

In reply to verif_learner:
I said that the call to func2 was bound at compile time because it was declared as a non-virtual method. Your case 5 has it declared as a virtual method. You are forgetting that once declared virtual, all overrides remain virtual.

Ok. I must have confused your remark about func2 as rules of late binding not applicable for cases where a function is not called directly using class_variable.function.

Thanks

In reply to Allan_mo:

In reply to verif_learner:
Hi,
Firstly,thank you for giving us so many case to reference. From your case, we can easily find a law. Once func1 or func2 was decleared as virtual, after p1.func1 has been done func1 or func2 in derived class was implemented.
Thanks
Best regard

thanks