Chapter 7. Expressions and Assignments

In Fortran, calculations are specified by writing expressions. Expressions look much like algebraic formulas in mathematics, particularly when the expressions involve calculations on numerical values.

Expressions often involve nonnumeric values, such as character strings, logical values, or structures; these also can be considered to be formulas that involve nonnumeric quantities rather than numeric ones.

This chapter describes how valid expressions can be formed, how they are interpreted, and how they are evaluated. One of the major uses of expressions is in assignment statements where the value of an expression is assigned to a variable. The assignment statement appears in four forms: intrinsic assignment, defined assignment, masked array assignment, and pointer assignment. In the first three forms, a value is computed by performing the computation specified in an expression and the value is assigned to a variable. In the fourth form, a pointer, the object on the left side, is made to point to the object or target on the right side. The four forms of the assignment statement are also described in this chapter.


Note: : The Fortran statement syntax in this manual is defined using the same terms used in the Fortran standard. In this chapter, however, certain terms from the standard have been changed to improve clarity.


Introduction to Expressions

Fortran allows you to define data types, operators for these types, and operators for intrinsic types. These capabilities are provided within the general framework for expressions, which consists of three sets of rules:

  • The rules for forming a valid expression

  • The rules for interpreting the expression (giving it a meaning)

  • The rules for evaluating the expression (how the computation may be carried out)

An expression is formed from operators and operands. There is no change from FORTRAN 77 in the rules for forming expressions, except that a new class of operators has been defined. These are user-defined operators, which are either unary or binary operators. They have the form of a sequence of letters surrounded by periods; .INVERSE. and .PLUS. are examples of possible user-defined operators.

The formal (BNF) rules for forming expressions imply an order for combining operands with operators. These rules specify that expressions enclosed in parentheses are combined first and that, for example, the multiply operator * is combined with its operands before the addition operator + is combined with its operands. This order for operators in the absence of specific parentheses is called the operator precedence and is summarized in “Precedence of Operators”. New operators, such as == and >=, have the same precedence and meaning as .EQ. and .GE., respectively.

The formation rules for expressions imply that the defined unary operators have highest precedence of all operators, and defined binary operators have the lowest precedence of all operators. When they appear in a context where two or more of these operators of the same precedence are adjacent, the operands are combined with their operators in a left-to-right manner, as is the case for the familiar + and - operators, or in a right-to-left manner for the exponentiation operator (**).

Intrinsic operators are generic in the sense that they can operate on operands of different types. For example, the plus operator + operates on operands of type integer as well as real and complex. Intrinsic operators can be extended further by the programmer to operate on operands of types for which there are no intrinsic operations. Similarly, you can use defined unary and defined binary operators to operate on operands of types for which there are no previous definitions. The Fortran Language Reference Manual, Volume 2, describes how any operator can be made generic by the programmer using a generic specifier on an interface block.

The rules for interpretation of an expression are provided by the interpretation of each operator in the expression. When the operator is an intrinsic operator such as +, *, or .NOT., and the operands are of intrinsic types allowed for the intrinsic operator, the interpretation is provided by the usual mathematical or symbolic meaning of the operation. Thus, + with two numeric operands means that the two operands are added together. For the user-defined operators, the interpretation is provided by a user-supplied function subprogram with a designation that this subprogram is to be used to define the operation. Fortran allows the intrinsic operator symbols to be extended to cases in which the operands are not of the usual intrinsic types defined by the standard. For example, the + operator can be defined for operands of type RATIONAL (a user-defined type) or for operands of type logical with the interpretation provided by a user-supplied function subprogram. The rules for construction of expressions (the syntax rules) are the same for user-defined operators as for intrinsic operators.

The general rule for evaluation of a Fortran expression states that any method that is mathematically equivalent to that provided by the construction and interpretation rules for the expression is permitted, provided the order of evaluation indicated by explicit parentheses in the expression is followed. Thus, a compiler has a great deal of freedom to rearrange or optimize the computation, provided the rearranged expression has the same mathematical meaning.

Arrays and pointers as objects can appear in expressions and assignment statements. This chapter describes using arrays and pointers in the following contexts:

  • As operands of intrinsic and user-defined operations

  • As the variables being assigned in intrinsic assignment statements

  • As the variables in pointer assignment statements and masked array assignment statements

Assignment

The result obtained from the evaluation of an expression can be used in many ways. For example, it can be printed or passed to a subprogram. In many cases, however, the value is assigned to a variable and that value can be used later in the program by referencing the variable.

Execution of the assignment statement causes the expression to be evaluated (by performing the computation indicated), and then the value of the expression is assigned to the variable on the left of the equal sign.

The following example shows an assignment statement:

REAL_AGE = REPORTED_AGE + 3.0

REPORTED_AGE + 3.0 is the expression that indicates how to compute a value, which is assigned to the variable REAL_AGE.

The following example involves subscripts:

A(I+3) = PI + A(I-3)

The value of the subscript expression I-3 is determined and the value of the I-3 element of A is added to the value of PI to produce a sum. Before the result of this expression is assigned, the value of the subscript expression I+3 is determined and the value of the sum is assigned to the element I+3 of A.

The previous examples are arithmetic. Fortran has expressions of other types, such as logical, character, and derived type. Values of expressions of these other types can be assigned to variables of these other types. As with operators, the programmer can extend the meaning of assignment to types not defined intrinsically and can redefine assignment for two objects of the same derived type. Such assignments are called defined assignments. In addition, arrays and pointers each have special forms of assignment statements called masked array assignment and pointer assignment, respectively. These two assignment statement forms are described later in this chapter.

Expressions

An assignment statement is only one of the Fortran statements in which expressions may occur. Expressions also can appear in subscripts, actual arguments, IF statements, PRINT statements, WHERE statements, declaration statements, and many other statements.

An expression represents a computation that results in a value and can be as simple as a constant or variable. The value of an expression has a type and can have zero, one, or two type parameter values. If the value is of a derived type, it has no type parameter. If it is of an intrinsic type, it has a kind type parameter, and if, in addition, it is of the type character, it has a character length parameter. In addition, the value is a scalar (including a structure) or an array.

A complex value or a structure value is a scalar, even though it can consist of more than one value (for example, a complex value consists of two real values).

Arrays and pointers can be used as operands of intrinsic and defined operators. For intrinsic operators, when an array is an operand, the operation is performed element-wise on the elements of the array. For intrinsic operators, when a pointer is an operand, the value of the target pointed to by (associated with) the pointer is used as the operand. For defined operators, the array or pointer is used in a manner determined by the procedure defining the operation.

As indicated in the introduction to this chapter, the presentation of expressions is described in terms of the following three basic sets of rules:

  • The rules for forming expressions (syntax)

  • The rules for interpreting expressions (semantics)

  • The rules for evaluating expressions (optimization)

The syntax rules indicate which forms of expressions are valid. The semantics indicate how each expression is to be interpreted. After an expression has been given an interpretation, a compiler can evaluate another completely different expression, provided the expression evaluated is mathematically equivalent to the one written.

To see how this works, consider the expression 2 * A + 2 * B in the following PRINT statement:

PRINT *, 2 * A + 2 * B

The syntax rules described later in this chapter indicate that the expression is valid and suggest an order of evaluation. The semantic rules specify the operations to be performed, which in this case, are the multiplication of the values of A and B by 2 and the addition of the two results. That is, the semantic rules indicate that the expression is to be interpreted as if it were the following:

((2 * A) + (2 * B))

After the correct interpretation has been determined, the Fortran rules of evaluation allow a different expression to be used to evaluate the expression, provided the different expression is mathematically equivalent to the one written. For example, a processor can first add A and B and then multiply the result by 2, because the following expression is mathematically equivalent to the one written:

2 * (A + B)

Although alternative evaluations are allowed, three properties should be noted:

  • Parentheses must not be violated. Consider the following expression:

    (2 * A) + (2 * B)

    This expression must not be evaluated as follows:

    2 * (A + B)

    This gives the programmer some control over the method of evaluation.

  • Integer division is not mathematically equivalent to real division. The value of 3/2 is 1 and so cannot be evaluated as 3*0.5, which is 1.5.

  • Mathematically equivalent expressions can produce computationally different results because of the implementation of arithmetic and rounding on computer systems. For example, the expression X/2.0 could be evaluated as 0.5*X, even though the results may be slightly different. Also, for example, the expression 2 * A + 2 * B could be evaluated as 2*(A+B); when A and B are of type real, the two mathematically equivalent expressions may yield different values because of different rounding errors and different arithmetic exceptions in the two expressions.

Formation of Expressions

An expression is formed from operands, operators, and parentheses. The simplest form of an expression is a constant or a variable.

Expression 

Meaning

3.1416 

A real constant

.TRUE. 

A logical constant

X 

A scalar variable

Y 

An array variable

Y(K) 

A variable that is an array element of Y

Y(2:10:2) 

A variable that is an array subsection of Y

M%N 

A variable that is a component of a structure M

Y(K)(I:I+3) 

A variable that is a substring of array element Y(K)

The values of these simple expressions are the constant value 3.1416, the constant value .TRUE., the value of the variable X, the value of the array Y, the value of the array element Y(K), the value of the array subsection Y(2:10:2), the value of the component N of structure M, and the value of a substring of an array element Y(K), respectively.

Operands

An operand in an expression can be one of the following items:

  • A constant or subobject of a constant

  • A variable (for example, a scalar, an array, a substring, or a pointer)

  • An array constructor

  • A structure constructor

  • A function reference (returning, for example, a scalar, an array, a character variable, or a pointer)

  • Another expression in parentheses

The following examples show operands:

A              ! scalar or an array
B(1)           ! array element or function
C(3:5)         ! array section or a substring
(A + COS(X))   ! expression in parentheses
(/ 1.2, 2.41 /)! array constructor
RATIONAL(1,2)  ! structure constructor or function
I_PTR          ! pointer to an integer target

Binary and Unary Operations

There are two forms that operations can take in an expression. One is an operation involving two operands, such as multiplying two numbers together. The other is an operation on one operand, such as making a number negative. These forms are called binary and unary operations, respectively.

Table 7-1 lists the intrinsic operators. You can use function subprograms to define additional operators. User-defined operators are either binary or unary operators.

Table 7-1. Intrinsic operators and the allowed types of their operands

Operator category

Intrinsic operator

Operand types

Arithmetic

**, *, /, +, -, unary +, unary -

Numeric of any combination of numeric types and kind type parameters or Cray pointer. Cray pointers are only allowed with the + or - operators.

Character

//

Character of any length.

Relational

.EQ., .NE., ==, /=

Both of any numeric type and any kind type parameter or Cray pointer, or both of type character with any character length parameter.

Relational

.GT., .GE., .LT., .LE., >, >=, <, <=

Both of any numeric type (except complex) and any kind type parameter or Cray pointer, or both of type character with any character length parameter.

Logical

.NOT., .AND., .OR., .XOR., .EQV., .NEQV.

Both of type logical with any combination of kind type parameters.

Bitwise masking (Boolean) expressions (EXT)

.NOT., .AND., .OR., .XOR., .EQV., .NEQV.

Integer, real, typeless, or Cray pointer.



Note: The Fortran standard does not specify the bitwise masking (Boolean) expressions, nor does it specify the .XOR. operator as a logical operator.

A binary operator combines two operands, as in the following:

x1operatorx2

Examples:

A + B
2 * C

The examples show an addition between two operands A and B, and a multiplication of two operands, the constant 2 and the operand C.

A unary operation acts on one operand, as in the following:

operatorx1

Examples:

- C
.NOT. L

The first example results in the value minus C. The second example produces a value that is the logical complement of L; the operator .NOT. is the only intrinsic operator that is a unary operator and is never a binary operator.

Intrinsic and Defined Operations

Intrinsic operations are those whose definitions are known to the compiler. They are built into Fortran and are always available for use in expressions. Table 7-1, lists the operators built into Fortran as specified by the standard.

The relational operator symbols ==, /=, >, >=, <, and <= are synonyms for the operators .EQ., .NE., .GT., .GE., .LT., and .LE., respectively.

The less than or greater than operation is represented by the <> operator and the .LG. keyword. This operation is suggested by the IEEE standard for floating-point arithmetic, and the CF90 and MIPSpro 7 Fortran 90 compilers support this operator. Only values of type real can appear on either side of the <> or .LG. operators. If the operands are not of the same kind type value, the compiler converts them to equivalent kind types. This operator's functionality differs slightly, depending on your platform, as follows:

  • On IRIX, on UNICOS/mk, and on CRAY T90 systems that support IEEE arithmetic, the <> and .LG. operators perform a less-than-or-greater-than operation as specified in the IEEE standard for floating-point arithmetic.

  • On UNICOS systems, except for CRAY T90 systems that support IEEE arithmetic, the <> and .LG. operators are evaluated as /= and .NE. operators. Note that <> and /= are still distinct operators on these platforms in the sense that different generic overloads may be written for each of them. As on systems that conform to the IEEE standard for floating-point arithmetic, if both operands are not of type real and the operator is not overloaded with a generic overload interface block, an error message is generated.


Note: The Fortran standard does not specify the <> or .LG. operators.

The CF90 and MIPSpro 7 Fortran 90 compilers allow abbreviations for the logical and masking operators. The abbreviations .A., .O., .N., and .X. are synonyms for .AND., .OR., .NOT., and .XOR., respectively. If you define the abbreviated operator for any type, the abbreviated form of the intrinsic operator cannot be used in any scope in which the defined operator is accessible.


Note: The Fortran standard does not specify abbreviations for the logical and masking operators.

In addition to the Fortran operators that are intrinsic (built in), there may be user-defined operators in expressions.

Defined operations are those that you define in the Fortran program and are made available to each program unit that uses them. The computation performed by a defined operation is described explicitly in a function that must appear as a subprogram in the Fortran program where it is used. The operator used in a defined operation is called a defined operator. In this way, you extend the repertoire of operations so that computations can be expressed in a natural way using operator notation. Function subprograms that define operators are explained in detail in the Fortran Language Reference Manual, Volume 2.

A defined operator uses a symbol that is either the symbol for an intrinsic operator or is a new operator symbol. The synonyms described above for the relational operators remain synonyms in all contexts, even when there are defined operators. For example, if the operator < is defined for a new type, say STRING, the same definition applies to the operator .LT. for the type STRING; if the operator .LT. is specified as private, the operator < is also private.

A distinction is made between a defined (or new) operator and an extended intrinsic operator. An extended intrinsic operator is one that uses the same symbol as an intrinsically defined Fortran operator, like plus + or multiply *. It also causes the operations to be combined in the same order as is specified for the intrinsic operator. A defined operator is one where the operator symbol is not the same as an intrinsic operator but is new, such as the .INVERSE. operator. Defined operators, however, have a fixed precedence; defined unary operators have the highest precedence of all operators, and defined binary operators have the lowest precedence of all operators. The precedences of all operators are described in more detail in “Precedence of Operators”.

A defined elemental operation is a defined operation for which the function is elemental.


Note: The masking or Boolean operators and their abbreviations, which are extensions to Fortran, can be redefined as defined operators. If you redefine a masking operator, your definition overrides the intrinsic masking operator definition. See Table 7-1, for a list of the operators.


Rules for Forming Expressions

Expressions are formed by combining operands. Operands can be constants, variables (scalars, array elements, arrays, array sections, structures, structure components, and pointers), array constructors, structure constructors, functions, and parenthesized expressions with intrinsic and defined operators.

The method used to specify the expression formation rules is a collection of syntax rules that determine the forms of expressions. The order of evaluation of the operations in an expression is determined by the usual semantics for the operations, and the syntax rules are designed to be consistent with these semantics. In fact, the order of evaluation defines a precedence order for operators that is summarized in Table 7-2.

Table 7-2. The hierarchy of expressions through forms

Term

Definition

expression

[expression defined_operator]equivalence_expression

equivalence_expression

[equivalence_expression .EQV. ]disjunct_expressionequivalence_expression.NEQV.disjunct_expression

Exclusive OR (extension)

[disjunct_expression .XOR. ]conjunct_expression

disjunct_expression

[disjunct_expression .OR. ]conjunct_expression

conjunct_expression

[conjunct_expression .AND. ]not_expression

not_expression

[ .NOT. ]comparison_expression

comparison_expression

[concatenation_expressionrelational_operator] concatenation_expression

concatenation_expression

[concatenation_expression // ]summation_expression

summation_expression

[summation_expression + ]multiplication_expressionsummation_expression - multiplication_expression

+ multiplication_expression

- multiplication_expression

multiplication_expression

[multiplication_expression * ]exponentiation_expressionmultiplication_expression / exponentiation_expression

exponentiation_expression

defined_unary_expression[ ** exponentiation_expression]

defined_unary_expression

[defined_operator]primary

primary

constant

constant_subobject

variable

array_constructor

structure_constructor

function_reference

(expression)

The set of syntax rules defines an expression at the highest level in terms of operators and operands, which are themselves expressions. As a result, the formal set of rules is recursive. The basic or lowest level of an expression is a primary, which, for example, can be a variable, a constant, or a function, or recursively an expression enclosed in parentheses. The rules for forming expressions are described from the lowest or most primitive level to the highest or most complex level; that is, the rules are stated from a primary up to an expression.

Primary

A primary is defined as follows:

 

primary

is

constant

 

 

or

constant_subobject

 

 

or

variable

 

 

or

array_constructor

 

 

or

structure_constructor

 

 

or

function_reference

 

 

or

(expr)

 

constant_subobject

is

subobject

A variable that is a primary must not be a whole assumed-size array or a section of an assumed-size array name, unless the last subscript position of the array is specified with a scalar subscript or a section subscript in which the upper bound is specified.

The following examples show primaries:

Primary 

Meaning

3.2 

A real constant

ONE 

A named constant

'ABCS'(I:I) 

A constant subobject

A 

A variable (scalar, array, structure, or pointer)

B(:,1:N) 

An assumed-size array with an upper bound in the last dimension

C(I) 

An array element

CH(I:J) 

A substring

(/ 1, J, 7 /) 

An array constructor

RATIONAL(I, J) 

A structure constructor

FCN(A) 

A function reference

(A * B) 

A parenthesized expression

In the previous examples, ONE is a named constant if it has the PARAMETER attribute or appears in a PARAMETER statement. 'ABCS'(I:I) is a constant subobject even though I may be a variable because its parent ('ABCS') is a constant; the reference 'ABCS'(I:I) is a constant subobject because it cannot be defined like a variable can be defined. RATIONAL is a derived type and FCN is a user-defined function.

When an array variable is a primary, the whole array is used, except in a masked assignment statement. In a masked assignment statement, only that part of the array specified by the mask is used.

When a pointer is a primary, the target associated with (pointed to by) the pointer is used, except possibly when the pointer is an actual argument of a procedure, or is an operand of a defined operation or a defined assignment. Whether the pointer or the target is used in these exceptional cases is determined by the procedure invoked by the reference.

Recall that an assumed-size array is a dummy argument whose shape is not completely specified in the subprogram in that the extent in the last dimension is determined by its corresponding actual argument. The implementation model is that the extent in the last dimension is never known to the subprogram but is specified by the use of a subscript, section subscript, or vector subscript expression that defines an upper bound in the last dimension. Unless the extent is specified in this way, such an object must not be used as a primary in an expression. On the other hand, if a subscript, section subscript with an extent for the upper bound, or a vector subscript is specified for the last dimension, the array value has a well-defined shape and hence can be used as a primary in any expression. For example, if A is declared as REAL A(3,*), array A(:,3) has a well-defined shape and can be used as a primary in an expression.

Expressions can be used as actual arguments in procedure references (function references or subroutine calls). Because actual arguments can be expressions involving operations, actual arguments must not contain assumed-size arrays, unless their shape is well-defined, as described above. An actual argument, however, can be just a variable, which then allows the actual argument to be the name of an assumed-size array. This implies that such actual arguments can be assumed-size arrays, unless the procedure requires the shape of the argument to be specified by the actual argument. Most of the intrinsic procedures that allow array arguments require the shape to be specified for the actual array arguments, and therefore assumed-size arrays cannot be used as actual arguments for most intrinsic functions. The exceptions are all references to the intrinsic function LBOUND(3i), and certain references to the intrinsic functions UBOUND(3i) and SIZE(3i).

Defined Unary Expression

Defined unary expressions have the highest operator precedence. A defined unary expression is a defined operator followed by a primary. These are defined as follows:

 

defined_unary_expr

is

[defined_operator]primary

 

defined_operator

is

. letter[letter] ... .

A defined operator must not contain more than 31 letters.

A defined operator must not be the same as any intrinsic operator (.NOT., .AND., .OR., .EQV., .NEQV., .EQ., .NE., .GT., .GE., .LT., .LE., or .LG.) or any logical literal constant (.FALSE. or .TRUE.).


Note: The Fortran standard does not describe the .LG. operator.

A defined operator can be the same as one of the masking or Boolean operators supported by the CF90 and MIPSpro 7 Fortran 90 compilers as extensions to the Fortran standard. The corresponding CF90 and MIPSpro 7 Fortran 90 operator loses its intrinsic properties. Note that the abbreviations .T., .F., .A., .O., .N., and .X. are synonyms for .TRUE., .FALSE., .AND., .OR., .NOT., and .XOR., respectively. If you define the abbreviated operator for any type, the abbreviated form of the intrinsic operator also cannot be used in any scope in which the defined operator is accessible, and the redefined abbreviated logical constants can no longer be used as logical constants.

The following examples show defined unary expressions:

Expression 

Meaning

.INVERSE. B 

A defined unary expression (where .INVERSE. is a defined operator)

A 

A primary is also a defined unary expression

Exponentiation Expression

An exponentiation expression is an expression in which the operator is the exponentiation operator **. This is defined as follows:

 

exponentiation_expr

is

defined_unary_expr[ ** exponentiation_expr]

Note that the definition is right recursive (that is, the defined term appears to the right of the operator **) which indicates that the precedence of the ** operator in contexts of equal precedence is right-to-left. Thus, the interpretation of the expression A ** B ** C is A ** ( B ** C ).

The following examples show exponentiation expressions:

Expression 

Meaning

A ** B 

An exponentiation expression

A ** B ** C 

An exponentiation expression with right-to-left precedence

.INVERSE. B 

A defined unary expression is also an exponentiation expression

A 

A primary is also an exponentiation expression

Multiplication Expression

A multiplication expression is an expression in which the operator is either * or /. It is defined as follows:

 

multiplication_expr

is

[multiplication_expr * ]exponentiation_expr

 

 

or

[multiplication_expr / ]exponentiation_expr

Note that the definition is left recursive (that is, the defined term appears to the left of the operator * or /) which indicates that the precedence of the * and / operators in contexts of equal precedence is left-to-right. Thus, the interpretation of the expression A * B * C is (A * B) * C, or A / B * C is (A / B) * C. This left-to-right precedence rule applies to the remaining binary operators except the relational operators.

The following examples show multiplication expressions:

Expression 

Meaning

A * B 

A multiplication expression

A * B * C 

A multiplication expression with left-to-right precedence

A / B 

A multiplication expression

A / B / C 

A multiplication expression with left-to-right precedence

A ** B 

An exponentiation expression is also a multiplication expression

.INVERSE. B 

A defined unary expression is also a multiplication expression

A 

A primary is also a multiplication expression

Summation Expression

A summation expression is an expression in which the operator is either + or -. It is defined as follows:

 

summation_expr

is

[[summation_expr] + ]multiplication_expr

 

 

or

[[summation_expr] - ]multiplication_expr

The following examples show summation expressions:

Expression 

Meaning

A + B 

A summation expression

A + B - C 

A summation expression with left-to-right precedence

- A - B - C 

A summation expression with left-to-right precedence

+ A 

A summation expression using unary +

- A 

A summation expression using unary -

A * B 

A multiplication expression is also a summation expression

A ** B 

An exponentiation expression is also a summation expression

.INVERSE. B 

A defined unary expression is also a summation expression

A 

A primary is also a summation expression

Concatenation Expression

A concatenation expression is an expression in which the operator is //. It is defined as follows:

 

concatenation_expr

is

[concatenation_expr // ]summation_expr

The following examples show concatenation expressions:

Expression 

Meaning

A // B 

A concatenation expression

A // B // C 

A concatenation expression with left-to-right precedence

A - B 

A summation expression is also a concatenation expression

- A 

A summation expression is also a concatenation expression

A * B 

A multiplication expression is also a concatenation expression

A ** B 

An exponentiation expression is also a concatenation expression

.INVERSE. B 

A defined unary expression is also a concatenation expression

A 

A primary is also a concatenation expression

Comparison Expression

A comparison expression is an expression in which the operator is a relational operator. It is defined as follows:

 

comparison_expr

is

[concatenation_expr rel_op]concatenation_expr

 

rel_op

is

.EQ.

 

 

or

.NE.

 

 

or

.LT.

 

 

or

.LE.

 

 

or

.GT.

 

 

or

.GE.

EXT

 

or

.LG.

 

 

or

==

 

 

or

/=

 

 

or

<

 

 

or

<=

 

 

or

>

 

 

or

>=

EXT

 

or

<>

The operators ==, /=, <, <=, >, >=, and <> are synonyms in all contexts for the operators .EQ., .NE., .LT., .LE., .GT., .GE., and .LG., respectively.


Note: The Fortran standard does not describe the .LG. or <> operators.

Note that the definition of a comparison expression is not recursive, and therefore comparison expressions cannot contain relational operators in contexts of equal precedence.

The following examples show comparison expressions:

Expression 

Meaning

A .EQ. B 

A comparison expression

A < B 

A comparison expression

A // B 

A concatenation expression is also a comparison expression

A - B 

A summation expression is also a comparison expression

- A 

A summation expression is also a comparison expression

A * B 

A multiplication expression is also a comparison expression

A ** B 

An exponentiation expression is also a comparison expression

.INVERSE. B 

A defined unary expression is also a comparison expression

A 

A primary is also a comparison expression

Not Expression

A not expression is an expression in which the operator is .NOT.. It is defined as follows:

 

not_expr

is

[ .NOT. ]comparison_expr

Note that the definition of a not expression is not recursive, and therefore not expressions cannot contain adjacent .NOT. operators.

The following examples show not expressions:

Expression 

Meaning

.NOT. A 

A not expression

A .EQ. B 

A comparison expression is also a not expression

A // B 

A concatenation expression is also a not expression

A - B 

A summation expression is also a not expression

- A 

A summation expression is also a not expression

A * B 

A multiplication expression is also a not expression

A ** B 

An exponentiation expression is also a not expression

.INVERSE. B 

A defined unary expression is also a not expression

A 

A primary is also a not expression

Conjunct Expression

A conjunct expression is an expression in which the operator is .AND.. It is defined as follows:

 

conjunct_expr

is

[conjunct_expr .AND. ]not_expr

Note that the definition of a conjunct expression is left recursive, and therefore the precedence of the .AND. operator in contexts of equal precedence is left-to-right. Thus, the interpretation of the expression A .AND. B .AND. C is (A .AND. B) .AND. C.

The following examples show conjunct expressions:

Expression 

Meaning

A .AND. B 

A conjunct expression

A .AND. B .AND. C 

A conjunct expression with left-to-right precedence

.NOT. A 

A not expression is also a conjunct expression

A .EQ. B 

A comparison expression is also a conjunct expression

A // B 

A concatenation expression is also a conjunct expression

A - B 

A summation expression is also a conjunct expression

- A 

A summation expression is also a conjunct expression

A * B 

A multiplication expression is also a conjunct expression

A ** B 

An exponentiation expression is also a conjunct expression

.INVERSE. B 

A defined unary expression is also a conjunct expression

A 

A primary is also a conjunct expression

Inclusive Disjunct Expression

An inclusive disjunct expression is an expression in which the operator is .OR.. It is defined as follows:

 

inclusive_disjunct_expr

is

[inclusive_disjunct_expr .OR. ]conjunct_expr

Note that the definition of an inclusive disjunct expression is left recursive, and therefore the precedence of the .OR. operator in contexts of equal precedence is left-to-right. Thus, the interpretation of the expression A. OR. B .OR. C is (A .OR. B) .OR. C.

The following examples show inclusive disjunct expressions:

Expression 

Meaning

A .OR. B 

An inclusive disjunct expression

A .OR. B .OR. C 

An inclusive disjunct expression with left-to-right precedence

A .AND. B 

A conjunct expression is also an inclusive disjunct expression

.NOT. A 

A not expression is also an inclusive disjunct expression

A .EQ. B 

A comparison expression is also an inclusive disjunct expression

A // B 

A concatenation expression is also an inclusive disjunct expression

A - B 

A summation expression is also an inclusive disjunct expression

- A 

A summation expression is also an inclusive disjunct expression

A * B 

A multiplication expression is also an inclusive disjunct expression

A ** B 

An exponentiation expression is also an inclusive disjunct expression

.INVERSE. B 

A defined unary expression is also an inclusive disjunct expression

A 

A primary is also an inclusive disjunct expression

Equivalence Expressions and Exclusive Disjunct Expressions

An equivalence expression is an expression in which the operator is either .EQV. or .NEQV.. It is defined as follows:

 

equivalence_expr

is

[equivalence_expr .EQV. ]inclusive_disjunct_expr

 

 

or

[equivalence_expr .NEQV. ]inclusive_disjunct_expr

An exclusive disjunct expression is an expression in which the operator is .XOR.. It is defined as follows:

EXT

exclusive_disjunct_expr

is

[exclusive_disjunct_expr .XOR. ]inclusive_disjunct_expr



Note: The Fortran standard does not specify the .XOR. operator.

Note the following:

  • In the following discussion, equivalence expression means either equivalence expression or exclusive disjunct expression.

  • The definition of an equivalence expression is left recursive, and therefore the precedence of the .EQV. or .NEQV. operators in contexts of equal precedence is left-to-right. Thus, the interpretation of the expression A .EQV. B .NEQV. C is (A .EQV. B) .NEQV. C.

The following examples show equivalence expressions:

Expression 

Meaning

A .EQV. B 

An equivalence expression

A .XOR. B 

An equivalence exclusive disjunct expression

A .NEQV. B .XOR. C 

An equivalence expression with left-to-right precedence

A .OR. B 

An inclusive disjunct expression is also an equivalence expression

A .AND. B 

A conjunct expression is also an equivalence expression

.NOT. A 

A not expression is also an equivalence expression

A .EQ. B 

A comparison expression is also an equivalence expression

A // B 

A concatenation expression is also an equivalence expression

A - B 

A summation expression is also an equivalence expression

- A 

A summation expression is also an equivalence expression

A * B 

A multiplication expression is also an equivalence expression

A ** B 

An exponentiation expression is also an equivalence expression

.INVERSE. B 

A defined unary expression is also an equivalence expression

A 

A primary is also an equivalence expression

Expression

The most general form of an expression is defined as follows:

 

expr

is

[expr defined_binary_op]equivalence_expr

 

defined_binary_op

is

.letter[letter] ....

Note that the definition of an expression is left recursive, and therefore the precedence of the binary defined operator in contexts of equal precedence is left-to-right. The interpretation of the expression A .PLUS. B .MINUS. C is thus (A .PLUS. B) .MINUS. C (where .MINUS. is a defined operator).

The following examples show expressions:

Expression 

Meaning

A .PLUS. B 

An expression (where .PLUS. is a defined operator)

A .CROSS. B .CROSS. C 

An expression with left-to-right precedence (where .CROSS. is a defined operator)

A .EQV. B 

An equivalence expression is also an expression

A .OR. B 

An inclusive disjunct expression is also an expression

A .AND. B 

A conjunct expression is also an expression

.NOT. A 

A not expression is also an expression

A .EQ. B 

A comparison expression is also an expression

A // B 

A concatenation expression is also an expression

A - B 

A summation expression is also an expression

- A 

A summation expression is also an expression

A * B 

A multiplication expression is also an expression

A ** B 

An exponentiation expression is also an expression

.INVERSE. B 

A defined unary expression is also an expression

A 

A primary is also an expression

Summary of the Forms and Hierarchy for Expressions

The previous sections have described in detail the sorts of expressions that can be formed. These expressions form a hierarchy that can best be illustrated by a figure. Figure 7-1, describes the hierarchy by placing the simplest form of an expression, a variable, at the center of a set of nested rectangles. The more general forms of an expression are the enclosing rectangles, from a primary to an exponentiation expression, to a summation expression, and finally to a general expression using a defined binary operator .CROSS.. Figure 7-1 demonstrates that an expression is all of these special case forms, including the simplest form, a primary.

Figure 7-1. The hierarchy of expressions by examples

The hierarchy of expressions by examples

Table 7-2, illustrates the relationship between the different sorts of expressions by summarizing the definitional forms in one table. The simplest form of an expression is at the bottom and is the primary, as in Figure 7-1. The next, more general, form is second from the bottom and is the defined unary expression; it uses the primary in its definition. At the top of the table is the most general form of an expression.

Precedence of Operators

Table 7-3 summarizes the relative precedence of operators, including the precedence when operators of equal precedence are adjacent. An entry of N/A in the rightmost column indicates that the operator cannot appear in such contexts. The leftmost column classifies the operators as defined, numeric, character, relational, and logical operators. Note that these operators are not intrinsic operators unless the types of the operands are those specified in Table 7-4.

Table 7-3. Categories of operations and relative precedences

Category of operator

Operator

Precedence

In context of equal precedence

Defined

Unary defined-operator

Highest

N/A

Numeric

**

.

Right-to-left

Numeric

* or /

.

Left-to-right

Numeric

Unary + or -

.

N/A

Numeric

Binary + or -

.

Left-to-right

Character

//

.

Left-to-right

Relational

.EQ., .NE., .LT., .LE., .GT., .GE., .LG., ==, /=, <, <=, >, >=, <>

.

N/A

Logical or Boolean

.NOT.

.

N/A

Logical or Boolean

.AND.

.

Left-to-right

Logical or Boolean

.OR.

.

Left-to-right

Logical

.EQV. or .NEQV.

.

Left-to-right

Logical or Boolean

.XOR.

.

Left-to-right

Defined

Binary defined-operator

Lowest

Left-to-right

For example, consider the following expression:

A .AND. B .AND. C .OR. D

Table 7-2, indicates that the .AND. operator is of higher precedence than the .OR. operator, and the .AND. operators are combined left-to-right when in contexts of equal precedence; thus, A and B are combined by the .AND. operator, the result A .AND. B is combined with C using the .AND. operator, and that result is combined with D using the .OR. operator. Thus, this expression is interpreted the same way as the following fully parenthesized expression:

(((A .AND. B) .AND. C) .OR. D)

Notice that the defined operators have fixed precedences; defined unary operators have the highest precedence of all operators and are all of equal precedence; defined binary operators have the lowest precedence, are all of equal precedence, and are combined left-to-right when in contexts of equal precedence. Both kinds of defined operators may have multiple definitions in the program unit and therefore may be generic just as intrinsic operators and intrinsic procedures are generic.

As a consequence of the expression formation rules, unary operators cannot appear in a context of equal precedence; the precedence must be specified by parentheses. There is thus no left-to-right or right-to-left rule for any unary operators. Similarly, the relational operators cannot appear in a context of equal precedence; consequently, there is no left-to-right or right-to-left rule for the relational operators. Use of some of the operators as Boolean or masking operators is an extension to the Fortran standard that is supported by the CF90 and MIPSpro 7 Fortran 90 compilers.


Note: The Fortran standard does not specify the use of operators as Boolean or masking operators.


Intrinsic Operations

Intrinsic operations are those known to the processor. For an operation to be intrinsic, an intrinsic operator symbol must be used, and the operands must be of the intrinsic types specified in Table 7-4.


Note: : In the following table, the symbols I, R, Z, C, L, B, and P stand for the types integer, real, complex, character, logical, Boolean, and Cray pointer, respectively. Where more than one type for x2 is given, the type of the result of the operation is given in the same relative position in the next column. Boolean and Cray pointer types are CF90 and MIPSpro 7 Fortran 90 extensions.


Table 7-4. Operand types and results for intrinsic operations

Intrinsic operator

Type of x1

Type of x2

Type of result

Unary +, -

 

I, R, Z, B, P

I, R, Z, I, P

Binary +, -, *, /, **

I

I, R, Z, B, P

I, R, Z, I, P

 

R

I, R, Z, B

R, R, Z, R

 

Z

I, R, Z

Z, Z, Z

 

B

I, R, B, P

I, R, B, P

 

P

I, B, P

P, P, P

 

(For Cray pointer, only + and - are allowed.)

 

 

//

C

C

C

.EQ., ==, .NE., /=

I

I, R, Z, B, P

L, L, L, L, L

 

R

I, R, Z, B, P

L, L, L, L, L

 

Z

I, R, Z, B, P

L, L, L, L, L

 

B

I, R, Z, B, P

L, L, L, L, L

 

P

I, R, Z, B, P

L, L, L, L, L

 

C

C

L

.GT., >, .GE., >=, .LT., <, .LE., <=

I

I, R, B, P

L, L, L, L

 

R

I, R, B

L, L, L

 

C

C

L

 

P

I, P

L, L

.LG., <>

R

R

L

.NOT.

 

L

L

 

 

I, R, B

B

.AND., .OR., .EQV., .NEQV., .XOR.

L

L

L

 

I, R, B

I, R, B

B



Note: The Fortran standard does not specify the use of type Boolean or the use of type Boolean in masking expressions, nor does it describe the .LG. and <> operators.

The intrinsic operations are either binary or unary. The binary operations use the binary intrinsic operator symbols +, -, *, /, **, //, .EQ., .NE., .LT., .GT., .LE., .GE., and .LG.(and their synonyms ==, /=, <>, <, >=, and <>), .AND., .OR., .XOR., .EQV., and .NEQV.. The unary operations use the unary intrinsic operator symbols +, -, and .NOT..

Note that the intrinsic operators .AND., .OR., .NOT., and .XOR. can be abbreviated as .A., .O., .N., or .X.. If a user-defined operator with the same name as the abbreviated name is accessible in a scope, the abbreviated forms of these operators may not be used as synonyms for .AND., .OR., .NOT., or .XOR..

The intrinsic operations are divided into five classes with different rules and restrictions for the types of the operands. The five classes are numeric intrinsic, character intrinsic, logical intrinsic, relational intrinsic operations, and bitwise masking expressions.

The numeric intrinsic operations use the intrinsic operators +, -, *, /, and **. The operands can be of any numeric type and with any kind type parameters. The result of the operation is of a type specified by Table 7-4, and has type parameters as specified in “Type and Type Parameters of the Result of an Operation”.

For example, the following expressions, in which I, R, D, and Z are declared to be of types integer, real, double-precision real, and complex, have the types and type parameters of the variables R, I, D, and Z, respectively:

I + R
I * I
I - D
I / Z

There is only one character intrinsic operation; it uses the intrinsic operator //. The result of a character intrinsic operation is type character.

The logical intrinsic operations use the intrinsic operators .AND., .OR., .NOT., .EQV., and .NEQV., respectively. The result of a logical intrinsic operation is type logical and has type parameters as specified in “Type and Type Parameters of the Result of an Operation”.

The relational intrinsic operations use the intrinsic operators .EQ., .NE., .GT., .GE., .LT., .LE., or .LG. or their symbolic synonyms. A relational intrinsic operation is a numeric relational intrinsic operation if its operands are of type integer, real, or complex. A relational intrinsic operation is a character relational intrinsic operation if its operands are of type character. The result of either kind of relational operation is type logical and has type parameters as specified in “Type and Type Parameters of the Result of an Operation”.


Note: The Fortran standard does not describe the .LG. operator.

The operators .NOT., .AND., .OR., .EQV., and .XOR. can also be used in the CF90 and MIPSpro 7 Fortran 90 compilers' bitwise masking expressions; these are extensions to the Fortran standard. On UNICOS and UNICOS/mk systems, the result is Boolean (typeless) and has no kind type parameters. On IRIX systems, the result is type integer.

Defined Operations

A defined operation is any nonintrinsic operation that is interpreted and evaluated by a function subprogram specified by an interface block with a generic specifier of the following form:

OPERATOR (defined_operator)

A defined elemental operation is a defined operation for which the function is elemental.

A defined operation uses either a defined operator or an intrinsic operator symbol, and it is either unary or binary. Its forms are as follows:

intrinsic_unary_opx2
defined_operatorx2
x1intrinsic_binary_opx2
x1defined_operatorx2

The terms intrinsic_unary_op and intrinsic_binary_op include all intrinsically defined operators; these terms are not specifically defined in any syntax rules.

x1 and x2 are operands. When an intrinsic operator symbol is used, the type of x2 (for a unary operator) and types of x1 and x2 (for a binary operator) must not be the same as the types of the operands specified in Table 7-4, for the particular intrinsic operator symbol. Thus, you cannot redefine intrinsic operations on intrinsic types.

When a defined operation uses an intrinsic operator symbol, the generic properties of that operator are extended to the new types specified by the interface block. When a defined operation uses a defined operator, the defined operation is called an extension operation, and the operator is called an extension operator. An extension operator can have generic properties by specifying more than one function subprogram in an interface block with a generic specifier of the form OPERATOR (defined_operator).

Data Type, Type Parameters, and Shape of an Expression

The data type, type parameters, and shape of a complete expression are determined by the data type, type parameters, and shape of each constant, variable, constructor, and function reference appearing in the expression. The determination is inside-out in the sense that the properties are determined first for the primaries. These properties are then determined repeatedly for the operations in precedence order, resulting eventually in the properties for the expression.

For example, consider the expression A + B * C, where A, B, and C are of numeric type. First, the data types, type parameter values, and shapes of the three variables A, B, and C are determined. Because * has a higher precedence than +, the operation B * C is performed first. The type, type parameters, and shape of the expression B * C are determined next, and then these properties for the entire expression are determined from those of A and B * C.

A defined elemental operation is a defined operation for which the function is elemental.

Data Type and Type Parameters of a Primary

The type, type parameters, and shape of a primary that is a nonpointer variable or constant are straightforward because these properties are determined by specification statements for the variable or named constant, or by the form of the constant. For example, if A is a variable, its declaration in a specification statement such as the following determines it as an explicit-shaped array of type real with a default kind parameter:

REAL A(10, 10)

For a constant such as the following, the form of the constant indicates that it is a scalar constant of type complex and of default kind:

(1.3, 2.9)

For a pointer variable, the type, type parameters, and rank are determined by the declaration of the pointer variable. However, if the pointer is of deferred shape, the shape (in particular, the extents in each dimension) is determined by the target of the pointer. Consider the following declarations and assume that pointer A is associated with the target B:

REAL, POINTER :: A(:, :)
REAL, TARGET  :: B(10, 20)

The shape of A is (10, 20).

The type and type parameters of an array constructor are determined by the contents of the constructor. Unless the element is of type Boolean (typeless), its type and type parameters are those of any element of the constructor because they must all be of the same type and type parameters. If the element is of type Boolean, the type and kind type of the array constructor are the same as the default integer type. Therefore, the type and type parameters of the following array constructor are integer and kind value 1:

(/ 1_1, 123_1, -10_1 /)

Its shape is always of rank one and of size equal to the number of elements.

The type of a structure constructor is the derived type used as the name of the constructor. A structure has no type parameters. So, the type of the following structure constructor is the derived type PERSON:

PERSON(56, 'Father')

(See “Array Constructors” in Chapter 4, for the type definition PERSON.)

A structure constructor is always a scalar.

The type, type parameters, and shape of a function are determined by one of the following:

  • An implicit type declaration for the function within the program unit referencing the function

  • An explicit type declaration for the function within the program unit referencing the function (just like a variable)

  • An explicit interface to the function. (When the interface is not explicit, the function is either an external function or a statement function.)

If the interface is explicit, the type, type parameter, and shape are determined by one of the following:

  • The type and other specification statements for the function in an interface block within the program unit referencing the function

  • The type and other specification statements for the internal or module procedure specifying the function

  • The description of the particular intrinsic function being referenced

Note, however, that because intrinsic functions and functions with interface blocks can be generic, these properties are determined by the type, type parameters, and shapes of the actual arguments of the particular function reference.

For example, consider the following statements as part of the program unit specifying an internal function FCN:

REAL FUNCTION FCN(X)
DIMENSION FCN(10, 15)

A reference to FCN(3.3) is of type default real with shape (10,15). As a second example, consider the following:

REAL(SINGLE)  X(10, 10, 10)
   . . .
   . . .  SIN(X)   . . .  

The interface to SIN(3i) is specified by the definition of the sine intrinsic function. In this case, the function reference SIN(X) is of type real with kind parameter value SINGLE and of shape (10,10,10).

The interface is implicit if the function is external (and no interface block is provided) or is a statement function. In these cases, the shape is always that of a scalar, and the type and type parameters are determined by the implicit type declaration rules in effect, or by an explicit type declaration for the function name. In the following example, FCN(X) is a scalar of type integer with kind type parameter value SHORT:

IMPLICIT INTEGER(SHORT) (A-F)
   . . .
   . . .  FCN(X)   . . .  

The one case for variables and functions that is not straightforward is the determination of the shape of a variable when it is of deferred shape or of assumed shape. For a deferred-shape array, the rank is known from the declaration but the size of each dimension is determined as the result of executing an ALLOCATE statement or a pointer assignment statement. For an assumed-shape array, the rank is also known from the declaration but the size is determined by information passed into the subprogram. In the case of pointers, the shape of the object is that of the target associated with (pointed to by) the pointer. The shape of deferred-shape and assumed-shape arrays thus cannot be determined in general until execution time.

Type and Type Parameters of the Result of an Operation

The type of the result of an intrinsic operation is determined by the type of the operands and the intrinsic operation and is specified by Table 7-4.

For nonnumeric operations, the type parameters of the result of an operation are determined as follows:

  • For the relational intrinsic operations, the kind type parameter is that for the default logical type.

  • For logical intrinsic operations, the result kind type parameter depends on that of the operands. If the operands have the same kind type parameter (that is the same value for KIND=value), the kind type parameter is that of the operands. If logical operands have different kind type parameter values, the value of the result is that of the greater kind type parameter value.

  • For the character intrinsic operation (//), the operands must have the same kind type parameter, so the result has that kind type parameter.

  • The character length parameter value for the result is the sum of the character length parameters of the operands.

For numeric intrinsic operations, the kind type parameter value of the result is determined as follows:

  • For unary operations, the kind type parameter value of the result is that of the operand.

  • Floating-point operands are those of type real or type complex. If an operation includes a floating-point operand and an integer, the result type and kind type parameter value is determined as follows:

    • The result type is that of the floating-point operand.

    • The result kind type parameter value is the same as the kind type parameter value of the floating-point operand.

      See Table 7-4, for more information.

  • If an operation includes operands of type complex and type real, the result type and kind type parameter value is determined as follows:

    • The result type is type complex.

    • The result kind type parameter value is that of the greater value. That is, the kind type parameter value of each operand is examined, and whichever kind type parameter value is greater is assigned to the result.

      See Table 7-4, for more information.

  • For binary operations, if the operands are of the same type and kind type parameters, the kind type parameter value of the result is the kind type parameter of the operands.

  • For binary operations, if the operands are both of type integer but with different kind type parameters, the kind type parameter value of the result is the kind type parameter of the operand with the larger decimal exponent range. If the decimal exponent ranges of the two kinds are the same, the kind type parameter value of the result is the same as each operand.

  • For binary operations, if the operands are both of type real or complex but with different kind type parameters, the kind type parameter of the result is the kind type parameter of the operand with the larger decimal precision. If the decimal precisions are the same, the kind type parameter value is that of the operands.

For numeric intrinsic operations, an easy way to remember the result type and type parameter rules is to consider that the numeric types (integer, real, complex, and CF90 and MIPSpro 7 Fortran 90 Boolean) are ordered by the increasing generality of numbers. Integers are contained in the set of real numbers and real numbers are contained in the set of complex numbers. Within the integer type, the kinds are ordered by increasing decimal exponent ranges. Within the real and complex types, the kinds for each type are ordered by increasing decimal precision.

Using this model, the result type of a numeric intrinsic operation is the same type as the operand of the greater generality. For the result type parameter, the rule is complicated: if one or both of the operands is of type real or complex, the type parameter is that of the set of numbers of the more general type described above and with a precision as large as the precision of the operands; if both are of type integer, the result type parameter is of a set of numbers that has a range as large as the range of the operands.

Figure 7-2. Example ordering of numeric types on UNICOS systems

Example ordering of numeric types on UNICOS systems

To illustrate this ordering, consider Figure 7-2, which shows ordering on UNICOS systems. For integers, KIND=4 is 32-bit format and KIND=8 is a 64-bit format, with decimal exponent ranges 9 and 18. For the two kinds of reals, KIND=8 is a 64-bit format and KIND=16 is a 128-bit format, with decimal precisions 13 and 27. For the two kinds of complex, KIND=8 is a 128-bit format and KIND=16 is a 256-bit format, with decimal precisions 13 and 27. Let variables of the 6 numeric types be I4, I8, R8, R16, C8, and C16, where the letter designates the type and the digits designate the kind type parameter. Using this ordering, Table 7-5 shows the type and type parameters of some simple expressions.

Table 7-5. Type and type parameters of some simple expressions

Expressions

Type and type parameters are the same as the variable

I4 + R8

R8

I8 * C16

C16

C8 / C16

C16

I4 - I8

I8

I4 ** C8

C8

R16 + C8

C16

C8 ** I4

C8

I8 - R8

R8

The type and type parameter values of a defined operation are determined by the interface block for the referenced operation and are the type and type parameters of the name of the function specified by the interface block. Note that the operator can be generic and therefore the type and type parameters can be determined by the operands. For example, consider the following interface:

INTERFACE OPERATOR (.PLUS.)

   TYPE(SET) FCN_SET_PLUS(X, Y)
      USE DEFINITIONS
      TYPE (SET)  X, Y
      INTENT(IN) X, Y
   END FUNCTION FCN_SET_PLUS

   TYPE(RATIONAL) FCN_RAT_PLUS(X, Y)
      USE DEFINITIONS
      TYPE(RATIONAL)  X, Y
      INTENT(IN) X, Y
   END FUNCTION FCN_RAT_PLUS

END INTERFACE

The operation A .PLUS. B, where A and B are of type RATIONAL, is an expression of type RATIONAL with no type parameters. The operation C .PLUS. D, where C and D are of type SET is an expression of type SET with no type parameters.

Shape of an Expression

The shape of an expression is determined by the shape of each operand in the expression in the same recursive manner as for the type and type parameters for an expression. That is, the shape of an expression is the shape of the result of the last operation determined by the interpretation of the expression.

However, the shape rules are simplified considerably by the requirement that the operands of binary intrinsic operations must be in shape conformance; two operands are in shape conformance if both are arrays of the same shape, or one or both operands are scalars. The operands of a defined operation have no such requirement but must match the shape of the corresponding dummy arguments of the defining function.

For primaries that are constants, variables, constructors, or functions, the shape is that of the constant, variable, constructor, or function name. Recall that structure constructors are always scalar, and array constructors are always rank-one arrays of size equal to the number of elements in the constructor. For unary intrinsic operations, the shape of the result is that of the operand. For binary intrinsic operations, the shape is that of the array operand if there is one and is scalar otherwise. For defined operations, the shape is that of the function name specifying the operation.

For example, consider the intrinsic operation A + B where A and B are of type default integer and default real respectively; assume A is a scalar and B is an array of shape (3, 5). Then, the result is of type default real with shape (3, 5).

The Extents of an Expression

For most contexts, the lower and upper bounds of an array expression are not needed; only the sizes of each dimension are needed to satisfy array conformance requirements for expressions. The bounds of an array expression when it is the ARRAY argument (first positional argument) of the LBOUND(3i) and UBOUND(3i) intrinsic functions are needed, however.

The functions LBOUND(3i) and UBOUND(3i) have two keyword arguments ARRAY and DIM. ARRAY is an array expression and DIM, which is optional, is an integer. If the DIM argument is present, LBOUND(3i) and UBOUND(3i) return the lower and upper bounds, respectively, of the dimension specified by the DIM argument. If DIM is absent, they return a rank-one array of the lower and upper bounds, respectively, of all dimensions of the ARRAY argument. As described below, these functions distinguish the special cases when the array argument is a name or structure component with no section subscript list from the general case when the array argument is a more general expression. Note that if A is a structure with an array component B, A%B is treated as if it were an array name and not an expression.

When the ARRAY argument is an array expression that is not a name or a structure component, the function LBOUND(3i) returns 1 if the DIM argument is specified and returns a rank-one array of 1s if the DIM argument is absent. For the same conditions, the function UBOUND(3i) returns as the upper bound the size of the requested dimension or the size of all dimensions in a rank-one array.

When the ARRAY argument is an array name or a structure component with no section subscript list, there are four cases to distinguish depending on the array specifier for the name. The following sections describe these four cases.

Explicit-shape Specifier

The LBOUND(3i) and UBOUND(3i) functions return the declared lower and upper bounds of the array name or the structure component with no section subscript list.

INTEGER  A(2:10, 11:12)
. . .
TYPE PASSENGER_INFO
   INTEGER NUMBER
   INTEGER TICKET_IDS(2:500)
END TYPE PASSENGER_INFO
. . .
TYPE(PASSENGER_INFO) PAL, MANY(3:10)

In this example, LBOUND(A) has the value (/ 2, 11 /), and UBOUND(A, 1) has the value 10. LBOUND(PAL%TICKET_IDS) has the value (/ 2 /) and UBOUND(MANY%TICKET_IDS(2), 1) has the value 10.

Assumed-shape Specifier

The name is a dummy argument whose extents are determined by the corresponding actual argument. The dummy argument may have its lower bound in a particular dimension specified but if not, the lower bound is defined to be 1. The LBOUND(3i) function returns these lower bounds. The upper bound for a particular dimension is the extent of the actual argument in that dimension, if no lower bound is specified for the dummy argument. It is the extent minus 1 plus the lower bound if a lower bound is specified. The UBOUND(3i) function returns these upper bounds.

REAL  C(2:10, 11:12)
   . . .
CALL  S(C(4:8, 7:9))
CONTAINS
  SUBROUTINE S(A)
    REAL  A(:, 2:)
       . . .
    ! Reference to LBOUND(A)  and  UBOUND(A)
       . . .  

Inside the body of subroutine S, LBOUND(A) has the value (/ 1, 2 /), because the array starts at subscript position 1 by default in the first dimension and starts at subscript position 2 by declaration in the second dimension. UBOUND(A) has the value (/ 5, 4 /), because there are five subscript positions (4 to 8) in the first dimension of the actual argument corresponding to A, and three subscript positions (7 to 9) in the second dimension of the same actual argument and the subscripts are specified to start at 2 by the declaration of the dummy argument A.

Assumed-size Specifier

The name is a dummy argument whose upper and lower bounds in all but the last dimension are declared for the dummy argument. The lower bound for the last dimension may be specified in the assumed-size specifier but, if absent, the lower bound is 1. The LBOUND(3i) function returns these lower bounds. The upper bound for all dimensions except the last one is known to the subprogram but the upper bound in the last dimension is not known. The UBOUND(3i) function, therefore, must not be referenced with the first argument being the name of an assumed-size array and no second argument, or the first argument being the name of an assumed-size array and the second argument specifying the last dimension of the array. Otherwise, the UBOUND(3i) function returns the upper bounds as declared for all but the last dimension.

REAL  C(2:10, 11:12)
   . . .
CALL  S (C(4:8, 7:9))
CONTAINS
  SUBROUTINE  S (A)
    REAL  A(-2:2, *)
       . . .
 ! Reference to LBOUND(A, 1)  and  UBOUND(A(:, 2))
 ! A reference to UBOUND(A) would be illegal.
 ! A reference to UBOUND(A, 2) would be illegal.
    . . .  

Inside the body of subroutine S, LBOUND(A, 1) has the value -2. UBOUND(A(:, 2)) has the value 5 because A(:,2) is an expression, which is an array section, not an array name, and has five elements in the first dimension.

Deferred-shape Specifier

The name is the name of an allocatable array, an array pointer, or a structure component with one of its part references being a pointer array. As such, if the array or a part reference has not been allocated or associated with a target, the LBOUND(3i) and UBOUND(3i) functions must not be invoked with the ARRAY argument equal to such an array name. If it is an array pointer, either its target has been allocated by an ALLOCATE statement or its target has become associated with the pointer using a pointer assignment statement. In the former case, the LBOUND(3i) and UBOUND(3i) functions return the lower and upper bounds specified in the ALLOCATE statement. In the latter case, LBOUND is always 1 in pointer assignment, and UBOUND is the extent of the dimension. Note that for zero-sized dimensions, LBOUND is always 1 and UBOUND is always 0.

REAL, ALLOCATABLE ::  A(:, :)
   . . .
ALLOCATE  ( A(5, 7:9) )
   . . .! Reference to LBOUND(A)  and  UBOUND(A)
   . . .  

After the ALLOCATE statement above is executed, LBOUND(A) has the value (/ 1, 7 /), and UBOUND(A) has the value (/ 5, 9 /).

Special Expressions

Expressions can appear in statements other than assignment statements, in particular in specification statements. In many cases, such expressions are restricted in some way; for example, the operands in expressions in a PARAMETER statement are essentially restricted to constants. The standard and this manual use specific terms for the various categories of expressions allowed in specific syntactic contexts. For example, the expressions that can be used in PARAMETER statements are called initialization expressions and can be evaluated at the time the program is compiled. Initialization expressions are restricted forms of constant expressions.

The expressions that can be used as array bounds and character lengths in specification statements are called specification expressions. These expressions are scalar and of type integer and can be evaluated on entry to the program unit at the time of execution. The remainder of this subsection describes and defines such limited expressions and summarizes where they can be used.

Constant Expressions

A constant expression is one of the following constant values or is an expression consisting of intrinsic operators whose operands are any of the following constant values:

  1. A literal or named constant, or a subobject of a constant where each subscript, section subscript, or starting and ending point of a substring range is a constant expression.

  2. An array constructor where every subexpression has primaries that are constant expressions or are implied-DO variables of the array constructor.

  3. A structure constructor where each component is a constant expression.

  4. An elemental intrinsic function reference that can be evaluated at compile time.

  5. A transformational intrinsic function reference that can be evaluated at compile time.

  6. A reference to NULL(3i).

  7. A reference to an intrinsic function that is one of the following:

    • An array inquiry function other than ALLOCATED(3i)

    • The bit inquiry function BIT_SIZE(3i)

    • The character inquiry function LEN(3i)

    • The KIND(3i) inquiry function

    • A numeric inquiry function

    Each argument of the function must be a constant expression or must be a variable whose type parameters or bounds inquired about are not assumed, defined by an expression that is not a constant expression, or definable by an ALLOCATE or pointer assignment statement.

  8. An implied-DO variable within an array constructor in which the bounds and strides of the corresponding implied-DO are constant expressions.

  9. A constant expression enclosed in parentheses.

The restriction in item 4 to intrinsic functions that can be evaluated at compile-time eliminates the use of the intrinsic functions PRESENT(3i), ALLOCATED(3i), and ASSOCIATED(3i). It also requires that each argument of the intrinsic function reference be a constant expression or a variable whose type parameters or bounds are known at compile time. This restriction excludes, for example, named variables that are assumed-shape arrays, assumed-size arrays for inquiries requiring the size of the last dimension, and variables that are pointer arrays or allocatable arrays. For example, if an array X has explicit bounds in all dimensions, an inquiry such as SIZE(X) can be computed at compile-time, and SIZE(X) + 10 is considered a constant expression.

Constant expressions can be used in any executable statement where general expressions (that is, unrestricted expressions) are permitted.

The following examples show constant expressions:

Expression

Meaning

2

An integer literal constant

-7.5_LARGE

A real literal constant where LARGE is a named integer constant

(/ 7, (I, I = 1, 10) /)

An array constructor

RATIONAL(1, 2+J)

A structure constructor where RATIONAL is a derived type and J is a named integer constant

LBOUND(A,1)+3

A reference to an inquiry intrinsic function where A is an explicit-shape array

INT(N)

An intrinsic function reference where N is a named constant

KIND(X)

An intrinsic function reference where X is a real variable with known type parameter

REAL(10+I)

An intrinsic function reference where I is a named integer constant

COUNT(A)

An intrinsic function where A is a named logical constant

I/3.3 + J**3.3

A numeric expression where I and J are named integer constants

SUM(A)

A reference to a transformational intrinsic function where A is a named integer array constant

Initialization Expressions

An initialization expression is a constant expression restricted as follows:

  • The exponentiation operator (**) is allowed only when the power (second operand) is of type integer; that is, X ** Y is allowed only if Y is of type integer.

  • Subscripts, section subscripts, starting and ending points of substring ranges, components of structure constructors, and arguments of intrinsic functions must be initialization expressions.

  • The elements of array constructors must be initialization expressions or implied-DOs for which the array constructor values and implied-DO parameters are expressions whose primaries are initialization expressions or implied-DO variables.

  • An elemental intrinsic function in an initialization expression must have arguments that are initialization expressions and are of type integer or character. These elemental intrinsic functions must return a result of type integer or character.

  • A transformational intrinsic function in an initialization expression must be one of the intrinsic functions NULL(3i), REPEAT(3i), RESHAPE(3i), SELECTED_INT_KIND(3i), SELECTED_REAL_KIND(3i), TRANSFER(3i), and TRIM(3i), and must have initialization expressions as arguments; this excludes the use of the transformational functions ALL(3i), ANY(3i), COUNT(3i), CSHIFT(3i), DOT_PRODUCT(3i), EOSHIFT(3i), MATMUL(3i), MAXLOC(3i), MAXVAL(3i), MINLOC(3i), MINVAL(3i), PACK(3i), PRODUCT(3i), SPREAD(3i), SUM(3i), TRANSPOSE(3i), and UNPACK(3i).

  • An inquiry intrinsic function is allowed, except that the arguments must either be initialization expressions or variables whose type parameters or bounds inquired about are not assumed, not defined by an ALLOCATE statement, or not defined by pointer assignment.

  • Any subexpression enclosed in parentheses must be an initialization expression.

All but the last examples in “Constant Expressions”, are initialization expressions. The last are not because initialization expressions cannot contain functions that return results of type real (REAL(3i), LOG(3i)), must not reference certain transformational functions (COUNT(3i), SUM(3i)), or cannot use the exponentiation operator when the second operand is of type real.

The following are examples of initialization expressions:

Expression

Meaning

SIZE(A, 1) * 4

An integer expression where A is an array with an explicit shape

KIND(0.0D0)

An inquiry function with a constant argument

SELECTED_REAL_KIND(6, 30)

An inquiry function with constant arguments

SELECTED_INT_KIND(2 * R)

An inquiry function with an argument that is an initialization expression, where R is a previously declared named constant of type integer

Initialization expressions must be used in the following contexts:

  • As initial values following the equal signs in PARAMETER statements and in type declaration statements with the PARAMETER attribute.

  • As initial values following the equal signs in type declaration statements for variables.

  • As expressions in structure constructors in DATA statement value lists.

  • As expressions in default initializers.

  • As kind type parameter values in type declaration statements; in this case, they also must be scalar and of type integer.

  • As actual arguments for the KIND(3i) dummy argument of the conversion intrinsic functions AINT(3i), ANINT(3i), CHAR(3i), INT(3i), LOGICAL(3i), NINT(3i), REAL(3i), CMPLX(3i); in this case, they also must be scalar and of type integer.

  • As case values in the CASE statement; in this situation, they must be scalar and of type integer, logical, or character.

  • As subscript or substring range expressions of equivalence objects in an EQUIVALENCE statement; in this case, they must be scalar and of type integer.

Initialization expressions must be used for situations where the value of the expression is needed at compile time. Note that the initialization expressions do not include intrinsic functions that return values of type real, logical, or complex, or have arguments of type real, logical, or complex.

Specification Expressions

A specification expression is a restricted expression that has a scalar value and is of type integer. Specification expressions are used as bounds for arrays and length parameter values for character entities in type declarations, attribute specifications, dimension declarations, and other specification statements (see Table 7-6). A constant specification expression is a specification expression that is also a constant.

Specification expressions are forms of restricted expressions (defined below), limited in type and rank. Briefly, a restricted expression is limited to constants and certain variables accessible to the scoping unit whose values can be determined on entry to the program unit before any executable statement is executed. For example, variables that are dummy arguments, are in a common block, are in a host program unit, or are in a module made accessible to the program unit can be evaluated on entry to a program unit. Array constructors, structure constructors, intrinsic function references, and parenthesized expressions made up of these primaries must depend only on restricted expressions as building blocks for operands in a restricted expression.

A restricted expression is an expression in which each operation is intrinsic and each primary is limited to one of the following:

  • A constant or constant subobject.

  • A variable that is a dummy argument with neither the OPTIONAL nor the INTENT(OUT) attribute.

  • A variable that is in a common block.

  • A variable made accessible from a module.

  • A variable from the host program unit.

  • A variable accessible through USE association.

  • An array constructor in which every expression has primaries that are restricted expressions or are implied-DO variables of the array constructor.

  • A structure constructor in which each component is a restricted expression.

  • An elemental intrinsic function whose result is of type integer or character and whose arguments are all restricted expressions of type integer or character.

  • One of the transformational intrinsic functions (REPEAT(3i), RESHAPE(3i), SELECTED_INT_KIND(3i), SELECTED_REAL_KIND(3i), TRANSFER(3i), or TRIM(3i)), in which each argument is a restricted expression of type integer or character (this excludes the use of the transformational functions ALL(3i), ANY(3i), COUNT(3i), CSHIFT(3i), DOT_PRODUCT(3i), EOSHIFT(3i), MATMUL(3i), MAXLOC(3i), MAXVAL(3i), MINLOC(3i), MINVAL(3i), PACK(3i), PRODUCT(3i), SPREAD(3i), SUM(3i), TRANSPOSE(3i), and UNPACK(3i)).

  • An inquiry intrinsic function (except for PRESENT(3i), ALLOCATED(3i), and ASSOCIATED(3i)), in which each argument is one of the following:

    • A restricted expression. Any subscript, section subscript, and starting or ending point of a substring range is a restricted expression.

    • A variable whose bounds or type parameters inquired about are not assumed, not defined by an ALLOCATE statement, and not defined by a pointer assignment statement

  • A reference to any other intrinsic function in which each argument is a restricted expression.

  • A reference to an external function whose result is a nonpointer scalar intrinsic type.

  • A reference to a specification function in which each argument is a restricted expression.

A function is a specification function if it is a pure function, is not an intrinsic function, is not an internal function, is not a statement function, does not have a dummy procedure argument, and is not RECURSIVE.


Note: The Fortran standard does not specify restricted expressions in which a primary can be a reference to an external function that is not a specification function and with a result that is a nonpointer scalar intrinsic type.


Initialization and Specification Expressions in Declarations

The following rules and restrictions apply to the use of initialization and specification expressions in specification statements.

The type and type parameters of a variable or named constant in one of these expressions must be specified in a prior specification in the same scoping unit, in a host scoping unit, in a module scoping unit made accessible to the current scoping unit, or by the implicit typing rules in effect. If the variable or named constant is explicitly given these attributes in a subsequent type declaration statement, it must confirm the implicit type and type parameters.

If an element of an array is referenced in one of these expressions, the array bounds must be specified in a prior specification.

If a specification expression includes a variable that provides a value within the expression, the expression must appear within the specification part of a subprogram. For example, consider variable N in the following program segment:

INTEGER  N
COMMON   N
REAL  A (N)

N is providing a value that determines the size of the array A. This program segment must not appear in a main program but may appear in the specification part of a subprogram.

A prior specification in the above cases may be in the same specification statement, but to the left of the reference. For example, the following declarations are valid:

INTEGER, DIMENSION(4), PARAMETER :: A = (/4, 3, 2, 1 /)
REAL, DIMENSION(A (2)) :: B, C(SIZE(B))

B and C are of size 3 (the second element of the array A). The following declaration, however, is invalid because SIZE(E) precedes E:

REAL, DIMENSION(2) :: D(SIZE(E)), E

Uses of the Various Kinds of Expressions

The various kinds of expressions may be somewhat confusing, and it can be difficult to remember where they can be used. To summarize the differences, “Rules for Forming Expressions”, specifies the most general kind of expression; the other kinds of expressions are restrictions of the most general kind. The classification of expressions forms two orderings, each from most general to least general, as follows:

  • Expression, restricted expression, and specification expression

  • Expression, constant expression, and initialization expression

The relationship between the various kinds of expression can be seen in the diagram in Figure 7-3.

Figure 7-3. Relationships between the kinds of expressions

Relationships between the kinds of expressions

Initialization expressions are not a subset of specification expressions because the result of an initialization expression can be of any type, whereas the result of a specification expression must be of type integer and scalar. Also, specification expressions are not a subset of initialization expressions because specification expressions allow certain variables (such as dummy arguments and variables in common blocks) to be primaries, where as initialization expressions do not allow such variables.

Table 7-6, describes the differences between initialization and specification expressions. Table 7-7, summarizes where each kind of expression is used and gives the restrictions as to their type and rank when used in the various contexts. For example, Table 7-6, indicates that initialization and specification expressions are different in that initialization expressions can be array valued, whereas specification expressions are scalar. A consequence of this difference, as indicated in Table 7-7, is that an initialization expression is used in a type declaration statement or a PARAMETER statement to specify the value of a named constant array, whereas a specification expression is used to specify the bounds of an array in a declaration statement.

Table 7-6. Differences and similarities between initialization and specification expressions

 

Kind of expression:

Property:

Initialization

Specification

Character result

Yes

No[a]

Integer result

Yes

Yes

Scalar result

Yes

Yes

Array result

Yes

No

Variables as primaries(limited to dummy arguments, common block objects, host objects, module objects)

No

Yes

Elemental intrinsic functions of type integer and character as primaries

Yes

Yes

Elemental intrinsic functions of type real, complex, logical, and derived type as primaries

No

No

Only constants as primaries

Yes

No

Only constant subscripts, strides, character lengths

Yes

No

One of the transformational intrinsic functions REPEAT, RESHAPE, SELECTED_INT_KIND, SELECTED_REAL_KIND, TRANSFER, or TRIM as primaries

Yes

Yes

Inquiry intrinsic functions (not including ALLOCATED, ASSOCIATED, or PRESENT) as primaries

Yes

Yes

Reference to specific functions

No

Yes

Reference to scalar external function with intrinsic type result that is not a specification function[b]

No

Yes

[a] Expression results of type character are allowed if they are arguments of an intrinsic function.

[b] This is an extension to the Fortran standard.


Table 7-7. Kinds of expressions and their uses

Context

Arb. expr.

Init. expr.

Spec. expr.

Type [a]

Rank [b]

Default integer expression

     

Bounds in declaration statement [c]

No

No

Yes

I

Scalar

Lengths in declaration statement [d]

No

No

Yes

I

Scalar

Subscripts and substring ranges in EQUIVALENCE statement

No

Yes

No

I

Scalar

Values in CASE statement

No

Yes

No

I,L,C

Scalar

Kind parameters in declaration statement

No

Yes

No

I

Scalar

Kind arguments in intrinsics

No

Yes

No

I

Scalar

Initial value in PARAMETER and type declaration statement

No

Yes

No

Any

Any

DATA implied-DO parameters

No

[e]

No

I

Scalar

Assignment

Yes

Yes

Yes

Any

Any

Subscripts in executable statement

Yes

Yes

Yes

I

≤1

Strides in executable statement

Yes

Yes

Yes

I

Scalar

Substring ranges in executable statement

Yes

Yes

Yes

I

Scalar

Expression in SELECT CASE

Yes

Yes

Yes

I,L,C

Scalar

IF statement

Yes

Yes

Yes

L

Scalar

Arithmetic IF statement

Yes

Yes

Yes

I,R

Scalar

DO statement

Yes

Yes

Yes

I,R

Scalar

Mask in WHERE statement

Yes

Yes

Yes

L

Array

Mask in WHERE construct

Yes

Yes

Yes

L

Array

IF-THEN statement

Yes

Yes

Yes

L

Scalar

ELSE-IF statement

Yes

Yes

Yes

L

Scalar

Output item list

Yes

Yes

Yes

Any

Any

I/O specifier values except character FMT= specifier [f]

Yes

Yes

Yes

I,C

Scalar

I/O FMT= specifier value

Yes

Yes

Yes

C

Any

RETURN statement

Yes

Yes

Yes

I

Scalar

Computed GO TO statement

Yes

Yes

Yes

I

Scalar

Array constructor implied-DO parameters

Yes

Yes

Yes

I

Scalar

Actual arguments

Yes

Yes

Yes

Any

Any

I/O implied-DO parameters

Yes

Yes

Yes

I,R

Scalar

FORALLtriplet_spec_list

Yes

Yes

Yes

I

Scalar

FORALLscalar_mask

Yes

Yes

Yes

L

Scalar

Expressions in statement function definitions

Yes

Yes

Yes

Any

Scalar

[a] "Any" in this column means any intrinsic or derived type.

[b] "Any" in this column means that the result may be a scalar or an array of any rank.

[c] The relevant declaration statements are type declaration, component definition, DIMENSION , TARGET , and COMMON statements.

[d] The relevant declaration statements are type declaration, component definition, IMPLICIT, and FUNCTION statements.

[e] A DATA implied-DO parameter may be an expression involving intrinsic operations with constants and DATA implied-DO variables as operands.

[f] If the I/O FMT= specifier is not of type character, it must be a default integer variable.

Example 1: The expressions I*3, 1+2*J, 5*J/3, 1, and 10 in the following statement, are all expressions allowed in subscripts and implied-DO parameter expressions in an implied-DO list in a DATA statement:

DATA ((A(I*3), I = 1+2*J, 5*J/3), J = 1, 10) /.../

Example 2: An expression such as RADIX(I) is not allowed as a data implied-DO parameter or subscript of a DATA statement object.

An expression such as N, where N is a variable in the scoping unit that contains the DATA statement, is not allowed because N is neither a named constant nor an implied-DO variable in a containing implied-DO list.

Such special expressions in DATA statements are restricted forms of initialization expressions in the sense that the primaries must not include references to any intrinsic function. On the other hand, they are extended forms of initialization expressions in the sense that they permit the use of implied-DO variables that have the scope of the implied-DO list.

Interpretation of Expressions

The interpretation of an expression specifies the value of the expression when it is evaluated. As with the rules for forming an expression, the rules for interpreting an expression are described from the bottom up, from the interpretation of constants, variables, constructors, and functions to the interpretation of each subexpression to the interpretation of the entire expression.

When an expression is interpreted, the value of each constant and variable is determined. After these are determined, the operations for which the variables and constants are interpreted in precedence order, and a value for the operation is determined by the interpretation rules for each operator. This repeats recursively until the entire expression is interpreted and a value is determined.

The interpretation rules for operations are of two sorts: rules for the intrinsic operations (intrinsic operators with operands of the intrinsic types specified by Table 7-4) and rules for the defined operations (provided by the programmer using function subprograms). Except for integer division, the intrinsic operations are interpreted by the usual mathematical method, subject to representation limitations imposed by a computer (for example, a finite range of integers, or finite precision of real numbers). The defined operations are interpreted by a function program that is specified in an interface block with a generic specifier of the form OPERATOR(defined_operator).

The interpretation rules for an intrinsic or a defined operation are independent of the context in which the expression occurs. That is, the type, type parameters, and interpretation of any expression do not depend on any part of a larger expression in which it occurs.

Interpretation of the Intrinsic Operations

When the arguments of the intrinsic operators satisfy the requirements of Table 7-4, the operations are intrinsic and are interpreted in the usual mathematical way as described in Table 7-8, except for integer division. For example, the binary operator * is interpreted as the mathematical operation multiplication and the unary operator - is interpreted as negation.

Table 7-8. Interpretation of the intrinsic operations

Use of operator

Interpretation

x1 ** x2

Raise x1 to the power x2

x1 / x2

Divide x1 by x2

x1 * x2

Multiply x1 by x2

x1 - x2

Subtract x2 from x1

- x2

Negate x2

x1 + x2

Add x1 and x2

+ x2

Same as x2

x1 // x2

Concatenate x1 with x2

x1 .LT. x2

x1 less than x2

x1 < x2

x1 less than x2

x1 .LE. x2

x1 less than or equal to x2

x1 <= x2

x1 less than or equal to x2

x1 .GT. x2

x1 greater than x2

x1 > x2

x1 greater than x2

x1 .GE. x2

x1 greater than or equal to x2

x1 >= x2

x1 greater than or equal to x2

x1 .EQ. x2

x1 equal to x2

x1 == x2

x1 equal to x2

x1 .NE. x2

x1 not equal to x2

x1 /= x2

x1 not equal to x2

x1 .LG. x2

x1 less than or greater than x2

x1 <> x2

x1 less than or greater than x2

.NOT. x2

True if x2 is false

x1 .AND. x2

True if x1 and x2 are both true

x1 .OR. x2

True if x1 and/or x2 is true

x1 .NEQV. x2

True if either x1 or x2 is true, but not both

x1 .XOR. x2

1 if corresponding bits differ; 0 otherwise (bitwise exclusive .OR.)

x1 .EQV. x2

True if both x1 or x2 are true or both are false


Interpretation of Numeric Intrinsic Operations

Except for exponentiation to an integer power, when an operand for a numeric intrinsic operation does not have the same type or type parameters as the result of the operation, the operand is converted to the type, type parameter, and shape of the result and the operation is then performed. For exponentiation to an integer power, the operation can be performed without the conversion of the integer power, say, by developing binary powers of the first operand and multiplying them together to obtain an efficient computation of the result.

For integer division, when both operands are of type integer, the result is of type integer, but the mathematical quotient is often not an integer. In this case, the result is specified to be the integer value closest to the quotient and between zero and the quotient inclusively.

For exponentiation, there are three cases that need to be further described. When both operands are of type integer, the result is of type integer; when x2 is negative, the operation x1**x2 is interpreted as the quotient 1/(x1**ABS(x2)). Note that it is subject to the rules for integer division. For example, 4**(-2) is 0.

The second case occurs when the first operand is a negative value of type integer or real and the second operand is of type real. A program is invalid if it causes a reference to the exponentiation operator with such operands. For example, a program that contains the expression (-1.0)**0.5 is an invalid program.

The third case occurs when the second operand is of type real or of type complex. In this case, the result returned is the principal value of the mathematical power function.

Interpretation of Standard Nonnumeric Intrinsic Operations

There is only one intrinsic character operation: concatenation. For this operation, the operands must be of type character. The length parameter values can be different. The result is of type character with a character length parameter value equal to the sum of the lengths of the operands. The result consists of the characters of the first operand in order followed by those of the second operand in order. For example, 'Fortran' // '95' yields the result 'Fortran 95'.

The intrinsic relational operations perform comparison operations for character and most numeric operands. For these operations, the operands must both be of numeric type or both be of character type. The kind type parameter values of the operands of the numeric types can be different and the lengths of character operands can be different. Complex operands must only be compared for equality and inequality; the reason is that complex numbers are not totally ordered. The result in all cases is of type default logical.

When the operands of an intrinsic relational operation are both numeric, but of different types or type parameters, each operand is converted to the type and type parameters they would have if the two operands were being added. Then, the operands are compared according to the usual mathematical interpretation of the particular relational operator.

When the operands are both of type character, the shorter one is padded on the right with blank padding characters until the operands are of equal length. Then, the operands are compared one character at a time in order, starting from the leftmost character of each operand until the corresponding characters differ. The first operand is less than or greater than the second operand according to whether the characters in the first position where they differ are less than or greater than each other. The operands are equal if both are of zero length or all corresponding characters are equal, including the padding characters.

There is no ordering defined for logical values. However, logical values can be compared for equality and inequality by using the logical equivalence and not equivalence operators .EQV. and .NEQV.. That is, L1 .EQV. L2 is true when L1 and L2 are both true or both false and is false otherwise. L1.NEQV. L2 is true if either L1 or L2 is true (but not both true) and is false otherwise.

The intrinsic logical operations perform many of the common operations for logical computation. For these operations, the operands must both be of logical type but can have different kind type parameters. If the kind type parameter values are the same, the kind type parameter value of the result is the kind type parameter value of the operands. If the kind type parameter values are different, the kind type parameter value is the larger of the two kind type parameter values. The values of the result in all cases are specified in Table 7-9.

Table 7-9. The values of operations involving logical operators

x1

x2

.NOT.x1

x1.AND.x2

x1.OR.x2

x1.EQV.x2

x1.NEQV.x2

True

True

False

True

True

True

False

True

False

False

False

True

False

True

False

True

True

False

True

False

True

False

False

True

False

False

True

False


Interpretation of Intrinsic Operations with Array Operands

Each of the intrinsic operations can have array operands; however, for the binary intrinsic operations, the operands must both be of the same shape, if both are arrays. When one operand is an array and the other is a scalar, the operation behaves as if the scalar operand were broadcast to an array of the result shape and the operation performed.

For both the unary and binary intrinsic operators, the operation is interpreted element-by-element; that is, the scalar operation is performed on each element of the operand or operands. For example, if A and B are arrays of the same shape, the expression A * B is interpreted by taking each element of A and the corresponding element of B and multiplying them together using the scalar intrinsic operation * to determine the corresponding element of the result. Note that this is not the same as matrix multiplication. As a second example, the expression -A is interpreted by taking each element of A and negating it to determine the corresponding element of the result.

For intrinsic operations that appear in masked assignment statements (in WHERE blocks, ELSEWHERE blocks, or in a WHERE statement), the scalar operation is performed only for those elements selected by the logical mask expression.

Note that there is no order specified for the interpretation of the scalar operations. A processor is allowed to perform them in any order, including all at the same time.

Interpretation of Intrinsic Operations with Pointer Operands

The intrinsic operations can have operands with the POINTER attribute. In such cases, each pointer must be associated with a target that is defined, and the value of the target is used as the operand. The target can be scalar or array-valued; the rules for interpretation of the operation are those appropriate for the operand being a scalar or an array, respectively.

Recall that an operand can be a structure component that is the component of a structure variable that is itself a pointer. In this case, the value used for the operand is the named component of the target structure associated with the structure variable. For example, consider the following declarations and assume that the pointer PTR is associated with T:

TYPE RATIONAL
   INTEGER :: N, D
END TYPE

TYPE(RATIONAL), POINTER :: PTR
TYPE(RATIONAL), TARGET  :: T

If PTR%N appears as an operand, its value is the component N of the target T, namely T%N.

Interpretation of Defined Operations

The interpretation of a defined operation is provided by a function subprogram with an OPERATOR interface (see the Fortran Language Reference Manual, Volume 2). When there is more than one function with the same OPERATOR interface, the function giving the interpretation of the operation is the one whose dummy arguments match the operands in order, types, kind type parameters, and rank. For example, for the operation A .PLUS. B, where A and B are structures of the derived type RATIONAL, the following interface specifies that the function RATIONAL_PLUS provides the interpretation of this operation.

Example:

INTERFACE OPERATOR(.PLUS.)

   FUNCTION RATIONAL_PLUS(L, R)
      USE RATIONAL_MODULE
      TYPE(RATIONAL), INTENT(IN) :: L, R
      TYPE(RATIONAL)          :: RATIONAL_PLUS
   END FUNCTION RATIONAL_PLUS

   FUNCTION LOGICAL_PLUS(L, R)
      LOGICAL, INTENT(IN) :: L, R
      LOGICAL              :: LOGICAL_PLUS
   END FUNCTION LOGICAL_PLUS

END INTERFACE

A defined operation is declared by using a function with one or two dummy arguments. (Note that the function can be an entry in an external or module function.)

The dummy arguments to the function represent the operands of the operation. If there is only one, the operation is a unary operation; otherwise it is a binary operation. For a binary operation, the first argument is the left operand and the second is the right operand.

There must be an interface block for the function with the generic specifier of the form OPERATOR (defined_operator).

The types and kind type parameters of the operands in the expression must be the same as those of the dummy arguments of the function.

The function is elemental or the rank of the operands in the expression must match the ranks of the corresponding dummy arguments of the function.

One of the following conditions must be true:

  • One of the dummy arguments must be of a derived type

  • Both of the dummy arguments are of intrinsic type but do not match the types and kind type parameters for intrinsic operations as specified in Table 7-4.

As with the intrinsic operations, the type, type parameters, and interpretation of a defined operation are independent of the context of the larger expression in which the defined operation appears. The interpretation of the same defined operation in different contexts is the same, but the results can be different because the results of the procedure being invoked may depend on values that are not operands and that are different for each invocation.

The relational operators ==, /=, >, >=, <, <, and <> are synonyms for the operators .EQ., .NE., .GT., .GE., .LT., .LE., and .LG.even when they are defined operators. It is invalid, therefore, to have an interface block for both == and .EQ., for example, for which the order, types, type parameters, and rank of the dummy arguments of two functions are the same.


Note: The Fortran standard does not describe the <> or .LG. operators.

Defined operations are either unary or binary. An existing unary operator (that is, one that has the same name as an intrinsic operator) cannot be defined as a binary operator unless it is also a binary operator. Similarly, an existing binary operator cannot be defined as a unary operator unless it is also a unary operator. However, a defined operator, .PLUS. say, (that is, one that does not have a name that is the same as an intrinsic operator) can be defined as both a unary and binary operator.

Evaluation of Expressions

The form of the expression and the meaning of the operations establish the interpretation; once established, a compiler can evaluate the expression in any way that provides the same interpretation with one exception; parentheses specify an order of evaluation that cannot be modified. This applies to both intrinsic operations and defined operations.

Essentially, two sorts of alternative evaluations are allowed:

  • The rearrangement of an expression that yields an equivalent expression; for example, A + B + C can be evaluated equivalently as A + (B + C) and would improve the efficiency of the compiled program if B + C were a subexpression whose value had already been computed.

  • The partial evaluation of an expression because the value of the unevaluated part can be proven not to affect the value of the entire expression. For example, when one operand of a disjunction (.OR. operator) is known to be true, the other operand need not be evaluated to determine the result of the operation. To be specific, the operand A * B < C need not be evaluated in the expression A < B .OR. A * B < C if A < B is true. This freedom for a compiler to use alternative equivalent evaluations permits the compiler to produce code that is more optimal in some sense (for example, fewer operations, array operations rather than scalar operations, or a reduction in the use of registers or work space), and thereby produce more efficient executable code.

Possible Alternative Evaluations

Before describing in more detail the possible evaluation orders, four basic issues need to be addressed, namely, definition of operands, well-defined operations, functions (and defined operations) with side effects, and equivalent interpretations.

Definition status is described in detail in the Fortran Language Reference Manual, Volume 2. For the purpose of evaluation of expressions, it is required that each operand is defined, including all of its parts, if the operand is an aggregate (an array, a structure, or a string). If the operand is a subobject (part of an array, structure, or string), only the selected part is required to be defined. If the operand is a pointer, it must be associated with a target that is defined. An integer operand must be defined with an integer value rather than a statement label.

For the numeric intrinsic operations, the operands must have values for which the operation is well-defined. For example, the divisor for the division operation must be nonzero, and the result of any of the numeric operations must be within the exponent range for the result data type; otherwise, the program is not standard conforming. Other cases include limitations on the operands of the exponentiation operation **: for example, a zero-valued first operand must not be raised to a nonpositive second operand; and a negative-valued first operand of type real cannot be raised to a real power.

The third issue is functions with side effects. In Fortran, functions are allowed to have side effects; that is, they are allowed to modify the state of the program so that the state is different after the function is invoked than before it is invoked. This possibility potentially affects the equivalence of two schemes for evaluating an expression, particularly if the function modifies objects appearing in other parts of the expression. However, Fortran prohibits the formation of statements with these kinds of side effects. That is, a function (or defined operation) within a statement is not permitted to change any entity in the same statement. Exceptions are those statements that have statements within them, for example, an IF statement or a WHERE statement. In these cases, the evaluation of functions in the logical expressions in parentheses after the IF keyword or WHERE keyword are allowed to affect objects in the statement following the closing right parenthesis. For example, if F and G are functions that change their actual argument I, the following statements are valid, even though I is changed when the functions are evaluated:

IF (F(I))  A = I
WHERE (G(I))  B = I

The following statements are examples of statements that are not valid because F and G change I, which is used elsewhere in the same statement:

A(I) = F(I)
Y = G(I) + I

It is also not valid for there to be two function references in a statement, if each causes a side effect and the order in which the functions are invoked yields a different final status, even though nothing in the statement is changed.

The fourth issue is equivalent interpretation. For the numeric intrinsic operations, the definition of equivalent interpretation is defined as being mathematical equivalence of the expression, not computational equivalence. Mathematical equivalence assumes exact arithmetic (no rounding errors and infinite exponent range) and thus assumes the rules of commutativity, associativity, and distributivity as well as other rules that can be used to determine equivalence (except that the order of operations specified by parentheses must be honored). Under these assumptions, two evaluations are mathematically equivalent if they yield the same values for all possible values of the operands. A + B + C and A + (B + C) are thus mathematically equivalent but are not necessarily computationally equivalent because of possible different rounding errors. On the other hand, I/2 and 0.5 * I (where I is an integer) is a mathematical difference because of the special Fortran definition of integer division.

Table 7-10, gives examples of equivalent evaluations of expressions where A, B, and C are operands of type real or complex, and X, Y, and Z are of any numeric type. All of the variables are assumed to be defined and have values that make all of the operations in this table well-defined.

Table 7-10. Equivalent evaluations for numeric intrinsic operations

Expression

Equivalent evaluations

X+Y

Y+X

X*Y

Y*X

-X+Y

Y-X

X+Y+Z

X+(Y+Z)

X-Y+Z

X-(Y-Z)

X*A/Z

X*(A/Z)

X*Y-X*Z

X*(Y-Z)

A/B/C

A/(B*C)

A/5.0

0.2*A

Table 7-11 provides examples of alternative evaluations that are not valid and are not mathematically equivalent to the original expression. In addition to the operands of the same names used in Table 7-10, Table 7-11 uses I and J as operands of type integer. Recall that when both operands of the division operator are of type integer, a Fortran integer division truncates the result toward zero to obtain the nearest integer quotient.

Table 7-11. Nonequivalent evaluations of numeric expressions

Expression

Prohibited evaluations

I/2

0.5*I

X*I/J

X*(I/J)

I/J/A

I/(J*A)

(X+Y)+Z

X+(Y+Z)

(X*Y)-(X*Z)

X*(Y-Z)

X*(Y-Z)

X*Y-X*Z


Partial Evaluations

For character, relational, and logical intrinsic operations, the definition of the equivalence of two evaluations is that, given the same values for their operands, each evaluation produces the same result. The definition for equivalence of two evaluations of the same defined operation also requires the results to be the same; note that this definition is more restrictive than for the numeric intrinsic operations, because only mathematical equivalence need be preserved for numeric operations. As described for numeric intrinsic operations, a compiler can choose any evaluation scheme equivalent to that provided by the interpretation. Table 7-12 gives some equivalent schemes for evaluating a few example expressions. For these examples, I and J are of type integer; L1, L2, and L3 are of type logical; and C1, C2, and C3 are of type character of the same length. All of the variables are assumed to be defined.

Table 7-12. Equivalent evaluations of other expressions

Expression

Equivalent evaluations

I .GT. J

(I-J) .GT. 0

L1 .OR. L2 .OR. L3

L1 .OR. (L2 .OR. L3)

L1 .AND. L1

L1

C3 = C1//C2

C3=C1 (C1, C2, C3 all of the same length)

These rules for equivalent evaluation schemes allow the compiler to not evaluate any part of an expression that has no effect on the resulting value of the expression. Consider the expression X*F(Y), where F is a function and X has the value 0. The result will be the same regardless of the value of F(Y); therefore, F(Y) need not be evaluated. This shortened evaluation is allowed in all cases, even if F(Y) has side effects. In this case every data object that F could affect is considered to be undefined after the expression is evaluated (that is, it does not have a predictable value).

The appearance of an array element, an array section, or a character substring reference requires, in most cases, the evaluation of the expressions that are the subscripts, strides, or substring ranges. The type or type parameters of the containing expression are not affected by the evaluation of such subscript, stride, or substring range expressions. It is not necessary for these expressions to be evaluated, if the array section can be shown to be zero-sized or the substring can be shown to be of a zero-length by other means. For example, in the expression A(1:0) + B(expr1: expr2), expr1 and expr2 need not be evaluated because the conformance rules for intrinsic operations require that the section of B be zero-sized.

The type and type parameters, if any, of a constructor are not affected by the evaluation of any expressions within the constructor.

Parentheses within the expression must be honored. This is particularly important for computations involving numeric values in which rounding errors or range errors may occur or for computations involving functions with side effects.

Assignment

The most common use of the result of an expression is to give a value to a variable. This is done with an assignment statement. For example:

RUG = BROWN + 2.34 / TINT

The forms of intrinsic assignment, defined assignment, and masked array assignment are the same, and they are defined as follows:

 

assignment_stmt

is

variable = expr

Assignment establishes a value for the variable on the left of the assignment symbol in an assignment statement. Execution of the assignment statement causes the expression to be evaluated (by performing the computation indicated), and then the value of the expression is assigned to the variable. If the variable has subscripts, section subscripts, or a substring range, the execution of the assignment statement must behave as if they were evaluated before any part of the value is assigned.

There are four forms of the assignment statement: intrinsic assignment, defined assignment, pointer assignment, and masked array assignment.

An assignment statement is a defined assignment if the following conditions are true:

  • There is a subroutine subprogram with an assignment interface of the form ASSIGNMENT (=).

  • The types, kind type parameters, and ranks (if arrays) of the variable and expression match in order the dummy arguments of the subroutine with the assignment interface.

An assignment statement is a masked array assignment if it appears in a WHERE construct, WHERE statement, FORALL statement, or FORALL construct; otherwise, it is an intrinsic or defined assignment.

The form of the pointer assignment statement is similar to the assignment statement except that the assignment operator is => instead of =.

The rules and restrictions for each of these forms of assignment are different and are described in the sections below for each form of assignment.

The following examples show the forms of assignment:

Expression 

Meaning

X = X + 1.0 

Intrinsic assignment for reals

CHAR(1:4) = "A123" 

Intrinsic assignment for characters

STUDENT = B_JONES 

Intrinsic assignment for structures if STUDENT and B_JONES are of the same derived type

STRING = "Brown" 

Defined assignment for structure if STRING is of derived type and an interface exists that defines the operator = for the types STRING and character

X=Y 

Defined elemental assignment in which X and Y are both arrays of derived type. In addition, an interface exists that defines the operator = for the type and shape of the array, and the specific is elemental.

WHERE (Z /= 0.0), A = B / Z, END WHERE 

Masked array assignment

PTR => X 

Pointer assignment

Intrinsic Assignment

Intrinsic assignment can be used to assign a value to a nonpointer variable of any type or to the target associated with a pointer variable. The assignment statement defines or redefines the value of the variable or the target, as appropriate. The value is determined by the evaluation of the expression on the right side of the equal sign.

The types and kind parameters of the variable and expression in an intrinsic assignment statement must be of the types given in Table 7-13.

Table 7-13. Types of the variable and expression in an intrinsic assignment

Type of the variable

Type of the expression

Integer

Integer, real, complex, Boolean, Cray pointer

Real

Integer, real, complex, Boolean

Complex

Integer, real, complex

Character

Character

Logical

Logical

Cray pointer

Cray pointer, integer, Boolean

Derived type

Same derived type as the variable



Note: The Fortran standard does not define Boolean or Cray pointer types.

If the variable is an array, the expression must either be a scalar or an array of the same shape as the variable. If the variable is a scalar, the expression must be a scalar. The shape of the variable can be specified in specification statements if it is an explicit-shape array. The shape of the variable can be determined by the section subscripts in the variable, by an actual argument if it is a assumed-shape array, or by an ALLOCATE statement or a pointer assignment statement if it is a deferred-shape array. It must not be an assumed-size array unless there is a vector subscript, a scalar subscript, or a section subscript containing an upper bound in the last dimension of the array. The shape of the expression is determined by the shape of the operands, the operators in the expression, and the functions referenced in the expression. A complete description of the shape of an expression appears in “Shape of an Expression”.

If the variable is a pointer, it must be associated with a target; the assignment statement assigns the value of the expression to the target of the pointer. The pointer can be associated with a target that is an array; the pointer determines the rank of the array, but the extents in each dimension are that of the target.

The evaluation of the expression on the right-hand side of the equal sign, including subscript and section subscript expressions that are part of the expression and part of the variable, must be performed before any portion of the assignment is performed. Before the assignment begins, any necessary type conversions are completed if the variable has a different numeric type or type parameter from the expression. The conversion is the same as that performed by the conversion intrinsic functions INT(3i), REAL(3i), CMPLX(3i), and LOGICAL(3i), as specified in Table 7-14. On UNICOS and UNICOS/mk systems, if the expression is of type Boolean or is a BOZ constant, no conversion is done. On IRIX systems, the result of a Boolean expression is of type integer; no conversion is done for BOZ or Hollerith constants.


Note: The Fortran standard does not specify type Boolean or BOZ constants in assignment statements.


Table 7-14. Conversion performed on an expression before assignment

Type of the variable

Value assigned

Integer

INT (expr, KIND (variable))

Real

REAL (expr, KIND (variable))

Complex

CMPLX (expr, KIND (variable))

Logical

LOGICAL (expr, KIND (variable))

An expression can use parts of the variable that appear on the left side of an assignment statement. For example, in evaluating a character string expression on the right-hand side of an assignment, the values in the variable on the left-hand side can be used, as in the following example:

DATE(2:5) = DATE(1:4)

If the variable and expression are of character type with different lengths, the assignment occurs as follows:

  • If the length of the variable is less than that of the expression, the value of the expression is truncated from the right.

  • If the length of the variable is greater than the expression, the value of the expression is filled with blanks on the right.

The evaluation of expressions in the variable on the left-hand side, such as subscript expressions, has no effect on, nor is affected by, the evaluation of the expression on the right-hand side, which is evaluated completely first. As usual, this requirement that the expression on the right be evaluated first is specifying the semantics of the statement and does not imply that an implementation must perform the computation in this way if there is an equivalent order that computes the same result.

When a scalar is assigned to an array, the assignment behaves as if the scalar is broadcast to an array of the shape of the variable; it is then in shape conformance with the variable. In the following example, all ten elements of the array A are assigned the value 1.0:

REAL A(10)
A = 1.0

Array assignment is element-by-element, but the order is not specified. Assume that A and B are real arrays of size 10, and the whole array assignment is as follows:

A = B

The first element of B would be assigned to the first element of A, the second element of B would be assigned to the second element of A, and this would continue element-by-element for 10 elements. The assignment of elements, however, may be performed in any order.

For derived-type intrinsic assignment, the derived types of the variable and the expression must be the same. Derived-type intrinsic assignment is performed component-by-component following the above rules, except when a component is a pointer. For pointer components, pointer assignment between corresponding components is used.

Defined Assignment

A defined assignment is an assignment operation provided by a subroutine with an assignment interface ASSIGNMENT (=). When the variable and expression in the assignment statement are of intrinsic types and do not satisfy the type matching rules described in Table 7-13, or are of derived type, a defined assignment operation will be used, provided the assignment interface and subroutine are accessible. For example, a defined assignment may apply when an integer object is to be assigned to a logical variable, provided a subroutine with a generic assignment interface is accessible. Assignment thus can be extended to types other than the intrinsic types or can replace the intrinsic assignment operation for derived types, if the programmer defines the rules for this assignment in a subroutine. For more information on the assignment interface, see the Fortran Language Reference Manual, Volume 2.

An assignment operation is declared by using a subroutine with two dummy arguments.

The dummy arguments to the subroutine represent the variable and the expression, in that order.

There must be an interface block for the subroutine with the generic specifier of the form ASSIGNMENT(=).

The types and kind type parameters of the variable and expression in the assignment statement must be the same as those of the dummy arguments.

The rank of the variable and the expression in the assignment must match the ranks of the corresponding dummy arguments.

One of the following conditions must be true:

  • One of the dummy arguments must be of a derived type

  • Both of the dummy arguments are of intrinsic type but do not match the types and kind type parameters for intrinsic operations as specified in Table 7-13.

  • The subroutine is elemental, and either both dummy arguments have the same shape or one is scalar.

Example:

INTERFACE  ASSIGNMENT (=)

   SUBROUTINE  RATIONAL_TO_REAL(L, R)
      USE  RATIONAL_MODULE
      TYPE(RATIONAL), INTENT(IN)   :: R
      REAL, INTENT(OUT)            :: L
   END SUBROUTINE  RATIONAL_TO_REAL
   SUBROUTINE  REAL_TO_RATIONAL(L, R)
      USE  RATIONAL_MODULE
      REAL, INTENT(IN)              :: R
      TYPE(RATIONAL), INTENT(OUT)   :: L
   END SUBROUTINE  REAL_TO_RATIONAL

END INTERFACE

The preceding interface block specifies two defined assignments for two assignment operations in terms of two external subroutines, one for assignment of objects of type RATIONAL to objects of type real, and the other for assignment of objects of type real to objects of type RATIONAL. With this interface block, the following assignment statements are defined:

REAL  R_VALUE
TYPE(RATIONAL)  RAT_VALUE

R_VALUE = RATIONAL(1, 2)
RAT_VALUE = 3.7

The effect of the defined assignment on variables in the program is determined by the referenced subroutine.

Pointer Assignment

A pointer is a variable with the POINTER attribute that points to another object. The term pointer association is used for the concept of "pointing to" and the term target is used for the object associated with a pointer.

A pointer assignment associates a pointer with a target. If the target is disassociated or undefined, the pointer becomes disassociated or undefined according to the status of the target.

Once a pointer assignment has been executed, the association status of the pointer remains unchanged until one of the following events occurs:

  • Another pointer assignment statement is executed that redefines the pointer.

  • An ALLOCATE, DEALLOCATE, or NULLIFY statement is executed that redefines the pointer.

  • A RETURN statement is executed. This changes the association status only if the pointer is local to the subprogram containing the return and the pointer does not have the SAVE attribute.

The pointer assignment statement is defined as follows:

 

pointer_assignment_stmt

is

pointer_object=>target

 

pointer_object

is

variable_name

 

 

or

structure_component

 

target

is

variable

 

 

or

expr

If the pointer object is a variable name, the name must have the POINTER attribute. If the pointer object is a structure component, the component must have the POINTER attribute.

The form of the expression permitted as a target is severely limited.

If the target is a variable, then it must have one of the following characteristics:

  • It must have the TARGET attribute.

  • It must be the component of a structure, the element of an array variable, or the substring of a character variable that has the TARGET attribute.

  • It must have the POINTER attribute.

The type, kind type parameters (including length, if character), and rank of the target must be the same as the pointer object.

If the variable on the right of => has the TARGET attribute, the pointer object on the left of => becomes associated with this target.

If the variable on the right of => has the POINTER attribute and is associated, the pointer object on the left of => points to the same data that the target points to after the pointer assignment statement is executed.

If the variable on the right of => has the POINTER attribute and is disassociated, or if it is a reference to the NULL(3i) intrinsic, the pointer object on the left of => becomes disassociated.

If the variable on the right of => has the POINTER attribute and has an undefined association status, the association status of the pointer object on the left of => becomes undefined.

A pointer assignment statement terminates any previous association for that pointer and creates a new association.

If the pointer object is a deferred-shape array, the pointer assignment statement establishes the extents for each dimension of the array, unless the target is a disassociated or undefined pointer. Except for the case of a disassociated or undefined pointer, the extents are those of the target. For example, if the following statements have been processed, the extents of P1 are those of T, namely 11 and 20, but those of P2 are 1 and 10, because T(:) has a section subscript list:

INTEGER, TARGET :: T(11:20)
INTEGER, POINTER :: P1(:), P2(:)
P1 => T
P2 => T(:)

The target must not be a variable that is an assumed-size array. If it is an array section of an assumed-size array, the upper bound for the last dimension must be specified.

If the target is an array section, it must not have a vector subscript.

If the target is an expression, it must deliver a pointer result. This implies that the expression must be a user-defined function reference or defined operation that returns a pointer (there are no intrinsic operations or functions that return results with the POINTER attribute). This also implies that a pointer can never point at a constant because constants cannot have the TARGET attribute.

If the target of a pointer cannot be referenced or defined, the pointer must not be referenced or defined.

If a structure has a component with the POINTER attribute and the structure is assigned a value using an intrinsic derived-type assignment, pointer assignment is used for each component with the POINTER attribute. Also, defined assignment may cause pointer assignment between some components of a structure.

Note that when a pointer appears on the right side of => in a pointer assignment, the pointer on the left side of => is defined or redefined to be associated with the target on the right side of the =>; neither the pointer on the right nor its target are changed in any way.

General examples:

MONTH => DAYS(1:30)
PTR => X(:, 5)
NUMBER => JONES % SOCSEC

Example 1: In this example, the target is another pointer:

REAL, POINTER :: PTR, P
REAL, TARGET :: A
REAL  B
A = 1.0
P => A
PTR => P
B = PTR + 2.0

The previous program segment defines A with the value 1.0, associates P with A; then PTR is associated with A as well (through P). The value assigned to B in the regular assignment statement is 3.0, because the reference to PTR in the expression yields the value of the target A which is the value 1.0.

Example 2: In this example, the target is an expression:

INTERFACE
   FUNCTION POINTER_FCN(X)
      REAL  X
      REAL, POINTER :: POINTER_FCN
   END FUNCTION
END INTERFACE

REAL, POINTER :: P
REAL  A

P => POINTER_FCN(A)

In this example, the function POINTER_FCN takes a real argument and returns a pointer to a real target. After execution of the pointer assignment statement, the pointer P points to this real target.

Pointers can become associated by using the ALLOCATE statement instead of a pointer assignment statement. Pointers can become disassociated by using the DEALLOCATE or NULLIFY statements, as well as with the pointer assignment statement.

A pointer can be used in an expression (see “Interpretation of Intrinsic Operations with Pointer Operands”, for the details). Briefly, any reference to a pointer in an expression, other than in a pointer assignment statement, or in certain procedure references, yields the value of the target associated with the pointer. When a pointer appears as an actual argument corresponding to a dummy argument that has the POINTER attribute, the reference is to the pointer and not the value. Note that a procedure must have an explicit interface if it has a dummy argument with the POINTER attribute. For information on explicit interfaces, see the Fortran Language Reference Manual, Volume 2.

Masked Array Assignment

Sometimes it is desirable to assign only certain elements of one array to another array. To invert the elements of an array element-by-element, for example, you have to avoid elements that are 0. The masked array assignment is ideal for such selective assignment, as the following example using a WHERE construct illustrates:

REAL  A(10,10)
  ...
WHERE (A /= 0.0)
    RECIP_A = 1.0 / A   ! Assign only where the
                        !    elements are nonzero
ELSEWHERE
    RECIP_A = 1.0       ! Use the value 1.0 for
                        !    the zero elements
END WHERE

The first array assignment statement is executed for only those elements where the mask A /= 0.0 is true. Next, the second assignment statement (after the ELSEWHERE statement) is executed for only those elements where the same mask is false. If the values of RECIP_A where A is 0 are never used, this example can be simply written by using the WHERE statement, rather than the WHERE construct, as follows:

WHERE (A /= 0.0) RECIP_A = 1.0 / A

A masked array assignment is an intrinsic assignment statement in a WHERE block, an ELSEWHERE block, or a WHERE statement for which the variable being assigned is an array. The WHERE statement and WHERE construct appear to have the characteristics of a control statement or construct such as the IF statement and IF construct. But there is a major difference: every assignment statement in a WHERE construct is executed, whereas at most one block in the IF construct is executed. Similarly, the assignment statement following a WHERE statement is always executed. For this reason, WHERE statements and constructs are discussed here under assignment rather than under control constructs.

In a masked array assignment, the assignment is made to certain elements of an array based on the value of a logical array expression serving as a mask for picking out the array elements. The logical array expression acts as an array-valued condition on the elemental intrinsic operations, functions, and assignment for each array assignment statement in the WHERE statement or WHERE construct.

As in an intrinsic array assignment, a pointer to an array can be used as the variable, and a pointer to a scalar or an array can be used as a primary in the expression. If the target of the pointer is an array, the target array is masked in the same manner as a nonpointer array used in a masked array assignment.

WHERE Statement and Construct

The WHERE construct is defined as follows:

 

where_stmt

is

WHERE (mask_expr) where_assignment_stmt

 

where_construct

is

where_construct_stmt
[where_body_construct] ...
[masked_elsewhere_stmt
[where_body_construct] ...] ...
[elsewhere_stmt
[where_body_construct] ... ]
end_where_stmt

 

where_construct_stmt

is

[where_construct_name: ] WHERE (mask_expr)

 

where_body_construct

is

where_assignment_stmt

 

 

or

where_stmt

 

 

or

where_construct

 

where_assignment_stmt

is

assignment_stmt

 

mask_expr

is

logical_expr

 

masked_elsewhere_stmt

is

ELSEWHERE (mask_expr) [where_construct_name]

 

elsewhere_stmt

is

ELSEWHERE [where_construct_name]

 

end_where_stmt

is

END WHERE [where_construct_name]

The definition of the WHERE construct can be simplified to the following general format:

WHERE (condition_1) ! STATEMENT_1
...
ELSEWHERE (condition_2)    ! STATEMENT_2
...
ELSEWHERE           ! STATEMENT_3
...
END WHERE

The following information applies to the preceding general format:

  • Following execution of STATEMENT_1, the control mask has the value condition_1 and the pending control mask has the value .NOT.condition_1.

  • Following execution of STATEMENT_2, the control mask has the value (.NOT.condition_1).AND.condition_2 and the pending control mask has the value (.NOT.condition_1).AND.(.NOT.condition_2).

  • Following execution of STATEMENT_3, the control mask has the value (.NOT.condition_1).AND.(.NOT.condition_2).

  • The false condition values are propogated through the execution of the masked ELSEWHERE statement.

If an array constructor appears in a where_assignment_stmt or in a mask_expr, the array constructor is evaluated without any masked control. After that, the where_assignment_stmt is executed or the mask_expr is evaluated.

When a where_assignment_stmt is executed, the values of expr that correspond to true values of the control mask are assigned to the corresponding elements of variable.

A statement that is part of a where_body_construct must not be a branch target statement. The value of the control mask is established by the execution of a WHERE statement, a WHERE construct statement, an ELSEWHERE statement, a masked ELSEWHERE statement, or an ENDWHERE statement. Subsequent changes to the value of entities in a mask_expr have no effect on the value of the control mask. The execution of a function reference in the mask expression of a WHERE statement is permitted to affect entities in the assignment statement. Execution of an END WHERE has no effect.

If the where_construct_stmt has a where_construct_name, then the corresponding end_where_stmt must specify the same name. If the construct also has an elsewhere_stmt or masked_ elsewhere_stmt, it must have the same where_construct_name. If no where_construct_name is specified for the where_construct, then the end_where_stmt and any elsewhere_stmt or masked_elsewhere_stmt must have the where_construct_name.

In a WHERE construct, only the WHERE construct statement can be labeled as a branch target statement.

The WHEREblock is the set of assignments between the WHERE construct statement and the ELSEWHERE statement (or END WHERE statement, if the ELSEWHERE statement is not present). The ELSEWHEREblock is the set of assignment statements between the ELSEWHERE and the END WHERE statements.

Each assignment in the ELSEWHERE block assigns a value to each array element that corresponds with a mask array element that is false.

The ELSEWHERE block is optional; when it is not present, no assignment is made to elements corresponding to mask array elements that are false.

All of the assignment statements are executed in sequence as they appear in the construct (in both the WHERE and ELSEWHERE blocks).

Any elemental intrinsic operation or function within an array assignment statement is evaluated only for the selected elements. In the following example, the square roots are taken only of the elements of A that are positive:

REAL  A(10, 20)
  ...
WHERE (A > 0.0)
  SQRT_A = SQRT(A)
END WHERE

An elemental function reference is evaluated independently for each element, and only those elements needed in the array assignment are referenced. A where_assignment_stmt that is a defined assignment must be elemental.

The expression in the array assignment statement can contain nonelemental function references. Nonelemental function references are references to any function or operation defined by a subprogram, without the ELEMENTAL keyword. All elements of the arguments of such functions and returned results (if arrays) are evaluated in full. If the result of the nonelemental function is an array and is an operand of an elemental operation or function, then only the selected elements are used in evaluating the remainder of the expression.

Example 1:

REAL  A(2, 3), B(3, 10), C(2, 10), D(2, 10)
INTRINSIC  MATMUL
  ...
WHERE (D < 0.0)
  C = MATMUL(A, B)
END WHERE

The matrix product A multiplied by B is performed, yielding all elements of the product, and only for those elements of D that are negative are the assignments to the corresponding elements of C made.

Example 2:

WHERE (TEMPERATURES > 90.0)  HOT_TEMPS = TEMPERATURES
WHERE (TEMPERATURES < 32.0)  COLD_TEMPS = TEMPERATURES

Example 3:

WHERE (TEMPERATURES > 90.0)
   NUMBER_OF_SWEATERS = 0
ELSEWHERE (TEMPERATURES < 0.0)
   NUMBER_OF_SWEATERS = 3
ELSEWHERE (TEMPERATURES < 40)
   NUMBER_OF_SWEATERS = 2
ELSEWHERE
   NUMBER_OF_SWEATERS = 1
ENDWHERE

Differences between the WHERE Construct and Control Constructs

One major difference between the WHERE construct and control constructs has been described in “Masked Array Assignment”. Another difference is that no transfers out of WHERE or ELSEWHERE blocks are possible (except by a function reference) because only intrinsic assignment statements are permitted within these blocks. Note that the execution of statements in the WHERE block can affect variables referenced in the ELSEWHERE block (because the statements in both blocks are executed).

FORALL Statement and Construct

FORALL statements and constructs control the execution of assignment and pointer assignment statements with selection by using index values and an optional mask expression.

FORALL Construct

The FORALL construct allows multiple assignments, masked array (WHERE) assignments, and nested FORALL constructs and statements to be controlled by a single forall_triplet_spec_list and scalar_mask.

The format of the FORALL construct is as follows:

 

forall_construct

is

forall_construct_stmt
[forall_body_construct] ...
end_forall_stmt

 

forall_construct_stmt

is

[forall_construct_name: ] FORALL forall_header

 

forall_header

is

(forall_triplet_spec_list[, scalar_mask_expr])

 

forall_triplet_spec

is

index_name = subscript : subscript[: stride]

 

subscript

is

scalar_int_expr

 

stride

is

scalar_int_expr

 

forall_body_construct

is

forall_assignment_stmt

 

 

or

where_stmt

 

 

or

where_construct

 

 

or

forall_construct

 

 

or

forall_stmt

 

forall_assignment_stmt

is

assignment_stmt

 

 

or

pointer_assignment_stmt

 

end_forall_stmt

is

END FORALL [forall_construct_name]

If the forall_construct_stmt has a forall_construct_name, the end_forall_stmt must have the same forall_construct_name. If the end_forall_stmt has a forall_construct_name, the forall_construct_stmt must have the same forall_construct_name.

The scalar_mask_expr must be scalar and of type logical.

A procedure that is referenced in the scalar_mask_expr, including one referenced by a defined operation, must be a pure procedure.

A procedure that is referenced in a forall_body_construct, including one referenced by a defined operation or assignment, must be a pure procedure.

The index_name must be a named scalar variable of type integer.

A subscript or stride in a forall_triplet_spec must not contain a reference to any index_name in the forall_triplet_spec_list in which it appears.

A statement in a forall_body_construct must not define an index_name of the forall_construct.

A forall_body_construct must not be a branch target.

Example:

REAL :: A(10, 10), B(10, 10) = 1.0
...
FORALL (I = 1:10, J = 1:10, B(I, J) /= 0.0)
   A(I, J) = REAL (I + J - 2)
   B(I, J) = A(I, J) + B(I, J) * REAL (I * J)
END FORALL

Each forall_body_construct is executed in the order in which it appears. Each construct is executed for all active combinations of the index_name values.

Execution of a forall_assignment_stmt that is an assignment_stmt causes the evaluation of expr and all expressions within variable for all active combinations of index_name values. After all evaluations have been performed, each expr value is assigned to the corresponding variable.

Execution of a forall_assignment_stmt that is a pointer_assignment_stmt causes the evaluation of all expressions within target and pointer_object, the determination of any pointers within pointer_object, and the determination of the target for all active combinations of index_name values. After these evaluations have been performed, each pointer_object is associated with the corresponding target.

In a forall_assignment_stmt, a defined assignment subroutine must not reference any variable that becomes defined or a pointer_object that becomes associated by the statement.

The following code fragment shows a FORALL construct with two assignment statements. The assignment to array B uses the values of array A computed in the previous statement, not the values A had prior to execution of the FORALL:

FORALL (I = 2:N-1, J = 2:N-1 )
   A (I, J) = A(I, J-1) + A(I,J+1) + A(I-1,J) + A(I+1, J)
   B (I, J) = 1.0 / A(I, J)
END FORALL

The following code fragment shows how to avoid an error condition by using an appropriate scalar_mask_expr that limits the active combinations of the index_name values:

FORALL (I = 1:N, Y(I) .NE. 0.0)
   X(I) = 1.0 / Y(I)
END FORALL

Each statement in a where_construct within a forall_construct is executed in sequence. When a where_stmt, where_construct_stmt, or masked_elsewhere_stmt is executed, the statement's mask_expr is evaluated for all active combinations of index_name values as determined by the outer forall_constructs, masked by any control mask corresponding to outer where_constructs. Any where_assignment_stmt is executed for all active combinations of index_name values, masked by the control mask in effect for the where_assignment_stmt. The following FORALL construct contains a WHERE statement and an assignment statement:

INTEGER A(5,4), B(5,4)
FORALL ( I = 1:5 )
   WHERE ( A(I,:) .EQ. 0 ) A(I,:) = I
   B (I,:) = I / A(I,:)
END FORALL

The preceding code is executed with array A as follows::

    0 0 0 0
    1 1 1 0
A = 2 2 0 2
    1 0 2 3
    0 0 0 0

The result is as follows:

    1 1 1 1         1 1 1 1
    1 1 1 2         2 2 2 1
A = 2 2 3 2     B = 1 1 1 1
    1 4 2 3         4 1 2 1
    5 5 5 5         1 1 1 1

When a forall_stmt or forall_construct is executed, the compiler evaluates the subscript and stride expressions in the forall_triplet_spec_list for all active combinations of the index_name values of the outer FORALL construct. The set of combinations of index_name values for the inner FORALL is the union of the sets defined by these bounds and strides for each active combination of the outer index_name values; it also includes the outer index_name values. The scalar_mask_expr is then evaluated for all combinations of the index_name values of the inner construct to produce a set of active combinations for the inner construct. If no scalar_mask_expr is specified, the compiler uses .TRUE. as its value. Each statement in the inner FORALL is then executed for each active combination of the index_name values. The following FORALL construct contains a nested FORALL construct. It assigns the transpose of the lower triangle of array A, which is the section below the main diagonal, to the upper triangle of A. The code fragment is as follows:

INTEGER A (3, 3)
FORALL (I = 1:N-1 )
   FORALL ( J=I+1:N )
      A(I,J) = A(J,I)
   END FORALL
END FORALL

Prior to execution of the preceding code, N=3 and array A is as follows:

    0 3 6
A = 1 4 7
    2 5 8

After the preceding code is executed, array A is as follows:

    0 1 2
A = 1 4 5
    2 5 8

You could also use the following FORALL statement to obtain identical results:

FORALL ( I = 1:N-1, J=1:N, J > I ) A(I,J) = A(J,I)

For more information on the FORALL statement, see “FORALL Statement”.

FORALL Statement

The FORALL statement allows a single assignment statement or pointer assignment to be controlled by a set of values and an optional mask expression. The format for this statement is as follows:

 

forall_stmt

is

FORALL forall_headerforall_assignment_stmt

Execution of a FORALL statement starts with the evaluation of the forall_triplet_spec_list for each index_name variable. All possible combinations of the values of the index_name variables are considered for execution of the FORALL body. The mask expression, if present, is then evaluated for each combination of index_name values and each combination that has a .TRUE. outcome is in the active combination of index_name values. This set of active combinations is then used in executing the FORALL body.

A FORALL statement is equivalent to a FORALL construct that contains a single forall_body_construct that is a forall_assignment_stmt.

The scope of an index_name in a forall_stmt is the statement itself.

The following FORALL statement assigns the elements of vector X to the elements of the main diagonal of matrix A:

FORALL (I=1:N) A(I,I) = X(I)

In the following FORALL statement, array element X(I,J) is assigned the value (1.0 / REAL (I+J-1)) for values of I and J between 1 and N, inclusive:

FORALL (I = 1:N, J = 1:N) X(I,J) = 1.0 / REAL (I+J-1)

The following statement takes the reciprocal of each nonzero off-diagonal element of array Y(1:N, 1:N) and assigns it to the corresponding element of array X. Elements of Y that are zero or are on the diagonal do not participate, and no assignments are made to the corresponding elements of X:

FORALL (I=1:N, J=1:N, Y(I,J) /= 0 .AND. I /= J) X(I,J) = 1.0 / Y(I,J)

Restrictions on FORALL Constructs and Statements

A many-to-one assignment is more than one assignment to the same object or subobject, or association of more than one target with the same pointer, whether the object is referenced directly or indirectly through a pointer. A many-to-one assignment must not occur within a single statement in a FORALL construct or statement. It is possible to assign, or pointer assign, to the same object in different assignment statements in a FORALL construct.

The appearance of each index_name in the identification of the left-hand side of an assignment statement is helpful in eliminating many-to-one assignments, but it is not sufficient to guarantee that there will be none. The following code fragment is permitted only if INDEX(1:10) contains no repeated values:

FORALL (I = 1:10)
   A (INDEX (I)) = B(I)
END FORALL

Within the scope of a FORALL construct, a nested FORALL statement or FORALL construct cannot have the same index_name. The forall_header expressions within a nested FORALL can depend on the values of outer index_name variables.