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

Problem with If in combination with and

hkausl
Beginner
3,635 Views
Hello,
I'm migrating my digital fortran projects now to Intel Fortran 12. Example code (I stripped it down)

CHARACTER * 40 VAR_:NAME (20)
..
IF ( INDEX_TEST .GT.0 .AND. VAR_NAME (INDEX_TEST) .EQ. 'xxx' ) THEN

INDEX_TEST maybe 0. Therefore I have the first condition. In Digital Foratran, the second conditionion is not handled, if the fist condition is not true.

In Intel fortran, the statment crashes when INDEX_TEST=0, because the second condition is always handled.
For my understanding, this is an error in the compiler. I'm using this kind of syntax on different compilers since more than 30 years, it seems, thatonly theintel fortran compilers work like this. Is there a compiler option to change this?

0 Kudos
14 Replies
rase
New Contributor I
3,635 Views
The Fortran standard does not specify the sequence of terms evaluated in an if instruction. The assumption ist wrong that the compiler has to treat the terms from left to right and stop the evaluation if a term becomes true. CVF and other older compilers evaluated the terms from left to right and quit if true, allright, but not the IVF compiler anymore. The IVF compiler designers left the possibility to evaluate the clauses in arbitrary sequence or in parallel to leave more room for optimizations. The compiler conforms to the standard, it's behavior is not an error. The workaround is to use two consecutive if instructions, the first with the 0 condition for the array index, the second with the if clause for the array member.
0 Kudos
John4
Valued Contributor I
3,635 Views

There's no short-circuit evaluation in Fortran IF statements; some compilers implement it as an extension, some don't. One solution is to just decompose the IF:

[fortran]if (index_test > 0) then
    if (var_name(index_test) == 'xxx') then
        ...
[/fortran]
See this and this for more details.

0 Kudos
hkausl
Beginner
3,635 Views
So I have to scan my code for such dangerous statemets. Thanks.
0 Kudos
jeremy_h
New Contributor I
3,635 Views
It may or may not be obvious, but the same problem applies to .OR. and other booleans also. It applies not just to IF's, but to any conditional, for instance, on a loop control. They can of course be nested in complicated ways. They can be tricky to "unwind" and you may have to unwind hundreds, and ones you overlook stay in your code as latent defects but they will all eventually blow up your program. A "CVF compatibility mode" would have been nice but instead we have a new, rich source of potential defects with a long half-life.

I strongly recommend you investigate VS IDE scripts to help your review. I've attached some scripts below, they may help get you started.

[vb]Imports System
Imports EnvDTE
Imports EnvDTE80
Imports EnvDTE90
Imports System.Diagnostics
Imports System.Windows

Public Module Module1

    Function PrepareFindANDs(ByVal prompt As String) As String
        Dim what As String

        DTE.Find.MatchWholeWord = False
        DTE.Find.Action = vsFindAction.vsFindActionFind
        DTE.Find.Target = vsFindTarget.vsFindTargetCurrentDocument
        DTE.Find.MatchCase = False
        DTE.Find.Backwards = False
        DTE.Find.MatchInHiddenText = True
        DTE.Find.PatternSyntax = vsFindPatternSyntax.vsFindPatternSyntaxLiteral

        what = ".and."
        DTE.Find.FindWhat = what
        Return what
    End Function

    Function CountANDs() As Integer
        Dim textSelection As EnvDTE.TextSelection
        Dim textSelectionPointSaved As TextPoint
        Dim count As Integer = 0
        Dim findResult As EnvDTE.vsFindResult
        Dim firstFindLocation As EnvDTE.TextPoint

        textSelection = DTE.ActiveWindow.Selection
        textSelectionPointSaved = textSelection.ActivePoint.CreateEditPoint()
        textSelection.StartOfDocument()
        PrepareFindANDs("Count Occurrences")
        
        ' The Version 7.0 & 7.1 macros used the following While statement:
        '   While ((findResult = vsFindResult.vsFindResultFound) And (InStr(DTE.StatusBar.Text, "Passed") <> 1))
        ' However, with the Find2.WaitForFindToComplete property the check 
        ' for specific text to be shown in the status bar can be removed.
        CType(DTE.Find, EnvDTE80.Find2).WaitForFindToComplete = True
        findResult = DTE.Find.Execute()
        While (findResult = vsFindResult.vsFindResultFound)
            If (firstFindLocation Is Nothing) Then
                Dim foundSelection As EnvDTE.TextSelection

                foundSelection = DTE.ActiveWindow.Selection
                firstFindLocation = foundSelection.ActivePoint.CreateEditPoint
            Else
                Dim foundSelection As EnvDTE.TextSelection

                foundSelection = DTE.ActiveWindow.Selection
                If (firstFindLocation.EqualTo(foundSelection.ActivePoint)) Then
                    Exit While
                End If
            End If
            count = count + 1
            findResult = DTE.Find.Execute()
        End While

        '' Restore caret to location before invoking this command.
        textSelection.MoveToPoint(textSelectionPointSaved)

        Return count
    End Function

    Sub UnfoldShortCircuit()
        Dim selection As EnvDTE.TextSelection
        Dim startPoint As EnvDTE.EditPoint
        Dim endPoint As TextPoint
        Dim originalText As String
        selection = DTE.ActiveDocument.Selection()
        If selection.Text = "" Then
            MsgBox("Select one complete line." + vbCr + _
                    "If clause runs over multiple lines," + vbCr + _
                    "delete CR/LF until the clause is on one line.", _
                     MsgBoxStyle.Information, "Unfold Short Circuit")
            Exit Sub
        Else
            ' ask user if they want to unfold-short-circuit
        End If
        originalText = selection.Text

        ' Determine whether IF-THEN or DO-WHILE
        Dim containsIF As Boolean = originalText.ToUpper.Contains("IF")
        Dim containsTHEN As Boolean = originalText.ToUpper.Contains("THEN")
        Dim containsDO As Boolean = originalText.ToUpper.Contains("DO")
        Dim containsWHILE As Boolean = originalText.ToUpper.Contains("WHILE")

        Dim IF_token_end As Integer
        Dim THEN_token_start As Integer
        Dim WHILE_token_end As Integer
        Dim controlStructKind As String = ""

        If containsIF And containsTHEN Then
            controlStructKind = "IFTHEN"
            IF_token_end = originalText.ToUpper.IndexOf("IF") + 2
            THEN_token_start = originalText.ToUpper.LastIndexOf("THEN")

        ElseIf containsDO And containsWHILE Then
            controlStructKind = "DOWHILE"
            WHILE_token_end = originalText.ToUpper.IndexOf("WHILE") + 5

        Else
            MsgBox("Select a line that contains a complete " + _
                   "IF-conditions-THEN or DO-WHILE-conditions", _
                     MsgBoxStyle.Exclamation, "Unfold Short-Circuit")
            Exit Sub
        End If

        ' andClause = most of the stuff between the IF and the THEN
        Dim andClause As String = ""
        If controlStructKind = "IFTHEN" Then
            andClause = originalText.Substring(IF_token_end, THEN_token_start - IF_token_end)
        ElseIf controlStructKind = "DOWHILE" Then
            andClause = originalText.Substring _
                (WHILE_token_end, originalText.Length - WHILE_token_end - 2)
        End If
        andClause = Trim(andClause)

        Dim AND_token_start As Integer
        AND_token_start = andClause.ToUpper.IndexOf(".AND.")
        If AND_token_start < 1 Then
            MsgBox("Did not find an .AND. in the selection.", _
                    MsgBoxStyle.Exclamation, "Unfold Short-Circuit")
        End If

        Dim lhs As String = ""
        Dim rhs As String = ""
        ' lhs = most of the stuff from the IF to the .AND.
        lhs = andClause.Substring(1, AND_token_start - 1)
        ' rhs = most of the stuff from the .AND. to the THEN
        rhs = andClause.Substring(AND_token_start + 5, andClause.Length - AND_token_start - 6)

        'Exit Sub
        ' Insert boilerplate
        Metrics.LoadMetrics()

        DTE.UndoContext.Open("Unfold Short-Circuit")
        Try
            startPoint = selection.TopPoint.CreateEditPoint()
            startPoint.Insert("C* IVFJPH Unfold short-circuit operator." + vbCrLf)

            If controlStructKind = "DOWHILE" Then
                startPoint.Insert(vbTab + vbTab + "DO" + vbCrLf)
            End If

            startPoint.Insert(vbTab + vbTab + "BOTH_TRUE = .FALSE." + vbCrLf)
            startPoint.Insert(vbTab + vbTab + "IF " + lhs + " THEN" + vbCrLf)
            startPoint.Insert(vbTab + vbTab + vbTab + "IF " + rhs + " THEN" + vbCrLf)
            startPoint.Insert(vbTab + vbTab + vbTab + vbTab + "BOTH_TRUE = .TRUE." + vbCrLf)
            startPoint.Insert(vbTab + vbTab + vbTab + "END IF" + vbCrLf)
            startPoint.Insert(vbTab + vbTab + "END IF" + vbCrLf)
            If controlStructKind = "IFTHEN" Then
                startPoint.Insert(vbTab + vbTab + "IF ( BOTH_TRUE ) THEN" + vbCrLf)
            ElseIf controlStructKind = "DOWHILE" Then
                startPoint.Insert(vbTab + vbTab + "IF (BOTH_TRUE .EQ. .FALSE.) EXIT" + vbCrLf)
            End If
            startPoint.Insert("C* IVFJPH Original text commented below." + vbCrLf)
            startPoint.Insert("C* IVFJPH ")

            Dim anItem As ModuleMetric

            If myMetrics.Contains(ProjectName() + DTE.ActiveDocument.Name) Then
                anItem = myMetrics.Item(ProjectName() + DTE.ActiveDocument.Name)
                myMetrics.Remove(ProjectName() + DTE.ActiveDocument.Name)
            Else
                anItem = New ModuleMetric
                anItem.Name = DTE.ActiveDocument.Name
                anItem.Project = ProjectName()
                anItem.Path = DTE.ActiveDocument.Path
                anItem.ANDcount = CountANDs()
                anItem.FIXcount = 0
                anItem.Inspected = 0
                anItem.Commented = 0
            End If
            anItem.FIXcount = anItem.FIXcount + 1
            myMetrics.Add(anItem, ProjectName() + DTE.ActiveDocument.Name)

        Finally
            ' If an error occurred, then make sure that the undo context is cleaned up.
            ' Otherwise, the editor can be left in a perpetual undo context.
            DTE.UndoContext.Close()
            Metrics.SaveMetrics()
        End Try

    End Sub

    Sub FindNextAND()
        Dim textSelection As EnvDTE.TextSelection
        Dim startPoint As EnvDTE.EditPoint

        textSelection = DTE.ActiveWindow.Selection

        Dim result As Boolean = textSelection.FindText(".and.")

        If result = False Then
            textSelection.StartOfDocument()
            LoadMetrics()
            Dim anItem As ModuleMetric
            If myMetrics.Contains(ProjectName() + DTE.ActiveDocument.Name) Then
                anItem = myMetrics.Item(ProjectName() + DTE.ActiveDocument.Name)
                myMetrics.Remove(ProjectName() + DTE.ActiveDocument.Name)
            Else
                anItem = New ModuleMetric
                anItem.Name = DTE.ActiveDocument.Name
                anItem.Project = ProjectName()
                anItem.Path = DTE.ActiveDocument.Path
                anItem.ANDcount = CountANDs()
                anItem.FIXcount = 0
            End If
            anItem.Inspected = 1
            myMetrics.Add(anItem, ProjectName() + DTE.ActiveDocument.Name)
            SaveMetrics()
        End If

        ' textSelection.StartOfLine()
    End Sub

End Module

[/vb]
0 Kudos
TimP
Honored Contributor III
3,635 Views
Quoting jeremy_h
A "CVF compatibility mode" would have been nice but instead we have a new, rich source of potential defects

Steve Lionel pointed out several times in the past that CVF didn't support shortcut evaluation in any consistent manner and he said he accumulated a long list of requests about that. I'd take his word for it that there would be no practical way to make a compiler which emulated the treatment of some version of CVF for incorrect source code. I suppose such an option would give up on vectorization.
I'm amazed that so much code was made somewhat intentionally non-portable by requiring CVF in this way.
The only translator in my experience which supported C-style shortcuts was f2c (which used literal C && and || translation).
0 Kudos
jeremy_h
New Contributor I
3,635 Views
I trust Steve's opinion. I don't want to go too far off-topic but to answer your amazement, short-circuit eval was an important performance optimization, starting with VAX and continuing through CVF. For decades it was in our style guide as a preferred coding idiom. Because of this, we have more latent defects moving from CVF to IVF than from VAX to CVF.

Well OK, whine over. This is just the way it is. The IDE macros are a help, and we hope that anybody finding this thread will take a look at them. We reviewed many hundreds k sloc using this, which (if you look carefully) also collect metrics so we could see which modules were likely to be the most troublesome.
0 Kudos
Steven_L_Intel1
Employee
3,635 Views
The compiler may choose to do comparisons in any order, and to any level of completeness, that is logically equivalent. The optimizer may choose to move some comparisons earlier in the code if it feels it is useful (and still correct.) The underlying principle is that you, as a Fortran programmer, must not depend on any particular order of evaluation not specified by the standard. If you do, the results are unpredictable.

The compiler may choose to do short-circuiting, but that is an optimization, not a semantic requirement. Neither VAX Fortran nor CVF would always evaluate a compound logical expression in strict left-to-right order. Just because it seemed to in your program, that doesn't mean it was deliberate on the part of the compiler.
0 Kudos
onkelhotte
New Contributor II
3,635 Views
I had to make the same experience.

When I started at my current work, Ive never used Fortran before. And from C++ I was used to that the && operator would not evaluate the second expression, only & does.

Sure, it would make life (or coding) in Fortran easier, when youd have the same behaviour. But we havent, so we have to code two (or more) if clauses, when your expression requires this.

Markus
0 Kudos
jimdempseyatthecove
Honored Contributor III
3,635 Views
>>Sure, it would make life (or coding) in Fortran easier, when youd have the same behaviour.

Since FORTRAN was created first, then if you insist on same behavior then get the C/C++ group to change their syntax. This is not likely to happen. Same issue can be said about comparing other languages syntax rules e.g. Forth, Lisp, ...

Jim Dempsey
0 Kudos
onkelhotte
New Contributor II
3,635 Views
Jim, only because Fortran is older doesnt mean that the behaviour is better. Why shouldnt you learn from other approaches? This sounds tpyical german, a thought-terminating clich is (translated) "We always did it this way, it always proved of value."

It is logical that when you have an AND comparison and the term on lhs is false, you dont need to evaluate rhs.

Markus
0 Kudos
IanH
Honored Contributor III
3,635 Views
It's equally as logical that if the rhs is false then you don't need to evaluate the lhs, and maybe it's faster to evaluate the right hand side in some circumstances first. Maybe it's faster to evaluate the two operands in parallel - perhaps relevant in the context of defined operations on user defined types or with all that fancy out-of-sequence instruction execution that processors can do these days.

By not having short-circuiting the compiler has the freedom to do what it thinks is best.


0 Kudos
Steven_L_Intel1
Employee
3,635 Views
What seems "logical" is only such when you learned the "logical" way first.

Remember that Fortran originated as a language for mathematicians and scientists, and in math, logical operations have commutative and associative properties. The way the language is defined is that there is no strict left to right order and that the compiler may evaluate any logically equivalent expression to any degree of completeness. This gives compilers flexibility to optimize, but the lack of a specific order and short-circuiting in the language does trip up people who assume that left-to-right and short-circuiting is an immutable law of programming.

There have been proposals to add short-circuit operators to Fortran, but they have not yet found acceptance.
0 Kudos
jimdempseyatthecove
Honored Contributor III
3,635 Views
>>doesnt mean that the behaviour is better

Number 1, you did not ask for better, you asked for same behavior.
Number 2, better as it applies to IF parsing is subjective

Consider:

IF(funcA() && funcB() && funcC()) call DoSomething()

Where each funcX is relatively long and where the compiler is multi-threaded aware. The compiler could set an internalshared reduction variable to .false. then perform all functions in parallel with a reduction operator of .OR. to the internal shared reduction variable.

What you are considering as "better" results in serial processing of the IF statement.

WhatFORTRAN considers as "better" results in the possibility for the compiler to multi-thread the expression .OR. use serial processingbut reorder the expressions due to heuristics or what it can descern from compiling the code in the func's.

Maybe Steve can comment on this:

What happens with

IF((((funcA()) && funcB()) && funcC())) call DoSomething()

IOW use parens to force evalution order
(this may also require the functions to be non-PURE)

Jim Dempsey
0 Kudos
onkelhotte
New Contributor II
3,635 Views
Thanks for all the answers! This makes it more clearer to me :-)

Of course it makes sense to reorder the evaluation when you have something like

IF(funcA() && funcB() && funcC()) call DoSomething()

instead of

if(a>0 .and. array(a) > b) call DoSomething()

Especially nowadays reordering makes even more sense with multithreading and/or multi core CPUs and/or multi CPUs systems that you had not "in the early days".

Markus
0 Kudos
Reply