Silverfrost Forums

Welcome to our forums

An unexpected single step exception has occurred

9 Jun 2011 9:14 #8363

Has anyone ever seen this runtime error before? I haven't, and I have just started to get it this morning, with the /RELEASE build, after adding some code to a previously working app.

At the moment I can get no handle on the cause, because the /CHECKMATE and /DEBUG builds both fall over with an unknown access violation and display of assembler code. I only ran the /RELEASE build in desperation!

So, unusually, the /RELEASE build is 'less unhelpful' than the other two builds. I confess I am baffled as to why a /RELEASE build should be single stepping in the first place, unless it is using a different meaning of single stepping to the usual one i.e. just going about its normal sequential instruction business without supervision :roll:

9 Jun 2011 10:30 #8364

If you compile with /debug then run with sdbg, do you at least get a trace back ?

9 Jun 2011 10:58 #8365

Ho John,

No, I don't. I was running from within Plato before, but the same thing happens if SDBG is explicitly in control of the /DEBUG build. Just a window of assembler.

However, I have now found the line of offensive code. I'm still trying to figure out in a model applet how I managed to sneak it past the compiler. The best I can offer at the moment is this:

        program strung_out
        use knots
        character (len = nb) chintz, chance
        chintz = 'xyz'
        chance = chaout (chintz) ! the error is (mis)diagnosed here
        stop
        end program strung_out

        module knots
        integer, parameter :: nb = 3
        contains
        character (len = nb) function chaout (chainp)
        character (len = nb) chainp
        integer b, inp (nb)
        do b = 1, nb
          inp (b) = ichar (chainp (b)) ! the error is here
        end do
        chaout = char (inp (2)) // char (inp (3)) // char (inp (1))
        end function chaout
        end module knots

which causes FTN95 to identify a problem but misdiagnose both its location and nature:

strung_out.F95(5) : error 327 - In the INTERFACE to CHAOUT (from MODULE KNOTS), the first dummy argument (CHAINP) was of type CHARACTER(LEN=3) FUNCTION, whereas the actual argument is of type CHARACTER(LEN=3)

It is failing to pick up the fundamental syntax error that chainp (b) should be chainp (b: b), even though chainp is explicitly typed only three lines previously in the same function!

This misdiagnosis is clearly at the root of the original problem. I'm sure a little more wrapping should allow me to reproduce it. Watch this space 😉

Andy

9 Jun 2011 1:26 #8366

Right, time to declare 'victory', spent too long on reproducing this already.

The following code snippet sneaks the fundamental syntax error past FTN95

        program strung_out
        use knots
        character (len = nb) chintz, chance
        chintz = 'xyz'
        call charcoal (chance, chintz)
        stop
        end program strung_out

        module knots
        integer, parameter :: nb = 3
        contains
        subroutine charcoal (champ, chimp)
        character (len = nb) champ, chimp
        champ = chaout (chimp)
        end subroutine charcoal
        character (len = nb) function chaout (chainp)
        character (len = nb) chainp
        integer b, inp (nb)
        do b = 1, nb
          inp (b) = ichar (chainp (b)) ! the error is here
        end do
        chaout = char (inp (2)) // char (inp (3)) // char (inp (1))
        end function chaout
        end module knots

The resulting /CHECKMATE and /DEBUG builds reproduce the runtime problem as originally reported. The /RELEASE build behaves a little differently, reporting as follows:

*Access Violation The instruction at address 20202020 attempted to read from location 20202020

20202020 routine at address 20202020 [+0000] 00401000 KNOTS!CHARCOAL [+001e] 004010d0 MAIN [+003c]

eax=0360fc02 ebx=00003a49 ecx=0360fc97 edx=000000fe esi=00402010 edi=0360fd28 ebp=0360fca8 esp=0360fc4c IOPL=0 ds=0023 es=0023 fs=003b gs=0000 cs=001b ss=0023 flgs=00010a03 [CA OP NZ SN DN OV]*

**NB1 **if subroutine charcoal is listed after function chaout rather than before, all three builds misdiagnose the nature and location of the error in the same way as the previous code snippet. The order has to be as per the code above, to fool the compiler.

**NB2 **if chintz and chance are declared in module knots rather than program strung_out, the runtime behaviour is slightly different:

  • /CHECKMATE and /DEBUG builds display source code instead of assembler, and /DEBUG build reports privileged instruction rather than access violation
  • /RELEASE build reports access violation a little differently:

*Access Violation The instruction at address 00404018 attempted to write to location 00003a49

00404018 routine at address 404018 [+0000] 00401000 KNOTS!CHARCOAL [+001e] 004010d0 MAIN [+0048]

eax=0360fca9 ebx=00003a49 ecx=0360fca0 edx=000000fe esi=00402010 edi=0360fd28 ebp=0360fcb0 esp=0360fc54 IOPL=3 ds=0023 es=0023 fs=003b gs=0000 cs=001b ss=0023 flgs=00010a13 [CA OP NZ SN DN OV]

04040ff outs
00404100 outsb
00404101 cmpb ah,[eax]*

10 Jun 2011 2:06 #8367

Andy,

I tried to compile your program and found what I think is a problem with your program and with modules, which has been discussed previously.

Your second line has 'use knots', but the version of the knots module it will use will be the previous one compiled and not the one listed below. Later in your program, the module knots will be updated. If you first use the command 'del knots.mod' you will see the problem. I would suggest that the module should be compiled (listed) before it is used, as I don't think FTN95 does address this issue. I don't know if the standard requires the compiler to be able to cope with this.

You should try the following :

        module knots 
        integer, parameter :: nb = 3 
        contains 
        character (len = nb) function chaout (chainp) 
        character (len = nb) chainp 
        integer b, inp (nb) 
        do b = 1, nb 
          inp (b) = ichar (chainp (b:b)) ! the error is here 
        end do 
        chaout = char (inp (2)) // char (inp (3)) // char (inp (1)) 
        chaout = chainp(2:nb) // chainp(1:1)    !  doesn't this do the same ??
        end function chaout 
        end module knots 

        program strung_out 
        use knots 
        character (len = nb) chintz, chance 
        chintz = 'xyz' 
        chance = chaout (chintz) ! the error is (mis)diagnosed here 
        stop 
        end program strung_out 

John

10 Jun 2011 9:10 #8369

Hi John,

Hmm, yes, this rings a bell. When I compile my code I always have main program and modules in separate files, and I rely on Plato to get the compilation order right - so this issue would not arise for me, and it is not the source of the problem I am reporting.

Andy

10 Jun 2011 10:08 #8370

Andy,

I had to put the module first to get it to compile. Afterwards, I modified the code as follows:

       module knots 
        integer, parameter :: nb = 3 
        contains 
        subroutine charcoal (champ, chimp) 
        character (len = nb) champ, chimp 
        champ = chaout (chimp) 
        end subroutine charcoal 
        character (len = nb) function chaout (chainp) 
        character (len = nb) chainp 
        integer b, inp (nb) 
        do b = 1, nb 
          inp (b) = ichar (chainp (b:b)) ! the error is here 
        end do 
        chaout = char (inp (2)) // char (inp (3)) // char (inp (1)) 
        end function chaout 
        end module knots

        program strung_out 
        use knots 
        character (len = nb) chintz, chance 
        chintz = 'xyz' 
        call charcoal (chance, chintz) 
        write(*,*) chance
        stop 
        end program strung_out 

The main mod is in the line where you get an error (changed CHAINP(B) to CHAINP(B:B)). I didn't know that ICHAR was capable of operating on more than one char in a string at any one time (is it?), so I would automatically try to extract one char at a time. My WRITE statement tells me you get the right reorganisation if you do this. Using substring notation feeds ICHAR with only one character.

Eddie

10 Jun 2011 10:56 #8371

I suggested two alternative changes:

    do b = 1, nb 
      inp (b) = ichar (chainp (b:b)) ! the error is here 
    end do 
    chaout = char (inp (2)) // char (inp (3)) // char (inp (1)) 

or chaout = chainp(2:nb) // chainp(1:1) ! doesn't this do the same ??

I think both address the problem you had.

10 Jun 2011 11:52 #8373

Eddie (edit: and John, who snuck in while I was composing this),

Evidently I did not make my point(s) clear.

The code I posted is designed to reproduce a FTN95 compile-time oversight and associated bad run-time behaviour.

Point 1: Compile-time oversight. The line of source with the comment '! the error is here', and which you corrected, is a basic error of syntax that FTN95 is not picking up. It is meant to be there! In my original code, it was committed by me, in haste. The resulting executable code, which should not have been produced by the compiler, (not surprisingly) failed at runtime, and not gracefully.

Point 2: Bad run-time behaviour. The /DEBUG and /CHECKMATE executable code that FTN95 generates fails at runtime with access violation and without diagnosis or traceback. In both cases, these builds are less helpful than the /RELEASE build - not Silverfrost's design intention for these builds.

The compile order issue is a red herring - although I do find it disturbing that a given compiler can generate different executable outputs from a given portion of source code, depending on how the source code is packaged. Before we had modules, I used to have every subroutine and function in a separate file. Now we have modules, I always keep the main program and every module in a separate file. It is the only way that makes sense to me. I'm going to have to make that clear when posting code in future, with multiple uses of 'code' tags.

Andy

10 Jun 2011 1:46 #8374

Andy,

I assumed that you knew a syntax that I didn't! Agreed, if it isn't valid, then the compiler should pick it up, and also agreed, that you should get more help if you go for error-checking than if you leave it out.

It isn't even that the issue is with the Fortran-95 or -90 part of the code, it is something that has been there from Fortran-77, and agreed, it needs looking at. I had a go at removing the MODULE stuff, and the error isn't picked up even if you modify your example to look like Fortran-77.

Prior to MODULEs, every routine was self-contained and could be compiled stand-alone (EXTERNAL resolves the one ambiguity regarding whether or not a symbolic name was a FUNCTION when used as a subprogram argument - it is evident from context in an assignment statement that it is either an undeclared array or a function, and that is resolvable during linking). With MODULE and USE, one may need the contents of a module to do a compilation, which I suppose is the reason for those MOD files, as the source code could be named anything, and finding it could be difficult.

This is a degree of complexity that I had not thought about before. Not having the MODULE to hand is like (in a Fortran-77 context) not finding an INCLUDE file, and everything may not be resolvable at link time as it used to be.

Eddie

11 Jun 2011 12:02 #8375

Andy,

My apologies. I now see the problem you have identified. Certainly the error does not report where the syntax problem is. I did look at the cryptic error report and tried to find how CHAINP could be interpreted as a function. I must admit I don't understand the need for CONTAINS and put the interpretation of 'ichar (chainp(b))' as valid/possible function reference to this. Is the code, as you wrote, inside MODULE and CONTAINS possible ? It isn't what you wanted but I don't know if it is illegal.

My attempt in a dos box with /check /lgo did not proceed to link and run to test SDBG.

There is a lot of new syntax in fortran now, with .net, 2003 and 2008. You should look at the 2003 or 2008 standard. Most of it I don't understand and I think all of it I don't need ! My sympathies are with the compiler writers.

John

11 Jun 2011 6:00 #8376
        program strung_out
        use knots
        character (len = nb) chintz, chance
        chintz = 'xyz'
        chance = chaout (chintz) ! the error is (mis)diagnosed here
        stop
        end program strung_out

        module knots
        integer, parameter :: nb = 3
        contains
        character (len = nb) function chaout (chainp)
        character (len = nb) chainp
        integer b, inp (nb)
        do b = 1, nb
          inp (b) = ichar (chainp (b)) ! the error is here
        end do
        chaout = char (inp (2)) // char (inp (3)) // char (inp (1))
        end function chaout
        end module knots

Actually, your function chaout is perfectly valid Fortran if chainp is an EXTERNAL function of type character(len=nb). The compiler treats it this way. The error then is that you don't pass a function to it but a character(len=nb) variable in your main program.

The compiler diagnosis is correct I think.

So, either change the main program to pass a function (declared EXTERNAL) or change chainp(b) to chainp(b:b).

Incidently, you **DO **need to move the module so it is first in the file. As it is, the compiler will compile your main program first and read the interface definition from the MOD file it created at the last compile. Your first compile will always fail, as will subsequent compiles that follow a change to the interface.

This is what JohnCampbell was saying I think.

Obviously, if you're using Plato and separate files for your main program and module you will be fine as Plato will determine the correct compilation order.

11 Jun 2011 7:25 #8378

chainp was not declared EXTERNAL, so should the compiler assume this?

Also, 'if you're using Plato and separate files for your main program and module you will be fine as Plato will determine the correct compilation order.' Can this be assumed ?

11 Jun 2011 9:40 #8380

Quoted from JohnCampbell chainp was not declared EXTERNAL, so should the compiler assume this?

The compiler should assume this. You only need an EXTERNAL statement if the function name is the same as an INTRINSIC function ('Fortran 95/2003 Explained', p. 82), or if the function is passed as an actual argument to a subroutine or function. (Its a dummy argument in the example, and EXTERNAL is not needed for those). These are the rules for FORTRAN 77, which Fortran 95 inherits.

Quoted from JohnCampbell

Also, 'if you're using Plato and separate files for your main program and module you will be fine as Plato will determine the correct compilation order.' Can this be assumed ?

Plato parses the file collection in a project and determines the correct compile order. I assume it uses Automake.exe to do this, as this utility is included in Plato's install directory.

12 Jun 2011 1:06 #8381

davidb,

I'm not sure that the default assumption is a function, as a text I have, 'Fortran 95 Handbook', by Adams et al, claims 'the processor assumes that the argument is a variable'. I'd expect that for 'chainp' to be assumed a function, it should have been declared EXTERNAL, although the only valid interpretation of 'chainp(b)' is that it is a function. You say 'The compiler should assume this', but I'm not sure that this is the case. My recollection of F77, is that EXTERNAL is required and not optional, but alas I've recently lost my copy of the Fortran 77 standard. I also think the argument for the intrinsic 'char' can not be character*3.

While we are discussing the subtleties of EXTERNAL, has anyone looked at the proposed 2008 standard ? The amount of new code structures in it is really scary. I wonder how many would use this new language. To me it looks like a lead weight that will kill the language. Who could afford to develop a compiler to support this ? I wonder how many Fortran users have given input to this latest colossus.

John

12 Jun 2011 2:11 #8382

@David, Hi!

I'm not sure if you have read the whole thread - in particular, you don't seem to:

  • have realised that I posted a modified version of my code which the compiler swallowed lock, stock and barrel, without offering any error message at all
  • have read my reply to Eddie and John of Fri Jun 10 11:52.

With the code I posted first, the compiler did at least diagnose an error of omission. I suppose it is a matter of taste whether a compiler should make sense of flawed source code by assuming a missing keyword and inferring an error of omission, rather than assuming nothing and inferring an error of commission. My own taste, according to Occam's razor, is that it should assume nothing to make its inferences.

Besides, the whole point of posting illustrative snippets of code is that they stand alone, isn't it? If I don't include a definition of a function called chainp, there's not meant to be one. The fact that I can make the compiler happy by telling it that it'll all be OK at link time rather defeats the purpose of posting code to illustrate a compiler bug :roll:

With the code I posted second, the compiler diagnoses nothing wrong at all. That is one issue. The second is that /DEBUG and /CHECKMATE builds of that code offer less helpful diagnosis of the ensuing runtime error than the /RELEASE build.

And yes, I am using Plato. As I have observed to Eddie, I need to be more careful about the way I present code snippets in future, making sure I describe explicitly any underlying division into files.

12 Jun 2011 4:30 #8384

Andy,

I do agree that it appears to be a compiler bug to not identify, either that

  • it has assumed a function that was not named as EXTERNAL, or
  • the syntax for the character variable is wrong, and
  • SDBG did not identify that the assumed function was not defined properly.

But at least, I ran your example on an old version of FTN95 and SDBG and it did 'stop' at the offending line.

Paul, do you have a view on this ?

12 Jun 2011 1:20 #8385

Quoted from JohnCampbell davidb,

I'm not sure that the default assumption is a function, as a text I have, 'Fortran 95 Handbook', by Adams et al, claims 'the processor assumes that the argument is a variable'. I'd expect that for 'chainp' to be assumed a function, it should have been declared EXTERNAL, although the only valid interpretation of 'chainp(b)' is that it is a function. You say 'The compiler should assume this', but I'm not sure that this is the case. My recollection of F77, is that EXTERNAL is required and not optional, but alas I've recently lost my copy of the Fortran 77 standard.

As you know Fortran allows individual compilation of files. When the module is compiled (first), it assumes from the syntax that chainp must be an external function as this is the only thing which makes sense to it. By default it assumes if a file looks OK it is OK (it doesn't look forward to what other file will make use of the file).

The function chaout does not to declare chainp as EXTERNAL. Only, the main program which calls chaout needs to declare chainp as EXTERNAL. This is what it says in Adams Book (which I have). As this is not done in the example code, an error message is issued.

Of course in this example, there is another interpretation, that the function chaout is incorrect, but the compiler cannot see this when it compiles the module since it does it separately.

The compiler should be reporting the error it reports for both DEBUG(CHECKMATE) and RELEASE builds. If it is not doing this then there is something wrong.

12 Jun 2011 1:32 #8386

Quoted from sparge I suppose it is a matter of taste whether a compiler should make sense of flawed source code by assuming a missing keyword

There is no missing keyword! 😃

Hopefully, Paul will throw some light on this when he gets a chance.

13 Jun 2011 10:02 #8393

Quoted from davidb [ When the module is compiled (first), it assumes from the syntax that chainp must be an external function as this is the only thing which makes sense to it.

David, I don't follow this logic? chainp is declared, within chaout, as character*3. Even if chainp is a function, ICHAR can only take a single character as an argument, and the compiler is not spotting that ICHAR is being passed *three *characters. I don't see how the code I posted is perfectly valid FORTRAN if chainp is declared as EXTERNAL in the main program?

Please login to reply.