Simulation results contradicts my understanding of polymorphism

In the below code, I have not declared the task disp_a as virtual in class b and class c. Upon creating the object of class c and assigning it to the handle of b, I see the task of class c getting executed. I thought that, since I didn’t declare the method as virtual in class b, class c’s method won’t be called. Kindly clarify.

class a;
  virtual task disp_a();
    $display("disp_a");
  endtask
endclass

class b extends a;
  task disp_a();
    $display("disp_a from b");
  endtask
endclass

class c extends b;
  task disp_a();
    $display("disp_a from c");
  endtask
endclass

module tb();
  initial
    begin
      b b1;
      c c1 = new();
      b1 = c1;
      b1.disp_a();
      $finish;
    end
endmodule

Output -
disp_a from c

In reply to naveensv:

As mentioned in IEEE 1800-2012 Section 8.20:

A virtual method may override a non-virtual method, but once a method has been identified as virtual, it shall remain virtual in any subclass that overrides it. In that case, the virtual keyword may be used in later declarations, but is not required.

Here, the class ‘a’ declares a method as ‘virtual’, so all the subsequent extended classes will automatically have virtual methods implemented in them. The virtual keyword is optional in this case.

Once the method becomes virtual in top most base class, then it shall remain virtual in all the subsequent classes.

The tool uses object’s type to call the method, if it is declared as virtual. While it uses the type of handle to call the method, if it is non-virtual. Here, since class ‘a’ has virtual method, so inherently the extended classes will have virtual method inside them. As a result the class ‘c’ method is invoked.

In order to test it out, you can try out the following things:

1. remove virtual from class a --> this will make all the methods non-virtual and invoke class b method
2. keep virtual in class a and add some input argument in class c method --> this will throw an error since the prototypes of inherent virtual methods will be different
3. remove virtual in class a and add some input argument in class c method --> this will invoke class c method since the method is non-virtual now

Hope this clears out the doubt.

In reply to sharvil111:

Got it. Thanks for clarifying.!