//===-- lib/Evaluate/intrinsics.cpp ---------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "flang/Evaluate/intrinsics.h"
#include "flang/Common/Fortran.h"
#include "flang/Common/enum-set.h"
#include "flang/Common/idioms.h"
#include "flang/Evaluate/check-expression.h"
#include "flang/Evaluate/common.h"
#include "flang/Evaluate/expression.h"
#include "flang/Evaluate/fold.h"
#include "flang/Evaluate/shape.h"
#include "flang/Evaluate/tools.h"
#include "flang/Evaluate/type.h"
#include "flang/Semantics/scope.h"
#include "flang/Semantics/tools.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <cmath>
#include <map>
#include <string>
#include <utility>
using namespace Fortran::parser::literals;
namespace Fortran::evaluate {
class FoldingContext;
// This file defines the supported intrinsic procedures and implements
// their recognition and validation. It is largely table-driven. See
// docs/intrinsics.md and section 16 of the Fortran 2018 standard
// for full details on each of the intrinsics. Be advised, they have
// complicated details, and the design of these tables has to accommodate
// that complexity.
// Dummy arguments to generic intrinsic procedures are each specified by
// their keyword name (rarely used, but always defined), allowable type
// categories, a kind pattern, a rank pattern, and information about
// optionality and defaults. The kind and rank patterns are represented
// here with code values that are significant to the matching/validation engine.
// An actual argument to an intrinsic procedure may be a procedure itself
// only if the dummy argument is Rank::reduceOperation,
// KindCode::addressable, or the special case of NULL(MOLD=procedurePointer).
// These are small bit-sets of type category enumerators.
// Note that typeless (BOZ literal) values don't have a distinct type category.
// These typeless arguments are represented in the tables as if they were
// INTEGER with a special "typeless" kind code. Arguments of intrinsic types
// that can also be typeless values are encoded with an "elementalOrBOZ"
// rank pattern.
// Assumed-type (TYPE(*)) dummy arguments can be forwarded along to some
// intrinsic functions that accept AnyType + Rank::anyOrAssumedRank,
// AnyType + Rank::arrayOrAssumedRank, or AnyType + Kind::addressable.
using CategorySet = common::EnumSet<TypeCategory, 8>;
static constexpr CategorySet IntType{TypeCategory::Integer};
static constexpr CategorySet RealType{TypeCategory::Real};
static constexpr CategorySet ComplexType{TypeCategory::Complex};
static constexpr CategorySet CharType{TypeCategory::Character};
static constexpr CategorySet LogicalType{TypeCategory::Logical};
static constexpr CategorySet IntOrRealType{IntType | RealType};
static constexpr CategorySet IntOrRealOrCharType{IntType | RealType | CharType};
static constexpr CategorySet IntOrLogicalType{IntType | LogicalType};
static constexpr CategorySet FloatingType{RealType | ComplexType};
static constexpr CategorySet NumericType{IntType | RealType | ComplexType};
static constexpr CategorySet RelatableType{IntType | RealType | CharType};
static constexpr CategorySet DerivedType{TypeCategory::Derived};
static constexpr CategorySet IntrinsicType{
IntType | RealType | ComplexType | CharType | LogicalType};
static constexpr CategorySet AnyType{IntrinsicType | DerivedType};
ENUM_CLASS(KindCode, none, defaultIntegerKind,
defaultRealKind, // is also the default COMPLEX kind
doublePrecision, defaultCharKind, defaultLogicalKind,
greaterOrEqualToKind, // match kind value greater than or equal to a single
// explicit kind value
any, // matches any kind value; each instance is independent
// match any kind, but all "same" kinds must be equal. For characters, also
// implies that lengths must be equal.
same,
// for characters that only require the same kind, not length
sameKind,
operand, // match any kind, with promotion (non-standard)
typeless, // BOZ literals are INTEGER with this kind
ieeeFlagType, // IEEE_FLAG_TYPE from ISO_FORTRAN_EXCEPTION
ieeeRoundType, // IEEE_ROUND_TYPE from ISO_FORTRAN_ARITHMETIC
teamType, // TEAM_TYPE from module ISO_FORTRAN_ENV (for coarrays)
kindArg, // this argument is KIND=
effectiveKind, // for function results: "kindArg" value, possibly defaulted
dimArg, // this argument is DIM=
likeMultiply, // for DOT_PRODUCT and MATMUL
subscript, // address-sized integer
size, // default KIND= for SIZE(), UBOUND, &c.
addressable, // for PRESENT(), &c.; anything (incl. procedure) but BOZ
nullPointerType, // for ASSOCIATED(NULL())
exactKind, // a single explicit exactKindValue
atomicIntKind, // atomic_int_kind from iso_fortran_env
atomicIntOrLogicalKind, // atomic_int_kind or atomic_logical_kind
sameAtom, // same type and kind as atom
)
struct TypePattern {
CategorySet categorySet;
KindCode kindCode{KindCode::none};
int kindValue{0}; // for KindCode::exactKind and greaterOrEqualToKind
llvm::raw_ostream &Dump(llvm::raw_ostream &) const;
};
// Abbreviations for argument and result patterns in the intrinsic prototypes:
// Match specific kinds of intrinsic types
static constexpr TypePattern DefaultInt{IntType, KindCode::defaultIntegerKind};
static constexpr TypePattern DefaultReal{RealType, KindCode::defaultRealKind};
static constexpr TypePattern DefaultComplex{
ComplexType, KindCode::defaultRealKind};
static constexpr TypePattern DefaultChar{CharType, KindCode::defaultCharKind};
static constexpr TypePattern DefaultLogical{
LogicalType, KindCode::defaultLogicalKind};
static constexpr TypePattern BOZ{IntType, KindCode::typeless};
static constexpr TypePattern IeeeFlagType{DerivedType, KindCode::ieeeFlagType};
static constexpr TypePattern IeeeRoundType{
DerivedType, KindCode::ieeeRoundType};
static constexpr TypePattern TeamType{DerivedType, KindCode::teamType};
static constexpr TypePattern DoublePrecision{
RealType, KindCode::doublePrecision};
static constexpr TypePattern DoublePrecisionComplex{
ComplexType, KindCode::doublePrecision};
static constexpr TypePattern SubscriptInt{IntType, KindCode::subscript};
// Match any kind of some intrinsic or derived types
static constexpr TypePattern AnyInt{IntType, KindCode::any};
static constexpr TypePattern AnyReal{RealType, KindCode::any};
static constexpr TypePattern AnyIntOrReal{IntOrRealType, KindCode::any};
static constexpr TypePattern AnyIntOrRealOrChar{
IntOrRealOrCharType, KindCode::any};
static constexpr TypePattern AnyIntOrLogical{IntOrLogicalType, KindCode::any};
static constexpr TypePattern AnyComplex{ComplexType, KindCode::any};
static constexpr TypePattern AnyFloating{FloatingType, KindCode::any};
static constexpr TypePattern AnyNumeric{NumericType, KindCode::any};
static constexpr TypePattern AnyChar{CharType, KindCode::any};
static constexpr TypePattern AnyLogical{LogicalType, KindCode::any};
static constexpr TypePattern AnyRelatable{RelatableType, KindCode::any};
static constexpr TypePattern AnyIntrinsic{IntrinsicType, KindCode::any};
static constexpr TypePattern ExtensibleDerived{DerivedType, KindCode::any};
static constexpr TypePattern AnyData{AnyType, KindCode::any};
// Type is irrelevant, but not BOZ (for PRESENT(), OPTIONAL(), &c.)
static constexpr TypePattern Addressable{AnyType, KindCode::addressable};
// Match some kind of some intrinsic type(s); all "Same" values must match,
// even when not in the same category (e.g., SameComplex and SameReal).
// Can be used to specify a result so long as at least one argument is
// a "Same".
static constexpr TypePattern SameInt{IntType, KindCode::same};
static constexpr TypePattern SameReal{RealType, KindCode::same};
static constexpr TypePattern SameIntOrReal{IntOrRealType, KindCode::same};
static constexpr TypePattern SameComplex{ComplexType, KindCode::same};
static constexpr TypePattern SameFloating{FloatingType, KindCode::same};
static constexpr TypePattern SameNumeric{NumericType, KindCode::same};
static constexpr TypePattern SameChar{CharType, KindCode::same};
static constexpr TypePattern SameCharNoLen{CharType, KindCode::sameKind};
static constexpr TypePattern SameLogical{LogicalType, KindCode::same};
static constexpr TypePattern SameRelatable{RelatableType, KindCode::same};
static constexpr TypePattern SameIntrinsic{IntrinsicType, KindCode::same};
static constexpr TypePattern SameDerivedType{
CategorySet{TypeCategory::Derived}, KindCode::same};
static constexpr TypePattern SameType{AnyType, KindCode::same};
// Match some kind of some INTEGER or REAL type(s); when argument types
// &/or kinds differ, their values are converted as if they were operands to
// an intrinsic operation like addition. This is a nonstandard but nearly
// universal extension feature.
static constexpr TypePattern OperandReal{RealType, KindCode::operand};
static constexpr TypePattern OperandInt{IntType, KindCode::operand};
static constexpr TypePattern OperandIntOrReal{IntOrRealType, KindCode::operand};
// For ASSOCIATED, the first argument is a typeless pointer
static constexpr TypePattern AnyPointer{AnyType, KindCode::nullPointerType};
// For DOT_PRODUCT and MATMUL, the result type depends on the arguments
static constexpr TypePattern ResultLogical{LogicalType, KindCode::likeMultiply};
static constexpr TypePattern ResultNumeric{NumericType, KindCode::likeMultiply};
// Result types with known category and KIND=
static constexpr TypePattern KINDInt{IntType, KindCode::effectiveKind};
static constexpr TypePattern KINDReal{RealType, KindCode::effectiveKind};
static constexpr TypePattern KINDComplex{ComplexType, KindCode::effectiveKind};
static constexpr TypePattern KINDChar{CharType, KindCode::effectiveKind};
static constexpr TypePattern KINDLogical{LogicalType, KindCode::effectiveKind};
static constexpr TypePattern AtomicInt{IntType, KindCode::atomicIntKind};
static constexpr TypePattern AtomicIntOrLogical{
IntOrLogicalType, KindCode::atomicIntOrLogicalKind};
static constexpr TypePattern SameAtom{IntOrLogicalType, KindCode::sameAtom};
// The default rank pattern for dummy arguments and function results is
// "elemental".
ENUM_CLASS(Rank,
elemental, // scalar, or array that conforms with other array arguments
elementalOrBOZ, // elemental, or typeless BOZ literal scalar
scalar, vector,
shape, // INTEGER vector of known length and no negative element
matrix,
array, // not scalar, rank is known and greater than zero
coarray, // rank is known and can be scalar; has nonzero corank
atom, // is scalar and has nonzero corank or is coindexed
known, // rank is known and can be scalar
anyOrAssumedRank, // any rank, or assumed; assumed-type TYPE(*) allowed
arrayOrAssumedRank, // rank >= 1 or assumed; assumed-type TYPE(*) allowed
conformable, // scalar, or array of same rank & shape as "array" argument
reduceOperation, // a pure function with constraints for REDUCE
dimReduced, // scalar if no DIM= argument, else rank(array)-1
dimRemovedOrScalar, // rank(array)-1 (less DIM) or scalar
scalarIfDim, // scalar if DIM= argument is present, else rank one array
locReduced, // vector(1:rank) if no DIM= argument, else rank(array)-1
rankPlus1, // rank(known)+1
shaped, // rank is length of SHAPE vector
)
ENUM_CLASS(Optionality, required,
optional, // unless DIM= for SIZE(assumedSize)
missing, // for DIM= cases like FINDLOC
repeats, // for MAX/MIN and their several variants
)
ENUM_CLASS(ArgFlag, none,
canBeNull, // actual argument can be NULL(with or without MOLD=)
canBeMoldNull, // actual argument can be NULL(with MOLD=)
defaultsToSameKind, // for MatchingDefaultKIND
defaultsToSizeKind, // for SizeDefaultKIND
defaultsToDefaultForResult, // for DefaultingKIND
notAssumedSize)
struct IntrinsicDummyArgument {
const char *keyword{nullptr};
TypePattern typePattern;
Rank rank{Rank::elemental};
Optionality optionality{Optionality::required};
common::Intent intent{common::Intent::In};
common::EnumSet<ArgFlag, 32> flags{};
llvm::raw_ostream &Dump(llvm::raw_ostream &) const;
};
// constexpr abbreviations for popular arguments:
// DefaultingKIND is a KIND= argument whose default value is the appropriate
// KIND(0), KIND(0.0), KIND(''), &c. value for the function result.
static constexpr IntrinsicDummyArgument DefaultingKIND{"kind",
{IntType, KindCode::kindArg}, Rank::scalar, Optionality::optional,
common::Intent::In, {ArgFlag::defaultsToDefaultForResult}};
// MatchingDefaultKIND is a KIND= argument whose default value is the
// kind of any "Same" function argument (viz., the one whose kind pattern is
// "same").
static constexpr IntrinsicDummyArgument MatchingDefaultKIND{"kind",
{IntType, KindCode::kindArg}, Rank::scalar, Optionality::optional,
common::Intent::In, {ArgFlag::defaultsToSameKind}};
// SizeDefaultKind is a KIND= argument whose default value should be
// the kind of INTEGER used for address calculations, and can be
// set so with a compiler flag; but the standard mandates the
// kind of default INTEGER.
static constexpr IntrinsicDummyArgument SizeDefaultKIND{"kind",
{IntType, KindCode::kindArg}, Rank::scalar, Optionality::optional,
common::Intent::In, {ArgFlag::defaultsToSizeKind}};
static constexpr IntrinsicDummyArgument RequiredDIM{"dim",
{IntType, KindCode::dimArg}, Rank::scalar, Optionality::required,
common::Intent::In};
static constexpr IntrinsicDummyArgument OptionalDIM{"dim",
{IntType, KindCode::dimArg}, Rank::scalar, Optionality::optional,
common::Intent::In};
static constexpr IntrinsicDummyArgument MissingDIM{"dim",
{IntType, KindCode::dimArg}, Rank::scalar, Optionality::missing,
common::Intent::In};
static constexpr IntrinsicDummyArgument OptionalMASK{"mask", AnyLogical,
Rank::conformable, Optionality::optional, common::Intent::In};
static constexpr IntrinsicDummyArgument OptionalTEAM{
"team", TeamType, Rank::scalar, Optionality::optional, common::Intent::In};
struct IntrinsicInterface {
static constexpr int maxArguments{7}; // if not a MAX/MIN(...)
const char *name{nullptr};
IntrinsicDummyArgument dummy[maxArguments];
TypePattern result;
Rank rank{Rank::elemental};
IntrinsicClass intrinsicClass{IntrinsicClass::elementalFunction};
std::optional<SpecificCall> Match(const CallCharacteristics &,
const common::IntrinsicTypeDefaultKinds &, ActualArguments &,
FoldingContext &context, const semantics::Scope *builtins) const;
int CountArguments() const;
llvm::raw_ostream &Dump(llvm::raw_ostream &) const;
};
int IntrinsicInterface::CountArguments() const {
int n{0};
while (n < maxArguments && dummy[n].keyword) {
++n;
}
return n;
}
// GENERIC INTRINSIC FUNCTION INTERFACES
// Each entry in this table defines a pattern. Some intrinsic
// functions have more than one such pattern. Besides the name
// of the intrinsic function, each pattern has specifications for
// the dummy arguments and for the result of the function.
// The dummy argument patterns each have a name (these are from the
// standard, but rarely appear in actual code), a type and kind
// pattern, allowable ranks, and optionality indicators.
// Be advised, the default rank pattern is "elemental".
static const IntrinsicInterface genericIntrinsicFunction[]{
{"abs", {{"a", SameIntOrReal}}, SameIntOrReal},
{"abs", {{"a", SameComplex}}, SameReal},
{"achar", {{"i", AnyInt, Rank::elementalOrBOZ}, DefaultingKIND}, KINDChar},
{"acos", {{"x", SameFloating}}, SameFloating},
{"acosd", {{"x", SameFloating}}, SameFloating},
{"acosh", {{"x", SameFloating}}, SameFloating},
{"adjustl", {{"string", SameChar}}, SameChar},
{"adjustr", {{"string", SameChar}}, SameChar},
{"aimag", {{"z", SameComplex}}, SameReal},
{"aint", {{"a", SameReal}, MatchingDefaultKIND}, KINDReal},
{"all", {{"mask", SameLogical, Rank::array}, OptionalDIM}, SameLogical,
Rank::dimReduced, IntrinsicClass::transformationalFunction},
{"allocated", {{"scalar", AnyData, Rank::scalar}}, DefaultLogical,
Rank::elemental, IntrinsicClass::inquiryFunction},
{"allocated", {{"array", AnyData, Rank::anyOrAssumedRank}}, DefaultLogical,
Rank::elemental, IntrinsicClass::inquiryFunction},
{"anint", {{"a", SameReal}, MatchingDefaultKIND}, KINDReal},
{"any", {{"mask", SameLogical, Rank::array}, OptionalDIM}, SameLogical,
Rank::dimReduced, IntrinsicClass::transformationalFunction},
{"asin", {{"x", SameFloating}}, SameFloating},
{"asind", {{"x", SameFloating}}, SameFloating},
{"asinh", {{"x", SameFloating}}, SameFloating},
{"associated",
{{"pointer", AnyPointer, Rank::anyOrAssumedRank, Optionality::required,
common::Intent::In, {ArgFlag::canBeNull}},
{"target", Addressable, Rank::anyOrAssumedRank,
Optionality::optional, common::Intent::In,
{ArgFlag::canBeNull}}},
DefaultLogical, Rank::elemental, IntrinsicClass::inquiryFunction},
{"atan", {{"x", SameFloating}}, SameFloating},
{"atan", {{"y", OperandReal}, {"x", OperandReal}}, OperandReal},
{"atand", {{"x", SameFloating}}, SameFloating},
{"atand", {{"y", OperandReal}, {"x", OperandReal}}, OperandReal},
{"atan2", {{"y", OperandReal}, {"x", OperandReal}}, OperandReal},
{"atan2d", {{"y", OperandReal}, {"x", OperandReal}}, OperandReal},
{"atanpi", {{"x", SameFloating}}, SameFloating},
{"atanpi", {{"y", OperandReal}, {"x", OperandReal}}, OperandReal},
{"atan2pi", {{"y", OperandReal}, {"x", OperandReal}}, OperandReal},
{"atanh", {{"x", SameFloating}}, SameFloating},
{"bessel_j0", {{"x", SameReal}}, SameReal},
{"bessel_j1", {{"x", SameReal}}, SameReal},
{"bessel_jn", {{"n", AnyInt}, {"x", SameReal}}, SameReal},
{"bessel_jn",
{{"n1", AnyInt, Rank::scalar}, {"n2", AnyInt, Rank::scalar},
{"x", SameReal, Rank::scalar}},
SameReal, Rank::vector, IntrinsicClass::transformationalFunction},
{"bessel_y0", {{"x", SameReal}}, SameReal},
{"bessel_y1", {{"x", SameReal}}, SameReal},
{"bessel_yn", {{"n", AnyInt}, {"x", SameReal}}, SameReal},
{"bessel_yn",
{{"n1", AnyInt, Rank::scalar}, {"n2", AnyInt, Rank::scalar},
{"x", SameReal, Rank::scalar}},
SameReal, Rank::vector, IntrinsicClass::transformationalFunction},
{"bge",
{{"i", AnyInt, Rank::elementalOrBOZ},
{"j", AnyInt, Rank::elementalOrBOZ}},
DefaultLogical},
{"bgt",
{{"i", AnyInt, Rank::elementalOrBOZ},
{"j", AnyInt, Rank::elementalOrBOZ}},
DefaultLogical},
{"bit_size",
{{"i", SameInt, Rank::anyOrAssumedRank, Optionality::required,
common::Intent::In, {ArgFlag::canBeMoldNull}}},
SameInt, Rank::scalar, IntrinsicClass::inquiryFunction},
{"ble",
{{"i", AnyInt, Rank::elementalOrBOZ},
{"j", AnyInt, Rank::elementalOrBOZ}},
DefaultLogical},
{"blt",
{{"i", AnyInt, Rank::elementalOrBOZ},
{"j", AnyInt, Rank::elementalOrBOZ}},
DefaultLogical},
{"btest", {{"i", AnyInt, Rank::elementalOrBOZ}, {"pos", AnyInt}},
DefaultLogical},
{"ceiling", {{"a", AnyReal}, DefaultingKIND}, KINDInt},
{"char", {{"i", AnyInt, Rank::elementalOrBOZ}, DefaultingKIND}, KINDChar},
{"cmplx", {{"x", AnyComplex}, DefaultingKIND}, KINDComplex},
{"cmplx",
{{"x", AnyIntOrReal, Rank::elementalOrBOZ},
{"y", AnyIntOrReal, Rank::elementalOrBOZ, Optionality::optional},
DefaultingKIND},
KINDComplex},
{"command_argument_count", {}, DefaultInt, Rank::scalar,
IntrinsicClass::transformationalFunction},
{"conjg", {{"z", SameComplex}}, SameComplex},
{"cos", {{"x", SameFloating}}, SameFloating},
{"cosd", {{"x", SameFloating}}, SameFloating},
{"cosh", {{"x", SameFloating}}, SameFloating},
{"count", {{"mask", AnyLogical, Rank::array}, OptionalDIM, DefaultingKIND},
KINDInt, Rank::dimReduced, IntrinsicClass::transformationalFunction},
{"cshift",
{{"array", SameType, Rank::array},
{"shift", AnyInt, Rank::dimRemovedOrScalar}, OptionalDIM},
SameType, Rank::conformable, IntrinsicClass::transformationalFunction},
{"dble", {{"a", AnyNumeric, Rank::elementalOrBOZ}}, DoublePrecision},
{"digits",
{{"x", AnyIntOrReal, Rank::anyOrAssumedRank, Optionality::required,
common::Intent::In, {ArgFlag::canBeMoldNull}}},
DefaultInt, Rank::scalar, IntrinsicClass::inquiryFunction},
{"dim", {{"x", OperandIntOrReal}, {"y", OperandIntOrReal}},
OperandIntOrReal},
{"dot_product",
{{"vector_a", AnyLogical, Rank::vector},
{"vector_b", AnyLogical, Rank::vector}},
ResultLogical, Rank::scalar, IntrinsicClass::transformationalFunction},
{"dot_product",
{{"vector_a", AnyComplex, Rank::vector},
{"vector_b", AnyNumeric, Rank::vector}},
ResultNumeric, Rank::scalar, // conjugates vector_a
IntrinsicClass::transformationalFunction},
{"dot_product",
{{"vector_a", AnyIntOrReal, Rank::vector},
{"vector_b", AnyNumeric, Rank::vector}},
ResultNumeric, Rank::scalar, IntrinsicClass::transformationalFunction},
{"dprod", {{"x", DefaultReal}, {"y", DefaultReal}}, DoublePrecision},
{"dshiftl",
{{"i", SameInt}, {"j", SameInt, Rank::elementalOrBOZ},
{"shift", AnyInt}},
SameInt},
{"dshiftl", {{"i", BOZ}, {"j", SameInt}, {"shift", AnyInt}}, SameInt},
{"dshiftr",
{{"i", SameInt}, {"j", SameInt, Rank::elementalOrBOZ},
{"shift", AnyInt}},
SameInt},
{"dshiftr", {{"i", BOZ}, {"j", SameInt}, {"shift", AnyInt}}, SameInt},
{"eoshift",
{{"array", SameIntrinsic, Rank::array},
{"shift", AnyInt, Rank::dimRemovedOrScalar},
{"boundary", SameIntrinsic, Rank::dimRemovedOrScalar,
Optionality::optional},
OptionalDIM},
SameIntrinsic, Rank::conformable,
IntrinsicClass::transformationalFunction},
{"eoshift",
{{"array", SameDerivedType, Rank::array},
{"shift", AnyInt, Rank::dimRemovedOrScalar},
// BOUNDARY= is not optional for derived types
{"boundary", SameDerivedType, Rank::dimRemovedOrScalar},
OptionalDIM},
SameDerivedType, Rank::conformable,
IntrinsicClass::transformationalFunction},
{"epsilon",
{{"x", SameReal, Rank::anyOrAssumedRank, Optionality::required,
common::Intent::In, {ArgFlag::canBeMoldNull}}},
SameReal, Rank::scalar, IntrinsicClass::inquiryFunction},
{"erf", {{"x", SameReal}}, SameReal},
{"erfc", {{"x", SameReal}}, SameReal},
{"erfc_scaled", {{"x", SameReal}}, SameReal},
{"etime",
{{"values", TypePattern{RealType, KindCode::exactKind, 4}, Rank::vector,
Optionality::required, common::Intent::Out}},
TypePattern{RealType, KindCode::exactKind, 4}},
{"exp", {{"x", SameFloating}}, SameFloating},
{"exp", {{"x", SameFloating}}, SameFloating},
{"exponent", {{"x", AnyReal}}, DefaultInt},
{"exp", {{"x", SameFloating}}, SameFloating},
{"extends_type_of",
{{"a", ExtensibleDerived, Rank::anyOrAssumedRank, Optionality::required,
common::Intent::In, {ArgFlag::canBeMoldNull}},
{"mold", ExtensibleDerived, Rank::anyOrAssumedRank,
Optionality::required, common::Intent::In,
{ArgFlag::canBeMoldNull}}},
DefaultLogical, Rank::scalar, IntrinsicClass::inquiryFunction},
{"failed_images", {OptionalTEAM, SizeDefaultKIND}, KINDInt, Rank::vector,
IntrinsicClass::transformationalFunction},
{"findloc",
{{"array", AnyNumeric, Rank::array},
{"value", AnyNumeric, Rank::scalar}, RequiredDIM, OptionalMASK,
SizeDefaultKIND,
{"back", AnyLogical, Rank::scalar, Optionality::optional}},
KINDInt, Rank::locReduced, IntrinsicClass::transformationalFunction},
{"findloc",
{{"array", AnyNumeric, Rank::array},
{"value", AnyNumeric, Rank::scalar}, MissingDIM, OptionalMASK,
SizeDefaultKIND,
{"back", AnyLogical, Rank::scalar, Optionality::optional}},
KINDInt, Rank::vector, IntrinsicClass::transformationalFunction},
{"findloc",
{{"array", SameCharNoLen, Rank::array},
{"value", SameCharNoLen, Rank::scalar}, RequiredDIM, OptionalMASK,
SizeDefaultKIND,
{"back", AnyLogical, Rank::scalar, Optionality::optional}},
KINDInt, Rank::locReduced, IntrinsicClass::transformationalFunction},
{"findloc",
{{"array", SameCharNoLen, Rank::array},
{"value", SameCharNoLen, Rank::scalar}, MissingDIM, OptionalMASK,
SizeDefaultKIND,
{"back", AnyLogical, Rank::scalar, Optionality::optional}},
KINDInt, Rank::vector, IntrinsicClass::transformationalFunction},
{"findloc",
{{"array", AnyLogical, Rank::array},
{"value", AnyLogical, Rank::scalar}, RequiredDIM, OptionalMASK,
SizeDefaultKIND,
{"back", AnyLogical, Rank::scalar, Optionality::optional}},
KINDInt, Rank::locReduced, IntrinsicClass::transformationalFunction},
{"findloc",
{{"array", AnyLogical, Rank::array},
{"value", AnyLogical, Rank::scalar}, MissingDIM, OptionalMASK,
SizeDefaultKIND,
{"back", AnyLogical, Rank::scalar, Optionality::optional}},
KINDInt, Rank::vector, IntrinsicClass::transformationalFunction},
{"floor", {{"a", AnyReal}, DefaultingKIND}, KINDInt},
{"fraction", {{"x", SameReal}}, SameReal},
{"gamma", {{"x", SameReal}}, SameReal},
{"get_team", {{"level", DefaultInt, Rank::scalar, Optionality::optional}},
TeamType, Rank::scalar, IntrinsicClass::transformationalFunction},
{"getcwd",
{{"c", DefaultChar, Rank::scalar, Optionality::required,
common::Intent::Out}},
TypePattern{IntType, KindCode::greaterOrEqualToKind, 4}},
{"getpid", {}, DefaultInt},
{"huge",
{{"x", SameIntOrReal, Rank::anyOrAssumedRank, Optionality::required,
common::Intent::In, {ArgFlag::canBeMoldNull}}},
SameIntOrReal, Rank::scalar, IntrinsicClass::inquiryFunction},
{"hypot", {{"x", OperandReal}, {"y", OperandReal}}, OperandReal},
{"iachar", {{"c", AnyChar}, DefaultingKIND}, KINDInt},
{"iall", {{"array", SameInt, Rank::array}, RequiredDIM, OptionalMASK},
SameInt, Rank::dimReduced, IntrinsicClass::transformationalFunction},
{"iall", {{"array", SameInt, Rank::array}, MissingDIM, OptionalMASK},
SameInt, Rank::scalar, IntrinsicClass::transformationalFunction},
{"iany", {{"array", SameInt, Rank::array}, RequiredDIM, OptionalMASK},
SameInt, Rank::dimReduced, IntrinsicClass::transformationalFunction},
{"iany", {{"array", SameInt, Rank::array}, MissingDIM, OptionalMASK},
SameInt, Rank::scalar, IntrinsicClass::transformationalFunction},
{"iparity", {{"array", SameInt, Rank::array}, RequiredDIM, OptionalMASK},
SameInt, Rank::dimReduced, IntrinsicClass::transformationalFunction},
{"iparity", {{"array", SameInt, Rank::array}, MissingDIM, OptionalMASK},
SameInt, Rank::scalar, IntrinsicClass::transformationalFunction},
{"iand", {{"i", OperandInt}, {"j", OperandInt, Rank::elementalOrBOZ}},
OperandInt},
{"iand", {{"i", BOZ}, {"j", SameInt}}, SameInt},
{"ibclr", {{"i", SameInt}, {"pos", AnyInt}}, SameInt},
{"ibits", {{"i", SameInt}, {"pos", AnyInt}, {"len", AnyInt}}, SameInt},
{"ibset", {{"i", SameInt}, {"pos", AnyInt}}, SameInt},
{"ichar", {{"c", AnyChar}, DefaultingKIND}, KINDInt},
{"ieor", {{"i", OperandInt}, {"j", OperandInt, Rank::elementalOrBOZ}},
OperandInt},
{"ieor", {{"i", BOZ}, {"j", SameInt}}, SameInt},
{"image_index",
{{"coarray", AnyData, Rank::coarray}, {"sub", AnyInt, Rank::vector}},
DefaultInt, Rank::scalar, IntrinsicClass::transformationalFunction},
{"image_index",
{{"coarray", AnyData, Rank::coarray}, {"sub", AnyInt, Rank::vector},
{"team", TeamType, Rank::scalar}},
DefaultInt, Rank::scalar, IntrinsicClass::transformationalFunction},
{"image_index",
{{"coarray", AnyData, Rank::coarray}, {"sub", AnyInt, Rank::vector},
{"team_number", AnyInt, Rank::scalar}},
DefaultInt, Rank::scalar, IntrinsicClass::transformationalFunction},
{"image_status", {{"image", SameInt}, OptionalTEAM}, DefaultInt},
{"index",
{{"string", SameCharNoLen}, {"substring", SameCharNoLen},
{"back", AnyLogical, Rank::elemental, Optionality::optional},
DefaultingKIND},
KINDInt},
{"int", {{"a", AnyNumeric, Rank::elementalOrBOZ}, DefaultingKIND}, KINDInt},
{"int_ptr_kind", {}, DefaultInt, Rank::scalar},
{"ior", {{"i", OperandInt}, {"j", OperandInt, Rank::elementalOrBOZ}},
OperandInt},
{"ior", {{"i", BOZ}, {"j", SameInt}}, SameInt},
{"ishft", {{"i", SameInt}, {"shift", AnyInt}}, SameInt},
{"ishftc",
{{"i", SameInt}, {"shift", AnyInt},
{"size", AnyInt, Rank::elemental, Optionality::optional}},
SameInt},
{"isnan", {{"a", AnyFloating}}, DefaultLogical},
{"is_contiguous", {{"array", Addressable, Rank::anyOrAssumedRank}},
DefaultLogical, Rank::elemental, IntrinsicClass::inquiryFunction},
{"is_iostat_end", {{"i", AnyInt}}, DefaultLogical},
{"is_iostat_eor", {{"i", AnyInt}}, DefaultLogical},
{"izext", {{"i", AnyInt}}, TypePattern{IntType, KindCode::exactKind, 2}},
{"jzext", {{"i", AnyInt}}, DefaultInt},
{"kind",
{{"x", AnyIntrinsic, Rank::anyOrAssumedRank, Optionality::required,
common::Intent::In, {ArgFlag::canBeMoldNull}}},
DefaultInt, Rank::elemental, IntrinsicClass::inquiryFunction},
{"lbound",
{{"array", AnyData, Rank::anyOrAssumedRank}, RequiredDIM,
SizeDefaultKIND},
KINDInt, Rank::scalar, IntrinsicClass::inquiryFunction},
{"lbound", {{"array", AnyData, Rank::arrayOrAssumedRank}, SizeDefaultKIND},
KINDInt, Rank::vector, IntrinsicClass::inquiryFunction},
{"lcobound",
{{"coarray", AnyData, Rank::coarray}, OptionalDIM, SizeDefaultKIND},
KINDInt, Rank::scalarIfDim, IntrinsicClass::inquiryFunction},
{"leadz", {{"i", AnyInt}}, DefaultInt},
{"len",
{{"string", AnyChar, Rank::anyOrAssumedRank, Optionality::required,
common::Intent::In, {ArgFlag::canBeMoldNull}},
DefaultingKIND},
KINDInt, Rank::scalar, IntrinsicClass::inquiryFunction},
{"len_trim", {{"string", AnyChar}, DefaultingKIND}, KINDInt},
{"lge", {{"string_a", SameCharNoLen}, {"string_b", SameCharNoLen}},
DefaultLogical},
{"lgt", {{"string_a", SameCharNoLen}, {"string_b", SameCharNoLen}},
DefaultLogical},
{"lle", {{"string_a", SameCharNoLen}, {"string_b", SameCharNoLen}},
DefaultLogical},
{"llt", {{"string_a", SameCharNoLen}, {"string_b", SameCharNoLen}},
DefaultLogical},
{"loc", {{"x", Addressable, Rank::anyOrAssumedRank}}, SubscriptInt,
Rank::scalar},
{"log", {{"x", SameFloating}}, SameFloating},
{"log10", {{"x", SameReal}}, SameReal},
{"logical", {{"l", AnyLogical}, DefaultingKIND}, KINDLogical},
{"log_gamma", {{"x", SameReal}}, SameReal},
{"matmul",
{{"matrix_a", AnyLogical, Rank::vector},
{"matrix_b", AnyLogical, Rank::matrix}},
ResultLogical, Rank::vector, IntrinsicClass::transformationalFunction},
{"matmul",
{{"matrix_a", AnyLogical, Rank::matrix},
{"matrix_b", AnyLogical, Rank::vector}},
ResultLogical, Rank::vector, IntrinsicClass::transformationalFunction},
{"matmul",
{{"matrix_a", AnyLogical, Rank::matrix},
{"matrix_b", AnyLogical, Rank::matrix}},
ResultLogical, Rank::matrix, IntrinsicClass::transformationalFunction},
{"matmul",
{{"matrix_a", AnyNumeric, Rank::vector},
{"matrix_b", AnyNumeric, Rank::matrix}},
ResultNumeric, Rank::vector, IntrinsicClass::transformationalFunction},
{"matmul",
{{"matrix_a", AnyNumeric, Rank::matrix},
{"matrix_b", AnyNumeric, Rank::vector}},
ResultNumeric, Rank::vector, IntrinsicClass::transformationalFunction},
{"matmul",
{{"matrix_a", AnyNumeric, Rank::matrix},
{"matrix_b", AnyNumeric, Rank::matrix}},
ResultNumeric, Rank::matrix, IntrinsicClass::transformationalFunction},
{"maskl", {{"i", AnyInt}, DefaultingKIND}, KINDInt},
{"maskr", {{"i", AnyInt}, DefaultingKIND}, KINDInt},
{"max",
{{"a1", OperandIntOrReal}, {"a2", OperandIntOrReal},
{"a3", OperandIntOrReal, Rank::elemental, Optionality::repeats}},
OperandIntOrReal},
{"max",
{{"a1", SameCharNoLen}, {"a2", SameCharNoLen},
{"a3", SameCharNoLen, Rank::elemental, Optionality::repeats}},
SameCharNoLen},
{"maxexponent",
{{"x", AnyReal, Rank::anyOrAssumedRank, Optionality::required,
common::Intent::In, {ArgFlag::canBeMoldNull}}},
DefaultInt, Rank::scalar, IntrinsicClass::inquiryFunction},
{"maxloc",
{{"array", AnyRelatable, Rank::array}, RequiredDIM, OptionalMASK,
SizeDefaultKIND,
{"back", AnyLogical, Rank::scalar, Optionality::optional}},
KINDInt, Rank::locReduced, IntrinsicClass::transformationalFunction},
{"maxloc",
{{"array", AnyRelatable, Rank::array}, MissingDIM, OptionalMASK,
SizeDefaultKIND,
{"back", AnyLogical, Rank::scalar, Optionality::optional}},
KINDInt, Rank::locReduced, IntrinsicClass::transformationalFunction},
{"maxval",
{{"array", SameRelatable, Rank::array}, RequiredDIM, OptionalMASK},
SameRelatable, Rank::dimReduced,
IntrinsicClass::transformationalFunction},
{"maxval",
{{"array", SameRelatable, Rank::array}, MissingDIM, OptionalMASK},
SameRelatable, Rank::scalar, IntrinsicClass::transformationalFunction},
{"merge",
{{"tsource", SameType}, {"fsource", SameType}, {"mask", AnyLogical}},
SameType},
{"merge_bits",
{{"i", SameInt}, {"j", SameInt, Rank::elementalOrBOZ},
{"mask", SameInt, Rank::elementalOrBOZ}},
SameInt},
{"merge_bits",
{{"i", BOZ}, {"j", SameInt}, {"mask", SameInt, Rank::elementalOrBOZ}},
SameInt},
{"min",
{{"a1", OperandIntOrReal}, {"a2", OperandIntOrReal},
{"a3", OperandIntOrReal, Rank::elemental, Optionality::repeats}},
OperandIntOrReal},
{"min",
{{"a1", SameCharNoLen}, {"a2", SameCharNoLen},
{"a3", SameCharNoLen, Rank::elemental, Optionality::repeats}},
SameCharNoLen},
{"minexponent",
{{"x", AnyReal, Rank::anyOrAssumedRank, Optionality::required,
common::Intent::In, {ArgFlag::canBeMoldNull}}},
DefaultInt, Rank::scalar, IntrinsicClass::inquiryFunction},
{"minloc",
{{"array", AnyRelatable, Rank::array}, RequiredDIM, OptionalMASK,
SizeDefaultKIND,
{"back", AnyLogical, Rank::scalar, Optionality::optional}},
KINDInt, Rank::locReduced, IntrinsicClass::transformationalFunction},
{"minloc",
{{"array", AnyRelatable, Rank::array}, MissingDIM, OptionalMASK,
SizeDefaultKIND,
{"back", AnyLogical, Rank::scalar, Optionality::optional}},
KINDInt, Rank::locReduced, IntrinsicClass::transformationalFunction},
{"minval",
{{"array", SameRelatable, Rank::array}, RequiredDIM, OptionalMASK},
SameRelatable, Rank::dimReduced,
IntrinsicClass::transformationalFunction},
{"minval",
{{"array", SameRelatable, Rank::array}, MissingDIM, OptionalMASK},
SameRelatable, Rank::scalar, IntrinsicClass::transformationalFunction},
{"mod", {{"a", OperandIntOrReal}, {"p", OperandIntOrReal}},
OperandIntOrReal},
{"modulo", {{"a", OperandIntOrReal}, {"p", OperandIntOrReal}},
OperandIntOrReal},
{"nearest", {{"x", SameReal}, {"s", AnyReal}}, SameReal},
{"new_line",
{{"a", SameCharNoLen, Rank::anyOrAssumedRank, Optionality::required,
common::Intent::In, {ArgFlag::canBeMoldNull}}},
SameCharNoLen, Rank::scalar, IntrinsicClass::inquiryFunction},
{"nint", {{"a", AnyReal}, DefaultingKIND}, KINDInt},
{"norm2", {{"x", SameReal, Rank::array}, RequiredDIM}, SameReal,
Rank::dimReduced, IntrinsicClass::transformationalFunction},
{"norm2", {{"x", SameReal, Rank::array}, MissingDIM}, SameReal,
Rank::scalar, IntrinsicClass::transformationalFunction},
{"not", {{"i", SameInt}}, SameInt},
// NULL() is a special case handled in Probe() below
{"num_images", {}, DefaultInt, Rank::scalar,
IntrinsicClass::transformationalFunction},
{"num_images", {{"team", TeamType, Rank::scalar}}, DefaultInt, Rank::scalar,
IntrinsicClass::transformationalFunction},
{"num_images", {{"team_number", AnyInt, Rank::scalar}}, DefaultInt,
Rank::scalar, IntrinsicClass::transformationalFunction},
{"out_of_range",
{{"x", AnyIntOrReal}, {"mold", AnyIntOrReal, Rank::scalar}},
DefaultLogical},
{"out_of_range",
{{"x", AnyReal}, {"mold", AnyInt, Rank::scalar},
{"round", AnyLogical, Rank::scalar, Optionality::optional}},
DefaultLogical},
{"out_of_range", {{"x", AnyReal}, {"mold", AnyReal}}, DefaultLogical},
{"pack",
{{"array", SameType, Rank::array},
{"mask", AnyLogical, Rank::conformable},
{"vector", SameType, Rank::vector, Optionality::optional}},
SameType, Rank::vector, IntrinsicClass::transformationalFunction},
{"parity", {{"mask", SameLogical, Rank::array}, OptionalDIM}, SameLogical,
Rank::dimReduced, IntrinsicClass::transformationalFunction},
{"popcnt", {{"i", AnyInt}}, DefaultInt},
{"poppar", {{"i", AnyInt}}, DefaultInt},
{"product",
{{"array", SameNumeric, Rank::array}, RequiredDIM, OptionalMASK},
SameNumeric, Rank::dimReduced,
IntrinsicClass::transformationalFunction},
{"product", {{"array", SameNumeric, Rank::array}, MissingDIM, OptionalMASK},
SameNumeric, Rank::scalar, IntrinsicClass::transformationalFunction},
{"precision",
{{"x", AnyFloating, Rank::anyOrAssumedRank, Optionality::required,
common::Intent::In, {ArgFlag::canBeMoldNull}}},
DefaultInt, Rank::scalar, IntrinsicClass::inquiryFunction},
{"present", {{"a", Addressable, Rank::anyOrAssumedRank}}, DefaultLogical,
Rank::scalar, IntrinsicClass::inquiryFunction},
{"radix",
{{"x", AnyIntOrReal, Rank::anyOrAssumedRank, Optionality::required,
common::Intent::In, {ArgFlag::canBeMoldNull}}},
DefaultInt, Rank::scalar, IntrinsicClass::inquiryFunction},
{"range",
{{"x", AnyNumeric, Rank::anyOrAssumedRank, Optionality::required,
common::Intent::In, {ArgFlag::canBeMoldNull}}},
DefaultInt, Rank::scalar, IntrinsicClass::inquiryFunction},
{"rank",
{{"a", AnyData, Rank::anyOrAssumedRank, Optionality::required,
common::Intent::In, {ArgFlag::canBeMoldNull}}},
DefaultInt, Rank::scalar, IntrinsicClass::inquiryFunction},
{"real", {{"a", SameComplex, Rank::elemental}},
SameReal}, // 16.9.160(4)(ii)
{"real", {{"a", AnyNumeric, Rank::elementalOrBOZ}, DefaultingKIND},
KINDReal},
{"reduce",
{{"array", SameType, Rank::array},
{"operation", SameType, Rank::reduceOperation}, RequiredDIM,
OptionalMASK,
{"identity", SameType, Rank::scalar, Optionality::optional},
{"ordered", AnyLogical, Rank::scalar, Optionality::optional}},
SameType, Rank::dimReduced, IntrinsicClass::transformationalFunction},
{"reduce",
{{"array", SameType, Rank::array},
{"operation", SameType, Rank::reduceOperation}, MissingDIM,
OptionalMASK,
{"identity", SameType, Rank::scalar, Optionality::optional},
{"ordered", AnyLogical, Rank::scalar, Optionality::optional}},
SameType, Rank::scalar, IntrinsicClass::transformationalFunction},
{"rename",
{{"path1", DefaultChar, Rank::scalar},
{"path2", DefaultChar, Rank::scalar}},
DefaultInt, Rank::scalar},
{"repeat",
{{"string", SameCharNoLen, Rank::scalar},
{"ncopies", AnyInt, Rank::scalar}},
SameCharNoLen, Rank::scalar, IntrinsicClass::transformationalFunction},
{"reshape",
{{"source", SameType, Rank::array}, {"shape", AnyInt, Rank::shape},
{"pad", SameType, Rank::array, Optionality::optional},
{"order", AnyInt, Rank::vector, Optionality::optional}},
SameType, Rank::shaped, IntrinsicClass::transformationalFunction},
{"rrspacing", {{"x", SameReal}}, SameReal},
{"same_type_as",
{{"a", ExtensibleDerived, Rank::anyOrAssumedRank, Optionality::required,
common::Intent::In, {ArgFlag::canBeMoldNull}},
{"b", ExtensibleDerived, Rank::anyOrAssumedRank,
Optionality::required, common::Intent::In,
{ArgFlag::canBeMoldNull}}},
DefaultLogical, Rank::scalar, IntrinsicClass::inquiryFunction},
{"scale", {{"x", SameReal}, {"i", AnyInt}}, SameReal}, // == IEEE_SCALB()
{"scan",
{{"string", SameCharNoLen}, {"set", SameCharNoLen},
{"back", AnyLogical, Rank::elemental, Optionality::optional},
DefaultingKIND},
KINDInt},
{"second", {}, DefaultReal, Rank::scalar},
{"selected_char_kind", {{"name", DefaultChar, Rank::scalar}}, DefaultInt,
Rank::scalar, IntrinsicClass::transformationalFunction},
{"selected_int_kind", {{"r", AnyInt, Rank::scalar}}, DefaultInt,
Rank::scalar, IntrinsicClass::transformationalFunction},
{"selected_logical_kind", {{"bits", AnyInt, Rank::scalar}}, DefaultInt,
Rank::scalar, IntrinsicClass::transformationalFunction},
{"selected_real_kind",
{{"p", AnyInt, Rank::scalar},
{"r", AnyInt, Rank::scalar, Optionality::optional},
{"radix", AnyInt, Rank::scalar, Optionality::optional}},
DefaultInt, Rank::scalar, IntrinsicClass::transformationalFunction},
{"selected_real_kind",
{{"p", AnyInt, Rank::scalar, Optionality::optional},
{"r", AnyInt, Rank::scalar},
{"radix", AnyInt, Rank::scalar, Optionality::optional}},
DefaultInt, Rank::scalar, IntrinsicClass::transformationalFunction},
{"selected_real_kind",
{{"p", AnyInt, Rank::scalar, Optionality::optional},
{"r", AnyInt, Rank::scalar, Optionality::optional},
{"radix", AnyInt, Rank::scalar}},
DefaultInt, Rank::scalar, IntrinsicClass::transformationalFunction},
{"set_exponent", {{"x", SameReal}, {"i", AnyInt}}, SameReal},
{"shape", {{"source", AnyData, Rank::anyOrAssumedRank}, SizeDefaultKIND},
KINDInt, Rank::vector, IntrinsicClass::inquiryFunction},
{"shifta", {{"i", SameInt}, {"shift", AnyInt}}, SameInt},
{"shiftl", {{"i", SameInt}, {"shift", AnyInt}}, SameInt},
{"shiftr", {{"i", SameInt}, {"shift", AnyInt}}, SameInt},
{"sign", {{"a", SameInt}, {"b", AnyInt}}, SameInt},
{"sign", {{"a", SameReal}, {"b", AnyReal}}, SameReal},
{"sin", {{"x", SameFloating}}, SameFloating},
{"sind", {{"x", SameFloating}}, SameFloating},
{"sinh", {{"x", SameFloating}}, SameFloating},
{"size",
{{"array", AnyData, Rank::arrayOrAssumedRank},
OptionalDIM, // unless array is assumed-size
SizeDefaultKIND},
KINDInt, Rank::scalar, IntrinsicClass::inquiryFunction},
{"sizeof", {{"a", AnyData, Rank::anyOrAssumedRank}}, SubscriptInt,
Rank::scalar, IntrinsicClass::inquiryFunction},
{"spacing", {{"x", SameReal}}, SameReal},
{"spread",
{{"source", SameType, Rank::known, Optionality::required,
common::Intent::In, {ArgFlag::notAssumedSize}},
RequiredDIM, {"ncopies", AnyInt, Rank::scalar}},
SameType, Rank::rankPlus1, IntrinsicClass::transformationalFunction},
{"sqrt", {{"x", SameFloating}}, SameFloating},
{"stopped_images", {OptionalTEAM, SizeDefaultKIND}, KINDInt, Rank::vector,
IntrinsicClass::transformationalFunction},
{"storage_size",
{{"a", AnyData, Rank::anyOrAssumedRank, Optionality::required,
common::Intent::In, {ArgFlag::canBeMoldNull}},
SizeDefaultKIND},
KINDInt, Rank::scalar, IntrinsicClass::inquiryFunction},
{"sum", {{"array", SameNumeric, Rank::array}, RequiredDIM, OptionalMASK},
SameNumeric, Rank::dimReduced,
IntrinsicClass::transformationalFunction},
{"sum", {{"array", SameNumeric, Rank::array}, MissingDIM, OptionalMASK},
SameNumeric, Rank::scalar, IntrinsicClass::transformationalFunction},
{"tan", {{"x", SameFloating}}, SameFloating},
{"tand", {{"x", SameFloating}}, SameFloating},
{"tanh", {{"x", SameFloating}}, SameFloating},
{"team_number", {OptionalTEAM}, DefaultInt, Rank::scalar,
IntrinsicClass::transformationalFunction},
{"this_image",
{{"coarray", AnyData, Rank::coarray}, RequiredDIM, OptionalTEAM},
DefaultInt, Rank::scalar, IntrinsicClass::transformationalFunction},
{"this_image", {{"coarray", AnyData, Rank::coarray}, OptionalTEAM},
DefaultInt, Rank::scalar, IntrinsicClass::transformationalFunction},
{"this_image", {OptionalTEAM}, DefaultInt, Rank::scalar,
IntrinsicClass::transformationalFunction},
{"tiny",
{{"x", SameReal, Rank::anyOrAssumedRank, Optionality::required,
common::Intent::In, {ArgFlag::canBeMoldNull}}},
SameReal, Rank::scalar, IntrinsicClass::inquiryFunction},
{"trailz", {{"i", AnyInt}}, DefaultInt},
{"transfer",
{{"source", AnyData, Rank::known}, {"mold", SameType, Rank::scalar}},
SameType, Rank::scalar, IntrinsicClass::transformationalFunction},
{"transfer",
{{"source", AnyData, Rank::known}, {"mold", SameType, Rank::array}},
SameType, Rank::vector, IntrinsicClass::transformationalFunction},
{"transfer",
{{"source", AnyData, Rank::anyOrAssumedRank},
{"mold", SameType, Rank::anyOrAssumedRank},
{"size", AnyInt, Rank::scalar}},
SameType, Rank::vector, IntrinsicClass::transformationalFunction},
{"transpose", {{"matrix", SameType, Rank::matrix}}, SameType, Rank::matrix,
IntrinsicClass::transformationalFunction},
{"trim", {{"string", SameCharNoLen, Rank::scalar}}, SameCharNoLen,
Rank::scalar, IntrinsicClass::transformationalFunction},
{"ubound",
{{"array", AnyData, Rank::anyOrAssumedRank}, RequiredDIM,
SizeDefaultKIND},
KINDInt, Rank::scalar, IntrinsicClass::inquiryFunction},
{"ubound", {{"array", AnyData, Rank::arrayOrAssumedRank}, SizeDefaultKIND},
KINDInt, Rank::vector, IntrinsicClass::inquiryFunction},
{"ucobound",
{{"coarray", AnyData, Rank::coarray}, OptionalDIM, SizeDefaultKIND},
KINDInt, Rank::scalarIfDim, IntrinsicClass::inquiryFunction},
{"unpack",
{{"vector", SameType, Rank::vector}, {"mask", AnyLogical, Rank::array},
{"field", SameType, Rank::conformable}},
SameType, Rank::conformable, IntrinsicClass::transformationalFunction},
{"verify",
{{"string", SameCharNoLen}, {"set", SameCharNoLen},
{"back", AnyLogical, Rank::elemental, Optionality::optional},
DefaultingKIND},
KINDInt},
{"__builtin_compiler_options", {}, DefaultChar},
{"__builtin_compiler_version", {}, DefaultChar},
{"__builtin_fma", {{"f1", SameReal}, {"f2", SameReal}, {"f3", SameReal}},
SameReal},
{"__builtin_ieee_is_nan", {{"a", AnyFloating}}, DefaultLogical},
{"__builtin_ieee_is_negative", {{"a", AnyFloating}}, DefaultLogical},
{"__builtin_ieee_is_normal", {{"a", AnyFloating}}, DefaultLogical},
{"__builtin_ieee_next_after", {{"x", SameReal}, {"y", AnyReal}}, SameReal},
{"__builtin_ieee_next_down", {{"x", SameReal}}, SameReal},
{"__builtin_ieee_next_up", {{"x", SameReal}}, SameReal},
{"__builtin_ieee_support_datatype",
{{"x", AnyReal, Rank::elemental, Optionality::optional}},
DefaultLogical},
{"__builtin_ieee_support_denormal",
{{"x", AnyReal, Rank::elemental, Optionality::optional}},
DefaultLogical},
{"__builtin_ieee_support_divide",
{{"x", AnyReal, Rank::elemental, Optionality::optional}},
DefaultLogical},
{"__builtin_ieee_support_flag",
{{"flag", IeeeFlagType, Rank::scalar},
{"x", AnyReal, Rank::elemental, Optionality::optional}},
DefaultLogical},
{"__builtin_ieee_support_halting", {{"flag", IeeeFlagType, Rank::scalar}},
DefaultLogical},
{"__builtin_ieee_support_inf",
{{"x", AnyReal, Rank::elemental, Optionality::optional}},
DefaultLogical},
{"__builtin_ieee_support_io",
{{"x", AnyReal, Rank::elemental, Optionality::optional}},
DefaultLogical},
{"__builtin_ieee_support_nan",
{{"x", AnyReal, Rank::elemental, Optionality::optional}},
DefaultLogical},
{"__builtin_ieee_support_rounding",
{{"round_value", IeeeRoundType, Rank::scalar},
{"x", AnyReal, Rank::elemental, Optionality::optional}},
DefaultLogical},
{"__builtin_ieee_support_sqrt",
{{"x", AnyReal, Rank::elemental, Optionality::optional}},
DefaultLogical},
{"__builtin_ieee_support_standard",
{{"x", AnyReal, Rank::elemental, Optionality::optional}},
DefaultLogical},
{"__builtin_ieee_support_subnormal",
{{"x", AnyReal, Rank::elemental, Optionality::optional}},
DefaultLogical},
{"__builtin_ieee_support_underflow_control",
{{"x", AnyReal, Rank::elemental, Optionality::optional}},
DefaultLogical},
{"__builtin_numeric_storage_size", {}, DefaultInt},
};
// TODO: Coarray intrinsic functions
// COSHAPE
// TODO: Non-standard intrinsic functions
// SHIFT,
// COMPL, EQV, NEQV, INT8, JINT, JNINT, KNINT,
// QCMPLX, QEXT, QFLOAT, QREAL, DNUM,
// INUM, JNUM, KNUM, QNUM, RNUM, RAN, RANF, ILEN,
// MCLOCK, SECNDS, COTAN, IBCHNG, ISHA, ISHC, ISHL, IXOR
// IARG, IARGC, NARGS, NUMARG, BADDRESS, IADDR, CACHESIZE,
// EOF, FP_CLASS, INT_PTR_KIND, MALLOC
// probably more (these are PGI + Intel, possibly incomplete)
// TODO: Optionally warn on use of non-standard intrinsics:
// LOC, probably others
// TODO: Optionally warn on operand promotion extension
// Aliases for a few generic intrinsic functions for legacy
// compatibility and builtins.
static const std::pair<const char *, const char *> genericAlias[]{
{"and", "iand"},
{"getenv", "get_environment_variable"},
{"imag", "aimag"},
{"lshift", "shiftl"},
{"or", "ior"},
{"rshift", "shifta"},
{"xor", "ieor"},
{"__builtin_ieee_selected_real_kind", "selected_real_kind"},
};
// The following table contains the intrinsic functions listed in
// Tables 16.2 and 16.3 in Fortran 2018. The "unrestricted" functions
// in Table 16.2 can be used as actual arguments, PROCEDURE() interfaces,
// and procedure pointer targets.
// Note that the restricted conversion functions dcmplx, dreal, float, idint,
// ifix, and sngl are extended to accept any argument kind because this is a
// common Fortran compilers behavior, and as far as we can tell, is safe and
// useful.
struct SpecificIntrinsicInterface : public IntrinsicInterface {
const char *generic{nullptr};
bool isRestrictedSpecific{false};
// Exact actual/dummy type matching is required by default for specific
// intrinsics. If useGenericAndForceResultType is set, then the probing will
// also attempt to use the related generic intrinsic and to convert the result
// to the specific intrinsic result type if needed. This also prevents
// using the generic name so that folding can insert the conversion on the
// result and not the arguments.
//
// This is not enabled on all specific intrinsics because an alternative
// is to convert the actual arguments to the required dummy types and this is
// not numerically equivalent.
// e.g. IABS(INT(i, 4)) not equiv to INT(ABS(i), 4).
// This is allowed for restricted min/max specific functions because
// the expected behavior is clear from their definitions. A warning is though
// always emitted because other compilers' behavior is not ubiquitous here and
// the results in case of conversion overflow might not be equivalent.
// e.g for MIN0: INT(MIN(2147483647_8, 2*2147483647_8), 4) = 2147483647_4
// but: MIN(INT(2147483647_8, 4), INT(2*2147483647_8, 4)) = -2_4
// xlf and ifort return the first, and pgfortran the later. f18 will return
// the first because this matches more closely the MIN0 definition in
// Fortran 2018 table 16.3 (although it is still an extension to allow
// non default integer argument in MIN0).
bool useGenericAndForceResultType{false};
};
static const SpecificIntrinsicInterface specificIntrinsicFunction[]{
{{"abs", {{"a", DefaultReal}}, DefaultReal}},
{{"acos", {{"x", DefaultReal}}, DefaultReal}},
{{"aimag", {{"z", DefaultComplex}}, DefaultReal}},
{{"aint", {{"a", DefaultReal}}, DefaultReal}},
{{"alog", {{"x", DefaultReal}}, DefaultReal}, "log"},
{{"alog10", {{"x", DefaultReal}}, DefaultReal}, "log10"},
{{"amax0",
{{"a1", DefaultInt}, {"a2", DefaultInt},
{"a3", DefaultInt, Rank::elemental, Optionality::repeats}},
DefaultReal},
"max", true, true},
{{"amax1",
{{"a1", DefaultReal}, {"a2", DefaultReal},
{"a3", DefaultReal, Rank::elemental, Optionality::repeats}},
DefaultReal},
"max", true, true},
{{"amin0",
{{"a1", DefaultInt}, {"a2", DefaultInt},
{"a3", DefaultInt, Rank::elemental, Optionality::repeats}},
DefaultReal},
"min", true, true},
{{"amin1",
{{"a1", DefaultReal}, {"a2", DefaultReal},
{"a3", DefaultReal, Rank::elemental, Optionality::repeats}},
DefaultReal},
"min", true, true},
{{"amod", {{"a", DefaultReal}, {"p", DefaultReal}}, DefaultReal}, "mod"},
{{"anint", {{"a", DefaultReal}}, DefaultReal}},
{{"asin", {{"x", DefaultReal}}, DefaultReal}},
{{"atan", {{"x", DefaultReal}}, DefaultReal}},
{{"atan2", {{"y", DefaultReal}, {"x", DefaultReal}}, DefaultReal}},
{{"babs", {{"a", TypePattern{IntType, KindCode::exactKind, 1}}},
TypePattern{IntType, KindCode::exactKind, 1}},
"abs"},
{{"cabs", {{"a", DefaultComplex}}, DefaultReal}, "abs"},
{{"ccos", {{"x", DefaultComplex}}, DefaultComplex}, "cos"},
{{"cdabs", {{"a", DoublePrecisionComplex}}, DoublePrecision}, "abs"},
{{"cdcos", {{"x", DoublePrecisionComplex}}, DoublePrecisionComplex}, "cos"},
{{"cdexp", {{"x", DoublePrecisionComplex}}, DoublePrecisionComplex}, "exp"},
{{"cdlog", {{"x", DoublePrecisionComplex}}, DoublePrecisionComplex}, "log"},
{{"cdsin", {{"x", DoublePrecisionComplex}}, DoublePrecisionComplex}, "sin"},
{{"cdsqrt", {{"x", DoublePrecisionComplex}}, DoublePrecisionComplex},
"sqrt"},
{{"cexp", {{"x", DefaultComplex}}, DefaultComplex}, "exp"},
{{"clog", {{"x", DefaultComplex}}, DefaultComplex}, "log"},
{{"conjg", {{"z", DefaultComplex}}, DefaultComplex}},
{{"cos", {{"x", DefaultReal}}, DefaultReal}},
{{"cosh", {{"x", DefaultReal}}, DefaultReal}},
{{"csin", {{"x", DefaultComplex}}, DefaultComplex}, "sin"},
{{"csqrt", {{"x", DefaultComplex}}, DefaultComplex}, "sqrt"},
{{"ctan", {{"x", DefaultComplex}}, DefaultComplex}, "tan"},
{{"dabs", {{"a", DoublePrecision}}, DoublePrecision}, "abs"},
{{"dacos", {{"x", DoublePrecision}}, DoublePrecision}, "acos"},
{{"dasin", {{"x", DoublePrecision}}, DoublePrecision}, "asin"},
{{"datan", {{"x", DoublePrecision}}, DoublePrecision}, "atan"},
{{"datan2", {{"y", DoublePrecision}, {"x", DoublePrecision}},
DoublePrecision},
"atan2"},
{{"dcmplx", {{"x", AnyComplex}}, DoublePrecisionComplex}, "cmplx", true},
{{"dcmplx",
{{"x", AnyIntOrReal, Rank::elementalOrBOZ},
{"y", AnyIntOrReal, Rank::elementalOrBOZ, Optionality::optional}},
DoublePrecisionComplex},
"cmplx", true},
{{"dconjg", {{"z", DoublePrecisionComplex}}, DoublePrecisionComplex},
"conjg"},
{{"dcos", {{"x", DoublePrecision}}, DoublePrecision}, "cos"},
{{"dcosh", {{"x", DoublePrecision}}, DoublePrecision}, "cosh"},
{{"ddim", {{"x", DoublePrecision}, {"y", DoublePrecision}},
DoublePrecision},
"dim"},
{{"derf", {{"x", DoublePrecision}}, DoublePrecision}, "erf"},
{{"dexp", {{"x", DoublePrecision}}, DoublePrecision}, "exp"},
{{"dfloat", {{"a", AnyInt}}, DoublePrecision}, "real", true},
{{"dim", {{"x", DefaultReal}, {"y", DefaultReal}}, DefaultReal}},
{{"dimag", {{"z", DoublePrecisionComplex}}, DoublePrecision}, "aimag"},
{{"dint", {{"a", DoublePrecision}}, DoublePrecision}, "aint"},
{{"dlog", {{"x", DoublePrecision}}, DoublePrecision}, "log"},
{{"dlog10", {{"x", DoublePrecision}}, DoublePrecision}, "log10"},
{{"dmax1",
{{"a1", DoublePrecision}, {"a2", DoublePrecision},
{"a3", DoublePrecision, Rank::elemental, Optionality::repeats}},
DoublePrecision},
"max", true, true},
{{"dmin1",
{{"a1", DoublePrecision}, {"a2", DoublePrecision},
{"a3", DoublePrecision, Rank::elemental, Optionality::repeats}},
DoublePrecision},
"min", true, true},
{{"dmod", {{"a", DoublePrecision}, {"p", DoublePrecision}},
DoublePrecision},
"mod"},
{{"dnint", {{"a", DoublePrecision}}, DoublePrecision}, "anint"},
{{"dprod", {{"x", DefaultReal}, {"y", DefaultReal}}, DoublePrecision}},
{{"dreal", {{"a", AnyComplex}}, DoublePrecision}, "real", true},
{{"dsign", {{"a", DoublePrecision}, {"b", DoublePrecision}},
DoublePrecision},
"sign"},
{{"dsin", {{"x", DoublePrecision}}, DoublePrecision}, "sin"},
{{"dsinh", {{"x", DoublePrecision}}, DoublePrecision}, "sinh"},
{{"dsqrt", {{"x", DoublePrecision}}, DoublePrecision}, "sqrt"},
{{"dtan", {{"x", DoublePrecision}}, DoublePrecision}, "tan"},
{{"dtanh", {{"x", DoublePrecision}}, DoublePrecision}, "tanh"},
{{"exp", {{"x", DefaultReal}}, DefaultReal}},
{{"float", {{"a", AnyInt}}, DefaultReal}, "real", true},
{{"iabs", {{"a", DefaultInt}}, DefaultInt}, "abs"},
{{"idim", {{"x", DefaultInt}, {"y", DefaultInt}}, DefaultInt}, "dim"},
{{"idint", {{"a", AnyReal}}, DefaultInt}, "int", true},
{{"idnint", {{"a", DoublePrecision}}, DefaultInt}, "nint"},
{{"ifix", {{"a", AnyReal}}, DefaultInt}, "int", true},
{{"iiabs", {{"a", TypePattern{IntType, KindCode::exactKind, 2}}},
TypePattern{IntType, KindCode::exactKind, 2}},
"abs"},
// The definition of the unrestricted specific intrinsic function INDEX
// in F'77 and F'90 has only two arguments; later standards omit the
// argument information for all unrestricted specific intrinsic
// procedures. No compiler supports an implementation that allows
// INDEX with BACK= to work when associated as an actual procedure or
// procedure pointer target.
{{"index", {{"string", DefaultChar}, {"substring", DefaultChar}},
DefaultInt}},
{{"isign", {{"a", DefaultInt}, {"b", DefaultInt}}, DefaultInt}, "sign"},
{{"jiabs", {{"a", TypePattern{IntType, KindCode::exactKind, 4}}},
TypePattern{IntType, KindCode::exactKind, 4}},
"abs"},
{{"kiabs", {{"a", TypePattern{IntType, KindCode::exactKind, 8}}},
TypePattern{IntType, KindCode::exactKind, 8}},
"abs"},
{{"kidnnt", {{"a", DoublePrecision}},
TypePattern{IntType, KindCode::exactKind, 8}},
"nint"},
{{"knint", {{"a", DefaultReal}},
TypePattern{IntType, KindCode::exactKind, 8}},
"nint"},
{{"len", {{"string", DefaultChar, Rank::anyOrAssumedRank}}, DefaultInt,
Rank::scalar, IntrinsicClass::inquiryFunction}},
{{"lge", {{"string_a", DefaultChar}, {"string_b", DefaultChar}},
DefaultLogical},
"lge", true},
{{"lgt", {{"string_a", DefaultChar}, {"string_b", DefaultChar}},
DefaultLogical},
"lgt", true},
{{"lle", {{"string_a", DefaultChar}, {"string_b", DefaultChar}},
DefaultLogical},
"lle", true},
{{"llt", {{"string_a", DefaultChar}, {"string_b", DefaultChar}},
DefaultLogical},
"llt", true},
{{"log", {{"x", DefaultReal}}, DefaultReal}},
{{"log10", {{"x", DefaultReal}}, DefaultReal}},
{{"max0",
{{"a1", DefaultInt}, {"a2", DefaultInt},
{"a3", DefaultInt, Rank::elemental, Optionality::repeats}},
DefaultInt},
"max", true, true},
{{"max1",
{{"a1", DefaultReal}, {"a2", DefaultReal},
{"a3", DefaultReal, Rank::elemental, Optionality::repeats}},
DefaultInt},
"max", true, true},
{{"min0",
{{"a1", DefaultInt}, {"a2", DefaultInt},
{"a3", DefaultInt, Rank::elemental, Optionality::repeats}},
DefaultInt},
"min", true, true},
{{"min1",
{{"a1", DefaultReal}, {"a2", DefaultReal},
{"a3", DefaultReal, Rank::elemental, Optionality::repeats}},
DefaultInt},
"min", true, true},
{{"mod", {{"a", DefaultInt}, {"p", DefaultInt}}, DefaultInt}},
{{"nint", {{"a", DefaultReal}}, DefaultInt}},
{{"sign", {{"a", DefaultReal}, {"b", DefaultReal}}, DefaultReal}},
{{"sin", {{"x", DefaultReal}}, DefaultReal}},
{{"sinh", {{"x", DefaultReal}}, DefaultReal}},
{{"sngl", {{"a", AnyReal}}, DefaultReal}, "real", true},
{{"sqrt", {{"x", DefaultReal}}, DefaultReal}},
{{"tan", {{"x", DefaultReal}}, DefaultReal}},
{{"tanh", {{"x", DefaultReal}}, DefaultReal}},
{{"zabs", {{"a", TypePattern{ComplexType, KindCode::exactKind, 8}}},
TypePattern{RealType, KindCode::exactKind, 8}},
"abs"},
};
static const IntrinsicInterface intrinsicSubroutine[]{
{"abort", {}, {}, Rank::elemental, IntrinsicClass::impureSubroutine},
{"atomic_and",
{{"atom", AtomicInt, Rank::atom, Optionality::required,
common::Intent::InOut},
{"value", AnyInt, Rank::scalar, Optionality::required,
common::Intent::In},
{"stat", AnyInt, Rank::scalar, Optionality::optional,
common::Intent::Out}},
{}, Rank::elemental, IntrinsicClass::atomicSubroutine},
{"atomic_cas",
{{"atom", SameAtom, Rank::atom, Optionality::required,
common::Intent::InOut},
{"old", SameAtom, Rank::scalar, Optionality::required,
common::Intent::Out},
{"compare", SameAtom, Rank::scalar, Optionality::required,
common::Intent::In},
{"new", SameAtom, Rank::scalar, Optionality::required,
common::Intent::In},
{"stat", AnyInt, Rank::scalar, Optionality::optional,
common::Intent::Out}},
{}, Rank::elemental, IntrinsicClass::atomicSubroutine},
{"atomic_define",
{{"atom", AtomicIntOrLogical, Rank::atom, Optionality::required,
common::Intent::Out},
{"value", AnyIntOrLogical, Rank::scalar, Optionality::required,
common::Intent::In},
{"stat", AnyInt, Rank::scalar, Optionality::optional,
common::Intent::Out}},
{}, Rank::elemental, IntrinsicClass::atomicSubroutine},
{"atomic_fetch_add",
{{"atom", AtomicInt, Rank::atom, Optionality::required,
common::Intent::InOut},
{"value", AnyInt, Rank::scalar, Optionality::required,
common::Intent::In},
{"old", AtomicInt, Rank::scalar, Optionality::required,
common::Intent::Out},
{"stat", AnyInt, Rank::scalar, Optionality::optional,
common::Intent::Out}},
{}, Rank::elemental, IntrinsicClass::atomicSubroutine},
{"atomic_fetch_and",
{{"atom", AtomicInt, Rank::atom, Optionality::required,
common::Intent::InOut},
{"value", AnyInt, Rank::scalar, Optionality::required,
common::Intent::In},
{"old", AtomicInt, Rank::scalar, Optionality::required,
common::Intent::Out},
{"stat", AnyInt, Rank::scalar, Optionality::optional,
common::Intent::Out}},
{}, Rank::elemental, IntrinsicClass::atomicSubroutine},
{"atomic_fetch_or",
{{"atom", AtomicInt, Rank::atom, Optionality::required,
common::Intent::InOut},
{"value", AnyInt, Rank::scalar, Optionality::required,
common::Intent::In},
{"old", AtomicInt, Rank::scalar, Optionality::required,
common::Intent::Out},
{"stat", AnyInt, Rank::scalar, Optionality::optional,
common::Intent::Out}},
{}, Rank::elemental, IntrinsicClass::atomicSubroutine},
{"atomic_fetch_xor",
{{"atom", AtomicInt, Rank::atom, Optionality::required,
common::Intent::InOut},
{"value", AnyInt, Rank::scalar, Optionality::required,
common::Intent::In},
{"old", AtomicInt, Rank::scalar, Optionality::required,
common::Intent::Out},
{"stat", AnyInt, Rank::scalar, Optionality::optional,
common::Intent::Out}},
{}, Rank::elemental, IntrinsicClass::atomicSubroutine},
{"atomic_or",
{{"atom", AtomicInt, Rank::atom, Optionality::required,
common::Intent::InOut},
{"value", AnyInt, Rank::scalar, Optionality::required,
common::Intent::In},
{"stat", AnyInt, Rank::scalar, Optionality::optional,
common::Intent::Out}},
{}, Rank::elemental, IntrinsicClass::atomicSubroutine},
{"atomic_ref",
{{"value", AnyIntOrLogical, Rank::scalar, Optionality::required,
common::Intent::Out},
{"atom", AtomicIntOrLogical, Rank::atom, Optionality::required,
common::Intent::In},
{"stat", AnyInt, Rank::scalar, Optionality::optional,
common::Intent::Out}},
{}, Rank::elemental, IntrinsicClass::atomicSubroutine},
{"atomic_xor",
{{"atom", AtomicInt, Rank::atom, Optionality::required,
common::Intent::InOut},
{"value", AnyInt, Rank::scalar, Optionality::required,
common::Intent::In},
{"stat", AnyInt, Rank::scalar, Optionality::optional,
common::Intent::Out}},
{}, Rank::elemental, IntrinsicClass::atomicSubroutine},
{"co_broadcast",
{{"a", AnyData, Rank::anyOrAssumedRank, Optionality::required,
common::Intent::InOut},
{"source_image", AnyInt, Rank::scalar, Optionality::required,
common::Intent::In},
{"stat", AnyInt, Rank::scalar, Optionality::optional,
common::Intent::Out},
{"errmsg", DefaultChar, Rank::scalar, Optionality::optional,
common::Intent::InOut}},
{}, Rank::elemental, IntrinsicClass::collectiveSubroutine},
{"co_max",
{{"a", AnyIntOrRealOrChar, Rank::anyOrAssumedRank,
Optionality::required, common::Intent::InOut},
{"result_image", AnyInt, Rank::scalar, Optionality::optional,
common::Intent::In},
{"stat", AnyInt, Rank::scalar, Optionality::optional,
common::Intent::Out},
{"errmsg", DefaultChar, Rank::scalar, Optionality::optional,
common::Intent::InOut}},
{}, Rank::elemental, IntrinsicClass::collectiveSubroutine},
{"co_min",
{{"a", AnyIntOrRealOrChar, Rank::anyOrAssumedRank,
Optionality::required, common::Intent::InOut},
{"result_image", AnyInt, Rank::scalar, Optionality::optional,
common::Intent::In},
{"stat", AnyInt, Rank::scalar, Optionality::optional,
common::Intent::Out},
{"errmsg", DefaultChar, Rank::scalar, Optionality::optional,
common::Intent::InOut}},
{}, Rank::elemental, IntrinsicClass::collectiveSubroutine},
{"co_sum",
{{"a", AnyNumeric, Rank::anyOrAssumedRank, Optionality::required,
common::Intent::InOut},
{"result_image", AnyInt, Rank::scalar, Optionality::optional,
common::Intent::In},
{"stat", AnyInt, Rank::scalar, Optionality::optional,
common::Intent::Out},
{"errmsg", DefaultChar, Rank::scalar, Optionality::optional,
common::Intent::InOut}},
{}, Rank::elemental, IntrinsicClass::collectiveSubroutine},
{"cpu_time",
{{"time", AnyReal, Rank::scalar, Optionality::required,
common::Intent::Out}},
{}, Rank::elemental, IntrinsicClass::impureSubroutine},
{"date_and_time",
{{"date", DefaultChar, Rank::scalar, Optionality::optional,
common::Intent::Out},
{"time", DefaultChar, Rank::scalar, Optionality::optional,
common::Intent::Out},
{"zone", DefaultChar, Rank::scalar, Optionality::optional,
common::Intent::Out},
{"values", AnyInt, Rank::vector, Optionality::optional,
common::Intent::Out}},
{}, Rank::elemental, IntrinsicClass::impureSubroutine},
{"etime",
{{"values", TypePattern{RealType, KindCode::exactKind, 4}, Rank::vector,
Optionality::required, common::Intent::Out},
{"time", TypePattern{RealType, KindCode::exactKind, 4},
Rank::scalar, Optionality::required, common::Intent::Out}},
{}, Rank::elemental, IntrinsicClass::impureSubroutine},
{"execute_command_line",
{{"command", DefaultChar, Rank::scalar},
{"wait", AnyLogical, Rank::scalar, Optionality::optional},
{"exitstat",
TypePattern{IntType, KindCode::greaterOrEqualToKind, 4},
Rank::scalar, Optionality::optional, common::Intent::InOut},
{"cmdstat", TypePattern{IntType, KindCode::greaterOrEqualToKind, 2},
Rank::scalar, Optionality::optional, common::Intent::Out},
{"cmdmsg", DefaultChar, Rank::scalar, Optionality::optional,
common::Intent::InOut}},
{}, Rank::elemental, IntrinsicClass::impureSubroutine},
{"exit", {{"status", DefaultInt, Rank::scalar, Optionality::optional}}, {},
Rank::elemental, IntrinsicClass::impureSubroutine},
{"get_command",
{{"command", DefaultChar, Rank::scalar, Optionality::optional,
common::Intent::Out},
{"length", AnyInt, Rank::scalar, Optionality::optional,
common::Intent::Out},
{"status", AnyInt, Rank::scalar, Optionality::optional,
common::Intent::Out},
{"errmsg", DefaultChar, Rank::scalar, Optionality::optional,
common::Intent::InOut}},
{}, Rank::elemental, IntrinsicClass::impureSubroutine},
{"get_command_argument",
{{"number", AnyInt, Rank::scalar},
{"value", DefaultChar, Rank::scalar, Optionality::optional,
common::Intent::Out},
{"length", AnyInt, Rank::scalar, Optionality::optional,
common::Intent::Out},
{"status", AnyInt, Rank::scalar, Optionality::optional,
common::Intent::Out},
{"errmsg", DefaultChar, Rank::scalar, Optionality::optional,
common::Intent::InOut}},
{}, Rank::elemental, IntrinsicClass::impureSubroutine},
{"get_environment_variable",
{{"name", DefaultChar, Rank::scalar},
{"value", DefaultChar, Rank::scalar, Optionality::optional,
common::Intent::Out},
{"length", AnyInt, Rank::scalar, Optionality::optional,
common::Intent::Out},
{"status", AnyInt, Rank::scalar, Optionality::optional,
common::Intent::Out},
{"trim_name", AnyLogical, Rank::scalar, Optionality::optional},
{"errmsg", DefaultChar, Rank::scalar, Optionality::optional,
common::Intent::InOut}},
{}, Rank::elemental, IntrinsicClass::impureSubroutine},
{"getcwd",
{{"c", DefaultChar, Rank::scalar, Optionality::required,
common::Intent::Out},
{"status", TypePattern{IntType, KindCode::greaterOrEqualToKind, 4},
Rank::scalar, Optionality::optional, common::Intent::Out}},
{}, Rank::elemental, IntrinsicClass::impureSubroutine},
{"move_alloc",
{{"from", SameType, Rank::known, Optionality::required,
common::Intent::InOut},
{"to", SameType, Rank::known, Optionality::required,
common::Intent::Out},
{"stat", AnyInt, Rank::scalar, Optionality::optional,
common::Intent::Out},
{"errmsg", DefaultChar, Rank::scalar, Optionality::optional,
common::Intent::InOut}},
{}, Rank::elemental, IntrinsicClass::pureSubroutine},
{"mvbits",
{{"from", SameInt}, {"frompos", AnyInt}, {"len", AnyInt},
{"to", SameInt, Rank::elemental, Optionality::required,
common::Intent::Out},
{"topos", AnyInt}},
{}, Rank::elemental, IntrinsicClass::elementalSubroutine}, // elemental
{"random_init",
{{"repeatable", AnyLogical, Rank::scalar},
{"image_distinct", AnyLogical, Rank::scalar}},
{}, Rank::elemental, IntrinsicClass::impureSubroutine},
{"random_number",
{{"harvest", AnyReal, Rank::known, Optionality::required,
common::Intent::Out, {ArgFlag::notAssumedSize}}},
{}, Rank::elemental, IntrinsicClass::impureSubroutine},
{"random_seed",
{{"size", DefaultInt, Rank::scalar, Optionality::optional,
common::Intent::Out},
{"put", DefaultInt, Rank::vector, Optionality::optional},
{"get", DefaultInt, Rank::vector, Optionality::optional,
common::Intent::Out}},
{}, Rank::elemental, IntrinsicClass::impureSubroutine},
{"rename",
{{"path1", DefaultChar, Rank::scalar},
{"path2", DefaultChar, Rank::scalar},
{"status", DefaultInt, Rank::scalar, Optionality::optional,
common::Intent::Out}},
{}, Rank::scalar, IntrinsicClass::impureSubroutine},
{"second", {{"time", DefaultReal, Rank::scalar}}, {}, Rank::scalar,
IntrinsicClass::impureSubroutine},
{"system",
{{"command", DefaultChar, Rank::scalar},
{"exitstat", DefaultInt, Rank::scalar, Optionality::optional,
common::Intent::Out}},
{}, Rank::elemental, IntrinsicClass::impureSubroutine},
{"system_clock",
{{"count", AnyInt, Rank::scalar, Optionality::optional,
common::Intent::Out},
{"count_rate", AnyIntOrReal, Rank::scalar, Optionality::optional,
common::Intent::Out},
{"count_max", AnyInt, Rank::scalar, Optionality::optional,
common::Intent::Out}},
{}, Rank::elemental, IntrinsicClass::impureSubroutine},
{"signal",
{{"number", AnyInt, Rank::scalar, Optionality::required,
common::Intent::In},
// note: any pointer also accepts AnyInt
{"handler", AnyPointer, Rank::scalar, Optionality::required,
common::Intent::In},
{"status", AnyInt, Rank::scalar, Optionality::optional,
common::Intent::Out}},
{}, Rank::elemental, IntrinsicClass::impureSubroutine},
{"sleep",
{{"seconds", AnyInt, Rank::scalar, Optionality::required,
common::Intent::In}},
{}, Rank::elemental, IntrinsicClass::impureSubroutine},
};
// TODO: Intrinsic subroutine EVENT_QUERY
// TODO: Atomic intrinsic subroutines: ATOMIC_ADD
// TODO: Collective intrinsic subroutines: co_reduce
// Finds a built-in derived type and returns it as a DynamicType.
static DynamicType GetBuiltinDerivedType(
const semantics::Scope *builtinsScope, const char *which) {
if (!builtinsScope) {
common::die("INTERNAL: The __fortran_builtins module was not found, and "
"the type '%s' was required",
which);
}
auto iter{
builtinsScope->find(semantics::SourceName{which, std::strlen(which)})};
if (iter == builtinsScope->cend()) {
// keep the string all together
// clang-format off
common::die(
"INTERNAL: The __fortran_builtins module does not define the type '%s'",
which);
// clang-format on
}
const semantics::Symbol &symbol{*iter->second};
const semantics::Scope &scope{DEREF(symbol.scope())};
const semantics::DerivedTypeSpec &derived{DEREF(scope.derivedTypeSpec())};
return DynamicType{derived};
}
static std::int64_t GetBuiltinKind(
const semantics::Scope *builtinsScope, const char *which) {
if (!builtinsScope) {
common::die("INTERNAL: The __fortran_builtins module was not found, and "
"the kind '%s' was required",
which);
}
auto iter{
builtinsScope->find(semantics::SourceName{which, std::strlen(which)})};
if (iter == builtinsScope->cend()) {
common::die(
"INTERNAL: The __fortran_builtins module does not define the kind '%s'",
which);
}
const semantics::Symbol &symbol{*iter->second};
const auto &details{
DEREF(symbol.detailsIf<semantics::ObjectEntityDetails>())};
if (const auto kind{ToInt64(details.init())}) {
return *kind;
} else {
common::die(
"INTERNAL: The __fortran_builtins module does not define the kind '%s'",
which);
return -1;
}
}
// Ensure that the keywords of arguments to MAX/MIN and their variants
// are of the form A123 with no duplicates or leading zeroes.
static bool CheckMaxMinArgument(parser::CharBlock keyword,
std::set<parser::CharBlock> &set, const char *intrinsicName,
parser::ContextualMessages &messages) {
std::size_t j{1};
for (; j < keyword.size(); ++j) {
char ch{(keyword)[j]};
if (ch < (j == 1 ? '1' : '0') || ch > '9') {
break;
}
}
if (keyword.size() < 2 || (keyword)[0] != 'a' || j < keyword.size()) {
messages.Say(keyword,
"argument keyword '%s=' is not known in call to '%s'"_err_en_US,
keyword, intrinsicName);
return false;
}
if (!set.insert(keyword).second) {
messages.Say(keyword,
"argument keyword '%s=' was repeated in call to '%s'"_err_en_US,
keyword, intrinsicName);
return false;
}
return true;
}
// Validate the keyword, if any, and ensure that A1 and A2 are always placed in
// first and second position in actualForDummy. A1 and A2 are special since they
// are not optional. The rest of the arguments are not sorted, there are no
// differences between them.
static bool CheckAndPushMinMaxArgument(ActualArgument &arg,
std::vector<ActualArgument *> &actualForDummy,
std::set<parser::CharBlock> &set, const char *intrinsicName,
parser::ContextualMessages &messages) {
if (std::optional<parser::CharBlock> keyword{arg.keyword()}) {
if (!CheckMaxMinArgument(*keyword, set, intrinsicName, messages)) {
return false;
}
const bool isA1{*keyword == parser::CharBlock{"a1", 2}};
if (isA1 && !actualForDummy[0]) {
actualForDummy[0] = &arg;
return true;
}
const bool isA2{*keyword == parser::CharBlock{"a2", 2}};
if (isA2 && !actualForDummy[1]) {
actualForDummy[1] = &arg;
return true;
}
if (isA1 || isA2) {
// Note that for arguments other than a1 and a2, this error will be caught
// later in check-call.cpp.
messages.Say(*keyword,
"keyword argument '%s=' to intrinsic '%s' was supplied "
"positionally by an earlier actual argument"_err_en_US,
*keyword, intrinsicName);
return false;
}
} else {
if (actualForDummy.size() == 2) {
if (!actualForDummy[0] && !actualForDummy[1]) {
actualForDummy[0] = &arg;
return true;
} else if (!actualForDummy[1]) {
actualForDummy[1] = &arg;
return true;
}
}
}
actualForDummy.push_back(&arg);
return true;
}
static bool CheckAtomicKind(const ActualArgument &arg,
const semantics::Scope *builtinsScope,
parser::ContextualMessages &messages) {
std::string atomicKindStr;
std::optional<DynamicType> type{arg.GetType()};
if (type->category() == TypeCategory::Integer) {
atomicKindStr = "atomic_int_kind";
} else if (type->category() == TypeCategory::Logical) {
atomicKindStr = "atomic_logical_kind";
} else {
common::die("atomic_int_kind or atomic_logical_kind from iso_fortran_env "
"must be used with IntType or LogicalType");
}
bool argOk = type->kind() ==
GetBuiltinKind(builtinsScope, ("__builtin_" + atomicKindStr).c_str());
if (!argOk) {
messages.Say(arg.sourceLocation(),
"Actual argument for 'atom=' must have kind=atomic_int_kind or atomic_logical_kind, but is '%s'"_err_en_US,
type->AsFortran());
}
return argOk;
}
// Intrinsic interface matching against the arguments of a particular
// procedure reference.
std::optional<SpecificCall> IntrinsicInterface::Match(
const CallCharacteristics &call,
const common::IntrinsicTypeDefaultKinds &defaults,
ActualArguments &arguments, FoldingContext &context,
const semantics::Scope *builtinsScope) const {
auto &messages{context.messages()};
// Attempt to construct a 1-1 correspondence between the dummy arguments in
// a particular intrinsic procedure's generic interface and the actual
// arguments in a procedure reference.
std::size_t dummyArgPatterns{0};
for (; dummyArgPatterns < maxArguments && dummy[dummyArgPatterns].keyword;
++dummyArgPatterns) {
}
// MAX and MIN (and others that map to them) allow their last argument to
// be repeated indefinitely. The actualForDummy vector is sized
// and null-initialized to the non-repeated dummy argument count
// for other instrinsics.
bool isMaxMin{dummyArgPatterns > 0 &&
dummy[dummyArgPatterns - 1].optionality == Optionality::repeats};
std::vector<ActualArgument *> actualForDummy(
isMaxMin ? 2 : dummyArgPatterns, nullptr);
bool anyMissingActualArgument{false};
std::set<parser::CharBlock> maxMinKeywords;
bool anyKeyword{false};
int which{0};
for (std::optional<ActualArgument> &arg : arguments) {
++which;
if (arg) {
if (arg->isAlternateReturn()) {
messages.Say(arg->sourceLocation(),
"alternate return specifier not acceptable on call to intrinsic '%s'"_err_en_US,
name);
return std::nullopt;
}
if (arg->keyword()) {
anyKeyword = true;
} else if (anyKeyword) {
messages.Say(arg ? arg->sourceLocation() : std::nullopt,
"actual argument #%d without a keyword may not follow an actual argument with a keyword"_err_en_US,
which);
return std::nullopt;
}
} else {
anyMissingActualArgument = true;
continue;
}
if (isMaxMin) {
if (!CheckAndPushMinMaxArgument(
*arg, actualForDummy, maxMinKeywords, name, messages)) {
return std::nullopt;
}
} else {
bool found{false};
for (std::size_t j{0}; j < dummyArgPatterns && !found; ++j) {
if (dummy[j].optionality == Optionality::missing) {
continue;
}
if (arg->keyword()) {
found = *arg->keyword() == dummy[j].keyword;
if (found) {
if (const auto *previous{actualForDummy[j]}) {
if (previous->keyword()) {
messages.Say(*arg->keyword(),
"repeated keyword argument to intrinsic '%s'"_err_en_US,
name);
} else {
messages.Say(*arg->keyword(),
"keyword argument to intrinsic '%s' was supplied "
"positionally by an earlier actual argument"_err_en_US,
name);
}
return std::nullopt;
}
}
} else {
found = !actualForDummy[j] && !anyMissingActualArgument;
}
if (found) {
actualForDummy[j] = &*arg;
}
}
if (!found) {
if (arg->keyword()) {
messages.Say(*arg->keyword(),
"unknown keyword argument to intrinsic '%s'"_err_en_US, name);
} else {
messages.Say(
"too many actual arguments for intrinsic '%s'"_err_en_US, name);
}
return std::nullopt;
}
}
}
std::size_t dummies{actualForDummy.size()};
// Check types and kinds of the actual arguments against the intrinsic's
// interface. Ensure that two or more arguments that have to have the same
// (or compatible) type and kind do so. Check for missing non-optional
// arguments now, too.
const ActualArgument *sameArg{nullptr};
const ActualArgument *operandArg{nullptr};
const IntrinsicDummyArgument *kindDummyArg{nullptr};
const ActualArgument *kindArg{nullptr};
std::optional<int> dimArg;
for (std::size_t j{0}; j < dummies; ++j) {
const IntrinsicDummyArgument &d{dummy[std::min(j, dummyArgPatterns - 1)]};
if (d.typePattern.kindCode == KindCode::kindArg) {
CHECK(!kindDummyArg);
kindDummyArg = &d;
}
const ActualArgument *arg{actualForDummy[j]};
if (!arg) {
if (d.optionality == Optionality::required) {
std::string kw{d.keyword};
if (isMaxMin && !actualForDummy[0] && !actualForDummy[1]) {
messages.Say("missing mandatory 'a1=' and 'a2=' arguments"_err_en_US);
} else {
messages.Say(
"missing mandatory '%s=' argument"_err_en_US, kw.c_str());
}
return std::nullopt; // missing non-OPTIONAL argument
} else {
continue;
}
}
if (d.optionality == Optionality::missing) {
messages.Say(arg->sourceLocation(), "unexpected '%s=' argument"_err_en_US,
d.keyword);
return std::nullopt;
}
if (!d.flags.test(ArgFlag::canBeNull)) {
if (const auto *expr{arg->UnwrapExpr()}; expr && IsNullPointer(*expr)) {
if (!IsBareNullPointer(expr) && IsNullObjectPointer(*expr) &&
d.flags.test(ArgFlag::canBeMoldNull)) {
// ok
} else {
messages.Say(arg->sourceLocation(),
"A NULL() pointer is not allowed for '%s=' intrinsic argument"_err_en_US,
d.keyword);
return std::nullopt;
}
}
}
if (d.flags.test(ArgFlag::notAssumedSize)) {
if (auto named{ExtractNamedEntity(*arg)}) {
if (semantics::IsAssumedSizeArray(named->GetLastSymbol())) {
messages.Say(arg->sourceLocation(),
"The '%s=' argument to the intrinsic procedure '%s' may not be assumed-size"_err_en_US,
d.keyword, name);
return std::nullopt;
}
}
}
if (arg->GetAssumedTypeDummy()) {
// TYPE(*) assumed-type dummy argument forwarded to intrinsic
if (d.typePattern.categorySet == AnyType &&
(d.rank == Rank::anyOrAssumedRank ||
d.rank == Rank::arrayOrAssumedRank) &&
(d.typePattern.kindCode == KindCode::any ||
d.typePattern.kindCode == KindCode::addressable)) {
continue;
} else {
messages.Say(arg->sourceLocation(),
"Assumed type TYPE(*) dummy argument not allowed for '%s=' intrinsic argument"_err_en_US,
d.keyword);
return std::nullopt;
}
}
std::optional<DynamicType> type{arg->GetType()};
if (!type) {
CHECK(arg->Rank() == 0);
const Expr<SomeType> &expr{DEREF(arg->UnwrapExpr())};
if (IsBOZLiteral(expr)) {
if (d.typePattern.kindCode == KindCode::typeless ||
d.rank == Rank::elementalOrBOZ) {
continue;
} else {
const IntrinsicDummyArgument *nextParam{
j + 1 < dummies ? &dummy[j + 1] : nullptr};
if (nextParam && nextParam->rank == Rank::elementalOrBOZ) {
messages.Say(arg->sourceLocation(),
"Typeless (BOZ) not allowed for both '%s=' & '%s=' arguments"_err_en_US, // C7109
d.keyword, nextParam->keyword);
} else {
messages.Say(arg->sourceLocation(),
"Typeless (BOZ) not allowed for '%s=' argument"_err_en_US,
d.keyword);
}
}
} else {
// NULL(no MOLD=), procedure, or procedure pointer
CHECK(IsProcedurePointerTarget(expr));
if (d.typePattern.kindCode == KindCode::addressable ||
d.rank == Rank::reduceOperation) {
continue;
} else if (d.typePattern.kindCode == KindCode::nullPointerType) {
continue;
} else if (IsBareNullPointer(&expr)) {
// checked elsewhere
continue;
} else {
CHECK(IsProcedure(expr) || IsProcedurePointer(expr));
messages.Say(arg->sourceLocation(),
"Actual argument for '%s=' may not be a procedure"_err_en_US,
d.keyword);
}
}
return std::nullopt;
} else if (!d.typePattern.categorySet.test(type->category())) {
messages.Say(arg->sourceLocation(),
"Actual argument for '%s=' has bad type '%s'"_err_en_US, d.keyword,
type->AsFortran());
return std::nullopt; // argument has invalid type category
}
bool argOk{false};
switch (d.typePattern.kindCode) {
case KindCode::none:
case KindCode::typeless:
argOk = false;
break;
case KindCode::ieeeFlagType:
argOk = !type->IsUnlimitedPolymorphic() &&
type->category() == TypeCategory::Derived &&
semantics::IsIeeeFlagType(&type->GetDerivedTypeSpec());
break;
case KindCode::ieeeRoundType:
argOk = !type->IsUnlimitedPolymorphic() &&
type->category() == TypeCategory::Derived &&
semantics::IsIeeeRoundType(&type->GetDerivedTypeSpec());
break;
case KindCode::teamType:
argOk = !type->IsUnlimitedPolymorphic() &&
type->category() == TypeCategory::Derived &&
semantics::IsTeamType(&type->GetDerivedTypeSpec());
break;
case KindCode::defaultIntegerKind:
argOk = type->kind() == defaults.GetDefaultKind(TypeCategory::Integer);
break;
case KindCode::defaultRealKind:
argOk = type->kind() == defaults.GetDefaultKind(TypeCategory::Real);
break;
case KindCode::doublePrecision:
argOk = type->kind() == defaults.doublePrecisionKind();
break;
case KindCode::defaultCharKind:
argOk = type->kind() == defaults.GetDefaultKind(TypeCategory::Character);
break;
case KindCode::defaultLogicalKind:
argOk = type->kind() == defaults.GetDefaultKind(TypeCategory::Logical);
break;
case KindCode::any:
argOk = true;
break;
case KindCode::kindArg:
CHECK(type->category() == TypeCategory::Integer);
CHECK(!kindArg);
kindArg = arg;
argOk = true;
break;
case KindCode::dimArg:
CHECK(type->category() == TypeCategory::Integer);
dimArg = j;
argOk = true;
break;
case KindCode::same:
if (!sameArg) {
sameArg = arg;
}
argOk = type->IsTkLenCompatibleWith(sameArg->GetType().value());
break;
case KindCode::sameKind:
if (!sameArg) {
sameArg = arg;
}
argOk = type->IsTkCompatibleWith(sameArg->GetType().value());
break;
case KindCode::operand:
if (!operandArg) {
operandArg = arg;
} else if (auto prev{operandArg->GetType()}) {
if (type->category() == prev->category()) {
if (type->kind() > prev->kind()) {
operandArg = arg;
}
} else if (prev->category() == TypeCategory::Integer) {
operandArg = arg;
}
}
argOk = true;
break;
case KindCode::effectiveKind:
common::die("INTERNAL: KindCode::effectiveKind appears on argument '%s' "
"for intrinsic '%s'",
d.keyword, name);
break;
case KindCode::addressable:
case KindCode::nullPointerType:
argOk = true;
break;
case KindCode::exactKind:
argOk = type->kind() == d.typePattern.kindValue;
break;
case KindCode::greaterOrEqualToKind:
argOk = type->kind() >= d.typePattern.kindValue;
break;
case KindCode::sameAtom:
if (!sameArg) {
sameArg = arg;
argOk = CheckAtomicKind(DEREF(arg), builtinsScope, messages);
} else {
argOk = type->IsTkCompatibleWith(sameArg->GetType().value());
if (!argOk) {
messages.Say(arg->sourceLocation(),
"Actual argument for '%s=' must have same type and kind as 'atom=', but is '%s'"_err_en_US,
d.keyword, type->AsFortran());
}
}
if (!argOk)
return std::nullopt;
break;
case KindCode::atomicIntKind:
argOk = type->kind() ==
GetBuiltinKind(builtinsScope, "__builtin_atomic_int_kind");
if (!argOk) {
messages.Say(arg->sourceLocation(),
"Actual argument for '%s=' must have kind=atomic_int_kind, but is '%s'"_err_en_US,
d.keyword, type->AsFortran());
return std::nullopt;
}
break;
case KindCode::atomicIntOrLogicalKind:
argOk = CheckAtomicKind(DEREF(arg), builtinsScope, messages);
if (!argOk)
return std::nullopt;
break;
default:
CRASH_NO_CASE;
}
if (!argOk) {
messages.Say(arg->sourceLocation(),
"Actual argument for '%s=' has bad type or kind '%s'"_err_en_US,
d.keyword, type->AsFortran());
return std::nullopt;
}
}
// Check the ranks of the arguments against the intrinsic's interface.
const ActualArgument *arrayArg{nullptr};
const char *arrayArgName{nullptr};
const ActualArgument *knownArg{nullptr};
std::optional<int> shapeArgSize;
int elementalRank{0};
for (std::size_t j{0}; j < dummies; ++j) {
const IntrinsicDummyArgument &d{dummy[std::min(j, dummyArgPatterns - 1)]};
if (const ActualArgument *arg{actualForDummy[j]}) {
bool isAssumedRank{IsAssumedRank(*arg)};
if (isAssumedRank && d.rank != Rank::anyOrAssumedRank &&
d.rank != Rank::arrayOrAssumedRank) {
messages.Say(arg->sourceLocation(),
"Assumed-rank array cannot be forwarded to '%s=' argument"_err_en_US,
d.keyword);
return std::nullopt;
}
int rank{arg->Rank()};
bool argOk{false};
switch (d.rank) {
case Rank::elemental:
case Rank::elementalOrBOZ:
if (elementalRank == 0) {
elementalRank = rank;
}
argOk = rank == 0 || rank == elementalRank;
break;
case Rank::scalar:
argOk = rank == 0;
break;
case Rank::vector:
argOk = rank == 1;
break;
case Rank::shape:
CHECK(!shapeArgSize);
if (rank != 1) {
messages.Say(arg->sourceLocation(),
"'shape=' argument must be an array of rank 1"_err_en_US);
return std::nullopt;
} else {
if (auto shape{GetShape(context, *arg)}) {
if (auto constShape{AsConstantShape(context, *shape)}) {
shapeArgSize = constShape->At(ConstantSubscripts{1}).ToInt64();
CHECK(*shapeArgSize >= 0);
argOk = true;
}
}
}
if (!argOk) {
messages.Say(arg->sourceLocation(),
"'shape=' argument must be a vector of known size"_err_en_US);
return std::nullopt;
}
break;
case Rank::matrix:
argOk = rank == 2;
break;
case Rank::array:
argOk = rank > 0;
if (!arrayArg) {
arrayArg = arg;
arrayArgName = d.keyword;
}
break;
case Rank::coarray:
argOk = IsCoarray(*arg);
if (!argOk) {
messages.Say(arg->sourceLocation(),
"'coarray=' argument must have corank > 0 for intrinsic '%s'"_err_en_US,
name);
return std::nullopt;
}
break;
case Rank::atom:
argOk = rank == 0 && (IsCoarray(*arg) || ExtractCoarrayRef(*arg));
if (!argOk) {
messages.Say(arg->sourceLocation(),
"'%s=' argument must be a scalar coarray or coindexed object for intrinsic '%s'"_err_en_US,
d.keyword, name);
return std::nullopt;
}
break;
case Rank::known:
if (!knownArg) {
knownArg = arg;
}
argOk = !isAssumedRank && rank == knownArg->Rank();
break;
case Rank::anyOrAssumedRank:
case Rank::arrayOrAssumedRank:
if (isAssumedRank) {
argOk = true;
break;
}
if (d.rank == Rank::arrayOrAssumedRank && rank == 0) {
argOk = false;
break;
}
if (!knownArg) {
knownArg = arg;
}
if (!dimArg && rank > 0 &&
(std::strcmp(name, "shape") == 0 ||
std::strcmp(name, "size") == 0 ||
std::strcmp(name, "ubound") == 0)) {
// Check for a whole assumed-size array argument.
// These are disallowed for SHAPE, and require DIM= for
// SIZE and UBOUND.
// (A previous error message for UBOUND will take precedence
// over this one, as this error is caught by the second entry
// for UBOUND.)
if (auto named{ExtractNamedEntity(*arg)}) {
if (semantics::IsAssumedSizeArray(named->GetLastSymbol())) {
if (strcmp(name, "shape") == 0) {
messages.Say(arg->sourceLocation(),
"The 'source=' argument to the intrinsic function 'shape' may not be assumed-size"_err_en_US);
} else {
messages.Say(arg->sourceLocation(),
"A dim= argument is required for '%s' when the array is assumed-size"_err_en_US,
name);
}
return std::nullopt;
}
}
}
argOk = true;
break;
case Rank::conformable: // arg must be conformable with previous arrayArg
CHECK(arrayArg);
CHECK(arrayArgName);
if (const std::optional<Shape> &arrayArgShape{
GetShape(context, *arrayArg)}) {
if (std::optional<Shape> argShape{GetShape(context, *arg)}) {
std::string arrayArgMsg{"'"};
arrayArgMsg = arrayArgMsg + arrayArgName + "='" + " argument";
std::string argMsg{"'"};
argMsg = argMsg + d.keyword + "='" + " argument";
CheckConformance(context.messages(), *arrayArgShape, *argShape,
CheckConformanceFlags::RightScalarExpandable,
arrayArgMsg.c_str(), argMsg.c_str());
}
}
argOk = true; // Avoid an additional error message
break;
case Rank::dimReduced:
case Rank::dimRemovedOrScalar:
CHECK(arrayArg);
argOk = rank == 0 || rank + 1 == arrayArg->Rank();
break;
case Rank::reduceOperation:
// The reduction function is validated in ApplySpecificChecks().
argOk = true;
break;
case Rank::scalarIfDim:
case Rank::locReduced:
case Rank::rankPlus1:
case Rank::shaped:
common::die("INTERNAL: result-only rank code appears on argument '%s' "
"for intrinsic '%s'",
d.keyword, name);
}
if (!argOk) {
messages.Say(arg->sourceLocation(),
"'%s=' argument has unacceptable rank %d"_err_en_US, d.keyword,
rank);
return std::nullopt;
}
}
}
// Calculate the characteristics of the function result, if any
std::optional<DynamicType> resultType;
if (auto category{result.categorySet.LeastElement()}) {
// The intrinsic is not a subroutine.
if (call.isSubroutineCall) {
return std::nullopt;
}
switch (result.kindCode) {
case KindCode::defaultIntegerKind:
CHECK(result.categorySet == IntType);
CHECK(*category == TypeCategory::Integer);
resultType = DynamicType{TypeCategory::Integer,
defaults.GetDefaultKind(TypeCategory::Integer)};
break;
case KindCode::defaultRealKind:
CHECK(result.categorySet == CategorySet{*category});
CHECK(FloatingType.test(*category));
resultType =
DynamicType{*category, defaults.GetDefaultKind(TypeCategory::Real)};
break;
case KindCode::doublePrecision:
CHECK(result.categorySet == CategorySet{*category});
CHECK(FloatingType.test(*category));
resultType = DynamicType{*category, defaults.doublePrecisionKind()};
break;
case KindCode::defaultLogicalKind:
CHECK(result.categorySet == LogicalType);
CHECK(*category == TypeCategory::Logical);
resultType = DynamicType{TypeCategory::Logical,
defaults.GetDefaultKind(TypeCategory::Logical)};
break;
case KindCode::defaultCharKind:
CHECK(result.categorySet == CharType);
CHECK(*category == TypeCategory::Character);
resultType = DynamicType{TypeCategory::Character,
defaults.GetDefaultKind(TypeCategory::Character)};
break;
case KindCode::same:
CHECK(sameArg);
if (std::optional<DynamicType> aType{sameArg->GetType()}) {
if (result.categorySet.test(aType->category())) {
if (const auto *sameChar{UnwrapExpr<Expr<SomeCharacter>>(*sameArg)}) {
if (auto len{ToInt64(Fold(context, sameChar->LEN()))}) {
resultType = DynamicType{aType->kind(), *len};
} else {
resultType = *aType;
}
} else {
resultType = *aType;
}
} else {
resultType = DynamicType{*category, aType->kind()};
}
}
break;
case KindCode::sameKind:
CHECK(sameArg);
if (std::optional<DynamicType> aType{sameArg->GetType()}) {
resultType = DynamicType{*category, aType->kind()};
}
break;
case KindCode::operand:
CHECK(operandArg);
resultType = operandArg->GetType();
CHECK(!resultType || result.categorySet.test(resultType->category()));
break;
case KindCode::effectiveKind:
CHECK(kindDummyArg);
CHECK(result.categorySet == CategorySet{*category});
if (kindArg) {
if (auto *expr{kindArg->UnwrapExpr()}) {
CHECK(expr->Rank() == 0);
if (auto code{ToInt64(*expr)}) {
if (context.targetCharacteristics().IsTypeEnabled(
*category, *code)) {
if (*category == TypeCategory::Character) { // ACHAR & CHAR
resultType = DynamicType{static_cast<int>(*code), 1};
} else {
resultType = DynamicType{*category, static_cast<int>(*code)};
}
break;
}
}
}
messages.Say("'kind=' argument must be a constant scalar integer "
"whose value is a supported kind for the "
"intrinsic result type"_err_en_US);
// use default kind below for error recovery
} else if (kindDummyArg->flags.test(ArgFlag::defaultsToSameKind)) {
CHECK(sameArg);
resultType = *sameArg->GetType();
} else if (kindDummyArg->flags.test(ArgFlag::defaultsToSizeKind)) {
CHECK(*category == TypeCategory::Integer);
resultType =
DynamicType{TypeCategory::Integer, defaults.sizeIntegerKind()};
} else {
CHECK(kindDummyArg->flags.test(ArgFlag::defaultsToDefaultForResult));
}
if (!resultType) {
int kind{defaults.GetDefaultKind(*category)};
if (*category == TypeCategory::Character) { // ACHAR & CHAR
resultType = DynamicType{kind, 1};
} else {
resultType = DynamicType{*category, kind};
}
}
break;
case KindCode::likeMultiply:
CHECK(dummies >= 2);
CHECK(actualForDummy[0]);
CHECK(actualForDummy[1]);
resultType = actualForDummy[0]->GetType()->ResultTypeForMultiply(
*actualForDummy[1]->GetType());
break;
case KindCode::subscript:
CHECK(result.categorySet == IntType);
CHECK(*category == TypeCategory::Integer);
resultType =
DynamicType{TypeCategory::Integer, defaults.subscriptIntegerKind()};
break;
case KindCode::size:
CHECK(result.categorySet == IntType);
CHECK(*category == TypeCategory::Integer);
resultType =
DynamicType{TypeCategory::Integer, defaults.sizeIntegerKind()};
break;
case KindCode::teamType:
CHECK(result.categorySet == DerivedType);
CHECK(*category == TypeCategory::Derived);
resultType = DynamicType{
GetBuiltinDerivedType(builtinsScope, "__builtin_team_type")};
break;
case KindCode::greaterOrEqualToKind:
case KindCode::exactKind:
resultType = DynamicType{*category, result.kindValue};
break;
case KindCode::typeless:
case KindCode::any:
case KindCode::kindArg:
case KindCode::dimArg:
common::die(
"INTERNAL: bad KindCode appears on intrinsic '%s' result", name);
break;
default:
CRASH_NO_CASE;
}
} else {
if (!call.isSubroutineCall) {
return std::nullopt;
}
CHECK(result.kindCode == KindCode::none);
}
// Emit warnings when the syntactic presence of a DIM= argument determines
// the semantics of the call but the associated actual argument may not be
// present at execution time.
if (dimArg) {
std::optional<int> arrayRank;
if (arrayArg) {
arrayRank = arrayArg->Rank();
if (auto dimVal{ToInt64(actualForDummy[*dimArg])}) {
if (*dimVal < 1) {
messages.Say(
"The value of DIM= (%jd) may not be less than 1"_err_en_US,
static_cast<std::intmax_t>(*dimVal));
} else if (*dimVal > *arrayRank) {
messages.Say(
"The value of DIM= (%jd) may not be greater than %d"_err_en_US,
static_cast<std::intmax_t>(*dimVal), *arrayRank);
}
}
}
switch (rank) {
case Rank::dimReduced:
case Rank::dimRemovedOrScalar:
case Rank::locReduced:
case Rank::scalarIfDim:
if (dummy[*dimArg].optionality == Optionality::required) {
if (const Symbol *whole{
UnwrapWholeSymbolOrComponentDataRef(actualForDummy[*dimArg])}) {
if (IsOptional(*whole) || IsAllocatableOrObjectPointer(whole)) {
if (context.languageFeatures().ShouldWarn(
common::UsageWarning::OptionalMustBePresent)) {
if (rank == Rank::scalarIfDim || arrayRank.value_or(-1) == 1) {
messages.Say(
"The actual argument for DIM= is optional, pointer, or allocatable, and it is assumed to be present and equal to 1 at execution time"_warn_en_US);
} else {
messages.Say(
"The actual argument for DIM= is optional, pointer, or allocatable, and may not be absent during execution; parenthesize to silence this warning"_warn_en_US);
}
}
}
}
}
break;
default:;
}
}
// At this point, the call is acceptable.
// Determine the rank of the function result.
int resultRank{0};
switch (rank) {
case Rank::elemental:
resultRank = elementalRank;
break;
case Rank::scalar:
resultRank = 0;
break;
case Rank::vector:
resultRank = 1;
break;
case Rank::matrix:
resultRank = 2;
break;
case Rank::conformable:
CHECK(arrayArg);
resultRank = arrayArg->Rank();
break;
case Rank::dimReduced:
CHECK(arrayArg);
resultRank = dimArg ? arrayArg->Rank() - 1 : 0;
break;
case Rank::locReduced:
CHECK(arrayArg);
resultRank = dimArg ? arrayArg->Rank() - 1 : 1;
break;
case Rank::rankPlus1:
CHECK(knownArg);
resultRank = knownArg->Rank() + 1;
break;
case Rank::shaped:
CHECK(shapeArgSize);
resultRank = *shapeArgSize;
break;
case Rank::scalarIfDim:
resultRank = dimArg ? 0 : 1;
break;
case Rank::elementalOrBOZ:
case Rank::shape:
case Rank::array:
case Rank::coarray:
case Rank::atom:
case Rank::known:
case Rank::anyOrAssumedRank:
case Rank::arrayOrAssumedRank:
case Rank::reduceOperation:
case Rank::dimRemovedOrScalar:
common::die("INTERNAL: bad Rank code on intrinsic '%s' result", name);
break;
}
CHECK(resultRank >= 0);
// Rearrange the actual arguments into dummy argument order.
ActualArguments rearranged(dummies);
for (std::size_t j{0}; j < dummies; ++j) {
if (ActualArgument *arg{actualForDummy[j]}) {
rearranged[j] = std::move(*arg);
}
}
// Characterize the specific intrinsic procedure.
characteristics::DummyArguments dummyArgs;
std::optional<int> sameDummyArg;
for (std::size_t j{0}; j < dummies; ++j) {
const IntrinsicDummyArgument &d{dummy[std::min(j, dummyArgPatterns - 1)]};
if (const auto &arg{rearranged[j]}) {
if (const Expr<SomeType> *expr{arg->UnwrapExpr()}) {
std::string kw{d.keyword};
if (arg->keyword()) {
kw = arg->keyword()->ToString();
} else if (isMaxMin) {
for (std::size_t k{j + 1};; ++k) {
kw = "a"s + std::to_string(k);
auto iter{std::find_if(dummyArgs.begin(), dummyArgs.end(),
[&kw](const characteristics::DummyArgument &prev) {
return prev.name == kw;
})};
if (iter == dummyArgs.end()) {
break;
}
}
}
if (auto dc{characteristics::DummyArgument::FromActual(std::move(kw),
*expr, context, /*forImplicitInterface=*/false)}) {
if (auto *dummyProc{
std::get_if<characteristics::DummyProcedure>(&dc->u)}) {
// Dummy procedures are never elemental.
dummyProc->procedure.value().attrs.reset(
characteristics::Procedure::Attr::Elemental);
}
dummyArgs.emplace_back(std::move(*dc));
if (d.typePattern.kindCode == KindCode::same && !sameDummyArg) {
sameDummyArg = j;
}
} else { // error recovery
messages.Say(
"Could not characterize intrinsic function actual argument '%s'"_err_en_US,
expr->AsFortran().c_str());
return std::nullopt;
}
} else {
CHECK(arg->GetAssumedTypeDummy());
dummyArgs.emplace_back(std::string{d.keyword},
characteristics::DummyDataObject{DynamicType::AssumedType()});
}
} else {
// optional argument is absent
CHECK(d.optionality != Optionality::required);
if (d.typePattern.kindCode == KindCode::same) {
dummyArgs.emplace_back(dummyArgs[sameDummyArg.value()]);
} else {
auto category{d.typePattern.categorySet.LeastElement().value()};
if (category == TypeCategory::Derived) {
// TODO: any other built-in derived types used as optional intrinsic
// dummies?
CHECK(d.typePattern.kindCode == KindCode::teamType);
characteristics::TypeAndShape typeAndShape{
GetBuiltinDerivedType(builtinsScope, "__builtin_team_type")};
dummyArgs.emplace_back(std::string{d.keyword},
characteristics::DummyDataObject{std::move(typeAndShape)});
} else {
characteristics::TypeAndShape typeAndShape{
DynamicType{category, defaults.GetDefaultKind(category)}};
dummyArgs.emplace_back(std::string{d.keyword},
characteristics::DummyDataObject{std::move(typeAndShape)});
}
}
dummyArgs.back().SetOptional();
}
dummyArgs.back().SetIntent(d.intent);
}
characteristics::Procedure::Attrs attrs;
if (elementalRank > 0) {
attrs.set(characteristics::Procedure::Attr::Elemental);
}
if (call.isSubroutineCall) {
if (intrinsicClass == IntrinsicClass::pureSubroutine /* MOVE_ALLOC */ ||
intrinsicClass == IntrinsicClass::elementalSubroutine /* MVBITS */) {
attrs.set(characteristics::Procedure::Attr::Pure);
}
return SpecificCall{
SpecificIntrinsic{
name, characteristics::Procedure{std::move(dummyArgs), attrs}},
std::move(rearranged)};
} else {
attrs.set(characteristics::Procedure::Attr::Pure);
characteristics::TypeAndShape typeAndShape{resultType.value(), resultRank};
characteristics::FunctionResult funcResult{std::move(typeAndShape)};
characteristics::Procedure chars{
std::move(funcResult), std::move(dummyArgs), attrs};
return SpecificCall{
SpecificIntrinsic{name, std::move(chars)}, std::move(rearranged)};
}
}
class IntrinsicProcTable::Implementation {
public:
explicit Implementation(const common::IntrinsicTypeDefaultKinds &dfts)
: defaults_{dfts} {
for (const IntrinsicInterface &f : genericIntrinsicFunction) {
genericFuncs_.insert(std::make_pair(std::string{f.name}, &f));
}
for (const std::pair<const char *, const char *> &a : genericAlias) {
aliases_.insert(
std::make_pair(std::string{a.first}, std::string{a.second}));
}
for (const SpecificIntrinsicInterface &f : specificIntrinsicFunction) {
specificFuncs_.insert(std::make_pair(std::string{f.name}, &f));
}
for (const IntrinsicInterface &f : intrinsicSubroutine) {
subroutines_.insert(std::make_pair(std::string{f.name}, &f));
}
}
void SupplyBuiltins(const semantics::Scope &builtins) {
builtinsScope_ = &builtins;
}
bool IsIntrinsic(const std::string &) const;
bool IsIntrinsicFunction(const std::string &) const;
bool IsIntrinsicSubroutine(const std::string &) const;
bool IsDualIntrinsic(const std::string &) const;
IntrinsicClass GetIntrinsicClass(const std::string &) const;
std::string GetGenericIntrinsicName(const std::string &) const;
std::optional<SpecificCall> Probe(
const CallCharacteristics &, ActualArguments &, FoldingContext &) const;
std::optional<SpecificIntrinsicFunctionInterface> IsSpecificIntrinsicFunction(
const std::string &) const;
llvm::raw_ostream &Dump(llvm::raw_ostream &) const;
private:
DynamicType GetSpecificType(const TypePattern &) const;
SpecificCall HandleNull(ActualArguments &, FoldingContext &) const;
std::optional<SpecificCall> HandleC_F_Pointer(
ActualArguments &, FoldingContext &) const;
std::optional<SpecificCall> HandleC_Loc(
ActualArguments &, FoldingContext &) const;
const std::string &ResolveAlias(const std::string &name) const {
auto iter{aliases_.find(name)};
return iter == aliases_.end() ? name : iter->second;
}
common::IntrinsicTypeDefaultKinds defaults_;
std::multimap<std::string, const IntrinsicInterface *> genericFuncs_;
std::multimap<std::string, const SpecificIntrinsicInterface *> specificFuncs_;
std::multimap<std::string, const IntrinsicInterface *> subroutines_;
const semantics::Scope *builtinsScope_{nullptr};
std::map<std::string, std::string> aliases_;
semantics::ParamValue assumedLen_{
semantics::ParamValue::Assumed(common::TypeParamAttr::Len)};
};
bool IntrinsicProcTable::Implementation::IsIntrinsicFunction(
const std::string &name0) const {
const std::string &name{ResolveAlias(name0)};
auto specificRange{specificFuncs_.equal_range(name)};
if (specificRange.first != specificRange.second) {
return true;
}
auto genericRange{genericFuncs_.equal_range(name)};
if (genericRange.first != genericRange.second) {
return true;
}
// special cases
return name == "__builtin_c_loc" || name == "null";
}
bool IntrinsicProcTable::Implementation::IsIntrinsicSubroutine(
const std::string &name0) const {
const std::string &name{ResolveAlias(name0)};
auto subrRange{subroutines_.equal_range(name)};
if (subrRange.first != subrRange.second) {
return true;
}
// special cases
return name == "__builtin_c_f_pointer";
}
bool IntrinsicProcTable::Implementation::IsIntrinsic(
const std::string &name) const {
return IsIntrinsicFunction(name) || IsIntrinsicSubroutine(name);
}
bool IntrinsicProcTable::Implementation::IsDualIntrinsic(
const std::string &name) const {
// Collection for some intrinsics with function and subroutine form,
// in order to pass the semantic check.
static const std::string dualIntrinsic[]{
{"etime"s}, {"getcwd"s}, {"rename"s}, {"second"s}};
return llvm::is_contained(dualIntrinsic, name);
}
IntrinsicClass IntrinsicProcTable::Implementation::GetIntrinsicClass(
const std::string &name) const {
auto specificIntrinsic{specificFuncs_.find(name)};
if (specificIntrinsic != specificFuncs_.end()) {
return specificIntrinsic->second->intrinsicClass;
}
auto genericIntrinsic{genericFuncs_.find(name)};
if (genericIntrinsic != genericFuncs_.end()) {
return genericIntrinsic->second->intrinsicClass;
}
auto subrIntrinsic{subroutines_.find(name)};
if (subrIntrinsic != subroutines_.end()) {
return subrIntrinsic->second->intrinsicClass;
}
return IntrinsicClass::noClass;
}
std::string IntrinsicProcTable::Implementation::GetGenericIntrinsicName(
const std::string &name) const {
auto specificIntrinsic{specificFuncs_.find(name)};
if (specificIntrinsic != specificFuncs_.end()) {
if (const char *genericName{specificIntrinsic->second->generic}) {
return {genericName};
}
}
return name;
}
bool CheckAndRearrangeArguments(ActualArguments &arguments,
parser::ContextualMessages &messages, const char *const dummyKeywords[],
std::size_t trailingOptionals) {
std::size_t numDummies{0};
while (dummyKeywords[numDummies]) {
++numDummies;
}
CHECK(trailingOptionals <= numDummies);
if (arguments.size() > numDummies) {
messages.Say("Too many actual arguments (%zd > %zd)"_err_en_US,
arguments.size(), numDummies);
return false;
}
ActualArguments rearranged(numDummies);
bool anyKeywords{false};
std::size_t position{0};
for (std::optional<ActualArgument> &arg : arguments) {
std::size_t dummyIndex{0};
if (arg && arg->keyword()) {
anyKeywords = true;
for (; dummyIndex < numDummies; ++dummyIndex) {
if (*arg->keyword() == dummyKeywords[dummyIndex]) {
break;
}
}
if (dummyIndex >= numDummies) {
messages.Say(*arg->keyword(),
"Unknown argument keyword '%s='"_err_en_US, *arg->keyword());
return false;
}
} else if (anyKeywords) {
messages.Say(arg ? arg->sourceLocation() : messages.at(),
"A positional actual argument may not appear after any keyword arguments"_err_en_US);
return false;
} else {
dummyIndex = position++;
}
if (rearranged[dummyIndex]) {
messages.Say(arg ? arg->sourceLocation() : messages.at(),
"Dummy argument '%s=' appears more than once"_err_en_US,
dummyKeywords[dummyIndex]);
return false;
}
rearranged[dummyIndex] = std::move(arg);
arg.reset();
}
bool anyMissing{false};
for (std::size_t j{0}; j < numDummies - trailingOptionals; ++j) {
if (!rearranged[j]) {
messages.Say("Dummy argument '%s=' is absent and not OPTIONAL"_err_en_US,
dummyKeywords[j]);
anyMissing = true;
}
}
arguments = std::move(rearranged);
return !anyMissing;
}
// The NULL() intrinsic is a special case.
SpecificCall IntrinsicProcTable::Implementation::HandleNull(
ActualArguments &arguments, FoldingContext &context) const {
static const char *const keywords[]{"mold", nullptr};
if (CheckAndRearrangeArguments(arguments, context.messages(), keywords, 1) &&
arguments[0]) {
Expr<SomeType> *mold{arguments[0]->UnwrapExpr()};
bool isBareNull{IsBareNullPointer(mold)};
if (isBareNull) {
// NULL(NULL()), NULL(NULL(NULL())), &c. are all just NULL()
mold = nullptr;
}
if (mold) {
if (IsAssumedRank(*arguments[0])) {
context.messages().Say(arguments[0]->sourceLocation(),
"MOLD= argument to NULL() must not be assumed-rank"_err_en_US);
}
bool isProcPtrTarget{
IsProcedurePointerTarget(*mold) && !IsNullObjectPointer(*mold)};
if (isProcPtrTarget || IsAllocatableOrPointerObject(*mold)) {
characteristics::DummyArguments args;
std::optional<characteristics::FunctionResult> fResult;
if (isProcPtrTarget) {
// MOLD= procedure pointer
std::optional<characteristics::Procedure> procPointer;
if (IsNullProcedurePointer(*mold)) {
procPointer =
characteristics::Procedure::Characterize(*mold, context);
} else {
const Symbol *last{GetLastSymbol(*mold)};
procPointer =
characteristics::Procedure::Characterize(DEREF(last), context);
}
// procPointer is vacant if there was an error with the analysis
// associated with the procedure pointer
if (procPointer) {
args.emplace_back("mold"s,
characteristics::DummyProcedure{common::Clone(*procPointer)});
fResult.emplace(std::move(*procPointer));
}
} else if (auto type{mold->GetType()}) {
// MOLD= object pointer
characteristics::TypeAndShape typeAndShape{
*type, GetShape(context, *mold)};
args.emplace_back(
"mold"s, characteristics::DummyDataObject{typeAndShape});
fResult.emplace(std::move(typeAndShape));
} else {
context.messages().Say(arguments[0]->sourceLocation(),
"MOLD= argument to NULL() lacks type"_err_en_US);
}
if (fResult) {
fResult->attrs.set(characteristics::FunctionResult::Attr::Pointer);
characteristics::Procedure::Attrs attrs;
attrs.set(characteristics::Procedure::Attr::NullPointer);
characteristics::Procedure chars{
std::move(*fResult), std::move(args), attrs};
return SpecificCall{SpecificIntrinsic{"null"s, std::move(chars)},
std::move(arguments)};
}
}
}
if (!isBareNull) {
context.messages().Say(arguments[0]->sourceLocation(),
"MOLD= argument to NULL() must be a pointer or allocatable"_err_en_US);
}
}
characteristics::Procedure::Attrs attrs;
attrs.set(characteristics::Procedure::Attr::NullPointer);
attrs.set(characteristics::Procedure::Attr::Pure);
arguments.clear();
return SpecificCall{
SpecificIntrinsic{"null"s,
characteristics::Procedure{characteristics::DummyArguments{}, attrs}},
std::move(arguments)};
}
// Subroutine C_F_POINTER(CPTR=,FPTR=[,SHAPE=]) from
// intrinsic module ISO_C_BINDING (18.2.3.3)
std::optional<SpecificCall>
IntrinsicProcTable::Implementation::HandleC_F_Pointer(
ActualArguments &arguments, FoldingContext &context) const {
characteristics::Procedure::Attrs attrs;
attrs.set(characteristics::Procedure::Attr::Subroutine);
static const char *const keywords[]{"cptr", "fptr", "shape", nullptr};
characteristics::DummyArguments dummies;
if (CheckAndRearrangeArguments(arguments, context.messages(), keywords, 1)) {
CHECK(arguments.size() == 3);
if (const auto *expr{arguments[0].value().UnwrapExpr()}) {
// General semantic checks will catch an actual argument that's not
// scalar.
if (auto type{expr->GetType()}) {
if (type->category() != TypeCategory::Derived ||
type->IsPolymorphic() ||
(type->GetDerivedTypeSpec().typeSymbol().name() !=
"__builtin_c_ptr" &&
type->GetDerivedTypeSpec().typeSymbol().name() !=
"__builtin_c_devptr")) {
context.messages().Say(arguments[0]->sourceLocation(),
"CPTR= argument to C_F_POINTER() must be a C_PTR"_err_en_US);
}
characteristics::DummyDataObject cptr{
characteristics::TypeAndShape{*type}};
cptr.intent = common::Intent::In;
dummies.emplace_back("cptr"s, std::move(cptr));
}
}
if (const auto *expr{arguments[1].value().UnwrapExpr()}) {
int fptrRank{expr->Rank()};
auto at{arguments[1]->sourceLocation()};
if (auto type{expr->GetType()}) {
if (type->HasDeferredTypeParameter()) {
context.messages().Say(at,
"FPTR= argument to C_F_POINTER() may not have a deferred type parameter"_err_en_US);
} else if (type->category() == TypeCategory::Derived) {
if (context.languageFeatures().ShouldWarn(
common::UsageWarning::Interoperability)) {
if (type->IsUnlimitedPolymorphic()) {
context.messages().Say(at,
"FPTR= argument to C_F_POINTER() should not be unlimited polymorphic"_warn_en_US);
} else if (!type->GetDerivedTypeSpec().typeSymbol().attrs().test(
semantics::Attr::BIND_C)) {
context.messages().Say(at,
"FPTR= argument to C_F_POINTER() should not have a derived type that is not BIND(C)"_warn_en_US);
}
}
} else if (!IsInteroperableIntrinsicType(
*type, &context.languageFeatures())
.value_or(true) &&
context.languageFeatures().ShouldWarn(
common::UsageWarning::Interoperability)) {
context.messages().Say(at,
"FPTR= argument to C_F_POINTER() should not have the non-interoperable intrinsic type %s"_warn_en_US,
type->AsFortran());
}
if (ExtractCoarrayRef(*expr)) {
context.messages().Say(at,
"FPTR= argument to C_F_POINTER() may not be a coindexed object"_err_en_US);
}
characteristics::DummyDataObject fptr{
characteristics::TypeAndShape{*type, fptrRank}};
fptr.intent = common::Intent::Out;
fptr.attrs.set(characteristics::DummyDataObject::Attr::Pointer);
dummies.emplace_back("fptr"s, std::move(fptr));
} else {
context.messages().Say(
at, "FPTR= argument to C_F_POINTER() must have a type"_err_en_US);
}
if (arguments[2] && fptrRank == 0) {
context.messages().Say(arguments[2]->sourceLocation(),
"SHAPE= argument to C_F_POINTER() may not appear when FPTR= is scalar"_err_en_US);
} else if (!arguments[2] && fptrRank > 0) {
context.messages().Say(
"SHAPE= argument to C_F_POINTER() must appear when FPTR= is an array"_err_en_US);
} else if (arguments[2]) {
if (const auto *argExpr{arguments[2].value().UnwrapExpr()}) {
if (argExpr->Rank() > 1) {
context.messages().Say(arguments[2]->sourceLocation(),
"SHAPE= argument to C_F_POINTER() must be a rank-one array."_err_en_US);
} else if (argExpr->Rank() == 1) {
if (auto constShape{GetConstantShape(context, *argExpr)}) {
if (constShape->At(ConstantSubscripts{1}).ToInt64() != fptrRank) {
context.messages().Say(arguments[2]->sourceLocation(),
"SHAPE= argument to C_F_POINTER() must have size equal to the rank of FPTR="_err_en_US);
}
}
}
}
}
}
}
if (dummies.size() == 2) {
DynamicType shapeType{TypeCategory::Integer, defaults_.sizeIntegerKind()};
if (arguments[2]) {
if (auto type{arguments[2]->GetType()}) {
if (type->category() == TypeCategory::Integer) {
shapeType = *type;
}
}
}
characteristics::DummyDataObject shape{
characteristics::TypeAndShape{shapeType, 1}};
shape.intent = common::Intent::In;
shape.attrs.set(characteristics::DummyDataObject::Attr::Optional);
dummies.emplace_back("shape"s, std::move(shape));
return SpecificCall{
SpecificIntrinsic{"__builtin_c_f_pointer"s,
characteristics::Procedure{std::move(dummies), attrs}},
std::move(arguments)};
} else {
return std::nullopt;
}
}
// Function C_LOC(X) from intrinsic module ISO_C_BINDING (18.2.3.6)
std::optional<SpecificCall> IntrinsicProcTable::Implementation::HandleC_Loc(
ActualArguments &arguments, FoldingContext &context) const {
static const char *const keywords[]{"x", nullptr};
if (CheckAndRearrangeArguments(arguments, context.messages(), keywords)) {
CHECK(arguments.size() == 1);
CheckForCoindexedObject(context.messages(), arguments[0], "c_loc", "x");
const auto *expr{arguments[0].value().UnwrapExpr()};
if (expr &&
!(IsObjectPointer(*expr) ||
(IsVariable(*expr) && GetLastTarget(GetSymbolVector(*expr))))) {
context.messages().Say(arguments[0]->sourceLocation(),
"C_LOC() argument must be a data pointer or target"_err_en_US);
}
if (auto typeAndShape{characteristics::TypeAndShape::Characterize(
arguments[0], context)}) {
if (expr && !IsContiguous(*expr, context).value_or(true)) {
context.messages().Say(arguments[0]->sourceLocation(),
"C_LOC() argument must be contiguous"_err_en_US);
}
if (auto constExtents{AsConstantExtents(context, typeAndShape->shape())};
constExtents && GetSize(*constExtents) == 0) {
context.messages().Say(arguments[0]->sourceLocation(),
"C_LOC() argument may not be a zero-sized array"_err_en_US);
}
if (!(typeAndShape->type().category() != TypeCategory::Derived ||
typeAndShape->type().IsAssumedType() ||
(!typeAndShape->type().IsPolymorphic() &&
CountNonConstantLenParameters(
typeAndShape->type().GetDerivedTypeSpec()) == 0))) {
context.messages().Say(arguments[0]->sourceLocation(),
"C_LOC() argument must have an intrinsic type, assumed type, or non-polymorphic derived type with no non-constant length parameter"_err_en_US);
} else if (typeAndShape->type().knownLength().value_or(1) == 0) {
context.messages().Say(arguments[0]->sourceLocation(),
"C_LOC() argument may not be zero-length character"_err_en_US);
} else if (typeAndShape->type().category() != TypeCategory::Derived &&
!IsInteroperableIntrinsicType(typeAndShape->type()).value_or(true) &&
context.languageFeatures().ShouldWarn(
common::UsageWarning::Interoperability)) {
context.messages().Say(arguments[0]->sourceLocation(),
"C_LOC() argument has non-interoperable intrinsic type, kind, or length"_warn_en_US);
}
characteristics::DummyDataObject ddo{std::move(*typeAndShape)};
ddo.intent = common::Intent::In;
return SpecificCall{
SpecificIntrinsic{"__builtin_c_loc"s,
characteristics::Procedure{
characteristics::FunctionResult{
DynamicType{GetBuiltinDerivedType(
builtinsScope_, "__builtin_c_ptr")}},
characteristics::DummyArguments{
characteristics::DummyArgument{"x"s, std::move(ddo)}},
characteristics::Procedure::Attrs{
characteristics::Procedure::Attr::Pure}}},
std::move(arguments)};
}
}
return std::nullopt;
}
static bool CheckForNonPositiveValues(FoldingContext &context,
const ActualArgument &arg, const std::string &procName,
const std::string &argName) {
bool ok{true};
if (arg.Rank() > 0) {
if (const Expr<SomeType> *expr{arg.UnwrapExpr()}) {
if (const auto *intExpr{std::get_if<Expr<SomeInteger>>(&expr->u)}) {
Fortran::common::visit(
[&](const auto &kindExpr) {
using IntType = typename std::decay_t<decltype(kindExpr)>::Result;
if (const auto *constArray{
UnwrapConstantValue<IntType>(kindExpr)}) {
for (std::size_t j{0}; j < constArray->size(); ++j) {
auto arrayExpr{constArray->values().at(j)};
if (arrayExpr.IsNegative() || arrayExpr.IsZero()) {
ok = false;
context.messages().Say(arg.sourceLocation(),
"'%s=' argument for intrinsic '%s' must contain all positive values"_err_en_US,
argName, procName);
}
}
}
},
intExpr->u);
}
}
} else {
if (auto val{ToInt64(arg.UnwrapExpr())}) {
if (*val <= 0) {
ok = false;
context.messages().Say(arg.sourceLocation(),
"'%s=' argument for intrinsic '%s' must be a positive value, but is %jd"_err_en_US,
argName, procName, static_cast<std::intmax_t>(*val));
}
}
}
return ok;
}
static bool CheckDimAgainstCorank(SpecificCall &call, FoldingContext &context) {
bool ok{true};
if (const auto &coarrayArg{call.arguments[0]}) {
if (const auto &dimArg{call.arguments[1]}) {
if (const auto *symbol{
UnwrapWholeSymbolDataRef(coarrayArg->UnwrapExpr())}) {
const auto corank = symbol->Corank();
if (const auto dimNum{ToInt64(dimArg->UnwrapExpr())}) {
if (dimNum < 1 || dimNum > corank) {
ok = false;
context.messages().Say(dimArg->sourceLocation(),
"DIM=%jd dimension is out of range for coarray with corank %d"_err_en_US,
static_cast<std::intmax_t>(*dimNum), corank);
}
}
}
}
}
return ok;
}
static bool CheckAtomicDefineAndRef(FoldingContext &context,
const std::optional<ActualArgument> &atomArg,
const std::optional<ActualArgument> &valueArg,
const std::optional<ActualArgument> &statArg, const std::string &procName) {
bool sameType{true};
if (valueArg && atomArg) {
// for atomic_define and atomic_ref, 'value' arg must be the same type as
// 'atom', but it doesn't have to be the same kind
if (valueArg->GetType()->category() != atomArg->GetType()->category()) {
sameType = false;
context.messages().Say(valueArg->sourceLocation(),
"'value=' argument to '%s' must have same type as 'atom=', but is '%s'"_err_en_US,
procName, valueArg->GetType()->AsFortran());
}
}
return sameType &&
CheckForCoindexedObject(context.messages(), statArg, procName, "stat");
}
// Applies any semantic checks peculiar to an intrinsic.
// TODO: Move the rest of these checks to Semantics/check-call.cpp.
static bool ApplySpecificChecks(SpecificCall &call, FoldingContext &context) {
bool ok{true};
const std::string &name{call.specificIntrinsic.name};
if (name == "allocated") {
const auto &arg{call.arguments[0]};
if (arg) {
if (const auto *expr{arg->UnwrapExpr()}) {
ok = evaluate::IsAllocatableDesignator(*expr);
}
}
if (!ok) {
context.messages().Say(
arg ? arg->sourceLocation() : context.messages().at(),
"Argument of ALLOCATED() must be an ALLOCATABLE object or component"_err_en_US);
}
} else if (name == "atomic_and" || name == "atomic_or" ||
name == "atomic_xor") {
return CheckForCoindexedObject(
context.messages(), call.arguments[2], name, "stat");
} else if (name == "atomic_cas") {
return CheckForCoindexedObject(
context.messages(), call.arguments[4], name, "stat");
} else if (name == "atomic_define") {
return CheckAtomicDefineAndRef(
context, call.arguments[0], call.arguments[1], call.arguments[2], name);
} else if (name == "atomic_fetch_add" || name == "atomic_fetch_and" ||
name == "atomic_fetch_or" || name == "atomic_fetch_xor") {
return CheckForCoindexedObject(
context.messages(), call.arguments[3], name, "stat");
} else if (name == "atomic_ref") {
return CheckAtomicDefineAndRef(
context, call.arguments[1], call.arguments[0], call.arguments[2], name);
} else if (name == "co_broadcast" || name == "co_max" || name == "co_min" ||
name == "co_sum") {
bool aOk{CheckForCoindexedObject(
context.messages(), call.arguments[0], name, "a")};
bool statOk{CheckForCoindexedObject(
context.messages(), call.arguments[2], name, "stat")};
bool errmsgOk{CheckForCoindexedObject(
context.messages(), call.arguments[3], name, "errmsg")};
ok = aOk && statOk && errmsgOk;
} else if (name == "image_status") {
if (const auto &arg{call.arguments[0]}) {
ok = CheckForNonPositiveValues(context, *arg, name, "image");
}
} else if (name == "lcobound") {
return CheckDimAgainstCorank(call, context);
} else if (name == "loc") {
const auto &arg{call.arguments[0]};
ok =
arg && (arg->GetAssumedTypeDummy() || GetLastSymbol(arg->UnwrapExpr()));
if (!ok) {
context.messages().Say(
arg ? arg->sourceLocation() : context.messages().at(),
"Argument of LOC() must be an object or procedure"_err_en_US);
}
} else if (name == "ucobound") {
return CheckDimAgainstCorank(call, context);
}
return ok;
}
static DynamicType GetReturnType(const SpecificIntrinsicInterface &interface,
const common::IntrinsicTypeDefaultKinds &defaults) {
TypeCategory category{TypeCategory::Integer};
switch (interface.result.kindCode) {
case KindCode::defaultIntegerKind:
break;
case KindCode::doublePrecision:
case KindCode::defaultRealKind:
category = TypeCategory::Real;
break;
default:
CRASH_NO_CASE;
}
int kind{interface.result.kindCode == KindCode::doublePrecision
? defaults.doublePrecisionKind()
: defaults.GetDefaultKind(category)};
return DynamicType{category, kind};
}
// Probe the configured intrinsic procedure pattern tables in search of a
// match for a given procedure reference.
std::optional<SpecificCall> IntrinsicProcTable::Implementation::Probe(
const CallCharacteristics &call, ActualArguments &arguments,
FoldingContext &context) const {
// All special cases handled here before the table probes below must
// also be recognized as special names in IsIntrinsicSubroutine().
if (call.isSubroutineCall) {
if (call.name == "__builtin_c_f_pointer") {
return HandleC_F_Pointer(arguments, context);
} else if (call.name == "random_seed") {
int optionalCount{0};
for (const auto &arg : arguments) {
if (const auto *expr{arg->UnwrapExpr()}) {
optionalCount +=
Fortran::evaluate::MayBePassedAsAbsentOptional(*expr);
}
}
if (arguments.size() - optionalCount > 1) {
context.messages().Say(
"RANDOM_SEED must have either 1 or no arguments"_err_en_US);
}
}
} else { // function
if (call.name == "__builtin_c_loc") {
return HandleC_Loc(arguments, context);
} else if (call.name == "null") {
return HandleNull(arguments, context);
}
}
if (call.isSubroutineCall) {
const std::string &name{ResolveAlias(call.name)};
auto subrRange{subroutines_.equal_range(name)};
for (auto iter{subrRange.first}; iter != subrRange.second; ++iter) {
if (auto specificCall{iter->second->Match(
call, defaults_, arguments, context, builtinsScope_)}) {
ApplySpecificChecks(*specificCall, context);
return specificCall;
}
}
if (IsIntrinsicFunction(call.name) && !IsDualIntrinsic(call.name)) {
context.messages().Say(
"Cannot use intrinsic function '%s' as a subroutine"_err_en_US,
call.name);
}
return std::nullopt;
}
// Helper to avoid emitting errors before it is sure there is no match
parser::Messages localBuffer;
parser::Messages *finalBuffer{context.messages().messages()};
parser::ContextualMessages localMessages{
context.messages().at(), finalBuffer ? &localBuffer : nullptr};
FoldingContext localContext{context, localMessages};
auto matchOrBufferMessages{
[&](const IntrinsicInterface &intrinsic,
parser::Messages &buffer) -> std::optional<SpecificCall> {
if (auto specificCall{intrinsic.Match(
call, defaults_, arguments, localContext, builtinsScope_)}) {
if (finalBuffer) {
finalBuffer->Annex(std::move(localBuffer));
}
return specificCall;
} else if (buffer.empty()) {
buffer.Annex(std::move(localBuffer));
} else {
// When there are multiple entries in the table for an
// intrinsic that has multiple forms depending on the
// presence of DIM=, use messages from a later entry if
// the messages from an earlier entry complain about the
// DIM= argument and it wasn't specified with a keyword.
for (const auto &m : buffer.messages()) {
if (m.ToString().find("'dim='") != std::string::npos) {
bool hadDimKeyword{false};
for (const auto &a : arguments) {
if (a) {
if (auto kw{a->keyword()}; kw && kw == "dim") {
hadDimKeyword = true;
break;
}
}
}
if (!hadDimKeyword) {
buffer = std::move(localBuffer);
}
break;
}
}
localBuffer.clear();
}
return std::nullopt;
}};
// Probe the generic intrinsic function table first; allow for
// the use of a legacy alias.
parser::Messages genericBuffer;
const std::string &name{ResolveAlias(call.name)};
auto genericRange{genericFuncs_.equal_range(name)};
for (auto iter{genericRange.first}; iter != genericRange.second; ++iter) {
if (auto specificCall{
matchOrBufferMessages(*iter->second, genericBuffer)}) {
ApplySpecificChecks(*specificCall, context);
return specificCall;
}
}
// Probe the specific intrinsic function table next.
parser::Messages specificBuffer;
auto specificRange{specificFuncs_.equal_range(call.name)};
for (auto specIter{specificRange.first}; specIter != specificRange.second;
++specIter) {
// We only need to check the cases with distinct generic names.
if (const char *genericName{specIter->second->generic}) {
if (auto specificCall{
matchOrBufferMessages(*specIter->second, specificBuffer)}) {
if (!specIter->second->useGenericAndForceResultType) {
specificCall->specificIntrinsic.name = genericName;
}
specificCall->specificIntrinsic.isRestrictedSpecific =
specIter->second->isRestrictedSpecific;
// TODO test feature AdditionalIntrinsics, warn on nonstandard
// specifics with DoublePrecisionComplex arguments.
return specificCall;
}
}
}
// If there was no exact match with a specific, try to match the related
// generic and convert the result to the specific required type.
if (context.languageFeatures().IsEnabled(common::LanguageFeature::
UseGenericIntrinsicWhenSpecificDoesntMatch)) {
for (auto specIter{specificRange.first}; specIter != specificRange.second;
++specIter) {
// We only need to check the cases with distinct generic names.
if (const char *genericName{specIter->second->generic}) {
if (specIter->second->useGenericAndForceResultType) {
auto genericRange{genericFuncs_.equal_range(genericName)};
for (auto genIter{genericRange.first}; genIter != genericRange.second;
++genIter) {
if (auto specificCall{
matchOrBufferMessages(*genIter->second, specificBuffer)}) {
// Force the call result type to the specific intrinsic result
// type, if possible.
DynamicType genericType{
DEREF(specificCall->specificIntrinsic.characteristics.value()
.functionResult.value()
.GetTypeAndShape())
.type()};
DynamicType newType{GetReturnType(*specIter->second, defaults_)};
if (genericType.category() == newType.category() ||
((genericType.category() == TypeCategory::Integer ||
genericType.category() == TypeCategory::Real) &&
(newType.category() == TypeCategory::Integer ||
newType.category() == TypeCategory::Real))) {
if (context.languageFeatures().ShouldWarn(
common::LanguageFeature::
UseGenericIntrinsicWhenSpecificDoesntMatch)) {
context.messages().Say(
"Argument types do not match specific intrinsic '%s' requirements; using '%s' generic instead and converting the result to %s if needed"_port_en_US,
call.name, genericName, newType.AsFortran());
}
specificCall->specificIntrinsic.name = call.name;
specificCall->specificIntrinsic.characteristics.value()
.functionResult.value()
.SetType(newType);
return specificCall;
}
}
}
}
}
}
}
if (specificBuffer.empty() && genericBuffer.empty() &&
IsIntrinsicSubroutine(call.name) && !IsDualIntrinsic(call.name)) {
context.messages().Say(
"Cannot use intrinsic subroutine '%s' as a function"_err_en_US,
call.name);
}
// No match; report the right errors, if any
if (finalBuffer) {
if (specificBuffer.empty()) {
finalBuffer->Annex(std::move(genericBuffer));
} else {
finalBuffer->Annex(std::move(specificBuffer));
}
}
return std::nullopt;
}
std::optional<SpecificIntrinsicFunctionInterface>
IntrinsicProcTable::Implementation::IsSpecificIntrinsicFunction(
const std::string &name) const {
auto specificRange{specificFuncs_.equal_range(name)};
for (auto iter{specificRange.first}; iter != specificRange.second; ++iter) {
const SpecificIntrinsicInterface &specific{*iter->second};
std::string genericName{name};
if (specific.generic) {
genericName = std::string(specific.generic);
}
characteristics::FunctionResult fResult{GetSpecificType(specific.result)};
characteristics::DummyArguments args;
int dummies{specific.CountArguments()};
for (int j{0}; j < dummies; ++j) {
characteristics::DummyDataObject dummy{
GetSpecificType(specific.dummy[j].typePattern)};
dummy.intent = specific.dummy[j].intent;
args.emplace_back(
std::string{specific.dummy[j].keyword}, std::move(dummy));
}
characteristics::Procedure::Attrs attrs;
attrs.set(characteristics::Procedure::Attr::Pure)
.set(characteristics::Procedure::Attr::Elemental);
characteristics::Procedure chars{
std::move(fResult), std::move(args), attrs};
return SpecificIntrinsicFunctionInterface{
std::move(chars), genericName, specific.isRestrictedSpecific};
}
return std::nullopt;
}
DynamicType IntrinsicProcTable::Implementation::GetSpecificType(
const TypePattern &pattern) const {
const CategorySet &set{pattern.categorySet};
CHECK(set.count() == 1);
TypeCategory category{set.LeastElement().value()};
if (pattern.kindCode == KindCode::doublePrecision) {
return DynamicType{category, defaults_.doublePrecisionKind()};
} else if (category == TypeCategory::Character) {
// All character arguments to specific intrinsic functions are
// assumed-length.
return DynamicType{defaults_.GetDefaultKind(category), assumedLen_};
} else {
return DynamicType{category, defaults_.GetDefaultKind(category)};
}
}
IntrinsicProcTable::~IntrinsicProcTable() = default;
IntrinsicProcTable IntrinsicProcTable::Configure(
const common::IntrinsicTypeDefaultKinds &defaults) {
IntrinsicProcTable result;
result.impl_ = std::make_unique<IntrinsicProcTable::Implementation>(defaults);
return result;
}
void IntrinsicProcTable::SupplyBuiltins(
const semantics::Scope &builtins) const {
DEREF(impl_.get()).SupplyBuiltins(builtins);
}
bool IntrinsicProcTable::IsIntrinsic(const std::string &name) const {
return DEREF(impl_.get()).IsIntrinsic(name);
}
bool IntrinsicProcTable::IsIntrinsicFunction(const std::string &name) const {
return DEREF(impl_.get()).IsIntrinsicFunction(name);
}
bool IntrinsicProcTable::IsIntrinsicSubroutine(const std::string &name) const {
return DEREF(impl_.get()).IsIntrinsicSubroutine(name);
}
IntrinsicClass IntrinsicProcTable::GetIntrinsicClass(
const std::string &name) const {
return DEREF(impl_.get()).GetIntrinsicClass(name);
}
std::string IntrinsicProcTable::GetGenericIntrinsicName(
const std::string &name) const {
return DEREF(impl_.get()).GetGenericIntrinsicName(name);
}
std::optional<SpecificCall> IntrinsicProcTable::Probe(
const CallCharacteristics &call, ActualArguments &arguments,
FoldingContext &context) const {
return DEREF(impl_.get()).Probe(call, arguments, context);
}
std::optional<SpecificIntrinsicFunctionInterface>
IntrinsicProcTable::IsSpecificIntrinsicFunction(const std::string &name) const {
return DEREF(impl_.get()).IsSpecificIntrinsicFunction(name);
}
llvm::raw_ostream &TypePattern::Dump(llvm::raw_ostream &o) const {
if (categorySet == AnyType) {
o << "any type";
} else {
const char *sep = "";
auto set{categorySet};
while (auto least{set.LeastElement()}) {
o << sep << EnumToString(*least);
sep = " or ";
set.reset(*least);
}
}
o << '(' << EnumToString(kindCode) << ')';
return o;
}
llvm::raw_ostream &IntrinsicDummyArgument::Dump(llvm::raw_ostream &o) const {
if (keyword) {
o << keyword << '=';
}
return typePattern.Dump(o)
<< ' ' << EnumToString(rank) << ' ' << EnumToString(optionality)
<< EnumToString(intent);
}
llvm::raw_ostream &IntrinsicInterface::Dump(llvm::raw_ostream &o) const {
o << name;
char sep{'('};
for (const auto &d : dummy) {
if (d.typePattern.kindCode == KindCode::none) {
break;
}
d.Dump(o << sep);
sep = ',';
}
if (sep == '(') {
o << "()";
}
return result.Dump(o << " -> ") << ' ' << EnumToString(rank);
}
llvm::raw_ostream &IntrinsicProcTable::Implementation::Dump(
llvm::raw_ostream &o) const {
o << "generic intrinsic functions:\n";
for (const auto &iter : genericFuncs_) {
iter.second->Dump(o << iter.first << ": ") << '\n';
}
o << "specific intrinsic functions:\n";
for (const auto &iter : specificFuncs_) {
iter.second->Dump(o << iter.first << ": ");
if (const char *g{iter.second->generic}) {
o << " -> " << g;
}
o << '\n';
}
o << "subroutines:\n";
for (const auto &iter : subroutines_) {
iter.second->Dump(o << iter.first << ": ") << '\n';
}
return o;
}
llvm::raw_ostream &IntrinsicProcTable::Dump(llvm::raw_ostream &o) const {
return DEREF(impl_.get()).Dump(o);
}
// In general C846 prohibits allocatable coarrays to be passed to INTENT(OUT)
// dummy arguments. This rule does not apply to intrinsics in general.
// Some intrinsic explicitly allow coarray allocatable in their description.
// It is assumed that unless explicitly allowed for an intrinsic,
// this is forbidden.
// Since there are very few intrinsic identified that allow this, they are
// listed here instead of adding a field in the table.
bool AcceptsIntentOutAllocatableCoarray(const std::string &intrinsic) {
return intrinsic == "move_alloc";
}
} // namespace Fortran::evaluate