- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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:
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
We don't tend to introduce deliberate extensions of this nature. It may just be a bug. We'll take a look - thanks.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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)
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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)?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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:
- 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.
- 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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I agree it's functionally fine, but if the rule is arguments must match exactly (excelpt class), it's not right (gfortran rejects it).
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Also ifort does not check the "intent"-ness of the dummy arguments, where gfortran does.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I am pretty sure we do track INTENT, but I will check this (and the other issue.)
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.

- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page