Abstract type and derived types - a problem with sample code

I’ve found interesting the list compiled by @Beliavsky, of Fortran tutorials on OOP, in which I have little-to-no experience. I started to read Chapter 11 of Holcomb’s tutorial and found something intriguing.
There is an example on abstract types and inheritance, starting on page 89, with a definition of abstract type animal, followed by a ‘normal’ type, mammal which is announced in the last paragraph before the example as derived (it seems that from animal). The definition of mammal type, however, has no reference to animal, only the module in which it is embedded, use-s animal module. When the module mammal_class is extracted from the code, with use animal_class commented out, it compiles cleanly.
I thought maybe the author just forgot to add extends(animal) to the type mammal but with this phrase added, compiler stops with the error:

   36 |   type, extends(animal) :: mammal
      |                                 1
Error: Derived-type 'mammal' declared at (1) must be ABSTRACT because 'print' is DEFERRED and not overridden

Any explanation would be welcomed. I am not including the code as I am not sure if it would be OK, so just the link to the full document.

Maybe it can be useful.
In “Modern Fortran explained” by Metcalf, section 15.10 pag 313:
The use of the abstract and deferred attributes ensures that objects of insufficient type cannot be created, and that when the abstract type to create a normal type, the programmer can expect a diagnostic from the compiler if he or she has forgotten to override any inherent deferred type-bound procedures

I had this very page of MFE opened on my desk. But the example on that page clearly shows the derived type (my_integer) is extending the abstract one (my_numeric_type).

I understand the error when extends (animal) is added to the Holcomb’s example but do not understand the idea of the types (mammal and all the other defined later which extend the mammal) if the mammal has no reference to the abstract type. Or, maybe, it does have but I don’t get how.

Output from Holcomb’s example pag 89

gfortran testabstract.f90 -o testabstract

Meow
Woof
Felis sylvestris catus
I am Itty,I am a tuxedo
I am Teddy, I am a poodle

GNU Fortran (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0

It looks like an erroneous example to me.

The mammal derived type, should be really declared with:

type, abstract, extends(animal) :: mammal
  ! ...
end type

As far as I can tell, the idea was that the print_species is implemented at the level of mammal, while the speak and print procedures are implemented at the child level, since they are specific to each animal.

You can see in the init_??? subroutines, that first a mammal instance is created using the structure-constructor:

type(cat) function init_cat(my_name,breed)
character(len=*), intent(in) :: breed
character(len=*), intent(in) :: my_name
init_cat%mammal=mammal('Felis sylvestris catus')  ! reference to the parent type
init_cat%name=my_name
init_cat%breed=breed
end function init_cat

TL;DR:

  • An extended derived type is “concrete” if it does not have the ABSTRACT attribute,
  • A “concrete” derived type must provide non-DEFERRED i.e., an overriding type-bound procedures of all the deferred bound procedures in the parent type i.e., actual implementations of any virtual methods stated in the base “class”
  • An extended type which is itself ABSTRACT can “defer” such “implementations” to its extensions (aka its children)!

Thus if you have

   type, abstract :: foo_t
      ..
   contains
      procedure(Ido_something), deferred :: do_something
      ..
   end type
   ..
   ! elided is abstract interface Ido_something

then a “concrete” extended type must override do_something:

   type, extends(foo_t) :: bar_t
      ..
   contains
      procedure :: do_something => bar_do_something
      ..
   end type
   ..

Thanks to all. The general idea, as given by @FortranFan, I do know (maybe with an exception of that there can be an extended abstract type). My question was more specifically directed to the example in which I did not find the connection between the abstract base and the derived type. As @ivanpribec stated, somehow confirming my doubts, the example is probably erroneous (in the sense of illustrating the use of abstract types), although, and that is also interesting, it compiles and works fine even without the reference to the abstract base, as shown by @alozada