Silverfrost Forums

Welcome to our forums

Compiler capability

11 Jan 2019 10:17 #23072

I use the compiler to help keep me honest. I collect all the routines and MAIN's in one file, and compile it. This let's me check for missing arguments, incorrect data types, etc.

I use a lot of TYPE's. So imagine the surprise when I ran a CHECKMATE version of my SW and it discovered, at run-time, a mistake in the TYPE being passed as an argument. My compile step to let the compiler check for me was completely error free. I ran some possible examples through PLATO and the compiler will detect some errors, but not across the module boundaries.

On reflection, it made sense to me that a new data type would be more difficult to detect as an error as if it were one of the elemental data types. On further reflection, though, I said to myself 'Well, if the incorrect type can be found at run-time, then all the information to make this comparison is available at compile time.'

So, I wondered if there were other's who might have a similar need to mine and would be in favor of some additional compile time checking. If that is at all possible. Or, is there another way I'm missing?

11 Jan 2019 11:46 (Edited: 12 Jan 2019 12:13) #23073

The compiler can check an actual argument list for correct types if it has available to it a complete interface in the same program unit as the one containing the call, or if the subprogram heading and dummy argument declarations are available in the same file. I feel that the description you have given is rather broad, and I should prefer to see actual code that does what you described.

Consider this example, with arguments being passed in the wrong order.

program tst
implicit none
integer :: i, j
real :: x,y
i = 2
j = 3
x = 1.5
call sub(i,x,j,y)
end program

subroutine sub(i,j,x,y)
implicit none
integer :: i,j
real :: x,y
if(i /= j)then
  y = x
else
  y = -x
endif
return
end subroutine

The compiler warns about the error at compile time and, if you link and run the program despite the warning, aborts with a message at run time.

Had the call said 'call sub(i,j,y,x)', no compile time warning would be possible. Even run time detection would not be possible unless INTENTs were provided for the dummy arguments.

However, if the two program units are placed in separate files, no compile time warning is possible.

Please show the code.

12 Jan 2019 12:11 #23074

Yes, this works for intrinsic types. The following code does not generate a compile time error, but will throw an exception when compiled/run as CHECKMATE.

!    PROGRAM SIMPLE
	PROGRAM MAIN 
	TYPE:: AAA
	INTEGER:: B=0
	END TYPE AAA
	TYPE:: BBB
	REAL:: B=0
	END TYPE BBB
	TYPE (AAA):: TYPE_A
	TYPE (BBB):: TYPE_B
	!CALL DEFG(TYPE_A,TYPE_B)
	CALL DEFG(TYPE_B,TYPE_A)
	END
	SUBROUTINE DEFG(AA,BB)
	TYPE:: AAA
	SEQUENCE
	INTEGER:: B=0
	END TYPE AAA
	TYPE:: BBB
	SEQUENCE
	REAL:: B=0
	END TYPE BBB
	TYPE (AAA):: AA
	TYPE(BBB):: BB
	AA%B = BB%B
	RETURN
	END
12 Jan 2019 12:38 #23075

I expected the following code would fail with FTN95, but it compiled with no errors using ver 8.20.0 ? gFortran warns of a possible error. This is my typical approach to address this problem. ! PROGRAM SIMPLE module my_types TYPE:: AAA SEQUENCE INTEGER:: B=1 END TYPE AAA TYPE:: BBB SEQUENCE REAL:: B=2 END TYPE BBB end module my_types

   PROGRAM MAIN 
   use my_types
   TYPE (AAA):: TYPE_A 
   TYPE (BBB):: TYPE_B 
   !CALL DEFG(TYPE_A,TYPE_B) 
   CALL DEFG(TYPE_B,TYPE_A) 
   write (*,*) type_a%b, type_b%b
   END 

   SUBROUTINE DEFG(AA,BB) 
   use my_types
   TYPE (AAA):: AA 
   TYPE(BBB):: BB 
   AA%B = BB%B 
   RETURN 
   END  
12 Jan 2019 12:53 #23076

Curioser and curioser!

Even with CALL DEFG(TYPE_A,TYPE_B) instead of CALL DEFG(TYPE_B,TYPE_A), the first program that you showed would be in error unless the SEQUENCE attribute is included in the derived type definitions. In that case, FTN95 /checkmate does not detect the error even at run time.

12 Jan 2019 1:31 #23077

I removed the SEQUENCE just to make it shorter. That made no difference anyway. I don't expect these kinds of examples to necessarily produce good results. Just illustrate an error of one type or another.

That said, John, the MODULE is fine, but the issue I'm working is the call to DEFG with the arguments reversed. Not solved by the MODULE. However, an INTERFACE will solve the problem. I implemented that in one of the complex routines just to make sure I got the arguments TYPEd right in each of the calls...

I had expected the compiler to complain, and it didn't, so I went away doing more work, thinking all was well. In my case, SEQUENCE is used, and the desired TYPE was first up (nested) in the data structure that was being passed. And was /RELEASE, so no errors. It wasn't until I had to run a /CHECKMATE version of the code that the error was revealed.

12 Jan 2019 3:47 #23078

Interesting, intrinsic types appear to be tested but not derived types; based on the following example extension ! PROGRAM SIMPLE module my_types TYPE:: AAA SEQUENCE INTEGER:: B=1 END TYPE AAA TYPE:: BBB SEQUENCE REAL:: B=2 END TYPE BBB end module my_types

   PROGRAM MAIN 
   use my_types
   TYPE (AAA):: TYPE_A 
   TYPE (BBB):: TYPE_B 
   integer :: aa=1
   real    :: bb=2
   !CALL DEFG(TYPE_A,TYPE_B) 
   CALL DEFG(TYPE_B,TYPE_A) 
   write (*,*) type_a%b, type_b%b
   CALL Dintrinsic (bb,aa) 
   write (*,*) bb, aa
   END 

   SUBROUTINE DEFG(AA,BB) 
   use my_types
   TYPE (AAA):: AA 
   TYPE(BBB):: BB 
   AA%B = BB%B 
   RETURN 
   END 

   SUBROUTINE Dintrinsic(AA,BB) 
   integer :: aa
   real    :: bb
   AA = BB 
   RETURN 
   END  
12 Jan 2019 8:10 #23080

Runtime checking for matching argument types can sometimes go wrong (giving false error reports or missing errors). Equally there are some really messy situations where FTN95 simply makes no attempt to check the type of the arguments.

I would need illustrative sample code in order to work out this particular case.

12 Jan 2019 3:32 #23085

Paul, I'm not all that concerned about run-time checking for individual elements of a TYPE. Whatever /CHECKMATE is doing for seeing if the name of the TYPE is the same between modules at run-time would satisfy many, if not most, occurrences.

I was more concerned about the compile time check. Again, not the individual elements of the TYPE, but even just the NAME of the type should be enough.

The example I gave illustrates that a compile time check is not performed. The fact that the run-time error is trapped would suggest that there is sufficient information at compile time to make that check.

If you need a more detailed example, I'd be happy to try and gen one up. I'd just need to know what kinds of examples you'd need. And if I am unable to do so, perhaps someone else can weigh in with examples that might help.

Bill

13 Jan 2019 8:26 #23087

Bill

Sorry but I missed the significance of your example.

The presenting fault is that the call to DEFG from the main program requires an interface for DEFG in the code. In other words the given code is not correct Fortran.

I have not tested this but if you include the required interface then everything should work as expected.

13 Jan 2019 10:26 #23089

Paul, thanks.

Yes, with the proper interface, the compiler does catch the error.

I had hoped that the compiler could/would without this, since it is in the same compilation module, similar to what happens when one has errors in intrinsic types.

In my example, if you change TYPE_A to INTEGER, and TYPE_B to REAL throughout, then compile it, the compiler will tell you that you had a difference in the argument types.

!    PROGRAM SIMPLE
	PROGRAM MAIN 
	INTEGER:: TYPE_A
	REAL:: TYPE_B
	CALL DEFG(TYPE_B,TYPE_A)
	END
	SUBROUTINE DEFG(AA,BB)
	INTEGER:: aa
	REAL:: bb
	aa=bb
	RETURN
	END

which generates the error message:

: warning 676 - In a call to DEFG from another procedure, the first argument was of type REAL(KIND=1), it is now INTEGER(KIND=3)

It is that warning that is 'good enough' for me to go check what the heck I did wrong!

13 Jan 2019 1:02 #23091

Bill

Strangely the converse might be better. Sometimes the fact that FTN95 remembers some things from one independent program/routine to another in the same file can cause problems and it might make more sense and be simpler for compilation process if this were not the case.

13 Jan 2019 4:09 #23092

Consider the following variant of Bill's test program:

PROGRAM MAIN
   TYPE:: AAA
      INTEGER:: B
   END TYPE AAA
   TYPE:: BBB
      REAL:: B
   END TYPE BBB
   TYPE (AAA):: TYPE_A
   TYPE (BBB):: TYPE_B
   TYPE_B%B = 2.5
   CALL DEFG(TYPE_A,TYPE_B)
   print *,TYPE_A%B

contains

   SUBROUTINE DEFG(AA,BB)
   TYPE:: AAA
      SEQUENCE
      INTEGER:: B
   END TYPE AAA
   TYPE:: BBB
      SEQUENCE
      REAL:: B
   END TYPE BBB
   TYPE (AAA):: AA
   TYPE(BBB):: BB
   AA%B = BB%B
   RETURN
   END SUBROUTINE

end program

The derived types AAA and BBB in the main program do not match the derived types of the same names in the contained subroutine, because the former ones (in the main program) do not contain SEQUENCE. For this program, Gfortran 6.3 says:

wah1.f90:11:27:

CALL DEFG(TYPE_A,TYPE_B)
                       1

Error: Type mismatch in argument 'aa' at (1); passed TYPE(aaa) to TYPE(aaa)

Even with /CHECKMATE, FTN95 compiles the program with no error messages and the resulting program outputs '2' as the result.

14 Jan 2019 5:59 #23093

Paul, I have experienced exactly that behavior a couple of years back. I wish I could remember what it was. Simple to just break the multiple modules into separate files, so I think that is what I did.

I'll be OK with whatever course of action (or inaction) you decide to take.

Bill

14 Jan 2019 7:26 #23094

mecej4

There may be a fault in gFortran. For me gFortran also gives an error report when SEQUENCE is omitted.

14 Jan 2019 1:49 #23098

Paul,

I am not sure if I interpret the Fortran 95 standard correctly, but the following excerpt, particularly in the last two sentences, seems to say that if SEQUENCE is not present, the types defined by two separate but otherwise identical TYPE definitions are not the same.

4.4.2 Determination of derived types

A particular type name shall be defined at most once in a scoping unit. Derived-type definitions with the same type name may appear in different scoping units, in which case they may be independent and describe different derived types or they may describe the same type.

Two data entities have the same type if they are declared with reference to the same derived-type definition. The definition may be accessed from a module or from a host scoping unit. Data entities in different scoping units also have the same type if they are declared with reference to different derived-type definitions that have the same name, have the SEQUENCE property, and have components that do not have PRIVATE accessibility and agree in order, name, and attributes. Otherwise, they are of different derived types.

Interpreting these rules is something that I found to be difficult because a derived type definition is said to constitute a separate 'scoping unit', in this part of F95 Art. 2.2:

2.2 Program unit concepts ... A program unit consists of a set of nonoverlapping scoping units. A scoping unit is

(1) A derived-type definition (4.4.1),

(2) A procedure interface body, excluding any derived-type definitions and procedure interface bodies in it (12.3.2.1), or

(3) A program unit or subprogram, excluding derived-type definitions, procedure interface bodies, and subprograms in it.

A scoping unit that immediately surrounds another scoping unit is called the host scoping unit.

14 Jan 2019 3:59 #23101

mecej4

Thank you for the clarification.

17 Jan 2019 11:01 #23118

One things that I found by accident is: If you have a TYPE as an argument to a function call, but that function call expects an intrinsic type (i.e. REAL), then the compiler will flag that as an error (certainly it should).

Another thing I found: For FTN95 intrinsic functions (i.e. FEXISTS@), I can specify the second argument (should be KIND=3) as KIND=2 and there is no compiler error/warning about this. Even if I put these two incarnations next to each other.

    ABCD = FEXISTS@('THIS IS A TEST',I4)
    ABCD = FEXISTS@('THIS IS A TEST',I2)

I found this because I had a failure at run-time where the I4 result was stored in an I2 variable. I'm having to go back to all my code to check since the FTN95 intrinsic functions take either KIND=2 or KIND=3 depending on the function.

And, I cannot define an INTERFACE to force the check because the functions are intrinsic, and the compiler quits with that as the error.

18 Jan 2019 8:34 (Edited: 24 Jan 2019 8:33) #23120

Bill

I think that FTN95 probably needs to be changed to allow users to provide interfaces for routines ending in @. In the meantime you can override the error message by using /IGNORE 1094 on the FTN95 command line.

program main
logical L
integer ercode
interface
logical function fexist@(path, error_code)
character(LEN=*) path
integer error_code
end function
end interface
L = fexists@('buildlog',ercode)
end

Correction: This fragment of code does not currently work as expected for routines ending with the @ symbol. I have added this topic to the wish list.

18 Jan 2019 1:15 #23122

Being able to specify interfaces would be quite helpful.

Some (all, perhaps?) of the FTN95 non-standard intrinsics appear to allow '$' at the end of their names instead of '@', but there are some inconsistencies. The documentation should clarify this matter.

The following program works fine with /64, but linking fails in 32-bit mode, saying that FEXISTS$ is a missing symbol.

program xintrin
implicit none
logical :: fex, fexists$
integer :: e1, e3
!
fex=fexists@('xin.f90',e3)
print '(1x,L1,1x,I10)',fex,e3

fex=fexists$('xin.*',e1)
print '(1x,L1,1x,I10)',fex,e1
end
Please login to reply.