Procedures¶
A function is a code abstraction that can be invoked by a call expression. Throughout this specification the term “function” is used in this programming-languages sense, rather than in the mathematical sense. A function has zero or more formal arguments, or simply formals. Upon a function call each formal is associated with the corresponding actual argument, or simply actual. Actual arguments are provided as part of the call expression, or at the the call site. Direct and indirect recursion is supported.
A function can be a procedure, which completes and returns to the call site exactly once, returning no result, a single result, or multiple results aggregated in a tuple. A function can also be an iterator, which can generate, or yield, multiple results (in sequence and/or in parallel). A function (either a procedure or an iterator) can be a method if it is bound to a type (often a class). An operator in this chapter is a procedure with a special name, which can be invoked using infix notation, i.e., via a unary or binary expression. This chapter defines procedures, but most of its contents apply to iterators and methods as well.
Functions are presented as follows:
procedures (this chapter)
operators Procedure Definitions, Operator Expressions
iterators Iterators
methods (when bound to a class) Class Methods
function calls Function Calls
various aspects of defining a procedure Procedure Definitions–Nested Functions
calling external functions from Chapel Calling External Functions
calling Chapel functions from external functions Calling Chapel Functions
determining the function to invoke for a given call site: function and operator overloading Function and Operator Overloading, function resolution Function Resolution
Function Calls¶
The syntax to call a non-method function is given by:
call-expression:
lvalue-expression ( named-expression-list )
lvalue-expression [ named-expression-list ]
parenthesesless-function-identifier
named-expression-list:
named-expression
named-expression , named-expression-list
named-expression:
expression
identifier = expression
parenthesesless-function-identifier:
identifier
A call-expression
is resolved to a particular function according to
the algorithm for function resolution described
in Function Resolution.
Functions can be called using either parentheses or brackets.
Rationale.
This provides an opportunity to blur the distinction between an array access and a function call and thereby exploit a possible space/time tradeoff.
Functions that are defined without parentheses must be called without parentheses as defined by scope resolution. Functions without parentheses are discussed in Functions without Parentheses.
A named-expression
is an expression that may be optionally named. It
provides an actual argument to the function being called. The optional
identifier
refers to a named formal argument described
in Named Arguments.
Calls to methods are defined in Section Class Method Calls.
Procedure Definitions¶
Procedures are defined with the following syntax:
procedure-declaration-statement:
privacy-specifier[OPT] procedure-kind[OPT] 'proc' identifier argument-list[OPT] return-intent[OPT] return-type[OPT] where-clause[OPT] function-body
privacy-specifier[OPT] procedure-kind[OPT] 'operator' operator-name argument-list return-intent[OPT] return-type[OPT] where-clause[OPT] function-body
procedure-kind:
'inline'
'export'
'extern'
'override'
operator-name: one of
'align' 'by'
+ - * / % ** : ! == != <= >= < > << >> & | ^ ~
= += -= *= /= %= **= &= |= ^= <<= >>= <=> <~> #
argument-list:
( formals[OPT] )
formals:
formal
formal , formals
formal:
formal-intent[OPT] identifier formal-type[OPT] default-expression[OPT]
formal-intent[OPT] identifier formal-type[OPT] variable-argument-expression
formal-intent[OPT] tuple-grouped-identifier-list formal-type[OPT] default-expression[OPT]
formal-intent[OPT] tuple-grouped-identifier-list formal-type[OPT] variable-argument-expression
formal-type:
: type-expression
: ? identifier[OPT]
default-expression:
= expression
variable-argument-expression:
... expression
... ? identifier[OPT]
...
formal-intent:
'const'
'const in'
'const ref'
'in'
'out'
'inout'
'ref'
'param'
'type'
return-intent:
'const'
'const ref'
'ref'
'param'
'type'
return-type:
: type-expression
where-clause:
'where' expression
function-body:
block-statement
return-statement
Functions do not require parentheses if they have no arguments. Such functions are described in Functions without Parentheses.
Formal arguments can be grouped together using a tuple notation as described in Splitting a Tuple into Multiple Formal Arguments in a Function Call.
Default expressions allow for the omission of actual arguments at the call site, resulting in the implicit passing of a default value. Default values are discussed in Default Values.
The intents const
, const in
, const ref
, in
, out
,
inout
and ref
are discussed in Argument Intents.
The intents param
and type
make a function generic and are
discussed in Generic Functions. If the formal argument’s
type is omitted, generic, or prefixed with a question mark, the function
is also generic and is discussed in Generic Functions.
Functions can take a variable number of arguments. Such functions are discussed in Variable Number of Arguments.
The return-intent
can be used to indicate how the value is returned
from a function. return-intent
is described further in
Return Intents.
Open issue.
Parameter and type procedures are supported. Parameter and type iterators are currently not supported.
The return-type
is optional and is discussed
in Return Types. A type function may not specify a return
type.
The where-clause
is optional and is discussed
in Where Clauses.
Function and operator overloading is supported in Chapel and is
discussed in Function and Operator Overloading. Operator overloading
is supported on the operators listed above (see operator-name
).
The optional privacy-specifier
keywords indicate the visibility of
module level procedures to outside modules. By default, procedures are
publicly visible. More details on visibility can be found in
Visibility Of A Module’s Symbols.
The linkage specifier inline
indicates that the function body must
be inlined at every call site.
Rationale.
A Chapel compiler is permitted to inline any function if it determines there is likely to be a performance benefit to do so. Hence an error must be reported if the compiler is unable to inline a procedure with this specifier. One example of a preventable inlining error is to define a sequence of inlined calls that includes a cycle back to an inlined procedure.
See the chapter on interoperability (Interoperability) for details on exported and imported functions.
Functions without Parentheses¶
Functions do not require parentheses if they have empty argument lists. Functions declared without parentheses around empty argument lists must be called without parentheses.
Example (function-no-parens.chpl).
Given the definitions
proc foo { writeln("In foo"); } proc bar() { writeln("In bar"); }the procedure
foo
can be called by writingfoo
and the procedurebar
can be called by writingbar()
. It is an error to use parentheses when callingfoo
or omit them when callingbar
.
Formal Arguments¶
A formal argument’s intent (Argument Intents) specifies how the actual argument is passed to the function. If no intent is specified, the default intent (The Default Intent) is applied, resulting in type-dependent behavior.
Named Arguments¶
A formal argument can be named at the call site to explicitly map an actual argument to a formal argument.
Example (named-args.chpl).
Running the code
proc foo(x: int, y: int) { writeln(x); writeln(y); } foo(x=2, y=3); foo(y=3, x=2);will produce the output
2 3 2 3named argument passing is used to map the actual arguments to the formal arguments. The two function calls are equivalent.
Named arguments are sometimes necessary to disambiguate calls or ignore arguments with default values. For a function that has many arguments, it is sometimes good practice to name the arguments at the call site for compiler-checked documentation.
Default Values¶
Default values can be specified for a formal argument by appending the assignment operator and a default expression to the declaration of the formal argument. If the actual argument is omitted from the function call, the default expression is evaluated when the function call is made and the evaluated result is passed to the formal argument as if it were passed from the call site. While the default expression is evaluated at the time of the function call, it is resolved in the scope of the definition of the called function, immediately before the called function is resolved. As a result, a default value expression can refer to previous formal arguments.
When a default value is provided for a formal argument without a type, the argument type will be inferred to match the type of the default value. This inference is similar to the type inference for variable declarations (see Local Type Inference). However, there is one difference: when the call provides a corresponding actual argument, and the actual argument is of a type that includes a runtime component (see Types with Runtime Components), the runtime component of the formal argument’s type will come from the actual argument, rather than from the default value expression.
Example (default-values.chpl).
The code
proc foo(x: int = 5, y: int = 7) { writeln(x); writeln(y); } foo(); foo(7); foo(y=5);writes out
5 7 7 7 5 5Default values are specified for the formal arguments
x
andy
. The three calls tofoo
are equivalent to the following three calls where the actual arguments are explicit:foo(5, 7)
,foo(7, 7)
, andfoo(5, 5)
. The examplefoo(y=5)
shows how to use a named argument fory
in order to use the default value forx
in the case whenx
appears earlier thany
in the formal argument list.Example (default-array-runtime-type.chpl).
This example shows that the runtime type of the default expression does not impact the runtime type of the formal argument in the case that an actual argument was provided.
var D = {1..4}; proc createArrayOverD() { var A:[D] int; return A; } proc bar(arg = createArrayOverD()) { writeln(arg.domain); } bar(); // arg uses the default, so outputs {1..4} var B:[0..2] int; bar(B); // arg refers to B and so has the runtime type from B // so outputs {0..2}
Argument Intents¶
Argument intents specify how an actual argument is passed to a function where it is represented by the corresponding formal argument.
Argument intents are categorized as being either concrete or abstract. Concrete intents are those in which the semantics of the intent keyword are independent of the argument’s type. Abstract intents are those in which the keyword (or lack thereof) expresses a general intention that will ultimately be implemented via one of the concrete intents. The specific choice of concrete intent depends on the argument’s type and may be implementation-defined. Abstract intents are provided to support productivity and code reuse.
Concrete Intents¶
The concrete intents are in
, out
, inout
, ref
,
const in
, and const ref
.
The In Intent¶
When in
is specified as the intent, the formal argument represents a
variable that is initialized from the value of the actual argument.
This initialization will be copy-initialization or move-initialization
according to Copy and Move Initialization.
For example, for integer arguments, the formal argument will store a copy of the actual argument.
An implicit conversion for a function call occurs from the actual argument to the type of the formal.
The formal can be modified within the function, but such changes are local to the function and not reflected back to the call site.
The Out Intent¶
The out
intent on a formal argument supports return-like behavior.
As such, the type of an out
formal is not considered when determining
candidate functions or choosing the best candidate (see
Function Resolution).
When a function with the out
intent returns, the actual argument is
set to the formal argument using assignment or possibly initialized
from the formal argument according to Split Initialization.
Within the function body, an out
formal argument is initialized
according Split Initialization. It will start with its default
value if one is supplied and can use the default value for the declared
type if no initialization point is found. The formal argument can be
modified within the function.
The Inout Intent¶
When inout
is specified as the intent, the actual argument is
copy-initialized into the formal argument, the called function body is
run, and then the actual argument is set to the formal argument with
assignment. As a result the behavior of the inout
intent is a
combination of the in
and out
intents.
inout
intent formals behave the same as in
formals for the
purposes of determining candidate functions and choosing the best
candidate (see Function Resolution).
The actual argument must be a valid lvalue. The formal argument can be modified within the function.
The Ref Intent¶
When ref
is specified as the intent, the actual argument is passed
by reference. Any reads of, or modifications to, the formal argument are
performed directly on the corresponding actual argument at the call
site. The actual argument must be a valid lvalue. The type of the actual
argument must be the same as the type of the formal.
The ref
intent differs from the inout
intent in that the
inout
intent requires copying from/to the actual argument on the way
in/out of the function, while ref
allows direct access to the actual
argument through the formal argument without copies. Note that
concurrent modifications to the ref
actual argument by other tasks
may be visible within the function, subject to the memory consistency
model.
The Const In Intent¶
The const in
intent is identical to the in
intent, except that
modifications to the formal argument are prohibited within the function.
The Const Ref Intent¶
The const ref
intent is identical to the ref
intent, except that
modifications to the formal argument are prohibited within the dynamic
scope of the function. Note that concurrent tasks may modify the actual
argument while the function is executing and that these modifications
may be visible to reads of the formal argument within the function’s
dynamic scope (subject to the memory consistency model).
Summary of Concrete Intents¶
The following table summarizes the differences between the concrete intents:
|
|
|
|
|
|
|
---|---|---|---|---|---|---|
initializes formal from actual? |
yes |
no |
yes |
no |
yes |
no |
sets actual from formal? |
no |
yes |
yes |
no |
no |
no |
refers to actual argument? |
no |
no |
no |
yes |
no |
yes |
formal can be read? |
yes |
yes |
yes |
yes |
yes |
yes |
formal can be modified? |
yes |
yes |
yes |
yes |
no |
no |
local changes affect the actual? |
no |
on return |
on return |
immediately |
N/A |
N/A |
Abstract Intents¶
The abstract intents are const
and the default intent (when no
intent is specified).
Abstract Intents Table¶
The following table summarizes what these abstract intents mean for each type:
meaning of |
meaning of |
||
type |
|
default intent |
notes |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
array |
|
|
|
tuple |
per element |
per element |
The Const Intent¶
The const
intent specifies the intention that the function will not
and cannot modify the formal argument within its dynamic scope. Whether
the actual argument will be passed by const in
or const ref
intent depends on its type. In general, small values, such as scalar
types, will be passed by const in
; while larger values, such as
domains and arrays, will be passed by const ref
intent. The
Abstract Intents Table earlier in this sub-section lists the
meaning of the const intent for each type.
The Default Intent¶
When no intent is specified for a formal argument, the default intent is applied. It is designed to take the most natural/least surprising action for the argument, based on its type. The Abstract Intents Table earlier in this sub-section lists the meaning of the default intent for each type.
Default argument passing for tuples generally matches the default argument passing strategy that would be applied if each tuple element was passed as a separate argument. See Tuple Argument Intents.
Default Intent for Arrays and Record ’this’¶
The default intent for arrays and for a this
argument of record
type (see The Method Receiver and the this Argument) is ref
or
const ref
. It is ref
if the formal argument is modified inside
the function, otherwise it is const ref
. Note that neither of these
cause an array or record to be copied by default. The choice between
ref
and const ref
is similar to and interacts with return intent
overloads (see Return Intent Overloads).
Variable Number of Arguments¶
Functions can be defined to take a variable number of arguments where those arguments can have any intent or can be types. A variable number of parameters is not supported. This allows the call site to pass a different number of actual arguments. There must be at least one actual argument.
If the variable argument expression contains an identifier prepended by a question mark, the number of actual arguments can vary, and the identifier will be bound to an integer parameter value indicating the number of arguments at a given call site. If the variable argument expression contains an expression without a question mark, that expression must evaluate to an integer parameter value requiring the call site to pass that number of arguments to the function.
Within the function, the formal argument that is marked with a variable argument expression is a tuple of the actual arguments. If the actual arguments all have the same type, the formal will be a homogeneous tuple, otherwise it will be a heterogeneous tuple.
Example (varargs.chpl).
The code
proc mywriteln(xs ...?k) { for x in xs do writeln(x); }defines a generic procedure called
mywriteln
that takes a variable number of arguments of any type and then writes them out on separate lines. The type ofxs
can also be constrained in the formal argument list to require that the actuals all have the same type. For examplexs: string...?k
would accept a variable number of string arguments.
Example (varargs-with-type.chpl).
Either or both the number of variable arguments and their types can be specified. For example, a basic procedure to sum the values of three integers can be written as
proc sum(x: int...3) return x(0) + x(1) + x(2);Specifying the type is useful if it is important that each argument have the same type. Specifying the number is useful in, for example, defining a method on a class that is instantiated over a rank parameter.
Example (varargs-returns-tuples.chpl).
The code
proc tuple(x ...) return x;defines a generic procedure that is equivalent to building a tuple. Therefore the expressions
tuple(1, 2)
and(1,2)
are equivalent, as are the expressionstuple(1)
and(1,)
.
Return Intents¶
The return-intent
specifies how the value is returned from a
function, and in what contexts that function is allowed to be used. By
default, or if the return-intent
is const
, the function returns
a value that cannot be used as an lvalue.
The Ref Return Intent¶
When using a ref
return intent, the function call is an lvalue
(specifically, a call expression for a procedure and an iterator
variable for an iterator).
The ref
return intent is specified by following the argument list
with the ref
keyword. The function must return or yield an lvalue.
Example (ref-return-intent.chpl).
The following code defines a procedure that can be interpreted as a simple two-element array where the elements are actually module level variables:
var x, y = 0; proc A(i: int) ref { if i < 0 || i > 1 then halt("array access out of bounds"); if i == 0 then return x; else return y; }Calls to this procedure can be assigned to in order to write to the “elements” of the array as in
A(0) = 1; A(1) = 2;It can be called as an expression to access the “elements” as in
writeln(A(0) + A(1));This code outputs the number
3
.
The Const Ref Return Intent¶
The const ref
return intent is also available. It is a restricted
form of the ref
return intent. Calls to functions marked with the
const ref
return intent are not lvalue expressions.
Return Intent Overloads¶
In some situations, it is useful to choose the function called based upon how the returned value is used. In particular, suppose that there are two functions that have the same formal arguments and differ only in their return intent. One might expect such a situation to result in an error indicating that it is ambiguous which function is called. However, the Chapel language includes a special rule for determining which function to call when the candidate functions are otherwise ambiguous except for their return intent. This rule enables data structures such as sparse arrays.
See Choosing Return Intent Overloads Based on Calling Context for a detailed description of how return intent overloads are chosen based upon calling context.
Example (ref-return-intent-pair.chpl).
Return intent overload can be used to ensure, for example, that the second element in the pseudo-array is only assigned a value if the first argument is positive. The following is an example:
var x, y = 0; proc doA(param setter, i: int) ref { if i < 0 || i > 1 then halt("array access out of bounds"); if setter && i == 1 && x <= 0 then halt("cannot assign value to A(1) if A(0) <= 0"); if i == 0 then return x; else return y; } proc A(i: int) ref { return doA(true, i); } proc A(i: int) { return doA(false, i); } A(0) = 0; A(1) = 1;
The Param Return Intent¶
A parameter function, or a param function, is a function that
returns a parameter expression. It is specified by following the
function’s argument list by the keyword param
. It is often, but not
necessarily, generic.
It is a compile-time error if a parameter function does not return a parameter expression. The result of a parameter function is computed during compilation and substituted for the call expression.
Example (param-functions.chpl).
In the code
proc sumOfSquares(param a: int, param b: int) param return a**2 + b**2; var x: sumOfSquares(2, 3)*int;
sumOfSquares
is a parameter procedure that takes two parameters as arguments. Calls to this procedure can be used in places where a parameter expression is required. In this example, the call is used in the declaration of a homogeneous tuple and so is required to be a parameter.
Parameter functions may not contain control flow that is not resolved at compile-time. This includes loops other than the parameter for loop Parameter For Loops and conditionals with a conditional expressions that is not a parameter.
The Type Return Intent¶
A type function is a function that returns a type, not a value. It is
specified by following the function’s argument list by the keyword
type
, without the subsequent return type. It is often, but not
necessarily, generic.
It is a compile-time error if a type function does not return a type. The result of a type function is computed during compilation.
As with parameter functions, type functions may not contain control flow that is not resolved at compile-time. This includes loops other than the parameter for loop Parameter For Loops and conditionals with a conditional expression that is not a parameter.
Example (type-functions.chpl).
In the code
proc myType(x) type { if numBits(x.type) <= 32 then return int(32); else return int(64); }
myType
is a type procedure that takes a single argumentx
and returnsint(32)
if the number of bits used to representx
is less than or equal to 32, otherwise it returnsint(64)
.numBits
is a param procedure defined in the standard Types module.
The Return Statement¶
The return statement can only appear in a function. It causes control to exit that function, returning it to the point at which that function was called.
A procedure can return a value by executing a return statement that includes an expression. If it does, that expression’s value becomes the value of the invoking call expression.
A return statement in a procedure of a non-void
return type
(Return Types) must include an expression. A return
statement in a procedure of a void
return type or in an iterator
must not include an expression. A return statement of a variable
procedure must contain an lvalue expression.
The syntax of the return statement is given by
return-statement:
'return' expression[OPT] ;
Example (return.chpl).
The following code defines a procedure that returns the sum of three integers:
proc sum(i1: int, i2: int, i3: int) return i1 + i2 + i3;
Return Types¶
Every procedure has a return type. The return type is either specified
explicitly via return-type
in the procedure declaration, or is
inferred implicitly.
Explicit Return Types¶
If a return type is specified and is not void
, each return statement
of the procedure must include an expression. For a non-ref
return
intent, an implicit conversion occurs from each return expression to the
specified return type. For a ref
return
intent (The Ref Return Intent), the return type must match
the type returned in all of the return statements exactly, when checked
after generic instantiation and parameter folding (if applicable).
Implicit Return Types¶
If a return type is not specified, it is inferred from the return
statements. It is illegal for a procedure to have a return statement
with an expression and a return statement without an expression. For
procedures without any return statements, or when none of the return
statements include an expression, the return type is void
.
Otherwise, the types of the expressions in all of the procedure’s return
statements are considered. If a function has a ref
return intent
(The Ref Return Intent), they all must be the same exact
type, which becomes the inferred return type. Otherwise, there must
exist exactly one type such that an implicit conversion is allowed
between every other type and that type, and that type becomes the
inferred return type. If the above requirements are not satisfied, it is
an error.
Where Clauses¶
The list of function candidates can be constrained by where clauses. A
where clause is specified in the definition of a
function (Procedure Definitions). The expression in the
where clause must be a boolean parameter expression that evaluates to
either true
or false
. If it evaluates to false
, the function
is rejected and thus is not a possible candidate for function
resolution.
Example (whereClause.chpl).
Given two overloaded function definitions
proc foo(x) where x.type == int { writeln("int"); } proc foo(x) where x.type == real { writeln("real"); }the call foo(3) resolves to the first definition because the where clause on the second function evaluates to false.
Nested Functions¶
A function defined in another function is called a nested function. Nesting of functions may be done to arbitrary degrees, i.e., a function can be nested in a nested function.
Nested functions are only visible to function calls within the lexical scope in which they are defined.
Nested functions may refer to variables defined in the function(s) in which they are nested.
Function and Operator Overloading¶
Functions that have the same name but different argument lists are called overloaded functions. Function calls to overloaded functions are resolved according to the function resolution algorithm in Function Resolution.
To define an overloaded operator, use the operator
keyword to define a
function with the same name as the operator. The operators that may be
overloaded are listed in the following table:
arity |
operators |
---|---|
unary |
|
binary |
|
binary |
|
binary |
|
binary |
|
binary |
|
The arity and precedence of the operator must be maintained when it is overloaded. Operator resolution follows the same algorithm as function resolution.
Assignment overloads are not supported for class types.
Function Resolution¶
Function resolution is the algorithm that determines which target function to invoke for a given call expression. Function resolution is defined as follows.
Identify the set of visible functions for the function call. A visible function is any function that satisfies the criteria in Determining Visible Functions. If no visible function can be found, the compiler will issue an error stating that the call cannot be resolved.
From the set of visible functions for the function call, determine the set of candidate functions for the function call. A candidate function is any function that satisfies the criteria in Determining Candidate Functions. If no candidate function can be found and the call is within a generic function, its point of instantiation(s) are visited searching for candidates as defined in Function Visibility in Generic Functions. If still no candidate functions are found, the compiler will issue an error stating that the call cannot be resolved. If exactly one candidate function is found, this is determined to be the target function.
From the set of candidate functions, determine the set of most specific functions. In most cases, there is one most specific function, but there can be several if they differ only in return intent. The set of most specific functions is the set of functions that are not more specific than each other but that are more specific than every other candidate function. The more specific relationship is defined in Determining More Specific Functions.
From the set of most specific functions, the compiler determines a best function for each return intent as described in Determining Best Functions. If there is more than one best function for a given return intent, the compiler will issue an error stating that the call is ambiguous. Otherwise, it will choose the target function from these best functions based on the calling context as described in Choosing Return Intent Overloads Based on Calling Context.
Notation¶
This section uses the following notation:
\(X\) is a function under consideration
The actual argument under consideration is \(A\) of type \(T_A\)
\(M_X\) represents the argument mapping from \(A\) to the formal argument \(F_X\) from function \(X\). \(F_X\) has type \(T_X\). When \(X\) is a generic function, \(F_X\) refers to the possibly generic argument and \(T_X\) refers to the instantiated type.
When needed in the exposition, \(Y\) is another function under consideration, with mapping \(M_Y\) from \(A\) to a formal argument \(F_Y\) of type \(T_Y\).
Determining Visible Functions¶
Given a function call, a function \(X\) is determined to be a visible function if its name is the same as the name of the function call and one of the following conditions is met:
\(X\) is defined in the same scope as the function call or in a lexical outer scope of the function call, or
\(X\) is
public
and is declared in a module that is used from the same scope as the function call or from its lexical outer scope, see also Using Modules, or\(X\) is
public
and is declared in a module that is imported from the same scope as the function call or from its lexical outer scope, and the call qualifies the function name with the module name, see also Importing Modules.Open issue.
What should be the visibility of methods? Applying the above rules excludes, for example, the methods defined in the same module as the receiver type when that module is neither visible nor reachable through module uses or imports from the scope of the function call.
Determining Candidate Functions¶
Given a function call, a function is determined to be a candidate function if there is a valid mapping from the function call to the function where each actual argument is mapped to a formal argument with a legal argument mapping.
Valid Mapping¶
The following algorithm determines a valid mapping from a function call to a function if one exists:
Each actual argument that is passed by name is matched to the formal argument with that name. If there is no formal argument with that name, there is no valid mapping.
The remaining actual arguments are mapped in order to the remaining formal arguments in order. If there are more actual arguments then formal arguments, there is no valid mapping. If any formal argument that is not mapped to by an actual argument does not have a default value, there is no valid mapping.
The valid mapping is the mapping of actual arguments to formal arguments plus default values to formal arguments that are not mapped to by actual arguments.
Legal Argument Mapping¶
An actual argument \(A\) of type \(T_A\) can be legally mapped to a formal argument \(F_X\) according to the following rules.
First, if \(A\) is a type
but \(F_X\) does not use the
type
intent, then it is not a legal argument mapping.
Then, if \(F_X\) is a generic argument:
if \(F_X\) uses
param
intent, then \(A\) must also be aparam
if \(F_X\) uses
type
intent, then \(A\) must also be atype
there must exist an instantiation \(T_X\) of the generic declared type of \(F_X\), if any, that is compatible with the type \(T_A\) according to the rules below.
Next, the type \(T_X\) - which is either the declared type of the formal argument \(F_X\) if it is concrete or the instantiated type if \(F_X\) is generic - must be compatible with the type \(T_A\) according to the concrete intent of \(F_X\):
if \(F_X\) uses
ref
intent, then \(T_A\) must be the same type as \(T_X\)if \(F_X\) uses
const ref
intent, then \(T_A\) and \(T_X\) must be the same type or a subtype of \(T_X\) (see Implicit Subtype Conversions)if \(F_X\) uses
in
orinout
intent, then \(T_A\) must be the same type, a subtype of, or implicitly convertible to \(T_X\).if \(F_X\) uses the
out
intent, it is always a legal argument mapping regardless of the type of the actual and formal. In the event that setting \(T_A\) from \(F_X\) is not possible then a compilation error will be emitted if this function is chosen as the best candidate.if \(F_X\) uses the
type
intent, then \(T_A\) must be the same type or a subtype of \(T_X\) (see Implicit Subtype Conversions).
Finally, if the above compatibility cannot be established, the mapping is
checked for promotion. If \(T_A\) is scalar promotable to \(T_X\)
(see Promotion), then the above rules are checked with the element
type, index type, or yielded type. For example, if \(T_A\) is an
array of int
and \(T_X\) is int
, then promotion occurs and
the above rules will be checked with \(T_A\) == int
.
Determining More Specific Functions¶
Given two candidate functions, \(X\) and \(Y\), the more specific function is determined by the first of the following steps that applies:
If \(X\) does not require promotion and \(Y\) does require promotion, then \(X\) is more specific.
If \(Y\) does not require promotion and \(X\) does require promotion, then \(Y\) is more specific.
If at least one of the legal argument mappings to \(X\) is a more specific argument mapping than the corresponding legal argument mapping to \(Y\) and none of the legal argument mappings to \(Y\) is a more specific argument mapping than the corresponding legal argument mapping to \(X\), then \(X\) is more specific.
If at least one of the legal argument mappings to \(Y\) is a more specific argument mapping than the corresponding legal argument mapping to \(X\) and none of the legal argument mappings to \(X\) is a more specific argument mapping than the corresponding legal argument mapping to \(Y\), then \(Y\) is more specific.
If neither \(X\) nor \(Y\) are methods, and \(X\) shadows \(Y\), then \(X\) is more specific.
If neither \(X\) nor \(Y\) are methods, and \(Y\) shadows \(X\), then \(Y\) is more specific.
If at least one of the legal argument mappings to \(X\) is weak preferred and none of the legal argument mappings to \(Y\) are weak preferred, then \(X\) is more specific.
If at least one of the legal argument mappings to \(Y\) is weak preferred and none of the legal argument mappings to \(X\) are weak preferred, then \(Y\) is more specific.
If at least one of the legal argument mappings to \(X\) is weaker preferred and none of the legal argument mappings to \(Y\) are weaker preferred, then \(X\) is more specific.
If at least one of the legal argument mappings to \(Y\) is weaker preferred and none of the legal argument mappings to \(X\) are weaker preferred, then \(Y\) is more specific.
If at least one of the legal argument mappings to \(X\) is weakest preferred and none of the legal argument mappings to \(Y\) are weakest preferred, then \(X\) is more specific.
If at least one of the legal argument mappings to \(Y\) is weakest preferred and none of the legal argument mappings to \(X\) are weakest preferred, then \(Y\) is more specific.
Otherwise neither function is more specific.
The next section discusses the level of preference for an argument mapping. As discussed above, \(M_X\) represents the argument mapping from \(A\) to the formal argument \(F_X\) from function \(X\) with type \(T_X\). When \(X\) is a generic function, \(F_X\) refers to the argument before instantiation and \(T_X\) represents the type of \(F_X\) after instantiation. \(M_Y\), \(F_Y\), and \(T_Y\) are defined in a similar manner to represent the argument mapping for \(Y\).
The level of preference for one of these argument mappings is determined by the first of the following steps that applies:
If \(F_X\) or \(F_Y\) uses the
out
intent, then neither argument mapping is preferred.If \(T_X\) and \(T_Y\) are the same type, \(F_X\) is an instantiated parameter, and \(F_Y\) is not an instantiated parameter, \(M_X\) is more specific.
If \(T_X\) and \(T_Y\) are the same type, \(F_Y\) is an instantiated parameter, and \(F_X\) is not an instantiated parameter, \(M_Y\) is more specific.
If \(M_X\) does not require scalar promotion and \(M_Y\) requires scalar promotion, \(M_X\) is more specific.
If \(M_X\) requires scalar promotion and \(M_Y\) does not require scalar promotion, \(M_Y\) is more specific.
If \(T_X\) and \(T_Y\) are the same type, \(F_X\) is generic, and \(F_Y\) is not generic, \(M_X\) is more specific.
If \(T_X\) and \(T_Y\) are the same type, \(F_Y\) is generic, and \(F_X\) is not generic, \(M_Y\) is more specific.
If \(F_X\) is not generic over all types and \(F_Y\) is generic over all types, \(M_X\) is more specific.
If \(F_X\) is generic over all types and \(F_Y\) is not generic over all types, \(M_Y\) is more specific.
If \(F_X\) and \(F_Y\) are both generic, and \(F_X\) is partially concrete but \(F_Y\) is not, then \(M_X\) is more specific.
If \(F_X\) and \(F_Y\) are both generic, and \(F_Y\) is partially concrete but \(F_X\) is not, then \(M_Y\) is more specific.
If \(F_X\) is a
param
argument but \(F_Y\) is not, then \(M_X\) is weak preferred.If \(F_Y\) is a
param
argument but \(F_X\) is not, then \(M_Y\) is weak preferred.If \(A\) is not a
param
argument with a default size and \(F_Y\) requires a narrowing conversion but \(F_X\) does not, then \(M_X\) is weak preferred.If \(A\) is not a
param
argument with a default size and \(F_X\) requires a narrowing conversion but \(F_Y\) does not, then \(M_Y\) is weak preferred.If \(T_A\) and \(T_X\) are the same type and \(T_A\) and \(T_Y\) are not the same type, \(M_X\) is more specific.
If \(T_A\) and \(T_X\) are not the same type and \(T_A\) and \(T_Y\) are the same type, \(M_Y\) is more specific.
If \(A\) uses a scalar promotion type equal to \(T_X\) but different from \(T_Y\), then \(M_X\) will be preferred as follows:
if \(A\) is a
param
argument with a default size, then \(M_X\) is weakest preferredif \(A\) is a
param
argument with non-default size, then \(M_X\) is weaker preferredotherwise, \(M_X\) is more specific
If \(A\) uses a scalar promotion type equal to \(T_Y\) but different from \(T_X\), then \(M_Y\) will be preferred as follows:
if \(A\) is a
param
argument with a default size, then \(M_Y\) is weakest preferredif \(A\) is a
param
argument with non-default size, then \(M_Y\) is weaker preferredotherwise, \(M_Y\) is more specific
If \(T_A\) or its scalar promotion type prefers conversion to \(T_X\) over conversion to \(T_Y\), then \(M_X\) is preferred. If \(A\) is a
param
argument with a default size, then \(M_X\) is weakest preferred. Otherwise, \(M_X\) is weaker preferred.Type conversion preferences are as follows:
Prefer converting a numeric argument to a numeric argument of a different width but the same category over converting to another type. Categories are
bool
enum
int or uint
real
imag
complex
Prefer an enum or bool cast to int over uint
Prefer an enum or bool cast to a default-sized int or uint over another size of int or uint
Prefer an int or uint cast to a real with the same width (if available) or next-largest width (if not) over a larger real
Prefer an int or uint cast to a complex whose components are the same width (if available) or the next largest width (if not) over a larger complex
Prefer real/imag cast to the complex with that component size (ie total width of twice the real/imag) over another size of complex
If \(T_A\) or its scalar promotion type prefers conversion to \(T_Y\) over conversion to \(T_X\), then \(M_Y\) is preferred. If \(A\) is a
param
argument with a default size, then \(M_Y\) is weakest preferred. Otherwise, \(M_Y\) is weaker preferred.If \(T_X\) is derived from \(T_Y\), then \(M_X\) is more specific.
If \(T_Y\) is derived from \(T_X\), then \(M_Y\) is more specific.
If there is an implicit conversion from \(T_X\) to \(T_Y\), then \(M_X\) is more specific.
If there is an implicit conversion from \(T_Y\) to \(T_X\), then \(M_Y\) is more specific.
If \(T_X\) is any
int
type and \(T_Y\) is anyuint
type, \(M_X\) is more specific.If \(T_Y\) is any
int
type and \(T_X\) is anyuint
type, \(M_Y\) is more specific.Otherwise neither mapping is more specific.
Determining Best Functions¶
Given the set of most specific functions for a given return intent, only the following function(s) are selected as best functions:
all functions, if none of them contain a
where
clause;only those functions that have a
where
clause, otherwise.
Choosing Return Intent Overloads Based on Calling Context¶
See also Return Intent Overloads.
The compiler can choose between overloads differing in return intent when:
there are zero or one best functions for each of
ref
,const ref
,const
, or the default (blank) return intentat least two of the above return intents have a best function.
In that case, the compiler is able to choose between ref
return,
const ref
return, and value return functions based upon the context
of the call. The compiler chooses between these return intent overloads
as follows:
If present, a ref
return version will be chosen when:
the call appears on the left-hand side of a variable initialization or assignment statement
the call is passed to another function as a formal argument with
out
,inout
, orref
intentthe call is captured into a
ref
variablethe call is returned from a function with
ref
return intent
Otherwise, the const ref
return or value return version will be
chosen. If only one of these is in the set of most specific functions,
it will be chosen. If both are present in the set, the choice will be
made as follows:
The const ref
version will be chosen when:
the call is passed to another function as a formal argument with
const ref
intentthe call is captured into a
const ref
variablethe call is returned from a function with
const ref
return intent
Otherwise, the value version will be chosen.