diff --git a/flang/docs/Extensions.md b/flang/docs/Extensions.md index 8b84045d20116..5e3d4dd092844 100644 --- a/flang/docs/Extensions.md +++ b/flang/docs/Extensions.md @@ -223,6 +223,9 @@ end as if they were comment lines, even if not begun with `!`. * Commas are required in FORMAT statements and character variables only when they prevent ambiguity. +* Legacy names `AND`, `OR`, and `XOR` are accepted as aliases for + the standard intrinsic functions `IAND`, `IOR`, and `IEOR` + respectively. ### Extensions supported when enabled by options diff --git a/flang/lib/Evaluate/intrinsics.cpp b/flang/lib/Evaluate/intrinsics.cpp index dff34f491a080..42b1a89da9183 100644 --- a/flang/lib/Evaluate/intrinsics.cpp +++ b/flang/lib/Evaluate/intrinsics.cpp @@ -793,16 +793,11 @@ static const IntrinsicInterface genericIntrinsicFunction[]{ DefaultingKIND}, KINDInt}, {"__builtin_ieee_is_nan", {{"a", AnyFloating}}, DefaultLogical}, - {"__builtin_ieee_is_normal", {{"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_selected_real_kind", // alias for selected_real_kind - {{"p", AnyInt, Rank::scalar}, - {"r", AnyInt, Rank::scalar, Optionality::optional}, - {"radix", AnyInt, Rank::scalar, Optionality::optional}}, - DefaultInt, Rank::scalar, IntrinsicClass::transformationalFunction}, {"__builtin_ieee_support_datatype", {{"x", AnyReal, Rank::elemental, Optionality::optional}}, DefaultLogical}, @@ -839,7 +834,7 @@ static const IntrinsicInterface genericIntrinsicFunction[]{ // LCOBOUND, UCOBOUND, FAILED_IMAGES, IMAGE_INDEX, // STOPPED_IMAGES, COSHAPE // TODO: Non-standard intrinsic functions -// AND, OR, XOR, LSHIFT, RSHIFT, SHIFT, ZEXT, IZEXT, +// LSHIFT, RSHIFT, SHIFT, ZEXT, IZEXT, // COMPL, EQV, NEQV, INT8, JINT, JNINT, KNINT, // QCMPLX, QEXT, QFLOAT, QREAL, DNUM, // INUM, JNUM, KNUM, QNUM, RNUM, RAN, RANF, ILEN, @@ -851,6 +846,15 @@ static const IntrinsicInterface genericIntrinsicFunction[]{ // 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 genericAlias[]{ + {"and", "iand"}, + {"or", "ior"}, + {"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, @@ -1897,6 +1901,10 @@ class IntrinsicProcTable::Implementation { for (const IntrinsicInterface &f : genericIntrinsicFunction) { genericFuncs_.insert(std::make_pair(std::string{f.name}, &f)); } + for (const std::pair &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)); } @@ -1929,16 +1937,22 @@ class IntrinsicProcTable::Implementation { SpecificCall HandleNull(ActualArguments &, FoldingContext &) const; std::optional HandleC_F_Pointer( 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 genericFuncs_; std::multimap specificFuncs_; std::multimap subroutines_; const semantics::Scope *builtinsScope_{nullptr}; + std::map aliases_; }; bool IntrinsicProcTable::Implementation::IsIntrinsicFunction( - const std::string &name) const { + const std::string &name0) const { + const std::string &name{ResolveAlias(name0)}; auto specificRange{specificFuncs_.equal_range(name)}; if (specificRange.first != specificRange.second) { return true; @@ -2427,9 +2441,11 @@ std::optional IntrinsicProcTable::Implementation::Probe( return std::nullopt; }}; - // Probe the generic intrinsic function table first. + // Probe the generic intrinsic function table first; allow for + // the use of a legacy alias. parser::Messages genericBuffer; - auto genericRange{genericFuncs_.equal_range(call.name)}; + 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)}) { diff --git a/flang/test/Semantics/boz-literal-constants.f90 b/flang/test/Semantics/boz-literal-constants.f90 index ca1676a696461..7c4e5928ca517 100644 --- a/flang/test/Semantics/boz-literal-constants.f90 +++ b/flang/test/Semantics/boz-literal-constants.f90 @@ -36,6 +36,7 @@ subroutine explicit(n, x, c) ! B) Argument to intrinsics listed from 16.9 below ! BGE, BGT, BLE, BLT, CMPLX, DBLE, DSHIFTL, ! DSHIFTR, IAND, IEOR, INT, IOR, MERGE_BITS, REAL + ! and legacy aliases AND, OR, XOR ! part A data f / Z"AA" / ! OK @@ -63,16 +64,25 @@ subroutine explicit(n, x, c) !ERROR: Typeless (BOZ) not allowed for both 'i=' & 'j=' arguments resint = IAND(B"0001", B"0011") resint = IAND(B"0001", 3) + !ERROR: Typeless (BOZ) not allowed for both 'i=' & 'j=' arguments + resint = AND(B"0001", B"0011") + resint = AND(B"0001", 3) !ERROR: Typeless (BOZ) not allowed for both 'i=' & 'j=' arguments resint = IEOR(B"0001", B"0011") resint = IEOR(B"0001", 3) + !ERROR: Typeless (BOZ) not allowed for both 'i=' & 'j=' arguments + resint = XOR(B"0001", B"0011") + resint = XOR(B"0001", 3) resint = INT(B"1010") !ERROR: Typeless (BOZ) not allowed for both 'i=' & 'j=' arguments res = IOR(B"0101", B"0011") res = IOR(B"0101", 3) + !ERROR: Typeless (BOZ) not allowed for both 'i=' & 'j=' arguments + res = OR(B"0101", B"0011") + res = OR(B"0101", 3) res = MERGE_BITS(13,3,11) res = MERGE_BITS(B"1101",3,11)