Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.

Procedure inheritance rules in Fortran 2003/2008 for class(*)

antony
Beginner
4,552 Views

The code below compiles in ifort. Accoring to at least one interpretation on the Stackoverflow question it is invalid, but ifort does not produce F2008/F2003 compliance warnings. Is intel's opion that this is correct, that it is an extension, or is it a bug? (if it is deliberate it is useful, but would also be nice to be able to get a warning as it doesn't work in gfortran, and know what the exact rule being applied is).

module classes
Type AData
end Type

Type A
contains
procedure :: Work
end type

Type, extends(AData) :: BData
end Type

Type, extends(A) :: B
contains
procedure :: Work => Work2
end type

contains

subroutine Work(this, D)
class(A) :: this
class(*) :: D
end subroutine

subroutine Work2(this, D)
class(B) :: this
class(BData) :: D
end subroutine

end module classes
0 Kudos
23 Replies
FortranFan
Honored Contributor III
3,818 Views

Very good question.  To me, this looks like a bug in Intel Fortran compiler relative to Fortran 2003/2008 standard.  Do you have standards checking on when you compile the code (/stand:f08)?

By the way, are you using the latest version, version 14.0, SP1, Update 2 that was released earlier this month?  If not (and if you can upgrade to this version), you should try it out with it since it seems to contain certain enhancements and fixes related to polymorphism.  But do consider getting this reviewed by Intel Premier Support also.

Here's the wording from the Intel Fortran standard:

Type-bound procedure overriding_0.png

Per line 13 of the above-mentioned section 4.5.7.3 in the standard, the dummy argument D in your Work procedure must have the same characteristics.  But in my reading, they are clearly two different types since CLASS(*) can be anything (INTEGER, REAL, CHARACTER, CLASS(AData), etc.) which can be distinguished from CLASS(BData) in a SELECT TYPE construct.

0 Kudos
Steven_L_Intel1
Employee
3,818 Views

We don't tend to introduce deliberate extensions of this nature. It may just be a bug. We'll take a look - thanks.

0 Kudos
antony
Beginner
3,818 Views

Thanks. There's also the related case

subroutine Work2(this, D)
class(B) :: this
class(*), target :: D
class(BData), pointer :: B

B=>D    
end subroutine

which also compiles in ifort but not in gfortran. (see updated stackoverflow question; I personally think this should be correct but with optional warnings)

0 Kudos
IanH
Honored Contributor III
3,819 Views

Does the BData type have the BIND or SEQUENCE attribute?  If not, the pointer assignment is not legal code, and the compiler should diagnose it.  If the BData type does have the BIND or SEQUENCE attribute, then the compiler should complain about your use of it in a polymorphic declaration type specifier.  Either way - this should not compile.

(A diagnostic isn't required for the argument mismatch case, but it would be polite for the compiler to issue one, given I think it always (?) has all the information it needs to see there's a mismatch to hand.)

The type system is there to protect you.  Don't try and fight it.

0 Kudos
FortranFan
Honored Contributor III
3,819 Views

antony wrote:

...

[fortran]

    ...

    class(*), target :: D

    class(BData), pointer :: B

    ...

    B=>D

[/fortran]

...

I'd think the above code structure, of an unlimited polymorphic target in a pointer assignment, would be invalid under any circumstances.  So I'm surprised the compiler doesn't raise that as an error.  One for Intel compiler folks to think about.

0 Kudos
FortranFan
Honored Contributor III
3,819 Views

IanH wrote:

..

A diagnostic isn't required for the argument mismatch case, but it would be polite for the compiler to issue one, given I think it always (?) has all the information it needs to see there's a mismatch to hand.

...

Isn't the issue one of consistency?  If the TBP in the parent type had any other declaration other than unlimited polymorphic type, say integer :: D or class(adata) :: D, then I'd think the compiler would issue a diagnostic about the mismatch with the overriding procedure in the child (unless some compilation flags can be invoked to suppress it).  So shouldn't it do the same with class(*) :: D declaration (assuming same compilation flags are used)?

0 Kudos
Steven_L_Intel1
Employee
3,819 Views

The case in the original post has been escalated as issue DPD200253724. I agree that ifort should not accept this case.

I am looking at the second issue now.

0 Kudos
Steven_L_Intel1
Employee
3,819 Views

The case with pointer assignment is now issue DPD200253728. Ian correctly identifies that this code violates constraint C715 in F2008, which means that the compiler is required to have the ability to diagnose the violation.

I do note that we DO detect the case where D has TARGET in Work2 but not in Work.

0 Kudos
antony
Beginner
3,819 Views

Thanks for checking, that's a pity! Beware that if you roll this out in a patch it will break some published code - e.g. my CosmoMC program (responsible for probably dozens of ifort sales over the last year..) has used class(*) for convenient casting of procedure arguments from base classes for a while now, and there are also third-party published likelihood class extensions from recent data which will also be incompatible if this is disallowed.

I'd just assumed it was a useful feature...  to make it pedantic standard compliant I'll have to introduce several intermediate up-casting procedures with up to three levels of nested select types. I guess this is a general issue for factory-model oop implementations in any language with strong type casting, where many functions operate on several specific types that are manufactured. The trouble is fortran doesn't have any equivalent of generics to make it easier.

0 Kudos
Steven_L_Intel1
Employee
3,819 Views

Given that gfortran already complains about this usage, I'm not terribly worried about published code breaking.

What you want might be TYPE(*) from the "Enhanced C Interoperability" Technical Spec TS29113, to be included in Fortran 2015. But this isn't implemented yet.

0 Kudos
FortranFan
Honored Contributor III
3,819 Views

antony wrote:

Thanks for checking, that's a pity! Beware that if you roll this out in a patch it will break some published code - e.g. my CosmoMC program (responsible for probably dozens of ifort sales over the last year..) has used class(*) for convenient casting of procedure arguments from base classes for a while now, and there are also third-party published likelihood class extensions from recent data which will also be incompatible if this is disallowed.

I'd just assumed it was a useful feature...  to make it pedantic standard compliant I'll have to introduce several intermediate up-casting procedures with up to three levels of nested select types. I guess this is a general issue for factory-model oop implementations in any language with strong type casting, where many functions operate on several specific types that are manufactured. The trouble is fortran doesn't have any equivalent of generics to make it easier.

Bugs happen unfortunately, but one fixes them and moves on.  But how can Intel ever be constrained based on code in circulation that might make use of bugs?

Thus far, you've not provided a convincing rationale for why you need to do what you're trying to do:

  1. The second issue re: your use of unlimited polymorphic target is clearly fraught with danger and no one can make a case that Intel compiler should continue allowing it.
  2. As to your first issue, on your posting at StackOverflow.com, a reader (francescalus) provided a nice workaround using Fortran 2003 GENERIC capability.  Did you try it?  What is wrong with that approach?

"a general issue for factory-model oop implementations in any language with strong type casting, where many functions operate on several specific types that are manufactured. The trouble is fortran doesn't have any equivalent of generics to make it easier." - I don't agree: this is too much of a leap in argument, difficult to buy since you're not making use of available generics options or stating why you can't use them.  I find one can go far with most OOP design patterns (GoF) with existing Fortran 2003/2008 capabilities, even if the Fortran implementations require some extra coding compared to, say, C++ or Java.  A separate thing to consider: many of these design patterns are not always the best approaches or the most efficient way to design code.  The value of these patterns is more in getting one to think differently, often in an educational setting, as opposed to providing a set blueprint for real code.

 

0 Kudos
antony
Beginner
3,819 Views

It should certainly be fixed to give an error or at least warning, but likewise it is sensible to warn people if changes will break existing code. (gfortran is unfortunately not quite yet usable in many instances)

Using fortran generics does not help if you call the procedures from a scope with only base class types (the test call from my comment in stackoverflow does not work). [by "generics" in my post I meant generic programming not fortran generic procedudes: class<T1, T2> and similar in other languages, which I think helps with this sort of issue in Java, C#, Delphi, etc.]

Another reason for somtimes having class(*) arguments in the base class here is the nasty problems with circular module dependencies. That at least should be mostly resolved if any compilers ever implement fortran standard submodules. Interesting possibility from type(*) if it's actually allowed in inherited procedures in this way.

0 Kudos
Steven_L_Intel1
Employee
3,819 Views

We have always felt it appropriate to add diagnostics for incorrect code. We can't always predict what sort of existing code will encounter new diagnostics, but we're glad to help customers understand the issue. In most cases they are happy to have the compiler reveal errors in their code, even if it means having to rewrite some things. Just because something compiled without errors in older versions, that doesn't mean that the code actually did something correct.

0 Kudos
antony
Beginner
3,819 Views

Incidentally, there are other non-class(*) argument checks that are also missed, e.g. it accepts this oddity

module classes

implicit none
    
    Type AData
    end Type

    Type, extends(AData) :: BData
    end Type

    Type A
    contains
    procedure :: Work
    end type

    Type, extends(A) :: B
    contains
    procedure :: Work => Work2
    end type

    contains

    subroutine Work(this, D)
    class(A) :: this
    class(BData)  :: D

    end subroutine

    subroutine Work2(this, D)
    class(B) :: this
    class(AData) :: D

    end subroutine

    end module classes

 

0 Kudos
FortranFan
Honored Contributor III
3,819 Views

antony wrote:

Incidentally, there are other non-class(*) argument checks that are also missed, e.g. it accepts this oddity

module classes

implicit none
    
    Type AData
    end Type

    Type, extends(AData) :: BData
    end Type

    Type A
    contains
    procedure :: Work
    end type

    Type, extends(A) :: B
    contains
    procedure :: Work => Work2
    end type

    contains

    subroutine Work(this, D)
    class(A) :: this
    class(BData)  :: D

    end subroutine

    subroutine Work2(this, D)
    class(B) :: this
    class(AData) :: D

    end subroutine

    end module classes

 

I don't see anything wrong with the above - I think the compiler is correct in this case.

I think this is a classic case of polymorphism.

Since Adata is the parent of Bdata, D in Work2 can be considered to have the same characteristics as D in Work.  The other way around will be a problem i.e., if D in Work were to be declared as AData while declaring D as BData in Work2.

0 Kudos
antony
Beginner
3,819 Views

I agree it's functionally fine, but if the rule is arguments must match exactly (excelpt class), it's not right (gfortran rejects it).

0 Kudos
antony
Beginner
3,819 Views

Also ifort does not check the "intent"-ness of the dummy arguments, where gfortran does.

0 Kudos
FortranFan
Honored Contributor III
3,819 Views

antony wrote:

Also ifort does not check the "intent"-ness of the dummy arguments, where gfortran does.

If you can provide an example (like above), that can lead to an escalation with a tracking incident number like the other two cases.

0 Kudos
Steven_L_Intel1
Employee
3,819 Views

I am pretty sure we do track INTENT, but I will check this (and the other issue.)

0 Kudos
Steven_L_Intel1
Employee
3,634 Views

The issue with INTENT is interesting. We do detect Work not specifying INTENT and Work2 specifying INTENT(IN). But if Work2 says INTENT(INOUT), we let that through and shouldn't - INOUT is not the same as not specifying INTENT. I have attached this and the different classes case to DPD200253724 as they're all part of the same problem.

0 Kudos
Reply