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

Programming Queries

dannycat
New Contributor I
4,163 Views
I've been struggling with an issue for about a week now with a program that I used to compile using CVF for the 32-bit Release Version and IVF 11.1.070 for 64-Bit Debug and Release versions.

At the end of last week my Visual Studio 6 died on my 64-bit XP Machine. I've tried reinstalling it but it still crashes everytime I try to open a file. The project can be selectedbutVS6 also crashes when loading project files.

CVF had been working quite happily on the same computer for over a year. The reasons Icontinued touse it were mainly because I prefer the resource editor in VS6 to that in VS2008 and the 32-bit application is the one used by my customers for over ten years.

For customers with 64-bit machines I have successfully used IVFand both variants of the program show consistency in their function and output etc.


My first thought was to simply finally drop CVF and switch to IVF for all versions (something I meant to do anyway so that new features can be adopted in the future releases) but unfortunately the IVF 32bit Release compiled version of my program crashes.

32bit debug, 64bit release & debug versions all seem to work fine. I uselots of allocatable user defined types that have allocatable components and it is during deallocaton of one of these that the program crashes. I identifid the cause by adding write statements at various places within the code.

During my debugging sessions (on release configuration) I came across a few dodgy programming quirks that I would like to discuss.

For all dtat types I write a module that manages all instances within the program. The module contains routines to allocate, copy instances (using operator module procedure), increase/decrease size (not using reallocate) and deallocate.

1) Is using the UBOUND function on an unallocated array or data structure a safe thing to do? It returns 0 which suggests it works but maybe inconsistent on other computers.

2) What does allocating something ALLOCATE (x(0)) actually mean? This caused problems on CVF as the ALLOCATED(x) result would have been .false. but DEALLOCATE(x) would crash the program. I have no suchdeclarations in my code that I am aware of. ShouldIVF allow this.

3) In order to initialise a data structure I have a single instance of the structure with all components initialised as required. In the case of allocatable components I allocate to a minimum size. I then use a single statement to initialise the whole array. eg

type DATAT
integer :: b
real ::a
end type

type(DATAT),allocatable :: x(:)
type(DATAT) :: x0

x0%a = 0.0
x0%b = 0

allocate(x(100)) ! Line XX
x = x0

However if for some reason you don't firstly allocate x, eg by removing Line XX the program still allows the
x = x0. Is this correct?

4) optional arguments.Should the following statement be allowed? If not could either compiler or run time error be issued?

subroutine XXX(a,b)

integer,intent(in) :: a
integer,intent(in),optional :: b

if(present(b).and.b.gt.0) then

! Do something

else

! Do something else

endif

Although I've not got a solution to my problem yetI thought I'd bring these to everyone's attention for comment.

Any further advice would be much appreciated.

Steve

0 Kudos
29 Replies
IanH
Honored Contributor III
3,117 Views
Quoting dannycat
1) Is using the UBOUND function on an unallocated array or data structure a safe thing to do? It returns 0 which suggests it works but maybe inconsistent on other computers.

No. F2008 13.7.171: ARRAY (the argument to UBOUND) ... shall not be an unallocated allocatable array or a pointer that is not associated.

The processor isn't required to diagnose this, but with ifort using /check:all I'd expect a runtime error.

2) What does allocating something ALLOCATE (x(0)) actually mean? This caused problems on CVF as the ALLOCATED(x) result would have been .false. but DEALLOCATE(x) would crash the program. I have no suchdeclarations in my code that I am aware of. ShouldIVF allow this.

It allocates an array of zero size. Note the array is still allocated (just as if it was allocated with a non-zero size) - the behaviour of CVF that you describe would be incorrect.

Zero size arrays are standard conforming (F2008 6.7.1.2). I use them quite a bit in IVF.

3) In order to initialise a data structure I have a single instance of the structure with all components initialised as required. In the case of allocatable components I allocate to a minimum size. I then use a single statement to initialise the whole array. eg

type DATAT
integer :: b
real ::a
end type

type(DATAT),allocatable :: x(:)
type(DATAT) :: x0

x0%a = 0.0
x0%b = 0

allocate(x(100)) ! Line XX
x = x0

However if for some reason you don't firstly allocate x, eg by removing Line XX the program still allows the
x = x0. Is this correct?

No. F2008 7.2.1.3: If the variable (left hand side of the assignment) is an unallocated allocatable array, expr (the right hand side) shall have the same rank (in your example the expr is a scalar (rank zero) while x is rank one).

The processor isn't required by the standard to diagnose this (but ifort should with /check:all).

Note (with the /standard-semantics flag) that x = [x0] (i.e. the right hand side is an array expression) is ok if x is unallocated - in that case x will be automatically allocated to a size one array for you, with the value of the single element being x0. You can use array constructors and intrinsics like SPREAD/RESHAPE/etc to allocate and assign more complicated examples in one step (personally I wouldn't bother here - I'd just stick with the allocate then assign that you have, but sometimes it can be convenient).



4) optional arguments.Should the following statement be allowed? If not could either compiler or run time error be issued?

subroutine XXX(a,b)

integer,intent(in) :: a
integer,intent(in),optional :: b

if(present(b).and.b.gt.0) then

! Do something

else

! Do something else

endif

The code doesn't conform (assuming a call is made without b present). The order of evaluation of the operands to the .AND. operator in the expression in the if statement is not specified. The compiler could evaluate the b .GT. 0 part before the PRESENT part (if it was really clever it could evaluate them in parallel - it could also use a coin toss to work out which one to evaluate first (the order could change from run to run)). If an optional argument isn't present then you can't reference it in that way (F2008 12.5.2.12 - basically all you can do is use the PRESENT intrinsic or pass it on to another procedure as an optional argument).

The processor isn't required to diagnose this. If it did evaluate the b .GT. 0 bit before PRESENT, then with ifort with /check:all I think you'd get a runtime error message about an NULL pointer access.

Note that because it uses optional arguments XXX would need to have an explicit interface (it needs to be a module procedure or have an INTERFACE block, etc) - F2008 12.4.2.2.
0 Kudos
dannycat
New Contributor I
3,117 Views
Thanks Ian,

Your replies were a great help. I hadn't used the check:all directive (only check:bounds). Now when I changed to check:all it has thrown up some interesting things:

1) It complains when usingNULL_RECT in the Validate and Invalidate API functions. I now have to obtain the actual window Rect and pass this instead. What is wrong with using NULL_RECT as documentation states that this implies the full window?

2) Whenever I read an array section like:

real :: array(100,8)

do i = 1 , 100
read(66) array(i,1:8)
enddo

I get a warning about a temporary array being created. I know I can turn this check off but in what circumstances would you need to use it?

Steve
0 Kudos
TimP
Honored Contributor III
3,117 Views
Quoting dannycat


2) Whenever I read an array section like:

real :: array(100,8)

do i = 1 , 100
read(66) array(i,1:8)
enddo

I get a warning about a temporary array being created. I know I can turn this check off but in what circumstances would you need to use it?


A purpose of the temporary array warning is to help you judge inefficiencies. You might argue that the inefficiency is obvious in this case, where you convert a group of 8 elements, then require an entire cache line to be updated for each move of an element from the temporary to the declared array. If the array were too large for L1D cache locality, it could be a significant performance issue.
0 Kudos
mecej4
Honored Contributor III
3,117 Views
If you execute the segment of code that reads unit-66 only a couple of times, the warning can be ignored. On the other hand, if you read many files, or read a large file, the following considerations should be noted.

If you want to read the whole array with unformatted reads, it would be much more efficient to use instead of your DO loop the statement

READ(66) array

However, the data file would have to be rewritten to match this READ statement.

If you wanted to fill only a part of the array, it would be more efficient to read by columns, for example:

DO J = 1, 7
READ(66) ARRAY(i,j), i=1,80)
END DO

Again, the data file would have to be rewritten.
0 Kudos
John_Campbell
New Contributor II
3,117 Views
A good way to initialise a data structure is to also declare a paramater type that is zero or initial values as you declare the TYPE structure. I'm not sure if the SEQUENCE attribute is also required or is a default for the compiler that I use.
For example:

type DATAT
integer :: b
real :: a
end type
type (DATAT), parameter :: DATAT_zero = DATAT (0, 0.0)
!
type(DATAT),allocatable :: x(:)
type(DATAT) :: x0
!
x0 = DATAT_zero
!
allocate (x(100)) ! Line XX

x = DATAT_zero

This is a clean way of defining the initial setting, as you define the TYPE structure. Other named settings for a type structure can also assist in documenting their use.

John

0 Kudos
Steven_L_Intel1
Employee
3,117 Views
If you're doing a derived type, you can put the initialization right in the type declaration. For example:

type DATAT
integer :: b = 0
real :: a = 0.0
end type

Now everything of that type will be initialized.

This usage has an interesting and unique (to the language) side effect. Anyone know what it is?
0 Kudos
acar
Beginner
3,117 Views
No I don't but I'd be interested to know!
0 Kudos
Steven_L_Intel1
Employee
3,117 Views
I'll give it another day to see if anyone comes up with it.
0 Kudos
mecej4
Honored Contributor III
3,117 Views
I'll try: unlike initializations given in variable declarations or DATA statements, initialization of some or all components in the TYPE definition need not imply SAVE?
0 Kudos
Steven_L_Intel1
Employee
3,117 Views
That's not exactly what I was thinking of, but it is related and is interesting in its own right. I'll give a hint - it has to do with dummy arguments of such a type.
0 Kudos
mecej4
Honored Contributor III
3,117 Views
This test program is founded on your hint, and shows that a variable of such a type, when passed to a subroutine which has the corresponding dummy declared INTENT(OUT) receives the default initialization. Without default initialization, components of such variables become undefined upon entry to the subroutine.

[fortran]program tease
  type DATAT
    integer :: b = 1
    real :: a = 2.0
  end type

  type(DATAT) :: v

  call sub(v)
  write(*,10)v%a, v%b
  10 format(' a = ',F8.4,'  b = ',i3)
  stop

  contains

  subroutine sub(w)
  type (DATAT), intent(out) :: w
  w%a = 3.0              ! Note w%b set by def. initialization
  return
  end subroutine sub

end program tease
[/fortran]
0 Kudos
Steven_L_Intel1
Employee
3,117 Views
Yep, that's it. It's the only case where a dummy argument with INTENT(OUT) does not start out in an undefined status. I will offer the caveat that this applies to non-pointer dummy arguments only.

What's more interesting is that this initialization will occur on every call to the procedure.

But what's maybe more interesting, and I had forgotten about this myself, is that it also applies to local variables of such a derived type. Unlike "static initialization", the components of such variables will get re-initialized on each call.
0 Kudos
John_Campbell
New Contributor II
3,117 Views
I would suggest that the use of derived type parameters, such as DATAT_zero or DATAT_one provide a moretransparent way of initialising derived types.

Your question prompted me to look at the changes to Fortran since 1995. There is a lot of non-fortran in 2003 !

John
0 Kudos
Arjen_Markus
Honored Contributor II
3,117 Views
What do you mean by "non-fortran"?

Regards,

Arjen
0 Kudos
John_Campbell
New Contributor II
3,117 Views
Arjem,

Steve's question sent me looking at the 2003 standard and then I reviewed an article "The New Features of Fortran 2003" by John Reid.
I have been using Fortran continually since 1973 and I'dexpect that other programmers of my vintage would find it difficult to appreciate where these new features in 2003 can be used. I think more in terms of numerical computation, such as equation solutions and least squares analysis of data sets. I typically choose a precision suitable for a numerical approach and use subroutines to handle different data structures. I don't think in terms of pointers and now procedure pointers. While I've read the term "polymorphic", I have little understanding of it or less idea of how and why I'd use it. In short I don't understand why these developments have taken place. I don't understand where Fortran is going. It doesn't appear to make my work any easier.
The problem I have is I do good work and it is of benefit in the industry I work. With the apparent emphasis in fortran development, I don't see any attention to the types of fortran usage I do and don't see new engineers educated with the type of skills I have. In 10 years who will be able to do this type of work, as polymorphic entries aren't very relevant to the numerical computation I do. Maybe the solution is they'll all use Matlab and hope the codes don't get lost. More likely, the work I do will be done differently in the future.
There have been problems in the ease of use of fortran on new types of hardware but the areas of progress don't appear to address these problems, such as the graphics interfaceand the use of new style multi-processors. (My recent attempt to use parallelization with ifort Ver 11 was not a success, althouigh Ver 12 may be better.)
To me, Fortran development has been sidetracked by non-fortran users. It's probably our own fault for not being more involved. Those who have progressed the language have put in a lot of effort. It now looks a lot more like C.. and probably with all the bugs that complex code structures provide. The KISS principal, which I was taught a long time ago appears to have been lost.

John
0 Kudos
Arjen_Markus
Honored Contributor II
3,117 Views
I have been using Fortran since 1982 or thereabouts, but I have also been using several other
programming languages. I can sympathesize up to a degree with your feelings about Fortran 2003
but on the other hand I do appreciate these new features too.

One possibility that Fortran 2003 offers that can not be so easily solved in previous versions is
designing library interfaces. Metcalf, Reid and Cohen give an example of this. It takes a bit of
getting used to perhaps, but it does help to have type-bound procedures in that case.

You also touch upon a wider issue - the whole matter of numerical computations."Computing" today
involves much less numerical computing than it does constructing web pages with a nice look and
feel. But that is a different story than the original subject of this thread.

Regards,

Arjen

0 Kudos
mecej4
Honored Contributor III
3,117 Views
John, it is not at all unusual to be put off by unfamiliar features in a programming language and to have misgivings about the cost/benefit aspects of learning and using those new features in one's work.

Fortran, however, leaves you in control of change. You can (with almost no exceptions) use the subset of Fortran 2xxx that is the same as Fortran 77 (less a few deprecated features). If you wish, and only if you do, you may gradually introduce some of the new features, after you have convinced yourself of the benefits, and at your pace.

Here are two related features that I have found very useful in removing bugs in Netlib code (those are rare, but when they exist they can be nasty). That they exist, even in code written by highly competent mathematicians, show that Fortran-77's reliance on the programmers to do the right thing was not quite justified.

Feature 1: Declaring and verifying INTENT of subroutine and function arguments.

Feature 2: Abstract procedure declarations. ANSI C provided the corresponding capability in 1978. We have had to wait until F2003 for this feature, which is essential if we are to write robust, reusable libraries of numerical methods. Consider as an example a nonlinear equation solution routine:

Call Solver(Func,x,Iter,Eps)

which you want to call with different Func-s at different times. In Fortran-77, you specify Func as EXTERNAL, which allows the compiler to recognize that Func is not just another variable and needs to be processed by the linker. However, there is no way in Fortran-95 to check that a specific Func that you use in this call has the correct type and argument list, unless you write an interface block for every instance of Func that you wish to use. Errors in function result type are easy to detect and fix, but errors in arguments are notoriously difficult to remove.

In contrast, ANSI C lets you write

extern void Solver(double (*Func) (const double), double *x, int *Iter, double Eps));

You can see a short example of how abstract procedure declarations let you do the equivalent in Fortran 2003 in this recent thread: "Compiler error - 12.0 Update 2".
0 Kudos
Arjen_Markus
Honored Contributor II
3,117 Views
mecej4,

I do not quite agree here:

subroutine solver( func, x, iter, eps )
real :: x, eps
integer :: iter

interface
real function func(z)
real :: z
end interface

...
end subroutine solver

is perfectly (modulo typos) acceptable Fortran 90.

It gets nastier if you need to pass arbitrary data to the function - then type-bound procedures
as I referred to get interesting.

Regards,

Arjen
0 Kudos
mecej4
Honored Contributor III
3,117 Views
Things are, indeed, fine at the receiving end of the CALL, which is the part that you showed.

Suppose you have three or four calls to Solver with different functions that are also in different source files than the line in which the call is made. How do you ensure that every one of these functions has the correct interface, even if all that we have are implicit interfaces, without using compiler features such as the /warn of IFort?
0 Kudos
John_Campbell
New Contributor II
3,040 Views

mecej4 and Arjen,

Thanks for your comments.
I like to think there is still a need for numerical computation.
Certainly the size and speed of Fortran computation allows for more accurate modelling.
The vast range of computational techniques available in alternatives like Matlab and even Excel is impressive. Fortran still allows for investigating the model assumptions.

You have highlighted two features, which I think date back to F90.
I find the use of INTENT and INTERFACE more useful for documentation, rather than assisting in providing bug-free code.
To me INTERFACE fails, as it merely documents the expected routine.
There is no guarantee it agrees with the latest version of the routine being called, especiallywhen developing code.
I've wanted the INTERFACE definitions to beincluded in a module, which can also be included in it's own routine.Rather than an error, the interface should be checked against the routine's declaration or better still, used as the routine's declaration. That way, similar to a .lib file, the compiler could check the use of each routine.
INTERFACE is a duplicate definition. If you change the routine, then you have to search all the code that could possibly call it.
Contrast this to MODULE which allowed a single definition of variables where previously INCLUDE/COMMON required dual definition. This has removed a significant source of obscure errors.
Duplication of code definition is a major source of errors when developing software. INTERFACE should only be allowed in a MODULE, rather than being declared each time the routine is called.MODULE was a significant advance at F90.

The number of subroutine arguments has always been a significant source of error. I'm not sure that INTENT has addressed this effectively.
It's a shame a simple argument count check has never been included for run time or linking. I know there are times it can not be used, but they are not the norm.
The call argument count should be checked against the routine at link or run time.
INTENT also has some side-effects as shown by the main part of this thread, especially if OUT is chosen.
Selecting INTENT (IN) or (OUT) often can highlight cases where it is not intended, with the need to revert to INTENT (IN OUT) and no significant improvement in the code.

My experience has shown Fortran to be easier to develop near bug-free code. My past performance has always been faster delivery of effective code, in comparison to other software sectors.
I'm always amazed at the amount of bug-riddled code there appears to be elsewhere, although publicly available packages can suffer from an unfriendly user-base.

As to the use of an EXTERNAL function as a subroutine argument, I last did that in an assignment in 1973. I prefer the use of an argument option to select from a range of explicitly coded functions, now via a SELECT CASE structure. I have access to all the code so don't need the flexibility.

John

0 Kudos
Reply