[flang-commits] [flang] [Flang] KIND De-Templatization (PR #206907)
Michael Kruse via flang-commits
flang-commits at lists.llvm.org
Tue Aug 11 00:59:35 PDT 2026
https://github.com/Meinersbur updated https://github.com/llvm/llvm-project/pull/206907
>From 9770edc6bbd7b07b0ccc9885624cc2e83cbc3eb8 Mon Sep 17 00:00:00 2001
From: Michael Kruse <llvm-project at meinersbur.de>
Date: Tue, 11 Aug 2026 02:29:10 +0200
Subject: [PATCH 1/2] [Flang] KIND De-templatization
---
flang/CMakeLists.txt | 1 +
flang/include/flang/Common/template.h | 32 -
flang/include/flang/Evaluate/call.h | 25 +-
.../include/flang/Evaluate/character-value.h | 196 ++++++
.../include/flang/Evaluate/characteristics.h | 3 +-
flang/include/flang/Evaluate/common.h | 7 +
flang/include/flang/Evaluate/complex-value.h | 166 +++++
flang/include/flang/Evaluate/complex.h | 114 ----
flang/include/flang/Evaluate/constant.h | 108 +++-
flang/include/flang/Evaluate/expression.h | 410 ++++++++----
.../include/flang/Evaluate/fold-designator.h | 10 +-
flang/include/flang/Evaluate/fold.h | 29 +-
flang/include/flang/Evaluate/initial-image.h | 43 +-
flang/include/flang/Evaluate/integer-value.h | 318 +++++++++
flang/include/flang/Evaluate/logical-value.h | 153 +++++
flang/include/flang/Evaluate/logical.h | 110 ----
flang/include/flang/Evaluate/match.h | 17 +-
flang/include/flang/Evaluate/object-sizes.h | 81 +++
flang/include/flang/Evaluate/real-value.h | 237 +++++++
flang/include/flang/Evaluate/rewrite.h | 19 +-
flang/include/flang/Evaluate/shape.h | 22 +-
flang/include/flang/Evaluate/static-data.h | 1 +
flang/include/flang/Evaluate/tools.h | 234 ++++---
flang/include/flang/Evaluate/type.h | 261 +++++---
flang/include/flang/Evaluate/variable.h | 52 +-
flang/include/flang/Lower/DirectivesCommon.h | 19 +-
flang/include/flang/Lower/Mangler.h | 19 +-
flang/include/flang/Lower/Support/Utils.h | 16 +-
flang/include/flang/Semantics/dump-expr.h | 6 +-
flang/include/flang/Semantics/scope.h | 3 +-
flang/include/flang/Semantics/type.h | 2 +
flang/lib/Evaluate/CMakeLists.txt | 17 +-
flang/lib/Evaluate/call.cpp | 3 +-
flang/lib/Evaluate/character-value-impl.cpp | 577 +++++++++++++++++
flang/lib/Evaluate/character-value-impl.h | 229 +++++++
flang/lib/Evaluate/character-value.cpp | 215 ++++++
flang/lib/Evaluate/character.h | 40 +-
flang/lib/Evaluate/characteristics.cpp | 8 +-
flang/lib/Evaluate/check-expression.cpp | 30 +-
flang/lib/Evaluate/common.cpp | 6 +
flang/lib/Evaluate/complex-value.cpp | 182 ++++++
flang/lib/Evaluate/complex.cpp | 136 ----
flang/lib/Evaluate/constant.cpp | 96 +--
flang/lib/Evaluate/expression.cpp | 107 +--
flang/lib/Evaluate/fold-character.cpp | 83 +--
flang/lib/Evaluate/fold-complex.cpp | 37 +-
flang/lib/Evaluate/fold-designator.cpp | 26 +-
flang/lib/Evaluate/fold-implementation.h | 593 ++++++++++-------
flang/lib/Evaluate/fold-integer.cpp | 610 ++++++++++--------
flang/lib/Evaluate/fold-logical.cpp | 558 ++++++----------
flang/lib/Evaluate/fold-matmul.h | 11 +-
flang/lib/Evaluate/fold-real.cpp | 169 ++---
flang/lib/Evaluate/fold-reduction.cpp | 5 +-
flang/lib/Evaluate/fold-reduction.h | 99 +--
flang/lib/Evaluate/fold.cpp | 8 +-
flang/lib/Evaluate/formatting.cpp | 71 +-
flang/lib/Evaluate/host.h | 101 ++-
flang/lib/Evaluate/initial-image.cpp | 84 +--
flang/lib/Evaluate/int-power.h | 10 +-
flang/lib/Evaluate/integer-value-impl.cpp | 583 +++++++++++++++++
flang/lib/Evaluate/integer-value-impl.h | 308 +++++++++
flang/lib/Evaluate/integer-value.cpp | 304 +++++++++
flang/lib/Evaluate/intrinsics-library.cpp | 16 +-
flang/lib/Evaluate/logical-value.cpp | 25 +
flang/lib/Evaluate/logical.cpp | 17 -
flang/lib/Evaluate/real-value-impl.cpp | 538 +++++++++++++++
flang/lib/Evaluate/real-value-impl.h | 266 ++++++++
flang/lib/Evaluate/real-value.cpp | 264 ++++++++
flang/lib/Evaluate/shape.cpp | 94 +--
flang/lib/Evaluate/static-data.cpp | 6 +
flang/lib/Evaluate/target.cpp | 38 +-
flang/lib/Evaluate/tools.cpp | 371 +++++------
flang/lib/Evaluate/type.cpp | 26 +-
flang/lib/Evaluate/variable.cpp | 39 +-
flang/lib/Lower/Bridge.cpp | 3 +-
flang/lib/Lower/CallInterface.cpp | 5 +-
flang/lib/Lower/ConvertArrayConstructor.cpp | 36 +-
flang/lib/Lower/ConvertConstant.cpp | 187 +++---
flang/lib/Lower/ConvertExprToHLFIR.cpp | 211 +++---
flang/lib/Lower/ConvertType.cpp | 38 +-
flang/lib/Lower/OpenMP/OpenMP.cpp | 4 +-
flang/lib/Lower/Support/Utils.cpp | 231 +++----
flang/lib/Semantics/check-call.cpp | 2 +-
flang/lib/Semantics/check-case.cpp | 40 +-
flang/lib/Semantics/check-coarray.cpp | 2 +-
flang/lib/Semantics/check-data.cpp | 2 +-
flang/lib/Semantics/check-io.h | 7 +-
flang/lib/Semantics/check-omp-atomic.cpp | 29 +-
flang/lib/Semantics/check-omp-structure.cpp | 2 +-
flang/lib/Semantics/data-to-inits.cpp | 4 +-
flang/lib/Semantics/dump-expr.cpp | 9 +-
flang/lib/Semantics/expression.cpp | 173 ++---
flang/lib/Semantics/openmp-utils.cpp | 4 +-
flang/lib/Semantics/pointer-assignment.cpp | 4 +-
flang/lib/Semantics/resolve-names-utils.cpp | 6 +-
flang/lib/Semantics/resolve-names.cpp | 39 +-
flang/lib/Semantics/runtime-type-info.cpp | 86 +--
flang/lib/Semantics/scope.cpp | 9 +-
flang/lib/Semantics/semantics.cpp | 4 +-
flang/lib/Semantics/type.cpp | 20 +-
flang/test/Evaluate/fold-ibits.f90 | 29 +
.../test/Evaluate/fold-real-storage-size.f90 | 24 +
.../Evaluate/fold-real10-storage-size.f90 | 41 ++
flang/test/Evaluate/fold-transfer-partial.f90 | 71 ++
flang/test/Lower/constant-literal-kinds.f90 | 63 ++
flang/tools/CMakeLists.txt | 1 +
flang/tools/object-size-probe/CMakeLists.txt | 42 ++
.../object-size-probe/object-size-probe.cpp | 99 +++
flang/unittests/CMakeLists.txt | 8 +
flang/unittests/Evaluate/expression.cpp | 23 +-
flang/unittests/Evaluate/folding.cpp | 27 +-
flang/unittests/Evaluate/intrinsics.cpp | 204 +++---
flang/unittests/Evaluate/logical.cpp | 57 +-
flang/unittests/Evaluate/real.cpp | 17 +-
114 files changed, 8403 insertions(+), 3180 deletions(-)
create mode 100644 flang/include/flang/Evaluate/character-value.h
create mode 100644 flang/include/flang/Evaluate/complex-value.h
delete mode 100644 flang/include/flang/Evaluate/complex.h
create mode 100644 flang/include/flang/Evaluate/integer-value.h
create mode 100644 flang/include/flang/Evaluate/logical-value.h
delete mode 100644 flang/include/flang/Evaluate/logical.h
create mode 100644 flang/include/flang/Evaluate/object-sizes.h
create mode 100644 flang/include/flang/Evaluate/real-value.h
create mode 100644 flang/lib/Evaluate/character-value-impl.cpp
create mode 100644 flang/lib/Evaluate/character-value-impl.h
create mode 100644 flang/lib/Evaluate/character-value.cpp
create mode 100644 flang/lib/Evaluate/complex-value.cpp
delete mode 100644 flang/lib/Evaluate/complex.cpp
create mode 100644 flang/lib/Evaluate/integer-value-impl.cpp
create mode 100644 flang/lib/Evaluate/integer-value-impl.h
create mode 100644 flang/lib/Evaluate/integer-value.cpp
create mode 100644 flang/lib/Evaluate/logical-value.cpp
delete mode 100644 flang/lib/Evaluate/logical.cpp
create mode 100644 flang/lib/Evaluate/real-value-impl.cpp
create mode 100644 flang/lib/Evaluate/real-value-impl.h
create mode 100644 flang/lib/Evaluate/real-value.cpp
create mode 100644 flang/test/Evaluate/fold-real-storage-size.f90
create mode 100644 flang/test/Evaluate/fold-real10-storage-size.f90
create mode 100644 flang/test/Evaluate/fold-transfer-partial.f90
create mode 100644 flang/test/Lower/constant-literal-kinds.f90
create mode 100644 flang/tools/object-size-probe/CMakeLists.txt
create mode 100644 flang/tools/object-size-probe/object-size-probe.cpp
diff --git a/flang/CMakeLists.txt b/flang/CMakeLists.txt
index 0c5a690f8712f..5a3b7c1d378be 100644
--- a/flang/CMakeLists.txt
+++ b/flang/CMakeLists.txt
@@ -301,6 +301,7 @@ endif()
set(LLVM_BUILD_TOOLS ON)
include_directories(BEFORE
+ ${FLANG_BINARY_DIR}/include/object-sizes/$<CONFIG>
${FLANG_BINARY_DIR}/include
${FLANG_SOURCE_DIR}/include)
diff --git a/flang/include/flang/Common/template.h b/flang/include/flang/Common/template.h
index 6501994133759..cf7ddd02cbfa5 100644
--- a/flang/include/flang/Common/template.h
+++ b/flang/include/flang/Common/template.h
@@ -289,37 +289,5 @@ std::optional<R> MapOptional(R (*f)(A &&...), std::optional<A> &&...x) {
return MapOptional(std::function<R(A && ...)>{f}, std::move(x)...);
}
-// Given a VISITOR class of the general form
-// struct VISITOR {
-// using Result = ...;
-// using Types = std::tuple<...>;
-// template<typename T> Result Test() { ... }
-// };
-// SearchTypes will traverse the element types in the tuple in order
-// and invoke VISITOR::Test<T>() on each until it returns a value that
-// casts to true. If no invocation of Test succeeds, SearchTypes will
-// return a default value.
-template <std::size_t J, typename VISITOR>
-common::IfNoLvalue<typename VISITOR::Result, VISITOR> SearchTypesHelper(
- VISITOR &&visitor, typename VISITOR::Result &&defaultResult) {
- using Tuple = typename VISITOR::Types;
- if constexpr (J < std::tuple_size_v<Tuple>) {
- if (auto result{visitor.template Test<std::tuple_element_t<J, Tuple>>()}) {
- return result;
- }
- return SearchTypesHelper<J + 1, VISITOR>(
- std::move(visitor), std::move(defaultResult));
- } else {
- return std::move(defaultResult);
- }
-}
-
-template <typename VISITOR>
-common::IfNoLvalue<typename VISITOR::Result, VISITOR> SearchTypes(
- VISITOR &&visitor,
- typename VISITOR::Result defaultResult = typename VISITOR::Result{}) {
- return SearchTypesHelper<0, VISITOR>(
- std::move(visitor), std::move(defaultResult));
-}
} // namespace Fortran::common
#endif // FORTRAN_COMMON_TEMPLATE_H_
diff --git a/flang/include/flang/Evaluate/call.h b/flang/include/flang/Evaluate/call.h
index f04ec6c373c88..5001fbc85920e 100644
--- a/flang/include/flang/Evaluate/call.h
+++ b/flang/include/flang/Evaluate/call.h
@@ -296,6 +296,11 @@ struct SpecificIntrinsic {
};
struct ProcedureDesignator {
+ static int kind() {
+ llvm_unreachable("This class has no kind");
+ return 0;
+ }
+
EVALUATE_UNION_CLASS_BOILERPLATE(ProcedureDesignator)
explicit ProcedureDesignator(SpecificIntrinsic &&i) : u{std::move(i)} {}
explicit ProcedureDesignator(const Symbol &n) : u{n} {}
@@ -333,6 +338,11 @@ using Chevrons = std::vector<Expr<SomeType>>;
class ProcedureRef {
public:
+ static int kind() {
+ llvm_unreachable("This class has no kind");
+ return 0;
+ }
+
CLASS_BOILERPLATE(ProcedureRef)
ProcedureRef(ProcedureDesignator &&p, ActualArguments &&a,
bool hasAlternateReturns = false)
@@ -393,15 +403,19 @@ class ProcedureRef {
template <typename A> class FunctionRef : public ProcedureRef {
public:
+ constexpr int kind() const { return kind_; }
+
using Result = A;
CLASS_BOILERPLATE(FunctionRef)
- explicit FunctionRef(ProcedureRef &&pr) : ProcedureRef{std::move(pr)} {}
- FunctionRef(ProcedureDesignator &&p, ActualArguments &&a)
- : ProcedureRef{std::move(p), std::move(a)} {}
+
+ explicit FunctionRef(int kind, ProcedureRef &&pr)
+ : ProcedureRef{std::move(pr)}, kind_{kind} {}
+ FunctionRef(int kind, ProcedureDesignator &&p, ActualArguments &&a)
+ : ProcedureRef{std::move(p), std::move(a)}, kind_{kind} {}
std::optional<DynamicType> GetType() const {
if constexpr (IsLengthlessIntrinsicType<A>) {
- return A::GetType();
+ return DynamicType{A::category, kind_};
} else if (auto type{proc_.GetType()}) {
// TODO: Non constant explicit length parameters of PDTs result should
// likely be dropped too. This is not as easy as for characters since some
@@ -413,6 +427,9 @@ template <typename A> class FunctionRef : public ProcedureRef {
return std::nullopt;
}
}
+
+private:
+ int kind_;
};
} // namespace Fortran::evaluate
#endif // FORTRAN_EVALUATE_CALL_H_
diff --git a/flang/include/flang/Evaluate/character-value.h b/flang/include/flang/Evaluate/character-value.h
new file mode 100644
index 0000000000000..aff74cf8a5cb1
--- /dev/null
+++ b/flang/include/flang/Evaluate/character-value.h
@@ -0,0 +1,196 @@
+//===-- include/flang/Evaluate/character-value.h ----------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef FORTRAN_EVALUATE_CHAR_VALUE_H_
+#define FORTRAN_EVALUATE_CHAR_VALUE_H_
+
+#include "flang/Evaluate/common.h"
+#include "flang/Evaluate/object-sizes.h"
+#include <cstddef>
+#include <iosfwd>
+#include <optional>
+#include <string>
+
+namespace Fortran::evaluate::value {
+class CharacterValueImpl;
+
+/// A character string with dynamic character representation with
+/// std::basic_string-like API.
+///
+/// The character type is dynamic between char, char16_t, and char32_t. As being
+/// able to represent all values, char32_t is used when passing single
+/// characters. It is also kind-aware, i.e. knows which CHARACTER kind it
+/// currently represents.
+///
+/// The implementation is hidden from this header using a pImpl-like idiom.
+class CharacterValue {
+public:
+ /// A default-initialized CharacterValue is in a so-called "monostate"; it
+ /// represents an empty string, but its kind is not yet known. Not all
+ /// operations are supported in this state.
+ CharacterValue();
+
+ // rule-of-five
+ ~CharacterValue();
+ CharacterValue(const CharacterValue &);
+ CharacterValue(CharacterValue &&);
+ CharacterValue &operator=(const CharacterValue &);
+ CharacterValue &operator=(CharacterValue &&);
+
+ // ctors
+ explicit CharacterValue(int kind, std::string s);
+ explicit CharacterValue(int kind, std::u16string s);
+ explicit CharacterValue(int kind, std::u32string s);
+
+ /// Fill constructor: create a string of n copies of the given character.
+ CharacterValue(int kind, std::size_t n, char32_t c);
+
+ // Named ctors
+ static CharacterValue Zero(int kind);
+ static CharacterValue FromRawBytes(
+ int kind, const void *raw, size_t byteSize);
+
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+ LLVM_DUMP_METHOD void dump() const;
+#endif
+
+ /// Whether this object represents a default-initialized value (zero) of
+ /// not-yet-known kind.
+ bool IsMonostate() const;
+
+ /// The kind of the value currently stored.
+ int kind() const;
+
+ bool empty() const;
+ std::size_t size() const;
+ std::size_t length() const { return size(); }
+
+ /// Byte size of one character unit (1, 2, or 4).
+ std::size_t charSize() const { return kind(); }
+
+ /// Number of bytes accessed by FromRawBytes/StoreRawBytes
+ size_t bytesStored() const { return length() * kind(); }
+
+ // Casting to other representations
+ std::optional<llvm::StringRef> AsStringRef() const;
+ std::optional<std::string> AsStdString() const {
+ if (auto str{AsStringRef()}) {
+ return str->str();
+ }
+ return std::nullopt;
+ }
+ std::optional<std::u16string> AsU16String() const;
+ std::optional<std::u32string> AsU32String() const;
+
+ // Comparisons
+ Ordering Compare(const CharacterValue &y) const;
+ bool operator<(const CharacterValue &y) const;
+ bool operator<=(const CharacterValue &y) const { return !(y < *this); }
+ bool operator==(const CharacterValue &y) const;
+ bool operator!=(const CharacterValue &y) const { return !(*this == y); }
+ bool operator>=(const CharacterValue &y) const { return !(*this < y); }
+ bool operator>(const CharacterValue &y) const { return y < *this; }
+
+ CharacterValue ToAscii(int kind) const;
+
+ /// Assign n copies of the given character, fixing the kind from the char
+ /// type.
+ void assign(int kind, std::size_t n, char32_t c);
+
+ /// Assign from a raw character pointer and length.
+ void assign(const char *p, std::size_t n);
+ void assign(const char16_t *p, std::size_t n);
+ void assign(const char32_t *p, std::size_t n);
+
+ /// Erase from position pos to end.
+ void erase(std::size_t pos);
+
+ /// Append n copies of the given character (widened to the stored type).
+ void append(std::size_t n, char32_t c);
+
+ /// Replace the substring [pos, pos+len) with characters from other.
+ CharacterValue &replace(
+ std::size_t pos, std::size_t len, const CharacterValue &other);
+
+ /// Return a suffix starting at pos.
+ CharacterValue substr(std::size_t pos) const;
+
+ /// Return a substring of len characters starting at pos.
+ CharacterValue substr(std::size_t pos, std::size_t len) const;
+
+ /// Reserve storage for at least n characters.
+ void reserve(std::size_t n);
+
+ /// Return the character at position i
+ char32_t operator[](std::size_t i) const;
+
+ /// Concatenate two same-kind strings.
+ CharacterValue operator+(const CharacterValue &y) const;
+
+ /// Append another same-kind string.
+ CharacterValue &operator+=(const CharacterValue &y);
+
+ /// Append a character, converting it to the string's element type.
+ CharacterValue &operator+=(char c);
+
+ /// Sentinel value for "not found" positions (same as std::string::npos).
+ static constexpr std::size_t npos{std::string::npos};
+
+ // Find-family methods; return npos when not found.
+ std::size_t find(const CharacterValue &pattern) const;
+ std::size_t rfind(const CharacterValue &pattern) const;
+ std::size_t find_first_of(const CharacterValue &set) const;
+ std::size_t find_last_of(const CharacterValue &set) const;
+ std::size_t find_first_not_of(char32_t c) const;
+ std::size_t find_last_not_of(char32_t c) const;
+ std::size_t find_first_not_of(const CharacterValue &set) const;
+ std::size_t find_last_not_of(const CharacterValue &set) const;
+
+ /// Raw byte pointer to the underlying character data
+ void *data();
+ const void *data() const;
+
+ /// Like data(), but pre-casted to char
+ char *charData() { return static_cast<char *>(data()); }
+ const char *charData() const { return static_cast<const char *>(data()); }
+
+ void *at(size_t pos) { return &charData()[pos * charSize()]; }
+ const void *at(size_t pos) const { return &charData()[pos * charSize()]; }
+
+ void StoreRawBytes(void *dst, size_t size, bool *changed = nullptr) const;
+
+ template <typename F> decltype(auto) withStdString(F &&f) const {
+ switch (kind()) {
+ case 1:
+ return f(*AsStdString());
+ case 2:
+ return f(*AsU16String());
+ case 4:
+ return f(*AsU32String());
+ default:
+ llvm_unreachable("unsupported KIND");
+ }
+ }
+
+private:
+ static CharacterValue FromImpl(const CharacterValueImpl &y);
+ static CharacterValue FromImpl(CharacterValueImpl &&y);
+
+ CharacterValueImpl &impl() {
+ return *reinterpret_cast<CharacterValueImpl *>(this);
+ }
+ const CharacterValueImpl &impl() const {
+ return *reinterpret_cast<const CharacterValueImpl *>(this);
+ }
+
+ [[maybe_unused]] alignas(
+ detail::kCharacterObjectAlign) char opaque_[detail::kCharacterObjectSize];
+};
+
+} // namespace Fortran::evaluate::value
+#endif // FORTRAN_EVALUATE_CHAR_VALUE_H_
diff --git a/flang/include/flang/Evaluate/characteristics.h b/flang/include/flang/Evaluate/characteristics.h
index 1ba0bf693c763..492a128ab10f1 100644
--- a/flang/include/flang/Evaluate/characteristics.h
+++ b/flang/include/flang/Evaluate/characteristics.h
@@ -118,9 +118,8 @@ class TypeAndShape {
}
// Specialization for character designators
- template <int KIND>
static std::optional<TypeAndShape> Characterize(
- const Designator<Type<TypeCategory::Character, KIND>> &x,
+ const Designator<Type<TypeCategory::Character>> &x,
FoldingContext &context, bool invariantOnly = true) {
const auto *symbol{UnwrapWholeSymbolOrComponentDataRef(x)};
if (symbol && !symbol->owner().IsDerivedType()) { // Whole variable
diff --git a/flang/include/flang/Evaluate/common.h b/flang/include/flang/Evaluate/common.h
index 6adf395442edf..7368a424b46b1 100644
--- a/flang/include/flang/Evaluate/common.h
+++ b/flang/include/flang/Evaluate/common.h
@@ -34,6 +34,10 @@ namespace Fortran::evaluate {
class IntrinsicProcTable;
class TargetCharacteristics;
+namespace value {
+class CharacterValue;
+}
+
using common::ConstantSubscript;
using common::RealFlag;
using common::RealFlags;
@@ -74,6 +78,9 @@ static constexpr Ordering Compare(
}
}
+Ordering Compare(
+ const value::CharacterValue &x, const value::CharacterValue &y);
+
static constexpr Ordering Reverse(Ordering ordering) {
if (ordering == Ordering::Less) {
return Ordering::Greater;
diff --git a/flang/include/flang/Evaluate/complex-value.h b/flang/include/flang/Evaluate/complex-value.h
new file mode 100644
index 0000000000000..9fc1b7eeb8fe6
--- /dev/null
+++ b/flang/include/flang/Evaluate/complex-value.h
@@ -0,0 +1,166 @@
+//===-- include/flang/Evaluate/complex-value.h ------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef FORTRAN_EVALUATE_COMPLEX_VALUE_H_
+#define FORTRAN_EVALUATE_COMPLEX_VALUE_H_
+
+#include "real-value.h"
+
+namespace llvm {
+class raw_ostream;
+}
+
+namespace Fortran::evaluate::value {
+
+/// A complex floating-point value with dynamic precision.
+///
+/// The precision is dynamic, but only a predefined set of Fortran kinds are
+/// allowed. It is also kind-aware, i.e. knows which COMPLEX kind it currently
+/// represents.
+///
+/// The implementation is a pair of RealValue objects.
+class ComplexValue {
+public:
+ ComplexValue() = default;
+ ComplexValue(const ComplexValue &) = default;
+ ComplexValue(ComplexValue &&) = default;
+ ComplexValue &operator=(const ComplexValue &) = default;
+ ComplexValue &operator=(ComplexValue &&) = default;
+
+ ComplexValue(const RealValue &r, const RealValue &i)
+ : re_{r},
+ im_{r.IsMonostate() ? i : RealValue::Convert(r.kind(), i).value} {}
+
+ explicit ComplexValue(const RealValue &r)
+ : ComplexValue{r, RealValue::Zero(r.kind())} {}
+
+ ComplexValue(int kind, const RealValue &r) : ComplexValue{r} {
+ CHECK(kind == r.kind());
+ }
+
+ ComplexValue(int kind, const ComplexValue &v) : ComplexValue{v} {
+ CHECK(kind == v.kind());
+ }
+
+ ComplexValue(int kind, ComplexValue &&v) : ComplexValue{std::move(v)} {
+ CHECK(kind == v.kind());
+ }
+
+ /// Creates a complex value (+0.0 + +0.0i) of a given kind. This is
+ /// different from the default-ctor which creates a "monostate" that
+ /// represents zero of unknown kind.
+ static ComplexValue Zero(int kind) {
+ RealValue zero{RealValue::Zero(kind)};
+ return ComplexValue{zero, zero};
+ }
+
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+ LLVM_DUMP_METHOD void dump() const;
+#endif
+
+ /// Whether this object represents a default-initialized value (zero) of
+ /// not-yet-known kind.
+ bool IsMonostate() const {
+ CHECK(re_.IsMonostate() == im_.IsMonostate());
+ return re_.IsMonostate();
+ }
+
+ /// The kind of the value currently stored.
+ int kind() const {
+ CHECK(re_.kind() == im_.kind());
+ return re_.kind();
+ }
+
+ /// Number of bytes accessed by FromRawBytes/StoreRawBytes
+ std::size_t bytesStored() const {
+ return re_.bytesStored() + im_.bytesStored();
+ }
+ static std::size_t bytesStored(int kind) {
+ return 2 * RealValue::bytesStored(kind);
+ }
+
+ RealValue REAL() const { return re_; }
+
+ RealValue AIMAG() const { return im_; }
+
+ ComplexValue CONJG() const { return ComplexValue{re_, im_.Negate()}; }
+
+ ComplexValue Negate() const {
+ return ComplexValue{re_.Negate(), im_.Negate()};
+ }
+
+ bool Equals(const ComplexValue &y) const {
+ return re_.Compare(y.re_) == Relation::Equal &&
+ im_.Compare(y.im_) == Relation::Equal;
+ }
+
+ bool operator==(const ComplexValue &y) const {
+ return re_ == y.re_ && im_ == y.im_;
+ }
+
+ bool operator!=(const ComplexValue &y) const { return !(*this == y); }
+
+ bool IsZero() const { return re_.IsZero() && im_.IsZero(); }
+
+ bool IsInfinite() const { return re_.IsInfinite() || im_.IsInfinite(); }
+
+ bool IsNotANumber() const { return re_.IsNotANumber() || im_.IsNotANumber(); }
+
+ bool IsSignalingNaN() const {
+ return re_.IsSignalingNaN() || im_.IsSignalingNaN();
+ }
+
+ static ValueWithRealFlags<ComplexValue> FromInteger(int kind,
+ const IntegerValue &n, bool isUnsigned = false,
+ Rounding rounding = TargetCharacteristics::defaultRounding);
+
+ ValueWithRealFlags<ComplexValue> Add(const ComplexValue &,
+ Rounding rounding = TargetCharacteristics::defaultRounding) const;
+
+ ValueWithRealFlags<ComplexValue> Subtract(const ComplexValue &,
+ Rounding rounding = TargetCharacteristics::defaultRounding) const;
+ ValueWithRealFlags<ComplexValue> Multiply(const ComplexValue &,
+ Rounding rounding = TargetCharacteristics::defaultRounding) const;
+ ValueWithRealFlags<ComplexValue> Divide(const ComplexValue &,
+ Rounding rounding = TargetCharacteristics::defaultRounding) const;
+
+ ValueWithRealFlags<ComplexValue> KahanSummation(const ComplexValue &y,
+ ComplexValue &correction,
+ Rounding rounding = TargetCharacteristics::defaultRounding) const;
+
+ /// ABS/CABS = HYPOT(re_, imag_) = SQRT(re_**2 + im_**2)
+ ValueWithRealFlags<RealValue> ABS(
+ Rounding rounding = TargetCharacteristics::defaultRounding) const {
+ return re_.HYPOT(im_, rounding);
+ }
+
+ ComplexValue FlushSubnormalToZero() const {
+ return ComplexValue{re_.FlushSubnormalToZero(), im_.FlushSubnormalToZero()};
+ }
+
+ static ComplexValue NotANumber(int kind) {
+ return {RealValue::NotANumber(kind), RealValue::NotANumber(kind)};
+ }
+
+ std::string DumpHexadecimal() const;
+
+ llvm::raw_ostream &AsFortran(llvm::raw_ostream &, int kind) const;
+
+ void StoreRawBytes(void *dst, size_t size, bool *changed = nullptr) const;
+
+ static ComplexValue FromRawBytes(
+ int kind, const void *raw, std::size_t expectedSize);
+
+ // TODO: unit testing
+
+private:
+ RealValue re_, im_;
+};
+
+} // namespace Fortran::evaluate::value
+#endif // FORTRAN_EVALUATE_COMPLEX_VALUE_H_
diff --git a/flang/include/flang/Evaluate/complex.h b/flang/include/flang/Evaluate/complex.h
deleted file mode 100644
index 9781db9a25a64..0000000000000
--- a/flang/include/flang/Evaluate/complex.h
+++ /dev/null
@@ -1,114 +0,0 @@
-//===-- include/flang/Evaluate/complex.h ------------------------*- C++ -*-===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef FORTRAN_EVALUATE_COMPLEX_H_
-#define FORTRAN_EVALUATE_COMPLEX_H_
-
-#include "formatting.h"
-#include "real.h"
-#include <string>
-
-namespace llvm {
-class raw_ostream;
-}
-
-namespace Fortran::evaluate::value {
-
-template <typename REAL_TYPE> class Complex {
-public:
- using Part = REAL_TYPE;
- static constexpr int bits{2 * Part::bits};
-
- constexpr Complex() {} // (+0.0, +0.0)
- constexpr Complex(const Complex &) = default;
- constexpr Complex(const Part &r, const Part &i) : re_{r}, im_{i} {}
- explicit constexpr Complex(const Part &r) : re_{r} {}
- constexpr Complex &operator=(const Complex &) = default;
- constexpr Complex &operator=(Complex &&) = default;
-
- constexpr bool operator==(const Complex &that) const {
- return re_ == that.re_ && im_ == that.im_;
- }
-
- constexpr const Part &REAL() const { return re_; }
- constexpr const Part &AIMAG() const { return im_; }
- constexpr Complex CONJG() const { return {re_, im_.Negate()}; }
- constexpr Complex Negate() const { return {re_.Negate(), im_.Negate()}; }
-
- constexpr bool Equals(const Complex &that) const {
- return re_.Compare(that.re_) == Relation::Equal &&
- im_.Compare(that.im_) == Relation::Equal;
- }
-
- constexpr bool IsZero() const { return re_.IsZero() && im_.IsZero(); }
-
- constexpr bool IsInfinite() const {
- return re_.IsInfinite() || im_.IsInfinite();
- }
-
- constexpr bool IsNotANumber() const {
- return re_.IsNotANumber() || im_.IsNotANumber();
- }
-
- constexpr bool IsSignalingNaN() const {
- return re_.IsSignalingNaN() || im_.IsSignalingNaN();
- }
-
- template <typename INT>
- static ValueWithRealFlags<Complex> FromInteger(const INT &n,
- bool isUnsigned = false,
- Rounding rounding = TargetCharacteristics::defaultRounding) {
- ValueWithRealFlags<Complex> result;
- result.value.re_ = Part::FromInteger(n, isUnsigned, rounding)
- .AccumulateFlags(result.flags);
- return result;
- }
-
- ValueWithRealFlags<Complex> Add(const Complex &,
- Rounding rounding = TargetCharacteristics::defaultRounding) const;
- ValueWithRealFlags<Complex> Subtract(const Complex &,
- Rounding rounding = TargetCharacteristics::defaultRounding) const;
- ValueWithRealFlags<Complex> Multiply(const Complex &,
- Rounding rounding = TargetCharacteristics::defaultRounding) const;
- ValueWithRealFlags<Complex> Divide(const Complex &,
- Rounding rounding = TargetCharacteristics::defaultRounding) const;
- ValueWithRealFlags<Complex> KahanSummation(const Complex &,
- Complex &correction,
- Rounding rounding = TargetCharacteristics::defaultRounding) const;
-
- // ABS/CABS = HYPOT(re_, imag_) = SQRT(re_**2 + im_**2)
- ValueWithRealFlags<Part> ABS(
- Rounding rounding = TargetCharacteristics::defaultRounding) const {
- return re_.HYPOT(im_, rounding);
- }
-
- constexpr Complex FlushSubnormalToZero() const {
- return {re_.FlushSubnormalToZero(), im_.FlushSubnormalToZero()};
- }
-
- static constexpr Complex NotANumber() {
- return {Part::NotANumber(), Part::NotANumber()};
- }
-
- std::string DumpHexadecimal() const;
- llvm::raw_ostream &AsFortran(llvm::raw_ostream &, int kind) const;
-
- // TODO: unit testing
-
-private:
- Part re_, im_;
-};
-
-extern template class Complex<Real<Integer<16>, 11>>;
-extern template class Complex<Real<Integer<16>, 8>>;
-extern template class Complex<Real<Integer<32>, 24>>;
-extern template class Complex<Real<Integer<64>, 53>>;
-extern template class Complex<Real<X87IntegerContainer, 64>>;
-extern template class Complex<Real<Integer<128>, 113>>;
-} // namespace Fortran::evaluate::value
-#endif // FORTRAN_EVALUATE_COMPLEX_H_
diff --git a/flang/include/flang/Evaluate/constant.h b/flang/include/flang/Evaluate/constant.h
index 9ae37cd999aa9..530328e1802bb 100644
--- a/flang/include/flang/Evaluate/constant.h
+++ b/flang/include/flang/Evaluate/constant.h
@@ -110,16 +110,48 @@ class ConstantBase : public ConstantBounds {
using Result = RESULT;
using Element = ELEMENT;
+ constexpr int kind() const { return kind_; }
+
// Constructor for creating ConstantBase from an actual value (i.e.
// literals, etc.)
- template <typename A,
- typename = std::enable_if_t<std::is_convertible_v<A, Element>>>
- ConstantBase(const A &x, Result res = Result{}) : result_{res}, values_{x} {}
+ template <typename A>
+ ConstantBase(int kind, const A &x, Result res)
+ : kind_{kind}, result_{res}, values_{A{kind, x}} {
+ CHECK_KIND(kind, RESULT);
+ }
+ ConstantBase(int kind, ELEMENT &&x)
+ : kind_{kind}, result_{Result{kind}}, values_{std::move(x)} {
+ CHECK_KIND(kind, RESULT);
+ }
- ConstantBase(ELEMENT &&x, Result res = Result{})
- : result_{res}, values_{std::move(x)} {}
+ template <TypeCategory CAT>
+ ConstantBase(int kind, const SomeKind<CAT> &x)
+ : kind_{kind}, result_{Result{kind}}, values_{x} {
+ CHECK_KIND(kind, RESULT);
+ }
+ template <TypeCategory CAT>
+ ConstantBase(int kind, SomeKind<CAT> &&x)
+ : kind_{kind}, result_{Result{kind}}, values_{std::move(x)} {
+ CHECK_KIND(kind, RESULT);
+ }
+
+ template <typename A>
+ ConstantBase(int kind, const A &x)
+ : kind_{kind}, result_{Result{kind}}, values_{A{kind, x}} {
+ CHECK_KIND(kind, RESULT);
+ }
+ ConstantBase(int kind, ELEMENT &&x, Result res)
+ : kind_{kind}, result_{res}, values_{std::move(x)} {
+ CHECK_KIND(kind, RESULT);
+ }
+
+ ConstantBase(int kind, std::vector<Element> &&x, ConstantSubscripts &&sh)
+ : ConstantBase{kind, std::move(x), std::move(sh), Result{kind}} {}
ConstantBase(
- std::vector<Element> &&, ConstantSubscripts &&, Result = Result{});
+ int kind, std::vector<Element> &&, ConstantSubscripts &&, Result);
+ template <typename A, typename B, typename C>
+ ConstantBase(int kind, const std::map<A, B, C> &x, Result res = Result{})
+ : kind_{kind}, result_{res}, values_{x} {}
DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(ConstantBase)
~ConstantBase();
@@ -140,6 +172,7 @@ class ConstantBase : public ConstantBounds {
std::size_t CopyFrom(const ConstantBase &source, std::size_t count,
ConstantSubscripts &resultSubscripts, const std::vector<int> *dimOrder);
+ int kind_;
Result result_; // usually empty except for Real & Complex
std::vector<Element> values_;
};
@@ -170,22 +203,24 @@ template <typename T> class Constant : public ConstantBase<T> {
ConstantSubscripts &resultSubscripts, const std::vector<int> *dimOrder);
};
-template <int KIND>
-class Constant<Type<TypeCategory::Character, KIND>> : public ConstantBounds {
+template <>
+class Constant<Type<TypeCategory::Character>> : public ConstantBounds {
public:
- using Result = Type<TypeCategory::Character, KIND>;
+ using Result = Type<TypeCategory::Character>;
using Element = Scalar<Result>;
+ constexpr int kind() const { return kind_; }
+
CLASS_BOILERPLATE(Constant)
- explicit Constant(const Scalar<Result> &);
- explicit Constant(Scalar<Result> &&);
- Constant(
- ConstantSubscript length, std::vector<Element> &&, ConstantSubscripts &&);
+ explicit Constant(int kind, const Scalar<Result> &);
+ explicit Constant(int kind, Scalar<Result> &&);
+ Constant(int kind, ConstantSubscript length, std::vector<Element> &&,
+ ConstantSubscripts &&);
~Constant();
bool operator==(const Constant &that) const {
- return LEN() == that.LEN() && shape() == that.shape() &&
- values_ == that.values_;
+ return kind() == that.kind() && LEN() == that.LEN() &&
+ shape() == that.shape() && values_ == that.values_;
}
bool empty() const;
std::size_t size() const;
@@ -212,11 +247,12 @@ class Constant<Type<TypeCategory::Character, KIND>> : public ConstantBounds {
Constant Reshape(ConstantSubscripts &&) const;
llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const;
std::string AsFortran() const;
- DynamicType GetType() const { return {KIND, length_}; }
+ DynamicType GetType() const { return {kind_, length_}; }
std::size_t CopyFrom(const Constant &source, std::size_t count,
ConstantSubscripts &resultSubscripts, const std::vector<int> *dimOrder);
private:
+ int kind_;
Scalar<Result> values_; // one contiguous string
ConstantSubscript length_;
bool wasHollerith_{false};
@@ -239,6 +275,12 @@ class Constant<SomeDerived>
Constant(const StructureConstructor &);
Constant(StructureConstructor &&);
+ Constant(int kind, const StructureConstructor &v) : Constant(v) {
+ CHECK(kind == 0);
+ }
+ Constant(int kind, StructureConstructor &&v) : Constant(std::move(v)) {
+ CHECK(kind == 0);
+ }
Constant(const semantics::DerivedTypeSpec &,
std::vector<StructureConstructorValues> &&, ConstantSubscripts &&);
Constant(const semantics::DerivedTypeSpec &,
@@ -254,6 +296,40 @@ class Constant<SomeDerived>
ConstantSubscripts &resultSubscripts, const std::vector<int> *dimOrder);
};
+inline Constant<SubscriptInteger> MakeSubscriptIntConstant(int64_t v) {
+ return Constant<SubscriptInteger>{
+ SubscriptIntegerKind, Scalar<SubscriptInteger>{SubscriptIntegerKind, v}};
+}
+
+inline Constant<CInteger> MakeCIntegerConstant(int32_t v) {
+ return Constant<CInteger>{CIntegerKind, Scalar<CInteger>{CIntegerKind, v}};
+}
+
+inline Constant<LogicalResult> MakeLogicalResultConstant(bool v) {
+ return Constant<LogicalResult>{
+ LogicalResultKind, Scalar<LogicalResult>{LogicalResultKind, v}};
+}
+
+template <typename T, typename CharT,
+ typename =
+ std::enable_if_t<std::is_same_v<T, Type<TypeCategory::Character>>>>
+inline Constant<T> MakeConstant(int kind, const std::basic_string<CharT> &v) {
+ return Constant<T>{kind, value::CharacterValue{kind, v}};
+}
+
+template <typename T, typename CharT,
+ typename =
+ std::enable_if_t<std::is_same_v<T, Type<TypeCategory::Character>>>>
+inline Constant<T> MakeConstant(int kind, std::basic_string<CharT> &&v) {
+ return Constant<T>{kind, value::CharacterValue{kind, std::move(v)}};
+}
+
+template <typename T,
+ typename = std::enable_if_t<std::is_same_v<T, Type<TypeCategory::Integer>>>>
+inline Constant<T> MakeConstant(int kind, int64_t v) {
+ return Constant<T>{kind, value::IntegerValue{kind, v}};
+}
+
FOR_EACH_LENGTHLESS_INTRINSIC_KIND(extern template class ConstantBase, )
extern template class ConstantBase<SomeDerived, StructureConstructorValues>;
FOR_EACH_INTRINSIC_KIND(extern template class Constant, )
diff --git a/flang/include/flang/Evaluate/expression.h b/flang/include/flang/Evaluate/expression.h
index 48a6b635f6350..ba8f2b744bd9c 100644
--- a/flang/include/flang/Evaluate/expression.h
+++ b/flang/include/flang/Evaluate/expression.h
@@ -60,12 +60,15 @@ using common::RelationalOperator;
// maps to some instantiation of Type<CATEGORY, KIND>, SomeKind<CATEGORY>,
// or SomeType. (Exception: BOZ literal constants in generic Expr<SomeType>.)
template <typename A> using ResultType = typename std::decay_t<A>::Result;
+template <typename A> constexpr int ResultKind = std::decay_t<A>::kind();
// Common Expr<> behaviors: every Expr<T> derives from ExpressionBase<T>.
template <typename RESULT> class ExpressionBase {
public:
using Result = RESULT;
+ int kind() const;
+
private:
using Derived = Expr<Result>;
#if defined(__APPLE__) && defined(__GNUC__)
@@ -125,6 +128,8 @@ class Operation {
(operands == 1 && std::is_same_v<Result, SomeDerived>));
template <int J> using Operand = std::tuple_element_t<J, OperandTypes>;
+ constexpr int kind() const { return kind_; }
+
// Unary operations wrap a single Expr with a CopyableIndirection.
// Binary operations wrap a tuple of CopyableIndirections to Exprs.
private:
@@ -134,8 +139,14 @@ class Operation {
public:
CLASS_BOILERPLATE(Operation)
- explicit Operation(const Expr<OPERANDS> &...x) : operand_{x...} {}
- explicit Operation(Expr<OPERANDS> &&...x) : operand_{std::move(x)...} {}
+ explicit Operation(int kind, const Expr<OPERANDS> &...x)
+ : kind_{kind}, operand_{x...} {
+ CHECK_KIND(kind, RESULT);
+ }
+ explicit Operation(int kind, Expr<OPERANDS> &&...x)
+ : kind_{kind}, operand_{std::move(x)...} {
+ CHECK_KIND(kind, RESULT);
+ }
Derived &derived() { return *static_cast<Derived *>(this); }
const Derived &derived() const { return *static_cast<const Derived *>(this); }
@@ -177,10 +188,8 @@ class Operation {
}
}
- static constexpr std::conditional_t<Result::category != TypeCategory::Derived,
- std::optional<DynamicType>, void>
- GetType() {
- return Result::GetType();
+ constexpr std::optional<DynamicType> GetType() const {
+ return DynamicType{Result::category, kind()};
}
int Rank() const {
int rank{left().Rank()};
@@ -199,6 +208,7 @@ class Operation {
llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const;
private:
+ int kind_;
Container operand_;
};
@@ -221,6 +231,7 @@ struct Convert : public Operation<Convert<TO, FROMCAT>, TO, SomeKind<FROMCAT>> {
using Operand = SomeKind<FROMCAT>;
using Base = Operation<Convert, Result, Operand>;
using Base::Base;
+ using Base::kind;
llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const;
};
@@ -230,6 +241,9 @@ struct Parentheses : public Operation<Parentheses<A>, A, A> {
using Operand = A;
using Base = Operation<Parentheses, A, A>;
using Base::Base;
+
+ Parentheses(const Expr<A> &x) : Base{x.kind(), x} {};
+ Parentheses(Expr<A> &&x) : Base{x.kind(), std::move(x)} {};
};
template <>
@@ -241,6 +255,9 @@ struct Parentheses<SomeDerived>
using Base = Operation<Parentheses, SomeDerived, SomeDerived>;
using Base::Base;
DynamicType GetType() const;
+
+ Parentheses(const Expr<SomeDerived> &x);
+ Parentheses(Expr<SomeDerived> &&x);
};
template <typename A> struct Negate : public Operation<Negate<A>, A, A> {
@@ -248,28 +265,27 @@ template <typename A> struct Negate : public Operation<Negate<A>, A, A> {
using Operand = A;
using Base = Operation<Negate, A, A>;
using Base::Base;
+
+ Negate(const Expr<A> &x) : Base{x.kind(), x} {};
+ Negate(Expr<A> &&x) : Base{x.kind(), std::move(x)} {};
};
-template <int KIND>
struct ComplexComponent
- : public Operation<ComplexComponent<KIND>, Type<TypeCategory::Real, KIND>,
- Type<TypeCategory::Complex, KIND>> {
- using Result = Type<TypeCategory::Real, KIND>;
- using Operand = Type<TypeCategory::Complex, KIND>;
+ : public Operation<ComplexComponent, Type<TypeCategory::Real>,
+ Type<TypeCategory::Complex>> {
+ using Result = Type<TypeCategory::Real>;
+ using Operand = Type<TypeCategory::Complex>;
using Base = Operation<ComplexComponent, Result, Operand>;
CLASS_BOILERPLATE(ComplexComponent)
- ComplexComponent(bool isImaginary, const Expr<Operand> &x)
- : Base{x}, isImaginaryPart{isImaginary} {}
- ComplexComponent(bool isImaginary, Expr<Operand> &&x)
- : Base{std::move(x)}, isImaginaryPart{isImaginary} {}
+ ComplexComponent(bool isImaginary, const Expr<Operand> &x);
+ ComplexComponent(bool isImaginary, Expr<Operand> &&x);
bool isImaginaryPart{true};
};
-template <int KIND>
-struct Not : public Operation<Not<KIND>, Type<TypeCategory::Logical, KIND>,
- Type<TypeCategory::Logical, KIND>> {
- using Result = Type<TypeCategory::Logical, KIND>;
+struct Not : public Operation<Not, Type<TypeCategory::Logical>,
+ Type<TypeCategory::Logical>> {
+ using Result = Type<TypeCategory::Logical>;
using Operand = Result;
using Base = Operation<Not, Result, Operand>;
using Base::Base;
@@ -279,11 +295,9 @@ struct Not : public Operation<Not<KIND>, Type<TypeCategory::Logical, KIND>,
// have explicit syntax for changing them. Expressions represent
// changes of length (e.g., for assignments and structure constructors)
// with this operation.
-template <int KIND>
-struct SetLength
- : public Operation<SetLength<KIND>, Type<TypeCategory::Character, KIND>,
- Type<TypeCategory::Character, KIND>, SubscriptInteger> {
- using Result = Type<TypeCategory::Character, KIND>;
+struct SetLength : public Operation<SetLength, Type<TypeCategory::Character>,
+ Type<TypeCategory::Character>, SubscriptInteger> {
+ using Result = Type<TypeCategory::Character>;
using CharacterOperand = Result;
using LengthOperand = SubscriptInteger;
using Base = Operation<SetLength, Result, CharacterOperand, LengthOperand>;
@@ -342,49 +356,55 @@ template <typename A> struct Extremum : public Operation<Extremum<A>, A, A, A> {
using Base = Operation<Extremum, A, A, A>;
CLASS_BOILERPLATE(Extremum)
Extremum(Ordering ord, const Expr<Operand> &x, const Expr<Operand> &y)
- : Base{x, y}, ordering{ord} {}
+ : Base{x.kind(), x, y}, ordering{ord} {
+ CHECK(x.kind() == y.kind());
+ }
Extremum(Ordering ord, Expr<Operand> &&x, Expr<Operand> &&y)
- : Base{std::move(x), std::move(y)}, ordering{ord} {}
+ : Base{x.kind(), std::move(x), std::move(y)}, ordering{ord} {
+ CHECK(x.kind() == y.kind());
+ }
bool operator==(const Extremum &) const;
Ordering ordering{Ordering::Greater};
};
-template <int KIND>
struct ComplexConstructor
- : public Operation<ComplexConstructor<KIND>,
- Type<TypeCategory::Complex, KIND>, Type<TypeCategory::Real, KIND>,
- Type<TypeCategory::Real, KIND>> {
- using Result = Type<TypeCategory::Complex, KIND>;
- using Operand = Type<TypeCategory::Real, KIND>;
+ : public Operation<ComplexConstructor, Type<TypeCategory::Complex>,
+ Type<TypeCategory::Real>, Type<TypeCategory::Real>> {
+ using Result = Type<TypeCategory::Complex>;
+ using Operand = Type<TypeCategory::Real>;
using Base = Operation<ComplexConstructor, Result, Operand, Operand>;
using Base::Base;
+
+ ComplexConstructor(const Expr<Type<TypeCategory::Real>> &re,
+ const Expr<Type<TypeCategory::Real>> &im);
+ ComplexConstructor(
+ Expr<Type<TypeCategory::Real>> &&re, Expr<Type<TypeCategory::Real>> &&im);
};
-template <int KIND>
struct Concat
- : public Operation<Concat<KIND>, Type<TypeCategory::Character, KIND>,
- Type<TypeCategory::Character, KIND>,
- Type<TypeCategory::Character, KIND>> {
- using Result = Type<TypeCategory::Character, KIND>;
+ : public Operation<Concat, Type<TypeCategory::Character>,
+ Type<TypeCategory::Character>, Type<TypeCategory::Character>> {
+ using Result = Type<TypeCategory::Character>;
using Operand = Result;
using Base = Operation<Concat, Result, Operand, Operand>;
using Base::Base;
+
+ Concat(const Expr<Type<TypeCategory::Character>> &x,
+ const Expr<Type<TypeCategory::Character>> &y);
+ Concat(Expr<Type<TypeCategory::Character>> &&x,
+ Expr<Type<TypeCategory::Character>> &&y);
};
-template <int KIND>
struct LogicalOperation
- : public Operation<LogicalOperation<KIND>,
- Type<TypeCategory::Logical, KIND>, Type<TypeCategory::Logical, KIND>,
- Type<TypeCategory::Logical, KIND>> {
- using Result = Type<TypeCategory::Logical, KIND>;
+ : public Operation<LogicalOperation, Type<TypeCategory::Logical>,
+ Type<TypeCategory::Logical>, Type<TypeCategory::Logical>> {
+ using Result = Type<TypeCategory::Logical>;
using Operand = Result;
using Base = Operation<LogicalOperation, Result, Operand, Operand>;
CLASS_BOILERPLATE(LogicalOperation)
LogicalOperation(
- LogicalOperator opr, const Expr<Operand> &x, const Expr<Operand> &y)
- : Base{x, y}, logicalOperator{opr} {}
- LogicalOperation(LogicalOperator opr, Expr<Operand> &&x, Expr<Operand> &&y)
- : Base{std::move(x), std::move(y)}, logicalOperator{opr} {}
+ LogicalOperator opr, const Expr<Operand> &x, const Expr<Operand> &y);
+ LogicalOperation(LogicalOperator opr, Expr<Operand> &&x, Expr<Operand> &&y);
bool operator==(const LogicalOperation &) const;
LogicalOperator logicalOperator;
};
@@ -394,11 +414,17 @@ struct LogicalOperation
template <typename T> class ConditionalExpr {
public:
using Result = T;
+
+ constexpr int kind() const { return kind_; }
+
CLASS_BOILERPLATE(ConditionalExpr)
ConditionalExpr(Expr<LogicalResult> &&cond, Expr<Result> &&thenVal,
Expr<Result> &&elseVal)
- : condition_{std::move(cond)}, thenValue_{std::move(thenVal)},
- elseValue_{std::move(elseVal)} {}
+ : kind_{thenVal.kind()}, condition_{std::move(cond)},
+ thenValue_{std::move(thenVal)}, elseValue_{std::move(elseVal)} {
+ CHECK_KIND(kind(), Result);
+ CHECK(thenVal.kind() == elseVal.kind());
+ }
bool operator==(const ConditionalExpr &) const;
Expr<LogicalResult> &condition() { return condition_.value(); }
const Expr<LogicalResult> &condition() const { return condition_.value(); }
@@ -425,6 +451,7 @@ template <typename T> class ConditionalExpr {
llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const;
private:
+ int kind_;
common::CopyableIndirection<Expr<LogicalResult>> condition_;
common::CopyableIndirection<Expr<Result>> thenValue_;
common::CopyableIndirection<Expr<Result>> elseValue_;
@@ -436,6 +463,12 @@ template <typename RESULT> class ArrayConstructorValues;
struct ImpliedDoIndex {
using Result = SubscriptInteger;
bool operator==(const ImpliedDoIndex &) const;
+
+ static constexpr int kind() { return SubscriptIntegerKind; }
+
+ static constexpr DynamicType GetType() {
+ return {TypeCategory::Integer, kind()};
+ }
static constexpr int Rank() { return 0; }
static constexpr int Corank() { return 0; }
parser::CharBlock name; // nested implied DOs must use distinct names
@@ -507,33 +540,55 @@ class ArrayConstructor : public ArrayConstructorValues<RESULT> {
public:
using Result = RESULT;
using Base = ArrayConstructorValues<Result>;
+
+ constexpr int kind() const { return kind_; }
+
DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(ArrayConstructor)
- explicit ArrayConstructor(Base &&values) : Base{std::move(values)} {}
- template <typename T> explicit ArrayConstructor(const Expr<T> &) {}
- static constexpr Result result() { return Result{}; }
- static constexpr DynamicType GetType() { return Result::GetType(); }
+ explicit ArrayConstructor(int kind, Base &&values)
+ : Base{std::move(values)}, kind_{kind} {
+ CHECK_KIND(kind, RESULT);
+ }
+ template <typename T>
+ explicit ArrayConstructor(int kind, const Expr<T> &) : kind_{kind} {
+ CHECK_KIND(kind, RESULT);
+ }
+ static constexpr Result result(int kind) { return Result{kind}; }
+ DynamicType GetType() const { return {Result::category, kind_}; }
llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const;
+
+private:
+ int kind_;
};
-template <int KIND>
-class ArrayConstructor<Type<TypeCategory::Character, KIND>>
- : public ArrayConstructorValues<Type<TypeCategory::Character, KIND>> {
+template <>
+class ArrayConstructor<Type<TypeCategory::Character>>
+ : public ArrayConstructorValues<Type<TypeCategory::Character>> {
public:
- using Result = Type<TypeCategory::Character, KIND>;
+ using Result = Type<TypeCategory::Character>;
using Base = ArrayConstructorValues<Result>;
+
+ constexpr int kind() const { return kind_; }
+
DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(ArrayConstructor)
- explicit ArrayConstructor(Base &&values) : Base{std::move(values)} {}
- template <typename T> explicit ArrayConstructor(const Expr<T> &) {}
+ explicit ArrayConstructor(int kind, Base &&values)
+ : Base{std::move(values)}, kind_{kind} {
+ CHECK(kind != 0);
+ }
+ template <typename T>
+ explicit ArrayConstructor(int kind, const Expr<T> &) : kind_{kind} {
+ CHECK(kind != 0);
+ }
ArrayConstructor &set_LEN(Expr<SubscriptInteger> &&);
bool operator==(const ArrayConstructor &) const;
- static constexpr Result result() { return Result{}; }
- static constexpr DynamicType GetType() { return Result::GetType(); }
+ static constexpr Result result(int kind) { return Result{kind}; }
+ DynamicType GetType() const { return {Result::category, kind()}; }
llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const;
const Expr<SubscriptInteger> *LEN() const {
return length_ ? &length_->value() : nullptr;
}
private:
+ int kind_;
std::optional<common::CopyableIndirection<Expr<SubscriptInteger>>> length_;
};
@@ -543,6 +598,9 @@ class ArrayConstructor<SomeDerived>
public:
using Result = SomeDerived;
using Base = ArrayConstructorValues<Result>;
+
+ constexpr int kind() const { return 0; }
+
CLASS_BOILERPLATE(ArrayConstructor)
ArrayConstructor(const semantics::DerivedTypeSpec &spec, Base &&v)
@@ -550,6 +608,11 @@ class ArrayConstructor<SomeDerived>
template <typename A>
explicit ArrayConstructor(const A &prototype)
: result_{prototype.GetType().value().GetDerivedTypeSpec()} {}
+ template <typename A>
+ explicit ArrayConstructor(int kind, const A &prototype)
+ : result_{prototype.GetType().value().GetDerivedTypeSpec()} {
+ CHECK(kind == 0);
+ }
bool operator==(const ArrayConstructor &) const;
constexpr Result result() const { return result_; }
@@ -562,11 +625,11 @@ class ArrayConstructor<SomeDerived>
// Expression representations for each type category.
-template <int KIND>
-class Expr<Type<TypeCategory::Integer, KIND>>
- : public ExpressionBase<Type<TypeCategory::Integer, KIND>> {
+template <>
+class Expr<Type<TypeCategory::Integer>>
+ : public ExpressionBase<Type<TypeCategory::Integer>> {
public:
- using Result = Type<TypeCategory::Integer, KIND>;
+ using Result = Type<TypeCategory::Integer>;
EVALUATE_UNION_CLASS_BOILERPLATE(Expr)
@@ -577,17 +640,10 @@ class Expr<Type<TypeCategory::Integer, KIND>>
using Operations = std::tuple<Parentheses<Result>, Negate<Result>,
Add<Result>, Subtract<Result>, Multiply<Result>, Divide<Result>,
Power<Result>, Extremum<Result>, ConditionalExpr<Result>>;
- using Indices = std::conditional_t<KIND == ImpliedDoIndex::Result::kind,
- std::tuple<ImpliedDoIndex>, std::tuple<>>;
- using TypeParamInquiries =
- std::conditional_t<KIND == TypeParamInquiry::Result::kind,
- std::tuple<TypeParamInquiry>, std::tuple<>>;
- using DescriptorInquiries =
- std::conditional_t<KIND == DescriptorInquiry::Result::kind,
- std::tuple<DescriptorInquiry>, std::tuple<>>;
- using RankOneBoundElements =
- std::conditional_t<KIND == RankOneBoundElement::Result::kind,
- std::tuple<RankOneBoundElement>, std::tuple<>>;
+ using Indices = std::tuple<ImpliedDoIndex>;
+ using TypeParamInquiries = std::tuple<TypeParamInquiry>;
+ using DescriptorInquiries = std::tuple<DescriptorInquiry>;
+ using RankOneBoundElements = std::tuple<RankOneBoundElement>;
using Others = std::tuple<Constant<Result>, ArrayConstructor<Result>,
Designator<Result>, FunctionRef<Result>>;
@@ -597,11 +653,11 @@ class Expr<Type<TypeCategory::Integer, KIND>>
u;
};
-template <int KIND>
-class Expr<Type<TypeCategory::Unsigned, KIND>>
- : public ExpressionBase<Type<TypeCategory::Unsigned, KIND>> {
+template <>
+class Expr<Type<TypeCategory::Unsigned>>
+ : public ExpressionBase<Type<TypeCategory::Unsigned>> {
public:
- using Result = Type<TypeCategory::Unsigned, KIND>;
+ using Result = Type<TypeCategory::Unsigned>;
EVALUATE_UNION_CLASS_BOILERPLATE(Expr)
@@ -620,14 +676,15 @@ class Expr<Type<TypeCategory::Unsigned, KIND>>
u;
};
-template <int KIND>
-class Expr<Type<TypeCategory::Real, KIND>>
- : public ExpressionBase<Type<TypeCategory::Real, KIND>> {
+template <>
+class Expr<Type<TypeCategory::Real>>
+ : public ExpressionBase<Type<TypeCategory::Real>> {
public:
- using Result = Type<TypeCategory::Real, KIND>;
+ using Result = Type<TypeCategory::Real>;
EVALUATE_UNION_CLASS_BOILERPLATE(Expr)
- explicit Expr(const Scalar<Result> &x) : u{Constant<Result>{x}} {}
+ explicit Expr(int kind, const Scalar<Result> &x)
+ : u{Constant<Result>{kind, x}} {}
private:
// N.B. Real->Complex and Complex->Real conversions are done with CMPLX
@@ -635,7 +692,7 @@ class Expr<Type<TypeCategory::Real, KIND>>
using Conversions = std::variant<Convert<Result, TypeCategory::Integer>,
Convert<Result, TypeCategory::Real>,
Convert<Result, TypeCategory::Unsigned>>;
- using Operations = std::variant<ComplexComponent<KIND>, Parentheses<Result>,
+ using Operations = std::variant<ComplexComponent, Parentheses<Result>,
Negate<Result>, Add<Result>, Subtract<Result>, Multiply<Result>,
Divide<Result>, Power<Result>, RealToIntPower<Result>, Extremum<Result>,
ConditionalExpr<Result>>;
@@ -646,17 +703,18 @@ class Expr<Type<TypeCategory::Real, KIND>>
common::CombineVariants<Operations, Conversions, Others> u;
};
-template <int KIND>
-class Expr<Type<TypeCategory::Complex, KIND>>
- : public ExpressionBase<Type<TypeCategory::Complex, KIND>> {
+template <>
+class Expr<Type<TypeCategory::Complex>>
+ : public ExpressionBase<Type<TypeCategory::Complex>> {
public:
- using Result = Type<TypeCategory::Complex, KIND>;
+ using Result = Type<TypeCategory::Complex>;
EVALUATE_UNION_CLASS_BOILERPLATE(Expr)
- explicit Expr(const Scalar<Result> &x) : u{Constant<Result>{x}} {}
+ explicit Expr(int kind, const Scalar<Result> &x)
+ : u{Constant<Result>{kind, x}} {}
using Operations = std::variant<Parentheses<Result>, Negate<Result>,
Convert<Result, TypeCategory::Complex>, Add<Result>, Subtract<Result>,
Multiply<Result>, Divide<Result>, Power<Result>, RealToIntPower<Result>,
- ComplexConstructor<KIND>, ConditionalExpr<Result>>;
+ ComplexConstructor, ConditionalExpr<Result>>;
using Others = std::variant<Constant<Result>, ArrayConstructor<Result>,
Designator<Result>, FunctionRef<Result>>;
@@ -669,20 +727,22 @@ FOR_EACH_UNSIGNED_KIND(extern template class Expr, )
FOR_EACH_REAL_KIND(extern template class Expr, )
FOR_EACH_COMPLEX_KIND(extern template class Expr, )
-template <int KIND>
-class Expr<Type<TypeCategory::Character, KIND>>
- : public ExpressionBase<Type<TypeCategory::Character, KIND>> {
+template <>
+class Expr<Type<TypeCategory::Character>>
+ : public ExpressionBase<Type<TypeCategory::Character>> {
public:
- using Result = Type<TypeCategory::Character, KIND>;
+ using Result = Type<TypeCategory::Character>;
EVALUATE_UNION_CLASS_BOILERPLATE(Expr)
- explicit Expr(const Scalar<Result> &x) : u{Constant<Result>{x}} {}
- explicit Expr(Scalar<Result> &&x) : u{Constant<Result>{std::move(x)}} {}
+ explicit Expr(int kind, const Scalar<Result> &x)
+ : u{Constant<Result>{kind, x}} {}
+ explicit Expr(int kind, Scalar<Result> &&x)
+ : u{Constant<Result>{kind, std::move(x)}} {}
std::optional<Expr<SubscriptInteger>> LEN() const;
std::variant<Constant<Result>, ArrayConstructor<Result>, Designator<Result>,
- FunctionRef<Result>, Parentheses<Result>, Convert<Result>, Concat<KIND>,
- Extremum<Result>, SetLength<KIND>, ConditionalExpr<Result>>
+ FunctionRef<Result>, Parentheses<Result>, Convert<Result>, Concat,
+ Extremum<Result>, SetLength, ConditionalExpr<Result>>
u;
};
@@ -710,9 +770,16 @@ class Relational : public Operation<Relational<T>, LogicalResult, T, T> {
CLASS_BOILERPLATE(Relational)
Relational(
RelationalOperator r, const Expr<Operand> &a, const Expr<Operand> &b)
- : Base{a, b}, opr{r} {}
+ : Base{LogicalResultKind, a, b}, opr{r} {
+ CHECK(a.kind() == b.kind());
+ }
Relational(RelationalOperator r, Expr<Operand> &&a, Expr<Operand> &&b)
- : Base{std::move(a), std::move(b)}, opr{r} {}
+ : Base{LogicalResultKind, std::move(a), std::move(b)}, opr{r} {
+ CHECK(a.kind() == b.kind());
+ }
+ static constexpr std::optional<DynamicType> GetType() {
+ return DynamicType{TypeCategory::Logical, LogicalResultKind};
+ }
bool operator==(const Relational &) const;
RelationalOperator opr;
};
@@ -724,7 +791,12 @@ template <> class Relational<SomeType> {
public:
using Result = LogicalResult;
EVALUATE_UNION_CLASS_BOILERPLATE(Relational)
- static constexpr DynamicType GetType() { return Result::GetType(); }
+ int kind() const {
+ return common::visit([](const auto &x) { return x.kind(); }, u);
+ }
+ static constexpr DynamicType GetType() {
+ return {TypeCategory::Logical, LogicalResultKind};
+ }
int Rank() const {
return common::visit([](const auto &x) { return x.Rank(); }, u);
}
@@ -743,20 +815,23 @@ extern template class Relational<SomeType>;
// do not include Relational<> operations as possibilities,
// since the results of Relationals are always LogicalResult
// (kind=4).
-template <int KIND>
-class Expr<Type<TypeCategory::Logical, KIND>>
- : public ExpressionBase<Type<TypeCategory::Logical, KIND>> {
+template <>
+class Expr<Type<TypeCategory::Logical>>
+ : public ExpressionBase<Type<TypeCategory::Logical>> {
public:
- using Result = Type<TypeCategory::Logical, KIND>;
+ using Result = Type<TypeCategory::Logical>;
EVALUATE_UNION_CLASS_BOILERPLATE(Expr)
- explicit Expr(const Scalar<Result> &x) : u{Constant<Result>{x}} {}
- explicit Expr(bool x) : u{Constant<Result>{x}} {}
+
+ explicit Expr(const Scalar<Result> &x) : u{Constant<Result>{x.kind(), x}} {}
+ explicit Expr(int kind, const Scalar<Result> &x)
+ : u{Constant<Result>{kind, x}} {}
+ explicit Expr(int kind, bool x)
+ : u{Constant<Result>{kind, Scalar<Result>{kind, x}}} {}
private:
- using Operations = std::tuple<Convert<Result>, Parentheses<Result>, Not<KIND>,
- LogicalOperation<KIND>, ConditionalExpr<Result>>;
- using Relations = std::conditional_t<KIND == LogicalResult::kind,
- std::tuple<Relational<SomeType>>, std::tuple<>>;
+ using Operations = std::tuple<Convert<Result>, Parentheses<Result>, Not,
+ LogicalOperation, ConditionalExpr<Result>>;
+ using Relations = std::tuple<Relational<SomeType>>;
using Others = std::tuple<Constant<Result>, ArrayConstructor<Result>,
Designator<Result>, FunctionRef<Result>>;
@@ -785,6 +860,8 @@ class StructureConstructor {
public:
using Result = SomeDerived;
+ static int kind() { return 0; }
+
explicit StructureConstructor(const semantics::DerivedTypeSpec &spec)
: result_{spec} {}
StructureConstructor(
@@ -845,7 +922,6 @@ class Expr<SomeKind<CAT>> : public ExpressionBase<SomeKind<CAT>> {
public:
using Result = SomeKind<CAT>;
EVALUATE_UNION_CLASS_BOILERPLATE(Expr)
- int GetKind() const;
common::MapTemplate<evaluate::Expr, CategoryTypes<CAT>> u;
};
@@ -853,7 +929,6 @@ template <> class Expr<SomeCharacter> : public ExpressionBase<SomeCharacter> {
public:
using Result = SomeCharacter;
EVALUATE_UNION_CLASS_BOILERPLATE(Expr)
- int GetKind() const;
std::optional<Expr<SubscriptInteger>> LEN() const;
common::MapTemplate<Expr, CategoryTypes<TypeCategory::Character>> u;
};
@@ -867,9 +942,12 @@ using CategoryExpression = common::MapTemplate<Expr, SomeCategory>;
// distinguishable from other integer constants, since they are permitted
// to be used in only a few situations.
using BOZLiteralConstant = typename LargestReal::Scalar::Word;
+static constexpr int BOZLiteralConstantKind = LargestRealKind;
// Null pointers without MOLD= arguments are typed by context.
struct NullPointer {
+ static constexpr int kind() { return 0; }
+
constexpr bool operator==(const NullPointer &) const { return true; }
static constexpr int Rank() { return 0; }
static constexpr int Corank() { return 0; }
@@ -886,6 +964,9 @@ using TypelessExpression = std::variant<BOZLiteralConstant, NullPointer,
template <> class Expr<SomeType> : public ExpressionBase<SomeType> {
public:
using Result = SomeType;
+
+ static int kind() { return 0; }
+
EVALUATE_UNION_CLASS_BOILERPLATE(Expr)
// Owning references to these generic expressions can appear in other
@@ -893,21 +974,18 @@ template <> class Expr<SomeType> : public ExpressionBase<SomeType> {
// its destructor is externalized to reduce redundant default instances.
~Expr();
- template <TypeCategory CAT, int KIND>
- explicit Expr(const Expr<Type<CAT, KIND>> &x) : u{Expr<SomeKind<CAT>>{x}} {}
+ template <TypeCategory CAT>
+ explicit Expr(const Expr<Type<CAT>> &x) : u{Expr<SomeKind<CAT>>{x}} {}
- template <TypeCategory CAT, int KIND>
- explicit Expr(Expr<Type<CAT, KIND>> &&x)
- : u{Expr<SomeKind<CAT>>{std::move(x)}} {}
+ template <TypeCategory CAT>
+ explicit Expr(Expr<Type<CAT>> &&x) : u{Expr<SomeKind<CAT>>{std::move(x)}} {}
- template <TypeCategory CAT, int KIND>
- Expr &operator=(const Expr<Type<CAT, KIND>> &x) {
+ template <TypeCategory CAT> Expr &operator=(const Expr<Type<CAT>> &x) {
u = Expr<SomeKind<CAT>>{x};
return *this;
}
- template <TypeCategory CAT, int KIND>
- Expr &operator=(Expr<Type<CAT, KIND>> &&x) {
+ template <TypeCategory CAT> Expr &operator=(Expr<Type<CAT>> &&x) {
u = Expr<SomeKind<CAT>>{std::move(x)};
return *this;
}
@@ -976,5 +1054,91 @@ FOR_EACH_INTRINSIC_KIND(extern template class ArrayConstructor, )
FOR_EACH_INTRINSIC_KIND(template class ArrayConstructorValues, ) \
FOR_EACH_INTRINSIC_KIND(template class ArrayConstructor, ) \
FOR_EACH_INTRINSIC_KIND(template class ConditionalExpr, )
+
+template <typename T>
+inline Expr<T> MakeConstantExpr(int kind, const Scalar<T> &v) {
+ CHECK(kind == v.kind());
+ return Expr<T>{Constant<T>{kind, v}};
+}
+
+template <typename T> inline Expr<T> MakeConstantExpr(int kind, Scalar<T> &&v) {
+ CHECK(kind == v.kind());
+ return Expr<T>{Constant<T>{kind, std::move(v)}};
+}
+
+template <typename T>
+inline Expr<T> MakeConstantExpr(int kind, const Constant<T> &c) {
+ CHECK(kind == c.kind());
+ return Expr<T>{c};
+}
+
+template <typename T>
+inline Expr<T> MakeConstantExpr(int kind, Constant<T> &&c) {
+ CHECK(kind == c.kind());
+ return Expr<T>{std::move(c)};
+}
+
+template <typename T,
+ typename =
+ std::enable_if_t<std::is_same_v<T, Type<TypeCategory::Character>>>>
+inline Expr<T> MakeConstantExpr(int kind, const std::string &v) {
+ return Expr<T>{MakeConstant<T>(kind, v)};
+}
+
+template <typename T,
+ typename = std::enable_if_t<std::is_same_v<T, Type<TypeCategory::Integer>>>>
+inline Expr<T> MakeConstantExpr(int kind, int64_t v) {
+ return Expr<T>{Constant<T>{kind, Scalar<T>{kind, v}}};
+}
+
+template <typename T,
+ typename =
+ std::enable_if_t<std::is_same_v<T, Type<TypeCategory::Unsigned>>>>
+inline Expr<T> MakeConstantExpr(int kind, uint64_t v) {
+ return Expr<T>{Constant<T>{kind, Scalar<T>{kind, v}}};
+}
+
+template <typename T,
+ typename = std::enable_if_t<std::is_same_v<T, Type<TypeCategory::Logical>>>>
+inline Expr<T> MakeConstantExpr(int kind, bool v) {
+ return Expr<T>{Constant<T>{kind, Scalar<T>{kind, v}}};
+}
+
+template <typename T> inline Expr<T> MakeZeroExpr(int kind) {
+ return MakeConstantExpr<T>(kind, Scalar<T>::Zero(kind));
+}
+
+inline Expr<SubscriptInteger> MakeSubscriptIntExpr(int64_t v) {
+ return Expr<SubscriptInteger>{MakeSubscriptIntConstant(v)};
+}
+
+inline Expr<SubscriptInteger> MakeSubscriptIntExpr(
+ const Scalar<SubscriptInteger> &v) {
+ return Expr<SubscriptInteger>{
+ Constant<SubscriptInteger>{SubscriptIntegerKind, v}};
+}
+
+inline Expr<SubscriptInteger> MakeSubscriptIntExpr(
+ Scalar<SubscriptInteger> &&v) {
+ return Expr<SubscriptInteger>{
+ Constant<SubscriptInteger>{SubscriptIntegerKind, std::move(v)}};
+}
+
+inline Expr<CInteger> MakeCIntegerExpr(int32_t v) {
+ return Expr<CInteger>{MakeCIntegerConstant(v)};
+}
+
+inline Expr<LogicalResult> MakeLogicalResultExpr(bool v) {
+ return Expr<LogicalResult>{MakeLogicalResultConstant(v)};
+}
+
+inline Expr<Ascii> MakeAsciiExpr(const std::string &v) {
+ return Expr<Ascii>{MakeConstant<Ascii>(AsciiKind, v)};
+}
+
+inline Expr<Ascii> MakeAsciiExpr(std::string &&v) {
+ return Expr<Ascii>{MakeConstant<Ascii>(AsciiKind, std::move(v))};
+}
+
} // namespace Fortran::evaluate
#endif // FORTRAN_EVALUATE_EXPRESSION_H_
diff --git a/flang/include/flang/Evaluate/fold-designator.h b/flang/include/flang/Evaluate/fold-designator.h
index 919f3c6db547b..8c1495b41511a 100644
--- a/flang/include/flang/Evaluate/fold-designator.h
+++ b/flang/include/flang/Evaluate/fold-designator.h
@@ -113,13 +113,13 @@ class DesignatorFolder {
return common::visit(
[&](const auto &x) { return FoldDesignator(x, which); }, designator.u);
}
- template <int KIND>
std::optional<OffsetSymbol> FoldDesignator(
- const Designator<Type<TypeCategory::Character, KIND>> &designator,
+ const Designator<Type<TypeCategory::Character>> &designator,
ConstantSubscript which) {
+ const int kind{designator.kind()};
return common::visit(
common::visitors{
- [&](const Substring &ss) {
+ [&, kind](const Substring &ss) {
if (const auto *dataRef{ss.GetParentIf<DataRef>()}) {
if (auto result{FoldDesignator(*dataRef, which)}) {
if (auto start{ToInt64(ss.lower())}) {
@@ -134,9 +134,9 @@ class DesignatorFolder {
if (*start < 1) {
isOutOfRange_ = true;
}
- result->Augment(KIND * (*start - 1));
+ result->Augment(kind * (*start - 1));
result->set_size(
- *end >= *start ? KIND * (*end - *start + 1) : 0);
+ *end >= *start ? kind * (*end - *start + 1) : 0);
if (len) {
if (auto lenVal{ToInt64(*len)}) {
if (*end > *lenVal) {
diff --git a/flang/include/flang/Evaluate/fold.h b/flang/include/flang/Evaluate/fold.h
index 709b40ec1aac3..40ed44b620356 100644
--- a/flang/include/flang/Evaluate/fold.h
+++ b/flang/include/flang/Evaluate/fold.h
@@ -74,25 +74,34 @@ constexpr auto GetScalarConstantValue(const EXPR &expr)
return std::nullopt;
}
}
+template <typename T, typename EXPR>
+constexpr auto GetScalarConstantValue(int kind, const EXPR &expr)
+ -> std::optional<Scalar<T>> {
+ if (const Constant<T> *constant{UnwrapConstantValue<T>(expr)}) {
+ if (constant->kind() == kind) {
+ return constant->GetScalarValue();
+ } else {
+ return std::nullopt;
+ }
+ } else {
+ return std::nullopt;
+ }
+}
// When an expression is a constant integer, ToInt64() extracts its value.
// Ensure that the expression has been folded beforehand when folding might
// be required.
-template <int KIND>
-constexpr std::optional<std::int64_t> ToInt64(
- const Expr<Type<TypeCategory::Integer, KIND>> &expr) {
- if (auto scalar{
- GetScalarConstantValue<Type<TypeCategory::Integer, KIND>>(expr)}) {
+inline std::optional<std::int64_t> ToInt64(
+ const Expr<Type<TypeCategory::Integer>> &expr) {
+ if (auto scalar{GetScalarConstantValue<Type<TypeCategory::Integer>>(expr)}) {
return scalar->ToInt64();
} else {
return std::nullopt;
}
}
-template <int KIND>
-constexpr std::optional<std::int64_t> ToInt64(
- const Expr<Type<TypeCategory::Unsigned, KIND>> &expr) {
- if (auto scalar{
- GetScalarConstantValue<Type<TypeCategory::Unsigned, KIND>>(expr)}) {
+inline std::optional<std::int64_t> ToInt64(
+ const Expr<Type<TypeCategory::Unsigned>> &expr) {
+ if (auto scalar{GetScalarConstantValue<Type<TypeCategory::Unsigned>>(expr)}) {
return scalar->ToInt64();
} else {
return std::nullopt;
diff --git a/flang/include/flang/Evaluate/initial-image.h b/flang/include/flang/Evaluate/initial-image.h
index 9a767db95f6c6..59de3a883f258 100644
--- a/flang/include/flang/Evaluate/initial-image.h
+++ b/flang/include/flang/Evaluate/initial-image.h
@@ -20,6 +20,25 @@
namespace Fortran::evaluate {
+template <typename SCALAR>
+inline void StoreSerialValues(int kind, char *dst,
+ llvm::ArrayRef<SCALAR> values, size_t elementSize,
+ bool *changed = nullptr) {
+ for (auto [i, v] : llvm::enumerate(values)) {
+ CHECK(v.kind() == kind);
+ v.StoreRawBytes(dst + i * elementSize, elementSize, changed);
+ }
+}
+
+template <typename SCALAR>
+inline void LoadSerialValues(int kind, const char *src,
+ llvm::MutableArrayRef<SCALAR> values, size_t stride) {
+ for (auto it : llvm::enumerate(values)) {
+ it.value() = SCALAR::FromRawBytes(
+ kind, src + stride * it.index(), SCALAR::bytesStored(kind));
+ }
+}
+
class InitialImage {
public:
enum Result {
@@ -44,6 +63,7 @@ class InitialImage {
template <typename T>
Result Add(ConstantSubscript offset, std::size_t bytes, const Constant<T> &x,
FoldingContext &context) {
+ const int kind{x.kind()};
if (offset < 0 || offset + bytes > data_.size()) {
return OutOfRange;
} else {
@@ -56,21 +76,16 @@ class InitialImage {
return OkNoChange;
} else {
// TODO endianness
- auto *to{&data_.at(offset)};
- const auto *from{&x.values().at(0)};
- if (std::memcmp(to, from, bytes) == 0) {
- return OkNoChange;
- } else {
- std::memcpy(to, from, bytes);
- return Ok;
- }
+ bool changed{false};
+ StoreSerialValues(kind, &data_.at(offset),
+ llvm::ArrayRef<Scalar<T>>(x.values()), *elementBytes, &changed);
+ return changed ? Ok : OkNoChange;
}
}
}
- template <int KIND>
Result Add(ConstantSubscript offset, std::size_t bytes,
- const Constant<Type<TypeCategory::Character, KIND>> &x,
- FoldingContext &) {
+ const Constant<Type<TypeCategory::Character>> &x, FoldingContext &) {
+ const int kind{x.kind()};
if (offset < 0 || offset + bytes > data_.size()) {
return OutOfRange;
} else {
@@ -87,13 +102,13 @@ class InitialImage {
} else {
Result result{OkNoChange};
for (auto at{x.lbounds()}; elements-- > 0; x.IncrementSubscripts(at)) {
- auto scalar{x.At(at)}; // this is a std string; size() in chars
- auto scalarBytes{scalar.size() * KIND};
+ auto scalar{x.At(at)}; // a CharacterValue; size() in chars
+ auto scalarBytes{scalar.size() * kind};
if (scalarBytes != elementBytes) {
result = LengthMismatch;
}
// Blank padding when short
- for (; scalarBytes < elementBytes; scalarBytes += KIND) {
+ for (; scalarBytes < elementBytes; scalarBytes += kind) {
scalar += ' ';
}
// TODO endianness
diff --git a/flang/include/flang/Evaluate/integer-value.h b/flang/include/flang/Evaluate/integer-value.h
new file mode 100644
index 0000000000000..c1777faf7ea95
--- /dev/null
+++ b/flang/include/flang/Evaluate/integer-value.h
@@ -0,0 +1,318 @@
+//===-- include/flang/Evaluate/integer-value.h ------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef FORTRAN_EVALUATE_INTEGER_VALUE_H_
+#define FORTRAN_EVALUATE_INTEGER_VALUE_H_
+
+#include "flang/Evaluate/common.h"
+#include "flang/Evaluate/object-sizes.h"
+#include <cstdint>
+#include <type_traits>
+
+// Some environments, viz. glibc 2.17 and *BSD, allow the macro HUGE
+// to leak out of <math.h>.
+#undef HUGE
+
+namespace Fortran::evaluate::value {
+class IntegerValueImpl;
+
+/// A two's-complement integer with dynamic bitwidth.
+///
+/// The bitwidth is dynamic, but only a predefined set of Fortran kinds are
+/// allowed. It is also kind-aware, i.e. knows which INTEGER kind it currently
+/// represents.
+///
+/// The implementation is hidden from this header using a pImpl-like idiom.
+class IntegerValue {
+ friend class RealValueImpl;
+
+public:
+ struct ValueWithOverflow;
+ struct ValueWithCarry;
+ struct Product;
+ struct QuotientWithRemainder;
+ struct PowerWithErrors;
+
+ IntegerValue();
+ ~IntegerValue();
+ IntegerValue(const IntegerValue &);
+ IntegerValue(IntegerValue &&);
+ IntegerValue &operator=(const IntegerValue &);
+ IntegerValue &operator=(IntegerValue &&);
+
+ IntegerValue(int kind, const IntegerValue &x) : IntegerValue(x) {
+ CHECK(x.kind() == kind);
+ }
+ IntegerValue(int kind, IntegerValue &&x) : IntegerValue(std::move(x)) {
+ CHECK(x.kind() == kind);
+ }
+
+ template <typename INT, typename = std::enable_if_t<std::is_integral_v<INT>>>
+ IntegerValue(int kind, INT n) {
+ ConstructFromIntegral(
+ kind, static_cast<std::uint64_t>(n), std::is_signed_v<INT>);
+ }
+
+ /// Creates an integer with value 0 of a given kind. This is different from
+ /// the default-ctor which creates a "monostate" that represents 0 of unknown
+ /// kind.
+ static IntegerValue Zero(int kind);
+
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+ LLVM_DUMP_METHOD void dump() const;
+#endif
+
+ /// Whether this object represents a default-initialized value (zero) of
+ /// not-yet-known kind.
+ bool IsMonostate() const;
+
+ /// The kind of the value currently stored.
+ int kind() const;
+
+ int bits() const { return bits(kind()); }
+ static constexpr int bits(int kind) { return bytesStored(kind) * 8; }
+
+ /// Number of bytes accessed by FromRawBytes/StoreRawBytes
+ std::size_t bytesStored() const { return bytesStored(kind()); }
+ static constexpr std::size_t bytesStored(int kind) {
+ switch (kind) {
+ case 3:
+ return 2;
+ case 10:
+ return 16;
+ default:
+ return kind;
+ }
+ }
+
+ bool operator<(const IntegerValue &y) const {
+ return CompareSigned(y) == Ordering::Less;
+ }
+ bool operator<=(const IntegerValue &y) const { return !(y < *this); }
+ bool operator==(const IntegerValue &y) const;
+ bool operator!=(const IntegerValue &y) const { return !(*this == y); }
+ bool operator>=(const IntegerValue &y) const { return !(*this < y); }
+ bool operator>(const IntegerValue &y) const { return y < *this; }
+
+ /// Left-justified mask (e.g., MASKL(1) has only its sign bit set)
+ static IntegerValue MASKL(int kind, int places);
+
+ /// Right-justified mask (e.g., MASKR(1) == 1, MASKR(2) == 3, &c.)
+ static IntegerValue MASKR(int kind, int places);
+
+ static ValueWithOverflow Read(
+ int kind, const char *&pp, int base, bool isSigned);
+
+ /// ZExt or Trunc
+ static ValueWithOverflow ConvertUnsigned(
+ const IntegerValue &from, int toBits);
+
+ /// SExt or Trunc
+ static ValueWithOverflow ConvertSigned(const IntegerValue &from, int toBits);
+
+ std::string UnsignedDecimal() const;
+
+ std::string SignedDecimal() const;
+
+ /// Omits a leading "0x".
+ std::string Hexadecimal() const;
+
+ static constexpr int DIGITS(int kind) {
+ // don't count the sign bit
+ return bits(kind) - 1;
+ }
+
+ static IntegerValue HUGE(int kind);
+
+ static IntegerValue Least(int kind);
+
+ static int RANGE(int kind);
+
+ static int UnsignedRANGE(int kind);
+
+ bool IsZero() const;
+
+ bool IsNegative() const;
+
+ Ordering CompareToZeroSigned() const;
+
+ /// Count the number of contiguous most-significant bit positions
+ /// that are clear.
+ int LEADZ() const;
+
+ /// Count the number of bit positions that are set.
+ int POPCNT() const;
+
+ /// True when POPCNT is odd.
+ bool POPPAR() const;
+
+ int TRAILZ() const;
+
+ bool BTEST(int pos) const;
+
+ Ordering CompareUnsigned(const IntegerValue &y) const;
+
+ bool BGE(const IntegerValue &y) const {
+ return CompareUnsigned(y) != Ordering::Less;
+ }
+ bool BGT(const IntegerValue &y) const {
+ return CompareUnsigned(y) == Ordering::Greater;
+ }
+ bool BLE(const IntegerValue &y) const { return !BGT(y); }
+ bool BLT(const IntegerValue &y) const { return !BGE(y); }
+
+ Ordering CompareSigned(const IntegerValue &y) const;
+
+ std::uint64_t ToUInt64() const;
+
+ std::int64_t ToInt64() const;
+
+ std::int64_t ToSInt() const { return ToInt64(); }
+
+ /// Ones'-complement (i.e., C's ~)
+ IntegerValue NOT() const;
+
+ /// Two's-complement negation (-x = ~x + 1).
+ /// An overflow flag accompanies the result, and will be true when the
+ /// operand is the most negative signed number (MASKL(1)).
+ ValueWithOverflow Negate() const;
+
+ ValueWithOverflow ABS() const;
+
+ /// Shifts the operand left when the count is positive, right when negative.
+ /// Vacated bit positions are filled with zeroes.
+ IntegerValue ISHFT(int count) const {
+ return count < 0 ? SHIFTR(-count) : SHIFTL(count);
+ }
+
+ /// Left shift with zero fill.
+ IntegerValue SHIFTL(int count) const;
+
+ /// Circular shift of a field of least-significant bits. The least-order
+ /// "size" bits are shifted circularly in place by "count" positions;
+ /// the shift is leftward if count is nonnegative, rightward otherwise.
+ /// Higher-order bits are unchanged.
+ IntegerValue ISHFTC(int count, int size) const;
+ IntegerValue ISHFTC(int count) const;
+
+ /// DSHIFTL(I,J) shifts I:J left; the second argument is the right fill.
+ IntegerValue DSHIFTL(const IntegerValue &fill, int count) const;
+
+ /// DSHIFTR(I,J) shifts I:J right; the *first* argument is the left fill.
+ IntegerValue DSHIFTR(const IntegerValue &v2, int count) const;
+
+ /// Vacated upper bits are filled with zeroes.
+ IntegerValue SHIFTR(int count) const;
+
+ /// Be advised, an arithmetic (sign-filling) right shift is not
+ /// the same as a division by a power of two in all cases.
+ IntegerValue SHIFTA(int count) const;
+
+ /// Clears a single bit.
+ IntegerValue IBCLR(int pos) const;
+
+ /// Sets a single bit.
+ IntegerValue IBSET(int pos) const;
+
+ /// Extracts a field.
+ IntegerValue IBITS(int pos, int size) const;
+
+ IntegerValue IAND(const IntegerValue &y) const;
+
+ IntegerValue IOR(const IntegerValue &y) const;
+
+ IntegerValue IEOR(const IntegerValue &y) const;
+
+ IntegerValue MERGE_BITS(
+ const IntegerValue &y, const IntegerValue &mask) const;
+
+ IntegerValue MAX(const IntegerValue &y) const {
+ return CompareSigned(y) == Ordering::Less ? y : *this;
+ }
+
+ IntegerValue MIN(const IntegerValue &y) const {
+ return CompareSigned(y) == Ordering::Less ? *this : y;
+ }
+
+ ValueWithCarry AddUnsigned(const IntegerValue &y, bool carryIn = false) const;
+
+ ValueWithOverflow AddSigned(const IntegerValue &y) const;
+
+ ValueWithOverflow SubtractSigned(const IntegerValue &y) const;
+
+ /// DIM(X,Y)=MAX(X-Y, 0)
+ ValueWithOverflow DIM(const IntegerValue &y) const;
+
+ ValueWithOverflow SIGN(const IntegerValue &sign) const;
+
+ Product MultiplyUnsigned(const IntegerValue &y) const;
+
+ Product MultiplySigned(const IntegerValue &y) const;
+
+ QuotientWithRemainder DivideUnsigned(const IntegerValue &y) const;
+
+ /// A nonzero remainder has the sign of the dividend, i.e., it computes
+ /// the MOD intrinsic (X-INT(X/Y)*Y), not MODULO (which is below).
+ /// 8/5 = 1r3; -8/5 = -1r-3; 8/-5 = -1r3; -8/-5 = 1r-3
+ QuotientWithRemainder DivideSigned(const IntegerValue &y) const;
+
+ /// Result has the sign of the divisor argument.
+ /// 8 mod 5 = 3; -8 mod 5 = 2; 8 mod -5 = -2; -8 mod -5 = -3
+ ValueWithOverflow MODULO(const IntegerValue &y) const;
+
+ PowerWithErrors Power(const IntegerValue &e) const;
+
+ static IntegerValue FromRawBytes(
+ int kind, const void *raw, std::size_t expectedSize);
+ void StoreRawBytes(void *dst, size_t size, bool *changed = nullptr) const;
+
+private:
+ void ConstructFromIntegral(int kind, std::uint64_t n, bool isSigned);
+
+ static IntegerValue FromImpl(const IntegerValueImpl &x);
+ static IntegerValue FromImpl(IntegerValueImpl &&x);
+
+ IntegerValueImpl &impl() {
+ return *reinterpret_cast<IntegerValueImpl *>(this);
+ }
+ const IntegerValueImpl &impl() const {
+ return *reinterpret_cast<const IntegerValueImpl *>(this);
+ }
+
+ [[maybe_unused]] alignas(
+ detail::kIntegerObjectAlign) char opaque_[detail::kIntegerObjectSize];
+};
+
+struct IntegerValue::ValueWithOverflow {
+ IntegerValue value;
+ bool overflow{false};
+};
+
+struct IntegerValue::ValueWithCarry {
+ IntegerValue value;
+ bool carry{false};
+};
+
+struct IntegerValue::Product {
+ IntegerValue upper, lower;
+ bool SignedMultiplicationOverflowed() const { return overflow; }
+ bool overflow{false};
+};
+
+struct IntegerValue::QuotientWithRemainder {
+ IntegerValue quotient, remainder;
+ bool divisionByZero{false}, overflow{false};
+};
+
+struct IntegerValue::PowerWithErrors {
+ IntegerValue power;
+ bool divisionByZero{false}, overflow{false}, zeroToZero{false};
+};
+
+} // namespace Fortran::evaluate::value
+#endif // FORTRAN_EVALUATE_INTEGER_VALUE_H_
diff --git a/flang/include/flang/Evaluate/logical-value.h b/flang/include/flang/Evaluate/logical-value.h
new file mode 100644
index 0000000000000..cfc5e8db6abd7
--- /dev/null
+++ b/flang/include/flang/Evaluate/logical-value.h
@@ -0,0 +1,153 @@
+//===-- include/flang/Evaluate/logical-value.h ------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef FORTRAN_EVALUATE_LOGICAL_VALUE_H_
+#define FORTRAN_EVALUATE_LOGICAL_VALUE_H_
+
+#include "integer-value.h"
+#include <utility>
+
+namespace Fortran::evaluate::value {
+
+/// A Fortran LOGICAL value.
+///
+/// The kind is dynamic, but only a predefined set of Fortran kinds are
+/// allowed. It is also kind-aware, i.e. knows which LOGICAL kind it currently
+/// represents.
+///
+/// It is implemented as a wrapper around IntegerValue.
+class LogicalValue {
+public:
+ using Word = IntegerValue;
+
+ LogicalValue() {}
+ LogicalValue(const LogicalValue &) = default;
+ LogicalValue(LogicalValue &&) = default;
+ LogicalValue &operator=(const LogicalValue &) = default;
+ LogicalValue &operator=(LogicalValue &&) = default;
+
+ LogicalValue(int kind, const LogicalValue &v) : LogicalValue{v} {
+ CHECK(kind == v.kind());
+ }
+
+ LogicalValue(int kind, LogicalValue &&v) : LogicalValue{std::move(v)} {
+ CHECK(kind == v.kind());
+ }
+
+ LogicalValue(int kind, bool truth) : word_(Represent(kind, truth)) {}
+
+ LogicalValue(int kind, const Word &w) : word_(kind, w) {}
+
+ /// Creates a logical with value 'false' of a given kind. This is in contrast
+ /// to the default-ctor which creates a "monostate" that represents 'false' of
+ /// a not-yet-known kind.
+ static LogicalValue Zero(int kind) { return LogicalValue{kind, false}; }
+
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+ LLVM_DUMP_METHOD void dump() const;
+#endif
+
+ /// Whether this object represents a default-initialized value ('false') of
+ /// unknown kind.
+ bool IsMonostate() const { return word_.IsMonostate(); }
+
+ /// The kind of the value currently stored.
+ int kind() const { return word_.kind(); }
+
+ int bits() const { return bits(kind()); }
+ static constexpr int bits(int kind) { return Word::bits(kind); }
+
+ /// Number of bytes accessed by FromRawBytes/StoreRawBytes
+ std::size_t bytesStored() const { return bytesStored(kind()); }
+ static constexpr std::size_t bytesStored(int kind) {
+ return Word::bytesStored(kind);
+ }
+
+ Word word() const { return word_; }
+
+ bool IsCanonical() const {
+ const int kind{this->kind()};
+ return word_ == canonicalFalse(kind) || word_ == canonicalTrue(kind);
+ }
+
+ /// Fortran actually has only .EQV. & .NEQV. relational operations
+ /// for LOGICAL, but this class supports more so that it can be used
+ /// with the STL for sorting and as a key type for std::set<> & std::map<>.
+ bool operator<(const LogicalValue &that) const {
+ return !IsTrue() && that.IsTrue();
+ }
+ bool operator<=(const LogicalValue &that) const { return !IsTrue(); }
+ bool operator==(const LogicalValue &that) const {
+ return IsTrue() == that.IsTrue();
+ }
+ bool operator!=(const LogicalValue &that) const {
+ return IsTrue() != that.IsTrue();
+ }
+
+ bool operator>=(const LogicalValue &that) const { return IsTrue(); }
+
+ bool operator>(const LogicalValue &that) const {
+ return IsTrue() && !that.IsTrue();
+ }
+
+ bool IsTrue() const { return !word_.IsZero(); }
+
+ LogicalValue NOT() const {
+ return FromWord(word_.IEOR(canonicalTrue(kind())));
+ }
+
+ LogicalValue AND(const LogicalValue &that) const {
+ return FromWord(word_.IAND(that.word()));
+ }
+
+ LogicalValue OR(const LogicalValue &that) const {
+ return FromWord(word_.IOR(that.word()));
+ }
+
+ LogicalValue EQV(const LogicalValue &that) const { return NEQV(that).NOT(); }
+
+ LogicalValue NEQV(const LogicalValue &that) const {
+ return FromWord(word_.IEOR(that.word()));
+ }
+
+ static LogicalValue FromRawBytes(
+ int kind, const void *raw, std::size_t expectedSize) {
+ Word w{Word::FromRawBytes(kind, raw, expectedSize)};
+ return LogicalValue{w.kind(), w};
+ }
+
+ void StoreRawBytes(void *dst, size_t size, bool *changed = nullptr) const {
+ word_.StoreRawBytes(dst, size, changed);
+ }
+
+private:
+ static Word canonicalTrue(int kind) { return Word{kind, 1}; }
+
+ static Word canonicalFalse(int kind) { return Word{kind, 0}; }
+
+ static Word Represent(int kind, bool x) {
+ return x ? canonicalTrue(kind) : canonicalFalse(kind);
+ }
+
+ static LogicalValue FromWord(const Word &w) {
+ LogicalValue v;
+ v.word_ = w;
+ return v;
+ }
+
+ static LogicalValue FromWord(Word &&w) {
+ LogicalValue v;
+ v.word_ = std::move(w);
+ return v;
+ }
+
+ Word word_;
+};
+
+} // namespace Fortran::evaluate::value
+#endif // FORTRAN_EVALUATE_LOGICAL_VALUE_H_
diff --git a/flang/include/flang/Evaluate/logical.h b/flang/include/flang/Evaluate/logical.h
deleted file mode 100644
index 5996853215e30..0000000000000
--- a/flang/include/flang/Evaluate/logical.h
+++ /dev/null
@@ -1,110 +0,0 @@
-//===-- include/flang/Evaluate/logical.h ------------------------*- C++ -*-===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef FORTRAN_EVALUATE_LOGICAL_H_
-#define FORTRAN_EVALUATE_LOGICAL_H_
-
-#include "integer.h"
-#include <cinttypes>
-
-namespace Fortran::evaluate::value {
-
-template <int BITS, bool IS_LIKE_C = true> class Logical {
-public:
- static constexpr int bits{BITS};
- using Word = Integer<bits>;
-
- // Module ISO_C_BINDING kind C_BOOL is LOGICAL(KIND=1) and must have
- // C's bit representation (.TRUE. -> 1, .FALSE. -> 0).
- static constexpr bool IsLikeC{BITS <= 8 || IS_LIKE_C};
-
- constexpr Logical() {} // .FALSE.
- template <int B, bool C>
- constexpr Logical(Logical<B, C> x) : word_{Represent(x.IsTrue())} {}
- constexpr Logical(bool truth) : word_{Represent(truth)} {}
- // A raw word, for DATA initialization
- constexpr Logical(Word &&w) : word_{std::move(w)} {}
-
- template <int B, bool C> constexpr Logical &operator=(Logical<B, C> x) {
- word_ = Represent(x.IsTrue());
- return *this;
- }
-
- Word word() const { return word_; }
- bool IsCanonical() const {
- return word_ == canonicalFalse || word_ == canonicalTrue;
- }
-
- // Fortran actually has only .EQV. & .NEQV. relational operations
- // for LOGICAL, but this template class supports more so that
- // it can be used with the STL for sorting and as a key type for
- // std::set<> & std::map<>.
- template <int B, bool C>
- constexpr bool operator<(const Logical<B, C> &that) const {
- return !IsTrue() && that.IsTrue();
- }
- template <int B, bool C>
- constexpr bool operator<=(const Logical<B, C> &) const {
- return !IsTrue();
- }
- template <int B, bool C>
- constexpr bool operator==(const Logical<B, C> &that) const {
- return IsTrue() == that.IsTrue();
- }
- template <int B, bool C>
- constexpr bool operator!=(const Logical<B, C> &that) const {
- return IsTrue() != that.IsTrue();
- }
- template <int B, bool C>
- constexpr bool operator>=(const Logical<B, C> &) const {
- return IsTrue();
- }
- template <int B, bool C>
- constexpr bool operator>(const Logical<B, C> &that) const {
- return IsTrue() && !that.IsTrue();
- }
-
- constexpr bool IsTrue() const {
- if constexpr (IsLikeC) {
- return !word_.IsZero();
- } else {
- return word_.BTEST(0);
- }
- }
-
- constexpr Logical NOT() const { return {word_.IEOR(canonicalTrue)}; }
-
- constexpr Logical AND(const Logical &that) const {
- return {word_.IAND(that.word_)};
- }
-
- constexpr Logical OR(const Logical &that) const {
- return {word_.IOR(that.word_)};
- }
-
- constexpr Logical EQV(const Logical &that) const { return NEQV(that).NOT(); }
-
- constexpr Logical NEQV(const Logical &that) const {
- return {word_.IEOR(that.word_)};
- }
-
-private:
- static constexpr Word canonicalTrue{IsLikeC ? 1 : -std::uint64_t{1}};
- static constexpr Word canonicalFalse{0};
- static constexpr Word Represent(bool x) {
- return x ? canonicalTrue : canonicalFalse;
- }
- Word word_;
-};
-
-extern template class Logical<8>;
-extern template class Logical<16>;
-extern template class Logical<32>;
-extern template class Logical<64>;
-} // namespace Fortran::evaluate::value
-#endif // FORTRAN_EVALUATE_LOGICAL_H_
diff --git a/flang/include/flang/Evaluate/match.h b/flang/include/flang/Evaluate/match.h
index dfbfd1b4b64e3..c579862752ee9 100644
--- a/flang/include/flang/Evaluate/match.h
+++ b/flang/include/flang/Evaluate/match.h
@@ -36,9 +36,9 @@ struct IsOperation<T, std::void_t<decltype(T::operands)>> {
template <typename T>
constexpr bool is_operation_v{detail::IsOperation<T>::value};
-template <common::TypeCategory C, int K>
-const evaluate::Expr<Type<C, K>> &deparen(const evaluate::Expr<Type<C, K>> &x) {
- if (auto *parens{std::get_if<Parentheses<Type<C, K>>>(&x.u)}) {
+template <common::TypeCategory C>
+const evaluate::Expr<Type<C>> &deparen(const evaluate::Expr<Type<C>> &x) {
+ if (auto *parens{std::get_if<Parentheses<Type<C>>>(&x.u)}) {
return deparen(parens->template operand<0>());
} else {
return x;
@@ -189,16 +189,13 @@ OperationPattern(const Ops &..., llvm::type_identity<OpType>)
// only from operand patterns. This will make it usable in AnyOfPattern.
template <common::LogicalOperator Operator, typename ValType, typename... Ops>
struct LogicalOperationPattern
- : public OperationPattern<LogicalOperation<ValType::kind>, Ops...> {
- using Base = OperationPattern<LogicalOperation<ValType::kind>, Ops...>;
+ : public OperationPattern<LogicalOperation, Ops...> {
+ using Base = OperationPattern<LogicalOperation, Ops...>;
static constexpr common::LogicalOperator opCode{Operator};
private:
- template <int K> bool matchOp(const LogicalOperation<K> &op) const {
- if constexpr (ValType::kind == K) {
- return op.logicalOperator == opCode;
- }
- return false;
+ bool matchOp(const LogicalOperation &op) const {
+ return op.logicalOperator == opCode;
}
template <typename U> bool matchOp(const U &) const { return false; }
diff --git a/flang/include/flang/Evaluate/object-sizes.h b/flang/include/flang/Evaluate/object-sizes.h
new file mode 100644
index 0000000000000..eae220d34fff0
--- /dev/null
+++ b/flang/include/flang/Evaluate/object-sizes.h
@@ -0,0 +1,81 @@
+//===-- include/flang/Evaluate/object-sizes.h -------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Object size/alignment for the opaque facades IntegerValue, RealValue,
+// CharacterValue and their variant-backed implementations IntegerValueImpl,
+// RealValueImpl, CharacterValueImpl.
+//
+// When not cross-compiling, flang-evaluate-object-size-probe measures these
+// with the very toolchain (and per build configuration) used for the build and
+// emits object-sizes-generated.h into the build tree's include
+// directory. Those values directly measured are preferred whenever that header
+// is available on the include path, regardless of -I ordering. The constants
+// below are the fallback used otherwise -- in particular when cross-compiling,
+// where the probe cannot run on the build host. They are verified against the
+// implementation classes by static_asserts in integer-value.cpp, real-value.cpp
+// and character-value.cpp.
+//
+// The probe itself (object-size-probe.cpp) compiles with
+// FLANG_OBJECT_SIZE_PROBE defined: it generates the header, so it
+// must not depend on it. The dedicated #if branch below omits __has_include so
+// dependency scanners do not record the generated header (probe -> generated
+// header -> probe cycle).
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef FORTRAN_EVALUATE_OBJECT_SIZES_H_
+#define FORTRAN_EVALUATE_OBJECT_SIZES_H_
+
+#include <cstddef>
+
+#ifdef FLANG_OBJECT_SIZE_PROBE
+#error This header must not be included into the object-size-probe executable itself (in particular, integer-value-impl.h, real-value-impl.h, character-value-impl.h); it would cause a dependency cycle in incremental builds.
+#endif
+
+#if __has_include(<flang/Evaluate/object-sizes-generated.h>)
+// Measured object sizes
+#include <flang/Evaluate/object-sizes-generated.h>
+#else
+// Fallback known object sizes
+//
+// These fallbacks assume a 64-bit (LP64/LLP64) host, which covers the targets
+// flang is built for (x86_64, AArch64, PowerPC64).
+namespace Fortran::evaluate::value::detail {
+
+inline constexpr std::size_t kIntegerObjectSize{20};
+inline constexpr std::size_t kIntegerObjectAlign{4};
+
+inline constexpr std::size_t kRealObjectSize{32};
+inline constexpr std::size_t kRealObjectAlign{16};
+
+// CharacterValueImpl is a
+// std::variant<std::string, std::u16string, std::u32string>.
+//
+// * MSVC STL: 48 bytes with _ITERATOR_DEBUG_LEVEL==2
+// 40 bytes otherwise
+// * libc++: 32 bytes
+// invariant to _LIBCPP_HARDENING_MODE
+// * libstdc++: 40 bytes
+// invariant to _GLIBCXX_ASSERTIONS or _GLIBCXX_DEBUG
+#if defined(_MSC_VER) && \
+ ((defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL >= 2) || \
+ (!defined(_ITERATOR_DEBUG_LEVEL) && defined(_DEBUG)))
+inline constexpr std::size_t kCharacterObjectSize{48};
+#elif defined(_LIBCPP_VERSION)
+inline constexpr std::size_t kCharacterObjectSize{32};
+#elif defined(__GLIBCXX__) || defined(__GLIBCPP__)
+inline constexpr std::size_t kCharacterObjectSize{40};
+#else
+#error Unknown STL implementation
+#endif
+inline constexpr std::size_t kCharacterObjectAlign{8};
+
+} // namespace Fortran::evaluate::value::detail
+#endif
+
+#endif // FORTRAN_EVALUATE_OBJECT_SIZES_H_
diff --git a/flang/include/flang/Evaluate/real-value.h b/flang/include/flang/Evaluate/real-value.h
new file mode 100644
index 0000000000000..aa1ab0164b20b
--- /dev/null
+++ b/flang/include/flang/Evaluate/real-value.h
@@ -0,0 +1,237 @@
+//===-- include/flang/Evaluate/real-value.h ---------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef FORTRAN_EVALUATE_REAL_VALUE_H_
+#define FORTRAN_EVALUATE_REAL_VALUE_H_
+
+#include "flang/Evaluate/integer-value.h"
+#include "flang/Evaluate/object-sizes.h"
+#include "flang/Evaluate/target.h"
+
+// Some environments, viz. glibc 2.17 and *BSD, allow the macro HUGE
+// to leak out of <math.h>.
+#undef HUGE
+
+namespace Fortran::evaluate::value {
+class RealValueImpl;
+
+/// A floating-point value with dynamic precision.
+///
+/// The precision is dynamic, but only a predefined set of Fortran kinds are
+/// allowed. It is also kind-aware, i.e. knows which REAL kind it currently
+/// represents.
+///
+/// The implementation is hidden from this header using a pImpl-like idiom.
+class RealValue {
+public:
+ using Word = IntegerValue;
+
+ RealValue();
+ ~RealValue();
+ RealValue(const RealValue &);
+ RealValue(RealValue &&);
+ RealValue &operator=(const RealValue &);
+ RealValue &operator=(RealValue &&);
+
+ RealValue(int kind, const RealValue &v) : RealValue(v) {
+ CHECK(kind == v.kind());
+ }
+ RealValue(int kind, RealValue &&v) : RealValue(std::move(v)) {
+ CHECK(kind == v.kind());
+ }
+
+ /// Interpret w as the raw bit pattern for the given runtime kind.
+ RealValue(int kind, const Word &w);
+
+ /// Creates a floating-point with value +0.0 of a given kind. In contrast, the
+ /// default ctor creates a "monostate" that represents +0.0 of unknown kind.
+ static RealValue Zero(int kind);
+
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+ LLVM_DUMP_METHOD void dump() const;
+#endif
+
+ /// Whether this object represents a default-initialized value (zero) or
+ /// unknown value.
+ bool IsMonostate() const;
+
+ /// The kind of the value currently stored.
+ int kind() const;
+
+ int bits() const { return bits(kind()); }
+ static constexpr int bits(int kind) { return bytesStored(kind) * 8; }
+
+ /// Number of bytes accessed by FromRawBytes/StoreRawBytes
+ std::size_t bytesStored() const { return bytesStored(kind()); }
+ static constexpr std::size_t bytesStored(int kind) {
+ switch (kind) {
+ case 3:
+ return 2;
+ case 10:
+ return 16;
+ default:
+ return kind;
+ }
+ }
+
+ bool operator==(const RealValue &y) const;
+ bool operator!=(const RealValue &y) const { return !operator==(y); }
+
+ bool IsNegative() const;
+
+ bool IsNotANumber() const;
+
+ bool IsSignalingNaN() const;
+
+ bool IsInfinite() const;
+
+ bool IsFinite() const;
+
+ bool IsZero() const;
+
+ bool IsNormal() const;
+
+ RealValue ABS() const;
+
+ RealValue SetSign(bool toNegative) const;
+
+ RealValue SIGN(const RealValue &x) const;
+
+ RealValue Negate() const;
+
+ Relation Compare(const RealValue &y) const;
+
+ ValueWithRealFlags<RealValue> Add(const RealValue &y,
+ Rounding rounding = TargetCharacteristics::defaultRounding) const;
+
+ ValueWithRealFlags<RealValue> Subtract(const RealValue &y,
+ Rounding rounding = TargetCharacteristics::defaultRounding) const;
+
+ ValueWithRealFlags<RealValue> Multiply(const RealValue &y,
+ Rounding rounding = TargetCharacteristics::defaultRounding) const;
+
+ ValueWithRealFlags<RealValue> Divide(const RealValue &y,
+ Rounding rounding = TargetCharacteristics::defaultRounding) const;
+
+ ValueWithRealFlags<RealValue> SQRT(
+ Rounding rounding = TargetCharacteristics::defaultRounding) const;
+
+ /// NEAREST(), IEEE_NEXT_AFTER(), IEEE_NEXT_UP(), and IEEE_NEXT_DOWN()
+ ValueWithRealFlags<RealValue> NEAREST(bool upward) const;
+
+ /// HYPOT(x,y)=SQRT(x**2 + y**2) computed so as to avoid spurious
+ /// intermediate overflows.
+ ValueWithRealFlags<RealValue> HYPOT(const RealValue &y,
+ Rounding rounding = TargetCharacteristics::defaultRounding) const;
+
+ /// DIM(X,Y) = MAX(X-Y, 0)
+ ValueWithRealFlags<RealValue> DIM(const RealValue &y,
+ Rounding rounding = TargetCharacteristics::defaultRounding) const;
+
+ /// MOD(x,y) = x - AINT(x/y)*y (in the standard)
+ ValueWithRealFlags<RealValue> MOD(const RealValue &y,
+ Rounding rounding = TargetCharacteristics::defaultRounding) const;
+
+ /// MODULO(x,y) = x - FLOOR(x/y)*y (in the standard)
+ ValueWithRealFlags<RealValue> MODULO(const RealValue &y,
+ Rounding rounding = TargetCharacteristics::defaultRounding) const;
+
+ ValueWithRealFlags<RealValue> KahanSummation(const RealValue &y,
+ RealValue &correction,
+ Rounding rounding = TargetCharacteristics::defaultRounding) const;
+
+ IntegerValue EXPONENT() const;
+
+ static RealValue EPSILON(int kind);
+
+ static RealValue HUGE(int kind);
+
+ static RealValue TINY(int kind);
+
+ static int DIGITS(int kind);
+
+ static int PRECISION(int kind);
+
+ static int RANGE(int kind);
+
+ static int MAXEXPONENT(int kind);
+
+ static int MINEXPONENT(int kind);
+
+ RealValue RRSPACING() const;
+
+ RealValue SPACING() const;
+
+ RealValue SET_EXPONENT(std::int64_t e) const;
+
+ RealValue FRACTION() const;
+
+ /// SCALE(); also known as IEEE_SCALB and (in IEEE-754 '08) ScaleB.
+ ValueWithRealFlags<RealValue> SCALE(const IntegerValue &by,
+ Rounding rounding = TargetCharacteristics::defaultRounding) const;
+
+ RealValue FlushSubnormalToZero() const;
+
+ // TODO: Configurable NotANumber representations
+ static RealValue NotANumber(int kind);
+
+ static ValueWithRealFlags<RealValue> FromInteger(int kind,
+ const IntegerValue &n, bool isUnsigned = false,
+ Rounding rounding = TargetCharacteristics::defaultRounding);
+
+ /// Conversion to integer in the same real format (AINT(), ANINT())
+ ValueWithRealFlags<RealValue> ToWholeNumber(
+ common::RoundingMode mode = common::RoundingMode::ToZero) const;
+
+ /// Conversion to an integer (INT(), NINT(), FLOOR(), CEILING())
+ ValueWithRealFlags<IntegerValue> ToInteger(
+ common::RoundingMode mode = common::RoundingMode::ToZero,
+ int toBits = 0) const;
+
+ static ValueWithRealFlags<RealValue> Convert(int kind, const RealValue &from,
+ Rounding rounding = TargetCharacteristics::defaultRounding);
+
+ Word RawBits() const;
+
+ /// Extracts "raw" biased exponent field.
+ int Exponent() const;
+
+ static ValueWithRealFlags<RealValue> Read(int kind, const char *&pp,
+ Rounding rounding = TargetCharacteristics::defaultRounding);
+
+ std::string DumpHexadecimal() const;
+
+ /// Emits a character representation for an equivalent Fortran constant
+ /// or parenthesized constant expression that produces this value.
+ llvm::raw_ostream &AsFortran(
+ llvm::raw_ostream &o, int kind, bool minimal = false) const;
+
+ static RealValue FromRawBytes(
+ int kind, const void *raw, std::size_t expectedSize);
+
+ void StoreRawBytes(void *dst, size_t size, bool *changed = nullptr) const;
+
+private:
+ static RealValue FromImpl(const RealValueImpl &x);
+ static RealValue FromImpl(RealValueImpl &&x);
+ static ValueWithRealFlags<RealValue> FromImpl(
+ const ValueWithRealFlags<RealValueImpl> &x);
+ static ValueWithRealFlags<RealValue> FromImpl(
+ ValueWithRealFlags<RealValueImpl> &&x);
+
+ RealValueImpl &impl() { return *reinterpret_cast<RealValueImpl *>(this); }
+ const RealValueImpl &impl() const {
+ return *reinterpret_cast<const RealValueImpl *>(this);
+ }
+
+ [[maybe_unused]] alignas(
+ detail::kRealObjectAlign) char opaque_[detail::kRealObjectSize];
+};
+
+} // namespace Fortran::evaluate::value
+#endif // FORTRAN_EVALUATE_REAL_VALUE_H_
diff --git a/flang/include/flang/Evaluate/rewrite.h b/flang/include/flang/Evaluate/rewrite.h
index b571a78b81bd6..92c058b29d792 100644
--- a/flang/include/flang/Evaluate/rewrite.h
+++ b/flang/include/flang/Evaluate/rewrite.h
@@ -126,7 +126,8 @@ template <typename Rewriter> struct Mutator {
template <typename D, size_t... Is>
D MutateOp(D &&op, std::index_sequence<Is...>) const {
- return D(Mutate(std::move(op.template operand<Is>()))...);
+ const int kind{op.kind()};
+ return D(kind, Mutate(std::move(op.template operand<Is>()))...);
}
template <typename T, size_t... Is>
@@ -135,17 +136,17 @@ template <typename Rewriter> struct Mutator {
op.ordering, Mutate(std::move(op.template operand<Is>()))...);
}
- template <int K, size_t... Is>
- ComplexComponent<K> MutateOp(
- ComplexComponent<K> &&op, std::index_sequence<Is...>) const {
- return ComplexComponent<K>(
+ template <size_t... Is>
+ ComplexComponent MutateOp(
+ ComplexComponent &&op, std::index_sequence<Is...>) const {
+ return ComplexComponent(
op.isImaginaryPart, Mutate(std::move(op.template operand<Is>()))...);
}
- template <int K, size_t... Is>
- LogicalOperation<K> MutateOp(
- LogicalOperation<K> &&op, std::index_sequence<Is...>) const {
- return LogicalOperation<K>(
+ template <size_t... Is>
+ LogicalOperation MutateOp(
+ LogicalOperation &&op, std::index_sequence<Is...>) const {
+ return LogicalOperation(
op.logicalOperator, Mutate(std::move(op.template operand<Is>()))...);
}
diff --git a/flang/include/flang/Evaluate/shape.h b/flang/include/flang/Evaluate/shape.h
index e82401dcfebd8..5bca399c50ef4 100644
--- a/flang/include/flang/Evaluate/shape.h
+++ b/flang/include/flang/Evaluate/shape.h
@@ -31,6 +31,26 @@ using ExtentExpr = Expr<ExtentType>;
using MaybeExtentExpr = std::optional<ExtentExpr>;
using Shape = std::vector<MaybeExtentExpr>;
+inline constexpr int ExtentIntKind = Fortran::evaluate::SubscriptIntegerKind;
+
+inline Constant<ExtentType> MakeExtentConstant(int64_t v) {
+ return Constant<ExtentType>{
+ ExtentIntKind, Scalar<ExtentType>{ExtentIntKind, v}};
+}
+
+inline Constant<ExtentType> MakeExtentConstant(const value::IntegerValue &v) {
+ return Constant<ExtentType>{
+ ExtentIntKind, Scalar<ExtentType>{ExtentIntKind, v}};
+}
+
+inline ExtentExpr MakeExtentExpr(int64_t v) {
+ return ExtentExpr{MakeExtentConstant(v)};
+}
+
+inline ExtentExpr MakeExtentExpr(const value::IntegerValue &v) {
+ return ExtentExpr{MakeExtentConstant(v)};
+}
+
bool IsImpliedShape(const Symbol &);
bool IsExplicitShape(const Symbol &);
@@ -258,7 +278,7 @@ class GetShapeHelper
template <typename T>
MaybeExtentExpr GetArrayConstructorExtent(
const ArrayConstructorValues<T> &values) const {
- ExtentExpr result{0};
+ ExtentExpr result{MakeExtentConstant(0)};
for (const auto &value : values) {
if (MaybeExtentExpr n{GetArrayConstructorValueExtent(value)}) {
AccumulateExtent(result, std::move(*n));
diff --git a/flang/include/flang/Evaluate/static-data.h b/flang/include/flang/Evaluate/static-data.h
index 833cc6cc6f3fa..f9cc60a4e574e 100644
--- a/flang/include/flang/Evaluate/static-data.h
+++ b/flang/include/flang/Evaluate/static-data.h
@@ -63,6 +63,7 @@ class StaticDataObject {
StaticDataObject &Push(const std::string &, bool /*ignored*/ = false);
StaticDataObject &Push(const std::u16string &, bool bigEndian = false);
StaticDataObject &Push(const std::u32string &, bool bigEndian = false);
+ StaticDataObject &Push(const value::CharacterValue &, bool bigEndian = false);
std::optional<std::string> AsString() const;
std::optional<std::u16string> AsU16String(bool bigEndian = false) const;
std::optional<std::u32string> AsU32String(bool bigEndian = false) const;
diff --git a/flang/include/flang/Evaluate/tools.h b/flang/include/flang/Evaluate/tools.h
index c877ec5f5705b..f50ddb53249fc 100644
--- a/flang/include/flang/Evaluate/tools.h
+++ b/flang/include/flang/Evaluate/tools.h
@@ -195,6 +195,27 @@ auto UnwrapExpr(B &x) -> common::Constify<A, B> * {
}
return nullptr;
}
+template <typename A, typename B>
+auto UnwrapExpr(int kind, B &x) -> common::Constify<A, B> * {
+ using Ty = std::decay_t<B>;
+ if (x.kind() != kind)
+ return nullptr;
+ if constexpr (std::is_same_v<A, Ty>) {
+ return &x;
+ } else if constexpr (std::is_same_v<Ty, ActualArgument>) {
+ if (auto *expr{x.UnwrapExpr()}) {
+ return UnwrapExpr<A>(*expr);
+ }
+ } else if constexpr (std::is_same_v<Ty, Expr<SomeType>>) {
+ return common::visit([](auto &x) { return UnwrapExpr<A>(x); }, x.u);
+ } else if constexpr (!common::HasMember<A, TypelessExpression>) {
+ if constexpr (std::is_same_v<Ty, Expr<ResultType<A>>> ||
+ std::is_same_v<Ty, Expr<SomeKind<ResultType<A>::category>>>) {
+ return common::visit([](auto &x) { return UnwrapExpr<A>(x); }, x.u);
+ }
+ }
+ return nullptr;
+}
template <typename A, typename B>
const A *UnwrapExpr(const std::optional<B> &x) {
@@ -204,6 +225,14 @@ const A *UnwrapExpr(const std::optional<B> &x) {
return nullptr;
}
}
+template <typename A, typename B>
+const A *UnwrapExpr(int kind, const std::optional<B> &x) {
+ if (x) {
+ return UnwrapExpr<A>(kind, *x);
+ } else {
+ return nullptr;
+ }
+}
template <typename A, typename B> A *UnwrapExpr(std::optional<B> &x) {
if (x) {
@@ -212,6 +241,13 @@ template <typename A, typename B> A *UnwrapExpr(std::optional<B> &x) {
return nullptr;
}
}
+template <typename A, typename B> A *UnwrapExpr(int kind, std::optional<B> &x) {
+ if (x) {
+ return UnwrapExpr<A>(kind, *x);
+ } else {
+ return nullptr;
+ }
+}
template <typename A, typename B> const A *UnwrapExpr(const B *x) {
if (x) {
@@ -221,6 +257,14 @@ template <typename A, typename B> const A *UnwrapExpr(const B *x) {
}
}
+template <typename A, typename B> const A *UnwrapExpr(int kind, const B *x) {
+ if (x) {
+ return UnwrapExpr<A>(kind, *x);
+ } else {
+ return nullptr;
+ }
+}
+
template <typename A, typename B> A *UnwrapExpr(B *x) {
if (x) {
return UnwrapExpr<A>(*x);
@@ -229,6 +273,14 @@ template <typename A, typename B> A *UnwrapExpr(B *x) {
}
}
+template <typename A, typename B> A *UnwrapExpr(int kind, B *x) {
+ if (x) {
+ return UnwrapExpr<A>(kind, *x);
+ } else {
+ return nullptr;
+ }
+}
+
// A variant of UnwrapExpr above that also skips through (parentheses)
// and conversions of kinds within a category. Useful for extracting LEN
// type parameter inquiries, at least.
@@ -545,50 +597,56 @@ const Symbol *GetLastPointerSymbol(const evaluate::DataRef &);
// one arbitrary expression to the type of another with ConvertTo(to, from).
template <typename TO, TypeCategory FROMCAT>
-Expr<TO> ConvertToType(Expr<SomeKind<FROMCAT>> &&x) {
+Expr<TO> ConvertToType(int toKind, Expr<SomeKind<FROMCAT>> &&x) {
static_assert(IsSpecificIntrinsicType<TO>);
if constexpr (FROMCAT == TO::category) {
- if (auto *already{std::get_if<Expr<TO>>(&x.u)}) {
+ auto *already{std::get_if<Expr<TO>>(&x.u)};
+ if (already && already->kind() == toKind) {
return std::move(*already);
} else {
- return Expr<TO>{Convert<TO, FROMCAT>{std::move(x)}};
+ return Expr<TO>{Convert<TO, FROMCAT>{toKind, std::move(x)}};
}
} else if constexpr (TO::category == TypeCategory::Complex) {
using Part = typename TO::Part;
- Scalar<Part> zero;
- return Expr<TO>{ComplexConstructor<TO::kind>{
- ConvertToType<Part>(std::move(x)), Expr<Part>{Constant<Part>{zero}}}};
+ return Expr<TO>{ComplexConstructor{
+ ConvertToType<Part>(toKind, std::move(x)), MakeZeroExpr<Part>(toKind)}};
} else if constexpr (FROMCAT == TypeCategory::Complex) {
// Extract and convert the real component of a complex value
return common::visit(
[&](auto &&z) {
using ZType = ResultType<decltype(z)>;
using Part = typename ZType::Part;
- return ConvertToType<TO, TypeCategory::Real>(Expr<SomeReal>{
- Expr<Part>{ComplexComponent<Part::kind>{false, std::move(z)}}});
+ return ConvertToType<TO, TypeCategory::Real>(toKind,
+ Expr<SomeReal>{
+ Expr<Part>{ComplexComponent{false, std::move(z)}}});
},
std::move(x.u));
} else {
- return Expr<TO>{Convert<TO, FROMCAT>{std::move(x)}};
+ return Expr<TO>{Convert<TO, FROMCAT>{toKind, std::move(x)}};
}
}
-template <typename TO, TypeCategory FROMCAT, int FROMKIND>
-Expr<TO> ConvertToType(Expr<Type<FROMCAT, FROMKIND>> &&x) {
- return ConvertToType<TO, FROMCAT>(Expr<SomeKind<FROMCAT>>{std::move(x)});
+template <typename TO, TypeCategory FROMCAT>
+Expr<TO> ConvertToType(int toKind, Expr<Type<FROMCAT>> &&x) {
+ return ConvertToType<TO, FROMCAT>(
+ toKind, Expr<SomeKind<FROMCAT>>{std::move(x)});
}
-template <typename TO> Expr<TO> ConvertToType(BOZLiteralConstant &&x) {
+template <typename TO>
+Expr<TO> ConvertToType(int toKind, BOZLiteralConstant &&x) {
static_assert(IsSpecificIntrinsicType<TO>);
if constexpr (TO::category == TypeCategory::Integer ||
TO::category == TypeCategory::Unsigned) {
- return Expr<TO>{
- Constant<TO>{Scalar<TO>::ConvertUnsigned(std::move(x)).value}};
+ return MakeConstantExpr<TO>(toKind,
+ Scalar<TO>::ConvertUnsigned(std::move(x), Scalar<TO>::bits(toKind))
+ .value);
} else {
static_assert(TO::category == TypeCategory::Real);
- using Word = typename Scalar<TO>::Word;
- return Expr<TO>{
- Constant<TO>{Scalar<TO>{Word::ConvertUnsigned(std::move(x)).value}}};
+ using Word = value::IntegerValue;
+ return MakeConstantExpr<TO>(toKind,
+ Scalar<TO>{toKind,
+ Word::ConvertUnsigned(std::move(x), Scalar<TO>::bits(toKind))
+ .value});
}
}
@@ -606,10 +664,10 @@ std::optional<Expr<SomeType>> ConvertToType(
const Symbol &, std::optional<Expr<SomeType>> &&);
// Conversions to the type of another expression
-template <TypeCategory TC, int TK, typename FROM>
-common::IfNoLvalue<Expr<Type<TC, TK>>, FROM> ConvertTo(
- const Expr<Type<TC, TK>> &, FROM &&x) {
- return ConvertToType<Type<TC, TK>>(std::move(x));
+template <TypeCategory TC, typename FROM>
+common::IfNoLvalue<Expr<Type<TC>>, FROM> ConvertTo(
+ int toKind, const Expr<Type<TC>> &to, FROM &&x) {
+ return ConvertToType<Type<TC>>(toKind, std::move(x));
}
template <TypeCategory TC, typename FROM>
@@ -618,46 +676,19 @@ common::IfNoLvalue<Expr<SomeKind<TC>>, FROM> ConvertTo(
return common::visit(
[&](const auto &toKindExpr) {
using KindExpr = std::decay_t<decltype(toKindExpr)>;
+ const int toKind{toKindExpr.kind()};
return AsCategoryExpr(
- ConvertToType<ResultType<KindExpr>>(std::move(from)));
- },
- to.u);
-}
-
-template <typename FROM>
-common::IfNoLvalue<Expr<SomeType>, FROM> ConvertTo(
- const Expr<SomeType> &to, FROM &&from) {
- return common::visit(
- [&](const auto &toCatExpr) {
- return AsGenericExpr(ConvertTo(toCatExpr, std::move(from)));
+ ConvertToType<ResultType<KindExpr>>(toKind, std::move(from)));
},
to.u);
}
// Convert an expression of some known category to a dynamically chosen
// kind of some category (usually but not necessarily distinct).
-template <TypeCategory TOCAT, typename VALUE> struct ConvertToKindHelper {
- using Result = std::optional<Expr<SomeKind<TOCAT>>>;
- using Types = CategoryTypes<TOCAT>;
- ConvertToKindHelper(int k, VALUE &&x) : kind{k}, value{std::move(x)} {}
- template <typename T> Result Test() {
- if (kind == T::kind) {
- return std::make_optional(
- AsCategoryExpr(ConvertToType<T>(std::move(value))));
- }
- return std::nullopt;
- }
- int kind;
- VALUE value;
-};
-
template <TypeCategory TOCAT, typename VALUE>
common::IfNoLvalue<Expr<SomeKind<TOCAT>>, VALUE> ConvertToKind(
int kind, VALUE &&x) {
- auto result{common::SearchTypes(
- ConvertToKindHelper<TOCAT, VALUE>{kind, std::move(x)})};
- CHECK(result.has_value());
- return *result;
+ return AsCategoryExpr(ConvertToType<Type<TOCAT>>(kind, std::move(x)));
}
// Given a type category CAT, SameKindExprs<CAT, N> is a variant that
@@ -678,25 +709,25 @@ using SameKindExprs =
template <TypeCategory CAT>
SameKindExprs<CAT, 2> AsSameKindExprs(
Expr<SomeKind<CAT>> &&x, Expr<SomeKind<CAT>> &&y) {
- return common::visit(
- [&](auto &&kx, auto &&ky) -> SameKindExprs<CAT, 2> {
- using XTy = ResultType<decltype(kx)>;
- using YTy = ResultType<decltype(ky)>;
- if constexpr (std::is_same_v<XTy, YTy>) {
- return {SameExprs<XTy>{std::move(kx), std::move(ky)}};
- } else if constexpr (XTy::kind < YTy::kind) {
- return {SameExprs<YTy>{ConvertTo(ky, std::move(kx)), std::move(ky)}};
- } else {
- return {SameExprs<XTy>{std::move(kx), ConvertTo(kx, std::move(ky))}};
- }
+ Expr<Type<CAT>> kx{std::get<Expr<Type<CAT>>>(std::move(x.u))};
+ int xKind{kx.kind()};
+ Expr<Type<CAT>> ky{std::get<Expr<Type<CAT>>>(std::move(y.u))};
+ int yKind{ky.kind()};
+ if (xKind == yKind) {
+ return {SameExprs<Type<CAT>>{std::move(kx), std::move(ky)}};
+ } else if (xKind < yKind) {
+ return {SameExprs<Type<CAT>>{
+ ConvertTo(yKind, ky, std::move(kx)), std::move(ky)}};
+ } else {
+ return {SameExprs<Type<CAT>>{
+ std::move(kx), ConvertTo(xKind, kx, std::move(ky))}};
+ }
#if !__clang__ && 100 * __GNUC__ + __GNUC_MINOR__ == 801
// Silence a bogus warning about a missing return with G++ 8.1.0.
// Doesn't execute, but must be correctly typed.
CHECK(!"can't happen");
- return {SameExprs<XTy>{std::move(kx), std::move(kx)}};
+ return {SameExprs<Type<CAT>>{std::move(kx), std::move(kx)}};
#endif
- },
- std::move(x.u), std::move(y.u));
}
// Ensure that both operands of an intrinsic REAL operation (or CMPLX()
@@ -719,9 +750,10 @@ std::optional<Expr<SomeComplex>> ConstructComplex(parser::ContextualMessages &,
template <typename A> Expr<TypeOf<A>> ScalarConstantToExpr(const A &x) {
using Ty = TypeOf<A>;
+ const int kind{x.kind()};
static_assert(
std::is_same_v<Scalar<Ty>, std::decay_t<A>>, "TypeOf<> is broken");
- return Expr<TypeOf<A>>{Constant<Ty>{x}};
+ return MakeConstantExpr<Ty>(kind, x);
}
// Combine two expressions of the same specific numeric type with an operation
@@ -729,7 +761,8 @@ template <typename A> Expr<TypeOf<A>> ScalarConstantToExpr(const A &x) {
template <template <typename> class OPR, typename SPECIFIC>
Expr<SPECIFIC> Combine(Expr<SPECIFIC> &&x, Expr<SPECIFIC> &&y) {
static_assert(IsSpecificIntrinsicType<SPECIFIC>);
- return AsExpr(OPR<SPECIFIC>{std::move(x), std::move(y)});
+ CHECK(x.kind() == y.kind());
+ return AsExpr(OPR<SPECIFIC>{x.kind(), std::move(x), std::move(y)});
}
// Given two expressions of arbitrary kind in the same intrinsic type
@@ -791,19 +824,18 @@ Expr<LogicalResult> PackageRelation(
Relational<SomeType>{Relational<T>{opr, std::move(x), std::move(y)}}};
}
-template <int K>
-Expr<Type<TypeCategory::Logical, K>> LogicalNegation(
- Expr<Type<TypeCategory::Logical, K>> &&x) {
- return AsExpr(Not<K>{std::move(x)});
+inline Expr<Type<TypeCategory::Logical>> LogicalNegation(
+ Expr<Type<TypeCategory::Logical>> &&x) {
+ const int kind{x.kind()};
+ return AsExpr(Not{kind, std::move(x)});
}
Expr<SomeLogical> LogicalNegation(Expr<SomeLogical> &&);
-template <int K>
-Expr<Type<TypeCategory::Logical, K>> BinaryLogicalOperation(LogicalOperator opr,
- Expr<Type<TypeCategory::Logical, K>> &&x,
- Expr<Type<TypeCategory::Logical, K>> &&y) {
- return AsExpr(LogicalOperation<K>{opr, std::move(x), std::move(y)});
+inline Expr<Type<TypeCategory::Logical>> BinaryLogicalOperation(
+ LogicalOperator opr, Expr<Type<TypeCategory::Logical>> &&x,
+ Expr<Type<TypeCategory::Logical>> &&y) {
+ return AsExpr(LogicalOperation{opr, std::move(x), std::move(y)});
}
Expr<SomeLogical> BinaryLogicalOperation(
@@ -814,29 +846,28 @@ Expr<SomeLogical> BinaryLogicalOperation(
// emit any message. Use the more general templates (above) in other
// situations.
-template <TypeCategory C, int K>
-Expr<Type<C, K>> operator-(Expr<Type<C, K>> &&x) {
- return AsExpr(Negate<Type<C, K>>{std::move(x)});
+template <TypeCategory C> Expr<Type<C>> operator-(Expr<Type<C>> &&x) {
+ return AsExpr(Negate<Type<C>>{std::move(x)});
}
-template <TypeCategory C, int K>
-Expr<Type<C, K>> operator+(Expr<Type<C, K>> &&x, Expr<Type<C, K>> &&y) {
- return AsExpr(Combine<Add, Type<C, K>>(std::move(x), std::move(y)));
+template <TypeCategory C>
+Expr<Type<C>> operator+(Expr<Type<C>> &&x, Expr<Type<C>> &&y) {
+ return AsExpr(Combine<Add, Type<C>>(std::move(x), std::move(y)));
}
-template <TypeCategory C, int K>
-Expr<Type<C, K>> operator-(Expr<Type<C, K>> &&x, Expr<Type<C, K>> &&y) {
- return AsExpr(Combine<Subtract, Type<C, K>>(std::move(x), std::move(y)));
+template <TypeCategory C>
+Expr<Type<C>> operator-(Expr<Type<C>> &&x, Expr<Type<C>> &&y) {
+ return AsExpr(Combine<Subtract, Type<C>>(std::move(x), std::move(y)));
}
-template <TypeCategory C, int K>
-Expr<Type<C, K>> operator*(Expr<Type<C, K>> &&x, Expr<Type<C, K>> &&y) {
- return AsExpr(Combine<Multiply, Type<C, K>>(std::move(x), std::move(y)));
+template <TypeCategory C>
+Expr<Type<C>> operator*(Expr<Type<C>> &&x, Expr<Type<C>> &&y) {
+ return AsExpr(Combine<Multiply, Type<C>>(std::move(x), std::move(y)));
}
-template <TypeCategory C, int K>
-Expr<Type<C, K>> operator/(Expr<Type<C, K>> &&x, Expr<Type<C, K>> &&y) {
- return AsExpr(Combine<Divide, Type<C, K>>(std::move(x), std::move(y)));
+template <TypeCategory C>
+Expr<Type<C>> operator/(Expr<Type<C>> &&x, Expr<Type<C>> &&y) {
+ return AsExpr(Combine<Divide, Type<C>>(std::move(x), std::move(y)));
}
template <TypeCategory C> Expr<SomeKind<C>> operator-(Expr<SomeKind<C>> &&x) {
@@ -879,9 +910,9 @@ struct TypeKindVisitor {
TypeKindVisitor(int k, VALUE &&x) : kind{k}, value{std::move(x)} {}
TypeKindVisitor(int k, const VALUE &x) : kind{k}, value{x} {}
- template <typename T> Result Test() {
- if (kind == T::kind) {
- return AsGenericExpr(TEMPLATE<T>{std::move(value)});
+ template <typename T> Result Test(int k) {
+ if (kind == k) {
+ return AsGenericExpr(TEMPLATE<T>{k, std::move(value)});
}
return std::nullopt;
}
@@ -897,7 +928,7 @@ template <TypeCategory CATEGORY, template <typename> typename WRAPPER,
typename WRAPPED>
common::IfNoLvalue<std::optional<Expr<SomeType>>, WRAPPED> WrapperHelper(
int kind, WRAPPED &&x) {
- return common::SearchTypes(
+ return SearchTypes(
TypeKindVisitor<CATEGORY, WRAPPER, WRAPPED>{kind, std::move(x)});
}
@@ -925,7 +956,8 @@ common::IfNoLvalue<std::optional<Expr<SomeType>>, WRAPPED> TypedWrapper(
return WrapperHelper<TypeCategory::Logical, WRAPPER, WRAPPED>(
dyType.kind(), std::move(x));
case TypeCategory::Derived:
- return AsGenericExpr(Expr<SomeDerived>{WRAPPER<SomeDerived>{std::move(x)}});
+ return AsGenericExpr(
+ Expr<SomeDerived>{WRAPPER<SomeDerived>{0, std::move(x)}});
}
}
@@ -1250,16 +1282,16 @@ class ScalarConstantExpander {
// If the type is Character or a derived type, take the length or type
// (resp.) from a another Constant.
template <typename T>
-Constant<T> PackageConstant(std::vector<Scalar<T>> &&elements,
+Constant<T> PackageConstant(int kind, std::vector<Scalar<T>> &&elements,
const Constant<T> &reference, const ConstantSubscripts &shape) {
if constexpr (T::category == TypeCategory::Character) {
return Constant<T>{
- reference.LEN(), std::move(elements), ConstantSubscripts{shape}};
+ kind, reference.LEN(), std::move(elements), ConstantSubscripts{shape}};
} else if constexpr (T::category == TypeCategory::Derived) {
return Constant<T>{reference.GetType().GetDerivedTypeSpec(),
std::move(elements), ConstantSubscripts{shape}};
} else {
- return Constant<T>{std::move(elements), ConstantSubscripts{shape}};
+ return Constant<T>{kind, std::move(elements), ConstantSubscripts{shape}};
}
}
@@ -1569,7 +1601,7 @@ using OperatorSet = common::EnumSet<Operator, 32>;
std::string ToString(Operator op);
-template <int Kind> Operator OperationCode(const LogicalOperation<Kind> &op) {
+inline Operator OperationCode(const LogicalOperation &op) {
switch (op.logicalOperator) {
case common::LogicalOperator::And:
return Operator::And;
diff --git a/flang/include/flang/Evaluate/type.h b/flang/include/flang/Evaluate/type.h
index 165784159b9ca..0974b87b01650 100644
--- a/flang/include/flang/Evaluate/type.h
+++ b/flang/include/flang/Evaluate/type.h
@@ -16,12 +16,13 @@
// are suitable for use as template parameters to instantiate other class
// templates, like expressions, over the supported types and kinds.
+#include "character-value.h"
#include "common.h"
-#include "complex.h"
+#include "complex-value.h"
#include "formatting.h"
-#include "integer.h"
-#include "logical.h"
-#include "real.h"
+#include "integer-value.h"
+#include "logical-value.h"
+#include "real-value.h"
#include "flang/Common/idioms.h"
#include "flang/Common/real.h"
#include "flang/Common/template.h"
@@ -52,15 +53,29 @@ using common::TypeCategory;
class TargetCharacteristics;
// Specific intrinsic types are represented by specializations of
-// this class template Type<CATEGORY, KIND>.
-template <TypeCategory CATEGORY, int KIND = 0> class Type;
+// this class template Type<CATEGORY>.
+// This used to be Type<CATEGORY,KIND>, but now KIND is passed at runtime.
+// TODO: Since they are functionally the same, replace all occurances of Type
+// with TypeCategory.
+template <TypeCategory CATEGORY> class Type;
-using SubscriptInteger = Type<TypeCategory::Integer, 8>;
-using CInteger = Type<TypeCategory::Integer, 4>;
-using LargestInt = Type<TypeCategory::Integer, 16>;
-using LogicalResult = Type<TypeCategory::Logical, 4>;
-using LargestReal = Type<TypeCategory::Real, 16>;
-using Ascii = Type<TypeCategory::Character, 1>;
+using SubscriptInteger = Type<TypeCategory::Integer>;
+inline constexpr int SubscriptIntegerKind{8};
+
+using CInteger = Type<TypeCategory::Integer>;
+inline constexpr int CIntegerKind{4};
+
+using LargestInt = Type<TypeCategory::Integer>;
+inline constexpr int LargestIntKind{16};
+
+using LogicalResult = Type<TypeCategory::Logical>;
+inline constexpr int LogicalResultKind{4};
+
+using LargestReal = Type<TypeCategory::Real>;
+inline constexpr int LargestRealKind{16};
+
+using Ascii = Type<TypeCategory::Character>;
+inline constexpr int AsciiKind{1};
// DynamicType is meant to be suitable for use as the result type for
// GetType() functions and member functions; consequently, it must be
@@ -252,26 +267,33 @@ const semantics::DerivedTypeSpec *GetDerivedTypeSpec(
const semantics::DerivedTypeSpec *GetParentTypeSpec(
const semantics::DerivedTypeSpec &);
-template <TypeCategory CATEGORY, int KIND = 0> struct TypeBase {
+template <TypeCategory CATEGORY> struct TypeBase {
+ constexpr int kind() const { return kind_; }
+
static constexpr TypeCategory category{CATEGORY};
- static constexpr int kind{KIND};
- constexpr bool operator==(const TypeBase &) const { return true; }
- static constexpr DynamicType GetType() { return {category, kind}; }
- static std::string AsFortran() { return GetType().AsFortran(); }
+ explicit constexpr TypeBase(int kind) : kind_{kind} {}
+ constexpr bool operator==(const TypeBase &that) const {
+ return kind_ == that.kind_;
+ }
+ constexpr DynamicType GetType() const { return {category, kind_}; }
+ std::string AsFortran() const { return GetType().AsFortran(); }
+
+private:
+ int kind_;
};
-template <int KIND>
-class Type<TypeCategory::Integer, KIND>
- : public TypeBase<TypeCategory::Integer, KIND> {
+template <>
+class Type<TypeCategory::Integer> : public TypeBase<TypeCategory::Integer> {
public:
- using Scalar = value::Integer<8 * KIND>;
+ using TypeBase::TypeBase;
+ using Scalar = value::IntegerValue;
};
-template <int KIND>
-class Type<TypeCategory::Unsigned, KIND>
- : public TypeBase<TypeCategory::Unsigned, KIND> {
+template <>
+class Type<TypeCategory::Unsigned> : public TypeBase<TypeCategory::Unsigned> {
public:
- using Scalar = value::Integer<8 * KIND>;
+ using TypeBase::TypeBase;
+ using Scalar = value::IntegerValue;
};
// Records when a default REAL literal constant is inexactly converted to binary
@@ -290,63 +312,40 @@ class TrackInexactLiteralConversion {
bool isFromInexactLiteralConversion_{false};
};
-template <int KIND>
-class Type<TypeCategory::Real, KIND>
- : public TypeBase<TypeCategory::Real, KIND>,
- public TrackInexactLiteralConversion {
+template <>
+class Type<TypeCategory::Real> : public TypeBase<TypeCategory::Real>,
+ public TrackInexactLiteralConversion {
public:
- static constexpr int precision{common::PrecisionOfRealKind(KIND)};
- static constexpr int bits{common::BitsForBinaryPrecision(precision)};
- using Scalar =
- value::Real<std::conditional_t<precision == 64,
- value::X87IntegerContainer, value::Integer<bits>>,
- precision>;
+ using TypeBase::TypeBase;
+ using Scalar = value::RealValue;
};
// The KIND type parameter on COMPLEX is the kind of each of its components.
-template <int KIND>
-class Type<TypeCategory::Complex, KIND>
- : public TypeBase<TypeCategory::Complex, KIND>,
- public TrackInexactLiteralConversion {
-public:
- using Part = Type<TypeCategory::Real, KIND>;
- using Scalar = value::Complex<typename Part::Scalar>;
-};
-
template <>
-class Type<TypeCategory::Character, 1>
- : public TypeBase<TypeCategory::Character, 1> {
+class Type<TypeCategory::Complex> : public TypeBase<TypeCategory::Complex>,
+ public TrackInexactLiteralConversion {
public:
- using Scalar = std::string;
+ using TypeBase::TypeBase;
+ using Part = Type<TypeCategory::Real>;
+ using Scalar = value::ComplexValue;
};
template <>
-class Type<TypeCategory::Character, 2>
- : public TypeBase<TypeCategory::Character, 2> {
+class Type<TypeCategory::Character> : public TypeBase<TypeCategory::Character> {
public:
- using Scalar = std::u16string;
+ using TypeBase::TypeBase;
+ using Scalar = value::CharacterValue;
};
template <>
-class Type<TypeCategory::Character, 4>
- : public TypeBase<TypeCategory::Character, 4> {
-public:
- using Scalar = std::u32string;
-};
-
-template <int KIND>
-class Type<TypeCategory::Logical, KIND>
- : public TypeBase<TypeCategory::Logical, KIND> {
+class Type<TypeCategory::Logical> : public TypeBase<TypeCategory::Logical> {
public:
- using Scalar = value::Logical<8 * KIND>;
+ using TypeBase::TypeBase;
+ using Scalar = value::LogicalValue;
};
// Type functions
-// Given a specific type, find the type of the same kind in another category.
-template <TypeCategory CATEGORY, typename T>
-using SameKind = Type<CATEGORY, std::decay_t<T>::kind>;
-
// Many expressions, including subscripts, CHARACTER lengths, array bounds,
// and effective type parameter values, are of a maximal kind of INTEGER.
using IndirectSubscriptIntegerExpr =
@@ -355,17 +354,8 @@ using IndirectSubscriptIntegerExpr =
// For each intrinsic type category CAT, CategoryTypes<CAT> is an instantiation
// of std::tuple<Type<CAT, K>> that comprises every kind value K in that
// category that could possibly be supported on any target.
-template <TypeCategory CATEGORY, int KIND>
-using CategoryKindTuple =
- std::conditional_t<common::IsValidKindOfIntrinsicType(CATEGORY, KIND),
- std::tuple<Type<CATEGORY, KIND>>, std::tuple<>>;
-
-template <TypeCategory CATEGORY, int... KINDS>
-using CategoryTypesHelper =
- common::CombineTuples<CategoryKindTuple<CATEGORY, KINDS>...>;
-
template <TypeCategory CATEGORY>
-using CategoryTypes = CategoryTypesHelper<CATEGORY, 1, 2, 3, 4, 8, 10, 16, 32>;
+using CategoryTypes = std::tuple<Type<CATEGORY>>;
using IntegerTypes = CategoryTypes<TypeCategory::Integer>;
using RealTypes = CategoryTypes<TypeCategory::Real>;
@@ -426,7 +416,9 @@ template <> class SomeKind<TypeCategory::Derived> {
static constexpr TypeCategory category{TypeCategory::Derived};
using Scalar = StructureConstructor;
- constexpr SomeKind() {} // CLASS(*)
+ // Argument provided for having the same signature as Types. Derived types
+ // don't have a kind, it is expected to be zero.
+ constexpr explicit SomeKind(int kind = 0) { CHECK(kind == 0); }
constexpr explicit SomeKind(const semantics::DerivedTypeSpec &dts)
: derivedTypeSpec_{&dts} {}
constexpr explicit SomeKind(const DynamicType &dt)
@@ -514,42 +506,41 @@ bool AreSameDerivedTypeIgnoringLengthParameters(
bool AreSameDerivedTypeIgnoringSequence(
const semantics::DerivedTypeSpec &, const semantics::DerivedTypeSpec &);
-// For generating "[extern] template class", &c. boilerplate
-#define EXPAND_FOR_EACH_INTEGER_KIND(M, P, S) \
- M(P, S, 1) M(P, S, 2) M(P, S, 4) M(P, S, 8) M(P, S, 16)
-#define EXPAND_FOR_EACH_REAL_KIND(M, P, S) \
- M(P, S, 2) M(P, S, 3) M(P, S, 4) M(P, S, 8) M(P, S, 10) M(P, S, 16)
-#define EXPAND_FOR_EACH_COMPLEX_KIND(M, P, S) EXPAND_FOR_EACH_REAL_KIND(M, P, S)
-#define EXPAND_FOR_EACH_CHARACTER_KIND(M, P, S) M(P, S, 1) M(P, S, 2) M(P, S, 4)
-#define EXPAND_FOR_EACH_LOGICAL_KIND(M, P, S) \
- M(P, S, 1) M(P, S, 2) M(P, S, 4) M(P, S, 8)
-#define EXPAND_FOR_EACH_UNSIGNED_KIND EXPAND_FOR_EACH_INTEGER_KIND
-
-#define FOR_EACH_INTEGER_KIND_HELP(PREFIX, SUFFIX, K) \
- PREFIX<Type<TypeCategory::Integer, K>> SUFFIX;
-#define FOR_EACH_REAL_KIND_HELP(PREFIX, SUFFIX, K) \
- PREFIX<Type<TypeCategory::Real, K>> SUFFIX;
-#define FOR_EACH_COMPLEX_KIND_HELP(PREFIX, SUFFIX, K) \
- PREFIX<Type<TypeCategory::Complex, K>> SUFFIX;
-#define FOR_EACH_CHARACTER_KIND_HELP(PREFIX, SUFFIX, K) \
- PREFIX<Type<TypeCategory::Character, K>> SUFFIX;
-#define FOR_EACH_LOGICAL_KIND_HELP(PREFIX, SUFFIX, K) \
- PREFIX<Type<TypeCategory::Logical, K>> SUFFIX;
-#define FOR_EACH_UNSIGNED_KIND_HELP(PREFIX, SUFFIX, K) \
- PREFIX<Type<TypeCategory::Unsigned, K>> SUFFIX;
+/// Check that KIND is consistent with thew type. That is, for a type category
+/// that has a kind as part of its type, it must be non-zero, and otherwise
+/// zero.
+#define CHECK_KIND(KIND, TY) \
+ { \
+ if constexpr (std::is_same_v<TY, SomeDerived> || \
+ std::is_same_v<TY, SomeKind<TypeCategory::Derived>> || \
+ std::is_same_v<TY, SomeType>) { \
+ CHECK((KIND) == 0 && "Type does not have a kind"); \
+ } else if constexpr (std::is_same_v<TY, Type<TypeCategory::Integer>> || \
+ std::is_same_v<TY, Type<TypeCategory::Unsigned>> || \
+ std::is_same_v<TY, Type<TypeCategory::Real>> || \
+ std::is_same_v<TY, Type<TypeCategory::Complex>> || \
+ std::is_same_v<TY, Type<TypeCategory::Logical>> || \
+ std::is_same_v<TY, Type<TypeCategory::Character>>) { \
+ CHECK((KIND) != 0 && "Type must come with a kind"); \
+ } else { \
+ static_assert(false, "Don't know whether TY should have a kind"); \
+ } \
+ }
+// TODO: The kind used to be part of Type<>, but since the FOR_EACH macros only
+// expand to a single entry, there is no use of them anymore.
#define FOR_EACH_INTEGER_KIND(PREFIX, SUFFIX) \
- EXPAND_FOR_EACH_INTEGER_KIND(FOR_EACH_INTEGER_KIND_HELP, PREFIX, SUFFIX)
+ PREFIX<Type<TypeCategory::Integer>> SUFFIX;
#define FOR_EACH_REAL_KIND(PREFIX, SUFFIX) \
- EXPAND_FOR_EACH_REAL_KIND(FOR_EACH_REAL_KIND_HELP, PREFIX, SUFFIX)
+ PREFIX<Type<TypeCategory::Real>> SUFFIX;
#define FOR_EACH_COMPLEX_KIND(PREFIX, SUFFIX) \
- EXPAND_FOR_EACH_COMPLEX_KIND(FOR_EACH_COMPLEX_KIND_HELP, PREFIX, SUFFIX)
+ PREFIX<Type<TypeCategory::Complex>> SUFFIX;
#define FOR_EACH_CHARACTER_KIND(PREFIX, SUFFIX) \
- EXPAND_FOR_EACH_CHARACTER_KIND(FOR_EACH_CHARACTER_KIND_HELP, PREFIX, SUFFIX)
+ PREFIX<Type<TypeCategory::Character>> SUFFIX;
#define FOR_EACH_LOGICAL_KIND(PREFIX, SUFFIX) \
- EXPAND_FOR_EACH_LOGICAL_KIND(FOR_EACH_LOGICAL_KIND_HELP, PREFIX, SUFFIX)
+ PREFIX<Type<TypeCategory::Logical>> SUFFIX;
#define FOR_EACH_UNSIGNED_KIND(PREFIX, SUFFIX) \
- EXPAND_FOR_EACH_UNSIGNED_KIND(FOR_EACH_UNSIGNED_KIND_HELP, PREFIX, SUFFIX)
+ PREFIX<Type<TypeCategory::Unsigned>> SUFFIX;
#define FOR_EACH_LENGTHLESS_INTRINSIC_KIND(PREFIX, SUFFIX) \
FOR_EACH_INTEGER_KIND(PREFIX, SUFFIX) \
@@ -576,5 +567,67 @@ bool AreSameDerivedTypeIgnoringSequence(
#define FOR_EACH_TYPE_AND_KIND(PREFIX, SUFFIX) \
FOR_EACH_INTRINSIC_KIND(PREFIX, SUFFIX) \
FOR_EACH_CATEGORY_TYPE(PREFIX, SUFFIX)
+
+/// Iterable lists of valid kinds for each TypeCategory for use by SearchTypes.
+template <TypeCategory CAT> struct KindsByType;
+template <> struct KindsByType<TypeCategory::Integer> {
+ static constexpr int kinds[] = FORTRAN_INTEGER_KINDS;
+};
+template <> struct KindsByType<TypeCategory::Unsigned> {
+ static constexpr int kinds[] = FORTRAN_UNSIGNED_KINDS;
+};
+template <> struct KindsByType<TypeCategory::Real> {
+ static constexpr int kinds[] = FORTRAN_REAL_KINDS;
+};
+template <> struct KindsByType<TypeCategory::Complex> {
+ static constexpr int kinds[] = FORTRAN_REAL_KINDS;
+};
+template <> struct KindsByType<TypeCategory::Logical> {
+ static constexpr int kinds[] = FORTRAN_LOGICAL_KINDS;
+};
+template <> struct KindsByType<TypeCategory::Character> {
+ static constexpr int kinds[] = FORTRAN_CHARACTER_KINDS;
+};
+template <> struct KindsByType<TypeCategory::Derived> {
+ static constexpr int kinds[] = {0};
+};
+
+// Given a VISITOR class of the general form
+// struct VISITOR {
+// using Result = ...;
+// using Types = std::tuple<...>;
+// template<typename T> Result Test(int kind) { ... }
+// };
+// SearchTypes will traverse the element types in the tuple in order,
+// and for each of them invoke VISITOR::Test<T>(kind) once per kind that
+// is supported by the type's category, until it returns a value that
+// casts to true. If no invocation of Test succeeds, SearchTypes will
+// return a default value.
+template <std::size_t J, typename VISITOR>
+common::IfNoLvalue<typename VISITOR::Result, VISITOR> SearchTypesHelper(
+ VISITOR &&visitor, typename VISITOR::Result &&defaultResult) {
+ using Tuple = typename VISITOR::Types;
+ if constexpr (J < std::tuple_size_v<Tuple>) {
+ using TYPE = std::tuple_element_t<J, Tuple>;
+ for (int kind : evaluate::KindsByType<TYPE::category>::kinds) {
+ if (auto result{visitor.template Test<TYPE>(kind)}) {
+ return result;
+ }
+ }
+ return SearchTypesHelper<J + 1, VISITOR>(
+ std::move(visitor), std::move(defaultResult));
+ } else {
+ return std::move(defaultResult);
+ }
+}
+
+template <typename VISITOR>
+common::IfNoLvalue<typename VISITOR::Result, VISITOR> SearchTypes(
+ VISITOR &&visitor,
+ typename VISITOR::Result defaultResult = typename VISITOR::Result{}) {
+ return SearchTypesHelper<0, VISITOR>(
+ std::move(visitor), std::move(defaultResult));
+}
+
} // namespace Fortran::evaluate
#endif // FORTRAN_EVALUATE_TYPE_H_
diff --git a/flang/include/flang/Evaluate/variable.h b/flang/include/flang/Evaluate/variable.h
index f510873ec2fe2..d4b861006ec36 100644
--- a/flang/include/flang/Evaluate/variable.h
+++ b/flang/include/flang/Evaluate/variable.h
@@ -136,6 +136,11 @@ class NamedEntity {
class TypeParamInquiry {
public:
using Result = SubscriptInteger;
+ static constexpr int ResultKind{SubscriptIntegerKind};
+ constexpr int kind() const { return SubscriptIntegerKind; }
+ static constexpr DynamicType GetType() {
+ return DynamicType{TypeCategory::Integer, SubscriptIntegerKind};
+ }
CLASS_BOILERPLATE(TypeParamInquiry)
TypeParamInquiry(NamedEntity &&x, const Symbol ¶m)
: base_{std::move(x)}, parameter_{param} {}
@@ -392,10 +397,39 @@ template <typename T> class Designator {
using Result = T;
static_assert(
IsSpecificIntrinsicType<Result> || std::is_same_v<Result, SomeDerived>);
- EVALUATE_UNION_CLASS_BOILERPLATE(Designator)
- Designator(const DataRef &that) : u{common::CopyVariant<Variant>(that.u)} {}
- Designator(DataRef &&that)
- : u{common::MoveVariant<Variant>(std::move(that.u))} {}
+
+ constexpr int kind() const { return kind_; }
+
+ CLASS_BOILERPLATE(Designator)
+ template <typename _A>
+ explicit Designator(int kind, const _A &x) : u{x}, kind_{kind} {
+ CHECK_KIND(kind, T);
+ }
+ template <typename _A, typename = common::NoLvalue<_A>>
+ explicit Designator(int kind, _A &&x) : u(std::move(x)), kind_{kind} {
+ CHECK_KIND(kind, T);
+ }
+ template <typename _A, typename U = T,
+ typename = std::enable_if_t<std::is_same_v<U, SomeDerived>>>
+ explicit Designator(const _A &x) : Designator(0, x) {}
+ template <typename _A, typename U = T, typename = common::NoLvalue<_A>,
+ typename = std::enable_if_t<std::is_same_v<U, SomeDerived>>>
+ explicit Designator(_A &&x) : Designator(0, std::move(x)) {}
+ bool operator==(const Designator &) const;
+ Designator(int kind, const DataRef &that)
+ : u{common::CopyVariant<Variant>(that.u)}, kind_{kind} {
+ CHECK_KIND(kind, T);
+ }
+ Designator(int kind, DataRef &&that)
+ : u{common::MoveVariant<Variant>(std::move(that.u))}, kind_{kind} {
+ CHECK_KIND(kind, T);
+ }
+ template <typename U = T,
+ typename = std::enable_if_t<std::is_same_v<U, SomeDerived>>>
+ Designator(const DataRef &that) : Designator(0, that) {}
+ template <typename U = T,
+ typename = std::enable_if_t<std::is_same_v<U, SomeDerived>>>
+ Designator(DataRef &&that) : Designator(0, std::move(that)) {}
std::optional<DynamicType> GetType() const;
int Rank() const;
@@ -406,6 +440,9 @@ template <typename T> class Designator {
llvm::raw_ostream &AsFortran(llvm::raw_ostream &o) const;
Variant u;
+
+private:
+ int kind_;
};
FOR_EACH_CHARACTER_KIND(extern template class Designator, )
@@ -413,6 +450,10 @@ FOR_EACH_CHARACTER_KIND(extern template class Designator, )
class DescriptorInquiry {
public:
using Result = SubscriptInteger;
+ static constexpr int kind() { return SubscriptIntegerKind; }
+ static constexpr DynamicType GetType() {
+ return DynamicType{TypeCategory::Integer, SubscriptIntegerKind};
+ }
ENUM_CLASS(Field, LowerBound, Extent, Stride, Rank, Len)
CLASS_BOILERPLATE(DescriptorInquiry)
@@ -442,6 +483,7 @@ class DescriptorInquiry {
class RankOneBoundElement {
public:
using Result = SubscriptInteger;
+ static constexpr int ResultKind{SubscriptIntegerKind};
CLASS_BOILERPLATE(RankOneBoundElement)
RankOneBoundElement(
common::CopyableIndirection<Expr<SubscriptInteger>> &&e, int dim)
@@ -453,6 +495,8 @@ class RankOneBoundElement {
Expr<SubscriptInteger> &base() { return base_.value(); }
int dimension() const { return dimension_; }
+ static constexpr int kind() { return ResultKind; }
+
static constexpr int Rank() { return 0; } // always scalar
static constexpr int Corank() { return 0; }
bool operator==(const RankOneBoundElement &) const;
diff --git a/flang/include/flang/Lower/DirectivesCommon.h b/flang/include/flang/Lower/DirectivesCommon.h
index 6f6089a4ffb6c..03f7783a53ec4 100644
--- a/flang/include/flang/Lower/DirectivesCommon.h
+++ b/flang/include/flang/Lower/DirectivesCommon.h
@@ -94,24 +94,21 @@ static T AsRvalueRef(const T &t) {
// (if present) is not needed. When it's present, though, it causes generated
// names to contain "int(..., kind=8)".
struct PeelConvert {
- template <Fortran::common::TypeCategory Category, int Kind>
+ template <Fortran::common::TypeCategory Category>
static Fortran::semantics::MaybeExpr visit_with_category(
- const Fortran::evaluate::Expr<Fortran::evaluate::Type<Category, Kind>>
- &expr) {
+ const Fortran::evaluate::Expr<Fortran::evaluate::Type<Category>> &expr) {
return Fortran::common::visit(
- [](auto &&s) { return visit_with_category<Category, Kind>(s); },
- expr.u);
+ [](auto &&s) { return visit_with_category<Category>(s); }, expr.u);
}
- template <Fortran::common::TypeCategory Category, int Kind>
+ template <
+ Fortran::common::TypeCategory Category,
+ typename = std::enable_if_t<Fortran::evaluate::IsSpecificIntrinsicType<
+ Fortran::evaluate::Type<Category>>>>
static Fortran::semantics::MaybeExpr visit_with_category(
- const Fortran::evaluate::Convert<Fortran::evaluate::Type<Category, Kind>,
+ const Fortran::evaluate::Convert<Fortran::evaluate::Type<Category>,
Category> &expr) {
return AsGenericExpr(AsRvalueRef(expr.left()));
}
- template <Fortran::common::TypeCategory Category, int Kind, typename T>
- static Fortran::semantics::MaybeExpr visit_with_category(const T &) {
- return std::nullopt; //
- }
template <Fortran::common::TypeCategory Category, typename T>
static Fortran::semantics::MaybeExpr visit_with_category(const T &) {
return std::nullopt; //
diff --git a/flang/include/flang/Lower/Mangler.h b/flang/include/flang/Lower/Mangler.h
index a75a08e64f033..a243b7824dfcd 100644
--- a/flang/include/flang/Lower/Mangler.h
+++ b/flang/include/flang/Lower/Mangler.h
@@ -65,22 +65,23 @@ mangleArrayLiteral(size_t size,
Fortran::common::ConstantSubscript charLen = -1,
llvm::StringRef derivedName = {});
-template <Fortran::common::TypeCategory TC, int KIND>
+template <Fortran::common::TypeCategory TC>
std::string mangleArrayLiteral(
mlir::Type,
- const Fortran::evaluate::Constant<Fortran::evaluate::Type<TC, KIND>> &x) {
+ const Fortran::evaluate::Constant<Fortran::evaluate::Type<TC>> &x) {
+ const int kind{x.kind()};
return mangleArrayLiteral(x.values().size() * sizeof(x.values()[0]),
- x.shape(), TC, KIND);
+ x.shape(), TC, kind);
}
-template <int KIND>
-std::string
-mangleArrayLiteral(mlir::Type,
- const Fortran::evaluate::Constant<Fortran::evaluate::Type<
- Fortran::common::TypeCategory::Character, KIND>> &x) {
+inline std::string mangleArrayLiteral(
+ mlir::Type,
+ const Fortran::evaluate::Constant<
+ Fortran::evaluate::Type<Fortran::common::TypeCategory::Character>> &x) {
+ const int kind{x.kind()};
return mangleArrayLiteral(x.values().size() * sizeof(x.values()[0]),
x.shape(), Fortran::common::TypeCategory::Character,
- KIND, x.LEN());
+ kind, x.LEN());
}
inline std::string mangleArrayLiteral(
diff --git a/flang/include/flang/Lower/Support/Utils.h b/flang/include/flang/Lower/Support/Utils.h
index 15d30905aadcd..555cdec21774e 100644
--- a/flang/include/flang/Lower/Support/Utils.h
+++ b/flang/include/flang/Lower/Support/Utils.h
@@ -62,10 +62,11 @@ static Fortran::lower::SomeExpr toEvExpr(const A &x) {
}
template <Fortran::common::TypeCategory FROM>
-static Fortran::lower::SomeExpr ignoreEvConvert(
- const Fortran::evaluate::Convert<
- Fortran::evaluate::Type<Fortran::common::TypeCategory::Integer, 8>,
- FROM> &x) {
+static Fortran::lower::SomeExpr
+ignoreEvConvert(const Fortran::evaluate::Convert<
+ Fortran::evaluate::Type<Fortran::common::TypeCategory::Integer>,
+ FROM> &x) {
+ CHECK(x.kind() == 8);
return toEvExpr(x.left());
}
template <typename A>
@@ -76,9 +77,10 @@ static Fortran::lower::SomeExpr ignoreEvConvert(const A &x) {
/// A vector subscript expression may be wrapped with a cast to INTEGER*8.
/// Get rid of it here so the vector can be loaded. Add it back when
/// generating the elemental evaluation (inside the loop nest).
-inline Fortran::lower::SomeExpr
-ignoreEvConvert(const Fortran::evaluate::Expr<Fortran::evaluate::Type<
- Fortran::common::TypeCategory::Integer, 8>> &x) {
+inline Fortran::lower::SomeExpr ignoreEvConvert(
+ const Fortran::evaluate::Expr<
+ Fortran::evaluate::Type<Fortran::common::TypeCategory::Integer>> &x) {
+ CHECK(x.kind() == 8);
return Fortran::common::visit(
[](const auto &v) { return ignoreEvConvert(v); }, x.u);
}
diff --git a/flang/include/flang/Semantics/dump-expr.h b/flang/include/flang/Semantics/dump-expr.h
index 868b64c64e60a..fe983cb0a6e8d 100644
--- a/flang/include/flang/Semantics/dump-expr.h
+++ b/flang/include/flang/Semantics/dump-expr.h
@@ -233,14 +233,12 @@ class DumpEvaluateExpr {
LLVM_DUMP_METHOD void DumpEvExpr(const evaluate::Expr<evaluate::SomeType> &x);
LLVM_DUMP_METHOD void DumpEvExpr(
- const evaluate::Expr<evaluate::Type<common::TypeCategory::Integer, 4>> &x);
-LLVM_DUMP_METHOD void DumpEvExpr(
- const evaluate::Expr<evaluate::Type<common::TypeCategory::Integer, 8>> &x);
+ const evaluate::Expr<evaluate::Type<common::TypeCategory::Integer>> &x);
LLVM_DUMP_METHOD void DumpEvExpr(const evaluate::ArrayRef &x);
LLVM_DUMP_METHOD void DumpEvExpr(const evaluate::DataRef &x);
LLVM_DUMP_METHOD void DumpEvExpr(const evaluate::Substring &x);
LLVM_DUMP_METHOD void DumpEvExpr(
- const evaluate::Designator<evaluate::Type<common::TypeCategory::Integer, 4>>
+ const evaluate::Designator<evaluate::Type<common::TypeCategory::Integer>>
&x);
} // namespace Fortran::semantics
diff --git a/flang/include/flang/Semantics/scope.h b/flang/include/flang/Semantics/scope.h
index 7cab6e2122319..324c206a4d2d8 100644
--- a/flang/include/flang/Semantics/scope.h
+++ b/flang/include/flang/Semantics/scope.h
@@ -235,8 +235,7 @@ class Scope {
const DeclTypeSpec *FindType(const DeclTypeSpec &) const;
const DeclTypeSpec &MakeNumericType(TypeCategory, KindExpr &&kind);
const DeclTypeSpec &MakeLogicalType(KindExpr &&kind);
- const DeclTypeSpec &MakeCharacterType(
- ParamValue &&length, KindExpr &&kind = KindExpr{0});
+ const DeclTypeSpec &MakeCharacterType(ParamValue &&length, KindExpr &&kind);
DeclTypeSpec &MakeDerivedType(DeclTypeSpec::Category, DerivedTypeSpec &&);
const DeclTypeSpec &MakeTypeStarType();
const DeclTypeSpec &MakeClassStarType();
diff --git a/flang/include/flang/Semantics/type.h b/flang/include/flang/Semantics/type.h
index b8f06dde4f562..2f216e3bc0cd6 100644
--- a/flang/include/flang/Semantics/type.h
+++ b/flang/include/flang/Semantics/type.h
@@ -53,6 +53,8 @@ using SubscriptIntExpr = evaluate::Expr<evaluate::SubscriptInteger>;
using MaybeSubscriptIntExpr = std::optional<SubscriptIntExpr>;
using KindExpr = SubscriptIntExpr;
+KindExpr MakeKindExpr(int v);
+
// An array spec bound: an explicit integer expression, assumed size
// or implied shape(*), or assumed or deferred shape(:). In the absence
// of explicit lower bounds it is not possible to distinguish assumed
diff --git a/flang/lib/Evaluate/CMakeLists.txt b/flang/lib/Evaluate/CMakeLists.txt
index 472ecb6d8d079..55e0a66fceb6d 100644
--- a/flang/lib/Evaluate/CMakeLists.txt
+++ b/flang/lib/Evaluate/CMakeLists.txt
@@ -30,10 +30,12 @@ endif ()
add_flang_library(FortranEvaluate
call.cpp
+ character-value.cpp
+ character-value-impl.cpp
characteristics.cpp
check-expression.cpp
common.cpp
- complex.cpp
+ complex-value.cpp
constant.cpp
expression.cpp
fold.cpp
@@ -48,10 +50,14 @@ add_flang_library(FortranEvaluate
host.cpp
initial-image.cpp
integer.cpp
+ integer-value.cpp
+ integer-value-impl.cpp
intrinsics.cpp
intrinsics-library.cpp
- logical.cpp
+ logical-value.cpp
real.cpp
+ real-value.cpp
+ real-value-impl.cpp
shape.cpp
static-data.cpp
target.cpp
@@ -74,9 +80,12 @@ add_flang_library(FortranEvaluate
[["flang/Evaluate/shape.h"]]
[["flang/Evaluate/characteristics.h"]]
[["flang/Evaluate/variable.h"]]
- [["flang/Evaluate/real.h"]]
+ [["flang/Evaluate/character-value.h"]]
+ [["flang/Evaluate/complex-value.h"]]
+ [["flang/Evaluate/integer-value.h"]]
+ [["flang/Evaluate/logical-value.h"]]
+ [["flang/Evaluate/real-value.h"]]
[["flang/Evaluate/type.h"]]
- [["flang/Evaluate/integer.h"]]
[["flang/Evaluate/expression.h"]]
[["flang/Evaluate/tools.h"]]
diff --git a/flang/lib/Evaluate/call.cpp b/flang/lib/Evaluate/call.cpp
index 57afa80a03209..c72e347dffa59 100644
--- a/flang/lib/Evaluate/call.cpp
+++ b/flang/lib/Evaluate/call.cpp
@@ -269,7 +269,8 @@ std::optional<Expr<SubscriptInteger>> ProcedureRef::LEN() const {
UnwrapExpr<Expr<SomeInteger>>(arguments_[1].value())};
CHECK(stringArg && nCopiesArg);
if (auto stringLen{stringArg->LEN()}) {
- auto converted{ConvertTo(*stringLen, common::Clone(*nCopiesArg))};
+ auto converted{ConvertTo(
+ stringLen->kind(), *stringLen, common::Clone(*nCopiesArg))};
return *std::move(stringLen) * std::move(converted);
}
}
diff --git a/flang/lib/Evaluate/character-value-impl.cpp b/flang/lib/Evaluate/character-value-impl.cpp
new file mode 100644
index 0000000000000..dfac05b1b8aae
--- /dev/null
+++ b/flang/lib/Evaluate/character-value-impl.cpp
@@ -0,0 +1,577 @@
+//===-- lib/Evaluate/character-value-impl.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 "character-value-impl.h"
+#include "flang/Common/idioms.h"
+#include "flang/Evaluate/common.h"
+#include "llvm/Support/ErrorHandling.h"
+#include <algorithm>
+#include <cstring>
+
+namespace Fortran::evaluate::value {
+
+CharacterValueImpl::CharacterValueImpl(int kind, std::size_t n, char32_t c) {
+ withCharProto(kind, [this, n, c](auto ct) {
+ using CharT = std::decay_t<decltype(ct)>;
+ storage_ = std::basic_string<CharT>(n, static_cast<CharT>(c));
+ });
+}
+
+CharacterValueImpl CharacterValueImpl::Zero(int kind) {
+ return withCharProto(kind, [kind](auto c) {
+ using Char = std::decay_t<decltype(c)>;
+ return CharacterValueImpl{kind, std::basic_string<Char>{}};
+ });
+}
+
+CharacterValueImpl CharacterValueImpl::FromRawBytes(
+ int kind, const void *raw, size_t byteSize) {
+ return withCharProto(kind, [kind, raw, byteSize](auto charProto) {
+ using CharT = decltype(charProto);
+ std::basic_string<CharT> s;
+ if (byteSize > 0) {
+ s.assign(static_cast<const CharT *>(raw), byteSize);
+ }
+ return CharacterValueImpl{kind, std::move(s)};
+ });
+}
+
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+LLVM_DUMP_METHOD void CharacterValueImpl::dump() const {
+ llvm::errs() << kind() << '_';
+ withStdString([](const auto &s) {
+ llvm::errs() << parser::QuoteCharacterLiteral(s, true) << '\n';
+ });
+}
+#endif
+
+std::size_t CharacterValueImpl::charSize() const {
+ return common::visit(
+ [](const auto &s) -> std::size_t {
+ if constexpr (std::is_same_v<std::decay_t<decltype(s)>,
+ std::monostate>) {
+ llvm_unreachable("operation not supported on uninitialized value");
+ } else {
+ return sizeof(typename std::decay_t<decltype(s)>::value_type);
+ }
+ },
+ storage_);
+}
+
+std::size_t CharacterValueImpl::size() const {
+ return common::visit(
+ [](const auto &s) -> std::size_t {
+ if constexpr (std::is_same_v<std::decay_t<decltype(s)>,
+ std::monostate>) {
+ return 0;
+ } else {
+ return s.size();
+ }
+ },
+ storage_);
+}
+
+void *CharacterValueImpl::charData() {
+ return common::visit(
+ [](auto &s) -> void * {
+ if constexpr (std::is_same_v<std::decay_t<decltype(s)>,
+ std::monostate>) {
+ // No data available in monostate
+ return nullptr;
+ } else {
+ return static_cast<void *>(s.data());
+ }
+ },
+ storage_);
+}
+
+const void *CharacterValueImpl::charData() const {
+ return common::visit(
+ [](const auto &s) -> const void * {
+ if constexpr (std::is_same_v<std::decay_t<decltype(s)>,
+ std::monostate>) {
+ // No data available in monostate
+ return nullptr;
+ } else {
+ return static_cast<const void *>(s.data());
+ }
+ },
+ storage_);
+}
+
+Ordering CharacterValueImpl::Compare(const CharacterValueImpl &y) const {
+ return common::visit(
+ [](const auto &xs, const auto &ys) -> Ordering {
+ using XS = std::decay_t<decltype(xs)>;
+ using YS = std::decay_t<decltype(ys)>;
+
+ // monostate represents an empty string of any type; here it is
+ // polymorhpic to what it is compared to
+ if constexpr (std::is_same_v<XS, YS>) {
+ return Fortran::evaluate::Compare(xs, ys);
+ } else if constexpr (std::is_same_v<XS, std::monostate> &&
+ !std::is_same_v<YS, std::monostate>) {
+ return Fortran::evaluate::Compare(YS{}, ys);
+ } else if constexpr (!std::is_same_v<XS, std::monostate> &&
+ std::is_same_v<YS, std::monostate>) {
+ return Fortran::evaluate::Compare(xs, XS{});
+ } else {
+ llvm_unreachable("character comparison across differing kinds");
+ }
+ },
+ this->storage_, y.storage_);
+}
+
+bool CharacterValueImpl::operator<(const CharacterValueImpl &y) const {
+ return common::visit(
+ [](const auto &xs, const auto &ys) -> bool {
+ using XS = std::decay_t<decltype(xs)>;
+ using YS = std::decay_t<decltype(ys)>;
+
+ // monostate represents an empty string of any type; here it is
+ // polymorphic to what it is compared to
+ if constexpr (std::is_same_v<XS, YS>) {
+ return xs < ys;
+ } else if constexpr (std::is_same_v<XS, std::monostate> &&
+ !std::is_same_v<YS, std::monostate>) {
+ return YS{} < ys;
+ } else if constexpr (!std::is_same_v<XS, std::monostate> &&
+ std::is_same_v<YS, std::monostate>) {
+ return xs < XS{};
+ } else {
+ llvm_unreachable("character comparison across differing kinds");
+ }
+ },
+ this->storage_, y.storage_);
+}
+
+bool CharacterValueImpl::operator==(const CharacterValueImpl &y) const {
+ return common::visit(
+ [](const auto &xs, const auto &ys) -> bool {
+ using XS = std::decay_t<decltype(xs)>;
+ using YS = std::decay_t<decltype(ys)>;
+
+ // monostate represents an empty string of any type; here it is
+ // polymorhpic to what it is compared to
+ if constexpr (std::is_same_v<XS, YS>) {
+ return xs == ys;
+ } else if constexpr (std::is_same_v<XS, std::monostate> &&
+ !std::is_same_v<YS, std::monostate>) {
+ return YS{} == ys;
+ } else if constexpr (!std::is_same_v<XS, std::monostate> &&
+ std::is_same_v<YS, std::monostate>) {
+ return xs == XS{};
+ } else {
+ llvm_unreachable("character comparison across differing kinds");
+ }
+ },
+ this->storage_, y.storage_);
+}
+
+void CharacterValueImpl::assign(int kind, std::size_t n, char32_t c) {
+ return withCharProto(kind, [this, n, c](auto ct) {
+ using CharT = decltype(ct);
+ storage_ = std::basic_string<CharT>(n, static_cast<CharT>(c));
+ });
+}
+
+void CharacterValueImpl::erase(std::size_t pos) {
+ common::visit(
+ [pos](auto &s) {
+ if constexpr (std::is_same_v<std::decay_t<decltype(s)>,
+ std::monostate>) {
+ llvm_unreachable("operation not supported on uninitialized value");
+ } else {
+ s.erase(pos);
+ }
+ },
+ storage_);
+}
+
+void CharacterValueImpl::append(std::size_t n, char32_t c) {
+ common::visit(
+ [n, c](auto &s) {
+ if constexpr (std::is_same_v<std::decay_t<decltype(s)>,
+ std::monostate>) {
+ llvm_unreachable("operation not supported on uninitialized value");
+ } else {
+ using CharT = typename std::decay_t<decltype(s)>::value_type;
+ s.append(n, static_cast<CharT>(c));
+ }
+ },
+ storage_);
+}
+
+CharacterValueImpl &CharacterValueImpl::replace(
+ std::size_t pos, std::size_t len, const CharacterValueImpl &other) {
+ common::visit(
+ [pos, len](auto &s, const auto &o) {
+ if constexpr (!std::is_same_v<std::decay_t<decltype(s)>,
+ std::monostate> &&
+ !std::is_same_v<std::decay_t<decltype(o)>, std::monostate> &&
+ std::is_same_v<std::decay_t<decltype(s)>,
+ std::decay_t<decltype(o)>>) {
+ s.replace(pos, len, o);
+ } else {
+ llvm_unreachable("operation not supported on uninitialized value or "
+ "values of different kinds");
+ }
+ },
+ storage_, other.storage_);
+ return *this;
+}
+
+CharacterValueImpl CharacterValueImpl::substr(std::size_t pos) const {
+ return common::visit(
+ [pos](const auto &s) -> CharacterValueImpl {
+ if constexpr (std::is_same_v<std::decay_t<decltype(s)>,
+ std::monostate>) {
+ llvm_unreachable("operation not supported on uninitialized value");
+ } else {
+ return CharacterValueImpl{s.substr(pos)};
+ }
+ },
+ storage_);
+}
+
+CharacterValueImpl CharacterValueImpl::substr(
+ std::size_t pos, std::size_t len) const {
+ return common::visit(
+ [pos, len](const auto &s) -> CharacterValueImpl {
+ if constexpr (std::is_same_v<std::decay_t<decltype(s)>,
+ std::monostate>) {
+ llvm_unreachable("operation not supported on uninitialized value");
+ } else {
+ return CharacterValueImpl{s.substr(pos, len)};
+ }
+ },
+ storage_);
+}
+
+std::optional<llvm::StringRef> CharacterValueImpl::AsStringRef() const {
+ if (IsMonostate()) {
+ return llvm::StringRef{};
+ }
+ if (const auto *s{std::get_if<std::string>(&storage_)}) {
+ return *s;
+ }
+ return std::nullopt;
+}
+
+/// Return the string as std::string if kind==1, or nullopt otherwise.
+std::optional<std::string> CharacterValueImpl::AsStdString() const {
+ if (IsMonostate()) {
+ return std::string{};
+ }
+
+ if (const auto *s{std::get_if<std::string>(&storage_)}) {
+ return *s;
+ } else {
+ return std::nullopt;
+ }
+}
+
+std::optional<std::u16string> CharacterValueImpl::AsU16String() const {
+ if (IsMonostate()) {
+ return std::u16string{};
+ }
+
+ if (const auto *s{std::get_if<std::u16string>(&storage_)}) {
+ return *s;
+ } else {
+ return std::nullopt;
+ }
+}
+
+std::optional<std::u32string> CharacterValueImpl::AsU32String() const {
+ if (IsMonostate()) {
+ return std::u32string{};
+ }
+
+ if (const auto *s{std::get_if<std::u32string>(&storage_)}) {
+ return *s;
+ } else {
+ return std::nullopt;
+ }
+}
+
+CharacterValueImpl CharacterValueImpl::ToAscii(int kind) const {
+ if (IsMonostate()) {
+ return Zero(kind);
+ }
+
+ return withStdString([kind](const auto &s) -> CharacterValueImpl {
+ return withCharProto(kind, [&s](auto ct) -> CharacterValueImpl {
+ using TO = std::basic_string<std::decay_t<decltype(ct)>>;
+ // Fortran character conversion is well defined between distinct kinds
+ // only when the actual characters are valid 7-bit ASCII.
+ TO str;
+ for (auto iter{s.cbegin()}; iter != s.cend(); ++iter) {
+ if (static_cast<std::uint64_t>(*iter) > 127) {
+ return Zero(sizeof(ct));
+ }
+ str.push_back(static_cast<typename TO::value_type>(*iter));
+ }
+ return CharacterValueImpl{str};
+ });
+ });
+}
+
+void CharacterValueImpl::reserve(std::size_t n) {
+ common::visit(
+ [n](auto &s) {
+ if constexpr (!std::is_same_v<std::decay_t<decltype(s)>,
+ std::monostate>) {
+ s.reserve(n);
+ }
+ },
+ storage_);
+}
+
+char32_t CharacterValueImpl::operator[](std::size_t i) const {
+ return common::visit(
+ [i](const auto &s) -> char32_t {
+ if constexpr (std::is_same_v<std::decay_t<decltype(s)>,
+ std::monostate>) {
+ llvm_unreachable("operation not supported on uninitialized value");
+ } else {
+ return static_cast<char32_t>(s[i]);
+ }
+ return 0;
+ },
+ storage_);
+}
+
+CharacterValueImpl CharacterValueImpl::operator+(
+ const CharacterValueImpl &y) const {
+ return common::visit(
+ [](const auto &a, const auto &b) -> CharacterValueImpl {
+ if constexpr (std::is_same_v<std::decay_t<decltype(a)>,
+ std::decay_t<decltype(b)>> &&
+ !std::is_same_v<std::decay_t<decltype(a)>, std::monostate>) {
+ return CharacterValueImpl{a + b};
+ } else {
+ llvm_unreachable("operation not supported on uninitialized value or "
+ "values of different kinds");
+ }
+ return CharacterValueImpl{};
+ },
+ storage_, y.storage_);
+}
+
+CharacterValueImpl &CharacterValueImpl::operator+=(
+ const CharacterValueImpl &y) {
+ common::visit(
+ [](auto &a, const auto &b) {
+ if constexpr (std::is_same_v<std::decay_t<decltype(a)>,
+ std::decay_t<decltype(b)>> &&
+ !std::is_same_v<std::decay_t<decltype(a)>, std::monostate>) {
+ a += b;
+ } else {
+ llvm_unreachable("operation not supported on uninitialized value or "
+ "values of different kinds");
+ }
+ },
+ storage_, y.storage_);
+ return *this;
+}
+
+CharacterValueImpl &CharacterValueImpl::operator+=(char c) {
+ common::visit(
+ [c](auto &s) {
+ if constexpr (std::is_same_v<std::decay_t<decltype(s)>,
+ std::monostate>) {
+ llvm_unreachable("operation not supported on uninitialized value");
+ } else {
+ using CharT = typename std::decay_t<decltype(s)>::value_type;
+ s.push_back(static_cast<CharT>(c));
+ }
+ },
+ storage_);
+ return *this;
+}
+
+std::size_t CharacterValueImpl::find_first_not_of(char32_t c) const {
+ return common::visit(
+ [c](const auto &s) -> std::size_t {
+ if constexpr (!std::is_same_v<std::decay_t<decltype(s)>,
+ std::monostate>) {
+ using CharT = typename std::decay_t<decltype(s)>::value_type;
+ return s.find_first_not_of(static_cast<CharT>(c));
+ } else {
+ llvm_unreachable("Unsupported combination of character kinds");
+ return std::string::npos;
+ }
+ },
+ storage_);
+}
+
+std::size_t CharacterValueImpl::find_last_not_of(char32_t c) const {
+ return common::visit(
+ [c](const auto &s) -> std::size_t {
+ if constexpr (!std::is_same_v<std::decay_t<decltype(s)>,
+ std::monostate>) {
+ using CharT = typename std::decay_t<decltype(s)>::value_type;
+ return s.find_last_not_of(static_cast<CharT>(c));
+ } else {
+ llvm_unreachable("Unsupported combination of character kinds");
+ return std::string::npos;
+ }
+ },
+ storage_);
+}
+
+std::size_t CharacterValueImpl::find_first_not_of(
+ const CharacterValueImpl &set) const {
+ return common::visit(
+ [](const auto &s, const auto &p) -> std::size_t {
+ if constexpr (std::is_same_v<std::decay_t<decltype(s)>,
+ std::monostate>) {
+ // Nothing to find in an empty string
+ return std::string::npos;
+ } else if constexpr (std::is_same_v<std::decay_t<decltype(s)>,
+ std::decay_t<decltype(p)>> &&
+ !std::is_same_v<std::decay_t<decltype(s)>, std::monostate>) {
+ return s.find_first_not_of(p);
+ } else {
+ llvm_unreachable("Unsupported combination of character kinds");
+ return std::string::npos;
+ }
+ },
+ storage_, set.storage_);
+}
+
+std::size_t CharacterValueImpl::find_last_not_of(
+ const CharacterValueImpl &set) const {
+ return common::visit(
+ [](const auto &s, const auto &p) -> std::size_t {
+ if constexpr (std::is_same_v<std::decay_t<decltype(s)>,
+ std::monostate>) {
+ // Nothing to find in an empty string
+ return std::string::npos;
+ } else if constexpr (std::is_same_v<std::decay_t<decltype(s)>,
+ std::decay_t<decltype(p)>> &&
+ !std::is_same_v<std::decay_t<decltype(s)>, std::monostate>) {
+ return s.find_last_not_of(p);
+ } else {
+ llvm_unreachable("Unsupported combination of character kinds");
+ return std::string::npos;
+ }
+ },
+ storage_, set.storage_);
+}
+
+std::size_t CharacterValueImpl::find(const CharacterValueImpl &pattern) const {
+ return common::visit(
+ [](const auto &s, const auto &p) -> std::size_t {
+ if constexpr (std::is_same_v<std::decay_t<decltype(s)>,
+ std::monostate>) {
+ // Nothing to find in an empty string
+ return std::string::npos;
+ } else if constexpr (std::is_same_v<std::decay_t<decltype(s)>,
+ std::decay_t<decltype(p)>> &&
+ !std::is_same_v<std::decay_t<decltype(s)>, std::monostate>) {
+ return s.find(p);
+ } else {
+ llvm_unreachable("Unsupported combination of character kinds");
+ return std::string::npos;
+ }
+ },
+ storage_, pattern.storage_);
+}
+
+std::size_t CharacterValueImpl::rfind(const CharacterValueImpl &pattern) const {
+ return common::visit(
+ [](const auto &s, const auto &p) -> std::size_t {
+ if constexpr (std::is_same_v<std::decay_t<decltype(s)>,
+ std::monostate>) {
+ // Nothing to find in an empty string
+ return std::string::npos;
+ } else if constexpr (std::is_same_v<std::decay_t<decltype(s)>,
+ std::decay_t<decltype(p)>> &&
+ !std::is_same_v<std::decay_t<decltype(s)>, std::monostate>) {
+ return s.rfind(p);
+ }
+ llvm_unreachable("Unsupported combination of character kinds");
+ return std::string::npos;
+ },
+ storage_, pattern.storage_);
+}
+
+std::size_t CharacterValueImpl::find_first_of(
+ const CharacterValueImpl &set) const {
+ return common::visit(
+ [](const auto &s, const auto &p) -> std::size_t {
+ if constexpr (std::is_same_v<std::decay_t<decltype(s)>,
+ std::monostate>) {
+ // Nothing to find in an empty string
+ return std::string::npos;
+ } else if constexpr (std::is_same_v<std::decay_t<decltype(s)>,
+ std::decay_t<decltype(p)>> &&
+ !std::is_same_v<std::decay_t<decltype(s)>, std::monostate>) {
+ return s.find_first_of(p);
+ } else {
+ llvm_unreachable("Unsupported combination of character kinds");
+ return std::string::npos;
+ }
+ },
+ storage_, set.storage_);
+}
+
+std::size_t CharacterValueImpl::find_last_of(
+ const CharacterValueImpl &set) const {
+ return common::visit(
+ [](const auto &s, const auto &p) -> std::size_t {
+ if constexpr (std::is_same_v<std::decay_t<decltype(s)>,
+ std::monostate>) {
+ // Nothing to find in an empty string
+ return std::string::npos;
+ } else if constexpr (std::is_same_v<std::decay_t<decltype(s)>,
+ std::decay_t<decltype(p)>> &&
+ !std::is_same_v<std::decay_t<decltype(s)>, std::monostate>) {
+ return s.find_last_of(p);
+ } else {
+ llvm_unreachable("Unsupported combination of character kinds");
+ return std::string::npos;
+ }
+ },
+ storage_, set.storage_);
+}
+
+void CharacterValueImpl::StoreRawBytes(
+ void *dst, size_t size, bool *changed) const {
+ common::visit(
+ [&](const auto &s) {
+ if constexpr (std::is_same_v<std::decay_t<decltype(s)>,
+ std::monostate>) {
+ CHECK(size == 0);
+ // Nothing to store
+ } else {
+ std::size_t payloadBytes{std::min(size,
+ s.size() *
+ sizeof(typename std::decay_t<decltype(s)>::value_type))};
+ if (std::memcmp(dst, s.data(), payloadBytes) != 0 ||
+ (payloadBytes < size &&
+ !std::all_of(
+ static_cast<const char *>(dst) + payloadBytes,
+ static_cast<const char *>(dst) + size,
+ [](char x) { return x == 0; }))) {
+ std::memcpy(dst, s.data(), payloadBytes);
+ if (payloadBytes < size) {
+ std::memset(static_cast<char *>(dst) + payloadBytes, 0,
+ size - payloadBytes);
+ }
+ if (changed)
+ *changed = true;
+ }
+ }
+ },
+ storage_);
+}
+
+} // namespace Fortran::evaluate::value
diff --git a/flang/lib/Evaluate/character-value-impl.h b/flang/lib/Evaluate/character-value-impl.h
new file mode 100644
index 0000000000000..f4d97fff95f87
--- /dev/null
+++ b/flang/lib/Evaluate/character-value-impl.h
@@ -0,0 +1,229 @@
+//===-- include/flang/Evaluate/character-value-impl.h -----------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef FORTRAN_EVALUATE_CHARACTER_VALUE_IMPL_H_
+#define FORTRAN_EVALUATE_CHARACTER_VALUE_IMPL_H_
+
+#include "flang/Evaluate/common.h"
+#include "llvm/Support/ErrorHandling.h"
+#include <cstddef>
+#include <optional>
+#include <string>
+#include <utility>
+#include <variant>
+
+namespace Fortran::evaluate::value {
+
+class CharacterValueImpl {
+ using Storage =
+ std::variant<std::monostate, std::string, std::u16string, std::u32string>;
+
+public:
+ // rule-of-five
+ ~CharacterValueImpl() = default;
+ CharacterValueImpl(const CharacterValueImpl &) = default;
+ CharacterValueImpl(CharacterValueImpl &&) = default;
+ CharacterValueImpl &operator=(const CharacterValueImpl &) = default;
+ CharacterValueImpl &operator=(CharacterValueImpl &&) = default;
+
+ CharacterValueImpl() = default;
+ explicit CharacterValueImpl(std::string s) : storage_{std::move(s)} {}
+ explicit CharacterValueImpl(std::u16string s) : storage_{std::move(s)} {}
+ explicit CharacterValueImpl(std::u32string s) : storage_{std::move(s)} {}
+
+ CharacterValueImpl(int kind, std::string s)
+ : CharacterValueImpl{std::move(s)} {
+ CHECK(kind == 1);
+ }
+
+ CharacterValueImpl(int kind, std::u16string s)
+ : CharacterValueImpl{std::move(s)} {
+ CHECK(kind == 2);
+ }
+
+ CharacterValueImpl(int kind, std::u32string s)
+ : CharacterValueImpl{std::move(s)} {
+ CHECK(kind == 4);
+ }
+
+ /// Fill constructors: create a string of n copies of the given character.
+ CharacterValueImpl(int kind, std::size_t n, char32_t c);
+
+ static CharacterValueImpl Zero(int kind);
+
+ static CharacterValueImpl FromRawBytes(
+ int kind, const void *raw, size_t byteSize);
+
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+ LLVM_DUMP_METHOD void dump() const;
+#endif
+
+ std::optional<llvm::StringRef> AsStringRef() const;
+
+ /// Return the string as std::string if kind==1, or nullopt otherwise.
+ std::optional<std::string> AsStdString() const;
+ std::optional<std::u16string> AsU16String() const;
+ std::optional<std::u32string> AsU32String() const;
+
+ bool IsMonostate() const { return storage_.index() == 0; }
+ int kind() const {
+ return withCharProto([](auto ct) { return sizeof(ct); });
+ }
+
+ /// Byte size of one character unit (1, 2, or 4).
+ std::size_t charSize() const;
+
+ /// Number of characters in this string.
+ std::size_t size() const;
+
+ /// String length (synonym for size()).
+ std::size_t length() const { return size(); }
+
+ /// True when the string is empty.
+ bool empty() const { return size() == 0; }
+
+ /// Raw byte pointer to the underlying character data.
+ void *data() { return charData(); }
+ const void *data() const { return charData(); }
+ void *charData();
+ const void *charData() const;
+
+ // Comparison operators
+ Ordering Compare(const CharacterValueImpl &y) const;
+ bool operator<(const CharacterValueImpl &y) const;
+ bool operator<=(const CharacterValueImpl &y) const { return !(y < *this); }
+ bool operator==(const CharacterValueImpl &y) const;
+ bool operator!=(const CharacterValueImpl &y) const { return !(*this == y); }
+ bool operator>=(const CharacterValueImpl &y) const { return !(*this < y); }
+ bool operator>(const CharacterValueImpl &y) const { return y < *this; }
+
+ /// Assign n copies of the given character.
+ void assign(int kind, std::size_t n, char32_t c);
+
+ /// Assign from a raw character pointer and length.
+ void assign(const char *p, std::size_t n) { storage_ = std::string(p, n); }
+ void assign(const char16_t *p, std::size_t n) {
+ storage_ = std::u16string(p, n);
+ }
+ void assign(const char32_t *p, std::size_t n) {
+ storage_ = std::u32string(p, n);
+ }
+
+ /// Erase from position pos to end.
+ void erase(std::size_t pos);
+
+ /// Append n copies of the given character.
+ void append(std::size_t n, char32_t c);
+
+ /// Replace the substring [pos, pos+len) with characters from other.
+ CharacterValueImpl &replace(
+ std::size_t pos, std::size_t len, const CharacterValueImpl &other);
+
+ /// Return a suffix starting at pos.
+ CharacterValueImpl substr(std::size_t pos) const;
+
+ /// Return a substring of len characters starting at pos.
+ CharacterValueImpl substr(std::size_t pos, std::size_t len) const;
+
+ CharacterValueImpl ToAscii(int kind) const;
+
+ /// Reserve storage for at least n characters.
+ void reserve(std::size_t n);
+
+ /// Return the character at position i as char32_t (safe for all kinds).
+ char32_t operator[](std::size_t i) const;
+
+ /// Concatenate two same-kind strings.
+ CharacterValueImpl operator+(const CharacterValueImpl &y) const;
+
+ /// Append another same-kind string.
+ CharacterValueImpl &operator+=(const CharacterValueImpl &y);
+
+ /// Append a character, converting it to the string's element type.
+ CharacterValueImpl &operator+=(char c);
+
+ /// Sentinel value for "not found" positions (same as std::string::npos).
+ static constexpr std::size_t npos{std::string::npos};
+
+ // Find-family methods; return npos when not found.
+ std::size_t find_first_not_of(char c) const {
+ return find_first_not_of(static_cast<char32_t>(c));
+ }
+ std::size_t find_first_not_of(char16_t c) const {
+ return find_first_not_of(static_cast<char32_t>(c));
+ }
+ std::size_t find_first_not_of(char32_t c) const;
+ std::size_t find_last_not_of(char c) const {
+ return find_last_not_of(static_cast<char32_t>(c));
+ }
+ std::size_t find_last_not_of(char16_t c) const {
+ return find_last_not_of(static_cast<char32_t>(c));
+ }
+ std::size_t find_last_not_of(char32_t c) const;
+ std::size_t find_first_not_of(const CharacterValueImpl &set) const;
+ std::size_t find_last_not_of(const CharacterValueImpl &set) const;
+ std::size_t find(const CharacterValueImpl &pattern) const;
+ std::size_t rfind(const CharacterValueImpl &pattern) const;
+ std::size_t find_first_of(const CharacterValueImpl &set) const;
+ std::size_t find_last_of(const CharacterValueImpl &set) const;
+
+ void StoreRawBytes(void *dst, size_t size, bool *changed) const;
+
+ // Compile-time dispatchers to current/specified kind
+
+ template <typename F>
+ auto withCharProto(F &&f) const
+ -> decltype(std::declval<F>()(std::declval<char>())) {
+ switch (storage_.index()) {
+ case 1:
+ return f(char{});
+ case 2:
+ return f(char16_t{});
+ case 3:
+ return f(char32_t{});
+ default:
+ llvm_unreachable("unsupported character kind/monostate");
+ }
+ }
+
+ template <typename F>
+ static auto withCharProto(int kind, F &&f)
+ -> decltype(std::declval<F>()(std::declval<char>())) {
+ switch (kind) {
+ case 1:
+ return f(char{});
+ case 2:
+ return f(char16_t{});
+ case 4:
+ return f(char32_t{});
+ default:
+ llvm_unreachable("unsupported character kind/monostate");
+ }
+ }
+
+ template <typename F>
+ auto withStdString(F &&f) const
+ -> decltype(std::declval<F>()(std::declval<const std::string &>())) {
+ switch (storage_.index()) {
+ case 1:
+ return f(std::get<std::string>(storage_));
+ case 2:
+ return f(std::get<std::u16string>(storage_));
+ case 3:
+ return f(std::get<std::u32string>(storage_));
+ default:
+ llvm_unreachable("operation on uninitialized CharacterValue");
+ }
+ }
+
+private:
+ Storage storage_;
+};
+
+} // namespace Fortran::evaluate::value
+#endif // FORTRAN_EVALUATE_CHARACTER_VALUE_IMPL_H_
diff --git a/flang/lib/Evaluate/character-value.cpp b/flang/lib/Evaluate/character-value.cpp
new file mode 100644
index 0000000000000..4348be0b4478c
--- /dev/null
+++ b/flang/lib/Evaluate/character-value.cpp
@@ -0,0 +1,215 @@
+//===-- lib/Evaluate/character-value.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/character-value.h"
+#include "character-value-impl.h"
+#include "flang/Evaluate/common.h"
+#include "llvm/Support/ErrorHandling.h"
+#include <new>
+#include <string>
+
+namespace Fortran::evaluate::value {
+static_assert(sizeof(CharacterValueImpl) == detail::kCharacterObjectSize);
+static_assert(alignof(CharacterValueImpl) == detail::kCharacterObjectAlign);
+static_assert(sizeof(CharacterValue) == sizeof(CharacterValueImpl));
+static_assert(alignof(CharacterValue) == alignof(CharacterValueImpl));
+
+CharacterValue::CharacterValue() { new (this) CharacterValueImpl(); }
+
+CharacterValue::~CharacterValue() { impl().~CharacterValueImpl(); }
+
+CharacterValue::CharacterValue(const CharacterValue &x) {
+ new (this) CharacterValueImpl(x.impl());
+}
+
+CharacterValue::CharacterValue(CharacterValue &&x) {
+ new (this) CharacterValueImpl(std::move(x.impl()));
+}
+
+CharacterValue &CharacterValue::operator=(const CharacterValue &x) {
+ impl() = x.impl();
+ return *this;
+}
+
+CharacterValue &CharacterValue::operator=(CharacterValue &&x) {
+ impl() = std::move(x.impl());
+ return *this;
+}
+
+CharacterValue::CharacterValue(int kind, std::string s) {
+ CHECK(kind == 1);
+ new (this) CharacterValueImpl(std::move(s));
+}
+
+CharacterValue::CharacterValue(int kind, std::u16string s) {
+ CHECK(kind == 2);
+ new (this) CharacterValueImpl(std::move(s));
+}
+
+CharacterValue::CharacterValue(int kind, std::u32string s) {
+ CHECK(kind == 4);
+ new (this) CharacterValueImpl(std::move(s));
+}
+
+CharacterValue::CharacterValue(int kind, std::size_t n, char32_t c) {
+ new (this) CharacterValueImpl(kind, n, c);
+}
+
+CharacterValue CharacterValue::Zero(int kind) {
+ return FromImpl(CharacterValueImpl::Zero(kind));
+}
+
+CharacterValue CharacterValue::FromRawBytes(
+ int kind, const void *raw, size_t byteSize) {
+ return FromImpl(CharacterValueImpl::FromRawBytes(kind, raw, byteSize));
+}
+
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+LLVM_DUMP_METHOD void CharacterValue::dump() const { impl().dump(); }
+#endif
+
+bool CharacterValue::IsMonostate() const { return impl().IsMonostate(); }
+
+bool CharacterValue::empty() const { return impl().empty(); }
+
+std::size_t CharacterValue::size() const { return impl().size(); }
+
+int CharacterValue::kind() const { return impl().kind(); }
+
+std::optional<llvm::StringRef> CharacterValue::AsStringRef() const {
+ return impl().AsStringRef();
+}
+
+std::optional<std::u16string> CharacterValue::AsU16String() const {
+ return impl().AsU16String();
+}
+
+std::optional<std::u32string> CharacterValue::AsU32String() const {
+ return impl().AsU32String();
+}
+
+Ordering CharacterValue::Compare(const CharacterValue &y) const {
+ return impl().Compare(y.impl());
+}
+
+bool CharacterValue::operator<(const CharacterValue &y) const {
+ return impl() < y.impl();
+}
+
+bool CharacterValue::operator==(const CharacterValue &y) const {
+ return impl() == y.impl();
+}
+
+CharacterValue CharacterValue::ToAscii(int kind) const {
+ return FromImpl(impl().ToAscii(kind));
+}
+
+void CharacterValue::assign(int kind, std::size_t n, char32_t c) {
+ impl().assign(kind, n, c);
+}
+
+void CharacterValue::assign(const char *p, std::size_t n) {
+ impl().assign(p, n);
+}
+
+void CharacterValue::assign(const char16_t *p, std::size_t n) {
+ impl().assign(p, n);
+}
+
+void CharacterValue::assign(const char32_t *p, std::size_t n) {
+ impl().assign(p, n);
+}
+
+void CharacterValue::erase(std::size_t pos) { impl().erase(pos); }
+
+void CharacterValue::append(std::size_t n, char32_t c) { impl().append(n, c); }
+
+CharacterValue &CharacterValue::replace(
+ std::size_t pos, std::size_t len, const CharacterValue &other) {
+ impl().replace(pos, len, other.impl());
+ return *this;
+}
+
+CharacterValue CharacterValue::substr(std::size_t pos) const {
+ return FromImpl(impl().substr(pos));
+}
+
+CharacterValue CharacterValue::substr(std::size_t pos, std::size_t len) const {
+ return FromImpl(impl().substr(pos, len));
+}
+
+void CharacterValue::reserve(std::size_t n) { impl().reserve(n); }
+char32_t CharacterValue::operator[](std::size_t i) const { return impl()[i]; }
+
+CharacterValue CharacterValue::operator+(const CharacterValue &y) const {
+ return FromImpl(impl() + y.impl());
+}
+
+CharacterValue &CharacterValue::operator+=(const CharacterValue &y) {
+ impl() += y.impl();
+ return *this;
+}
+
+CharacterValue &CharacterValue::operator+=(char c) {
+ impl() += c;
+ return *this;
+}
+
+std::size_t CharacterValue::find(const CharacterValue &pattern) const {
+ return impl().find(pattern.impl());
+}
+
+std::size_t CharacterValue::rfind(const CharacterValue &pattern) const {
+ return impl().rfind(pattern.impl());
+}
+
+std::size_t CharacterValue::find_first_of(const CharacterValue &set) const {
+ return impl().find_first_of(set.impl());
+}
+
+std::size_t CharacterValue::find_last_of(const CharacterValue &set) const {
+ return impl().find_last_of(set.impl());
+}
+
+std::size_t CharacterValue::find_first_not_of(char32_t c) const {
+ return impl().find_first_not_of(c);
+}
+
+std::size_t CharacterValue::find_last_not_of(char32_t c) const {
+ return impl().find_last_not_of(c);
+}
+
+std::size_t CharacterValue::find_first_not_of(const CharacterValue &set) const {
+ return impl().find_first_not_of(set.impl());
+}
+
+std::size_t CharacterValue::find_last_not_of(const CharacterValue &set) const {
+ return impl().find_last_not_of(set.impl());
+}
+
+void *CharacterValue::data() { return impl().data(); }
+const void *CharacterValue::data() const { return impl().data(); }
+
+void CharacterValue::StoreRawBytes(
+ void *dst, size_t size, bool *changed) const {
+ impl().StoreRawBytes(dst, size, changed);
+}
+
+CharacterValue CharacterValue::FromImpl(const CharacterValueImpl &y) {
+ CharacterValue result;
+ result.impl() = y;
+ return result;
+}
+
+CharacterValue CharacterValue::FromImpl(CharacterValueImpl &&y) {
+ CharacterValue result;
+ result.impl() = std::move(y);
+ return result;
+}
+
+} // namespace Fortran::evaluate::value
diff --git a/flang/lib/Evaluate/character.h b/flang/lib/Evaluate/character.h
index 2d6747741161b..c1a34257e5682 100644
--- a/flang/lib/Evaluate/character.h
+++ b/flang/lib/Evaluate/character.h
@@ -9,7 +9,9 @@
#ifndef FORTRAN_EVALUATE_CHARACTER_H_
#define FORTRAN_EVALUATE_CHARACTER_H_
+#include "flang/Evaluate/character-value.h"
#include "flang/Evaluate/type.h"
+#include <cstdint>
#include <string>
// Provides implementations of intrinsic functions operating on character
@@ -17,41 +19,53 @@
namespace Fortran::evaluate {
-template <int KIND> class CharacterUtils {
- using Character = Scalar<Type<TypeCategory::Character, KIND>>;
- using CharT = typename Character::value_type;
+class CharacterUtils {
+ using Character = Scalar<Type<TypeCategory::Character>>;
+ using CharT = char32_t;
public:
// CHAR also implements ACHAR under assumption that character encodings
// contain ASCII
- static Character CHAR(std::uint64_t code) {
- return Character{{static_cast<CharT>(code)}};
+ static Character CHAR(int kind, std::uint64_t code) {
+ return Character{kind, 1, static_cast<CharT>(code)};
}
// ICHAR also implements IACHAR under assumption that character encodings
// contain ASCII
static std::int64_t ICHAR(const Character &c) {
CHECK(c.length() == 1);
- // Convert first to an unsigned integer type to avoid sign extension
- return static_cast<common::HostUnsignedIntType<(8 * KIND)>>(c[0]);
+ // Mask to the character kind width to avoid sign extension
+ auto ch{static_cast<std::uint64_t>(c[0])};
+ switch (c.kind()) {
+ case 1:
+ return static_cast<std::int64_t>(ch & 0xffu);
+ case 2:
+ return static_cast<std::int64_t>(ch & 0xffffu);
+ case 4:
+ return static_cast<std::int64_t>(ch & 0xffffffffu);
+ }
+ llvm_unreachable("unsupported character kind");
}
- static Character NEW_LINE() { return Character{{NewLine()}}; }
+ static Character NEW_LINE(int kind) { return Character{kind, 1, NewLine()}; }
static Character ADJUSTL(const Character &str) {
+ const int kind{str.kind()};
auto pos{str.find_first_not_of(Space())};
if (pos != Character::npos && pos != 0) {
- return Character{str.substr(pos) + Character(pos, Space())};
+ return Character{str.substr(pos) + Character{kind, pos, Space()}};
}
// else empty or only spaces, or no leading spaces
return str;
}
static Character ADJUSTR(const Character &str) {
+ const int kind{str.kind()};
auto pos{str.find_last_not_of(Space())};
if (pos != Character::npos && pos != str.length() - 1) {
auto delta{str.length() - 1 - pos};
- return Character{Character(delta, Space()) + str.substr(0, pos + 1)};
+ return Character{
+ Character{kind, delta, Space()} + str.substr(0, pos + 1)};
}
// else empty or only spaces, or no trailing spaces
return str;
@@ -78,9 +92,10 @@ template <int KIND> class CharacterUtils {
// Resize adds spaces on the right if the new size is bigger than the
// original, or by trimming the rightmost characters otherwise.
static Character Resize(const Character &str, std::size_t newLength) {
+ const int kind{str.kind()};
auto oldLength{str.length()};
if (newLength > oldLength) {
- return str + Character(newLength - oldLength, Space());
+ return str + Character{kind, newLength - oldLength, Space()};
} else {
return str.substr(0, newLength);
}
@@ -97,7 +112,8 @@ template <int KIND> class CharacterUtils {
}
static Character REPEAT(const Character &str, ConstantSubscript ncopies) {
- Character result;
+ const int kind{str.kind()};
+ Character result{Character::Zero(kind)};
if (!str.empty() && ncopies > 0) {
result.reserve(ncopies * str.size());
while (ncopies-- > 0) {
diff --git a/flang/lib/Evaluate/characteristics.cpp b/flang/lib/Evaluate/characteristics.cpp
index 4b05a25fd8f58..4f1c6831e3aeb 100644
--- a/flang/lib/Evaluate/characteristics.cpp
+++ b/flang/lib/Evaluate/characteristics.cpp
@@ -9,6 +9,7 @@
#include "flang/Evaluate/characteristics.h"
#include "flang/Common/indirection.h"
#include "flang/Evaluate/check-expression.h"
+#include "flang/Evaluate/expression.h"
#include "flang/Evaluate/fold.h"
#include "flang/Evaluate/intrinsics.h"
#include "flang/Evaluate/tools.h"
@@ -194,10 +195,9 @@ std::optional<Expr<SubscriptInteger>> TypeAndShape::MeasureElementSizeInBytes(
if (LEN_) {
CHECK(type_.category() == TypeCategory::Character);
return Fold(foldingContext,
- Expr<SubscriptInteger>{
- foldingContext.targetCharacteristics().GetByteSize(
- type_.category(), type_.kind())} *
- Expr<SubscriptInteger>{*LEN_});
+ MakeSubscriptIntExpr(foldingContext.targetCharacteristics().GetByteSize(
+ type_.category(), type_.kind())) *
+ common::Clone(*LEN_));
}
if (auto elementBytes{type_.MeasureSizeInBytes(foldingContext, align)}) {
return Fold(foldingContext, std::move(*elementBytes));
diff --git a/flang/lib/Evaluate/check-expression.cpp b/flang/lib/Evaluate/check-expression.cpp
index 737502a504d61..d3722c46632f3 100644
--- a/flang/lib/Evaluate/check-expression.cpp
+++ b/flang/lib/Evaluate/check-expression.cpp
@@ -71,10 +71,8 @@ class IsConstantExprHelper
return (*this)(component.base());
}
// Prevent integer division by known zeroes in constant expressions.
- template <int KIND>
- bool operator()(
- const Divide<Type<TypeCategory::Integer, KIND>> &division) const {
- using T = Type<TypeCategory::Integer, KIND>;
+ bool operator()(const Divide<Type<TypeCategory::Integer>> &division) const {
+ using T = Type<TypeCategory::Integer>;
if ((*this)(division.left()) && (*this)(division.right())) {
const auto divisor{GetScalarConstantValue<T>(division.right())};
return !divisor || !divisor->IsZero();
@@ -481,9 +479,9 @@ class SuspiciousRealLiteralFinder
SuspiciousRealLiteralFinder(int kind, FoldingContext &c)
: Base{*this}, kind_{kind}, context_{c} {}
using Base::operator();
- template <int KIND>
- bool operator()(const Constant<Type<TypeCategory::Real, KIND>> &x) const {
- if (kind_ > KIND && x.result().isFromInexactLiteralConversion()) {
+ bool operator()(const Constant<Type<TypeCategory::Real>> &x) const {
+ const int kind{x.kind()};
+ if (kind_ > kind && x.result().isFromInexactLiteralConversion()) {
context_.Warn(common::UsageWarning::RealConstantWidening,
"Default real literal in REAL(%d) context might need a kind suffix, as its rounded value %s is inexact"_warn_en_US,
kind_, x.AsFortran());
@@ -492,9 +490,9 @@ class SuspiciousRealLiteralFinder
return false;
}
}
- template <int KIND>
- bool operator()(const Constant<Type<TypeCategory::Complex, KIND>> &x) const {
- if (kind_ > KIND && x.result().isFromInexactLiteralConversion()) {
+ bool operator()(const Constant<Type<TypeCategory::Complex>> &x) const {
+ const int kind{x.kind()};
+ if (kind_ > kind && x.result().isFromInexactLiteralConversion()) {
context_.Warn(common::UsageWarning::RealConstantWidening,
"Default real literal in COMPLEX(%d) context might need a kind suffix, as its rounded value %s is inexact"_warn_en_US,
kind_, x.AsFortran());
@@ -503,13 +501,14 @@ class SuspiciousRealLiteralFinder
return false;
}
}
- template <TypeCategory TOCAT, int TOKIND, TypeCategory FROMCAT>
- bool operator()(const Convert<Type<TOCAT, TOKIND>, FROMCAT> &x) const {
+ template <TypeCategory TOCAT, TypeCategory FROMCAT>
+ bool operator()(const Convert<Type<TOCAT>, FROMCAT> &x) const {
+ const int toKind{x.kind()};
if constexpr ((TOCAT == TypeCategory::Real ||
TOCAT == TypeCategory::Complex) &&
(FROMCAT == TypeCategory::Real || FROMCAT == TypeCategory::Complex)) {
auto fromType{x.left().GetType()};
- if (!fromType || fromType->kind() < TOKIND) {
+ if (!fromType || fromType->kind() < toKind) {
return false;
}
}
@@ -548,9 +547,8 @@ class InexactLiteralConversionFlagClearer
using Base = AnyTraverse<InexactLiteralConversionFlagClearer>;
InexactLiteralConversionFlagClearer() : Base(*this) {}
using Base::operator();
- template <int KIND>
- bool operator()(const Constant<Type<TypeCategory::Real, KIND>> &x) const {
- auto &mut{const_cast<Type<TypeCategory::Real, KIND> &>(x.result())};
+ bool operator()(const Constant<Type<TypeCategory::Real>> &x) const {
+ auto &mut{const_cast<Type<TypeCategory::Real> &>(x.result())};
mut.set_isFromInexactLiteralConversion(false);
return false;
}
diff --git a/flang/lib/Evaluate/common.cpp b/flang/lib/Evaluate/common.cpp
index 119ea3c5612a5..49874c4b5a744 100644
--- a/flang/lib/Evaluate/common.cpp
+++ b/flang/lib/Evaluate/common.cpp
@@ -8,11 +8,17 @@
#include "flang/Evaluate/common.h"
#include "flang/Common/idioms.h"
+#include "flang/Evaluate/character-value.h"
using namespace Fortran::parser::literals;
namespace Fortran::evaluate {
+Ordering Compare(
+ const value::CharacterValue &x, const value::CharacterValue &y) {
+ return x.Compare(y);
+}
+
void FoldingContext::RealFlagWarnings(
const RealFlags &flags, const char *operation) {
static constexpr auto warning{common::UsageWarning::FoldingException};
diff --git a/flang/lib/Evaluate/complex-value.cpp b/flang/lib/Evaluate/complex-value.cpp
new file mode 100644
index 0000000000000..9697a0bdb18e7
--- /dev/null
+++ b/flang/lib/Evaluate/complex-value.cpp
@@ -0,0 +1,182 @@
+//===-- lib/Evaluate/complex-value.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/complex-value.h"
+#include "flang/Common/idioms.h"
+#include "llvm/Support/raw_ostream.h"
+#include <string>
+
+namespace Fortran::evaluate::value {
+
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+LLVM_DUMP_METHOD void ComplexValue::dump() const {
+ AsFortran(llvm::errs(), kind()) << '\n';
+}
+#endif
+
+ValueWithRealFlags<ComplexValue> ComplexValue::FromInteger(
+ int kind, const IntegerValue &n, bool isUnsigned, Rounding rounding) {
+ CHECK(!n.IsMonostate());
+
+ ValueWithRealFlags<ComplexValue> result;
+ result.value.re_ = RealValue::FromInteger(kind, n, isUnsigned, rounding)
+ .AccumulateFlags(result.flags);
+ result.value.im_ = RealValue::Zero(kind);
+ return result;
+}
+
+ValueWithRealFlags<ComplexValue> ComplexValue::Add(
+ const ComplexValue &y, Rounding rounding) const {
+ CHECK(!IsMonostate());
+
+ RealFlags flags;
+ RealValue reSum{re_.Add(y.re_, rounding).AccumulateFlags(flags)};
+ RealValue imSum{im_.Add(y.im_, rounding).AccumulateFlags(flags)};
+ return {ComplexValue{reSum, imSum}, flags};
+}
+
+ValueWithRealFlags<ComplexValue> ComplexValue::Subtract(
+ const ComplexValue &y, Rounding rounding) const {
+ CHECK(!IsMonostate());
+
+ RealFlags flags;
+ RealValue reDiff{re_.Subtract(y.re_, rounding).AccumulateFlags(flags)};
+ RealValue imDiff{im_.Subtract(y.im_, rounding).AccumulateFlags(flags)};
+ return {ComplexValue{reDiff, imDiff}, flags};
+}
+
+ValueWithRealFlags<ComplexValue> ComplexValue::Multiply(
+ const ComplexValue &y, Rounding rounding) const {
+ CHECK(!IsMonostate());
+
+ // (a + ib)*(c + id) -> ac - bd + i(ad + bc)
+ RealFlags flags;
+ RealValue ac{re_.Multiply(y.re_, rounding).AccumulateFlags(flags)};
+ RealValue bd{im_.Multiply(y.im_, rounding).AccumulateFlags(flags)};
+ RealValue ad{re_.Multiply(y.im_, rounding).AccumulateFlags(flags)};
+ RealValue bc{im_.Multiply(y.re_, rounding).AccumulateFlags(flags)};
+ RealValue acbd{ac.Subtract(bd, rounding).AccumulateFlags(flags)};
+ RealValue adbc{ad.Add(bc, rounding).AccumulateFlags(flags)};
+ return {ComplexValue{acbd, adbc}, flags};
+}
+
+ValueWithRealFlags<ComplexValue> ComplexValue::Divide(
+ const ComplexValue &that, Rounding rounding) const {
+ CHECK(!IsMonostate());
+
+ // (a + ib)/(c + id) -> [(a+ib)*(c-id)] / [(c+id)*(c-id)]
+ // -> [ac+bd+i(bc-ad)] / (cc+dd) -- note (cc+dd) is real
+ // -> ((ac+bd)/(cc+dd)) + i((bc-ad)/(cc+dd))
+ RealFlags flags;
+ RealValue cc{that.re_.Multiply(that.re_, rounding).AccumulateFlags(flags)};
+ RealValue dd{that.im_.Multiply(that.im_, rounding).AccumulateFlags(flags)};
+ RealValue ccPdd{cc.Add(dd, rounding).AccumulateFlags(flags)};
+ if (!flags.test(RealFlag::Overflow) && !flags.test(RealFlag::Underflow)) {
+ // den = (cc+dd) did not overflow or underflow; try the naive
+ // sequence without scaling to avoid extra roundings.
+ RealValue ac{re_.Multiply(that.re_, rounding).AccumulateFlags(flags)};
+ RealValue ad{re_.Multiply(that.im_, rounding).AccumulateFlags(flags)};
+ RealValue bc{im_.Multiply(that.re_, rounding).AccumulateFlags(flags)};
+ RealValue bd{im_.Multiply(that.im_, rounding).AccumulateFlags(flags)};
+ RealValue acPbd{ac.Add(bd, rounding).AccumulateFlags(flags)};
+ RealValue bcSad{bc.Subtract(ad, rounding).AccumulateFlags(flags)};
+ RealValue re{acPbd.Divide(ccPdd, rounding).AccumulateFlags(flags)};
+ RealValue im{bcSad.Divide(ccPdd, rounding).AccumulateFlags(flags)};
+ if (!flags.test(RealFlag::Overflow) && !flags.test(RealFlag::Underflow)) {
+ return {ComplexValue{re, im}, flags};
+ }
+ }
+ // Scale numerator and denominator by d/c (if c>=d) or c/d (if c<d)
+ flags.clear();
+ RealValue scale; // will be <= 1.0 in magnitude
+ bool cGEd{that.re_.ABS().Compare(that.im_.ABS()) != Relation::Less};
+ if (cGEd) {
+ scale = that.im_.Divide(that.re_, rounding).AccumulateFlags(flags);
+ } else {
+ scale = that.re_.Divide(that.im_, rounding).AccumulateFlags(flags);
+ }
+ RealValue den;
+ if (cGEd) {
+ RealValue dS{scale.Multiply(that.im_, rounding).AccumulateFlags(flags)};
+ den = dS.Add(that.re_, rounding).AccumulateFlags(flags);
+ } else {
+ RealValue cS{scale.Multiply(that.re_, rounding).AccumulateFlags(flags)};
+ den = cS.Add(that.im_, rounding).AccumulateFlags(flags);
+ }
+ RealValue aS{scale.Multiply(re_, rounding).AccumulateFlags(flags)};
+ RealValue bS{scale.Multiply(im_, rounding).AccumulateFlags(flags)};
+ RealValue re1, im1;
+ if (cGEd) {
+ re1 = re_.Add(bS, rounding).AccumulateFlags(flags);
+ im1 = im_.Subtract(aS, rounding).AccumulateFlags(flags);
+ } else {
+ re1 = aS.Add(im_, rounding).AccumulateFlags(flags);
+ im1 = bS.Subtract(re_, rounding).AccumulateFlags(flags);
+ }
+ RealValue re{re1.Divide(den, rounding).AccumulateFlags(flags)};
+ RealValue im{im1.Divide(den, rounding).AccumulateFlags(flags)};
+ return {ComplexValue{re, im}, flags};
+}
+
+ValueWithRealFlags<ComplexValue> ComplexValue::KahanSummation(
+ const ComplexValue &y, ComplexValue &correction, Rounding rounding) const {
+ CHECK(!y.IsMonostate());
+ CHECK(!correction.IsMonostate());
+
+ RealFlags flags;
+ RealValue reSum{re_.KahanSummation(y.re_, correction.re_, rounding)
+ .AccumulateFlags(flags)};
+ RealValue imSum{im_.KahanSummation(y.im_, correction.im_, rounding)
+ .AccumulateFlags(flags)};
+ return {ComplexValue{reSum, imSum}, flags};
+}
+
+std::string ComplexValue::DumpHexadecimal() const {
+ CHECK(!IsMonostate());
+
+ std::string result{'('};
+ result += re_.DumpHexadecimal();
+ result += ',';
+ result += im_.DumpHexadecimal();
+ result += ')';
+ return result;
+}
+
+llvm::raw_ostream &ComplexValue::AsFortran(
+ llvm::raw_ostream &o, int kind) const {
+ CHECK(!IsMonostate());
+
+ re_.AsFortran(o << '(', kind);
+ im_.AsFortran(o << ',', kind);
+ return o << ')';
+}
+
+void ComplexValue::StoreRawBytes(
+ void *dst, [[maybe_unused]] size_t expectedSize, bool *changed) const {
+ CHECK(!IsMonostate());
+ CHECK(re_.bits() == im_.bits());
+ CHECK(expectedSize == re_.bytesStored() + im_.bytesStored());
+
+ re_.StoreRawBytes(dst, re_.bytesStored(), changed);
+ im_.StoreRawBytes(
+ static_cast<char *>(dst) + re_.bytesStored(), im_.bytesStored(), changed);
+}
+
+ComplexValue ComplexValue::FromRawBytes(
+ int kind, const void *raw, std::size_t expectedSize) {
+ CHECK(expectedSize == static_cast<size_t>(-1) ||
+ expectedSize == bytesStored(kind));
+ std::size_t partBytes{RealValue::bytesStored(kind)};
+ const char *data{static_cast<const char *>(raw)};
+ RealValue realPart{RealValue::FromRawBytes(kind, data, partBytes)};
+ RealValue imagPart{
+ RealValue::FromRawBytes(kind, data + partBytes, partBytes)};
+ return {realPart, imagPart};
+}
+
+} // namespace Fortran::evaluate::value
diff --git a/flang/lib/Evaluate/complex.cpp b/flang/lib/Evaluate/complex.cpp
deleted file mode 100644
index a245fb38c82b9..0000000000000
--- a/flang/lib/Evaluate/complex.cpp
+++ /dev/null
@@ -1,136 +0,0 @@
-//===-- lib/Evaluate/complex.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/complex.h"
-#include "llvm/Support/raw_ostream.h"
-
-namespace Fortran::evaluate::value {
-
-template <typename R>
-ValueWithRealFlags<Complex<R>> Complex<R>::Add(
- const Complex &that, Rounding rounding) const {
- RealFlags flags;
- Part reSum{re_.Add(that.re_, rounding).AccumulateFlags(flags)};
- Part imSum{im_.Add(that.im_, rounding).AccumulateFlags(flags)};
- return {Complex{reSum, imSum}, flags};
-}
-
-template <typename R>
-ValueWithRealFlags<Complex<R>> Complex<R>::Subtract(
- const Complex &that, Rounding rounding) const {
- RealFlags flags;
- Part reDiff{re_.Subtract(that.re_, rounding).AccumulateFlags(flags)};
- Part imDiff{im_.Subtract(that.im_, rounding).AccumulateFlags(flags)};
- return {Complex{reDiff, imDiff}, flags};
-}
-
-template <typename R>
-ValueWithRealFlags<Complex<R>> Complex<R>::Multiply(
- const Complex &that, Rounding rounding) const {
- // (a + ib)*(c + id) -> ac - bd + i(ad + bc)
- RealFlags flags;
- Part ac{re_.Multiply(that.re_, rounding).AccumulateFlags(flags)};
- Part bd{im_.Multiply(that.im_, rounding).AccumulateFlags(flags)};
- Part ad{re_.Multiply(that.im_, rounding).AccumulateFlags(flags)};
- Part bc{im_.Multiply(that.re_, rounding).AccumulateFlags(flags)};
- Part acbd{ac.Subtract(bd, rounding).AccumulateFlags(flags)};
- Part adbc{ad.Add(bc, rounding).AccumulateFlags(flags)};
- return {Complex{acbd, adbc}, flags};
-}
-
-template <typename R>
-ValueWithRealFlags<Complex<R>> Complex<R>::Divide(
- const Complex &that, Rounding rounding) const {
- // (a + ib)/(c + id) -> [(a+ib)*(c-id)] / [(c+id)*(c-id)]
- // -> [ac+bd+i(bc-ad)] / (cc+dd) -- note (cc+dd) is real
- // -> ((ac+bd)/(cc+dd)) + i((bc-ad)/(cc+dd))
- RealFlags flags;
- Part cc{that.re_.Multiply(that.re_, rounding).AccumulateFlags(flags)};
- Part dd{that.im_.Multiply(that.im_, rounding).AccumulateFlags(flags)};
- Part ccPdd{cc.Add(dd, rounding).AccumulateFlags(flags)};
- if (!flags.test(RealFlag::Overflow) && !flags.test(RealFlag::Underflow)) {
- // den = (cc+dd) did not overflow or underflow; try the naive
- // sequence without scaling to avoid extra roundings.
- Part ac{re_.Multiply(that.re_, rounding).AccumulateFlags(flags)};
- Part ad{re_.Multiply(that.im_, rounding).AccumulateFlags(flags)};
- Part bc{im_.Multiply(that.re_, rounding).AccumulateFlags(flags)};
- Part bd{im_.Multiply(that.im_, rounding).AccumulateFlags(flags)};
- Part acPbd{ac.Add(bd, rounding).AccumulateFlags(flags)};
- Part bcSad{bc.Subtract(ad, rounding).AccumulateFlags(flags)};
- Part re{acPbd.Divide(ccPdd, rounding).AccumulateFlags(flags)};
- Part im{bcSad.Divide(ccPdd, rounding).AccumulateFlags(flags)};
- if (!flags.test(RealFlag::Overflow) && !flags.test(RealFlag::Underflow)) {
- return {Complex{re, im}, flags};
- }
- }
- // Scale numerator and denominator by d/c (if c>=d) or c/d (if c<d)
- flags.clear();
- Part scale; // will be <= 1.0 in magnitude
- bool cGEd{that.re_.ABS().Compare(that.im_.ABS()) != Relation::Less};
- if (cGEd) {
- scale = that.im_.Divide(that.re_, rounding).AccumulateFlags(flags);
- } else {
- scale = that.re_.Divide(that.im_, rounding).AccumulateFlags(flags);
- }
- Part den;
- if (cGEd) {
- Part dS{scale.Multiply(that.im_, rounding).AccumulateFlags(flags)};
- den = dS.Add(that.re_, rounding).AccumulateFlags(flags);
- } else {
- Part cS{scale.Multiply(that.re_, rounding).AccumulateFlags(flags)};
- den = cS.Add(that.im_, rounding).AccumulateFlags(flags);
- }
- Part aS{scale.Multiply(re_, rounding).AccumulateFlags(flags)};
- Part bS{scale.Multiply(im_, rounding).AccumulateFlags(flags)};
- Part re1, im1;
- if (cGEd) {
- re1 = re_.Add(bS, rounding).AccumulateFlags(flags);
- im1 = im_.Subtract(aS, rounding).AccumulateFlags(flags);
- } else {
- re1 = aS.Add(im_, rounding).AccumulateFlags(flags);
- im1 = bS.Subtract(re_, rounding).AccumulateFlags(flags);
- }
- Part re{re1.Divide(den, rounding).AccumulateFlags(flags)};
- Part im{im1.Divide(den, rounding).AccumulateFlags(flags)};
- return {Complex{re, im}, flags};
-}
-
-template <typename R>
-ValueWithRealFlags<Complex<R>> Complex<R>::KahanSummation(
- const Complex &that, Complex &correction, Rounding rounding) const {
- RealFlags flags;
- Part reSum{re_.KahanSummation(that.re_, correction.re_, rounding)
- .AccumulateFlags(flags)};
- Part imSum{im_.KahanSummation(that.im_, correction.im_, rounding)
- .AccumulateFlags(flags)};
- return {Complex{reSum, imSum}, flags};
-}
-
-template <typename R> std::string Complex<R>::DumpHexadecimal() const {
- std::string result{'('};
- result += re_.DumpHexadecimal();
- result += ',';
- result += im_.DumpHexadecimal();
- result += ')';
- return result;
-}
-
-template <typename R>
-llvm::raw_ostream &Complex<R>::AsFortran(llvm::raw_ostream &o, int kind) const {
- re_.AsFortran(o << '(', kind);
- im_.AsFortran(o << ',', kind);
- return o << ')';
-}
-
-template class Complex<Real<Integer<16>, 11>>;
-template class Complex<Real<Integer<16>, 8>>;
-template class Complex<Real<Integer<32>, 24>>;
-template class Complex<Real<Integer<64>, 53>>;
-template class Complex<Real<X87IntegerContainer, 64>>;
-template class Complex<Real<Integer<128>, 113>>;
-} // namespace Fortran::evaluate::value
diff --git a/flang/lib/Evaluate/constant.cpp b/flang/lib/Evaluate/constant.cpp
index 7fe000892ac1a..7a78c1ade352a 100644
--- a/flang/lib/Evaluate/constant.cpp
+++ b/flang/lib/Evaluate/constant.cpp
@@ -144,8 +144,10 @@ bool HasNegativeExtent(const ConstantSubscripts &shape) {
template <typename RESULT, typename ELEMENT>
ConstantBase<RESULT, ELEMENT>::ConstantBase(
- std::vector<Element> &&x, ConstantSubscripts &&sh, Result res)
- : ConstantBounds(std::move(sh)), result_{res}, values_(std::move(x)) {
+ int kind, std::vector<Element> &&x, ConstantSubscripts &&sh, Result res)
+ : ConstantBounds(std::move(sh)), kind_{kind}, result_{res},
+ values_(std::move(x)) {
+ CHECK_KIND(kind, RESULT);
CHECK(TotalElementCount(shape()) && size() == *TotalElementCount(shape()));
}
@@ -154,7 +156,8 @@ ConstantBase<RESULT, ELEMENT>::~ConstantBase() {}
template <typename RESULT, typename ELEMENT>
bool ConstantBase<RESULT, ELEMENT>::operator==(const ConstantBase &that) const {
- return shape() == that.shape() && values_ == that.values_;
+ return kind() == that.kind() && shape() == that.shape() &&
+ values_ == that.values_;
}
template <typename RESULT, typename ELEMENT>
@@ -198,7 +201,8 @@ auto Constant<T>::At(const ConstantSubscripts &index) const -> Element {
template <typename T>
auto Constant<T>::Reshape(ConstantSubscripts &&dims) const -> Constant {
- return {Base::Reshape(dims), std::move(dims)};
+ const int kind{Base::kind()};
+ return {kind, Base::Reshape(dims), std::move(dims)};
}
template <typename T>
@@ -207,25 +211,28 @@ std::size_t Constant<T>::CopyFrom(const Constant<T> &source, std::size_t count,
return Base::CopyFrom(source, count, resultSubscripts, dimOrder);
}
-// Constant<Type<TypeCategory::Character, KIND> specializations
-template <int KIND>
-Constant<Type<TypeCategory::Character, KIND>>::Constant(
- const Scalar<Result> &str)
- : values_{str}, length_{static_cast<ConstantSubscript>(values_.size())} {}
+// Constant<Type<TypeCategory::Character>> specialization
+Constant<Type<TypeCategory::Character>>::Constant(
+ int kind, const Scalar<Result> &str)
+ : kind_{kind}, values_{str},
+ length_{static_cast<ConstantSubscript>(values_.size())} {
+ CHECK(str.kind() == kind);
+}
-template <int KIND>
-Constant<Type<TypeCategory::Character, KIND>>::Constant(Scalar<Result> &&str)
- : values_{std::move(str)}, length_{static_cast<ConstantSubscript>(
- values_.size())} {}
+Constant<Type<TypeCategory::Character>>::Constant(
+ int kind, Scalar<Result> &&str)
+ : kind_{kind}, values_{std::move(str)},
+ length_{static_cast<ConstantSubscript>(values_.size())} {
+ CHECK(str.kind() == kind);
+}
-template <int KIND>
-Constant<Type<TypeCategory::Character, KIND>>::Constant(ConstantSubscript len,
- std::vector<Scalar<Result>> &&strings, ConstantSubscripts &&sh)
- : ConstantBounds(std::move(sh)), length_{len} {
+Constant<Type<TypeCategory::Character>>::Constant(int kind,
+ ConstantSubscript len, std::vector<Scalar<Result>> &&strings,
+ ConstantSubscripts &&sh)
+ : ConstantBounds(std::move(sh)), kind_{kind}, length_{len} {
CHECK(TotalElementCount(shape()) &&
strings.size() == *TotalElementCount(shape()));
- values_.assign(strings.size() * length_,
- static_cast<typename Scalar<Result>::value_type>(' '));
+ values_.assign(kind, strings.size() * length_, ' ');
ConstantSubscript at{0};
for (const auto &str : strings) {
auto strLen{static_cast<ConstantSubscript>(str.size())};
@@ -239,16 +246,13 @@ Constant<Type<TypeCategory::Character, KIND>>::Constant(ConstantSubscript len,
CHECK(at == static_cast<ConstantSubscript>(values_.size()));
}
-template <int KIND>
-Constant<Type<TypeCategory::Character, KIND>>::~Constant() {}
+Constant<Type<TypeCategory::Character>>::~Constant() {}
-template <int KIND>
-bool Constant<Type<TypeCategory::Character, KIND>>::empty() const {
+bool Constant<Type<TypeCategory::Character>>::empty() const {
return size() == 0;
}
-template <int KIND>
-std::size_t Constant<Type<TypeCategory::Character, KIND>>::size() const {
+std::size_t Constant<Type<TypeCategory::Character>>::size() const {
if (length_ == 0) {
std::optional<uint64_t> n{TotalElementCount(shape())};
CHECK(n);
@@ -258,23 +262,20 @@ std::size_t Constant<Type<TypeCategory::Character, KIND>>::size() const {
}
}
-template <int KIND>
-auto Constant<Type<TypeCategory::Character, KIND>>::At(
+auto Constant<Type<TypeCategory::Character>>::At(
const ConstantSubscripts &index) const -> Scalar<Result> {
auto offset{SubscriptsToOffset(index)};
return values_.substr(offset * length_, length_);
}
-template <int KIND>
-auto Constant<Type<TypeCategory::Character, KIND>>::Substring(
- ConstantSubscript lo, ConstantSubscript hi) const
- -> std::optional<Constant> {
+auto Constant<Type<TypeCategory::Character>>::Substring(ConstantSubscript lo,
+ ConstantSubscript hi) const -> std::optional<Constant> {
std::vector<Element> elements;
ConstantSubscript n{GetSize(shape())};
ConstantSubscript newLength{0};
if (lo > hi) { // zero-length results
while (n-- > 0) {
- elements.emplace_back(); // ""
+ elements.emplace_back(Scalar<Result>::Zero(kind())); // ""
}
} else if (lo < 1 || hi > length_) {
return std::nullopt;
@@ -284,11 +285,11 @@ auto Constant<Type<TypeCategory::Character, KIND>>::Substring(
elements.emplace_back(At(at).substr(lo - 1, newLength));
}
}
- return Constant{newLength, std::move(elements), ConstantSubscripts{shape()}};
+ return Constant{
+ kind(), newLength, std::move(elements), ConstantSubscripts{shape()}};
}
-template <int KIND>
-auto Constant<Type<TypeCategory::Character, KIND>>::Reshape(
+auto Constant<Type<TypeCategory::Character>>::Reshape(
ConstantSubscripts &&dims) const -> Constant<Result> {
std::optional<uint64_t> optN{TotalElementCount(dims)};
CHECK(optN);
@@ -304,14 +305,12 @@ auto Constant<Type<TypeCategory::Character, KIND>>::Reshape(
at = 0;
}
}
- return {length_, std::move(elements), std::move(dims)};
+ return {kind(), length_, std::move(elements), std::move(dims)};
}
-template <int KIND>
-std::size_t Constant<Type<TypeCategory::Character, KIND>>::CopyFrom(
- const Constant<Type<TypeCategory::Character, KIND>> &source,
- std::size_t count, ConstantSubscripts &resultSubscripts,
- const std::vector<int> *dimOrder) {
+std::size_t Constant<Type<TypeCategory::Character>>::CopyFrom(
+ const Constant<Type<TypeCategory::Character>> &source, std::size_t count,
+ ConstantSubscripts &resultSubscripts, const std::vector<int> *dimOrder) {
CHECK(length_ == source.length_);
if (length_ == 0) {
// It's possible that the array of strings consists of all empty strings.
@@ -320,11 +319,11 @@ std::size_t Constant<Type<TypeCategory::Character, KIND>>::CopyFrom(
return count;
} else {
std::size_t copied{0};
- std::size_t elementBytes{length_ * sizeof(decltype(values_[0]))};
+ std::size_t elementBytes{static_cast<std::size_t>(length_) * kind()};
ConstantSubscripts sourceSubscripts{source.lbounds()};
while (copied < count) {
- auto *dest{&values_.at(SubscriptsToOffset(resultSubscripts) * length_)};
- const auto *src{&source.values_.at(
+ auto *dest{values_.at(SubscriptsToOffset(resultSubscripts) * length_)};
+ const auto *src{source.values_.at(
source.SubscriptsToOffset(sourceSubscripts) * length_)};
std::memcpy(dest, src, elementBytes);
copied++;
@@ -337,14 +336,14 @@ std::size_t Constant<Type<TypeCategory::Character, KIND>>::CopyFrom(
// Constant<SomeDerived> specialization
Constant<SomeDerived>::Constant(const StructureConstructor &x)
- : Base{x.values(), Result{x.derivedTypeSpec()}} {}
+ : Base{/*kind=*/0, x.values(), Result{x.derivedTypeSpec()}} {}
Constant<SomeDerived>::Constant(StructureConstructor &&x)
- : Base{std::move(x.values()), Result{x.derivedTypeSpec()}} {}
+ : Base{/*kind=*/0, std::move(x.values()), Result{x.derivedTypeSpec()}} {}
Constant<SomeDerived>::Constant(const semantics::DerivedTypeSpec &spec,
std::vector<StructureConstructorValues> &&x, ConstantSubscripts &&s)
- : Base{std::move(x), std::move(s), Result{spec}} {}
+ : Base{/*kind=*/0, std::move(x), std::move(s), Result{spec}} {}
static std::vector<StructureConstructorValues> AcquireValues(
std::vector<StructureConstructor> &&x) {
@@ -357,7 +356,8 @@ static std::vector<StructureConstructorValues> AcquireValues(
Constant<SomeDerived>::Constant(const semantics::DerivedTypeSpec &spec,
std::vector<StructureConstructor> &&x, ConstantSubscripts &&shape)
- : Base{AcquireValues(std::move(x)), std::move(shape), Result{spec}} {}
+ : Base{/*kind=*/0, AcquireValues(std::move(x)), std::move(shape),
+ Result{spec}} {}
std::optional<StructureConstructor>
Constant<SomeDerived>::GetScalarValue() const {
diff --git a/flang/lib/Evaluate/expression.cpp b/flang/lib/Evaluate/expression.cpp
index fe8565fbe42a8..d341320658e24 100644
--- a/flang/lib/Evaluate/expression.cpp
+++ b/flang/lib/Evaluate/expression.cpp
@@ -23,14 +23,13 @@ using namespace Fortran::parser::literals;
namespace Fortran::evaluate {
-template <int KIND>
std::optional<Expr<SubscriptInteger>>
-Expr<Type<TypeCategory::Character, KIND>>::LEN() const {
+Expr<Type<TypeCategory::Character>>::LEN() const {
using T = std::optional<Expr<SubscriptInteger>>;
return common::visit(
common::visitors{
[](const Constant<Result> &c) -> T {
- return AsExpr(Constant<SubscriptInteger>{c.LEN()});
+ return MakeSubscriptIntExpr(c.LEN());
},
[](const ArrayConstructor<Result> &a) -> T {
if (const auto *len{a.LEN()}) {
@@ -44,7 +43,7 @@ Expr<Type<TypeCategory::Character, KIND>>::LEN() const {
return common::visit(
[&](const auto &kx) { return kx.LEN(); }, x.left().u);
},
- [](const Concat<KIND> &c) -> T {
+ [](const Concat &c) -> T {
if (auto llen{c.left().LEN()}) {
if (auto rlen{c.right().LEN()}) {
return *std::move(llen) + *std::move(rlen);
@@ -73,7 +72,7 @@ Expr<Type<TypeCategory::Character, KIND>>::LEN() const {
},
[](const Designator<Result> &dr) { return dr.LEN(); },
[](const FunctionRef<Result> &fr) { return fr.LEN(); },
- [](const SetLength<KIND> &x) -> T { return x.right(); },
+ [](const SetLength &x) -> T { return x.right(); },
},
u);
}
@@ -92,10 +91,16 @@ const typename ExpressionBase<A>::Derived &ExpressionBase<A>::derived() const {
}
#endif
+template <typename A> int ExpressionBase<A>::kind() const {
+ // Storing/deriving the kind handled by the subclasses
+ return common::visit(
+ [&](const auto &x) -> int { return x.kind(); }, derived().u);
+}
+
template <typename A>
std::optional<DynamicType> ExpressionBase<A>::GetType() const {
if constexpr (IsLengthlessIntrinsicType<Result>) {
- return Result::GetType();
+ return DynamicType{Result::category, kind()};
} else {
return common::visit(
[&](const auto &x) -> std::optional<DynamicType> {
@@ -154,8 +159,7 @@ bool ConditionalExpr<A>::operator==(const ConditionalExpr &that) const {
elseValue_ == that.elseValue_;
}
-template <int KIND>
-bool LogicalOperation<KIND>::operator==(const LogicalOperation &that) const {
+bool LogicalOperation::operator==(const LogicalOperation &that) const {
return logicalOperator == that.logicalOperator && Base::operator==(that);
}
@@ -191,15 +195,13 @@ bool ArrayConstructorValues<R>::operator==(
return values_ == that.values_;
}
-template <int KIND>
-auto ArrayConstructor<Type<TypeCategory::Character, KIND>>::set_LEN(
+auto ArrayConstructor<Type<TypeCategory::Character>>::set_LEN(
Expr<SubscriptInteger> &&len) -> ArrayConstructor & {
length_.emplace(std::move(len));
return *this;
}
-template <int KIND>
-bool ArrayConstructor<Type<TypeCategory::Character, KIND>>::operator==(
+bool ArrayConstructor<Type<TypeCategory::Character>>::operator==(
const ArrayConstructor &that) const {
return length_ == that.length_ &&
static_cast<const Base &>(*this) == static_cast<const Base &>(that);
@@ -224,39 +226,33 @@ bool StructureConstructor::operator==(const StructureConstructor &that) const {
return result_ == that.result_ && values_ == that.values_;
}
-template <int KIND>
-bool Expr<Type<TypeCategory::Integer, KIND>>::operator==(
- const Expr<Type<TypeCategory::Integer, KIND>> &that) const {
+bool Expr<Type<TypeCategory::Integer>>::operator==(
+ const Expr<Type<TypeCategory::Integer>> &that) const {
return u == that.u;
}
-template <int KIND>
-bool Expr<Type<TypeCategory::Real, KIND>>::operator==(
- const Expr<Type<TypeCategory::Real, KIND>> &that) const {
+bool Expr<Type<TypeCategory::Real>>::operator==(
+ const Expr<Type<TypeCategory::Real>> &that) const {
return u == that.u;
}
-template <int KIND>
-bool Expr<Type<TypeCategory::Complex, KIND>>::operator==(
- const Expr<Type<TypeCategory::Complex, KIND>> &that) const {
+bool Expr<Type<TypeCategory::Complex>>::operator==(
+ const Expr<Type<TypeCategory::Complex>> &that) const {
return u == that.u;
}
-template <int KIND>
-bool Expr<Type<TypeCategory::Logical, KIND>>::operator==(
- const Expr<Type<TypeCategory::Logical, KIND>> &that) const {
+bool Expr<Type<TypeCategory::Logical>>::operator==(
+ const Expr<Type<TypeCategory::Logical>> &that) const {
return u == that.u;
}
-template <int KIND>
-bool Expr<Type<TypeCategory::Character, KIND>>::operator==(
- const Expr<Type<TypeCategory::Character, KIND>> &that) const {
+bool Expr<Type<TypeCategory::Character>>::operator==(
+ const Expr<Type<TypeCategory::Character>> &that) const {
return u == that.u;
}
-template <int KIND>
-bool Expr<Type<TypeCategory::Unsigned, KIND>>::operator==(
- const Expr<Type<TypeCategory::Unsigned, KIND>> &that) const {
+bool Expr<Type<TypeCategory::Unsigned>>::operator==(
+ const Expr<Type<TypeCategory::Unsigned>> &that) const {
return u == that.u;
}
@@ -366,20 +362,51 @@ void GenericAssignmentWrapper::Deleter(GenericAssignmentWrapper *p) {
delete p;
}
-template <TypeCategory CAT> int Expr<SomeKind<CAT>>::GetKind() const {
- return common::visit(
- [](const auto &kx) { return std::decay_t<decltype(kx)>::Result::kind; },
- u);
+std::optional<Expr<SubscriptInteger>> Expr<SomeCharacter>::LEN() const {
+ return common::visit([](const auto &kx) { return kx.LEN(); }, u);
}
-int Expr<SomeCharacter>::GetKind() const {
- return common::visit(
- [](const auto &kx) { return std::decay_t<decltype(kx)>::Result::kind; },
- u);
+Parentheses<SomeDerived>::Parentheses(const Expr<SomeDerived> &x)
+ : Base{x.kind(), x} {}
+Parentheses<SomeDerived>::Parentheses(Expr<SomeDerived> &&x)
+ : Base{x.kind(), std::move(x)} {}
+
+ComplexComponent::ComplexComponent(bool isImaginary, const Expr<Operand> &x)
+ : Base{x.kind(), x}, isImaginaryPart{isImaginary} {}
+ComplexComponent::ComplexComponent(bool isImaginary, Expr<Operand> &&x)
+ : Base{x.kind(), std::move(x)}, isImaginaryPart{isImaginary} {}
+
+ComplexConstructor::ComplexConstructor(const Expr<Type<TypeCategory::Real>> &re,
+ const Expr<Type<TypeCategory::Real>> &im)
+ : Base{re.kind(), re, im} {
+ CHECK(re.kind() == im.kind());
+}
+ComplexConstructor::ComplexConstructor(
+ Expr<Type<TypeCategory::Real>> &&re, Expr<Type<TypeCategory::Real>> &&im)
+ : Base{re.kind(), std::move(re), std::move(im)} {
+ CHECK(left().kind() == right().kind());
}
-std::optional<Expr<SubscriptInteger>> Expr<SomeCharacter>::LEN() const {
- return common::visit([](const auto &kx) { return kx.LEN(); }, u);
+LogicalOperation::LogicalOperation(
+ LogicalOperator opr, const Expr<Operand> &x, const Expr<Operand> &y)
+ : Base{x.kind(), x, y}, logicalOperator{opr} {
+ CHECK(x.kind() == y.kind());
+}
+LogicalOperation::LogicalOperation(
+ LogicalOperator opr, Expr<Operand> &&x, Expr<Operand> &&y)
+ : Base{x.kind(), std::move(x), std::move(y)}, logicalOperator{opr} {
+ CHECK(x.kind() == y.kind());
+}
+
+Concat::Concat(const Expr<Type<TypeCategory::Character>> &x,
+ const Expr<Type<TypeCategory::Character>> &y)
+ : Base{x.kind(), x, y} {
+ CHECK(x.kind() == y.kind());
+}
+Concat::Concat(Expr<Type<TypeCategory::Character>> &&x,
+ Expr<Type<TypeCategory::Character>> &&y)
+ : Base{x.kind(), std::move(x), std::move(y)} {
+ CHECK(left().kind() == right().kind());
}
#ifdef _MSC_VER // disable bogus warning about missing definitions
diff --git a/flang/lib/Evaluate/fold-character.cpp b/flang/lib/Evaluate/fold-character.cpp
index a43742ae8dc68..0a23c91df0654 100644
--- a/flang/lib/Evaluate/fold-character.cpp
+++ b/flang/lib/Evaluate/fold-character.cpp
@@ -36,65 +36,66 @@ template <typename T>
static std::optional<Scalar<T>> Identity(
Scalar<T> str, std::optional<ConstantSubscript> len) {
if (len) {
- return CharacterUtils<T::kind>::REPEAT(
- str, std::max<ConstantSubscript>(*len, 0));
+ return CharacterUtils::REPEAT(str, std::max<ConstantSubscript>(*len, 0));
} else {
return std::nullopt;
}
}
-template <int KIND>
-Expr<Type<TypeCategory::Character, KIND>> FoldIntrinsicFunction(
+Expr<Type<TypeCategory::Character>> FoldIntrinsicFunction(
FoldingContext &context,
- FunctionRef<Type<TypeCategory::Character, KIND>> &&funcRef) {
- using T = Type<TypeCategory::Character, KIND>;
- using StringType = Scalar<T>; // std::string or larger
- using SingleCharType = typename StringType::value_type; // char &c.
+ FunctionRef<Type<TypeCategory::Character>> &&funcRef) {
+ using T = Type<TypeCategory::Character>;
+ using StringType = Scalar<T>; // CharacterValue
+ const int kind{funcRef.kind()};
auto *intrinsic{std::get_if<SpecificIntrinsic>(&funcRef.proc().u)};
CHECK(intrinsic);
std::string name{intrinsic->name};
if (name == "achar" || name == "char") {
using IntT = SubscriptInteger;
- return FoldElementalIntrinsic<T, IntT>(context, std::move(funcRef),
+ return FoldElementalIntrinsic<T, IntT>(kind, {SubscriptIntegerKind},
+ context, std::move(funcRef),
ScalarFunc<T, IntT>([&](const Scalar<IntT> &i) {
- if (i.IsNegative() || i.BGE(Scalar<IntT>{0}.IBSET(8 * KIND))) {
+ if (i.IsNegative() ||
+ i.BGE(Scalar<IntT>{SubscriptIntegerKind, 0}.IBSET(8 * kind))) {
context.Warn(common::UsageWarning::FoldingValueChecks,
"%s(I=%jd) is out of range for CHARACTER(KIND=%d)"_warn_en_US,
parser::ToUpperCaseLetters(name),
- static_cast<std::intmax_t>(i.ToInt64()), KIND);
+ static_cast<std::intmax_t>(i.ToInt64()), kind);
}
- return CharacterUtils<KIND>::CHAR(i.ToUInt64());
+ return CharacterUtils::CHAR(kind, i.ToUInt64());
}));
} else if (name == "adjustl") {
return FoldElementalIntrinsic<T, T>(
- context, std::move(funcRef), CharacterUtils<KIND>::ADJUSTL);
+ kind, {kind}, context, std::move(funcRef), CharacterUtils::ADJUSTL);
} else if (name == "adjustr") {
return FoldElementalIntrinsic<T, T>(
- context, std::move(funcRef), CharacterUtils<KIND>::ADJUSTR);
+ kind, {kind}, context, std::move(funcRef), CharacterUtils::ADJUSTR);
} else if (name == "max") {
return FoldMINorMAX(context, std::move(funcRef), Ordering::Greater);
} else if (name == "maxval") {
- SingleCharType least{0};
- if (auto identity{Identity<T>(
- StringType{least}, GetConstantLength(context, funcRef, 0))}) {
+ StringType least{kind, 1, '\0'};
+ if (auto identity{
+ Identity<T>(least, GetConstantLength(context, funcRef, 0))}) {
return FoldMaxvalMinval<T>(
- context, std::move(funcRef), RelationalOperator::GT, *identity);
+ kind, context, std::move(funcRef), RelationalOperator::GT, *identity);
}
} else if (name == "min") {
return FoldMINorMAX(context, std::move(funcRef), Ordering::Less);
} else if (name == "minval") {
// Collating sequences correspond to positive integers (3.31)
- auto most{static_cast<SingleCharType>(0xffffffff >> (8 * (4 - KIND)))};
- if (auto identity{Identity<T>(
- StringType{most}, GetConstantLength(context, funcRef, 0))}) {
+ StringType most{kind, 1, 0xffffffff >> (8 * (4 - kind))};
+ if (auto identity{
+ Identity<T>(most, GetConstantLength(context, funcRef, 0))}) {
return FoldMaxvalMinval<T>(
- context, std::move(funcRef), RelationalOperator::LT, *identity);
+ kind, context, std::move(funcRef), RelationalOperator::LT, *identity);
}
} else if (name == "new_line") {
- return Expr<T>{Constant<T>{CharacterUtils<KIND>::NEW_LINE()}};
+ return MakeConstantExpr<T>(kind, CharacterUtils::NEW_LINE(kind));
} else if (name == "repeat") { // not elemental
if (auto scalars{GetScalarConstantArguments<T, SubscriptInteger>(
- context, funcRef.arguments(), /*hasOptionalArgument=*/false)}) {
+ {kind, SubscriptIntegerKind}, context, funcRef.arguments(),
+ /*hasOptionalArgument=*/false)}) {
auto str{std::get<Scalar<T>>(*scalars)};
auto n{std::get<Scalar<SubscriptInteger>>(*scalars).ToInt64()};
if (n < 0) {
@@ -107,45 +108,45 @@ Expr<Type<TypeCategory::Character, KIND>> FoldIntrinsicFunction(
"Result of REPEAT() is too large to compute at compilation time (%g characters)"_port_en_US,
static_cast<double>(n) * str.size());
} else {
- return Expr<T>{Constant<T>{CharacterUtils<KIND>::REPEAT(str, n)}};
+ return MakeConstantExpr<T>(kind, CharacterUtils::REPEAT(str, n));
}
}
} else if (name == "trim") { // not elemental
- if (auto scalar{GetScalarConstantArguments<T>(
- context, funcRef.arguments(), /*hasOptionalArgument=*/false)}) {
- return Expr<T>{Constant<T>{
- CharacterUtils<KIND>::TRIM(std::get<Scalar<T>>(*scalar))}};
+ if (auto scalar{GetScalarConstantArguments<T>({kind}, context,
+ funcRef.arguments(), /*hasOptionalArgument=*/false)}) {
+ return MakeConstantExpr<T>(
+ kind, CharacterUtils::TRIM(std::get<Scalar<T>>(*scalar)));
}
} else if (name == "__builtin_compiler_options") {
auto &o = context.targetCharacteristics().compilerOptionsString();
- return Expr<T>{Constant<T>{StringType(o.begin(), o.end())}};
+ return MakeConstantExpr<T>(kind, o);
} else if (name == "__builtin_compiler_version") {
auto &v = context.targetCharacteristics().compilerVersionString();
- return Expr<T>{Constant<T>{StringType(v.begin(), v.end())}};
+ return MakeConstantExpr<T>(kind, v);
}
return Expr<T>{std::move(funcRef)};
}
-template <int KIND>
-Expr<Type<TypeCategory::Character, KIND>> FoldOperation(
- FoldingContext &context, Concat<KIND> &&x) {
+Expr<Type<TypeCategory::Character>> FoldOperation(
+ FoldingContext &context, Concat &&x) {
+ const int kind{x.kind()};
if (auto array{ApplyElementwise(context, x)}) {
return *array;
}
- using Result = Type<TypeCategory::Character, KIND>;
+ using Result = Type<TypeCategory::Character>;
if (auto folded{OperandsAreConstants(x)}) {
- return Expr<Result>{Constant<Result>{folded->first + folded->second}};
+ return MakeConstantExpr<Result>(kind, folded->first + folded->second);
}
return Expr<Result>{std::move(x)};
}
-template <int KIND>
-Expr<Type<TypeCategory::Character, KIND>> FoldOperation(
- FoldingContext &context, SetLength<KIND> &&x) {
+Expr<Type<TypeCategory::Character>> FoldOperation(
+ FoldingContext &context, SetLength &&x) {
+ const int kind{x.kind()};
if (auto array{ApplyElementwise(context, x)}) {
return *array;
}
- using Result = Type<TypeCategory::Character, KIND>;
+ using Result = Type<TypeCategory::Character>;
if (auto folded{OperandsAreConstants(x)}) {
auto oldLength{static_cast<ConstantSubscript>(folded->first.size())};
auto newLength{folded->second.ToInt64()};
@@ -155,7 +156,7 @@ Expr<Type<TypeCategory::Character, KIND>> FoldOperation(
folded->first.append(newLength - oldLength, ' ');
}
CHECK(static_cast<ConstantSubscript>(folded->first.size()) == newLength);
- return Expr<Result>{Constant<Result>{std::move(folded->first)}};
+ return MakeConstantExpr<Result>(kind, std::move(folded->first));
}
return Expr<Result>{std::move(x)};
}
diff --git a/flang/lib/Evaluate/fold-complex.cpp b/flang/lib/Evaluate/fold-complex.cpp
index 84066ee5be71b..fda86906d7fb4 100644
--- a/flang/lib/Evaluate/fold-complex.cpp
+++ b/flang/lib/Evaluate/fold-complex.cpp
@@ -12,11 +12,10 @@
namespace Fortran::evaluate {
-template <int KIND>
-Expr<Type<TypeCategory::Complex, KIND>> FoldIntrinsicFunction(
- FoldingContext &context,
- FunctionRef<Type<TypeCategory::Complex, KIND>> &&funcRef) {
- using T = Type<TypeCategory::Complex, KIND>;
+Expr<Type<TypeCategory::Complex>> FoldIntrinsicFunction(FoldingContext &context,
+ FunctionRef<Type<TypeCategory::Complex>> &&funcRef) {
+ const int kind{funcRef.kind()};
+ using T = Type<TypeCategory::Complex>;
using Part = typename T::Part;
ActualArguments &args{funcRef.arguments()};
auto *intrinsic{std::get_if<SpecificIntrinsic>(&funcRef.proc().u)};
@@ -26,22 +25,22 @@ Expr<Type<TypeCategory::Complex, KIND>> FoldIntrinsicFunction(
name == "atan" || name == "atanh" || name == "cos" || name == "cosh" ||
name == "exp" || name == "log" || name == "sin" || name == "sinh" ||
name == "sqrt" || name == "tan" || name == "tanh") {
- if (auto callable{GetHostRuntimeWrapper<T, T>(name)}) {
+ if (auto callable{GetHostRuntimeWrapper<T, T>(kind, {kind}, name)}) {
return FoldElementalIntrinsic<T, T>(
- context, std::move(funcRef), *callable);
+ kind, {kind}, context, std::move(funcRef), *callable);
} else {
context.Warn(common::UsageWarning::FoldingFailure,
"%s(complex(kind=%d)) cannot be folded on host"_warn_en_US, name,
- KIND);
+ kind);
}
} else if (name == "conjg") {
return FoldElementalIntrinsic<T, T>(
- context, std::move(funcRef), &Scalar<T>::CONJG);
+ kind, {kind}, context, std::move(funcRef), &Scalar<T>::CONJG);
} else if (name == "cmplx") {
if (args.size() > 0 && args[0].has_value()) {
if (auto *x{UnwrapExpr<Expr<SomeComplex>>(args[0])}) {
// CMPLX(X [, KIND]) with complex X
- return Fold(context, ConvertToType<T>(std::move(*x)));
+ return Fold(context, ConvertToType<T>(kind, std::move(*x)));
} else {
if (args.size() >= 2 && args[1].has_value()) {
// Do not fold CMPLX with an Y argument that may be absent at runtime
@@ -56,11 +55,11 @@ Expr<Type<TypeCategory::Complex, KIND>> FoldIntrinsicFunction(
Expr<SomeType> re{std::move(*args[0].value().UnwrapExpr())};
Expr<SomeType> im{args.size() >= 2 && args[1].has_value()
? std::move(*args[1]->UnwrapExpr())
- : AsGenericExpr(Constant<Part>{Scalar<Part>{}})};
+ : AsGenericExpr(MakeZeroExpr<Part>(kind))};
return Fold(context,
Expr<T>{
- ComplexConstructor<KIND>{ToReal<KIND>(context, std::move(re)),
- ToReal<KIND>(context, std::move(im))}});
+ ComplexConstructor{kind, ToReal(kind, context, std::move(re)),
+ ToReal(kind, context, std::move(im))}});
}
}
} else if (name == "dot_product") {
@@ -68,7 +67,7 @@ Expr<Type<TypeCategory::Complex, KIND>> FoldIntrinsicFunction(
} else if (name == "matmul") {
return FoldMatmul(context, std::move(funcRef));
} else if (name == "product") {
- auto one{Scalar<Part>::FromInteger(value::Integer<8>{1}).value};
+ auto one{Scalar<Part>::FromInteger(kind, value::IntegerValue{1, 1}).value};
return FoldProduct<T>(context, std::move(funcRef), Scalar<T>{one});
} else if (name == "sum") {
return FoldSum<T>(context, std::move(funcRef));
@@ -76,17 +75,17 @@ Expr<Type<TypeCategory::Complex, KIND>> FoldIntrinsicFunction(
return Expr<T>{std::move(funcRef)};
}
-template <int KIND>
-Expr<Type<TypeCategory::Complex, KIND>> FoldOperation(
- FoldingContext &context, ComplexConstructor<KIND> &&x) {
+Expr<Type<TypeCategory::Complex>> FoldOperation(
+ FoldingContext &context, ComplexConstructor &&x) {
+ const int kind{x.kind()};
if (auto array{ApplyElementwise(context, x)}) {
return *array;
}
- using ComplexType = Type<TypeCategory::Complex, KIND>;
+ using ComplexType = Type<TypeCategory::Complex>;
if (auto folded{OperandsAreConstants(x)}) {
using RealType = typename ComplexType::Part;
Constant<ComplexType> result{
- Scalar<ComplexType>{folded->first, folded->second}};
+ kind, Scalar<ComplexType>{folded->first, folded->second}};
if (const auto *re{UnwrapConstantValue<RealType>(x.left())};
re && re->result().isFromInexactLiteralConversion()) {
result.result().set_isFromInexactLiteralConversion();
diff --git a/flang/lib/Evaluate/fold-designator.cpp b/flang/lib/Evaluate/fold-designator.cpp
index d7751ec389917..4e0cc1b841194 100644
--- a/flang/lib/Evaluate/fold-designator.cpp
+++ b/flang/lib/Evaluate/fold-designator.cpp
@@ -98,9 +98,11 @@ std::optional<OffsetSymbol> DesignatorFolder::FoldDesignator(
},
[&](const Triplet &triplet) {
auto start{ToInt64(Fold(context_,
- triplet.lower().value_or(ExtentExpr{lower})))};
+ triplet.lower().value_or(
+ MakeExtentExpr(lower))))};
auto end{ToInt64(Fold(context_,
- triplet.upper().value_or(ExtentExpr{upper})))};
+ triplet.upper().value_or(
+ MakeExtentExpr(upper))))};
auto step{ToInt64(Fold(context_, triplet.stride()))};
if (start && end && step) {
if (*step != 0) {
@@ -237,11 +239,11 @@ static std::optional<ArrayRef> OffsetToArrayRef(FoldingContext &context,
}
auto quotient{at / extent};
auto remainder{at - quotient * extent};
- subscripts.emplace_back(ExtentExpr{(*lower)[dim] + remainder});
+ subscripts.emplace_back(MakeExtentExpr((*lower)[dim] + remainder));
at = quotient;
}
// This final subscript might be out of range for use in error reporting.
- subscripts.emplace_back(ExtentExpr{(*lower)[rank - 1] + at});
+ subscripts.emplace_back(MakeExtentExpr((*lower)[rank - 1] + at));
offset -= element * static_cast<std::size_t>(*elementBytes);
return ArrayRef{std::move(entity), std::move(subscripts)};
}
@@ -338,8 +340,10 @@ std::optional<Expr<SomeType>> OffsetToDesignator(FoldingContext &context,
return common::visit(
[&](const auto &z) -> std::optional<Expr<SomeType>> {
using PartType = typename ResultType<decltype(z)>::Part;
- return AsGenericExpr(Designator<PartType>{ComplexPart{
- ExtractDataRef(std::move(*zExpr)).value(), part}});
+ const int kind{z.kind()};
+ return AsGenericExpr(Designator<PartType>{kind,
+ ComplexPart{
+ ExtractDataRef(std::move(*zExpr)).value(), part}});
},
zExpr->u);
}
@@ -350,12 +354,12 @@ std::optional<Expr<SomeType>> OffsetToDesignator(FoldingContext &context,
return common::visit(
[&](const auto &x) -> std::optional<Expr<SomeType>> {
using T = typename std::decay_t<decltype(x)>::Result;
- return AsGenericExpr(Designator<T>{
+ const int kind{x.kind()};
+ return AsGenericExpr(Designator<T>{kind,
Substring{ExtractDataRef(std::move(*cExpr)).value(),
- std::optional<Expr<SubscriptInteger>>{
- 1 + (offset / T::kind)},
- std::optional<Expr<SubscriptInteger>>{
- 1 + ((offset + size - 1) / T::kind)}}});
+ MakeSubscriptIntExpr(1 + (offset / kind)),
+ MakeSubscriptIntExpr(
+ 1 + ((offset + size - 1) / kind))}});
},
cExpr->u);
}
diff --git a/flang/lib/Evaluate/fold-implementation.h b/flang/lib/Evaluate/fold-implementation.h
index 467bc6f0f7005..f694a826a2496 100644
--- a/flang/lib/Evaluate/fold-implementation.h
+++ b/flang/lib/Evaluate/fold-implementation.h
@@ -31,6 +31,7 @@
#include "flang/Semantics/symbol.h"
#include "flang/Semantics/tools.h"
#include <algorithm>
+#include <array>
#include <cmath>
#include <cstdio>
#include <optional>
@@ -52,8 +53,13 @@ static constexpr bool useKahanSummation{false};
// Utilities
template <typename T> class Folder {
public:
+ explicit Folder(int kind, FoldingContext &c, bool forOptionalArgument = false)
+ : kind_{kind}, context_{c}, forOptionalArgument_{forOptionalArgument} {}
+ template <typename U = T,
+ typename = std::enable_if_t<std::is_same_v<U, SomeDerived>>>
explicit Folder(FoldingContext &c, bool forOptionalArgument = false)
- : context_{c}, forOptionalArgument_{forOptionalArgument} {}
+ : Folder(0, c, forOptionalArgument) {}
+
std::optional<Constant<T>> GetNamedConstant(const Symbol &);
std::optional<Constant<T>> ApplySubscripts(const Constant<T> &array,
const std::vector<Constant<SubscriptInteger>> &subscripts);
@@ -79,6 +85,7 @@ template <typename T> class Folder {
Expr<T> TRANSFER(FunctionRef<T> &&);
private:
+ int kind_;
FoldingContext &context_;
bool forOptionalArgument_{false};
};
@@ -86,16 +93,18 @@ template <typename T> class Folder {
std::optional<Constant<SubscriptInteger>> GetConstantSubscript(
FoldingContext &, Subscript &, const NamedEntity &, int dim);
-// Helper to use host runtime on scalars for folding.
-template <typename TR, typename... TA>
-std::optional<std::function<Scalar<TR>(FoldingContext &, Scalar<TA>...)>>
-GetHostRuntimeWrapper(const std::string &name) {
- std::vector<DynamicType> argTypes{TA{}.GetType()...};
- if (auto hostWrapper{GetHostRuntimeWrapper(name, TR{}.GetType(), argTypes)}) {
- return [hostWrapper](
+template <typename TR, typename... TA, std::size_t... I>
+static std::optional<std::function<Scalar<TR>(FoldingContext &, Scalar<TA>...)>>
+GetHostRuntimeWrapperHelper(int resultKind,
+ std::array<int, sizeof...(TA)> argKinds, const std::string &name,
+ std::index_sequence<I...>) {
+ std::vector<DynamicType> argTypes{DynamicType{TA::category, argKinds[I]}...};
+ if (auto hostWrapper{GetHostRuntimeWrapper(
+ name, DynamicType{TR::category, resultKind}, argTypes)}) {
+ return [hostWrapper, argKinds](
FoldingContext &context, Scalar<TA>... args) -> Scalar<TR> {
std::vector<Expr<SomeType>> genericArgs{
- AsGenericExpr(Constant<TA>{args})...};
+ AsGenericExpr(Constant<TA>{argKinds[I], args})...};
return GetScalarConstantValue<TR>(
(*hostWrapper)(context, std::move(genericArgs)))
.value();
@@ -104,6 +113,15 @@ GetHostRuntimeWrapper(const std::string &name) {
return std::nullopt;
}
+// Helper to use host runtime on scalars for folding.
+template <typename TR, typename... TA>
+static std::optional<std::function<Scalar<TR>(FoldingContext &, Scalar<TA>...)>>
+GetHostRuntimeWrapper(int resultKind, std::array<int, sizeof...(TA)> argKinds,
+ const std::string &name) {
+ return GetHostRuntimeWrapperHelper<TR, TA...>(
+ resultKind, argKinds, name, std::index_sequence_for<TA...>{});
+}
+
// FoldOperation() rewrites expression tree nodes.
// If there is any possibility that the rewritten node will
// not have the same representation type, the result of
@@ -119,6 +137,11 @@ common::IfNoLvalue<Expr<ResultType<A>>, A> FoldOperation(
return Expr<ResultType<A>>{std::move(x)};
}
+// Forward declarations needed to ensure overload lookup considers all possible
+// implementations.
+ComplexPart FoldOperation(FoldingContext &, ComplexPart &&);
+Expr<Type<TypeCategory::Complex>> FoldOperation(
+ FoldingContext &, ComplexConstructor &&);
Component FoldOperation(FoldingContext &, Component &&);
NamedEntity FoldOperation(FoldingContext &, NamedEntity &&);
Triplet FoldOperation(FoldingContext &, Triplet &&);
@@ -127,12 +150,12 @@ ArrayRef FoldOperation(FoldingContext &, ArrayRef &&);
CoarrayRef FoldOperation(FoldingContext &, CoarrayRef &&);
DataRef FoldOperation(FoldingContext &, DataRef &&);
Substring FoldOperation(FoldingContext &, Substring &&);
-ComplexPart FoldOperation(FoldingContext &, ComplexPart &&);
template <typename T>
Expr<T> FoldOperation(FoldingContext &, FunctionRef<T> &&);
template <typename T>
Expr<T> FoldOperation(FoldingContext &context, Designator<T> &&designator) {
- return Folder<T>{context}.Folding(std::move(designator));
+ const int kind{designator.kind()};
+ return Folder<T>{kind, context}.Folding(std::move(designator));
}
Expr<TypeParamInquiry::Result> FoldOperation(
FoldingContext &, TypeParamInquiry &&);
@@ -203,6 +226,7 @@ std::optional<Constant<T>> Folder<T>::Folding(DataRef &ref) {
template <typename T>
std::optional<Constant<T>> Folder<T>::ApplySubscripts(const Constant<T> &array,
const std::vector<Constant<SubscriptInteger>> &subscripts) {
+ const int kind{array.kind()};
const auto &shape{array.shape()};
const auto &lbounds{array.lbounds()};
int rank{GetRank(shape)};
@@ -252,12 +276,14 @@ std::optional<Constant<T>> Folder<T>::ApplySubscripts(const Constant<T> &array,
CHECK(k == GetRank(resultShape));
}
if constexpr (T::category == TypeCategory::Character) {
- return Constant<T>{array.LEN(), std::move(values), std::move(resultShape)};
+ return Constant<T>{
+ kind, array.LEN(), std::move(values), std::move(resultShape)};
} else if constexpr (std::is_same_v<T, SomeDerived>) {
+ CHECK(kind == 0);
return Constant<T>{array.result().derivedTypeSpec(), std::move(values),
std::move(resultShape)};
} else {
- return Constant<T>{std::move(values), std::move(resultShape)};
+ return Constant<T>{kind, std::move(values), std::move(resultShape)};
}
}
@@ -291,9 +317,9 @@ std::optional<Constant<T>> Folder<T>::ApplyComponent(
// information is propagated to the array constructor.
auto *typedExpr{UnwrapExpr<Expr<T>>(expr.value())};
CHECK(typedExpr);
- array = std::make_unique<ArrayConstructor<T>>(*typedExpr);
+ array = std::make_unique<ArrayConstructor<T>>(kind_, *typedExpr);
if constexpr (T::category == TypeCategory::Character) {
- array->set_LEN(Expr<SubscriptInteger>{value->LEN()});
+ array->set_LEN(MakeSubscriptIntExpr(value->LEN()));
}
}
if (subscripts) {
@@ -349,6 +375,7 @@ std::optional<Constant<T>> Folder<T>::GetConstantComponent(Component &component,
}
template <typename T> Expr<T> Folder<T>::Folding(Designator<T> &&designator) {
+ const int kind{designator.kind()};
if constexpr (T::category == TypeCategory::Character) {
if (auto *substring{common::Unwrap<Substring>(designator.u)}) {
if (std::optional<Expr<SomeCharacter>> folded{
@@ -364,14 +391,14 @@ template <typename T> Expr<T> Folder<T>::Folding(Designator<T> &&designator) {
} else if constexpr (T::category == TypeCategory::Real) {
if (auto *zPart{std::get_if<ComplexPart>(&designator.u)}) {
*zPart = FoldOperation(context_, std::move(*zPart));
- using ComplexT = Type<TypeCategory::Complex, T::kind>;
- if (auto zConst{Folder<ComplexT>{context_}.Folding(zPart->complex())}) {
+ using ComplexT = Type<TypeCategory::Complex>;
+ if (auto zConst{
+ Folder<ComplexT>{kind, context_}.Folding(zPart->complex())}) {
return Fold(context_,
- Expr<T>{ComplexComponent<T::kind>{
- zPart->part() == ComplexPart::Part::IM,
+ Expr<T>{ComplexComponent{zPart->part() == ComplexPart::Part::IM,
Expr<ComplexT>{std::move(*zConst)}}});
} else {
- return Expr<T>{Designator<T>{std::move(*zPart)}};
+ return Expr<T>{Designator<T>{kind, std::move(*zPart)}};
}
}
}
@@ -383,25 +410,25 @@ template <typename T> Expr<T> Folder<T>::Folding(Designator<T> &&designator) {
}
return Expr<T>{std::move(designator)};
},
- [&](ArrayRef &&aRef) {
+ [&, kind](ArrayRef &&aRef) {
aRef = FoldOperation(context_, std::move(aRef));
if (auto c{Folding(aRef)}) {
return Expr<T>{std::move(*c)};
} else {
- return Expr<T>{Designator<T>{std::move(aRef)}};
+ return Expr<T>{Designator<T>{kind, std::move(aRef)}};
}
},
- [&](Component &&component) {
+ [&, kind](Component &&component) {
component = FoldOperation(context_, std::move(component));
if (auto c{GetConstantComponent(component)}) {
return Expr<T>{std::move(*c)};
} else {
- return Expr<T>{Designator<T>{std::move(component)}};
+ return Expr<T>{Designator<T>{kind, std::move(component)}};
}
},
- [&](auto &&x) {
+ [&, kind](auto &&x) {
return Expr<T>{
- Designator<T>{FoldOperation(context_, std::move(x))}};
+ Designator<T>{kind, FoldOperation(context_, std::move(x))}};
},
},
std::move(designator.u));
@@ -414,15 +441,15 @@ Constant<T> *Folder<T>::Folding(std::optional<ActualArgument> &arg) {
if (auto *expr{UnwrapExpr<Expr<SomeType>>(arg)}) {
*expr = Fold(context_, std::move(*expr));
if constexpr (T::category != TypeCategory::Derived) {
- if (!UnwrapExpr<Expr<T>>(*expr)) {
+ if (!UnwrapExpr<Expr<T>>(kind_, *expr)) {
if (const Symbol *
var{forOptionalArgument_
? UnwrapWholeSymbolOrComponentDataRef(*expr)
: nullptr};
var && (IsOptional(*var) || IsAllocatableOrObjectPointer(var))) {
// can't safely convert item that may not be present
- } else if (auto converted{
- ConvertToType(T::GetType(), std::move(*expr))}) {
+ } else if (auto converted{ConvertToType(
+ DynamicType{T::category, kind_}, std::move(*expr))}) {
*expr = Fold(context_, std::move(*converted));
}
}
@@ -434,11 +461,13 @@ Constant<T> *Folder<T>::Folding(std::optional<ActualArgument> &arg) {
template <typename... A, std::size_t... I>
std::optional<std::tuple<const Constant<A> *...>> GetConstantArgumentsHelper(
- FoldingContext &context, ActualArguments &arguments,
- bool hasOptionalArgument, std::index_sequence<I...>) {
+ const std::array<int, sizeof...(A)> &kinds, FoldingContext &context,
+ ActualArguments &arguments, bool hasOptionalArgument,
+ std::index_sequence<I...>) {
static_assert(sizeof...(A) > 0);
std::tuple<const Constant<A> *...> args{
- Folder<A>{context, hasOptionalArgument}.Folding(arguments.at(I))...};
+ Folder<A>{kinds[I], context, hasOptionalArgument}.Folding(
+ arguments.at(I))...};
if ((... && (std::get<I>(args)))) {
return args;
} else {
@@ -448,17 +477,19 @@ std::optional<std::tuple<const Constant<A> *...>> GetConstantArgumentsHelper(
template <typename... A>
std::optional<std::tuple<const Constant<A> *...>> GetConstantArguments(
- FoldingContext &context, ActualArguments &args, bool hasOptionalArgument) {
- return GetConstantArgumentsHelper<A...>(
- context, args, hasOptionalArgument, std::index_sequence_for<A...>{});
+ const std::array<int, sizeof...(A)> &kinds, FoldingContext &context,
+ ActualArguments &args, bool hasOptionalArgument) {
+ return GetConstantArgumentsHelper<A...>(kinds, context, args,
+ hasOptionalArgument, std::index_sequence_for<A...>{});
}
template <typename... A, std::size_t... I>
std::optional<std::tuple<Scalar<A>...>> GetScalarConstantArgumentsHelper(
- FoldingContext &context, ActualArguments &args, bool hasOptionalArgument,
+ const std::array<int, sizeof...(A)> &kinds, FoldingContext &context,
+ ActualArguments &args, bool hasOptionalArgument,
std::index_sequence<I...>) {
- if (auto constArgs{
- GetConstantArguments<A...>(context, args, hasOptionalArgument)}) {
+ if (auto constArgs{GetConstantArguments<A...>(
+ kinds, context, args, hasOptionalArgument)}) {
return std::tuple<Scalar<A>...>{
std::get<I>(*constArgs)->GetScalarValue().value()...};
} else {
@@ -468,9 +499,10 @@ std::optional<std::tuple<Scalar<A>...>> GetScalarConstantArgumentsHelper(
template <typename... A>
std::optional<std::tuple<Scalar<A>...>> GetScalarConstantArguments(
- FoldingContext &context, ActualArguments &args, bool hasOptionalArgument) {
- return GetScalarConstantArgumentsHelper<A...>(
- context, args, hasOptionalArgument, std::index_sequence_for<A...>{});
+ const std::array<int, sizeof...(A)> &kinds, FoldingContext &context,
+ ActualArguments &args, bool hasOptionalArgument) {
+ return GetScalarConstantArgumentsHelper<A...>(kinds, context, args,
+ hasOptionalArgument, std::index_sequence_for<A...>{});
}
// helpers to fold intrinsic function references
@@ -485,12 +517,15 @@ using ScalarFuncWithContext =
template <template <typename, typename...> typename WrapperType, typename TR,
typename... TA, std::size_t... I>
-Expr<TR> FoldElementalIntrinsicHelper(FoldingContext &context,
+Expr<TR> FoldElementalIntrinsicHelper(int resultKind,
+ const std::array<int, sizeof...(TA)> &argKinds, FoldingContext &context,
FunctionRef<TR> &&funcRef, WrapperType<TR, TA...> func,
bool hasOptionalArgument, std::index_sequence<I...>) {
+ CHECK(funcRef.kind() == resultKind);
+ std::array<int, sizeof...(TA)> kinds{argKinds[I]...};
if (std::optional<std::tuple<const Constant<TA> *...>> args{
GetConstantArguments<TA...>(
- context, funcRef.arguments(), hasOptionalArgument)}) {
+ kinds, context, funcRef.arguments(), hasOptionalArgument)}) {
// Compute the shape of the result based on shapes of arguments
ConstantSubscripts shape;
int rank{0};
@@ -543,7 +578,8 @@ Expr<TR> FoldElementalIntrinsicHelper(FoldingContext &context,
if constexpr (TR::category == TypeCategory::Character) {
auto len{static_cast<ConstantSubscript>(
results.empty() ? 0 : results[0].length())};
- return Expr<TR>{Constant<TR>{len, std::move(results), std::move(shape)}};
+ return Expr<TR>{
+ Constant<TR>{resultKind, len, std::move(results), std::move(shape)}};
} else if constexpr (TR::category == TypeCategory::Derived) {
if (!results.empty()) {
return Expr<TR>{rank == 0
@@ -552,27 +588,30 @@ Expr<TR> FoldElementalIntrinsicHelper(FoldingContext &context,
std::move(results), std::move(shape)}};
}
} else {
- return Expr<TR>{Constant<TR>{std::move(results), std::move(shape)}};
+ return Expr<TR>{
+ Constant<TR>{resultKind, std::move(results), std::move(shape)}};
}
}
return Expr<TR>{std::move(funcRef)};
}
template <typename TR, typename... TA>
-Expr<TR> FoldElementalIntrinsic(FoldingContext &context,
+Expr<TR> FoldElementalIntrinsic(int resultKind,
+ const std::array<int, sizeof...(TA)> &argKinds, FoldingContext &context,
FunctionRef<TR> &&funcRef, ScalarFunc<TR, TA...> func,
bool hasOptionalArgument = false) {
- return FoldElementalIntrinsicHelper<ScalarFunc, TR, TA...>(context,
- std::move(funcRef), func, hasOptionalArgument,
+ return FoldElementalIntrinsicHelper<ScalarFunc, TR, TA...>(resultKind,
+ argKinds, context, std::move(funcRef), func, hasOptionalArgument,
std::index_sequence_for<TA...>{});
}
template <typename TR, typename... TA>
-Expr<TR> FoldElementalIntrinsic(FoldingContext &context,
+Expr<TR> FoldElementalIntrinsic(int resultKind,
+ const std::array<int, sizeof...(TA)> &argKinds, FoldingContext &context,
FunctionRef<TR> &&funcRef, ScalarFuncWithContext<TR, TA...> func,
bool hasOptionalArgument = false) {
- return FoldElementalIntrinsicHelper<ScalarFuncWithContext, TR, TA...>(context,
- std::move(funcRef), func, hasOptionalArgument,
- std::index_sequence_for<TA...>{});
+ return FoldElementalIntrinsicHelper<ScalarFuncWithContext, TR, TA...>(
+ resultKind, argKinds, context, std::move(funcRef), func,
+ hasOptionalArgument, std::index_sequence_for<TA...>{});
}
std::optional<std::int64_t> GetInt64ArgOr(
@@ -606,13 +645,15 @@ std::optional<std::vector<A>> GetIntegerVector(const B &x) {
// This to prevent generating warnings over and over if the expression
// gets re-folded.
template <typename T> Expr<T> MakeInvalidIntrinsic(FunctionRef<T> &&funcRef) {
+ const int kind{funcRef.kind()};
SpecificIntrinsic invalid{std::get<SpecificIntrinsic>(funcRef.proc().u)};
invalid.name = IntrinsicProcTable::InvalidName;
- return Expr<T>{FunctionRef<T>{ProcedureDesignator{std::move(invalid)},
+ return Expr<T>{FunctionRef<T>{kind, ProcedureDesignator{std::move(invalid)},
ActualArguments{std::move(funcRef.arguments())}}};
}
template <typename T> Expr<T> Folder<T>::CSHIFT(FunctionRef<T> &&funcRef) {
+ const int kind{funcRef.kind()};
auto args{funcRef.arguments()};
CHECK(args.size() == 3);
const auto *array{UnwrapConstantValue<T>(args[0])};
@@ -622,7 +663,8 @@ template <typename T> Expr<T> Folder<T>::CSHIFT(FunctionRef<T> &&funcRef) {
return Expr<T>{std::move(funcRef)};
}
auto convertedShift{Fold(context_,
- ConvertToType<SubscriptInteger>(Expr<SomeInteger>{*shiftExpr}))};
+ ConvertToType<SubscriptInteger>(
+ SubscriptIntegerKind, Expr<SomeInteger>{*shiftExpr}))};
const auto *shift{UnwrapConstantValue<SubscriptInteger>(convertedShift)};
if (!shift) {
return Expr<T>{std::move(funcRef)};
@@ -683,7 +725,7 @@ template <typename T> Expr<T> Folder<T>::CSHIFT(FunctionRef<T> &&funcRef) {
array->IncrementSubscripts(arrayAt);
}
return Expr<T>{PackageConstant<T>(
- std::move(resultElements), *array, array->shape())};
+ kind, std::move(resultElements), *array, array->shape())};
}
}
// Invalid, prevent re-folding
@@ -691,6 +733,7 @@ template <typename T> Expr<T> Folder<T>::CSHIFT(FunctionRef<T> &&funcRef) {
}
template <typename T> Expr<T> Folder<T>::EOSHIFT(FunctionRef<T> &&funcRef) {
+ const int kind{funcRef.kind()};
auto args{funcRef.arguments()};
CHECK(args.size() == 4);
const auto *array{UnwrapConstantValue<T>(args[0])};
@@ -701,7 +744,8 @@ template <typename T> Expr<T> Folder<T>::EOSHIFT(FunctionRef<T> &&funcRef) {
}
// Apply type conversions to the shift= and boundary= arguments.
auto convertedShift{Fold(context_,
- ConvertToType<SubscriptInteger>(Expr<SomeInteger>{*shiftExpr}))};
+ ConvertToType<SubscriptInteger>(
+ SubscriptIntegerKind, Expr<SomeInteger>{*shiftExpr}))};
const auto *shift{UnwrapConstantValue<SubscriptInteger>(convertedShift)};
if (!shift) {
return Expr<T>{std::move(funcRef)};
@@ -804,11 +848,10 @@ template <typename T> Expr<T> Folder<T>::EOSHIFT(FunctionRef<T> &&funcRef) {
T::category == TypeCategory::Real ||
T::category == TypeCategory::Complex ||
T::category == TypeCategory::Logical) {
- resultElements.emplace_back();
+ resultElements.emplace_back(Scalar<T>::Zero(kind));
} else if constexpr (T::category == TypeCategory::Character) {
auto len{static_cast<std::size_t>(array->LEN())};
- typename Scalar<T>::value_type space{' '};
- resultElements.emplace_back(len, space);
+ resultElements.emplace_back(kind, len, ' ');
} else {
DIE("no derived type boundary");
}
@@ -816,7 +859,7 @@ template <typename T> Expr<T> Folder<T>::EOSHIFT(FunctionRef<T> &&funcRef) {
array->IncrementSubscripts(arrayAt);
}
return Expr<T>{PackageConstant<T>(
- std::move(resultElements), *array, array->shape())};
+ kind, std::move(resultElements), *array, array->shape())};
}
}
// Invalid, prevent re-folding
@@ -824,8 +867,9 @@ template <typename T> Expr<T> Folder<T>::EOSHIFT(FunctionRef<T> &&funcRef) {
}
template <typename T> Expr<T> Folder<T>::MERGE(FunctionRef<T> &&funcRef) {
- return FoldElementalIntrinsic<T, T, T, LogicalResult>(context_,
- std::move(funcRef),
+ const int kind{funcRef.kind()};
+ return FoldElementalIntrinsic<T, T, T, LogicalResult>(kind,
+ {kind, kind, LogicalResultKind}, context_, std::move(funcRef),
ScalarFunc<T, T, T, LogicalResult>(
[](const Scalar<T> &ifTrue, const Scalar<T> &ifFalse,
const Scalar<LogicalResult> &predicate) -> Scalar<T> {
@@ -834,12 +878,13 @@ template <typename T> Expr<T> Folder<T>::MERGE(FunctionRef<T> &&funcRef) {
}
template <typename T> Expr<T> Folder<T>::PACK(FunctionRef<T> &&funcRef) {
+ const int kind{funcRef.kind()};
auto args{funcRef.arguments()};
CHECK(args.size() == 3);
const auto *array{UnwrapConstantValue<T>(args[0])};
const auto *vector{UnwrapConstantValue<T>(args[2])};
auto convertedMask{Fold(context_,
- ConvertToType<LogicalResult>(
+ ConvertToType<LogicalResult>(LogicalResultKind,
Expr<SomeLogical>{DEREF(UnwrapExpr<Expr<SomeLogical>>(args[1]))}))};
const auto *mask{UnwrapConstantValue<LogicalResult>(convertedMask)};
if (!array || !mask || (args[2] && !vector)) {
@@ -893,7 +938,7 @@ template <typename T> Expr<T> Folder<T>::PACK(FunctionRef<T> &&funcRef) {
++vectorAt[0];
}
}
- return Expr<T>{PackageConstant<T>(std::move(resultElements), *array,
+ return Expr<T>{PackageConstant<T>(kind, std::move(resultElements), *array,
ConstantSubscripts{static_cast<ConstantSubscript>(resultSize)})};
}
@@ -1022,6 +1067,7 @@ template <typename T> Expr<T> Folder<T>::SPREAD(FunctionRef<T> &&funcRef) {
}
template <typename T> Expr<T> Folder<T>::TRANSPOSE(FunctionRef<T> &&funcRef) {
+ const int kind{funcRef.kind()};
auto args{funcRef.arguments()};
CHECK(args.size() == 1);
const auto *matrix{UnwrapConstantValue<T>(args[0])};
@@ -1040,15 +1086,17 @@ template <typename T> Expr<T> Folder<T>::TRANSPOSE(FunctionRef<T> &&funcRef) {
}
at = matrix->shape();
std::swap(at[0], at[1]);
- return Expr<T>{PackageConstant<T>(std::move(resultElements), *matrix, at)};
+ return Expr<T>{
+ PackageConstant<T>(kind, std::move(resultElements), *matrix, at)};
}
template <typename T> Expr<T> Folder<T>::UNPACK(FunctionRef<T> &&funcRef) {
+ const int kind{funcRef.kind()};
auto args{funcRef.arguments()};
CHECK(args.size() == 3);
const auto *vector{UnwrapConstantValue<T>(args[0])};
auto convertedMask{Fold(context_,
- ConvertToType<LogicalResult>(
+ ConvertToType<LogicalResult>(LogicalResultKind,
Expr<SomeLogical>{DEREF(UnwrapExpr<Expr<SomeLogical>>(args[1]))}))};
const auto *mask{UnwrapConstantValue<LogicalResult>(convertedMask)};
const auto *field{UnwrapConstantValue<T>(args[2])};
@@ -1089,8 +1137,8 @@ template <typename T> Expr<T> Folder<T>::UNPACK(FunctionRef<T> &&funcRef) {
mask->IncrementSubscripts(maskAt);
field->IncrementSubscripts(fieldAt);
}
- return Expr<T>{
- PackageConstant<T>(std::move(resultElements), *vector, mask->shape())};
+ return Expr<T>{PackageConstant<T>(
+ kind, std::move(resultElements), *vector, mask->shape())};
}
std::optional<Expr<SomeType>> FoldTransfer(
@@ -1109,6 +1157,7 @@ template <typename T> Expr<T> Folder<T>::TRANSFER(FunctionRef<T> &&funcRef) {
template <typename T>
Expr<T> FoldMINorMAX(
FoldingContext &context, FunctionRef<T> &&funcRef, Ordering order) {
+ const int kind{funcRef.kind()};
static_assert(T::category == TypeCategory::Integer ||
T::category == TypeCategory::Unsigned ||
T::category == TypeCategory::Real ||
@@ -1123,8 +1172,8 @@ Expr<T> FoldMINorMAX(
// optional arguments that may show up in 3rd + argument.
// - The code below only accepts more than 2 arguments if all the
// arguments are constant (and hence known to be present).
- // - ConvertExprToHLFIR can't currently handle Extremum<Character>
- // - Semantics doesn't currently generate Extremum<Character>
+ // - ConvertExprToHLFIR can't currently handle Extremum<CharacterValue>
+ // - Semantics doesn't currently generate Extremum<CharacterValue>
// The original code did the folding of arguments and the overall extremum
// operation in a single pass. This was shorter code-wise, but took me
// a while to tease out all the logic and was doing redundant work.
@@ -1141,7 +1190,7 @@ Expr<T> FoldMINorMAX(
bool extremumAnyway{nargs == 2 && T::category != TypeCategory::Character};
// 1a)Fold the first two arguments.
{
- Folder<T> folder{context, /*forOptionalArgument=*/false};
+ Folder<T> folder{kind, context, /*forOptionalArgument=*/false};
if (!folder.Folding(args[0])) {
allArgsConstant = false;
}
@@ -1151,7 +1200,7 @@ Expr<T> FoldMINorMAX(
}
// 1b) Fold any optional arguments.
if (nargs > 2) {
- Folder<T> folder{context, /*forOptionalArgument=*/true};
+ Folder<T> folder{kind, context, /*forOptionalArgument=*/true};
for (std::size_t i{2}; i < nargs; ++i) {
if (args[i]) {
if (!folder.Folding(args[i])) {
@@ -1224,9 +1273,11 @@ Expr<T> RewriteSpecificMINorMAX(
intrinsic.characteristics.value().functionResult.value().SetType(*resultType);
auto insertConversion{[&](const auto &x) -> Expr<T> {
using TR = ResultType<decltype(x)>;
+ const int kind{x.kind()};
FunctionRef<TR> maxRef{
- ProcedureDesignator{funcRef.proc()}, ActualArguments{args}};
- return Fold(context, ConvertToType<T>(AsCategoryExpr(std::move(maxRef))));
+ kind, ProcedureDesignator{funcRef.proc()}, ActualArguments{args}};
+ return Fold(
+ context, ConvertToType<T>(kind, AsCategoryExpr(std::move(maxRef))));
}};
if (auto *sx{UnwrapExpr<Expr<SomeReal>>(*resultTypeArg)}) {
return common::visit(insertConversion, sx->u);
@@ -1238,25 +1289,20 @@ Expr<T> RewriteSpecificMINorMAX(
}
// FoldIntrinsicFunction()
-template <int KIND>
-Expr<Type<TypeCategory::Integer, KIND>> FoldIntrinsicFunction(
- FoldingContext &context, FunctionRef<Type<TypeCategory::Integer, KIND>> &&);
-template <int KIND>
-Expr<Type<TypeCategory::Unsigned, KIND>> FoldIntrinsicFunction(
- FoldingContext &context,
- FunctionRef<Type<TypeCategory::Unsigned, KIND>> &&);
-template <int KIND>
-Expr<Type<TypeCategory::Real, KIND>> FoldIntrinsicFunction(
- FoldingContext &context, FunctionRef<Type<TypeCategory::Real, KIND>> &&);
-template <int KIND>
-Expr<Type<TypeCategory::Complex, KIND>> FoldIntrinsicFunction(
- FoldingContext &context, FunctionRef<Type<TypeCategory::Complex, KIND>> &&);
-template <int KIND>
-Expr<Type<TypeCategory::Logical, KIND>> FoldIntrinsicFunction(
- FoldingContext &context, FunctionRef<Type<TypeCategory::Logical, KIND>> &&);
+Expr<Type<TypeCategory::Integer>> FoldIntrinsicFunction(
+ FoldingContext &context, FunctionRef<Type<TypeCategory::Integer>> &&);
+Expr<Type<TypeCategory::Unsigned>> FoldIntrinsicFunction(
+ FoldingContext &context, FunctionRef<Type<TypeCategory::Unsigned>> &&);
+Expr<Type<TypeCategory::Real>> FoldIntrinsicFunction(
+ FoldingContext &context, FunctionRef<Type<TypeCategory::Real>> &&);
+Expr<Type<TypeCategory::Complex>> FoldIntrinsicFunction(
+ FoldingContext &context, FunctionRef<Type<TypeCategory::Complex>> &&);
+Expr<Type<TypeCategory::Logical>> FoldIntrinsicFunction(
+ FoldingContext &context, FunctionRef<Type<TypeCategory::Logical>> &&);
template <typename T>
Expr<T> FoldOperation(FoldingContext &context, FunctionRef<T> &&funcRef) {
+ const int kind{funcRef.kind()};
ActualArguments &args{funcRef.arguments()};
const auto *intrinsic{std::get_if<SpecificIntrinsic>(&funcRef.proc().u)};
if (!intrinsic || intrinsic->name != "kind") {
@@ -1313,23 +1359,23 @@ Expr<T> FoldOperation(FoldingContext &context, FunctionRef<T> &&funcRef) {
}
const std::string name{intrinsic->name};
if (name == "cshift") {
- return Folder<T>{context}.CSHIFT(std::move(funcRef));
+ return Folder<T>{kind, context}.CSHIFT(std::move(funcRef));
} else if (name == "eoshift") {
- return Folder<T>{context}.EOSHIFT(std::move(funcRef));
+ return Folder<T>{kind, context}.EOSHIFT(std::move(funcRef));
} else if (name == "merge") {
- return Folder<T>{context}.MERGE(std::move(funcRef));
+ return Folder<T>{kind, context}.MERGE(std::move(funcRef));
} else if (name == "pack") {
- return Folder<T>{context}.PACK(std::move(funcRef));
+ return Folder<T>{kind, context}.PACK(std::move(funcRef));
} else if (name == "reshape") {
- return Folder<T>{context}.RESHAPE(std::move(funcRef));
+ return Folder<T>{kind, context}.RESHAPE(std::move(funcRef));
} else if (name == "spread") {
- return Folder<T>{context}.SPREAD(std::move(funcRef));
+ return Folder<T>{kind, context}.SPREAD(std::move(funcRef));
} else if (name == "transfer") {
- return Folder<T>{context}.TRANSFER(std::move(funcRef));
+ return Folder<T>{kind, context}.TRANSFER(std::move(funcRef));
} else if (name == "transpose") {
- return Folder<T>{context}.TRANSPOSE(std::move(funcRef));
+ return Folder<T>{kind, context}.TRANSPOSE(std::move(funcRef));
} else if (name == "unpack") {
- return Folder<T>{context}.UNPACK(std::move(funcRef));
+ return Folder<T>{kind, context}.UNPACK(std::move(funcRef));
}
// TODO: extends_type_of, same_type_as
if constexpr (!std::is_same_v<T, SomeDerived>) {
@@ -1342,9 +1388,11 @@ Expr<T> FoldOperation(FoldingContext &context, FunctionRef<T> &&funcRef) {
// Array constructor folding
template <typename T> class ArrayConstructorFolder {
public:
- explicit ArrayConstructorFolder(FoldingContext &c) : context_{c} {}
+ explicit ArrayConstructorFolder(int kind, FoldingContext &c)
+ : context_{c}, resultInfo_{kind} {}
Expr<T> FoldArray(ArrayConstructor<T> &&array) {
+ const int kind{array.kind()};
if constexpr (T::category == TypeCategory::Character) {
if (const auto *len{array.LEN()}) {
charLength_ = ToInt64(Fold(context_, common::Clone(*len)));
@@ -1355,16 +1403,23 @@ template <typename T> class ArrayConstructorFolder {
if (FoldArray(array)) {
auto n{static_cast<ConstantSubscript>(elements_.size())};
if constexpr (std::is_same_v<T, SomeDerived>) {
+ CHECK(kind == 0);
return Expr<T>{Constant<T>{array.GetType().GetDerivedTypeSpec(),
std::move(elements_), ConstantSubscripts{n}}};
} else if constexpr (T::category == TypeCategory::Character) {
if (charLength_) {
return Expr<T>{Constant<T>{
- *charLength_, std::move(elements_), ConstantSubscripts{n}}};
+ kind, *charLength_, std::move(elements_), ConstantSubscripts{n}}};
}
} else {
+ // resultInfo_ is default-constructed (kind 0) and only ever has its
+ // isFromInexactLiteralConversion flag set above; give it the actual
+ // runtime kind here so that the Constant's result_ carries a valid
+ // kind (kind_ is a public field of Type<CAT>, not a category with
+ // its own constructor call, so this preserves that flag).
+ assert(resultInfo_.kind() == kind);
return Expr<T>{Constant<T>{
- std::move(elements_), ConstantSubscripts{n}, resultInfo_}};
+ kind, std::move(elements_), ConstantSubscripts{n}, resultInfo_}};
}
}
return Expr<T>{std::move(array)};
@@ -1447,7 +1502,8 @@ template <typename T> class ArrayConstructorFolder {
template <typename T>
Expr<T> FoldOperation(FoldingContext &context, ArrayConstructor<T> &&array) {
- return ArrayConstructorFolder<T>{context}.FoldArray(std::move(array));
+ const int kind{array.kind()};
+ return ArrayConstructorFolder<T>{kind, context}.FoldArray(std::move(array));
}
// Array operation elemental application: When all operands to an operation
@@ -1470,12 +1526,13 @@ bool ArrayConstructorIsFlat(const ArrayConstructorValues<T> &values) {
template <typename T>
std::optional<Expr<T>> AsFlatArrayConstructor(const Expr<T> &expr) {
+ const int kind{expr.kind()};
if (const auto *c{UnwrapConstantValue<T>(expr)}) {
- ArrayConstructor<T> result{expr};
+ ArrayConstructor<T> result{kind, expr};
if (!c->empty()) {
ConstantSubscripts at{c->lbounds()};
do {
- result.Push(Expr<T>{Constant<T>{c->At(at)}});
+ result.Push(MakeConstantExpr<T>(kind, c->At(at)));
} while (c->IncrementSubscripts(at));
}
return std::make_optional<Expr<T>>(std::move(result));
@@ -1543,11 +1600,11 @@ std::optional<Expr<T>> FromArrayConstructor(
// Unary case
template <typename RESULT, typename OPERAND>
-std::optional<Expr<RESULT>> MapOperation(FoldingContext &context,
+std::optional<Expr<RESULT>> MapOperation(int kind, FoldingContext &context,
std::function<Expr<RESULT>(Expr<OPERAND> &&)> &&f, const Shape &shape,
[[maybe_unused]] std::optional<Expr<SubscriptInteger>> &&length,
Expr<OPERAND> &&values) {
- ArrayConstructor<RESULT> result{values};
+ ArrayConstructor<RESULT> result{kind, values};
if constexpr (common::HasMember<OPERAND, AllIntrinsicCategoryTypes>) {
common::visit(
[&](auto &&kindExpr) {
@@ -1575,9 +1632,9 @@ std::optional<Expr<RESULT>> MapOperation(FoldingContext &context,
}
template <typename RESULT, typename A>
-ArrayConstructor<RESULT> ArrayConstructorFromMold(
- const A &prototype, std::optional<Expr<SubscriptInteger>> &&length) {
- ArrayConstructor<RESULT> result{prototype};
+ArrayConstructor<RESULT> ArrayConstructorFromMold(int kind, const A &prototype,
+ std::optional<Expr<SubscriptInteger>> &&length) {
+ ArrayConstructor<RESULT> result{kind, prototype};
if constexpr (RESULT::category == TypeCategory::Character) {
if (length) {
result.set_LEN(std::move(*length));
@@ -1610,12 +1667,13 @@ bool ShapesMatch(FoldingContext &context,
// array * array case
template <typename RESULT, typename LEFT, typename RIGHT>
-auto MapOperation(FoldingContext &context,
+auto MapOperation(int kind, FoldingContext &context,
std::function<Expr<RESULT>(Expr<LEFT> &&, Expr<RIGHT> &&)> &&f,
const Shape &shape, std::optional<Expr<SubscriptInteger>> &&length,
Expr<LEFT> &&leftValues, Expr<RIGHT> &&rightValues)
-> std::optional<Expr<RESULT>> {
- auto result{ArrayConstructorFromMold<RESULT>(leftValues, std::move(length))};
+ auto result{
+ ArrayConstructorFromMold<RESULT>(kind, leftValues, std::move(length))};
auto &leftArrConst{std::get<ArrayConstructor<LEFT>>(leftValues.u)};
if constexpr (common::HasMember<RIGHT, AllIntrinsicCategoryTypes>) {
bool mapped{common::visit(
@@ -1661,12 +1719,13 @@ auto MapOperation(FoldingContext &context,
// array * scalar case
template <typename RESULT, typename LEFT, typename RIGHT>
-auto MapOperation(FoldingContext &context,
+auto MapOperation(int kind, FoldingContext &context,
std::function<Expr<RESULT>(Expr<LEFT> &&, Expr<RIGHT> &&)> &&f,
const Shape &shape, std::optional<Expr<SubscriptInteger>> &&length,
Expr<LEFT> &&leftValues, const Expr<RIGHT> &rightScalar)
-> std::optional<Expr<RESULT>> {
- auto result{ArrayConstructorFromMold<RESULT>(leftValues, std::move(length))};
+ auto result{
+ ArrayConstructorFromMold<RESULT>(kind, leftValues, std::move(length))};
auto &leftArrConst{std::get<ArrayConstructor<LEFT>>(leftValues.u)};
for (auto &leftValue : leftArrConst) {
auto &leftScalar{std::get<Expr<LEFT>>(leftValue.u)};
@@ -1678,12 +1737,13 @@ auto MapOperation(FoldingContext &context,
// scalar * array case
template <typename RESULT, typename LEFT, typename RIGHT>
-auto MapOperation(FoldingContext &context,
+auto MapOperation(int kind, FoldingContext &context,
std::function<Expr<RESULT>(Expr<LEFT> &&, Expr<RIGHT> &&)> &&f,
const Shape &shape, std::optional<Expr<SubscriptInteger>> &&length,
const Expr<LEFT> &leftScalar, Expr<RIGHT> &&rightValues)
-> std::optional<Expr<RESULT>> {
- auto result{ArrayConstructorFromMold<RESULT>(leftScalar, std::move(length))};
+ auto result{
+ ArrayConstructorFromMold<RESULT>(kind, leftScalar, std::move(length))};
if constexpr (common::HasMember<RIGHT, AllIntrinsicCategoryTypes>) {
common::visit(
[&](auto &&kindExpr) {
@@ -1726,12 +1786,13 @@ auto ApplyElementwise(FoldingContext &context,
Operation<DERIVED, RESULT, OPERAND> &operation,
std::function<Expr<RESULT>(Expr<OPERAND> &&)> &&f)
-> std::optional<Expr<RESULT>> {
+ const int kind{operation.kind()};
auto &expr{operation.left()};
expr = Fold(context, std::move(expr));
if (expr.Rank() > 0) {
if (std::optional<Shape> shape{GetShape(context, expr)}) {
if (auto values{AsFlatArrayConstructor(expr)}) {
- return MapOperation(context, std::move(f), *shape,
+ return MapOperation(kind, context, std::move(f), *shape,
ComputeResultLength(operation), std::move(*values));
}
}
@@ -1743,10 +1804,11 @@ template <typename DERIVED, typename RESULT, typename OPERAND>
auto ApplyElementwise(
FoldingContext &context, Operation<DERIVED, RESULT, OPERAND> &operation)
-> std::optional<Expr<RESULT>> {
+ const int kind{operation.kind()};
return ApplyElementwise(context, operation,
std::function<Expr<RESULT>(Expr<OPERAND> &&)>{
- [](Expr<OPERAND> &&operand) {
- return Expr<RESULT>{DERIVED{std::move(operand)}};
+ [kind](Expr<OPERAND> &&operand) {
+ return Expr<RESULT>{DERIVED{kind, std::move(operand)}};
}});
}
@@ -1755,6 +1817,7 @@ auto ApplyElementwise(FoldingContext &context,
Operation<DERIVED, RESULT, LEFT, RIGHT> &operation,
std::function<Expr<RESULT>(Expr<LEFT> &&, Expr<RIGHT> &&)> &&f)
-> std::optional<Expr<RESULT>> {
+ const int kind{operation.kind()};
auto resultLength{ComputeResultLength(operation)};
auto &leftExpr{operation.left()};
auto &rightExpr{operation.right()};
@@ -1773,18 +1836,18 @@ auto ApplyElementwise(FoldingContext &context,
if (CheckConformance(context.messages(), *leftShape, *rightShape,
CheckConformanceFlags::EitherScalarExpandable)
.value_or(false /*fail if not known now to conform*/)) {
- return MapOperation(context, std::move(f), *leftShape,
+ return MapOperation(kind, context, std::move(f), *leftShape,
std::move(resultLength), std::move(*left),
std::move(*right));
} else {
return std::nullopt;
}
- return MapOperation(context, std::move(f), *leftShape,
+ return MapOperation(kind, context, std::move(f), *leftShape,
std::move(resultLength), std::move(*left), std::move(*right));
}
}
} else if (IsExpandableScalar(rightExpr, context, *leftShape)) {
- return MapOperation(context, std::move(f), *leftShape,
+ return MapOperation(kind, context, std::move(f), *leftShape,
std::move(resultLength), std::move(*left), rightExpr);
}
}
@@ -1793,7 +1856,7 @@ auto ApplyElementwise(FoldingContext &context,
if (std::optional<Shape> rightShape{GetShape(context, rightExpr)}) {
if (IsExpandableScalar(leftExpr, context, *rightShape)) {
if (auto right{AsFlatArrayConstructor(rightExpr)}) {
- return MapOperation(context, std::move(f), *rightShape,
+ return MapOperation(kind, context, std::move(f), *rightShape,
std::move(resultLength), leftExpr, std::move(*right));
}
}
@@ -1806,46 +1869,46 @@ template <typename DERIVED, typename RESULT, typename LEFT, typename RIGHT>
auto ApplyElementwise(
FoldingContext &context, Operation<DERIVED, RESULT, LEFT, RIGHT> &operation)
-> std::optional<Expr<RESULT>> {
+ const int kind{operation.kind()};
return ApplyElementwise(context, operation,
std::function<Expr<RESULT>(Expr<LEFT> &&, Expr<RIGHT> &&)>{
- [](Expr<LEFT> &&left, Expr<RIGHT> &&right) {
- return Expr<RESULT>{DERIVED{std::move(left), std::move(right)}};
+ [kind](Expr<LEFT> &&left, Expr<RIGHT> &&right) {
+ return Expr<RESULT>{
+ DERIVED{kind, std::move(left), std::move(right)}};
}});
}
// Unary operations
template <typename TO, typename FROM>
-common::IfNoLvalue<std::optional<TO>, FROM> ConvertString(FROM &&s) {
- if constexpr (std::is_same_v<TO, FROM>) {
+common::IfNoLvalue<std::optional<TO>, FROM> ConvertString(
+ int toKind, FROM &&s) {
+ const int fromKind{s.kind()};
+ if (std::is_same_v<TO, FROM> && toKind == fromKind) {
return std::make_optional<TO>(std::move(s));
+ } else if (auto result{s.ToAscii(toKind)}; !result.IsMonostate()) {
+ return result;
} else {
- // Fortran character conversion is well defined between distinct kinds
- // only when the actual characters are valid 7-bit ASCII.
- TO str;
- for (auto iter{s.cbegin()}; iter != s.cend(); ++iter) {
- if (static_cast<std::uint64_t>(*iter) > 127) {
- return std::nullopt;
- }
- str.push_back(static_cast<typename TO::value_type>(*iter));
- }
- return std::make_optional<TO>(std::move(str));
+ return std::nullopt;
}
}
template <typename TO, TypeCategory FROMCAT>
Expr<TO> FoldOperation(
FoldingContext &context, Convert<TO, FROMCAT> &&convert) {
+ const int toKind{convert.kind()};
if (auto array{ApplyElementwise(context, convert)}) {
return *array;
}
struct {
FoldingContext &context;
Convert<TO, FROMCAT> &convert;
- } msvcWorkaround{context, convert};
+ int toKind;
+ } msvcWorkaround{context, convert, toKind};
return common::visit(
[&msvcWorkaround](auto &kindExpr) -> Expr<TO> {
using Operand = ResultType<decltype(kindExpr)>;
+ const int toKind{msvcWorkaround.toKind};
// This variable is a workaround for msvc which emits an error when
// using the FROMCAT template parameter below.
TypeCategory constexpr FromCat{FROMCAT};
@@ -1853,95 +1916,113 @@ Expr<TO> FoldOperation(
auto &convert{msvcWorkaround.convert};
if (auto value{GetScalarConstantValue<Operand>(kindExpr)}) {
FoldingContext &ctx{msvcWorkaround.context};
+ const int fromKind{value->kind()};
if constexpr (TO::category == TypeCategory::Integer) {
if constexpr (FromCat == TypeCategory::Integer) {
- auto converted{Scalar<TO>::ConvertSigned(*value)};
+ auto converted{
+ Scalar<TO>::ConvertSigned(*value, Scalar<TO>::bits(toKind))};
if (converted.overflow) {
ctx.Warn(common::UsageWarning::FoldingException,
"conversion of %s_%d to INTEGER(%d) overflowed; result is %s"_warn_en_US,
- value->SignedDecimal(), Operand::kind, TO::kind,
+ value->SignedDecimal(), fromKind, toKind,
converted.value.SignedDecimal());
}
- return ScalarConstantToExpr(std::move(converted.value));
+ return MakeConstantExpr<TO>(toKind, std::move(converted.value));
} else if constexpr (FromCat == TypeCategory::Unsigned) {
- auto converted{Scalar<TO>::ConvertUnsigned(*value)};
+ auto converted{Scalar<TO>::ConvertUnsigned(
+ *value, Scalar<TO>::bits(toKind))};
if ((converted.overflow || converted.value.IsNegative())) {
ctx.Warn(common::UsageWarning::FoldingException,
"conversion of %s_U%d to INTEGER(%d) overflowed; result is %s"_warn_en_US,
- value->UnsignedDecimal(), Operand::kind, TO::kind,
+ value->UnsignedDecimal(), fromKind, toKind,
converted.value.SignedDecimal());
}
- return ScalarConstantToExpr(std::move(converted.value));
+ return MakeConstantExpr<TO>(toKind, std::move(converted.value));
} else if constexpr (FromCat == TypeCategory::Real) {
- auto converted{value->template ToInteger<Scalar<TO>>()};
+ auto converted{value->ToInteger(
+ common::RoundingMode::ToZero, Scalar<TO>::bits(toKind))};
if (converted.flags.test(RealFlag::InvalidArgument)) {
ctx.Warn(common::UsageWarning::FoldingException,
"REAL(%d) to INTEGER(%d) conversion: invalid argument"_warn_en_US,
- Operand::kind, TO::kind);
+ fromKind, toKind);
} else if (converted.flags.test(RealFlag::Overflow)) {
ctx.Warn(common::UsageWarning::FoldingException,
"REAL(%d) to INTEGER(%d) conversion overflowed"_warn_en_US,
- Operand::kind, TO::kind);
+ fromKind, toKind);
}
- return ScalarConstantToExpr(std::move(converted.value));
+ return MakeConstantExpr<TO>(toKind, std::move(converted.value));
}
} else if constexpr (TO::category == TypeCategory::Unsigned) {
if constexpr (FromCat == TypeCategory::Integer ||
FromCat == TypeCategory::Unsigned) {
- return Expr<TO>{
- Constant<TO>{Scalar<TO>::ConvertUnsigned(*value).value}};
+ return MakeConstantExpr<TO>(toKind,
+ Scalar<TO>::ConvertUnsigned(*value, Scalar<TO>::bits(toKind))
+ .value);
} else if constexpr (FromCat == TypeCategory::Real) {
- return Expr<TO>{
- Constant<TO>{value->template ToInteger<Scalar<TO>>().value}};
+ return MakeConstantExpr<TO>(toKind,
+ value
+ ->ToInteger(common::RoundingMode::ToZero,
+ Scalar<TO>::bits(toKind))
+ .value);
}
} else if constexpr (TO::category == TypeCategory::Real) {
if constexpr (FromCat == TypeCategory::Integer ||
FromCat == TypeCategory::Unsigned) {
auto converted{Scalar<TO>::FromInteger(
- *value, FromCat == TypeCategory::Unsigned)};
+ toKind, *value, FromCat == TypeCategory::Unsigned)};
if (!converted.flags.empty()) {
char buffer[64];
std::snprintf(buffer, sizeof buffer,
- "INTEGER(%d) to REAL(%d) conversion", Operand::kind,
- TO::kind);
+ "INTEGER(%d) to REAL(%d) conversion", fromKind, toKind);
ctx.RealFlagWarnings(converted.flags, buffer);
}
- return ScalarConstantToExpr(std::move(converted.value));
+ return MakeConstantExpr<TO>(toKind, std::move(converted.value));
} else if constexpr (FromCat == TypeCategory::Real) {
- auto converted{Scalar<TO>::Convert(*value)};
+ auto converted{Scalar<TO>::Convert(toKind, *value)};
char buffer[64];
if (!converted.flags.empty()) {
std::snprintf(buffer, sizeof buffer,
- "REAL(%d) to REAL(%d) conversion", Operand::kind, TO::kind);
+ "REAL(%d) to REAL(%d) conversion", fromKind, toKind);
ctx.RealFlagWarnings(converted.flags, buffer);
}
if (ctx.targetCharacteristics().areSubnormalsFlushedToZero()) {
converted.value = converted.value.FlushSubnormalToZero();
}
- return ScalarConstantToExpr(std::move(converted.value));
+ return MakeConstantExpr<TO>(toKind, std::move(converted.value));
}
} else if constexpr (TO::category == TypeCategory::Complex) {
if constexpr (FromCat == TypeCategory::Complex) {
return FoldOperation(ctx,
- ComplexConstructor<TO::kind>{
- AsExpr(Convert<typename TO::Part>{AsCategoryExpr(
- Constant<typename Operand::Part>{value->REAL()})}),
- AsExpr(Convert<typename TO::Part>{AsCategoryExpr(
- Constant<typename Operand::Part>{value->AIMAG()})})});
+ ComplexConstructor{toKind,
+ Fold(ctx,
+ AsExpr(Convert<typename TO::Part>(toKind,
+ AsCategoryExpr(Constant<typename Operand::Part>{
+ toKind, value->REAL()})))),
+ Fold(ctx,
+ AsExpr(Convert<typename TO::Part>(toKind,
+ AsCategoryExpr(Constant<typename Operand::Part>{
+ toKind, value->AIMAG()}))))});
}
} else if constexpr (TO::category == TypeCategory::Character &&
FromCat == TypeCategory::Character) {
- if (auto converted{ConvertString<Scalar<TO>>(std::move(*value))}) {
+ if (auto converted{
+ ConvertString<Scalar<TO>>(toKind, std::move(*value))}) {
return ScalarConstantToExpr(std::move(*converted));
}
} else if constexpr (TO::category == TypeCategory::Logical &&
FromCat == TypeCategory::Logical) {
- return Expr<TO>{value->IsTrue()};
+ // The conversion's target kind is a runtime property; build the
+ // result LOGICAL constant with that kind rather than letting it
+ // default (the Expr<Logical>(bool) constructor would otherwise
+ // produce LogicalResultKind=4 and silently drop the conversion).
+ return Expr<TO>{Constant<TO>{
+ toKind, value::LogicalValue{toKind, value->IsTrue()}}};
}
} else if constexpr (TO::category == FromCat &&
FromCat != TypeCategory::Character) {
// Conversion of non-constant in same type category
- if constexpr (std::is_same_v<Operand, TO>) {
+ auto fromTy{kindExpr.GetType()};
+ if (fromTy && fromTy->kind() == toKind) {
return std::move(kindExpr); // remove needless conversion
} else if constexpr (TO::category == TypeCategory::Logical ||
TO::category == TypeCategory::Integer) {
@@ -1949,16 +2030,26 @@ Expr<TO> FoldOperation(
std::get_if<Convert<Operand, TO::category>>(&kindExpr.u)}) {
// Conversion of conversion of same category & kind
if (auto *x{std::get_if<Expr<TO>>(&innerConv->left().u)}) {
- if constexpr (TO::category == TypeCategory::Logical ||
- TO::kind <= Operand::kind) {
- return std::move(*x); // no-op Logical or Integer
- // widening/narrowing conversion pair
- } else if constexpr (std::is_same_v<TO,
- DescriptorInquiry::Result>) {
- if (std::holds_alternative<DescriptorInquiry>(x->u) ||
- std::holds_alternative<TypeParamInquiry>(x->u)) {
+ // intermediateKind is the kind of the middle (Operand) result;
+ // xKind is the kind of the innermost expression, which must
+ // match the outer target for the round trip to be a no-op.
+ auto intermediateTy{kindExpr.GetType()};
+ auto xTy{x->GetType()};
+ int intermediateKind{
+ intermediateTy ? intermediateTy->kind() : 0};
+ int xKind{xTy ? xTy->kind() : 0};
+ if constexpr (TO::category == TypeCategory::Logical) {
+ return std::move(*x); // no-op Logical conversion pair
+ } else { // Integer
+ if (xKind == toKind && toKind <= intermediateKind) {
+ return std::move(*x); // widening/narrowing conversion pair
+ } else if (toKind == SubscriptIntegerKind &&
+ xKind == toKind) {
// int(int(size(...),kind=k),kind=8) -> size(...)
- return std::move(*x);
+ if (std::holds_alternative<DescriptorInquiry>(x->u) ||
+ std::holds_alternative<TypeParamInquiry>(x->u)) {
+ return std::move(*x);
+ }
}
}
}
@@ -1972,11 +2063,12 @@ Expr<TO> FoldOperation(
template <typename T>
Expr<T> FoldOperation(FoldingContext &context, Parentheses<T> &&x) {
+ const int kind{x.kind()};
auto &operand{x.left()};
operand = Fold(context, std::move(operand));
if (auto value{GetScalarConstantValue<T>(operand)}) {
// Preserve parentheses, even around constants.
- return Expr<T>{Parentheses<T>{Expr<T>{Constant<T>{*value}}}};
+ return Expr<T>{Parentheses<T>{kind, MakeConstantExpr<T>(kind, *value)}};
} else if (std::holds_alternative<Parentheses<T>>(operand.u)) {
// ((x)) -> (x)
return std::move(operand);
@@ -1987,6 +2079,7 @@ Expr<T> FoldOperation(FoldingContext &context, Parentheses<T> &&x) {
template <typename T>
Expr<T> FoldOperation(FoldingContext &context, Negate<T> &&x) {
+ const int kind{x.kind()};
if (auto array{ApplyElementwise(context, x)}) {
return *array;
}
@@ -2003,14 +2096,14 @@ Expr<T> FoldOperation(FoldingContext &context, Negate<T> &&x) {
auto negated{value->Negate()};
if (negated.overflow) {
context.Warn(common::UsageWarning::FoldingException,
- "INTEGER(%d) negation overflowed"_warn_en_US, T::kind);
+ "INTEGER(%d) negation overflowed"_warn_en_US, value->kind());
}
- return Expr<T>{Constant<T>{std::move(negated.value)}};
+ return Expr<T>{Constant<T>{kind, std::move(negated.value)}};
} else if constexpr (T::category == TypeCategory::Unsigned) {
- return Expr<T>{Constant<T>{std::move(value->Negate().value)}};
+ return Expr<T>{Constant<T>{kind, std::move(value->Negate().value)}};
} else {
// REAL & COMPLEX negation: no exceptions possible
- return Expr<T>{Constant<T>{value->Negate()}};
+ return Expr<T>{Constant<T>{kind, value->Negate()}};
}
}
return Expr<T>{std::move(x)};
@@ -2037,6 +2130,7 @@ std::optional<std::pair<Scalar<LEFT>, Scalar<RIGHT>>> OperandsAreConstants(
template <typename T>
Expr<T> FoldOperation(FoldingContext &context, Add<T> &&x) {
+ const int kind{x.kind()};
if (auto array{ApplyElementwise(context, x)}) {
return *array;
}
@@ -2045,12 +2139,12 @@ Expr<T> FoldOperation(FoldingContext &context, Add<T> &&x) {
auto sum{folded->first.AddSigned(folded->second)};
if (sum.overflow) {
context.Warn(common::UsageWarning::FoldingException,
- "INTEGER(%d) addition overflowed"_warn_en_US, T::kind);
+ "INTEGER(%d) addition overflowed"_warn_en_US, folded->first.kind());
}
- return Expr<T>{Constant<T>{sum.value}};
+ return Expr<T>{Constant<T>{kind, sum.value}};
} else if constexpr (T::category == TypeCategory::Unsigned) {
return Expr<T>{
- Constant<T>{folded->first.AddUnsigned(folded->second).value}};
+ Constant<T>{kind, folded->first.AddUnsigned(folded->second).value}};
} else {
auto sum{folded->first.Add(
folded->second, context.targetCharacteristics().roundingMode())};
@@ -2058,7 +2152,7 @@ Expr<T> FoldOperation(FoldingContext &context, Add<T> &&x) {
if (context.targetCharacteristics().areSubnormalsFlushedToZero()) {
sum.value = sum.value.FlushSubnormalToZero();
}
- return Expr<T>{Constant<T>{sum.value}};
+ return Expr<T>{Constant<T>{kind, sum.value}};
}
} else if constexpr (T::category == TypeCategory::Integer ||
T::category == TypeCategory::Unsigned) {
@@ -2085,6 +2179,7 @@ Expr<T> FoldOperation(FoldingContext &context, Add<T> &&x) {
template <typename T>
Expr<T> FoldOperation(FoldingContext &context, Subtract<T> &&x) {
+ const int kind{x.kind()};
if (auto array{ApplyElementwise(context, x)}) {
return *array;
}
@@ -2093,12 +2188,13 @@ Expr<T> FoldOperation(FoldingContext &context, Subtract<T> &&x) {
auto difference{folded->first.SubtractSigned(folded->second)};
if (difference.overflow) {
context.Warn(common::UsageWarning::FoldingException,
- "INTEGER(%d) subtraction overflowed"_warn_en_US, T::kind);
+ "INTEGER(%d) subtraction overflowed"_warn_en_US,
+ folded->first.kind());
}
- return Expr<T>{Constant<T>{difference.value}};
+ return Expr<T>{Constant<T>{kind, difference.value}};
} else if constexpr (T::category == TypeCategory::Unsigned) {
- return Expr<T>{
- Constant<T>{folded->first.SubtractSigned(folded->second).value}};
+ return Expr<T>{Constant<T>{
+ kind, folded->first.SubtractSigned(folded->second).value}};
} else {
auto difference{folded->first.Subtract(
folded->second, context.targetCharacteristics().roundingMode())};
@@ -2106,7 +2202,7 @@ Expr<T> FoldOperation(FoldingContext &context, Subtract<T> &&x) {
if (context.targetCharacteristics().areSubnormalsFlushedToZero()) {
difference.value = difference.value.FlushSubnormalToZero();
}
- return Expr<T>{Constant<T>{difference.value}};
+ return Expr<T>{Constant<T>{kind, difference.value}};
}
} else if constexpr (T::category == TypeCategory::Integer ||
T::category == TypeCategory::Unsigned) {
@@ -2125,6 +2221,7 @@ Expr<T> FoldOperation(FoldingContext &context, Subtract<T> &&x) {
template <typename T>
Expr<T> FoldOperation(FoldingContext &context, Multiply<T> &&x) {
+ const int kind{x.kind()};
if (auto array{ApplyElementwise(context, x)}) {
return *array;
}
@@ -2133,12 +2230,13 @@ Expr<T> FoldOperation(FoldingContext &context, Multiply<T> &&x) {
auto product{folded->first.MultiplySigned(folded->second)};
if (product.SignedMultiplicationOverflowed()) {
context.Warn(common::UsageWarning::FoldingException,
- "INTEGER(%d) multiplication overflowed"_warn_en_US, T::kind);
+ "INTEGER(%d) multiplication overflowed"_warn_en_US,
+ folded->first.kind());
}
- return Expr<T>{Constant<T>{product.lower}};
+ return Expr<T>{Constant<T>{kind, product.lower}};
} else if constexpr (T::category == TypeCategory::Unsigned) {
- return Expr<T>{
- Constant<T>{folded->first.MultiplyUnsigned(folded->second).lower}};
+ return Expr<T>{Constant<T>{
+ kind, folded->first.MultiplyUnsigned(folded->second).lower}};
} else {
auto product{folded->first.Multiply(
folded->second, context.targetCharacteristics().roundingMode())};
@@ -2146,23 +2244,25 @@ Expr<T> FoldOperation(FoldingContext &context, Multiply<T> &&x) {
if (context.targetCharacteristics().areSubnormalsFlushedToZero()) {
product.value = product.value.FlushSubnormalToZero();
}
- return Expr<T>{Constant<T>{product.value}};
+ return Expr<T>{Constant<T>{kind, product.value}};
}
} else if constexpr (T::category == TypeCategory::Integer) {
if (auto c{GetScalarConstantValue<T>(x.right())}) {
x.right() = std::move(x.left());
- x.left() = Expr<T>{std::move(*c)};
+ x.left() = Expr<T>{Constant<T>(kind, *c)};
}
if (auto c{GetScalarConstantValue<T>(x.left())}) {
if (c->IsZero() && x.right().Rank() == 0) {
return std::move(x.left());
- } else if (c->CompareSigned(Scalar<T>{1}) == Ordering::Equal) {
+ } else if (c->CompareSigned(value::IntegerValue{kind, 1}) ==
+ Ordering::Equal) {
if (IsVariable(x.right())) {
return FoldOperation(context, Parentheses<T>{std::move(x.right())});
} else {
return std::move(x.right());
}
- } else if (c->CompareSigned(Scalar<T>{-1}) == Ordering::Equal) {
+ } else if (c->CompareSigned(value::IntegerValue{kind, -1}) ==
+ Ordering::Equal) {
return FoldOperation(context, Negate<T>{std::move(x.right())});
}
}
@@ -2172,6 +2272,7 @@ Expr<T> FoldOperation(FoldingContext &context, Multiply<T> &&x) {
template <typename T>
Expr<T> FoldOperation(FoldingContext &context, Divide<T> &&x) {
+ const int kind{x.kind()};
if (auto array{ApplyElementwise(context, x)}) {
return *array;
}
@@ -2180,22 +2281,22 @@ Expr<T> FoldOperation(FoldingContext &context, Divide<T> &&x) {
auto quotAndRem{folded->first.DivideSigned(folded->second)};
if (quotAndRem.divisionByZero) {
context.Warn(common::UsageWarning::FoldingException,
- "INTEGER(%d) division by zero"_warn_en_US, T::kind);
+ "INTEGER(%d) division by zero"_warn_en_US, folded->first.kind());
return Expr<T>{std::move(x)};
}
if (quotAndRem.overflow) {
context.Warn(common::UsageWarning::FoldingException,
- "INTEGER(%d) division overflowed"_warn_en_US, T::kind);
+ "INTEGER(%d) division overflowed"_warn_en_US, folded->first.kind());
}
- return Expr<T>{Constant<T>{quotAndRem.quotient}};
+ return Expr<T>{Constant<T>{kind, quotAndRem.quotient}};
} else if constexpr (T::category == TypeCategory::Unsigned) {
auto quotAndRem{folded->first.DivideUnsigned(folded->second)};
if (quotAndRem.divisionByZero) {
context.Warn(common::UsageWarning::FoldingException,
- "UNSIGNED(%d) division by zero"_warn_en_US, T::kind);
+ "UNSIGNED(%d) division by zero"_warn_en_US, folded->first.kind());
return Expr<T>{std::move(x)};
}
- return Expr<T>{Constant<T>{quotAndRem.quotient}};
+ return Expr<T>{Constant<T>{kind, quotAndRem.quotient}};
} else {
auto quotient{folded->first.Divide(
folded->second, context.targetCharacteristics().roundingMode())};
@@ -2206,10 +2307,10 @@ Expr<T> FoldOperation(FoldingContext &context, Divide<T> &&x) {
if constexpr (T::category == TypeCategory::Real) {
if (folded->second.IsZero() && context.moduleFileName().has_value()) {
using IntType = typename T::Scalar::Word;
- auto intNumerator{folded->first.template ToInteger<IntType>()};
+ auto intNumerator{folded->first.ToInteger()};
isCanonicalNaNOrInf = intNumerator.flags == RealFlags{} &&
- intNumerator.value >= IntType{-1} &&
- intNumerator.value <= IntType{1};
+ intNumerator.value >= IntType{-1, 16} &&
+ intNumerator.value <= IntType{1, 16};
}
}
if (!isCanonicalNaNOrInf) {
@@ -2218,7 +2319,7 @@ Expr<T> FoldOperation(FoldingContext &context, Divide<T> &&x) {
if (context.targetCharacteristics().areSubnormalsFlushedToZero()) {
quotient.value = quotient.value.FlushSubnormalToZero();
}
- return Expr<T>{Constant<T>{quotient.value}};
+ return Expr<T>{Constant<T>{kind, quotient.value}};
}
}
return Expr<T>{std::move(x)};
@@ -2226,6 +2327,7 @@ Expr<T> FoldOperation(FoldingContext &context, Divide<T> &&x) {
template <typename T>
Expr<T> FoldOperation(FoldingContext &context, Power<T> &&x) {
+ const int kind{x.kind()};
if (auto array{ApplyElementwise(context, x)}) {
return *array;
}
@@ -2234,30 +2336,33 @@ Expr<T> FoldOperation(FoldingContext &context, Power<T> &&x) {
auto power{folded->first.Power(folded->second)};
if (power.divisionByZero) {
context.Warn(common::UsageWarning::FoldingException,
- "INTEGER(%d) zero to negative power"_warn_en_US, T::kind);
+ "INTEGER(%d) zero to negative power"_warn_en_US,
+ folded->first.kind());
} else if (power.overflow) {
context.Warn(common::UsageWarning::FoldingException,
- "INTEGER(%d) power overflowed"_warn_en_US, T::kind);
+ "INTEGER(%d) power overflowed"_warn_en_US, folded->first.kind());
} else if (power.zeroToZero) {
context.Warn(common::UsageWarning::FoldingException,
- "INTEGER(%d) 0**0 is not defined"_warn_en_US, T::kind);
+ "INTEGER(%d) 0**0 is not defined"_warn_en_US, folded->first.kind());
}
- return Expr<T>{Constant<T>{power.power}};
+ return Expr<T>{Constant<T>{kind, power.power}};
} else {
if (folded->first.IsZero()) {
if (folded->second.IsZero()) {
context.Warn(common::UsageWarning::FoldingException,
"REAL/COMPLEX 0**0 is not defined"_warn_en_US);
} else {
- return Expr<T>(Constant<T>{folded->first}); // 0. ** nonzero -> 0.
+ return Expr<T>(
+ Constant<T>{kind, folded->first}); // 0. ** nonzero -> 0.
}
- } else if (auto callable{GetHostRuntimeWrapper<T, T, T>("pow")}) {
- return Expr<T>{
- Constant<T>{(*callable)(context, folded->first, folded->second)}};
+ } else if (auto callable{GetHostRuntimeWrapper<T, T, T>(
+ kind, {kind, kind}, "pow")}) {
+ return Expr<T>{Constant<T>{
+ kind, (*callable)(context, folded->first, folded->second)}};
} else {
context.Warn(common::UsageWarning::FoldingFailure,
"Power for %s cannot be folded on host"_warn_en_US,
- T{}.AsFortran());
+ DynamicType{T::category, folded->first.kind()}.AsFortran());
}
}
}
@@ -2266,18 +2371,19 @@ Expr<T> FoldOperation(FoldingContext &context, Power<T> &&x) {
template <typename T>
Expr<T> FoldOperation(FoldingContext &context, RealToIntPower<T> &&x) {
+ const int kind{x.kind()};
if (auto array{ApplyElementwise(context, x)}) {
return *array;
}
return common::visit(
- [&](auto &y) -> Expr<T> {
+ [&, kind](auto &y) -> Expr<T> {
if (auto folded{OperandsAreConstants(x.left(), y)}) {
auto power{evaluate::IntPower(folded->first, folded->second)};
context.RealFlagWarnings(power.flags, "power with INTEGER exponent");
if (context.targetCharacteristics().areSubnormalsFlushedToZero()) {
power.value = power.value.FlushSubnormalToZero();
}
- return Expr<T>{Constant<T>{power.value}};
+ return Expr<T>{Constant<T>{kind, power.value}};
} else {
return Expr<T>{std::move(x)};
}
@@ -2298,6 +2404,7 @@ Expr<T> FoldOperation(FoldingContext &context, ConditionalExpr<T> &&x) {
template <typename T>
Expr<T> FoldOperation(FoldingContext &context, Extremum<T> &&x) {
+ const int kind{x.kind()};
if (auto array{ApplyElementwise(context, x,
std::function<Expr<T>(Expr<T> &&, Expr<T> &&)>{[=](Expr<T> &&l,
Expr<T> &&r) {
@@ -2308,17 +2415,17 @@ Expr<T> FoldOperation(FoldingContext &context, Extremum<T> &&x) {
if (auto folded{OperandsAreConstants(x)}) {
if constexpr (T::category == TypeCategory::Integer) {
if (folded->first.CompareSigned(folded->second) == x.ordering) {
- return Expr<T>{Constant<T>{folded->first}};
+ return Expr<T>{Constant<T>{kind, folded->first}};
}
} else if constexpr (T::category == TypeCategory::Unsigned) {
if (folded->first.CompareUnsigned(folded->second) == x.ordering) {
- return Expr<T>{Constant<T>{folded->first}};
+ return Expr<T>{Constant<T>{kind, folded->first}};
}
} else if constexpr (T::category == TypeCategory::Real) {
if (folded->first.IsNotANumber() ||
(folded->first.Compare(folded->second) == Relation::Less) ==
(x.ordering == Ordering::Less)) {
- return Expr<T>{Constant<T>{folded->first}};
+ return Expr<T>{Constant<T>{kind, folded->first}};
}
} else {
static_assert(T::category == TypeCategory::Character);
@@ -2327,38 +2434,38 @@ Expr<T> FoldOperation(FoldingContext &context, Extremum<T> &&x) {
auto maxLen{std::max(folded->first.length(), folded->second.length())};
bool isFirst{x.ordering == Compare(folded->first, folded->second)};
auto res{isFirst ? std::move(folded->first) : std::move(folded->second)};
- res = res.length() == maxLen
- ? std::move(res)
- : CharacterUtils<T::kind>::Resize(res, maxLen);
- return Expr<T>{Constant<T>{std::move(res)}};
+ if (res.length() != maxLen) {
+ res = CharacterUtils::Resize(res, maxLen);
+ }
+ return Expr<T>{Constant<T>{kind, std::move(res)}};
}
- return Expr<T>{Constant<T>{folded->second}};
+ return Expr<T>{Constant<T>{kind, folded->second}};
}
return Expr<T>{std::move(x)};
}
-template <int KIND>
-Expr<Type<TypeCategory::Real, KIND>> ToReal(
- FoldingContext &context, Expr<SomeType> &&expr) {
- using Result = Type<TypeCategory::Real, KIND>;
+inline Expr<Type<TypeCategory::Real>> ToReal(
+ int kind, FoldingContext &context, Expr<SomeType> &&expr) {
+ using Result = Type<TypeCategory::Real>;
std::optional<Expr<Result>> result;
common::visit(
[&](auto &&x) {
using From = std::decay_t<decltype(x)>;
if constexpr (std::is_same_v<From, BOZLiteralConstant>) {
// Move the bits without any integer->real conversion
- From original{x};
- result = ConvertToType<Result>(std::move(x));
+ BOZLiteralConstant original{x};
+ result = ConvertToType<Result>(kind, std::move(x));
const auto *constant{UnwrapExpr<Constant<Result>>(*result)};
CHECK(constant);
Scalar<Result> real{constant->GetScalarValue().value()};
- From converted{From::ConvertUnsigned(real.RawBits()).value};
+ BOZLiteralConstant converted{
+ BOZLiteralConstant::ConvertUnsigned(real.RawBits(), 128).value};
if (original != converted) { // C1601
context.Warn(common::UsageWarning::FoldingValueChecks,
"Nonzero bits truncated from BOZ literal constant in REAL intrinsic"_warn_en_US);
}
} else if constexpr (IsNumericCategoryExpr<From>()) {
- result = Fold(context, ConvertToType<Result>(std::move(x)));
+ result = Fold(context, ConvertToType<Result>(kind, std::move(x)));
} else {
common::die("ToReal: bad argument expression");
}
@@ -2368,25 +2475,25 @@ Expr<Type<TypeCategory::Real, KIND>> ToReal(
}
// REAL(z) and AIMAG(z)
-template <int KIND>
-Expr<Type<TypeCategory::Real, KIND>> FoldOperation(
- FoldingContext &context, ComplexComponent<KIND> &&x) {
- using Operand = Type<TypeCategory::Complex, KIND>;
- using Result = Type<TypeCategory::Real, KIND>;
+inline Expr<Type<TypeCategory::Real>> FoldOperation(
+ FoldingContext &context, ComplexComponent &&x) {
+ const int kind{x.kind()};
+ using Operand = Type<TypeCategory::Complex>;
+ using Result = Type<TypeCategory::Real>;
if (auto array{ApplyElementwise(context, x,
std::function<Expr<Result>(Expr<Operand> &&)>{
[=](Expr<Operand> &&operand) {
- return Expr<Result>{ComplexComponent<KIND>{
- x.isImaginaryPart, std::move(operand)}};
+ return Expr<Result>{
+ ComplexComponent{x.isImaginaryPart, std::move(operand)}};
}})}) {
return *array;
}
auto &operand{x.left()};
if (auto value{GetScalarConstantValue<Operand>(operand)}) {
if (x.isImaginaryPart) {
- return Expr<Result>{Constant<Result>{value->AIMAG()}};
+ return Expr<Result>{Constant<Result>{kind, value->AIMAG()}};
} else {
- return Expr<Result>{Constant<Result>{value->REAL()}};
+ return Expr<Result>{Constant<Result>{kind, value->REAL()}};
}
}
return Expr<Result>{std::move(x)};
diff --git a/flang/lib/Evaluate/fold-integer.cpp b/flang/lib/Evaluate/fold-integer.cpp
index c7db4069e3e28..2545f4b4210b5 100644
--- a/flang/lib/Evaluate/fold-integer.cpp
+++ b/flang/lib/Evaluate/fold-integer.cpp
@@ -10,6 +10,7 @@
#include "fold-matmul.h"
#include "fold-reduction.h"
#include "flang/Evaluate/check-expression.h"
+#include "flang/Evaluate/shape.h"
namespace Fortran::evaluate {
@@ -17,16 +18,17 @@ namespace Fortran::evaluate {
// Return scalar value if asScalar == true and shape-dim array otherwise.
template <typename T>
Expr<T> PackageConstantBounds(
- const ConstantSubscripts &&bounds, bool asScalar = false) {
+ int kind, const ConstantSubscripts &&bounds, bool asScalar = false) {
if (asScalar) {
- return Expr<T>{Constant<T>{bounds.at(0)}};
+ return MakeConstantExpr<T>(kind, bounds.at(0));
} else {
// As rank-dim array
const int rank{GetRank(bounds)};
std::vector<Scalar<T>> packed(rank);
std::transform(bounds.begin(), bounds.end(), packed.begin(),
- [](ConstantSubscript x) { return Scalar<T>(x); });
- return Expr<T>{Constant<T>{std::move(packed), ConstantSubscripts{rank}}};
+ [kind](ConstantSubscript x) { return Scalar<T>(kind, x); });
+ return Expr<T>{
+ Constant<T>{kind, std::move(packed), ConstantSubscripts{rank}}};
}
}
@@ -99,16 +101,16 @@ class GetConstantArrayBoundHelper {
public:
template <typename T>
static Expr<T> GetLbound(
- const Expr<SomeType> &array, std::optional<int> dim) {
- return PackageConstantBounds<T>(
+ int kind, const Expr<SomeType> &array, std::optional<int> dim) {
+ return PackageConstantBounds<T>(kind,
GetConstantArrayBoundHelper(dim, /*getLbound=*/true).Get(array),
dim.has_value());
}
template <typename T>
static Expr<T> GetUbound(
- const Expr<SomeType> &array, std::optional<int> dim) {
- return PackageConstantBounds<T>(
+ int kind, const Expr<SomeType> &array, std::optional<int> dim) {
+ return PackageConstantBounds<T>(kind,
GetConstantArrayBoundHelper(dim, /*getLbound=*/false).Get(array),
dim.has_value());
}
@@ -173,10 +175,10 @@ class GetConstantArrayBoundHelper {
bool arrayFromParenthesesExpr{false};
};
-template <int KIND>
-Expr<Type<TypeCategory::Integer, KIND>> LBOUND(FoldingContext &context,
- FunctionRef<Type<TypeCategory::Integer, KIND>> &&funcRef) {
- using T = Type<TypeCategory::Integer, KIND>;
+Expr<Type<TypeCategory::Integer>> LBOUND(FoldingContext &context,
+ FunctionRef<Type<TypeCategory::Integer>> &&funcRef) {
+ using T = Type<TypeCategory::Integer>;
+ const int kind{funcRef.kind()};
ActualArguments &args{funcRef.arguments()};
if (const auto *array{UnwrapExpr<Expr<SomeType>>(args[0])}) {
std::optional<int> dim;
@@ -201,33 +203,33 @@ Expr<Type<TypeCategory::Integer, KIND>> LBOUND(FoldingContext &context,
lowerBoundsAreOne = false;
if (dim) {
if (auto lb{GetLBOUND(context, *named, *dim)}) {
- return Fold(context, ConvertToType<T>(std::move(*lb)));
+ return Fold(context, ConvertToType<T>(kind, std::move(*lb)));
}
} else if (auto extents{
AsExtentArrayExpr(GetLBOUNDs(context, *named))}) {
return Fold(context,
- ConvertToType<T>(Expr<ExtentType>{std::move(*extents)}));
+ ConvertToType<T>(kind, Expr<ExtentType>{std::move(*extents)}));
}
} else {
lowerBoundsAreOne = symbol.Rank() == 0; // LBOUND(array%component)
}
}
if (IsActuallyConstant(*array)) {
- return GetConstantArrayBoundHelper::GetLbound<T>(*array, dim);
+ return GetConstantArrayBoundHelper::GetLbound<T>(kind, *array, dim);
}
if (lowerBoundsAreOne) {
ConstantSubscripts ones(rank, ConstantSubscript{1});
- return PackageConstantBounds<T>(std::move(ones), dim.has_value());
+ return PackageConstantBounds<T>(kind, std::move(ones), dim.has_value());
}
}
}
return Expr<T>{std::move(funcRef)};
}
-template <int KIND>
-Expr<Type<TypeCategory::Integer, KIND>> UBOUND(FoldingContext &context,
- FunctionRef<Type<TypeCategory::Integer, KIND>> &&funcRef) {
- using T = Type<TypeCategory::Integer, KIND>;
+Expr<Type<TypeCategory::Integer>> UBOUND(FoldingContext &context,
+ FunctionRef<Type<TypeCategory::Integer>> &&funcRef) {
+ using T = Type<TypeCategory::Integer>;
+ const int kind{funcRef.kind()};
ActualArguments &args{funcRef.arguments()};
if (auto *array{UnwrapExpr<Expr<SomeType>>(args[0])}) {
std::optional<int> dim;
@@ -249,17 +251,18 @@ Expr<Type<TypeCategory::Integer, KIND>> UBOUND(FoldingContext &context,
takeBoundsFromShape = false;
if (dim) {
if (auto ub{GetUBOUND(context, *named, *dim)}) {
- return Fold(context, ConvertToType<T>(std::move(*ub)));
+ return Fold(context, ConvertToType<T>(kind, std::move(*ub)));
}
} else {
Shape ubounds{GetUBOUNDs(context, *named)};
if (semantics::IsAssumedSizeArray(symbol)) {
CHECK(!ubounds.back());
- ubounds.back() = ExtentExpr{-1};
+ ubounds.back() = MakeExtentExpr(-1);
}
if (auto extents{AsExtentArrayExpr(ubounds)}) {
return Fold(context,
- ConvertToType<T>(Expr<ExtentType>{std::move(*extents)}));
+ ConvertToType<T>(
+ kind, Expr<ExtentType>{std::move(*extents)}));
}
}
} else {
@@ -267,17 +270,18 @@ Expr<Type<TypeCategory::Integer, KIND>> UBOUND(FoldingContext &context,
}
}
if (IsActuallyConstant(*array)) {
- return GetConstantArrayBoundHelper::GetUbound<T>(*array, dim);
+ return GetConstantArrayBoundHelper::GetUbound<T>(kind, *array, dim);
}
if (takeBoundsFromShape) {
if (auto shape{GetContextFreeShape(context, *array)}) {
if (dim) {
if (auto &dimSize{shape->at(*dim)}) {
return Fold(context,
- ConvertToType<T>(Expr<ExtentType>{std::move(*dimSize)}));
+ ConvertToType<T>(
+ kind, Expr<ExtentType>{std::move(*dimSize)}));
}
} else if (auto shapeExpr{AsExtentArrayExpr(*shape)}) {
- return Fold(context, ConvertToType<T>(std::move(*shapeExpr)));
+ return Fold(context, ConvertToType<T>(kind, std::move(*shapeExpr)));
}
}
}
@@ -287,10 +291,10 @@ Expr<Type<TypeCategory::Integer, KIND>> UBOUND(FoldingContext &context,
}
// LCOBOUND() & UCOBOUND()
-template <int KIND>
-Expr<Type<TypeCategory::Integer, KIND>> COBOUND(FoldingContext &context,
- FunctionRef<Type<TypeCategory::Integer, KIND>> &&funcRef, bool isUCOBOUND) {
- using T = Type<TypeCategory::Integer, KIND>;
+Expr<Type<TypeCategory::Integer>> COBOUND(FoldingContext &context,
+ FunctionRef<Type<TypeCategory::Integer>> &&funcRef, bool isUCOBOUND) {
+ using T = Type<TypeCategory::Integer>;
+ const int kind{funcRef.kind()};
ActualArguments &args{funcRef.arguments()};
if (const Symbol * coarray{UnwrapWholeSymbolOrComponentDataRef(args[0])}) {
std::optional<int> dim;
@@ -306,27 +310,32 @@ Expr<Type<TypeCategory::Integer, KIND>> COBOUND(FoldingContext &context,
if (dim) {
if (auto cb{isUCOBOUND ? GetUCOBOUND(*coarray, *dim)
: GetLCOBOUND(*coarray, *dim)}) {
- return Fold(context, ConvertToType<T>(std::move(*cb)));
+ return Fold(context, ConvertToType<T>(kind, std::move(*cb)));
}
} else if (auto cbs{
AsExtentArrayExpr(isUCOBOUND ? GetUCOBOUNDs(*coarray)
: GetLCOBOUNDs(*coarray))}) {
- return Fold(context, ConvertToType<T>(Expr<ExtentType>{std::move(*cbs)}));
+ return Fold(
+ context, ConvertToType<T>(kind, Expr<ExtentType>{std::move(*cbs)}));
}
}
return Expr<T>{std::move(funcRef)};
}
// COUNT()
-template <typename T, int MASK_KIND> class CountAccumulator {
- using MaskT = Type<TypeCategory::Logical, MASK_KIND>;
+template <typename T> class CountAccumulator {
+ using MaskT = Type<TypeCategory::Logical>;
public:
- CountAccumulator(const Constant<MaskT> &mask) : mask_{mask} {}
+ constexpr int kind() const { return kind_; }
+
+ CountAccumulator(int kind, const Constant<MaskT> &mask)
+ : kind_{kind}, mask_{mask} {}
void operator()(
Scalar<T> &element, const ConstantSubscripts &at, bool /*first*/) {
+ CHECK(element.kind() == kind());
if (mask_.At(at).IsTrue()) {
- auto incremented{element.AddSigned(Scalar<T>{1})};
+ auto incremented{element.AddSigned(Scalar<T>{kind(), 1})};
overflow_ |= incremented.overflow;
element = incremented.value;
}
@@ -335,21 +344,23 @@ template <typename T, int MASK_KIND> class CountAccumulator {
void Done(Scalar<T> &) const {}
private:
+ int kind_;
const Constant<MaskT> &mask_;
bool overflow_{false};
};
-template <typename T, int maskKind>
+template <typename T>
static Expr<T> FoldCount(FoldingContext &context, FunctionRef<T> &&ref) {
- using KindLogical = Type<TypeCategory::Logical, maskKind>;
+ using KindLogical = Type<TypeCategory::Logical>;
static_assert(T::category == TypeCategory::Integer);
+ const int kind{ref.kind()};
std::optional<int> dim;
if (std::optional<ArrayAndMask<KindLogical>> arrayAndMask{
- ProcessReductionArgs<KindLogical>(
- context, ref.arguments(), dim, /*ARRAY=*/0, /*DIM=*/1)}) {
- CountAccumulator<T, maskKind> accumulator{arrayAndMask->array};
- Constant<T> result{DoReduction<T>(arrayAndMask->array, arrayAndMask->mask,
- dim, Scalar<T>{}, accumulator)};
+ ProcessReductionArgs<KindLogical>(LogicalResultKind, context,
+ ref.arguments(), dim, /*ARRAY=*/0, /*DIM=*/1)}) {
+ CountAccumulator<T> accumulator{kind, arrayAndMask->array};
+ Constant<T> result{DoReduction<T>(kind, arrayAndMask->array,
+ arrayAndMask->mask, dim, Scalar<T>{kind, 0}, accumulator)};
if (accumulator.overflow()) {
context.Warn(common::UsageWarning::FoldingException,
"Result of intrinsic function COUNT overflows its result type"_warn_en_US);
@@ -370,12 +381,12 @@ template <WhichLocation WHICH> class LocationHelper {
using Types = std::conditional_t<WHICH == WhichLocation::Findloc,
AllIntrinsicTypes, RelationalTypes>;
- template <typename T> Result Test() const {
- if (T::category != type_.category() || T::kind != type_.kind()) {
+ template <typename T> Result Test(int kind) const {
+ if (T::category != type_.category() || kind != type_.kind()) {
return std::nullopt;
}
CHECK(arg_.size() == (WHICH == WhichLocation::Findloc ? 6 : 5));
- Folder<T> folder{context_};
+ Folder<T> folder{kind, context_};
Constant<T> *array{folder.Folding(arg_[0])};
if (!array) {
return std::nullopt;
@@ -397,9 +408,9 @@ template <WhichLocation WHICH> class LocationHelper {
}
bool back{false};
if (arg_[backArg]) {
- const auto *backConst{
- Folder<LogicalResult>{context_, /*forOptionalArgument=*/true}.Folding(
- arg_[backArg])};
+ const auto *backConst{Folder<LogicalResult>{
+ LogicalResultKind, context_, /*forOptionalArgument=*/true}
+ .Folding(arg_[backArg])};
if (backConst) {
back = backConst->GetScalarValue().value().IsTrue();
} else {
@@ -422,7 +433,7 @@ template <WhichLocation WHICH> class LocationHelper {
ConstantSubscript n{GetSize(array->shape())};
std::vector<Scalar<LogicalResult>> mask_elements(
n, Scalar<LogicalResult>{scalarMask.value()});
- *mask = Constant<LogicalResult>{
+ *mask = Constant<LogicalResult>{LogicalResultKind,
std::move(mask_elements), ConstantSubscripts{array->shape()}};
}
mask->SetLowerBoundsToOne();
@@ -487,9 +498,9 @@ template <WhichLocation WHICH> class LocationHelper {
}
std::vector<Scalar<SubscriptInteger>> resultElements;
for (ConstantSubscript j : resultIndices) {
- resultElements.emplace_back(j);
+ resultElements.emplace_back(SubscriptIntegerKind, j);
}
- return Constant<SubscriptInteger>{
+ return Constant<SubscriptInteger>{SubscriptIntegerKind,
std::move(resultElements), std::move(resultShape)};
}
@@ -499,15 +510,16 @@ template <WhichLocation WHICH> class LocationHelper {
std::optional<Constant<T>> &value,
[[maybe_unused]] RelationalOperator relation,
[[maybe_unused]] bool back) const {
+ const int kind{element.kind()};
std::optional<Expr<LogicalResult>> cmp;
bool result{true};
if (value) {
if constexpr (T::category == TypeCategory::Logical) {
// array(at) .EQV. value?
static_assert(WHICH == WhichLocation::Findloc);
- cmp.emplace(ConvertToType<LogicalResult>(
- Expr<T>{LogicalOperation<T::kind>{LogicalOperator::Eqv,
- Expr<T>{Constant<T>{element}}, Expr<T>{Constant<T>{*value}}}}));
+ cmp.emplace(Expr<LogicalResult>{LogicalOperation{LogicalOperator::Eqv,
+ MakeConstantExpr<T>(kind, element),
+ MakeConstantExpr<T>(kind, *value)}});
} else { // compare array(at) to value
if constexpr (T::category == TypeCategory::Real &&
(WHICH == WhichLocation::Maxloc ||
@@ -515,12 +527,13 @@ template <WhichLocation WHICH> class LocationHelper {
if (value && value->GetScalarValue().value().IsNotANumber() &&
(back || !element.IsNotANumber())) {
// Replace NaN
- cmp.emplace(Constant<LogicalResult>{Scalar<LogicalResult>{true}});
+ cmp.emplace(MakeLogicalResultConstant(true));
}
}
if (!cmp) {
- cmp.emplace(PackageRelation(relation, Expr<T>{Constant<T>{element}},
- Expr<T>{Constant<T>{*value}}));
+ cmp.emplace(
+ PackageRelation(relation, MakeConstantExpr<T>(kind, element),
+ MakeConstantExpr<T>(kind, *value)));
}
}
Expr<LogicalResult> folded{Fold(context_, std::move(*cmp))};
@@ -531,7 +544,7 @@ template <WhichLocation WHICH> class LocationHelper {
if constexpr (WHICH == WhichLocation::Maxloc ||
WHICH == WhichLocation::Minloc) {
if (result) {
- value.emplace(std::move(element));
+ value.emplace(Constant<T>{kind, element});
}
}
return result;
@@ -562,8 +575,7 @@ static std::optional<Constant<SubscriptInteger>> FoldLocationCall(
}
}
}
- return common::SearchTypes(
- LocationHelper<which>{std::move(*type), arg, context});
+ return SearchTypes(LocationHelper<which>{std::move(*type), arg, context});
}
}
return std::nullopt;
@@ -572,10 +584,11 @@ static std::optional<Constant<SubscriptInteger>> FoldLocationCall(
template <WhichLocation which, typename T>
static Expr<T> FoldLocation(FoldingContext &context, FunctionRef<T> &&ref) {
static_assert(T::category == TypeCategory::Integer);
+ const int kind{ref.kind()};
if (std::optional<Constant<SubscriptInteger>> found{
FoldLocationCall<which>(ref.arguments(), context)}) {
- return Expr<T>{Fold(
- context, ConvertToType<T>(Expr<SubscriptInteger>{std::move(*found)}))};
+ return Expr<T>{Fold(context,
+ ConvertToType<T>(kind, Expr<SubscriptInteger>{std::move(*found)}))};
} else {
return Expr<T>{std::move(ref)};
}
@@ -586,15 +599,16 @@ template <typename T>
static Expr<T> FoldBitReduction(FoldingContext &context, FunctionRef<T> &&ref,
Scalar<T> (Scalar<T>::*operation)(const Scalar<T> &) const,
Scalar<T> identity) {
+ const int kind{ref.kind()};
static_assert(T::category == TypeCategory::Integer ||
T::category == TypeCategory::Unsigned);
std::optional<int> dim;
if (std::optional<ArrayAndMask<T>> arrayAndMask{
- ProcessReductionArgs<T>(context, ref.arguments(), dim,
+ ProcessReductionArgs<T>(kind, context, ref.arguments(), dim,
/*ARRAY=*/0, /*DIM=*/1, /*MASK=*/2)}) {
OperationAccumulator<T> accumulator{arrayAndMask->array, operation};
- return Expr<T>{DoReduction<T>(
- arrayAndMask->array, arrayAndMask->mask, dim, identity, accumulator)};
+ return Expr<T>{DoReduction<T>(kind, arrayAndMask->array, arrayAndMask->mask,
+ dim, identity, accumulator)};
}
return Expr<T>{std::move(ref)};
}
@@ -603,38 +617,44 @@ static Expr<T> FoldBitReduction(FoldingContext &context, FunctionRef<T> &&ref,
template <typename T>
std::optional<Expr<T>> FoldIntrinsicFunctionCommon(
FoldingContext &context, FunctionRef<T> &funcRef) {
+ const int kind{funcRef.kind()};
ActualArguments &args{funcRef.arguments()};
auto *intrinsic{std::get_if<SpecificIntrinsic>(&funcRef.proc().u)};
CHECK(intrinsic);
std::string name{intrinsic->name};
- using Int4 = Type<TypeCategory::Integer, 4>;
+ using Int4 = Type<TypeCategory::Integer>;
if (name == "bit_size") {
- return Expr<T>{Scalar<T>::bits};
+ return MakeConstantExpr<T>(kind, Scalar<T>::bits(kind));
} else if (name == "digits") {
if (const auto *cx{UnwrapExpr<Expr<SomeInteger>>(args[0])}) {
- return Expr<T>{common::visit(
- [](const auto &kx) {
- return Scalar<ResultType<decltype(kx)>>::DIGITS;
- },
- cx->u)};
+ return MakeConstantExpr<T>(kind,
+ common::visit(
+ [](const auto &kx) {
+ return Scalar<ResultType<decltype(kx)>>::DIGITS(kx.kind());
+ },
+ cx->u));
} else if (const auto *cx{UnwrapExpr<Expr<SomeUnsigned>>(args[0])}) {
- return Expr<T>{common::visit(
- [](const auto &kx) {
- return Scalar<ResultType<decltype(kx)>>::DIGITS + 1;
- },
- cx->u)};
+ return MakeConstantExpr<T>(kind,
+ common::visit(
+ [](const auto &kx) {
+ return Scalar<ResultType<decltype(kx)>>::DIGITS(kx.kind()) + 1;
+ },
+ cx->u));
} else if (const auto *cx{UnwrapExpr<Expr<SomeReal>>(args[0])}) {
- return Expr<T>{common::visit(
- [](const auto &kx) {
- return Scalar<ResultType<decltype(kx)>>::DIGITS;
- },
- cx->u)};
+ return MakeConstantExpr<T>(kind,
+ common::visit(
+ [](const auto &kx) {
+ return Scalar<ResultType<decltype(kx)>>::DIGITS(kx.kind());
+ },
+ cx->u));
} else if (const auto *cx{UnwrapExpr<Expr<SomeComplex>>(args[0])}) {
- return Expr<T>{common::visit(
- [](const auto &kx) {
- return Scalar<typename ResultType<decltype(kx)>::Part>::DIGITS;
- },
- cx->u)};
+ return MakeConstantExpr<T>(kind,
+ common::visit(
+ [](const auto &kx) {
+ return Scalar<typename ResultType<decltype(kx)>::Part>::DIGITS(
+ kx.kind());
+ },
+ cx->u));
}
} else if (name == "dot_product") {
return FoldDotProduct<T>(context, std::move(funcRef));
@@ -643,24 +663,27 @@ std::optional<Expr<T>> FoldIntrinsicFunctionCommon(
name == "dshiftl" ? &Scalar<T>::DSHIFTL : &Scalar<T>::DSHIFTR};
// Third argument can be of any kind. However, it must be smaller or equal
// than BIT_SIZE. It can be converted to Int4 to simplify.
- if (const auto *argCon{Folder<T>(context).Folding(args[0])};
+ if (const auto *argCon{Folder<T>(kind, context).Folding(args[0])};
argCon && argCon->empty()) {
- } else if (const auto *shiftCon{Folder<Int4>(context).Folding(args[2])}) {
+ } else if (const auto *shiftCon{Folder<Int4>(
+ /*kind=*/4, context)
+ .Folding(args[2])}) {
for (const auto &scalar : shiftCon->values()) {
std::int64_t shiftVal{scalar.ToInt64()};
if (shiftVal < 0) {
context.messages().Say("SHIFT=%jd count for %s is negative"_err_en_US,
std::intmax_t{shiftVal}, name);
break;
- } else if (shiftVal > T::Scalar::bits) {
+ } else if (shiftVal > Scalar<T>::bits(kind)) {
context.messages().Say(
"SHIFT=%jd count for %s is greater than %d"_err_en_US,
- std::intmax_t{shiftVal}, name, T::Scalar::bits);
+ std::intmax_t{shiftVal}, name, Scalar<T>::bits(kind));
break;
}
}
}
- return FoldElementalIntrinsic<T, T, T, Int4>(context, std::move(funcRef),
+ return FoldElementalIntrinsic<T, T, T, Int4>(kind, {kind, kind, 4}, context,
+ std::move(funcRef),
ScalarFunc<T, T, T, Int4>(
[&fptr](const Scalar<T> &i, const Scalar<T> &j,
const Scalar<Int4> &shift) -> Scalar<T> {
@@ -676,14 +699,14 @@ std::optional<Expr<T>> FoldIntrinsicFunctionCommon(
} else {
common::die("missing case to fold intrinsic function %s", name.c_str());
}
- return FoldElementalIntrinsic<T, T, T>(
- context, std::move(funcRef), ScalarFunc<T, T, T>(fptr));
+ return FoldElementalIntrinsic<T, T, T>(kind, {kind, kind}, context,
+ std::move(funcRef), ScalarFunc<T, T, T>(fptr));
} else if (name == "iall") {
- return FoldBitReduction(
- context, std::move(funcRef), &Scalar<T>::IAND, Scalar<T>{}.NOT());
+ return FoldBitReduction(context, std::move(funcRef), &Scalar<T>::IAND,
+ Scalar<T>{kind, 0}.NOT());
} else if (name == "iany") {
return FoldBitReduction(
- context, std::move(funcRef), &Scalar<T>::IOR, Scalar<T>{});
+ context, std::move(funcRef), &Scalar<T>::IOR, Scalar<T>{kind, 0});
} else if (name == "ibclr" || name == "ibset") {
// Second argument can be of any kind. However, it must be smaller
// than BIT_SIZE. It can be converted to Int4 to simplify.
@@ -694,9 +717,9 @@ std::optional<Expr<T>> FoldIntrinsicFunctionCommon(
} else {
common::die("missing case to fold intrinsic function %s", name.c_str());
}
- if (const auto *argCon{Folder<T>(context).Folding(args[0])};
+ if (const auto *argCon{Folder<T>(kind, context).Folding(args[0])};
argCon && argCon->empty()) {
- } else if (const auto *posCon{Folder<Int4>(context).Folding(args[1])}) {
+ } else if (const auto *posCon{Folder<Int4>(4, context).Folding(args[1])}) {
for (const auto &scalar : posCon->values()) {
std::int64_t posVal{scalar.ToInt64()};
if (posVal < 0) {
@@ -704,23 +727,24 @@ std::optional<Expr<T>> FoldIntrinsicFunctionCommon(
"bit position for %s (%jd) is negative"_err_en_US, name,
std::intmax_t{posVal});
break;
- } else if (posVal >= T::Scalar::bits) {
+ } else if (posVal >= Scalar<T>::bits(kind)) {
context.messages().Say(
"bit position for %s (%jd) is not less than %d"_err_en_US, name,
- std::intmax_t{posVal}, T::Scalar::bits);
+ std::intmax_t{posVal}, Scalar<T>::bits(kind));
break;
}
}
}
- return FoldElementalIntrinsic<T, T, Int4>(context, std::move(funcRef),
+ return FoldElementalIntrinsic<T, T, Int4>(kind, {kind, 4}, context,
+ std::move(funcRef),
ScalarFunc<T, T, Int4>(
[&](const Scalar<T> &i, const Scalar<Int4> &pos) -> Scalar<T> {
return std::invoke(fptr, i, static_cast<int>(pos.ToInt64()));
}));
} else if (name == "ibits") {
- const auto *posCon{Folder<Int4>(context).Folding(args[1])};
- const auto *lenCon{Folder<Int4>(context).Folding(args[2])};
- if (const auto *argCon{Folder<T>(context).Folding(args[0])};
+ const auto *posCon{Folder<Int4>(4, context).Folding(args[1])};
+ const auto *lenCon{Folder<Int4>(4, context).Folding(args[2])};
+ if (const auto *argCon{Folder<T>(kind, context).Folding(args[0])};
argCon && argCon->empty()) {
} else {
std::size_t posCt{posCon ? posCon->size() : 0};
@@ -743,15 +767,16 @@ std::optional<Expr<T>> FoldIntrinsicFunctionCommon(
"bit length for IBITS(LEN=%jd) is negative"_err_en_US,
std::intmax_t{lenVal});
break;
- } else if (posVal + lenVal > T::Scalar::bits) {
+ } else if (posVal + lenVal > Scalar<T>::bits(kind)) {
context.messages().Say(
"IBITS() must have POS+LEN (>=%jd) no greater than %d"_err_en_US,
- std::intmax_t{posVal + lenVal}, T::Scalar::bits);
+ std::intmax_t{posVal + lenVal}, Scalar<T>::bits(kind));
break;
}
}
}
- return FoldElementalIntrinsic<T, T, Int4, Int4>(context, std::move(funcRef),
+ return FoldElementalIntrinsic<T, T, Int4, Int4>(kind, {kind, 4, 4}, context,
+ std::move(funcRef),
ScalarFunc<T, T, Int4, Int4>(
[&](const Scalar<T> &i, const Scalar<Int4> &pos,
const Scalar<Int4> &len) -> Scalar<T> {
@@ -768,7 +793,7 @@ std::optional<Expr<T>> FoldIntrinsicFunctionCommon(
if (derived->IsEnumerationType()) {
if (auto ordExpr{GetEnumerationOrdinal(*derivedExpr)}) {
if (auto ordVal{ToInt64(*ordExpr)}) {
- return Expr<T>{Constant<T>{Scalar<T>{*ordVal}}};
+ return MakeConstantExpr<T>(kind, *ordVal);
}
}
// Non-constant enumeration argument — leave unfolded
@@ -782,7 +807,7 @@ std::optional<Expr<T>> FoldIntrinsicFunctionCommon(
using From = std::decay_t<decltype(x)>;
if constexpr (std::is_same_v<From, BOZLiteralConstant> ||
IsNumericCategoryExpr<From>()) {
- return Fold(context, ConvertToType<T>(std::move(x)));
+ return Fold(context, ConvertToType<T>(kind, std::move(x)));
}
DIE("int() argument type not valid");
},
@@ -790,13 +815,13 @@ std::optional<Expr<T>> FoldIntrinsicFunctionCommon(
}
} else if (name == "iparity") {
return FoldBitReduction(
- context, std::move(funcRef), &Scalar<T>::IEOR, Scalar<T>{});
+ context, std::move(funcRef), &Scalar<T>::IEOR, Scalar<T>{kind, 0});
} else if (name == "ishft" || name == "ishftc") {
- const auto *argCon{Folder<T>(context).Folding(args[0])};
- const auto *shiftCon{Folder<Int4>(context).Folding(args[1])};
+ const auto *argCon{Folder<T>(kind, context).Folding(args[0])};
+ const auto *shiftCon{Folder<Int4>(4, context).Folding(args[1])};
const auto *shiftVals{shiftCon ? &shiftCon->values() : nullptr};
const auto *sizeCon{args.size() == 3
- ? Folder<Int4>{context, /*forOptionalArgument=*/true}.Folding(
+ ? Folder<Int4>{4, context, /*forOptionalArgument=*/true}.Folding(
args[2])
: nullptr};
const auto *sizeVals{sizeCon ? &sizeCon->values() : nullptr};
@@ -806,15 +831,15 @@ std::optional<Expr<T>> FoldIntrinsicFunctionCommon(
} else {
for (const auto &scalar : *shiftVals) {
std::int64_t shiftVal{scalar.ToInt64()};
- if (shiftVal < -T::Scalar::bits) {
+ if (shiftVal < -Scalar<T>::bits(kind)) {
context.messages().Say(
"SHIFT=%jd count for %s is less than %d"_err_en_US,
- std::intmax_t{shiftVal}, name, -T::Scalar::bits);
+ std::intmax_t{shiftVal}, name, -Scalar<T>::bits(kind));
break;
- } else if (shiftVal > T::Scalar::bits) {
+ } else if (shiftVal > Scalar<T>::bits(kind)) {
context.messages().Say(
"SHIFT=%jd count for %s is greater than %d"_err_en_US,
- std::intmax_t{shiftVal}, name, T::Scalar::bits);
+ std::intmax_t{shiftVal}, name, Scalar<T>::bits(kind));
break;
}
}
@@ -826,10 +851,10 @@ std::optional<Expr<T>> FoldIntrinsicFunctionCommon(
"SIZE=%jd count for ishftc is not positive"_err_en_US,
std::intmax_t{sizeVal}, name);
break;
- } else if (sizeVal > T::Scalar::bits) {
+ } else if (sizeVal > Scalar<T>::bits(kind)) {
context.messages().Say(
"SIZE=%jd count for ishftc is greater than %d"_err_en_US,
- std::intmax_t{sizeVal}, T::Scalar::bits);
+ std::intmax_t{sizeVal}, Scalar<T>::bits(kind));
break;
}
}
@@ -852,20 +877,22 @@ std::optional<Expr<T>> FoldIntrinsicFunctionCommon(
}
}
if (name == "ishft") {
- return FoldElementalIntrinsic<T, T, Int4>(context, std::move(funcRef),
+ return FoldElementalIntrinsic<T, T, Int4>(kind, {kind, 4}, context,
+ std::move(funcRef),
ScalarFunc<T, T, Int4>(
[&](const Scalar<T> &i, const Scalar<Int4> &shift) -> Scalar<T> {
return i.ISHFT(static_cast<int>(shift.ToInt64()));
}));
} else if (!args.at(2)) { // ISHFTC(no SIZE=)
- return FoldElementalIntrinsic<T, T, Int4>(context, std::move(funcRef),
+ return FoldElementalIntrinsic<T, T, Int4>(kind, {kind, 4}, context,
+ std::move(funcRef),
ScalarFunc<T, T, Int4>(
[&](const Scalar<T> &i, const Scalar<Int4> &shift) -> Scalar<T> {
return i.ISHFTC(static_cast<int>(shift.ToInt64()));
}));
} else { // ISHFTC(with SIZE=)
- return FoldElementalIntrinsic<T, T, Int4, Int4>(context,
- std::move(funcRef),
+ return FoldElementalIntrinsic<T, T, Int4, Int4>(kind, {kind, 4, 4},
+ context, std::move(funcRef),
ScalarFunc<T, T, Int4, Int4>(
[&](const Scalar<T> &i, const Scalar<Int4> &shift,
const Scalar<Int4> &size) -> Scalar<T> {
@@ -880,10 +907,10 @@ std::optional<Expr<T>> FoldIntrinsicFunctionCommon(
if (auto *expr{UnwrapExpr<Expr<SomeKind<T::category>>>(args[0])}) {
// Rewrite to IAND(INT(n,k),255_k) for k=KIND(T)
intrinsic->name = "iand";
- auto converted{ConvertToType<T>(std::move(*expr))};
+ auto converted{ConvertToType<T>(kind, std::move(*expr))};
*expr =
Fold(context, Expr<SomeKind<T::category>>{std::move(converted)});
- args.emplace_back(AsGenericExpr(Expr<T>{Scalar<T>{255}}));
+ args.emplace_back(AsGenericExpr(MakeConstantExpr<T>(kind, 255)));
return FoldIntrinsicFunction(context, std::move(funcRef));
}
}
@@ -893,36 +920,38 @@ std::optional<Expr<T>> FoldIntrinsicFunctionCommon(
// It can be safely converted to Int4 to simplify.
const auto fptr{name == "maskl" || name == "umaskl" ? &Scalar<T>::MASKL
: &Scalar<T>::MASKR};
- return FoldElementalIntrinsic<T, Int4>(context, std::move(funcRef),
- ScalarFunc<T, Int4>([&fptr](const Scalar<Int4> &places) -> Scalar<T> {
- return fptr(static_cast<int>(places.ToInt64()));
- }));
+ return FoldElementalIntrinsic<T, Int4>(kind, {4}, context,
+ std::move(funcRef),
+ ScalarFunc<T, Int4>(
+ [&fptr, kind](const Scalar<Int4> &places) -> Scalar<T> {
+ return fptr(kind, static_cast<int>(places.ToInt64()));
+ }));
} else if (name == "matmul") {
return FoldMatmul(context, std::move(funcRef));
} else if (name == "max") {
return FoldMINorMAX(context, std::move(funcRef), Ordering::Greater);
} else if (name == "maxval") {
- return FoldMaxvalMinval<T>(context, std::move(funcRef),
+ return FoldMaxvalMinval<T>(kind, context, std::move(funcRef),
RelationalOperator::GT,
T::category == TypeCategory::Unsigned ? typename T::Scalar{}
- : T::Scalar::Least());
+ : T::Scalar::Least(kind));
} else if (name == "merge_bits") {
- return FoldElementalIntrinsic<T, T, T, T>(
- context, std::move(funcRef), &Scalar<T>::MERGE_BITS);
+ return FoldElementalIntrinsic<T, T, T, T>(kind, {kind, kind, kind}, context,
+ std::move(funcRef), &Scalar<T>::MERGE_BITS);
} else if (name == "min") {
return FoldMINorMAX(context, std::move(funcRef), Ordering::Less);
} else if (name == "minval") {
- return FoldMaxvalMinval<T>(context, std::move(funcRef),
+ return FoldMaxvalMinval<T>(kind, context, std::move(funcRef),
RelationalOperator::LT,
T::category == TypeCategory::Unsigned ? typename T::Scalar{}.NOT()
- : T::Scalar::HUGE());
+ : T::Scalar::HUGE(kind));
} else if (name == "not") {
return FoldElementalIntrinsic<T, T>(
- context, std::move(funcRef), &Scalar<T>::NOT);
+ kind, {kind}, context, std::move(funcRef), &Scalar<T>::NOT);
} else if (name == "product") {
- return FoldProduct<T>(context, std::move(funcRef), Scalar<T>{1});
+ return FoldProduct<T>(context, std::move(funcRef), Scalar<T>{kind, 1});
} else if (name == "radix") {
- return Expr<T>{2};
+ return Expr<T>{Constant<T>{kind, typename T::Scalar{kind, 2}}};
} else if (name == "shifta" || name == "shiftr" || name == "shiftl") {
// Second argument can be of any kind. However, it must be smaller or
// equal than BIT_SIZE. It can be converted to Int4 to simplify.
@@ -935,24 +964,26 @@ std::optional<Expr<T>> FoldIntrinsicFunctionCommon(
} else {
common::die("missing case to fold intrinsic function %s", name.c_str());
}
- if (const auto *argCon{Folder<T>(context).Folding(args[0])};
+ if (const auto *argCon{Folder<T>(kind, context).Folding(args[0])};
argCon && argCon->empty()) {
- } else if (const auto *shiftCon{Folder<Int4>(context).Folding(args[1])}) {
+ } else if (const auto *shiftCon{
+ Folder<Int4>(4, context).Folding(args[1])}) {
for (const auto &scalar : shiftCon->values()) {
std::int64_t shiftVal{scalar.ToInt64()};
if (shiftVal < 0) {
context.messages().Say("SHIFT=%jd count for %s is negative"_err_en_US,
- std::intmax_t{shiftVal}, name, -T::Scalar::bits);
+ std::intmax_t{shiftVal}, name, -Scalar<T>::bits(kind));
break;
- } else if (shiftVal > T::Scalar::bits) {
+ } else if (shiftVal > Scalar<T>::bits(kind)) {
context.messages().Say(
"SHIFT=%jd count for %s is greater than %d"_err_en_US,
- std::intmax_t{shiftVal}, name, T::Scalar::bits);
+ std::intmax_t{shiftVal}, name, Scalar<T>::bits(kind));
break;
}
}
}
- return FoldElementalIntrinsic<T, T, Int4>(context, std::move(funcRef),
+ return FoldElementalIntrinsic<T, T, Int4>(kind, {kind, 4}, context,
+ std::move(funcRef),
ScalarFunc<T, T, Int4>(
[&](const Scalar<T> &i, const Scalar<Int4> &shift) -> Scalar<T> {
return std::invoke(fptr, i, static_cast<int>(shift.ToInt64()));
@@ -963,22 +994,21 @@ std::optional<Expr<T>> FoldIntrinsicFunctionCommon(
return std::nullopt;
}
-template <int KIND>
-Expr<Type<TypeCategory::Integer, KIND>> FoldIntrinsicFunction(
- FoldingContext &context,
- FunctionRef<Type<TypeCategory::Integer, KIND>> &&funcRef) {
+Expr<Type<TypeCategory::Integer>> FoldIntrinsicFunction(FoldingContext &context,
+ FunctionRef<Type<TypeCategory::Integer>> &&funcRef) {
+ using T = Type<TypeCategory::Integer>;
+ const int kind{funcRef.kind()};
if (auto foldedCommon{FoldIntrinsicFunctionCommon(context, funcRef)}) {
return std::move(*foldedCommon);
}
- using T = Type<TypeCategory::Integer, KIND>;
ActualArguments &args{funcRef.arguments()};
auto *intrinsic{std::get_if<SpecificIntrinsic>(&funcRef.proc().u)};
CHECK(intrinsic);
std::string name{intrinsic->name};
- auto FromInt64{[&name, &context](std::int64_t n) {
- Scalar<T> result{n};
+ auto FromInt64{[&name, &context, kind](std::int64_t n) {
+ Scalar<T> result{kind, n};
if (result.ToInt64() != n) {
context.Warn(common::UsageWarning::FoldingException,
"Result of intrinsic function '%s' (%jd) overflows its result type"_warn_en_US,
@@ -988,12 +1018,13 @@ Expr<Type<TypeCategory::Integer, KIND>> FoldIntrinsicFunction(
}};
if (name == "abs") { // incl. babs, iiabs, jiaabs, & kiabs
- return FoldElementalIntrinsic<T, T>(context, std::move(funcRef),
- ScalarFunc<T, T>([&context](const Scalar<T> &i) -> Scalar<T> {
+ return FoldElementalIntrinsic<T, T>(kind, {kind}, context,
+ std::move(funcRef),
+ ScalarFunc<T, T>([&context, kind](const Scalar<T> &i) -> Scalar<T> {
typename Scalar<T>::ValueWithOverflow j{i.ABS()};
if (j.overflow) {
context.Warn(common::UsageWarning::FoldingException,
- "abs(integer(kind=%d)) folding overflowed"_warn_en_US, KIND);
+ "abs(integer(kind=%d)) folding overflowed"_warn_en_US, kind);
}
return j.value;
}));
@@ -1006,9 +1037,9 @@ Expr<Type<TypeCategory::Integer, KIND>> FoldIntrinsicFunction(
return common::visit(
[&](const auto &kx) {
using TR = ResultType<decltype(kx)>;
- return FoldElementalIntrinsic<T, TR>(context, std::move(funcRef),
- ScalarFunc<T, TR>([&](const Scalar<TR> &x) {
- auto y{x.template ToInteger<Scalar<T>>(mode)};
+ return FoldElementalIntrinsic<T, TR>(kind, {kx.kind()}, context,
+ std::move(funcRef), ScalarFunc<T, TR>([&](const Scalar<TR> &x) {
+ auto y{x.ToInteger(mode, Scalar<T>::bits(kind))};
if (y.flags.test(RealFlag::Overflow)) {
context.Warn(common::UsageWarning::FoldingException,
"%s intrinsic folding overflow"_warn_en_US, name);
@@ -1019,20 +1050,10 @@ Expr<Type<TypeCategory::Integer, KIND>> FoldIntrinsicFunction(
cx->u);
}
} else if (name == "count") {
- int maskKind = args[0]->GetType()->kind();
- switch (maskKind) {
- SWITCH_COVERS_ALL_CASES
- case 1:
- return FoldCount<T, 1>(context, std::move(funcRef));
- case 2:
- return FoldCount<T, 2>(context, std::move(funcRef));
- case 4:
- return FoldCount<T, 4>(context, std::move(funcRef));
- case 8:
- return FoldCount<T, 8>(context, std::move(funcRef));
- }
+ return FoldCount<T>(context, std::move(funcRef));
} else if (name == "dim") {
- return FoldElementalIntrinsic<T, T, T>(context, std::move(funcRef),
+ return FoldElementalIntrinsic<T, T, T>(kind, {kind, kind}, context,
+ std::move(funcRef),
ScalarFunc<T, T, T>(
[&context](const Scalar<T> &x, const Scalar<T> &y) -> Scalar<T> {
auto result{x.DIM(y)};
@@ -1045,10 +1066,10 @@ Expr<Type<TypeCategory::Integer, KIND>> FoldIntrinsicFunction(
} else if (name == "exponent") {
if (auto *sx{UnwrapExpr<Expr<SomeReal>>(args[0])}) {
return common::visit(
- [&funcRef, &context](const auto &x) -> Expr<T> {
+ [&funcRef, &context, kind](const auto &x) -> Expr<T> {
using TR = typename std::decay_t<decltype(x)>::Result;
- return FoldElementalIntrinsic<T, TR>(context, std::move(funcRef),
- &Scalar<TR>::template EXPONENT<Scalar<T>>);
+ return FoldElementalIntrinsic<T, TR>(kind, {x.kind()}, context,
+ std::move(funcRef), &Scalar<TR>::EXPONENT);
},
sx->u);
} else {
@@ -1057,7 +1078,7 @@ Expr<Type<TypeCategory::Integer, KIND>> FoldIntrinsicFunction(
} else if (name == "findloc") {
return FoldLocation<WhichLocation::Findloc, T>(context, std::move(funcRef));
} else if (name == "huge") {
- return Expr<T>{Scalar<T>::HUGE()};
+ return MakeConstantExpr<T>(kind, Scalar<T>::HUGE(kind));
} else if (name == "iachar" || name == "ichar") {
auto *someChar{UnwrapExpr<Expr<SomeCharacter>>(args[0])};
CHECK(someChar);
@@ -1074,16 +1095,15 @@ Expr<Type<TypeCategory::Integer, KIND>> FoldIntrinsicFunction(
name);
}
return common::visit(
- [&funcRef, &context, &FromInt64](const auto &str) -> Expr<T> {
+ [&funcRef, &context, &FromInt64, kind](const auto &str) -> Expr<T> {
using Char = typename std::decay_t<decltype(str)>::Result;
- (void)FromInt64;
- return FoldElementalIntrinsic<T, Char>(context,
- std::move(funcRef),
+ return FoldElementalIntrinsic<T, Char>(kind, {str.kind()},
+ context, std::move(funcRef),
ScalarFunc<T, Char>(
#ifndef _MSC_VER
[&FromInt64](const Scalar<Char> &c) {
- return FromInt64(CharacterUtils<Char::kind>::ICHAR(
- CharacterUtils<Char::kind>::Resize(c, 1)));
+ return FromInt64(CharacterUtils::ICHAR(
+ CharacterUtils::Resize(c, 1)));
}));
#else // _MSC_VER
// MSVC 14 get confused by the original code above and
@@ -1092,9 +1112,10 @@ Expr<Type<TypeCategory::Integer, KIND>> FoldIntrinsicFunction(
// CharacterUtils<2>::ICHAR(). Can't find a work-around,
// so remove the FromInt64 error checking lambda that
// seems to have caused the proble.
- [](const Scalar<Char> &c) {
- return CharacterUtils<Char::kind>::ICHAR(
- CharacterUtils<Char::kind>::Resize(c, 1));
+ [kind](const Scalar<Char> &c) -> Scalar<T> {
+ return Scalar<T>{kind,
+ CharacterUtils::ICHAR(
+ CharacterUtils::Resize(c, 1))};
}));
#endif // _MSC_VER
},
@@ -1106,33 +1127,34 @@ Expr<Type<TypeCategory::Integer, KIND>> FoldIntrinsicFunction(
return common::visit(
[&](const auto &kch) -> Expr<T> {
using TC = typename std::decay_t<decltype(kch)>::Result;
+ const int kchKind{kch.kind()};
if (UnwrapExpr<Expr<SomeLogical>>(args[2])) { // BACK=
- return FoldElementalIntrinsic<T, TC, TC, LogicalResult>(context,
+ return FoldElementalIntrinsic<T, TC, TC, LogicalResult>(kind,
+ {kchKind, kchKind, LogicalResultKind}, context,
std::move(funcRef),
ScalarFunc<T, TC, TC, LogicalResult>{
[&name, &FromInt64](const Scalar<TC> &str,
const Scalar<TC> &other,
const Scalar<LogicalResult> &back) {
return FromInt64(name == "index"
- ? CharacterUtils<TC::kind>::INDEX(
- str, other, back.IsTrue())
- : name == "scan"
- ? CharacterUtils<TC::kind>::SCAN(
+ ? CharacterUtils::INDEX(
str, other, back.IsTrue())
- : CharacterUtils<TC::kind>::VERIFY(
- str, other, back.IsTrue()));
+ : name == "scan" ? CharacterUtils::SCAN(str,
+ other, back.IsTrue())
+ : CharacterUtils::VERIFY(str,
+ other, back.IsTrue()));
}});
} else {
- return FoldElementalIntrinsic<T, TC, TC>(context,
- std::move(funcRef),
+ return FoldElementalIntrinsic<T, TC, TC>(kind, {kchKind, kchKind},
+ context, std::move(funcRef),
ScalarFunc<T, TC, TC>{
[&name, &FromInt64](
const Scalar<TC> &str, const Scalar<TC> &other) {
return FromInt64(name == "index"
- ? CharacterUtils<TC::kind>::INDEX(str, other)
+ ? CharacterUtils::INDEX(str, other)
: name == "scan"
- ? CharacterUtils<TC::kind>::SCAN(str, other)
- : CharacterUtils<TC::kind>::VERIFY(str, other));
+ ? CharacterUtils::SCAN(str, other)
+ : CharacterUtils::VERIFY(str, other));
}});
}
},
@@ -1141,7 +1163,7 @@ Expr<Type<TypeCategory::Integer, KIND>> FoldIntrinsicFunction(
DIE("first argument must be CHARACTER");
}
} else if (name == "int_ptr_kind") {
- return Expr<T>{8};
+ return MakeConstantExpr<T>(kind, 8);
} else if (name == "kind") {
// FoldOperation(FunctionRef &&) in fold-implementation.h will not
// have folded the argument; in the case of TypeParamInquiry,
@@ -1152,12 +1174,12 @@ Expr<Type<TypeCategory::Integer, KIND>> FoldIntrinsicFunction(
if (const auto *intrinType{typeSpec->AsIntrinsic()}) {
if (auto k{ToInt64(Fold(
context, Expr<SubscriptInteger>{intrinType->kind()}))}) {
- return Expr<T>{*k};
+ return MakeConstantExpr<T>(kind, *k);
}
}
}
} else if (auto dyType{expr->GetType()}) {
- return Expr<T>{dyType->kind()};
+ return MakeConstantExpr<T>(kind, dyType->kind());
}
}
} else if (name == "lbound") {
@@ -1168,12 +1190,13 @@ Expr<Type<TypeCategory::Integer, KIND>> FoldIntrinsicFunction(
name == "popcnt") {
if (auto *sn{UnwrapExpr<Expr<SomeKind<T::category>>>(args[0])}) {
return common::visit(
- [&funcRef, &context, &name](const auto &n) -> Expr<T> {
+ [&funcRef, &context, &name, kind](const auto &n) -> Expr<T> {
using TI = typename std::decay_t<decltype(n)>::Result;
if (name == "poppar") {
- return FoldElementalIntrinsic<T, TI>(context, std::move(funcRef),
- ScalarFunc<T, TI>([](const Scalar<TI> &i) -> Scalar<T> {
- return Scalar<T>{i.POPPAR() ? 1 : 0};
+ return FoldElementalIntrinsic<T, TI>(kind, {n.kind()}, context,
+ std::move(funcRef),
+ ScalarFunc<T, TI>([kind](const Scalar<TI> &i) -> Scalar<T> {
+ return Scalar<T>{kind, i.POPPAR() ? 1 : 0};
}));
}
auto fptr{&Scalar<TI>::LEADZ};
@@ -1186,12 +1209,13 @@ Expr<Type<TypeCategory::Integer, KIND>> FoldIntrinsicFunction(
common::die(
"missing case to fold intrinsic function %s", name.c_str());
}
- return FoldElementalIntrinsic<T, TI>(context, std::move(funcRef),
+ return FoldElementalIntrinsic<T, TI>(kind, {n.kind()}, context,
+ std::move(funcRef),
// `i` should be declared as `const Scalar<TI>&`.
// We declare it as `auto` to workaround an msvc bug:
// https://developercommunity.visualstudio.com/t/Regression:-nested-closure-assumes-wrong/10130223
- ScalarFunc<T, TI>([&fptr](const auto &i) -> Scalar<T> {
- return Scalar<T>{std::invoke(fptr, i)};
+ ScalarFunc<T, TI>([&fptr, kind](const auto &i) -> Scalar<T> {
+ return Scalar<T>{kind, std::invoke(fptr, i)};
}));
},
sn->u);
@@ -1204,7 +1228,7 @@ Expr<Type<TypeCategory::Integer, KIND>> FoldIntrinsicFunction(
[&](auto &kx) {
if (auto len{kx.LEN()}) {
if (IsScopeInvariantExpr(*len, &context)) {
- return Fold(context, ConvertToType<T>(*std::move(len)));
+ return Fold(context, ConvertToType<T>(kind, *std::move(len)));
} else {
return Expr<T>{std::move(funcRef)};
}
@@ -1221,9 +1245,10 @@ Expr<Type<TypeCategory::Integer, KIND>> FoldIntrinsicFunction(
return common::visit(
[&](const auto &kch) -> Expr<T> {
using TC = typename std::decay_t<decltype(kch)>::Result;
- return FoldElementalIntrinsic<T, TC>(context, std::move(funcRef),
+ return FoldElementalIntrinsic<T, TC>(kind, {kch.kind()}, context,
+ std::move(funcRef),
ScalarFunc<T, TC>{[&FromInt64](const Scalar<TC> &str) {
- return FromInt64(CharacterUtils<TC::kind>::LEN_TRIM(str));
+ return FromInt64(CharacterUtils::LEN_TRIM(str));
}});
},
charExpr->u);
@@ -1235,9 +1260,9 @@ Expr<Type<TypeCategory::Integer, KIND>> FoldIntrinsicFunction(
} else if (name == "maxexponent") {
if (auto *sx{UnwrapExpr<Expr<SomeReal>>(args[0])}) {
return common::visit(
- [](const auto &x) {
+ [&](const auto &x) {
using TR = typename std::decay_t<decltype(x)>::Result;
- return Expr<T>{Scalar<TR>::MAXEXPONENT};
+ return MakeConstantExpr<T>(kind, Scalar<TR>::MAXEXPONENT(x.kind()));
},
sx->u);
}
@@ -1248,9 +1273,9 @@ Expr<Type<TypeCategory::Integer, KIND>> FoldIntrinsicFunction(
} else if (name == "minexponent") {
if (auto *sx{UnwrapExpr<Expr<SomeReal>>(args[0])}) {
return common::visit(
- [](const auto &x) {
+ [&](const auto &x) {
using TR = typename std::decay_t<decltype(x)>::Result;
- return Expr<T>{Scalar<TR>::MINEXPONENT};
+ return MakeConstantExpr<T>(kind, Scalar<TR>::MINEXPONENT(x.kind()));
},
sx->u);
}
@@ -1267,7 +1292,8 @@ Expr<Type<TypeCategory::Integer, KIND>> FoldIntrinsicFunction(
badPConst = true;
}
}
- return FoldElementalIntrinsic<T, T, T>(context, std::move(funcRef),
+ return FoldElementalIntrinsic<T, T, T>(kind, {kind, kind}, context,
+ std::move(funcRef),
ScalarFuncWithContext<T, T, T>(
[badPConst](FoldingContext &context, const Scalar<T> &x,
const Scalar<T> &y) -> Scalar<T> {
@@ -1292,7 +1318,8 @@ Expr<Type<TypeCategory::Integer, KIND>> FoldIntrinsicFunction(
badPConst = true;
}
}
- return FoldElementalIntrinsic<T, T, T>(context, std::move(funcRef),
+ return FoldElementalIntrinsic<T, T, T>(kind, {kind, kind}, context,
+ std::move(funcRef),
ScalarFuncWithContext<T, T, T>([badPConst](FoldingContext &context,
const Scalar<T> &x,
const Scalar<T> &y) -> Scalar<T> {
@@ -1305,43 +1332,55 @@ Expr<Type<TypeCategory::Integer, KIND>> FoldIntrinsicFunction(
}));
} else if (name == "precision") {
if (const auto *cx{UnwrapExpr<Expr<SomeReal>>(args[0])}) {
- return Expr<T>{common::visit(
- [](const auto &kx) {
- return Scalar<ResultType<decltype(kx)>>::PRECISION;
- },
- cx->u)};
+ return MakeConstantExpr<T>(kind,
+ common::visit(
+ [](const auto &kx) -> std::int64_t {
+ using KX = ResultType<decltype(kx)>;
+ return Scalar<KX>::PRECISION(kx.kind());
+ },
+ cx->u));
} else if (const auto *cx{UnwrapExpr<Expr<SomeComplex>>(args[0])}) {
- return Expr<T>{common::visit(
- [](const auto &kx) {
- return Scalar<typename ResultType<decltype(kx)>::Part>::PRECISION;
- },
- cx->u)};
+ return MakeConstantExpr<T>(kind,
+ common::visit(
+ [](const auto &kx) -> std::int64_t {
+ using Part = typename ResultType<decltype(kx)>::Part;
+ return Scalar<Part>::PRECISION(kx.kind());
+ },
+ cx->u));
}
} else if (name == "range") {
if (const auto *cx{UnwrapExpr<Expr<SomeInteger>>(args[0])}) {
- return Expr<T>{common::visit(
- [](const auto &kx) {
- return Scalar<ResultType<decltype(kx)>>::RANGE;
- },
- cx->u)};
+ return MakeConstantExpr<T>(kind,
+ common::visit(
+ [](const auto &kx) -> std::int64_t {
+ using KX = ResultType<decltype(kx)>;
+ return Scalar<KX>::RANGE(kx.kind());
+ },
+ cx->u));
} else if (const auto *cx{UnwrapExpr<Expr<SomeUnsigned>>(args[0])}) {
- return Expr<T>{common::visit(
- [](const auto &kx) {
- return Scalar<ResultType<decltype(kx)>>::UnsignedRANGE;
- },
- cx->u)};
+ return MakeConstantExpr<T>(kind,
+ common::visit(
+ [](const auto &kx) -> std::int64_t {
+ using KX = ResultType<decltype(kx)>;
+ return Scalar<KX>::UnsignedRANGE(kx.kind());
+ },
+ cx->u));
} else if (const auto *cx{UnwrapExpr<Expr<SomeReal>>(args[0])}) {
- return Expr<T>{common::visit(
- [](const auto &kx) {
- return Scalar<ResultType<decltype(kx)>>::RANGE;
- },
- cx->u)};
+ return MakeConstantExpr<T>(kind,
+ common::visit(
+ [](const auto &kx) -> std::int64_t {
+ using KX = ResultType<decltype(kx)>;
+ return Scalar<KX>::RANGE(kx.kind());
+ },
+ cx->u));
} else if (const auto *cx{UnwrapExpr<Expr<SomeComplex>>(args[0])}) {
- return Expr<T>{common::visit(
- [](const auto &kx) {
- return Scalar<typename ResultType<decltype(kx)>::Part>::RANGE;
- },
- cx->u)};
+ return MakeConstantExpr<T>(kind,
+ common::visit(
+ [](const auto &kx) -> std::int64_t {
+ using Part = typename ResultType<decltype(kx)>::Part;
+ return Scalar<Part>::RANGE(kx.kind());
+ },
+ cx->u));
}
} else if (name == "rank") {
if (args[0]) {
@@ -1352,55 +1391,58 @@ Expr<Type<TypeCategory::Integer, KIND>> FoldIntrinsicFunction(
symbol = args[0]->GetAssumedTypeDummy();
}
if (symbol && IsAssumedRank(*symbol)) {
- // DescriptorInquiry can only be placed in expression of kind
- // DescriptorInquiry::Result::kind.
- return ConvertToType<T>(
- Expr<Type<TypeCategory::Integer, DescriptorInquiry::Result::kind>>{
- DescriptorInquiry{
- NamedEntity{*symbol}, DescriptorInquiry::Field::Rank}});
+ return ConvertToType<T>(kind,
+ Expr<DescriptorInquiry::Result>{DescriptorInquiry{
+ NamedEntity{*symbol}, DescriptorInquiry::Field::Rank}});
}
- return Expr<T>{args[0]->Rank()};
+ return MakeConstantExpr<T>(kind, args[0]->Rank());
}
} else if (name == "selected_char_kind") {
- if (const auto *chCon{UnwrapExpr<Constant<TypeOf<std::string>>>(args[0])}) {
- if (std::optional<std::string> value{chCon->GetScalarValue()}) {
+ if (const auto *chCon{
+ UnwrapExpr<Constant<Type<TypeCategory::Character>>>(args[0])}) {
+ if (std::optional<value::CharacterValue> charVal{
+ chCon->GetScalarValue()}) {
int defaultKind{
context.defaults().GetDefaultKind(TypeCategory::Character)};
- return Expr<T>{SelectedCharKind(*value, defaultKind)};
+ return MakeConstantExpr<T>(
+ kind, SelectedCharKind(*charVal->AsStdString(), defaultKind));
}
}
} else if (name == "selected_int_kind" || name == "selected_unsigned_kind") {
if (auto p{ToInt64(args[0])}) {
- return Expr<T>{context.targetCharacteristics().SelectedIntKind(*p)};
+ return MakeConstantExpr<T>(
+ kind, context.targetCharacteristics().SelectedIntKind(*p));
}
} else if (name == "selected_logical_kind") {
if (auto p{ToInt64(args[0])}) {
- return Expr<T>{context.targetCharacteristics().SelectedLogicalKind(*p)};
+ return MakeConstantExpr<T>(
+ kind, context.targetCharacteristics().SelectedLogicalKind(*p));
}
} else if (name == "selected_real_kind" ||
name == "__builtin_ieee_selected_real_kind") {
if (auto p{GetInt64ArgOr(args[0], 0)}) {
if (auto r{GetInt64ArgOr(args[1], 0)}) {
if (auto radix{GetInt64ArgOr(args[2], 2)}) {
- return Expr<T>{
- context.targetCharacteristics().SelectedRealKind(*p, *r, *radix)};
+ return MakeConstantExpr<T>(kind,
+ context.targetCharacteristics().SelectedRealKind(*p, *r, *radix));
}
}
}
} else if (name == "shape") {
if (auto shape{GetContextFreeShape(context, args[0])}) {
if (auto shapeExpr{AsExtentArrayExpr(*shape)}) {
- return Fold(context, ConvertToType<T>(std::move(*shapeExpr)));
+ return Fold(context, ConvertToType<T>(kind, std::move(*shapeExpr)));
}
}
} else if (name == "sign") {
- return FoldElementalIntrinsic<T, T, T>(context, std::move(funcRef),
- ScalarFunc<T, T, T>([&context](const Scalar<T> &j,
+ return FoldElementalIntrinsic<T, T, T>(kind, {kind, kind}, context,
+ std::move(funcRef),
+ ScalarFunc<T, T, T>([&context, kind](const Scalar<T> &j,
const Scalar<T> &k) -> Scalar<T> {
typename Scalar<T>::ValueWithOverflow result{j.SIGN(k)};
if (result.overflow) {
context.Warn(common::UsageWarning::FoldingException,
- "sign(integer(kind=%d)) folding overflowed"_warn_en_US, KIND);
+ "sign(integer(kind=%d)) folding overflowed"_warn_en_US, kind);
}
return result.value;
}));
@@ -1413,31 +1455,34 @@ Expr<Type<TypeCategory::Integer, KIND>> FoldIntrinsicFunction(
return MakeInvalidIntrinsic<T>(std::move(funcRef));
} else if (dim) {
if (auto &extent{shape->at(*dim)}) {
- return Fold(context, ConvertToType<T>(std::move(*extent)));
+ return Fold(context, ConvertToType<T>(kind, std::move(*extent)));
}
}
} else if (auto extents{common::AllElementsPresent(std::move(*shape))}) {
// DIM= is absent; compute PRODUCT(SHAPE())
- ExtentExpr product{1};
+ ExtentExpr product{MakeExtentExpr(1)};
for (auto &&extent : std::move(*extents)) {
product = std::move(product) * std::move(extent);
}
- return Expr<T>{ConvertToType<T>(Fold(context, std::move(product)))};
+ return Expr<T>{
+ ConvertToType<T>(kind, Fold(context, std::move(product)))};
}
}
} else if (name == "sizeof") { // in bytes; extension
if (auto info{
characteristics::TypeAndShape::Characterize(args[0], context)}) {
if (auto bytes{info->MeasureSizeInBytes(context)}) {
- return Expr<T>{Fold(context, ConvertToType<T>(std::move(*bytes)))};
+ return Expr<T>{
+ Fold(context, ConvertToType<T>(kind, std::move(*bytes)))};
}
}
} else if (name == "storage_size") { // in bits
if (auto info{
characteristics::TypeAndShape::Characterize(args[0], context)}) {
if (auto bytes{info->MeasureElementSizeInBytes(context, true)}) {
- return Expr<T>{
- Fold(context, Expr<T>{8} * ConvertToType<T>(std::move(*bytes)))};
+ return Expr<T>{Fold(context,
+ MakeConstantExpr<T>(kind, 8) *
+ ConvertToType<T>(kind, std::move(*bytes)))};
}
}
} else if (name == "ubound") {
@@ -1463,26 +1508,26 @@ Expr<Type<TypeCategory::Integer, KIND>> FoldIntrinsicFunction(
common::UsageWarning::FoldingValueChecks, *context.moduleFileName(),
"NUMERIC_STORAGE_SIZE from ISO_FORTRAN_ENV is not well-defined when default INTEGER and REAL are not consistent due to compiler options"_warn_en_US);
}
- return Expr<T>{8 * std::min(intBytes, realBytes)};
+ return MakeConstantExpr<T>(kind, 8 * std::min(intBytes, realBytes));
}
}
return Expr<T>{std::move(funcRef)};
}
-template <int KIND>
-Expr<Type<TypeCategory::Unsigned, KIND>> FoldIntrinsicFunction(
+Expr<Type<TypeCategory::Unsigned>> FoldIntrinsicFunction(
FoldingContext &context,
- FunctionRef<Type<TypeCategory::Unsigned, KIND>> &&funcRef) {
+ FunctionRef<Type<TypeCategory::Unsigned>> &&funcRef) {
+ using T = Type<TypeCategory::Unsigned>;
+ const int kind{funcRef.kind()};
if (auto foldedCommon{FoldIntrinsicFunctionCommon(context, funcRef)}) {
return std::move(*foldedCommon);
}
- using T = Type<TypeCategory::Unsigned, KIND>;
ActualArguments &args{funcRef.arguments()};
auto *intrinsic{std::get_if<SpecificIntrinsic>(&funcRef.proc().u)};
CHECK(intrinsic);
std::string name{intrinsic->name};
if (name == "huge") {
- return Expr<T>{Scalar<T>{}.NOT()};
+ return MakeConstantExpr<T>(kind, Scalar<T>::Zero(kind).NOT());
} else if (name == "mod" || name == "modulo") {
bool badPConst{false};
if (auto *pExpr{UnwrapExpr<Expr<T>>(args[1])}) {
@@ -1494,7 +1539,8 @@ Expr<Type<TypeCategory::Unsigned, KIND>> FoldIntrinsicFunction(
badPConst = true;
}
}
- return FoldElementalIntrinsic<T, T, T>(context, std::move(funcRef),
+ return FoldElementalIntrinsic<T, T, T>(kind, {kind, kind}, context,
+ std::move(funcRef),
ScalarFuncWithContext<T, T, T>(
[badPConst, &name](FoldingContext &context, const Scalar<T> &x,
const Scalar<T> &y) -> Scalar<T> {
@@ -1528,7 +1574,8 @@ Expr<TypeParamInquiry::Result> FoldOperation(
if (paramExpr && IsConstantExpr(*paramExpr, &context)) {
Expr<SomeInteger> intExpr{*paramExpr};
return Fold(context,
- ConvertToType<TypeParamInquiry::Result>(std::move(intExpr)));
+ ConvertToType<TypeParamInquiry::Result>(
+ TypeParamInquiry::ResultKind, std::move(intExpr)));
}
}
}
@@ -1550,7 +1597,8 @@ Expr<TypeParamInquiry::Result> FoldOperation(
(!isLen || ToInt64(*initExpr))) {
Expr<SomeInteger> expr{*initExpr};
return Fold(context,
- ConvertToType<TypeParamInquiry::Result>(std::move(expr)));
+ ConvertToType<TypeParamInquiry::Result>(
+ TypeParamInquiry::ResultKind, std::move(expr)));
}
}
}
@@ -1559,6 +1607,7 @@ Expr<TypeParamInquiry::Result> FoldOperation(
if (value->isExplicit()) {
auto folded{Fold(context,
AsExpr(ConvertToType<TypeParamInquiry::Result>(
+ TypeParamInquiry::ResultKind,
Expr<SomeInteger>{value->GetExplicit().value()})))};
if (!isLen || ToInt64(folded)) {
return folded;
@@ -1578,7 +1627,8 @@ Expr<RankOneBoundElement::Result> FoldOperation(
// Base is a constant array; extract the element at dimension_ (0-based).
ConstantSubscripts at{c->lbounds()};
at[0] = c->lbounds()[0] + x.dimension();
- return Expr<ResultType>{Constant<ResultType>{c->At(at)}};
+ return MakeConstantExpr<ResultType>(
+ RankOneBoundElement::ResultKind, c->At(at));
}
return Expr<ResultType>{
RankOneBoundElement{std::move(folded), x.dimension()}};
diff --git a/flang/lib/Evaluate/fold-logical.cpp b/flang/lib/Evaluate/fold-logical.cpp
index ab8c5876a13f5..f8fe642f38536 100644
--- a/flang/lib/Evaluate/fold-logical.cpp
+++ b/flang/lib/Evaluate/fold-logical.cpp
@@ -18,25 +18,28 @@ template <typename T>
static std::optional<Expr<SomeType>> ZeroExtend(const Constant<T> &c) {
std::vector<Scalar<LargestInt>> exts;
for (const auto &v : c.values()) {
- exts.push_back(Scalar<LargestInt>::ConvertUnsigned(v).value);
+ exts.push_back(Scalar<LargestInt>::ConvertUnsigned(
+ v, Scalar<LargestInt>::bits(LargestIntKind))
+ .value);
}
- return AsGenericExpr(
- Constant<LargestInt>(std::move(exts), ConstantSubscripts(c.shape())));
+ return AsGenericExpr(Constant<LargestInt>{
+ LargestIntKind, std::move(exts), ConstantSubscripts{c.shape()}});
}
// for ALL, ANY & PARITY
template <typename T>
-static Expr<T> FoldAllAnyParity(FoldingContext &context, FunctionRef<T> &&ref,
+static Expr<T> FoldAllAnyParity(int kind, FoldingContext &context,
+ FunctionRef<T> &&ref,
Scalar<T> (Scalar<T>::*operation)(const Scalar<T> &) const,
Scalar<T> identity) {
static_assert(T::category == TypeCategory::Logical);
std::optional<int> dim;
if (std::optional<ArrayAndMask<T>> arrayAndMask{
- ProcessReductionArgs<T>(context, ref.arguments(), dim,
+ ProcessReductionArgs<T>(kind, context, ref.arguments(), dim,
/*ARRAY(MASK)=*/0, /*DIM=*/1)}) {
OperationAccumulator accumulator{arrayAndMask->array, operation};
- return Expr<T>{DoReduction<T>(
- arrayAndMask->array, arrayAndMask->mask, dim, identity, accumulator)};
+ return Expr<T>{DoReduction<T>(kind, arrayAndMask->array, arrayAndMask->mask,
+ dim, identity, accumulator)};
}
return Expr<T>{std::move(ref)};
}
@@ -46,22 +49,22 @@ static Expr<T> FoldAllAnyParity(FoldingContext &context, FunctionRef<T> &&ref,
// are constant. It is guaranteed that 'x' is evaluated at most once.
// TODO: unsigned
-template <int X_RKIND, int MOLD_IKIND>
-Expr<SomeReal> RealToIntBoundHelper(bool round, bool negate) {
- using RType = Type<TypeCategory::Real, X_RKIND>;
- using RealType = Scalar<RType>;
- using IntType = Scalar<Type<TypeCategory::Integer, MOLD_IKIND>>;
- RealType result{}; // 0.
+static Expr<SomeReal> RealToIntBound(
+ int xRKind, int moldIKind, bool round, bool negate) {
+ using RealType = Scalar<Type<TypeCategory::Real>>;
+ using IntType = Scalar<Type<TypeCategory::Integer>>;
+ RealType result{RealType::Zero(xRKind)}; // 0.
common::RoundingMode roundingMode{round
? common::RoundingMode::TiesAwayFromZero
: common::RoundingMode::ToZero};
// Add decreasing powers of two to the result to find the largest magnitude
// value that can be converted to the integer type without overflow.
- RealType at{RealType::FromInteger(IntType{negate ? -1 : 1}).value};
+ RealType at{
+ RealType::FromInteger(xRKind, IntType{moldIKind, negate ? -1 : 1}).value};
bool decrement{true};
- while (!at.template ToInteger<IntType>(roundingMode)
- .flags.test(RealFlag::Overflow)) {
- auto tmp{at.SCALE(IntType{1})};
+ while (!at.ToInteger(roundingMode, IntType::bits(moldIKind))
+ .flags.test(RealFlag::Overflow)) {
+ auto tmp{at.SCALE(IntType{moldIKind, 1})};
if (tmp.flags.test(RealFlag::Overflow)) {
decrement = false;
break;
@@ -70,64 +73,20 @@ Expr<SomeReal> RealToIntBoundHelper(bool round, bool negate) {
}
while (true) {
if (decrement) {
- at = at.SCALE(IntType{-1}).value;
+ at = at.SCALE(IntType{moldIKind, -1}).value;
} else {
decrement = true;
}
auto tmp{at.Add(result)};
if (tmp.flags.test(RealFlag::Inexact)) {
break;
- } else if (!tmp.value.template ToInteger<IntType>(roundingMode)
- .flags.test(RealFlag::Overflow)) {
+ } else if (!tmp.value.ToInteger(roundingMode, IntType::bits(moldIKind))
+ .flags.test(RealFlag::Overflow)) {
result = tmp.value;
}
}
- return AsCategoryExpr(Constant<RType>{std::move(result)});
-}
-
-static Expr<SomeReal> RealToIntBound(
- int xRKind, int moldIKind, bool round, bool negate) {
- switch (xRKind) {
-#define ICASES(RK) \
- switch (moldIKind) { \
- case 1: \
- return RealToIntBoundHelper<RK, 1>(round, negate); \
- break; \
- case 2: \
- return RealToIntBoundHelper<RK, 2>(round, negate); \
- break; \
- case 4: \
- return RealToIntBoundHelper<RK, 4>(round, negate); \
- break; \
- case 8: \
- return RealToIntBoundHelper<RK, 8>(round, negate); \
- break; \
- case 16: \
- return RealToIntBoundHelper<RK, 16>(round, negate); \
- break; \
- } \
- break
- case 2:
- ICASES(2);
- break;
- case 3:
- ICASES(3);
- break;
- case 4:
- ICASES(4);
- break;
- case 8:
- ICASES(8);
- break;
- case 10:
- ICASES(10);
- break;
- case 16:
- ICASES(16);
- break;
- }
- DIE("RealToIntBound: no case");
-#undef ICASES
+ return AsCategoryExpr(
+ Constant<Type<TypeCategory::Real>>{xRKind, std::move(result)});
}
class RealToIntLimitHelper {
@@ -137,9 +96,9 @@ class RealToIntLimitHelper {
RealToIntLimitHelper(
FoldingContext &context, Expr<SomeReal> &&hi, Expr<SomeReal> &lo)
: context_{context}, hi_{std::move(hi)}, lo_{lo} {}
- template <typename T> Result Test() {
- if (UnwrapExpr<Expr<T>>(hi_)) {
- bool promote{T::kind < 16};
+ template <typename T> Result Test(int kind) {
+ if (UnwrapExpr<Expr<T>>(kind, hi_)) {
+ bool promote{kind < 16};
Result constResult;
if (auto hiV{GetScalarConstantValue<T>(hi_)}) {
auto loV{GetScalarConstantValue<T>(lo_)};
@@ -148,13 +107,15 @@ class RealToIntLimitHelper {
promote = promote &&
(diff.flags.test(RealFlag::Overflow) ||
diff.flags.test(RealFlag::Inexact));
- constResult = AsCategoryExpr(Constant<T>{std::move(diff.value)});
+ constResult = AsCategoryExpr(Constant<T>{kind, std::move(diff.value)});
}
if (promote) {
- constexpr int nextKind{T::kind < 4 ? 4 : T::kind == 4 ? 8 : 16};
- using T2 = Type<TypeCategory::Real, nextKind>;
- hi_ = Expr<SomeReal>{Fold(context_, ConvertToType<T2>(std::move(hi_)))};
- lo_ = Expr<SomeReal>{Fold(context_, ConvertToType<T2>(std::move(lo_)))};
+ int nextKind{kind < 4 ? 4 : kind == 4 ? 8 : 16};
+ using T2 = Type<TypeCategory::Real>;
+ hi_ = Expr<SomeReal>{
+ Fold(context_, ConvertToType<T2>(nextKind, std::move(hi_)))};
+ lo_ = Expr<SomeReal>{
+ Fold(context_, ConvertToType<T2>(nextKind, std::move(lo_)))};
if (constResult) {
// Use promoted constants on next iteration of SearchTypes
return std::nullopt;
@@ -178,84 +139,37 @@ class RealToIntLimitHelper {
static std::optional<Expr<SomeReal>> RealToIntLimit(
FoldingContext &context, Expr<SomeReal> &&hi, Expr<SomeReal> &lo) {
- return common::SearchTypes(RealToIntLimitHelper{context, std::move(hi), lo});
+ return SearchTypes(RealToIntLimitHelper{context, std::move(hi), lo});
}
// RealToRealBounds() returns a pair (HUGE(x),REAL(HUGE(mold),KIND(x)))
// when REAL(HUGE(x),KIND(mold)) overflows, and std::nullopt otherwise.
-template <int X_RKIND, int MOLD_RKIND>
-std::optional<std::pair<Expr<SomeReal>, Expr<SomeReal>>>
-RealToRealBoundsHelper() {
- using RType = Type<TypeCategory::Real, X_RKIND>;
- using RealType = Scalar<RType>;
- using MoldRealType = Scalar<Type<TypeCategory::Real, MOLD_RKIND>>;
- if (!MoldRealType::Convert(RealType::HUGE()).flags.test(RealFlag::Overflow)) {
- return std::nullopt;
- } else {
- return std::make_pair(AsCategoryExpr(Constant<RType>{
- RealType::Convert(MoldRealType::HUGE()).value}),
- AsCategoryExpr(Constant<RType>{RealType::HUGE()}));
- }
-}
-
static std::optional<std::pair<Expr<SomeReal>, Expr<SomeReal>>>
RealToRealBounds(int xRKind, int moldRKind) {
- switch (xRKind) {
-#define RCASES(RK) \
- switch (moldRKind) { \
- case 2: \
- return RealToRealBoundsHelper<RK, 2>(); \
- break; \
- case 3: \
- return RealToRealBoundsHelper<RK, 3>(); \
- break; \
- case 4: \
- return RealToRealBoundsHelper<RK, 4>(); \
- break; \
- case 8: \
- return RealToRealBoundsHelper<RK, 8>(); \
- break; \
- case 10: \
- return RealToRealBoundsHelper<RK, 10>(); \
- break; \
- case 16: \
- return RealToRealBoundsHelper<RK, 16>(); \
- break; \
- } \
- break
- case 2:
- RCASES(2);
- break;
- case 3:
- RCASES(3);
- break;
- case 4:
- RCASES(4);
- break;
- case 8:
- RCASES(8);
- break;
- case 10:
- RCASES(10);
- break;
- case 16:
- RCASES(16);
- break;
+ using RType = Type<TypeCategory::Real>;
+ using RealType = Scalar<Type<TypeCategory::Real>>;
+ using MoldRealType = Scalar<Type<TypeCategory::Real>>;
+ if (!RealType::Convert(moldRKind, RealType::HUGE(xRKind))
+ .flags.test(RealFlag::Overflow)) {
+ return std::nullopt;
+ } else {
+ return std::make_pair(
+ AsCategoryExpr(Constant<RType>{xRKind,
+ RealType::Convert(xRKind, MoldRealType::HUGE(moldRKind)).value}),
+ AsCategoryExpr(Constant<RType>{xRKind, RealType::HUGE(xRKind)}));
}
- DIE("RealToRealBounds: no case");
-#undef RCASES
}
-template <int X_IKIND, int MOLD_RKIND>
-std::optional<Expr<SomeInteger>> IntToRealBoundHelper(bool negate) {
- using IType = Type<TypeCategory::Integer, X_IKIND>;
- using IntType = Scalar<IType>;
- using RealType = Scalar<Type<TypeCategory::Real, MOLD_RKIND>>;
- IntType result{}; // 0
+static std::optional<Expr<SomeInteger>> IntToRealBoundHelper(
+ int xIKind, int moldRKind, bool negate) {
+ using IType = Type<TypeCategory::Integer>;
+ using IntType = Scalar<Type<TypeCategory::Integer>>;
+ using RealType = Scalar<Type<TypeCategory::Real>>;
+ IntType result{xIKind, 0}; // 0
while (true) {
std::optional<IntType> next;
- for (int bit{0}; bit < IntType::bits; ++bit) {
- IntType power{IntType{}.IBSET(bit)};
+ for (int bit{0}; bit < IntType::bits(xIKind); ++bit) {
+ IntType power{IntType{xIKind, 0}.IBSET(bit)};
if (power.IsNegative()) {
if (!negate) {
break;
@@ -265,7 +179,8 @@ std::optional<Expr<SomeInteger>> IntToRealBoundHelper(bool negate) {
}
auto tmp{power.AddSigned(result)};
if (tmp.overflow ||
- RealType::FromInteger(tmp.value).flags.test(RealFlag::Overflow)) {
+ RealType::FromInteger(moldRKind, tmp.value)
+ .flags.test(RealFlag::Overflow)) {
break;
}
next = tmp.value;
@@ -277,112 +192,37 @@ std::optional<Expr<SomeInteger>> IntToRealBoundHelper(bool negate) {
break;
}
}
- if (result.CompareSigned(IntType::HUGE()) == Ordering::Equal) {
+ if (result.CompareSigned(IntType::HUGE(xIKind)) == Ordering::Equal) {
return std::nullopt;
} else {
- return AsCategoryExpr(Constant<IType>{std::move(result)});
+ return AsCategoryExpr(Constant<IType>{xIKind, std::move(result)});
}
}
static std::optional<Expr<SomeInteger>> IntToRealBound(
int xIKind, int moldRKind, bool negate) {
- switch (xIKind) {
-#define RCASES(IK) \
- switch (moldRKind) { \
- case 2: \
- return IntToRealBoundHelper<IK, 2>(negate); \
- break; \
- case 3: \
- return IntToRealBoundHelper<IK, 3>(negate); \
- break; \
- case 4: \
- return IntToRealBoundHelper<IK, 4>(negate); \
- break; \
- case 8: \
- return IntToRealBoundHelper<IK, 8>(negate); \
- break; \
- case 10: \
- return IntToRealBoundHelper<IK, 10>(negate); \
- break; \
- case 16: \
- return IntToRealBoundHelper<IK, 16>(negate); \
- break; \
- } \
- break
- case 1:
- RCASES(1);
- break;
- case 2:
- RCASES(2);
- break;
- case 4:
- RCASES(4);
- break;
- case 8:
- RCASES(8);
- break;
- case 16:
- RCASES(16);
- break;
- }
- DIE("IntToRealBound: no case");
-#undef RCASES
+ return IntToRealBoundHelper(xIKind, moldRKind, negate);
}
-template <int X_IKIND, int MOLD_IKIND>
-std::optional<Expr<SomeInteger>> IntToIntBoundHelper() {
- if constexpr (X_IKIND <= MOLD_IKIND) {
+static std::optional<Expr<SomeInteger>> IntToIntBoundHelper(
+ int xIKind, int moldIKind) {
+ if (xIKind <= moldIKind) {
return std::nullopt;
} else {
- using XIType = Type<TypeCategory::Integer, X_IKIND>;
+ using XIType = Type<TypeCategory::Integer>;
using IntegerType = Scalar<XIType>;
- using MoldIType = Type<TypeCategory::Integer, MOLD_IKIND>;
+ using MoldIType = Type<TypeCategory::Integer>;
using MoldIntegerType = Scalar<MoldIType>;
- return AsCategoryExpr(Constant<XIType>{
- IntegerType::ConvertSigned(MoldIntegerType::HUGE()).value});
+ return AsCategoryExpr(Constant<XIType>{xIKind,
+ IntegerType::ConvertSigned(
+ MoldIntegerType::HUGE(moldIKind), Scalar<XIType>::bits(xIKind))
+ .value});
}
}
static std::optional<Expr<SomeInteger>> IntToIntBound(
int xIKind, int moldIKind) {
- switch (xIKind) {
-#define ICASES(IK) \
- switch (moldIKind) { \
- case 1: \
- return IntToIntBoundHelper<IK, 1>(); \
- break; \
- case 2: \
- return IntToIntBoundHelper<IK, 2>(); \
- break; \
- case 4: \
- return IntToIntBoundHelper<IK, 4>(); \
- break; \
- case 8: \
- return IntToIntBoundHelper<IK, 8>(); \
- break; \
- case 16: \
- return IntToIntBoundHelper<IK, 16>(); \
- break; \
- } \
- break
- case 1:
- ICASES(1);
- break;
- case 2:
- ICASES(2);
- break;
- case 4:
- ICASES(4);
- break;
- case 8:
- ICASES(8);
- break;
- case 16:
- ICASES(16);
- break;
- }
- DIE("IntToIntBound: no case");
-#undef ICASES
+ return IntToIntBoundHelper(xIKind, moldIKind);
}
// ApplyIntrinsic() constructs the typed expression representation
@@ -397,10 +237,10 @@ class IntrinsicCallHelper {
}
using Result = std::optional<Expr<SomeType>>;
using Types = LengthlessIntrinsicTypes;
- template <typename T> Result Test() {
+ template <typename T> Result Test(int kind) {
if (T::category == typeAndShape_->type().category() &&
- T::kind == typeAndShape_->type().kind()) {
- return AsGenericExpr(FunctionRef<T>{
+ kind == typeAndShape_->type().kind()) {
+ return AsGenericExpr(FunctionRef<T>{typeAndShape_->type().kind(),
ProcedureDesignator{std::move(call_.specificIntrinsic)},
std::move(call_.arguments)});
} else {
@@ -420,7 +260,7 @@ static Expr<SomeType> ApplyIntrinsic(
auto found{
context.intrinsics().Probe(CallCharacteristics{func}, args, context)};
CHECK(found.has_value());
- auto result{common::SearchTypes(IntrinsicCallHelper{std::move(*found)})};
+ auto result{SearchTypes(IntrinsicCallHelper{std::move(*found)})};
CHECK(result.has_value());
return *result;
}
@@ -446,8 +286,10 @@ static Expr<SomeType> IntTransferMold(
if (asVector) {
shape = ConstantSubscripts{1};
}
- Constant<SubscriptInteger> value{
- std::vector<Scalar<SubscriptInteger>>{0}, std::move(shape)};
+ Constant<SubscriptInteger> value{SubscriptIntegerKind,
+ std::vector<Scalar<SubscriptInteger>>{
+ Scalar<SubscriptInteger>{SubscriptIntegerKind, 0}},
+ std::move(shape)};
auto expr{ConvertToType(iType, AsGenericExpr(std::move(value)))};
CHECK(expr.has_value());
return std::move(*expr);
@@ -463,11 +305,11 @@ static Expr<SomeType> GetRealBits(FoldingContext &context, Expr<SomeReal> &&x) {
context.targetCharacteristics(), *xType, asVector)}});
}
-template <int KIND>
-static Expr<Type<TypeCategory::Logical, KIND>> RewriteOutOfRange(
+static Expr<Type<TypeCategory::Logical>> RewriteOutOfRange(
FoldingContext &context,
- FunctionRef<Type<TypeCategory::Logical, KIND>> &&funcRef) {
- using ResultType = Type<TypeCategory::Logical, KIND>;
+ FunctionRef<Type<TypeCategory::Logical>> &&funcRef) {
+ using ResultType = Type<TypeCategory::Logical>;
+ const int resultKind{funcRef.kind()};
ActualArguments &args{funcRef.arguments()};
// Fold x= and round= unconditionally
if (auto *x{UnwrapExpr<Expr<SomeType>>(args[0])}) {
@@ -494,11 +336,11 @@ static Expr<Type<TypeCategory::Logical, KIND>> RewriteOutOfRange(
// 'hi' is INT(HUGE(mold), KIND(x))
// OUT_OF_RANGE(x,mold) = (x + (hi + 1)) .UGT. (2*hi + 1)
auto one{DEREF(UnwrapExpr<Expr<SomeInteger>>(ConvertToType(
- xType, AsGenericExpr(Constant<SubscriptInteger>{1}))))};
+ xType, AsGenericExpr(MakeSubscriptIntConstant(1)))))};
auto lhs{std::move(*iXExpr) +
(Expr<SomeInteger>{*hi} + Expr<SomeInteger>{one})};
auto two{DEREF(UnwrapExpr<Expr<SomeInteger>>(ConvertToType(
- xType, AsGenericExpr(Constant<SubscriptInteger>{2}))))};
+ xType, AsGenericExpr(MakeSubscriptIntConstant(2)))))};
auto rhs{std::move(two) * std::move(*hi) + std::move(one)};
result = CompareUnsigned(context, "bgt",
Expr<SomeType>{std::move(lhs)}, Expr<SomeType>{std::move(rhs)});
@@ -585,7 +427,7 @@ static Expr<Type<TypeCategory::Logical, KIND>> RewriteOutOfRange(
GetRealBits(context, std::move(absR) - std::move(moldHuge))};
auto &diffBitsI{DEREF(UnwrapExpr<Expr<SomeInteger>>(diffBits))};
Expr<SomeType> decr{std::move(diffBitsI) -
- Expr<SomeInteger>{Expr<SubscriptInteger>{1}}};
+ Expr<SomeInteger>{MakeSubscriptIntExpr(1)}};
result = CompareUnsigned(context, "blt", std::move(decr),
GetRealBits(context, std::move(xHuge)));
} else {
@@ -597,13 +439,11 @@ static Expr<Type<TypeCategory::Logical, KIND>> RewriteOutOfRange(
// xType can never overflow moldType, so
// OUT_OF_RANGE(x) = (x /= 0) .AND. .FALSE.
// which has the same shape as x.
- Expr<LogicalResult> scalarFalse{
- Constant<LogicalResult>{Scalar<LogicalResult>{false}}};
+ Expr<LogicalResult> scalarFalse{MakeLogicalResultExpr(false)};
if (x->Rank() > 0) {
if (auto nez{Relate(context.messages(), RelationalOperator::NE,
- std::move(*x),
- AsGenericExpr(Constant<SubscriptInteger>{0}))}) {
- result = Expr<LogicalResult>{LogicalOperation<LogicalResult::kind>{
+ std::move(*x), AsGenericExpr(MakeSubscriptIntConstant(0)))}) {
+ result = Expr<LogicalResult>{LogicalOperation{
LogicalOperator::And, std::move(*nez), std::move(scalarFalse)}};
}
} else {
@@ -612,8 +452,8 @@ static Expr<Type<TypeCategory::Logical, KIND>> RewriteOutOfRange(
}
if (result) {
auto restorer{context.messages().DiscardMessages()};
- return Fold(
- context, AsExpr(ConvertToType<ResultType>(std::move(*result))));
+ return Fold(context,
+ AsExpr(ConvertToType<ResultType>(resultKind, std::move(*result))));
}
}
}
@@ -638,29 +478,28 @@ static std::optional<common::RoundingMode> GetRoundingMode(
return std::nullopt;
}
-template <int KIND>
-Expr<Type<TypeCategory::Logical, KIND>> FoldIntrinsicFunction(
- FoldingContext &context,
- FunctionRef<Type<TypeCategory::Logical, KIND>> &&funcRef) {
- using T = Type<TypeCategory::Logical, KIND>;
+Expr<Type<TypeCategory::Logical>> FoldIntrinsicFunction(FoldingContext &context,
+ FunctionRef<Type<TypeCategory::Logical>> &&funcRef) {
+ using T = Type<TypeCategory::Logical>;
+ const int kind{funcRef.kind()};
ActualArguments &args{funcRef.arguments()};
auto *intrinsic{std::get_if<SpecificIntrinsic>(&funcRef.proc().u)};
CHECK(intrinsic);
std::string name{intrinsic->name};
if (name == "all") {
- return FoldAllAnyParity(
- context, std::move(funcRef), &Scalar<T>::AND, Scalar<T>{true});
+ return FoldAllAnyParity(kind, context, std::move(funcRef), &Scalar<T>::AND,
+ Scalar<T>{kind, true});
} else if (name == "allocated") {
if (IsNullAllocatable(args[0]->UnwrapExpr())) {
- return Expr<T>{false};
+ return MakeConstantExpr<T>(kind, false);
}
} else if (name == "any") {
- return FoldAllAnyParity(
- context, std::move(funcRef), &Scalar<T>::OR, Scalar<T>{false});
+ return FoldAllAnyParity(kind, context, std::move(funcRef), &Scalar<T>::OR,
+ Scalar<T>{kind, false});
} else if (name == "associated") {
if (IsNullPointer(args[0]->UnwrapExpr()) ||
(args[1] && IsNullPointer(args[1]->UnwrapExpr()))) {
- return Expr<T>{false};
+ return MakeConstantExpr<T>(kind, false);
}
} else if (name == "bge" || name == "bgt" || name == "ble" || name == "blt") {
static_assert(std::is_same_v<Scalar<LargestInt>, BOZLiteralConstant>);
@@ -673,7 +512,9 @@ Expr<Type<TypeCategory::Logical, KIND>> FoldIntrinsicFunction(
std::optional<Expr<SomeType>> constArgs[2];
for (int i{0}; i <= 1; i++) {
if (BOZLiteralConstant * x{UnwrapExpr<BOZLiteralConstant>(args[i])}) {
- constArgs[i] = AsGenericExpr(Constant<LargestInt>{std::move(*x)});
+ // Copy rather than move: when only one operand is constant the fold
+ // below is skipped and args[i] must retain its original BOZ value.
+ constArgs[i] = AsGenericExpr(Constant<LargestInt>{LargestIntKind, *x});
} else if (auto *x{UnwrapExpr<Expr<SomeInteger>>(args[i])}) {
common::visit(
[&](const auto &ix) {
@@ -703,33 +544,33 @@ Expr<Type<TypeCategory::Logical, KIND>> FoldIntrinsicFunction(
*args[i] = std::move(constArgs[i].value());
}
- return FoldElementalIntrinsic<T, LargestInt, LargestInt>(context,
- std::move(funcRef),
+ return FoldElementalIntrinsic<T, LargestInt, LargestInt>(kind,
+ {LargestIntKind, LargestIntKind}, context, std::move(funcRef),
ScalarFunc<T, LargestInt, LargestInt>(
- [&fptr](
+ [&fptr, kind](
const Scalar<LargestInt> &i, const Scalar<LargestInt> &j) {
- return Scalar<T>{std::invoke(fptr, i, j)};
+ return Scalar<T>{kind, std::invoke(fptr, i, j)};
}));
} else {
return Expr<T>{std::move(funcRef)};
}
} else if (name == "btest") {
- using SameInt = Type<TypeCategory::Integer, KIND>;
+ using SameInt = Type<TypeCategory::Integer>;
if (const auto *ix{UnwrapExpr<Expr<SomeInteger>>(args[0])}) {
return common::visit(
[&](const auto &x) {
using IT = ResultType<decltype(x)>;
- return FoldElementalIntrinsic<T, IT, SameInt>(context,
- std::move(funcRef),
+ return FoldElementalIntrinsic<T, IT, SameInt>(kind,
+ {x.kind(), kind}, context, std::move(funcRef),
ScalarFunc<T, IT, SameInt>(
[&](const Scalar<IT> &x, const Scalar<SameInt> &pos) {
auto posVal{pos.ToInt64()};
- if (posVal < 0 || posVal >= x.bits) {
+ if (posVal < 0 || posVal >= x.bits()) {
context.messages().Say(
"POS=%jd out of range for BTEST"_err_en_US,
static_cast<std::intmax_t>(posVal));
}
- return Scalar<T>{x.BTEST(posVal)};
+ return Scalar<T>{kind, x.BTEST(posVal)};
}));
},
ix->u);
@@ -737,17 +578,17 @@ Expr<Type<TypeCategory::Logical, KIND>> FoldIntrinsicFunction(
return common::visit(
[&](const auto &x) {
using UT = ResultType<decltype(x)>;
- return FoldElementalIntrinsic<T, UT, SameInt>(context,
- std::move(funcRef),
+ return FoldElementalIntrinsic<T, UT, SameInt>(kind,
+ {x.kind(), kind}, context, std::move(funcRef),
ScalarFunc<T, UT, SameInt>(
[&](const Scalar<UT> &x, const Scalar<SameInt> &pos) {
auto posVal{pos.ToInt64()};
- if (posVal < 0 || posVal >= x.bits) {
+ if (posVal < 0 || posVal >= x.bits()) {
context.messages().Say(
"POS=%jd out of range for BTEST"_err_en_US,
static_cast<std::intmax_t>(posVal));
}
- return Scalar<T>{x.BTEST(posVal)};
+ return Scalar<T>{kind, x.BTEST(posVal)};
}));
},
ux->u);
@@ -762,7 +603,7 @@ Expr<Type<TypeCategory::Logical, KIND>> FoldIntrinsicFunction(
auto t1{args[1]->GetType()};
if (t0 && t1) {
if (auto result{t0->ExtendsTypeOf(*t1)}) {
- return Expr<T>{*result};
+ return MakeConstantExpr<T>(kind, *result);
}
}
}
@@ -771,31 +612,38 @@ Expr<Type<TypeCategory::Logical, KIND>> FoldIntrinsicFunction(
if (args[0] && args[0]->UnwrapExpr() &&
IsActuallyConstant(*args[0]->UnwrapExpr())) {
auto restorer{context.messages().DiscardMessages()};
- using DefaultReal = Type<TypeCategory::Real, 4>;
- return FoldElementalIntrinsic<T, DefaultReal>(context, std::move(funcRef),
- ScalarFunc<T, DefaultReal>([](const Scalar<DefaultReal> &x) {
- return Scalar<T>{x.IsNotANumber()};
+ using DefaultReal = Type<TypeCategory::Real>;
+ constexpr int DefaultRealKind{4};
+ return FoldElementalIntrinsic<T, DefaultReal>(kind, {DefaultRealKind},
+ context, std::move(funcRef),
+ ScalarFunc<T, DefaultReal>([kind](const Scalar<DefaultReal> &x) {
+ return Scalar<T>{kind, x.IsNotANumber()};
}));
}
} else if (name == "__builtin_ieee_is_negative") {
auto restorer{context.messages().DiscardMessages()};
- using DefaultReal = Type<TypeCategory::Real, 4>;
+ using DefaultReal = Type<TypeCategory::Real>;
+ constexpr int DefaultRealKind{4};
if (args[0] && args[0]->UnwrapExpr() &&
IsActuallyConstant(*args[0]->UnwrapExpr())) {
- return FoldElementalIntrinsic<T, DefaultReal>(context, std::move(funcRef),
- ScalarFunc<T, DefaultReal>([](const Scalar<DefaultReal> &x) {
- return Scalar<T>{x.IsNegative()};
+ return FoldElementalIntrinsic<T, DefaultReal>(kind, {DefaultRealKind},
+ context, std::move(funcRef),
+ ScalarFunc<T, DefaultReal>([kind](const Scalar<DefaultReal> &x) {
+ return Scalar<T>{kind, x.IsNegative()};
}));
}
} else if (name == "__builtin_ieee_is_normal") {
auto restorer{context.messages().DiscardMessages()};
- using DefaultReal = Type<TypeCategory::Real, 4>;
+ using DefaultReal = Type<TypeCategory::Real>;
+ constexpr int DefaultRealKind = 4;
if (args[0] && args[0]->UnwrapExpr() &&
IsActuallyConstant(*args[0]->UnwrapExpr())) {
- return FoldElementalIntrinsic<T, DefaultReal>(context, std::move(funcRef),
- ScalarFunc<T, DefaultReal>([](const Scalar<DefaultReal> &x) {
- return Scalar<T>{x.IsNormal()};
- }));
+ return FoldElementalIntrinsic<T, DefaultReal>(kind, {DefaultRealKind},
+ context, std::move(funcRef),
+ ScalarFunc<T, DefaultReal>([kind](const Scalar<DefaultReal> &x) {
+ return Scalar<T>{kind, x.IsNormal()};
+ }),
+ /*hasOptionalArgument=*/false);
}
} else if (name == "is_contiguous") {
if (args.at(0)) {
@@ -812,25 +660,31 @@ Expr<Type<TypeCategory::Logical, KIND>> FoldIntrinsicFunction(
"is_contiguous() is always true for named constants and subobjects of named constants"_warn_en_US);
}
}
- return Expr<T>{*knownContiguous};
+ return MakeConstantExpr<T>(kind, *knownContiguous);
}
}
} else if (name == "is_iostat_end") {
if (args[0] && args[0]->UnwrapExpr() &&
IsActuallyConstant(*args[0]->UnwrapExpr())) {
- using Int64 = Type<TypeCategory::Integer, 8>;
- return FoldElementalIntrinsic<T, Int64>(context, std::move(funcRef),
- ScalarFunc<T, Int64>([](const Scalar<Int64> &x) {
- return Scalar<T>{x.ToInt64() == FORTRAN_RUNTIME_IOSTAT_END};
+ // Int64 used to be Type<Integer,8>; force the argument to that kind as
+ // before.
+ using Int64 = Type<TypeCategory::Integer>;
+ constexpr int Int64Kind{8};
+ return FoldElementalIntrinsic<T, Int64>(kind, {Int64Kind}, context,
+ std::move(funcRef),
+ ScalarFunc<T, Int64>([kind](const Scalar<Int64> &x) {
+ return Scalar<T>{kind, x.ToInt64() == FORTRAN_RUNTIME_IOSTAT_END};
}));
}
} else if (name == "is_iostat_eor") {
if (args[0] && args[0]->UnwrapExpr() &&
IsActuallyConstant(*args[0]->UnwrapExpr())) {
- using Int64 = Type<TypeCategory::Integer, 8>;
- return FoldElementalIntrinsic<T, Int64>(context, std::move(funcRef),
- ScalarFunc<T, Int64>([](const Scalar<Int64> &x) {
- return Scalar<T>{x.ToInt64() == FORTRAN_RUNTIME_IOSTAT_EOR};
+ using Int64 = Type<TypeCategory::Integer>;
+ constexpr int Int64Kind{8};
+ return FoldElementalIntrinsic<T, Int64>(kind, {Int64Kind}, context,
+ std::move(funcRef),
+ ScalarFunc<T, Int64>([kind](const Scalar<Int64> &x) {
+ return Scalar<T>{kind, x.ToInt64() == FORTRAN_RUNTIME_IOSTAT_EOR};
}));
}
} else if (name == "lge" || name == "lgt" || name == "lle" || name == "llt") {
@@ -839,25 +693,25 @@ Expr<Type<TypeCategory::Logical, KIND>> FoldIntrinsicFunction(
auto *cx1{UnwrapExpr<Expr<SomeCharacter>>(args[1])};
if (cx0 && cx1) {
return Fold(context,
- ConvertToType<T>(
+ ConvertToType<T>(kind,
PackageRelation(name == "lge" ? RelationalOperator::GE
: name == "lgt" ? RelationalOperator::GT
: name == "lle" ? RelationalOperator::LE
: RelationalOperator::LT,
- ConvertToType<Ascii>(std::move(*cx0)),
- ConvertToType<Ascii>(std::move(*cx1)))));
+ ConvertToType<Ascii>(AsciiKind, std::move(*cx0)),
+ ConvertToType<Ascii>(AsciiKind, std::move(*cx1)))));
}
} else if (name == "logical") {
if (auto *expr{UnwrapExpr<Expr<SomeLogical>>(args[0])}) {
- return Fold(context, ConvertToType<T>(std::move(*expr)));
+ return Fold(context, ConvertToType<T>(kind, std::move(*expr)));
}
} else if (name == "matmul") {
return FoldMatmul(context, std::move(funcRef));
} else if (name == "out_of_range") {
- return RewriteOutOfRange<KIND>(context, std::move(funcRef));
+ return RewriteOutOfRange(context, std::move(funcRef));
} else if (name == "parity") {
- return FoldAllAnyParity(
- context, std::move(funcRef), &Scalar<T>::NEQV, Scalar<T>{false});
+ return FoldAllAnyParity(kind, context, std::move(funcRef), &Scalar<T>::NEQV,
+ Scalar<T>{kind, false});
} else if (name == "same_type_as") {
// Type equality testing with SAME_TYPE_AS() ignores any type parameters.
// Returns a constant truth value when the result is known now.
@@ -866,18 +720,20 @@ Expr<Type<TypeCategory::Logical, KIND>> FoldIntrinsicFunction(
auto t1{args[1]->GetType()};
if (t0 && t1) {
if (auto result{t0->SameTypeAs(*t1)}) {
- return Expr<T>{*result};
+ return MakeConstantExpr<T>(kind, *result);
}
}
}
} else if (name == "__builtin_ieee_support_datatype") {
- return Expr<T>{true};
+ return MakeConstantExpr<T>(kind, true);
} else if (name == "__builtin_ieee_support_denormal") {
- return Expr<T>{context.targetCharacteristics().ieeeFeatures().test(
- IeeeFeature::Denormal)};
+ return MakeConstantExpr<T>(kind,
+ context.targetCharacteristics().ieeeFeatures().test(
+ IeeeFeature::Denormal));
} else if (name == "__builtin_ieee_support_divide") {
- return Expr<T>{context.targetCharacteristics().ieeeFeatures().test(
- IeeeFeature::Divide)};
+ return MakeConstantExpr<T>(kind,
+ context.targetCharacteristics().ieeeFeatures().test(
+ IeeeFeature::Divide));
} else if (name == "__builtin_ieee_support_flag") {
if (context.targetCharacteristics().ieeeFeatures().test(
IeeeFeature::Flags)) {
@@ -890,20 +746,22 @@ Expr<Type<TypeCategory::Logical, KIND>> FoldIntrinsicFunction(
if (auto flag{ToInt64(value)}) {
if (flag != _FORTRAN_RUNTIME_IEEE_DENORM) {
// Check for suppport for standard exceptions.
- return Expr<T>{
+ return MakeConstantExpr<T>(kind,
context.targetCharacteristics().ieeeFeatures().test(
- IeeeFeature::Flags)};
+ IeeeFeature::Flags));
} else if (args[1]) {
// Check for nonstandard ieee_denorm exception support for
// a given kind.
- return Expr<T>{context.targetCharacteristics()
+ return MakeConstantExpr<T>(kind,
+ context.targetCharacteristics()
.hasSubnormalExceptionSupport(
- args[1]->GetType().value().kind())};
+ args[1]->GetType().value().kind()));
} else {
// Check for nonstandard ieee_denorm exception support for
// all kinds.
- return Expr<T>{context.targetCharacteristics()
- .hasSubnormalExceptionSupport()};
+ return MakeConstantExpr<T>(kind,
+ context.targetCharacteristics()
+ .hasSubnormalExceptionSupport());
}
}
}
@@ -914,48 +772,52 @@ Expr<Type<TypeCategory::Logical, KIND>> FoldIntrinsicFunction(
} else if (name == "__builtin_ieee_support_halting") {
if (!context.targetCharacteristics()
.haltingSupportIsUnknownAtCompileTime()) {
- return Expr<T>{context.targetCharacteristics().ieeeFeatures().test(
- IeeeFeature::Halting)};
+ return MakeConstantExpr<T>(kind,
+ context.targetCharacteristics().ieeeFeatures().test(
+ IeeeFeature::Halting));
}
} else if (name == "__builtin_ieee_support_inf") {
- return Expr<T>{
- context.targetCharacteristics().ieeeFeatures().test(IeeeFeature::Inf)};
+ return MakeConstantExpr<T>(kind,
+ context.targetCharacteristics().ieeeFeatures().test(IeeeFeature::Inf));
} else if (name == "__builtin_ieee_support_io") {
- return Expr<T>{
- context.targetCharacteristics().ieeeFeatures().test(IeeeFeature::Io)};
+ return MakeConstantExpr<T>(kind,
+ context.targetCharacteristics().ieeeFeatures().test(IeeeFeature::Io));
} else if (name == "__builtin_ieee_support_nan") {
- return Expr<T>{
- context.targetCharacteristics().ieeeFeatures().test(IeeeFeature::NaN)};
+ return MakeConstantExpr<T>(kind,
+ context.targetCharacteristics().ieeeFeatures().test(IeeeFeature::NaN));
} else if (name == "__builtin_ieee_support_rounding") {
if (context.targetCharacteristics().ieeeFeatures().test(
IeeeFeature::Rounding)) {
if (auto mode{GetRoundingMode(args[0])}) {
- return Expr<T>{mode < common::RoundingMode::TiesAwayFromZero};
+ return MakeConstantExpr<T>(
+ kind, mode < common::RoundingMode::TiesAwayFromZero);
}
}
} else if (name == "__builtin_ieee_support_sqrt") {
- return Expr<T>{
- context.targetCharacteristics().ieeeFeatures().test(IeeeFeature::Sqrt)};
+ return MakeConstantExpr<T>(kind,
+ context.targetCharacteristics().ieeeFeatures().test(IeeeFeature::Sqrt));
} else if (name == "__builtin_ieee_support_standard") {
// ieee_support_standard depends in part on ieee_support_halting.
if (!context.targetCharacteristics()
.haltingSupportIsUnknownAtCompileTime()) {
- return Expr<T>{context.targetCharacteristics().ieeeFeatures().test(
- IeeeFeature::Standard)};
+ return MakeConstantExpr<T>(kind,
+ context.targetCharacteristics().ieeeFeatures().test(
+ IeeeFeature::Standard));
}
} else if (name == "__builtin_ieee_support_subnormal") {
- return Expr<T>{context.targetCharacteristics().ieeeFeatures().test(
- IeeeFeature::Subnormal)};
+ return MakeConstantExpr<T>(kind,
+ context.targetCharacteristics().ieeeFeatures().test(
+ IeeeFeature::Subnormal));
} else if (name == "__builtin_ieee_support_underflow_control") {
// Setting kind=0 checks subnormal flushing control across all type kinds.
if (args[0]) {
- return Expr<T>{
+ return MakeConstantExpr<T>(kind,
context.targetCharacteristics().hasSubnormalFlushingControl(
- args[0]->GetType().value().kind())};
+ args[0]->GetType().value().kind()));
} else {
- return Expr<T>{
+ return MakeConstantExpr<T>(kind,
context.targetCharacteristics().hasSubnormalFlushingControl(
- /*any=*/false)};
+ /*any=*/false));
}
}
return Expr<T>{std::move(funcRef)};
@@ -990,7 +852,7 @@ Expr<LogicalResult> FoldOperation(
} else {
static_assert(T::category != TypeCategory::Logical);
}
- return Expr<LogicalResult>{Constant<LogicalResult>{result}};
+ return MakeLogicalResultExpr(result);
}
return Expr<LogicalResult>{Relational<SomeType>{std::move(relation)}};
}
@@ -1004,28 +866,28 @@ Expr<LogicalResult> FoldOperation(
std::move(relation.u));
}
-template <int KIND>
-Expr<Type<TypeCategory::Logical, KIND>> FoldOperation(
- FoldingContext &context, Not<KIND> &&x) {
+Expr<Type<TypeCategory::Logical>> FoldOperation(
+ FoldingContext &context, Not &&x) {
if (auto array{ApplyElementwise(context, x)}) {
return *array;
}
- using Ty = Type<TypeCategory::Logical, KIND>;
+ using Ty = Type<TypeCategory::Logical>;
+ const int kind{x.kind()};
auto &operand{x.left()};
if (auto value{GetScalarConstantValue<Ty>(operand)}) {
- return Expr<Ty>{Constant<Ty>{!value->IsTrue()}};
+ return MakeConstantExpr<Ty>(kind, !value->IsTrue());
}
return Expr<Ty>{x};
}
-template <int KIND>
-Expr<Type<TypeCategory::Logical, KIND>> FoldOperation(
- FoldingContext &context, LogicalOperation<KIND> &&operation) {
- using LOGICAL = Type<TypeCategory::Logical, KIND>;
+Expr<Type<TypeCategory::Logical>> FoldOperation(
+ FoldingContext &context, LogicalOperation &&operation) {
+ using LOGICAL = Type<TypeCategory::Logical>;
+ const int kind{operation.kind()};
if (auto array{ApplyElementwise(context, operation,
std::function<Expr<LOGICAL>(Expr<LOGICAL> &&, Expr<LOGICAL> &&)>{
[=](Expr<LOGICAL> &&x, Expr<LOGICAL> &&y) {
- return Expr<LOGICAL>{LogicalOperation<KIND>{
+ return Expr<LOGICAL>{LogicalOperation{
operation.logicalOperator, std::move(x), std::move(y)}};
}})}) {
return *array;
@@ -1048,7 +910,7 @@ Expr<Type<TypeCategory::Logical, KIND>> FoldOperation(
case LogicalOperator::Not:
DIE("not a binary operator");
}
- return Expr<LOGICAL>{Constant<LOGICAL>{result}};
+ return MakeConstantExpr<LOGICAL>(kind, result);
}
return Expr<LOGICAL>{std::move(operation)};
}
diff --git a/flang/lib/Evaluate/fold-matmul.h b/flang/lib/Evaluate/fold-matmul.h
index a8a24c09774e8..e0c58acdbb4bc 100644
--- a/flang/lib/Evaluate/fold-matmul.h
+++ b/flang/lib/Evaluate/fold-matmul.h
@@ -15,10 +15,11 @@ namespace Fortran::evaluate {
template <typename T>
static Expr<T> FoldMatmul(FoldingContext &context, FunctionRef<T> &&funcRef) {
+ const int kind{funcRef.kind()};
using Element = typename Constant<T>::Element;
auto args{funcRef.arguments()};
CHECK(args.size() == 2);
- Folder<T> folder{context};
+ Folder<T> folder{kind, context};
Constant<T> *ma{folder.Folding(args[0])};
Constant<T> *mb{folder.Folding(args[1])};
if (!ma || !mb) {
@@ -51,8 +52,8 @@ static Expr<T> FoldMatmul(FoldingContext &context, FunctionRef<T> &&funcRef) {
if (mb->Rank() == 2) {
bAt[1] += ci;
}
- Element sum{};
- [[maybe_unused]] Element correction{};
+ Element sum{Element::Zero(kind)};
+ [[maybe_unused]] Element correction{Element::Zero(kind)};
for (ConstantSubscript j{0}; j < commonExtent; ++j) {
Element aElt{ma->At(aAt)};
Element bElt{mb->At(bAt)};
@@ -90,7 +91,7 @@ static Expr<T> FoldMatmul(FoldingContext &context, FunctionRef<T> &&funcRef) {
if (overflow) {
context.Warn(common::UsageWarning::FoldingException,
"MATMUL of %s data overflowed during computation"_warn_en_US,
- T::AsFortran());
+ Type<T::category>(kind).AsFortran());
}
ConstantSubscripts shape;
if (ma->Rank() == 2) {
@@ -99,7 +100,7 @@ static Expr<T> FoldMatmul(FoldingContext &context, FunctionRef<T> &&funcRef) {
if (mb->Rank() == 2) {
shape.push_back(columns);
}
- return Expr<T>{Constant<T>{std::move(elements), std::move(shape)}};
+ return Expr<T>{Constant<T>{kind, std::move(elements), std::move(shape)}};
}
} // namespace Fortran::evaluate
#endif // FORTRAN_EVALUATE_FOLD_MATMUL_H_
diff --git a/flang/lib/Evaluate/fold-real.cpp b/flang/lib/Evaluate/fold-real.cpp
index 9c591e2ef36ec..57b935ac05799 100644
--- a/flang/lib/Evaluate/fold-real.cpp
+++ b/flang/lib/Evaluate/fold-real.cpp
@@ -15,15 +15,17 @@ namespace Fortran::evaluate {
template <typename T>
static Expr<T> FoldTransformationalBessel(
FunctionRef<T> &&funcRef, FoldingContext &context) {
+ const int kind{funcRef.kind()};
CHECK(funcRef.arguments().size() == 3);
/// Bessel runtime functions use `int` integer arguments. Convert integer
/// arguments to Int4, any overflow error will be reported during the
/// conversion folding.
- using Int4 = Type<TypeCategory::Integer, 4>;
- if (auto args{GetConstantArguments<Int4, Int4, T>(
- context, funcRef.arguments(), /*hasOptionalArgument=*/false)}) {
+ using Int4 = Type<TypeCategory::Integer>;
+ if (auto args{GetConstantArguments<Int4, Int4, T>({4, 4, kind}, context,
+ funcRef.arguments(), /*hasOptionalArgument=*/false)}) {
const std::string &name{std::get<SpecificIntrinsic>(funcRef.proc().u).name};
- if (auto elementalBessel{GetHostRuntimeWrapper<T, Int4, T>(name)}) {
+ if (auto elementalBessel{
+ GetHostRuntimeWrapper<T, Int4, T>(kind, {4, kind}, name)}) {
std::vector<Scalar<T>> results;
int n1{static_cast<int>(
std::get<0>(*args)->GetScalarValue().value().ToInt64())};
@@ -31,27 +33,29 @@ static Expr<T> FoldTransformationalBessel(
std::get<1>(*args)->GetScalarValue().value().ToInt64())};
Scalar<T> x{std::get<2>(*args)->GetScalarValue().value()};
for (int i{n1}; i <= n2; ++i) {
- results.emplace_back((*elementalBessel)(context, Scalar<Int4>{i}, x));
+ results.emplace_back(
+ (*elementalBessel)(context, value::IntegerValue{4, i}, x));
}
- return Expr<T>{Constant<T>{
- std::move(results), ConstantSubscripts{std::max(n2 - n1 + 1, 0)}}};
+ return Expr<T>{Constant<T>{kind, std::move(results),
+ ConstantSubscripts{std::max(n2 - n1 + 1, 0)}}};
} else {
context.Warn(common::UsageWarning::FoldingFailure,
"%s(integer(kind=4), real(kind=%d)) cannot be folded on host"_warn_en_US,
- name, T::kind);
+ name, kind);
}
}
return Expr<T>{std::move(funcRef)};
}
// NORM2
-template <int KIND> class Norm2Accumulator {
- using T = Type<TypeCategory::Real, KIND>;
+class Norm2Accumulator {
+ using T = Type<TypeCategory::Real>;
public:
Norm2Accumulator(
const Constant<T> &array, const Constant<T> &maxAbs, Rounding rounding)
- : array_{array}, maxAbs_{maxAbs}, rounding_{rounding} {};
+ : array_{array}, maxAbs_{maxAbs}, rounding_{rounding},
+ correction_{Scalar<T>::Zero(array.kind())} {};
void operator()(
Scalar<T> &element, const ConstantSubscripts &at, bool /*first*/) {
// Summation of scaled elements:
@@ -103,44 +107,42 @@ template <int KIND> class Norm2Accumulator {
const Constant<T> &maxAbs_;
const Rounding rounding_;
bool overflow_{false};
- Scalar<T> correction_{};
+ Scalar<T> correction_;
ConstantSubscripts maxAbsAt_{maxAbs_.lbounds()};
};
-template <int KIND>
-static Expr<Type<TypeCategory::Real, KIND>> FoldNorm2(FoldingContext &context,
- FunctionRef<Type<TypeCategory::Real, KIND>> &&funcRef) {
- using T = Type<TypeCategory::Real, KIND>;
+static Expr<Type<TypeCategory::Real>> FoldNorm2(int kind,
+ FoldingContext &context, FunctionRef<Type<TypeCategory::Real>> &&funcRef) {
+ using T = Type<TypeCategory::Real>;
using Element = typename Constant<T>::Element;
std::optional<int> dim;
if (std::optional<ArrayAndMask<T>> arrayAndMask{
- ProcessReductionArgs<T>(context, funcRef.arguments(), dim,
+ ProcessReductionArgs<T>(kind, context, funcRef.arguments(), dim,
/*X=*/0, /*DIM=*/1)}) {
MaxvalMinvalAccumulator<T, /*ABS=*/true> maxAbsAccumulator{
- RelationalOperator::GT, context, arrayAndMask->array};
- const Element identity{};
- Constant<T> maxAbs{DoReduction<T>(arrayAndMask->array, arrayAndMask->mask,
- dim, identity, maxAbsAccumulator)};
+ kind, RelationalOperator::GT, context, arrayAndMask->array};
+ const Element identity{Element::Zero(kind)};
+ Constant<T> maxAbs{DoReduction<T>(kind, arrayAndMask->array,
+ arrayAndMask->mask, dim, identity, maxAbsAccumulator)};
Norm2Accumulator norm2Accumulator{arrayAndMask->array, maxAbs,
context.targetCharacteristics().roundingMode()};
- Constant<T> result{DoReduction<T>(arrayAndMask->array, arrayAndMask->mask,
- dim, identity, norm2Accumulator)};
+ Constant<T> result{DoReduction<T>(kind, arrayAndMask->array,
+ arrayAndMask->mask, dim, identity, norm2Accumulator)};
if (norm2Accumulator.overflow()) {
context.Warn(common::UsageWarning::FoldingException,
- "NORM2() of REAL(%d) data overflowed"_warn_en_US, KIND);
+ "NORM2() of REAL(%d) data overflowed"_warn_en_US, kind);
}
return Expr<T>{std::move(result)};
}
return Expr<T>{std::move(funcRef)};
}
-template <int KIND>
-Expr<Type<TypeCategory::Real, KIND>> FoldIntrinsicFunction(
- FoldingContext &context,
- FunctionRef<Type<TypeCategory::Real, KIND>> &&funcRef) {
- using T = Type<TypeCategory::Real, KIND>;
- using ComplexT = Type<TypeCategory::Complex, KIND>;
- using Int4 = Type<TypeCategory::Integer, 4>;
+Expr<Type<TypeCategory::Real>> FoldIntrinsicFunction(
+ FoldingContext &context, FunctionRef<Type<TypeCategory::Real>> &&funcRef) {
+ const int kind{funcRef.kind()};
+ using T = Type<TypeCategory::Real>;
+ using ComplexT = Type<TypeCategory::Complex>;
+ using Int4 = Type<TypeCategory::Integer>;
ActualArguments &args{funcRef.arguments()};
auto *intrinsic{std::get_if<SpecificIntrinsic>(&funcRef.proc().u)};
CHECK(intrinsic);
@@ -154,12 +156,12 @@ Expr<Type<TypeCategory::Real, KIND>> FoldIntrinsicFunction(
name == "log_gamma" || name == "sin" || name == "sinh" || name == "tan" ||
name == "tanh") {
CHECK(args.size() == 1);
- if (auto callable{GetHostRuntimeWrapper<T, T>(name)}) {
+ if (auto callable{GetHostRuntimeWrapper<T, T>(kind, {kind}, name)}) {
return FoldElementalIntrinsic<T, T>(
- context, std::move(funcRef), *callable);
+ kind, {kind}, context, std::move(funcRef), *callable);
} else {
context.Warn(common::UsageWarning::FoldingFailure,
- "%s(real(kind=%d)) cannot be folded on host"_warn_en_US, name, KIND);
+ "%s(real(kind=%d)) cannot be folded on host"_warn_en_US, name, kind);
}
} else if (name == "amax0" || name == "amin0" || name == "amin1" ||
name == "amax1" || name == "dmin1" || name == "dmax1") {
@@ -167,24 +169,26 @@ Expr<Type<TypeCategory::Real, KIND>> FoldIntrinsicFunction(
} else if (name == "atan" || name == "atan2") {
std::string localName{name == "atan" ? "atan2" : name};
CHECK(args.size() == 2);
- if (auto callable{GetHostRuntimeWrapper<T, T, T>(localName)}) {
+ if (auto callable{
+ GetHostRuntimeWrapper<T, T, T>(kind, {kind, kind}, localName)}) {
return FoldElementalIntrinsic<T, T, T>(
- context, std::move(funcRef), *callable);
+ kind, {kind, kind}, context, std::move(funcRef), *callable);
} else {
context.Warn(common::UsageWarning::FoldingFailure,
"%s(real(kind=%d), real(kind%d)) cannot be folded on host"_warn_en_US,
- name, KIND, KIND);
+ name, kind, kind);
}
} else if (name == "bessel_jn" || name == "bessel_yn") {
if (args.size() == 2) { // elemental
// runtime functions use int arg
- if (auto callable{GetHostRuntimeWrapper<T, Int4, T>(name)}) {
+ if (auto callable{
+ GetHostRuntimeWrapper<T, Int4, T>(kind, {4, kind}, name)}) {
return FoldElementalIntrinsic<T, Int4, T>(
- context, std::move(funcRef), *callable);
+ kind, {4, kind}, context, std::move(funcRef), *callable);
} else {
context.Warn(common::UsageWarning::FoldingFailure,
"%s(integer(kind=4), real(kind=%d)) cannot be folded on host"_warn_en_US,
- name, KIND);
+ name, kind);
}
} else {
return FoldTransformationalBessel<T>(std::move(funcRef), context);
@@ -193,9 +197,10 @@ Expr<Type<TypeCategory::Real, KIND>> FoldIntrinsicFunction(
// Argument can be complex or real
if (UnwrapExpr<Expr<SomeReal>>(args[0])) {
return FoldElementalIntrinsic<T, T>(
- context, std::move(funcRef), &Scalar<T>::ABS);
+ kind, {kind}, context, std::move(funcRef), &Scalar<T>::ABS);
} else if (UnwrapExpr<Expr<SomeComplex>>(args[0])) {
- return FoldElementalIntrinsic<T, ComplexT>(context, std::move(funcRef),
+ return FoldElementalIntrinsic<T, ComplexT>(kind, {kind}, context,
+ std::move(funcRef),
ScalarFunc<T, ComplexT>([&name, &context](
const Scalar<ComplexT> &z) -> Scalar<T> {
ValueWithRealFlags<Scalar<T>> y{z.ABS()};
@@ -217,7 +222,8 @@ Expr<Type<TypeCategory::Real, KIND>> FoldIntrinsicFunction(
common::RoundingMode mode{name == "aint"
? common::RoundingMode::ToZero
: common::RoundingMode::TiesAwayFromZero};
- return FoldElementalIntrinsic<T, T>(context, std::move(funcRef),
+ return FoldElementalIntrinsic<T, T>(kind, {kind}, context,
+ std::move(funcRef),
ScalarFunc<T, T>(
[&name, &context, mode](const Scalar<T> &x) -> Scalar<T> {
ValueWithRealFlags<Scalar<T>> y{x.ToWholeNumber(mode)};
@@ -228,7 +234,8 @@ Expr<Type<TypeCategory::Real, KIND>> FoldIntrinsicFunction(
return y.value;
}));
} else if (name == "dim") {
- return FoldElementalIntrinsic<T, T, T>(context, std::move(funcRef),
+ return FoldElementalIntrinsic<T, T, T>(kind, {kind, kind}, context,
+ std::move(funcRef),
ScalarFunc<T, T, T>([&context](const Scalar<T> &x,
const Scalar<T> &y) -> Scalar<T> {
ValueWithRealFlags<Scalar<T>> result{x.DIM(y)};
@@ -247,21 +254,23 @@ Expr<Type<TypeCategory::Real, KIND>> FoldIntrinsicFunction(
const auto *yExpr{args[1]->UnwrapExpr()};
if (xExpr && yExpr) {
return Fold(context,
- ToReal<T::kind>(context, common::Clone(*xExpr)) *
- ToReal<T::kind>(context, common::Clone(*yExpr)));
+ ToReal(kind, context, common::Clone(*xExpr)) *
+ ToReal(kind, context, common::Clone(*yExpr)));
}
}
} else if (name == "epsilon") {
- return Expr<T>{Scalar<T>::EPSILON()};
+ return MakeConstantExpr<T>(kind, Scalar<T>::EPSILON(kind));
} else if (name == "fraction") {
- return FoldElementalIntrinsic<T, T>(context, std::move(funcRef),
+ return FoldElementalIntrinsic<T, T>(kind, {kind}, context,
+ std::move(funcRef),
ScalarFunc<T, T>(
[](const Scalar<T> &x) -> Scalar<T> { return x.FRACTION(); }));
} else if (name == "huge") {
- return Expr<T>{Scalar<T>::HUGE()};
+ return MakeConstantExpr<T>(kind, Scalar<T>::HUGE(kind));
} else if (name == "hypot") {
CHECK(args.size() == 2);
- return FoldElementalIntrinsic<T, T, T>(context, std::move(funcRef),
+ return FoldElementalIntrinsic<T, T, T>(kind, {kind, kind}, context,
+ std::move(funcRef),
ScalarFunc<T, T, T>(
[&](const Scalar<T> &x, const Scalar<T> &y) -> Scalar<T> {
ValueWithRealFlags<Scalar<T>> result{x.HYPOT(y)};
@@ -276,13 +285,13 @@ Expr<Type<TypeCategory::Real, KIND>> FoldIntrinsicFunction(
} else if (name == "max") {
return FoldMINorMAX(context, std::move(funcRef), Ordering::Greater);
} else if (name == "maxval") {
- return FoldMaxvalMinval<T>(context, std::move(funcRef),
- RelationalOperator::GT, T::Scalar::HUGE().Negate());
+ return FoldMaxvalMinval<T>(kind, context, std::move(funcRef),
+ RelationalOperator::GT, Scalar<T>::HUGE(kind).Negate());
} else if (name == "min") {
return FoldMINorMAX(context, std::move(funcRef), Ordering::Less);
} else if (name == "minval") {
- return FoldMaxvalMinval<T>(
- context, std::move(funcRef), RelationalOperator::LT, T::Scalar::HUGE());
+ return FoldMaxvalMinval<T>(kind, context, std::move(funcRef),
+ RelationalOperator::LT, Scalar<T>::HUGE(kind));
} else if (name == "mod") {
CHECK(args.size() == 2);
bool badPConst{false};
@@ -295,7 +304,8 @@ Expr<Type<TypeCategory::Real, KIND>> FoldIntrinsicFunction(
badPConst = true;
}
}
- return FoldElementalIntrinsic<T, T, T>(context, std::move(funcRef),
+ return FoldElementalIntrinsic<T, T, T>(kind, {kind, kind}, context,
+ std::move(funcRef),
ScalarFunc<T, T, T>([&context, badPConst](const Scalar<T> &x,
const Scalar<T> &y) -> Scalar<T> {
auto result{x.MOD(y)};
@@ -317,7 +327,8 @@ Expr<Type<TypeCategory::Real, KIND>> FoldIntrinsicFunction(
badPConst = true;
}
}
- return FoldElementalIntrinsic<T, T, T>(context, std::move(funcRef),
+ return FoldElementalIntrinsic<T, T, T>(kind, {kind, kind}, context,
+ std::move(funcRef),
ScalarFunc<T, T, T>([&context, badPConst](const Scalar<T> &x,
const Scalar<T> &y) -> Scalar<T> {
auto result{x.MODULO(y)};
@@ -341,7 +352,8 @@ Expr<Type<TypeCategory::Real, KIND>> FoldIntrinsicFunction(
sConst->IsZero() ? "zero" : "NaN");
badSConst = true;
}
- return FoldElementalIntrinsic<T, T, TS>(context, std::move(funcRef),
+ return FoldElementalIntrinsic<T, T, TS>(kind, {kind, sVal.kind()},
+ context, std::move(funcRef),
ScalarFunc<T, T, TS>([&](const Scalar<T> &x,
const Scalar<TS> &s) -> Scalar<T> {
if (!badSConst && (s.IsZero() || s.IsNotANumber())) {
@@ -360,16 +372,17 @@ Expr<Type<TypeCategory::Real, KIND>> FoldIntrinsicFunction(
sExpr->u);
}
} else if (name == "norm2") {
- return FoldNorm2<T::kind>(context, std::move(funcRef));
+ return FoldNorm2(kind, context, std::move(funcRef));
} else if (name == "product") {
- auto one{Scalar<T>::FromInteger(value::Integer<8>{1}).value};
+ auto one{Scalar<T>::FromInteger(kind, value::IntegerValue{1, 1}).value};
return FoldProduct<T>(context, std::move(funcRef), one);
} else if (name == "real" || name == "dble") {
if (auto *expr{args[0].value().UnwrapExpr()}) {
- return ToReal<KIND>(context, std::move(*expr));
+ return ToReal(kind, context, std::move(*expr));
}
} else if (name == "rrspacing") {
- return FoldElementalIntrinsic<T, T>(context, std::move(funcRef),
+ return FoldElementalIntrinsic<T, T>(kind, {kind}, context,
+ std::move(funcRef),
ScalarFunc<T, T>(
[](const Scalar<T> &x) -> Scalar<T> { return x.RRSPACING(); }));
} else if (name == "scale") {
@@ -377,12 +390,11 @@ Expr<Type<TypeCategory::Real, KIND>> FoldIntrinsicFunction(
return common::visit(
[&](const auto &byVal) {
using TBY = ResultType<decltype(byVal)>;
- return FoldElementalIntrinsic<T, T, TBY>(context,
- std::move(funcRef),
+ return FoldElementalIntrinsic<T, T, TBY>(kind, {kind, byVal.kind()},
+ context, std::move(funcRef),
ScalarFunc<T, T, TBY>(
[&](const Scalar<T> &x, const Scalar<TBY> &y) -> Scalar<T> {
- ValueWithRealFlags<Scalar<T>> result{
- x.template SCALE<Scalar<TBY>>(y)};
+ ValueWithRealFlags<Scalar<T>> result{x.SCALE(y)};
if (result.flags.test(RealFlag::Overflow)) {
context.Warn(common::UsageWarning::FoldingException,
"SCALE/IEEE_SCALB intrinsic folding overflow"_warn_en_US);
@@ -397,7 +409,8 @@ Expr<Type<TypeCategory::Real, KIND>> FoldIntrinsicFunction(
return common::visit(
[&](const auto &iVal) {
using TY = ResultType<decltype(iVal)>;
- return FoldElementalIntrinsic<T, T, TY>(context, std::move(funcRef),
+ return FoldElementalIntrinsic<T, T, TY>(kind, {kind, iVal.kind()},
+ context, std::move(funcRef),
ScalarFunc<T, T, TY>(
[&](const Scalar<T> &x, const Scalar<TY> &i) -> Scalar<T> {
return x.SET_EXPONENT(i.ToInt64());
@@ -407,13 +420,15 @@ Expr<Type<TypeCategory::Real, KIND>> FoldIntrinsicFunction(
}
} else if (name == "sign") {
return FoldElementalIntrinsic<T, T, T>(
- context, std::move(funcRef), &Scalar<T>::SIGN);
+ kind, {kind, kind}, context, std::move(funcRef), &Scalar<T>::SIGN);
} else if (name == "spacing") {
- return FoldElementalIntrinsic<T, T>(context, std::move(funcRef),
+ return FoldElementalIntrinsic<T, T>(kind, {kind}, context,
+ std::move(funcRef),
ScalarFunc<T, T>(
[](const Scalar<T> &x) -> Scalar<T> { return x.SPACING(); }));
} else if (name == "sqrt") {
- return FoldElementalIntrinsic<T, T>(context, std::move(funcRef),
+ return FoldElementalIntrinsic<T, T>(kind, {kind}, context,
+ std::move(funcRef),
ScalarFunc<T, T>([&context](const Scalar<T> &x) -> Scalar<T> {
ValueWithRealFlags<Scalar<T>> result{x.SQRT()};
if (result.flags.test(RealFlag::InvalidArgument)) {
@@ -425,7 +440,7 @@ Expr<Type<TypeCategory::Real, KIND>> FoldIntrinsicFunction(
} else if (name == "sum") {
return FoldSum<T>(context, std::move(funcRef));
} else if (name == "tiny") {
- return Expr<T>{Scalar<T>::TINY()};
+ return MakeConstantExpr<T>(kind, Scalar<T>::TINY(kind));
} else if (name == "__builtin_fma") {
CHECK(args.size() == 3);
} else if (name == "__builtin_ieee_next_after") {
@@ -433,16 +448,19 @@ Expr<Type<TypeCategory::Real, KIND>> FoldIntrinsicFunction(
return common::visit(
[&](const auto &yVal) {
using TY = ResultType<decltype(yVal)>;
- return FoldElementalIntrinsic<T, T, TY>(context, std::move(funcRef),
+ return FoldElementalIntrinsic<T, T, TY>(kind, {kind, yVal.kind()},
+ context, std::move(funcRef),
ScalarFunc<T, T, TY>([&](const Scalar<T> &x,
const Scalar<TY> &y) -> Scalar<T> {
- auto xBig{Scalar<LargestReal>::Convert(x).value};
- auto yBig{Scalar<LargestReal>::Convert(y).value};
+ auto xBig{
+ Scalar<LargestReal>::Convert(LargestRealKind, x).value};
+ auto yBig{
+ Scalar<LargestReal>::Convert(LargestRealKind, y).value};
switch (xBig.Compare(yBig)) {
case Relation::Unordered:
context.Warn(common::UsageWarning::FoldingValueChecks,
"IEEE_NEXT_AFTER intrinsic folding: arguments are unordered"_warn_en_US);
- return x.NotANumber();
+ return Scalar<T>::NotANumber(kind);
case Relation::Equal:
break;
case Relation::Less:
@@ -459,7 +477,8 @@ Expr<Type<TypeCategory::Real, KIND>> FoldIntrinsicFunction(
name == "__builtin_ieee_next_down") {
bool upward{name == "__builtin_ieee_next_up"};
const char *iName{upward ? "IEEE_NEXT_UP" : "IEEE_NEXT_DOWN"};
- return FoldElementalIntrinsic<T, T>(context, std::move(funcRef),
+ return FoldElementalIntrinsic<T, T>(kind, {kind}, context,
+ std::move(funcRef),
ScalarFunc<T, T>([&](const Scalar<T> &x) -> Scalar<T> {
auto result{x.NEAREST(upward)};
if (result.flags.test(RealFlag::InvalidArgument)) {
diff --git a/flang/lib/Evaluate/fold-reduction.cpp b/flang/lib/Evaluate/fold-reduction.cpp
index c5f5e1996b6b1..d5ea820b471fb 100644
--- a/flang/lib/Evaluate/fold-reduction.cpp
+++ b/flang/lib/Evaluate/fold-reduction.cpp
@@ -17,7 +17,8 @@ bool CheckReductionDIM(std::optional<int> &dim, FoldingContext &context,
return true; // no DIM= argument
}
if (auto *dimConst{
- Folder<SubscriptInteger>{context}.Folding(arg[*dimIndex])}) {
+ Folder<SubscriptInteger>{SubscriptIntegerKind, context}.Folding(
+ arg[*dimIndex])}) {
if (auto dimScalar{dimConst->GetScalarValue()}) {
auto dimVal{dimScalar->ToInt64()};
if (dimVal >= 1 && dimVal <= rank) {
@@ -37,7 +38,7 @@ Constant<LogicalResult> *GetReductionMASK(
std::optional<ActualArgument> &maskArg, const ConstantSubscripts &shape,
FoldingContext &context) {
Constant<LogicalResult> *mask{
- Folder<LogicalResult>{context}.Folding(maskArg)};
+ Folder<LogicalResult>{LogicalResultKind, context}.Folding(maskArg)};
if (mask &&
!CheckConformance(context.messages(), AsShape(shape),
AsShape(mask->shape()), CheckConformanceFlags::RightScalarExpandable,
diff --git a/flang/lib/Evaluate/fold-reduction.h b/flang/lib/Evaluate/fold-reduction.h
index a068364135295..0acc731fd58b1 100644
--- a/flang/lib/Evaluate/fold-reduction.h
+++ b/flang/lib/Evaluate/fold-reduction.h
@@ -18,9 +18,10 @@ template <typename T>
static Expr<T> FoldDotProduct(
FoldingContext &context, FunctionRef<T> &&funcRef) {
using Element = typename Constant<T>::Element;
+ const int kind{funcRef.kind()};
auto args{funcRef.arguments()};
CHECK(args.size() == 2);
- Folder<T> folder{context};
+ Folder<T> folder{kind, context};
Constant<T> *va{folder.Folding(args[0])};
Constant<T> *vb{folder.Folding(args[1])};
if (va && vb) {
@@ -31,7 +32,7 @@ static Expr<T> FoldDotProduct(
va->size(), vb->size());
return MakeInvalidIntrinsic(std::move(funcRef));
}
- Element sum{};
+ Element sum{Element::Zero(kind)};
bool overflow{false};
if constexpr (T::category == TypeCategory::Complex) {
std::vector<Element> conjugates;
@@ -39,11 +40,11 @@ static Expr<T> FoldDotProduct(
conjugates.emplace_back(x.CONJG());
}
Constant<T> conjgA{
- std::move(conjugates), ConstantSubscripts{va->shape()}};
+ kind, std::move(conjugates), ConstantSubscripts{va->shape()}};
Expr<T> products{Fold(
context, Expr<T>{std::move(conjgA)} * Expr<T>{Constant<T>{*vb}})};
Constant<T> &cProducts{DEREF(UnwrapConstantValue<T>(products))};
- [[maybe_unused]] Element correction{};
+ [[maybe_unused]] Element correction{Element::Zero(kind)};
const auto &rounding{context.targetCharacteristics().roundingMode()};
for (const Element &x : cProducts.values()) {
if constexpr (useKahanSummation) {
@@ -58,12 +59,12 @@ static Expr<T> FoldDotProduct(
}
} else if constexpr (T::category == TypeCategory::Logical) {
Expr<T> conjunctions{Fold(context,
- Expr<T>{LogicalOperation<T::kind>{LogicalOperator::And,
+ Expr<T>{LogicalOperation{LogicalOperator::And,
Expr<T>{Constant<T>{*va}}, Expr<T>{Constant<T>{*vb}}}})};
Constant<T> &cConjunctions{DEREF(UnwrapConstantValue<T>(conjunctions))};
for (const Element &x : cConjunctions.values()) {
if (x.IsTrue()) {
- sum = Element{true};
+ sum = Element{kind, true};
break;
}
}
@@ -88,7 +89,7 @@ static Expr<T> FoldDotProduct(
Expr<T> products{
Fold(context, Expr<T>{Constant<T>{*va}} * Expr<T>{Constant<T>{*vb}})};
Constant<T> &cProducts{DEREF(UnwrapConstantValue<T>(products))};
- [[maybe_unused]] Element correction{};
+ [[maybe_unused]] Element correction{Element::Zero(kind)};
const auto &rounding{context.targetCharacteristics().roundingMode()};
for (const Element &x : cProducts.values()) {
if constexpr (useKahanSummation) {
@@ -105,9 +106,9 @@ static Expr<T> FoldDotProduct(
if (overflow) {
context.Warn(common::UsageWarning::FoldingException,
"DOT_PRODUCT of %s data overflowed during computation"_warn_en_US,
- T::AsFortran());
+ T{kind}.AsFortran());
}
- return Expr<T>{Constant<T>{std::move(sum)}};
+ return MakeConstantExpr<T>(kind, std::move(sum));
}
return Expr<T>{std::move(funcRef)};
}
@@ -132,14 +133,14 @@ template <typename T> struct ArrayAndMask {
Constant<LogicalResult> mask;
};
template <typename T>
-static std::optional<ArrayAndMask<T>> ProcessReductionArgs(
+static std::optional<ArrayAndMask<T>> ProcessReductionArgs(int kind,
FoldingContext &context, ActualArguments &arg, std::optional<int> &dim,
int arrayIndex, std::optional<int> dimIndex = std::nullopt,
std::optional<int> maskIndex = std::nullopt) {
if (arg.empty()) {
return std::nullopt;
}
- Constant<T> *folded{Folder<T>{context}.Folding(arg[arrayIndex])};
+ Constant<T> *folded{Folder<T>{kind, context}.Folding(arg[arrayIndex])};
if (!folded || folded->Rank() < 1) {
return std::nullopt;
}
@@ -153,8 +154,8 @@ static std::optional<ArrayAndMask<T>> ProcessReductionArgs(
if (const Constant<LogicalResult> *origMask{
GetReductionMASK(arg[*maskIndex], folded->shape(), context)}) {
if (auto scalarMask{origMask->GetScalarValue()}) {
- maskElement =
- std::vector<Scalar<LogicalResult>>(n, scalarMask->IsTrue());
+ maskElement = std::vector<Scalar<LogicalResult>>(
+ n, Scalar<LogicalResult>{LogicalResultKind, scalarMask->IsTrue()});
} else {
maskElement = origMask->values();
}
@@ -162,11 +163,12 @@ static std::optional<ArrayAndMask<T>> ProcessReductionArgs(
return std::nullopt;
}
} else {
- maskElement = std::vector<Scalar<LogicalResult>>(n, true);
+ maskElement = std::vector<Scalar<LogicalResult>>(
+ n, Scalar<LogicalResult>{LogicalResultKind, true});
}
return ArrayAndMask<T>{Constant<T>(*folded),
- Constant<LogicalResult>{
- std::move(maskElement), ConstantSubscripts{folded->shape()}}};
+ Constant<LogicalResult>{LogicalResultKind, std::move(maskElement),
+ ConstantSubscripts{folded->shape()}}};
}
// Generalized reduction to an array of one dimension fewer (w/ DIM=)
@@ -174,7 +176,7 @@ static std::optional<ArrayAndMask<T>> ProcessReductionArgs(
// operator()(Scalar<T> &, const ConstantSubscripts &, bool first)
// and Done(Scalar<T> &).
template <typename T, typename ACCUMULATOR, typename ARRAY>
-static Constant<T> DoReduction(const Constant<ARRAY> &array,
+static Constant<T> DoReduction(int kind, const Constant<ARRAY> &array,
const Constant<LogicalResult> &mask, std::optional<int> &dim,
const Scalar<T> &identity, ACCUMULATOR &accumulator) {
ConstantSubscripts at{array.lbounds()};
@@ -220,19 +222,21 @@ static Constant<T> DoReduction(const Constant<ARRAY> &array,
accumulator.Done(elements.back());
}
if constexpr (T::category == TypeCategory::Character) {
- return {static_cast<ConstantSubscript>(identity.size()),
+ return {kind, static_cast<ConstantSubscript>(identity.size()),
std::move(elements), std::move(resultShape)};
} else {
- return {std::move(elements), std::move(resultShape)};
+ return {kind, std::move(elements), std::move(resultShape)};
}
}
// MAXVAL & MINVAL
template <typename T, bool ABS = false> class MaxvalMinvalAccumulator {
public:
- MaxvalMinvalAccumulator(
- RelationalOperator opr, FoldingContext &context, const Constant<T> &array)
- : opr_{opr}, context_{context}, array_{array} {};
+ constexpr int kind() const { return kind_; }
+
+ MaxvalMinvalAccumulator(int kind, RelationalOperator opr,
+ FoldingContext &context, const Constant<T> &array)
+ : kind_{kind}, opr_{opr}, context_{context}, array_{array} {};
void operator()(Scalar<T> &element, const ConstantSubscripts &at,
[[maybe_unused]] bool firstUnmasked) const {
auto aAt{array_.At(at)};
@@ -247,8 +251,9 @@ template <typename T, bool ABS = false> class MaxvalMinvalAccumulator {
return;
}
}
- Expr<LogicalResult> test{PackageRelation(
- opr_, Expr<T>{Constant<T>{aAt}}, Expr<T>{Constant<T>{element}})};
+ Expr<LogicalResult> test{
+ PackageRelation(opr_, MakeConstantExpr<T>(kind(), aAt),
+ MakeConstantExpr<T>(kind(), element))};
auto folded{GetScalarConstantValue<LogicalResult>(
test.Rewrite(context_, std::move(test)))};
CHECK(folded.has_value());
@@ -259,25 +264,27 @@ template <typename T, bool ABS = false> class MaxvalMinvalAccumulator {
void Done(Scalar<T> &) const {}
private:
+ int kind_;
RelationalOperator opr_;
FoldingContext &context_;
const Constant<T> &array_;
};
template <typename T>
-static Expr<T> FoldMaxvalMinval(FoldingContext &context, FunctionRef<T> &&ref,
- RelationalOperator opr, const Scalar<T> &identity) {
+static Expr<T> FoldMaxvalMinval(int kind, FoldingContext &context,
+ FunctionRef<T> &&ref, RelationalOperator opr, const Scalar<T> &identity) {
static_assert(T::category == TypeCategory::Integer ||
T::category == TypeCategory::Unsigned ||
T::category == TypeCategory::Real ||
T::category == TypeCategory::Character);
std::optional<int> dim;
- if (std::optional<ArrayAndMask<T>> arrayAndMask{
- ProcessReductionArgs<T>(context, ref.arguments(), dim,
- /*ARRAY=*/0, /*DIM=*/1, /*MASK=*/2)}) {
- MaxvalMinvalAccumulator<T> accumulator{opr, context, arrayAndMask->array};
- return Expr<T>{DoReduction<T>(
- arrayAndMask->array, arrayAndMask->mask, dim, identity, accumulator)};
+ if (std::optional<ArrayAndMask<T>> arrayAndMask{ProcessReductionArgs<T>(kind,
+ context, ref.arguments(), dim, /*ARRAY=*/0, /*DIM=*/1,
+ /*MASK=*/2)}) {
+ MaxvalMinvalAccumulator<T> accumulator{
+ kind, opr, context, arrayAndMask->array};
+ return Expr<T>{DoReduction<T>(kind, arrayAndMask->array, arrayAndMask->mask,
+ dim, identity, accumulator)};
}
return Expr<T>{std::move(ref)};
}
@@ -315,16 +322,17 @@ static Expr<T> FoldProduct(
T::category == TypeCategory::Unsigned ||
T::category == TypeCategory::Real ||
T::category == TypeCategory::Complex);
+ const int kind{ref.kind()};
std::optional<int> dim;
if (std::optional<ArrayAndMask<T>> arrayAndMask{
- ProcessReductionArgs<T>(context, ref.arguments(), dim,
+ ProcessReductionArgs<T>(kind, context, ref.arguments(), dim,
/*ARRAY=*/0, /*DIM=*/1, /*MASK=*/2)}) {
ProductAccumulator accumulator{arrayAndMask->array};
- auto result{Expr<T>{DoReduction<T>(
- arrayAndMask->array, arrayAndMask->mask, dim, identity, accumulator)}};
+ auto result{Expr<T>{DoReduction<T>(kind, arrayAndMask->array,
+ arrayAndMask->mask, dim, identity, accumulator)}};
if (accumulator.overflow()) {
context.Warn(common::UsageWarning::FoldingException,
- "PRODUCT() of %s data overflowed"_warn_en_US, T::AsFortran());
+ "PRODUCT() of %s data overflowed"_warn_en_US, T{kind}.AsFortran());
}
return result;
}
@@ -337,7 +345,8 @@ template <typename T> class SumAccumulator {
public:
SumAccumulator(const Constant<T> &array, Rounding rounding)
- : array_{array}, rounding_{rounding} {}
+ : kind_{array.kind()}, array_{array}, rounding_{rounding},
+ correction_{Element::Zero(array.kind())} {}
void operator()(
Element &element, const ConstantSubscripts &at, bool /*first*/) {
if constexpr (T::category == TypeCategory::Integer) {
@@ -358,16 +367,17 @@ template <typename T> class SumAccumulator {
T::category != TypeCategory::Unsigned) {
auto corrected{element.Add(correction_, rounding_)};
overflow_ |= corrected.flags.test(RealFlag::Overflow);
- correction_ = Scalar<T>{};
+ correction_ = Element::Zero(kind_);
element = corrected.value;
}
}
private:
+ int kind_;
const Constant<T> &array_;
Rounding rounding_;
bool overflow_{false};
- Element correction_{};
+ Element correction_;
};
template <typename T>
@@ -377,18 +387,19 @@ static Expr<T> FoldSum(FoldingContext &context, FunctionRef<T> &&ref) {
T::category == TypeCategory::Real ||
T::category == TypeCategory::Complex);
using Element = typename Constant<T>::Element;
+ const int kind{ref.kind()};
std::optional<int> dim;
- Element identity{};
+ Element identity{Element::Zero(kind)};
if (std::optional<ArrayAndMask<T>> arrayAndMask{
- ProcessReductionArgs<T>(context, ref.arguments(), dim,
+ ProcessReductionArgs<T>(kind, context, ref.arguments(), dim,
/*ARRAY=*/0, /*DIM=*/1, /*MASK=*/2)}) {
SumAccumulator accumulator{
arrayAndMask->array, context.targetCharacteristics().roundingMode()};
- auto result{Expr<T>{DoReduction<T>(
- arrayAndMask->array, arrayAndMask->mask, dim, identity, accumulator)}};
+ auto result{Expr<T>{DoReduction<T>(kind, arrayAndMask->array,
+ arrayAndMask->mask, dim, identity, accumulator)}};
if (accumulator.overflow()) {
context.Warn(common::UsageWarning::FoldingException,
- "SUM() of %s data overflowed"_warn_en_US, T::AsFortran());
+ "SUM() of %s data overflowed"_warn_en_US, T{kind}.AsFortran());
}
return result;
}
diff --git a/flang/lib/Evaluate/fold.cpp b/flang/lib/Evaluate/fold.cpp
index f20d32077602d..7c44fb5e99a12 100644
--- a/flang/lib/Evaluate/fold.cpp
+++ b/flang/lib/Evaluate/fold.cpp
@@ -51,10 +51,11 @@ std::optional<Constant<SubscriptInteger>> GetConstantSubscript(
std::vector<SubscriptInteger::Scalar> values;
while ((*stride > 0 && *lbi <= *ubi) ||
(*stride < 0 && *lbi >= *ubi)) {
- values.emplace_back(*lbi);
+ values.emplace_back(SubscriptIntegerKind, *lbi);
*lbi += *stride;
}
- return Constant<SubscriptInteger>{std::move(values),
+ return Constant<SubscriptInteger>{SubscriptIntegerKind,
+ std::move(values),
ConstantSubscripts{
static_cast<ConstantSubscript>(values.size())}};
} else {
@@ -214,7 +215,8 @@ std::optional<std::int64_t> GetInt64ArgOr(
Expr<ImpliedDoIndex::Result> FoldOperation(
FoldingContext &context, ImpliedDoIndex &&iDo) {
if (std::optional<ConstantSubscript> value{context.GetImpliedDo(iDo.name)}) {
- return Expr<ImpliedDoIndex::Result>{*value};
+ return MakeConstantExpr<ImpliedDoIndex::Result>(
+ SubscriptIntegerKind, *value);
} else {
return Expr<ImpliedDoIndex::Result>{std::move(iDo)};
}
diff --git a/flang/lib/Evaluate/formatting.cpp b/flang/lib/Evaluate/formatting.cpp
index fcedd15ee1791..7ee14e37baee6 100644
--- a/flang/lib/Evaluate/formatting.cpp
+++ b/flang/lib/Evaluate/formatting.cpp
@@ -73,22 +73,24 @@ llvm::raw_ostream &ConstantBase<RESULT, VALUE>::AsFortran(
o << ',';
}
if constexpr (Result::category == TypeCategory::Integer) {
- o << value.SignedDecimal() << '_' << Result::kind;
+ o << value.SignedDecimal() << '_' << kind_;
} else if constexpr (Result::category == TypeCategory::Unsigned) {
- o << value.UnsignedDecimal() << "U_" << Result::kind;
+ o << value.UnsignedDecimal() << "U_" << kind_;
} else if constexpr (Result::category == TypeCategory::Real ||
Result::category == TypeCategory::Complex) {
- value.AsFortran(o, Result::kind);
+ value.AsFortran(o, kind_);
} else if constexpr (Result::category == TypeCategory::Character) {
- o << Result::kind << '_' << parser::QuoteCharacterLiteral(value, true);
+ o << value.kind() << '_';
+ value.withStdString(
+ [&o](const auto &s) { o << parser::QuoteCharacterLiteral(s, true); });
} else if constexpr (Result::category == TypeCategory::Logical) {
if (!value.IsCanonical()) {
- o << "transfer(" << value.word().ToInt64() << "_8,.false._"
- << Result::kind << ')';
+ o << "transfer(" << value.word().ToInt64() << "_8,.false._" << kind_
+ << ')';
} else if (value.IsTrue()) {
- o << ".true." << '_' << Result::kind;
+ o << ".true." << '_' << kind_;
} else {
- o << ".false." << '_' << Result::kind;
+ o << ".false." << '_' << kind_;
}
} else {
StructureConstructor{result_.derivedTypeSpec(), value}.AsFortran(o);
@@ -109,8 +111,7 @@ std::string ConstantBase<RESULT, VALUE>::AsFortran() const {
return result;
}
-template <int KIND>
-llvm::raw_ostream &Constant<Type<TypeCategory::Character, KIND>>::AsFortran(
+llvm::raw_ostream &Constant<Type<TypeCategory::Character>>::AsFortran(
llvm::raw_ostream &o) const {
bool hasNonDefaultLowerBound{printLbounds && HasNonDefaultLowerBound()};
if (Rank() > 1 || hasNonDefaultLowerBound) {
@@ -125,10 +126,11 @@ llvm::raw_ostream &Constant<Type<TypeCategory::Character, KIND>>::AsFortran(
if (j > 0) {
o << ',';
}
- if (Result::kind != 1) {
- o << Result::kind << '_';
+ if (kind_ != 1) {
+ o << kind_ << '_';
}
- o << parser::QuoteCharacterLiteral(value);
+ value.withStdString(
+ [&o](const auto &s) { o << parser::QuoteCharacterLiteral(s); });
}
if (Rank() > 0) {
o << ']';
@@ -137,8 +139,7 @@ llvm::raw_ostream &Constant<Type<TypeCategory::Character, KIND>>::AsFortran(
return o;
}
-template <int KIND>
-std::string Constant<Type<TypeCategory::Character, KIND>>::AsFortran() const {
+std::string Constant<Type<TypeCategory::Character>>::AsFortran() const {
std::string result;
llvm::raw_string_ostream sstream(result);
AsFortran(sstream);
@@ -353,8 +354,7 @@ enum class Precedence { // in increasing order for sane comparisons
template <typename A> constexpr Precedence ToPrecedence(const A &) {
return Precedence::Top;
}
-template <int KIND>
-static Precedence ToPrecedence(const LogicalOperation<KIND> &x) {
+static Precedence ToPrecedence(const LogicalOperation &x) {
switch (x.logicalOperator) {
SWITCH_COVERS_ALL_CASES
case LogicalOperator::And:
@@ -368,9 +368,7 @@ static Precedence ToPrecedence(const LogicalOperation<KIND> &x) {
return Precedence::Equivalence;
}
}
-template <int KIND> constexpr Precedence ToPrecedence(const Not<KIND> &) {
- return Precedence::Not;
-}
+inline Precedence ToPrecedence(const Not &) { return Precedence::Not; }
template <typename T> constexpr Precedence ToPrecedence(const Relational<T> &) {
return Precedence::Relational;
}
@@ -380,9 +378,7 @@ template <typename T> constexpr Precedence ToPrecedence(const Add<T> &) {
template <typename T> constexpr Precedence ToPrecedence(const Subtract<T> &) {
return Precedence::Additive;
}
-template <int KIND> constexpr Precedence ToPrecedence(const Concat<KIND> &) {
- return Precedence::Additive;
-}
+inline Precedence ToPrecedence(const Concat &) { return Precedence::Additive; }
template <typename T> constexpr Precedence ToPrecedence(const Negate<T> &) {
return Precedence::Negate;
}
@@ -445,20 +441,16 @@ template <typename A>
constexpr OperatorSpelling SpellOperator(const Parentheses<A> &) {
return OperatorSpelling{"(", "", ")"};
}
-template <int KIND>
-static OperatorSpelling SpellOperator(const ComplexComponent<KIND> &x) {
+static OperatorSpelling SpellOperator(const ComplexComponent &x) {
return {x.isImaginaryPart ? "aimag(" : "real(", "", ")"};
}
-template <int KIND>
-constexpr OperatorSpelling SpellOperator(const Not<KIND> &) {
+constexpr OperatorSpelling SpellOperator(const Not &) {
return OperatorSpelling{".NOT.", "", ""};
}
-template <int KIND>
-constexpr OperatorSpelling SpellOperator(const SetLength<KIND> &) {
+constexpr OperatorSpelling SpellOperator(const SetLength &) {
return OperatorSpelling{"%SET_LENGTH(", ",", ")"};
}
-template <int KIND>
-constexpr OperatorSpelling SpellOperator(const ComplexConstructor<KIND> &) {
+constexpr OperatorSpelling SpellOperator(const ComplexConstructor &) {
return OperatorSpelling{"(", ",", ")"};
}
template <typename A> constexpr OperatorSpelling SpellOperator(const Add<A> &) {
@@ -489,12 +481,10 @@ static OperatorSpelling SpellOperator(const Extremum<A> &x) {
return OperatorSpelling{
x.ordering == Ordering::Less ? "min(" : "max(", ",", ")"};
}
-template <int KIND>
-constexpr OperatorSpelling SpellOperator(const Concat<KIND> &) {
+constexpr OperatorSpelling SpellOperator(const Concat &) {
return OperatorSpelling{"", "//", ""};
}
-template <int KIND>
-static OperatorSpelling SpellOperator(const LogicalOperation<KIND> &x) {
+static OperatorSpelling SpellOperator(const LogicalOperation &x) {
return OperatorSpelling{"", AsFortran(x.logicalOperator), ""};
}
template <typename T>
@@ -556,7 +546,7 @@ llvm::raw_ostream &Convert<TO, FROMCAT>::AsFortran(llvm::raw_ostream &o) const {
} else {
this->left().AsFortran(o << "uint(");
}
- return o << ",kind=" << TO::kind << ')';
+ return o << ",kind=" << kind() << ')';
}
llvm::raw_ostream &Relational<SomeType>::AsFortran(llvm::raw_ostream &o) const {
@@ -577,7 +567,8 @@ template <typename T>
llvm::raw_ostream &EmitArray(llvm::raw_ostream &o, const ImpliedDo<T> &implDo) {
o << '(';
EmitArray(o, implDo.values());
- o << ',' << ImpliedDoIndex::Result::AsFortran()
+ o << ','
+ << DynamicType{TypeCategory::Integer, SubscriptIntegerKind}.AsFortran()
<< "::" << implDo.name().ToString() << '=';
implDo.lower().AsFortran(o) << ',';
implDo.upper().AsFortran(o) << ',';
@@ -604,9 +595,7 @@ llvm::raw_ostream &ArrayConstructor<T>::AsFortran(llvm::raw_ostream &o) const {
return o << ']';
}
-template <int KIND>
-llvm::raw_ostream &
-ArrayConstructor<Type<TypeCategory::Character, KIND>>::AsFortran(
+llvm::raw_ostream &ArrayConstructor<Type<TypeCategory::Character>>::AsFortran(
llvm::raw_ostream &o) const {
o << '[';
if (const auto *len{LEN()}) {
@@ -891,7 +880,7 @@ llvm::raw_ostream &DescriptorInquiry::AsFortran(llvm::raw_ostream &o) const {
o << ",dim=" << (dimension_ + 1);
}
}
- return o << ",kind=" << DescriptorInquiry::Result::kind << ")";
+ return o << ",kind=" << DescriptorInquiry::kind() << ")";
}
llvm::raw_ostream &RankOneBoundElement::AsFortran(llvm::raw_ostream &o) const {
diff --git a/flang/lib/Evaluate/host.h b/flang/lib/Evaluate/host.h
index 7f6bf76bb5c53..e6756de68c36b 100644
--- a/flang/lib/Evaluate/host.h
+++ b/flang/lib/Evaluate/host.h
@@ -55,6 +55,34 @@ class HostFloatingPointEnvironment {
// Type mapping from F18 types to host types
struct UnsupportedType {}; // There is no host type for the F18 type
+/// Because HostType<T> depends on the type's kind as well, KIND must be part of
+/// the template as well where Type<CAT> does not.
+template <common::TypeCategory CAT, int KIND> struct TypeKind {
+ using FortranType = Fortran::evaluate::Type<CAT>;
+ using Scalar = Fortran::evaluate::Scalar<FortranType>;
+ static constexpr common::TypeCategory category{CAT};
+ static constexpr int kind{KIND};
+
+ static constexpr DynamicType GetType() { return DynamicType{CAT, KIND}; }
+
+ // Meaningful for COMPLEX only: the real component's (category, kind) tag.
+ using Part = TypeKind<common::TypeCategory::Real, KIND>;
+};
+
+using AllHostKindTypes = std::tuple<TypeKind<TypeCategory::Integer, 1>,
+ TypeKind<TypeCategory::Integer, 2>, TypeKind<TypeCategory::Integer, 4>,
+ TypeKind<TypeCategory::Integer, 8>, TypeKind<TypeCategory::Integer, 16>,
+ TypeKind<TypeCategory::Real, 2>, TypeKind<TypeCategory::Real, 3>,
+ TypeKind<TypeCategory::Real, 4>, TypeKind<TypeCategory::Real, 8>,
+ TypeKind<TypeCategory::Real, 10>, TypeKind<TypeCategory::Real, 16>,
+ TypeKind<TypeCategory::Complex, 2>, TypeKind<TypeCategory::Complex, 3>,
+ TypeKind<TypeCategory::Complex, 4>, TypeKind<TypeCategory::Complex, 8>,
+ TypeKind<TypeCategory::Complex, 10>, TypeKind<TypeCategory::Complex, 16>,
+ TypeKind<TypeCategory::Logical, 1>, TypeKind<TypeCategory::Logical, 2>,
+ TypeKind<TypeCategory::Logical, 4>, TypeKind<TypeCategory::Logical, 8>,
+ TypeKind<TypeCategory::Character, 1>, TypeKind<TypeCategory::Character, 2>,
+ TypeKind<TypeCategory::Character, 4>>;
+
template <typename FTN_T> struct HostTypeHelper {
using Type = UnsupportedType;
};
@@ -64,6 +92,19 @@ template <typename... T> constexpr inline bool HostTypeExists() {
return (... && (!std::is_same_v<HostType<T>, UnsupportedType>));
}
+#if 0
+template <typename, typename = void> struct HasValueMethod : std::false_type {};
+template <typename T>
+struct HasValueMethod<T,
+ std::void_t<decltype(std::declval<const T &>().value())>> : std::true_type {
+};
+
+template <typename, typename = void> struct HasRawBits : std::false_type {};
+template <typename T>
+struct HasRawBits<T, std::void_t<decltype(std::declval<const T &>().RawBits())>>
+ : std::true_type {};
+#endif
+
// Type mapping from host types to F18 types FortranType<HOST_T> is defined
// after all HosTypeHelper definition because it reverses them to avoid
// duplication.
@@ -73,13 +114,13 @@ template <typename FTN_T>
inline constexpr Scalar<FTN_T> CastHostToFortran(const HostType<FTN_T> &x) {
static_assert(HostTypeExists<FTN_T>());
if constexpr (FTN_T::category == TypeCategory::Complex &&
- sizeof(Scalar<FTN_T>) != sizeof(HostType<FTN_T>)) {
+ 2 * sizeof(HostType<typename FTN_T::Part>) != sizeof(HostType<FTN_T>)) {
// X87 is usually padded to 12 or 16bytes. Need to cast piecewise for
// complex
return Scalar<FTN_T>{CastHostToFortran<typename FTN_T::Part>(std::real(x)),
CastHostToFortran<typename FTN_T::Part>(std::imag(x))};
} else {
- return *reinterpret_cast<const Scalar<FTN_T> *>(&x);
+ return Scalar<FTN_T>::FromRawBytes(FTN_T::kind, &x, sizeof(x));
}
}
@@ -91,36 +132,32 @@ inline constexpr HostType<FTN_T> CastFortranToHost(const Scalar<FTN_T> &x) {
using FortranPartType = typename FTN_T::Part;
return HostType<FTN_T>{CastFortranToHost<FortranPartType>(x.REAL()),
CastFortranToHost<FortranPartType>(x.AIMAG())};
- } else if constexpr (std::is_same_v<FTN_T, Type<TypeCategory::Real, 10>>) {
- // x87 80-bit floating-point occupies 16 bytes as a C "long double";
- // copy the data to avoid a legitimate (but benign due to little-endianness)
- // warning from GCC >= 11.2.0.
- HostType<FTN_T> y;
- std::memcpy(&y, &x, sizeof x);
- return y;
} else {
- static_assert(sizeof x == sizeof(HostType<FTN_T>));
- return *reinterpret_cast<const HostType<FTN_T> *>(&x);
+ CHECK(x.bytesStored() == sizeof(HostType<FTN_T>));
+ HostType<FTN_T> result;
+ CHECK(x.kind() == FTN_T::kind);
+ x.StoreRawBytes(&result, sizeof(result));
+ return result;
}
}
-template <> struct HostTypeHelper<Type<TypeCategory::Integer, 1>> {
+template <> struct HostTypeHelper<TypeKind<TypeCategory::Integer, 1>> {
using Type = std::int8_t;
};
-template <> struct HostTypeHelper<Type<TypeCategory::Integer, 2>> {
+template <> struct HostTypeHelper<TypeKind<TypeCategory::Integer, 2>> {
using Type = std::int16_t;
};
-template <> struct HostTypeHelper<Type<TypeCategory::Integer, 4>> {
+template <> struct HostTypeHelper<TypeKind<TypeCategory::Integer, 4>> {
using Type = std::int32_t;
};
-template <> struct HostTypeHelper<Type<TypeCategory::Integer, 8>> {
+template <> struct HostTypeHelper<TypeKind<TypeCategory::Integer, 8>> {
using Type = std::int64_t;
};
-template <> struct HostTypeHelper<Type<TypeCategory::Integer, 16>> {
+template <> struct HostTypeHelper<TypeKind<TypeCategory::Integer, 16>> {
#if (defined(__GNUC__) || defined(__clang__)) && defined(__SIZEOF_INT128__)
using Type = __int128_t;
#else
@@ -133,7 +170,7 @@ template <> struct HostTypeHelper<Type<TypeCategory::Integer, 16>> {
template <>
struct HostTypeHelper<
- Type<TypeCategory::Real, common::RealKindForPrecision(24)>> {
+ TypeKind<TypeCategory::Real, common::RealKindForPrecision(24)>> {
// IEEE 754 32bits
using Type = std::conditional_t<sizeof(float) == 4 &&
std::numeric_limits<float>::is_iec559,
@@ -142,7 +179,7 @@ struct HostTypeHelper<
template <>
struct HostTypeHelper<
- Type<TypeCategory::Real, common::RealKindForPrecision(53)>> {
+ TypeKind<TypeCategory::Real, common::RealKindForPrecision(53)>> {
// IEEE 754 64bits
using Type = std::conditional_t<sizeof(double) == 8 &&
std::numeric_limits<double>::is_iec559,
@@ -151,7 +188,7 @@ struct HostTypeHelper<
template <>
struct HostTypeHelper<
- Type<TypeCategory::Real, common::RealKindForPrecision(64)>> {
+ TypeKind<TypeCategory::Real, common::RealKindForPrecision(64)>> {
// X87 80bits
using Type = std::conditional_t<sizeof(long double) >= 10 &&
std::numeric_limits<long double>::digits == 64 &&
@@ -160,12 +197,12 @@ struct HostTypeHelper<
};
#if HAS_QUADMATHLIB
-template <> struct HostTypeHelper<Type<TypeCategory::Real, 16>> {
+template <> struct HostTypeHelper<TypeKind<TypeCategory::Real, 16>> {
// IEEE 754 128bits
using Type = __float128;
};
#else
-template <> struct HostTypeHelper<Type<TypeCategory::Real, 16>> {
+template <> struct HostTypeHelper<TypeKind<TypeCategory::Real, 16>> {
// IEEE 754 128bits
using Type = std::conditional_t<sizeof(long double) == 16 &&
std::numeric_limits<long double>::digits == 113 &&
@@ -174,26 +211,28 @@ template <> struct HostTypeHelper<Type<TypeCategory::Real, 16>> {
};
#endif
-template <int KIND> struct HostTypeHelper<Type<TypeCategory::Complex, KIND>> {
- using RealT = Fortran::evaluate::Type<TypeCategory::Real, KIND>;
+template <int KIND>
+struct HostTypeHelper<TypeKind<TypeCategory::Complex, KIND>> {
+ using RealT = TypeKind<TypeCategory::Real, KIND>;
using Type = std::conditional_t<HostTypeExists<RealT>(),
std::complex<HostType<RealT>>, UnsupportedType>;
};
#if HAS_QUADMATHLIB
-template <> struct HostTypeHelper<Type<TypeCategory::Complex, 16>> {
- using RealT = Fortran::evaluate::Type<TypeCategory::Real, 16>;
+template <> struct HostTypeHelper<TypeKind<TypeCategory::Complex, 16>> {
+ using RealT = TypeKind<TypeCategory::Real, 16>;
using Type = __complex128;
};
#endif
-template <int KIND> struct HostTypeHelper<Type<TypeCategory::Logical, KIND>> {
+template <int KIND>
+struct HostTypeHelper<TypeKind<TypeCategory::Logical, KIND>> {
using Type = std::conditional_t<KIND <= 8, std::uint8_t, UnsupportedType>;
};
-template <int KIND> struct HostTypeHelper<Type<TypeCategory::Character, KIND>> {
- using Type =
- Scalar<typename Fortran::evaluate::Type<TypeCategory::Character, KIND>>;
+template <int KIND>
+struct HostTypeHelper<TypeKind<TypeCategory::Character, KIND>> {
+ using Type = typename TypeKind<TypeCategory::Character, KIND>::Scalar;
};
// Type mapping from host types to F18 types. This need to be placed after all
@@ -206,13 +245,13 @@ struct IndexInTupleHelper<T, std::tuple<TT...>> {
struct UnknownType {}; // the host type does not match any F18 types
template <typename HOST_T> struct FortranTypeHelper {
using HostTypeMapping =
- common::MapTemplate<HostType, AllIntrinsicTypes, std::tuple>;
+ common::MapTemplate<HostType, AllHostKindTypes, std::tuple>;
static constexpr int index{
IndexInTupleHelper<HOST_T, HostTypeMapping>::value};
// Both conditional types are "instantiated", so a valid type must be
// created for invalid index even if not used.
using Type = std::conditional_t<index >= 0,
- std::tuple_element_t<(index >= 0) ? index : 0, AllIntrinsicTypes>,
+ std::tuple_element_t<(index >= 0) ? index : 0, AllHostKindTypes>,
UnknownType>;
};
diff --git a/flang/lib/Evaluate/initial-image.cpp b/flang/lib/Evaluate/initial-image.cpp
index 050c55e399b57..672b88f3d954e 100644
--- a/flang/lib/Evaluate/initial-image.cpp
+++ b/flang/lib/Evaluate/initial-image.cpp
@@ -96,15 +96,16 @@ class AsConstantHelper {
extents_{extents}, padWithZero_{padWithZero}, offset_{offset} {
CHECK(!type.IsPolymorphic());
}
- template <typename T> Result Test() {
+ template <typename T> Result Test(int kind) {
if (T::category != type_.category()) {
return std::nullopt;
}
if constexpr (T::category != TypeCategory::Derived) {
- if (T::kind != type_.kind()) {
+ if (kind != type_.kind()) {
return std::nullopt;
}
}
+ CHECK_KIND(kind, T);
using Const = Constant<T>;
using Scalar = typename Const::Element;
std::optional<uint64_t> optElements{TotalElementCount(extents_)};
@@ -159,53 +160,54 @@ class AsConstantHelper {
return AsGenericExpr(
Const{derived, std::move(typedValue), std::move(extents_)});
} else if constexpr (T::category == TypeCategory::Character) {
- auto length{static_cast<ConstantSubscript>(stride) / T::kind};
+ auto length{static_cast<ConstantSubscript>(stride) / kind};
+ llvm::SmallVector<char, 256> buffer;
+ const char *data{GetTailPaddedData(offset_, elements * stride, buffer)};
for (std::size_t j{0}; j < elements; ++j) {
- using Char = typename Scalar::value_type;
- auto at{static_cast<std::size_t>(offset_ + j * stride)};
- auto chunk{length};
- if (at + chunk > image_.data_.size()) {
- CHECK(padWithZero_);
- if (at >= image_.data_.size()) {
- chunk = 0;
- } else {
- chunk = image_.data_.size() - at;
- }
- }
- if (chunk > 0) {
- const Char *data{reinterpret_cast<const Char *>(&image_.data_[at])};
- typedValue[j].assign(data, chunk);
- }
- if (chunk < length && padWithZero_) {
- typedValue[j].append(length - chunk, Char{});
- }
+ typedValue[j] = evaluate::Scalar<T>::FromRawBytes(
+ kind, data + j * stride, length * kind);
}
return AsGenericExpr(
- Const{length, std::move(typedValue), std::move(extents_)});
+ Const{kind, length, std::move(typedValue), std::move(extents_)});
} else {
// Lengthless intrinsic type
- CHECK(sizeof(Scalar) <= stride);
- for (std::size_t j{0}; j < elements; ++j) {
- auto at{static_cast<std::size_t>(offset_ + j * stride)};
- std::size_t chunk{sizeof(Scalar)};
- if (at + chunk > image_.data_.size()) {
- CHECK(padWithZero_);
- if (at >= image_.data_.size()) {
- chunk = 0;
- } else {
- chunk = image_.data_.size() - at;
- }
- }
- // TODO endianness
- if (chunk > 0) {
- std::memcpy(&typedValue[j], &image_.data_[at], chunk);
- }
- }
- return AsGenericExpr(Const{std::move(typedValue), std::move(extents_)});
+ llvm::SmallVector<char, 256> buffer;
+ const char *data{GetTailPaddedData(offset_,
+ elements == 0 ? 0
+ : (elements - 1) * stride +
+ evaluate::Scalar<T>::bytesStored(kind),
+ buffer)};
+ // TODO endianness
+ LoadSerialValues(kind, data,
+ llvm::MutableArrayRef<evaluate::Scalar<T>>(typedValue), stride);
+ return AsGenericExpr(
+ Const{kind, std::move(typedValue), std::move(extents_)});
}
}
private:
+ /// Returns the image's bytes, extended with zero bytes when a value is being
+ /// built whose representation reaches past the end of the image. That
+ /// happens when TRANSFER() is folded with a MOLD= whose representation is
+ /// longer than SOURCE=, and when deserializing a scalar accesses more bytes
+ /// than its element size because its host representation is padded (e.g.,
+ /// REAL(10)). F2023 16.9.212 leaves the bytes beyond SOURCE= processor
+ /// dependent; flang zero-fills them, as the runtime does.
+ const char *GetTailPaddedData(std::size_t offset, std::size_t bytes,
+ llvm::SmallVectorImpl<char> &buffer) const {
+ if (bytes + offset <= image_.data_.size()) {
+ // If no padding is needed, use original data without copy
+ return image_.data_.data() + offset;
+ }
+ CHECK(padWithZero_);
+ buffer.assign(bytes, 0);
+ if (offset < image_.data_.size()) {
+ std::memcpy(buffer.data(), image_.data_.data() + offset,
+ image_.data_.size() - offset);
+ }
+ return buffer.data();
+ }
+
FoldingContext &context_;
const DynamicType &type_;
std::optional<std::int64_t> charLength_;
@@ -219,7 +221,7 @@ std::optional<Expr<SomeType>> InitialImage::AsConstant(FoldingContext &context,
const DynamicType &type, std::optional<std::int64_t> charLength,
const ConstantSubscripts &extents, bool padWithZero,
ConstantSubscript offset) const {
- return common::SearchTypes(AsConstantHelper{
+ return SearchTypes(AsConstantHelper{
context, type, charLength, extents, *this, padWithZero, offset});
}
diff --git a/flang/lib/Evaluate/int-power.h b/flang/lib/Evaluate/int-power.h
index 2ee012ceb77a3..3acdec06286f7 100644
--- a/flang/lib/Evaluate/int-power.h
+++ b/flang/lib/Evaluate/int-power.h
@@ -19,9 +19,11 @@ template <typename REAL, typename INT>
ValueWithRealFlags<REAL> TimesIntPowerOf(const REAL &factor, const REAL &base,
const INT &power,
Rounding rounding = TargetCharacteristics::defaultRounding) {
+ const int realKind{base.kind()};
+ CHECK(factor.kind() == base.kind());
ValueWithRealFlags<REAL> result{factor};
if (base.IsNotANumber()) {
- result.value = REAL::NotANumber();
+ result.value = REAL::NotANumber(realKind);
result.flags.set(RealFlag::InvalidArgument);
} else if (power.IsZero()) {
if (base.IsZero() || base.IsInfinite()) {
@@ -31,7 +33,7 @@ ValueWithRealFlags<REAL> TimesIntPowerOf(const REAL &factor, const REAL &base,
bool negativePower{power.IsNegative()};
INT absPower{power.ABS().value};
REAL squares{base};
- int nbits{INT::bits - absPower.LEADZ()};
+ int nbits{absPower.bits() - absPower.LEADZ()};
for (int j{0}; j < nbits; ++j) {
if (j > 0) { // avoid spurious overflow on last iteration
squares =
@@ -54,7 +56,9 @@ ValueWithRealFlags<REAL> TimesIntPowerOf(const REAL &factor, const REAL &base,
template <typename REAL, typename INT>
ValueWithRealFlags<REAL> IntPower(const REAL &base, const INT &power,
Rounding rounding = TargetCharacteristics::defaultRounding) {
- REAL one{REAL::FromInteger(INT{1}).value};
+ const int realKind{base.kind()};
+ const int intKind{power.kind()};
+ REAL one{REAL::FromInteger(realKind, INT{intKind, 1}).value};
return TimesIntPowerOf(one, base, power, rounding);
}
} // namespace Fortran::evaluate
diff --git a/flang/lib/Evaluate/integer-value-impl.cpp b/flang/lib/Evaluate/integer-value-impl.cpp
new file mode 100644
index 0000000000000..a1f8708423998
--- /dev/null
+++ b/flang/lib/Evaluate/integer-value-impl.cpp
@@ -0,0 +1,583 @@
+//===-- lib/Evaluate/integer-value.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 "integer-value-impl.h"
+#include "flang/Evaluate/integer-value.h"
+#include <new>
+
+namespace Fortran::evaluate::value {
+
+IntegerValueImpl IntegerValueImpl::Zero(int kind) {
+ return withWordProto(kind, [](auto proto) {
+ using T = decltype(proto);
+ return FromWord(T{});
+ });
+}
+
+IntegerValueImpl IntegerValueImpl::FromRawBytes(
+ int kind, const void *raw, std::size_t expectedSize) {
+ CHECK(expectedSize == IntegerValue::bytesStored(kind));
+
+ return withWordProto(kind, [&](auto proto) {
+ assert(IntegerValue::bytesStored(kind) == sizeof(proto));
+ std::decay_t<decltype(proto)> t{};
+ memcpy(&t, raw, sizeof(proto));
+ return FromWord(t);
+ });
+}
+
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+LLVM_DUMP_METHOD void IntegerValueImpl::dump() const {
+ llvm::errs() << SignedDecimal() << '_' << kind() << '\n';
+}
+#endif
+
+int IntegerValueImpl::kind() const {
+ if (IsMonostate()) {
+ llvm_unreachable("default-initialized value representing 0 with unknown "
+ "width does not know its kind");
+ return 0;
+ }
+ return withWord(
+ [](const auto &x) -> int { return std::decay_t<decltype(x)>::bits / 8; });
+}
+
+int IntegerValueImpl::bits() const {
+ if (IsMonostate()) {
+ return 0;
+ }
+ return withWord(
+ [](const auto &x) -> int { return std::decay_t<decltype(x)>::bits; });
+}
+
+bool IntegerValueImpl::IsZero() const {
+ if (IsMonostate()) {
+ return true; // uninitialized int representing 0 is zero
+ }
+ return withWord([](const auto &x) { return x.IsZero(); });
+}
+
+bool IntegerValueImpl::operator==(const IntegerValueImpl &y) const {
+ if (IsMonostate() && y.IsMonostate()) {
+ return true;
+ }
+ if (IsMonostate() != y.IsMonostate() || bits() != y.bits()) {
+ llvm_unreachable("uncomparable integers");
+ return false;
+ }
+ return withWord([&](const auto &x) -> bool {
+ using T = std::decay_t<decltype(x)>;
+ return x == std::get<T>(y.storage_);
+ });
+}
+
+IntegerValueImpl IntegerValueImpl::MASKL(int kind, int places) {
+ return withWordProto(kind, [&](auto proto) {
+ using T = decltype(proto);
+ return FromWord(T::MASKL(places));
+ });
+}
+
+IntegerValueImpl IntegerValueImpl::MASKR(int kind, int places) {
+ return withWordProto(kind, [&](auto proto) {
+ using T = decltype(proto);
+ return FromWord(T::MASKR(places));
+ });
+}
+
+IntegerValueImpl IntegerValueImpl::HUGE(int kind) {
+ return withWordProto(kind, [&](auto proto) {
+ using T = decltype(proto);
+ return FromWord(T::HUGE());
+ });
+}
+
+IntegerValueImpl IntegerValueImpl::Least(int kind) {
+ return withWordProto(kind, [&](auto proto) {
+ using T = decltype(proto);
+ return FromWord(T::Least());
+ });
+}
+
+bool IntegerValueImpl::IsNegative() const {
+ if (IsMonostate()) {
+ return false; // uninitialized int representing 0 is not negative
+ }
+ return withWord([](const auto &x) { return x.IsNegative(); });
+}
+
+std::uint64_t IntegerValueImpl::ToUInt64() const {
+ if (IsMonostate()) {
+ return 0;
+ }
+ return withWord([](const auto &x) { return x.ToUInt64(); });
+}
+
+std::int64_t IntegerValueImpl::ToInt64() const {
+ if (IsMonostate()) {
+ return 0;
+ }
+ return withWord([](const auto &x) { return x.ToInt64(); });
+}
+
+Ordering IntegerValueImpl::CompareSigned(const IntegerValueImpl &y) const {
+ if (IsMonostate()) {
+ llvm_unreachable("uncomparable ints");
+ return Ordering::Equal;
+ }
+ return withWord([&](const auto &x) -> Ordering {
+ using T = std::decay_t<decltype(x)>;
+ return x.CompareSigned(Coerce<T>(y));
+ });
+}
+
+Ordering IntegerValueImpl::CompareUnsigned(const IntegerValueImpl &y) const {
+ if (IsMonostate()) {
+ llvm_unreachable("uncomparable ints; cast bitwidth first");
+ return Ordering::Equal;
+ }
+ return withWord([&](const auto &x) -> Ordering {
+ using T = std::decay_t<decltype(x)>;
+ return x.CompareUnsigned(Coerce<T>(y));
+ });
+}
+
+Ordering IntegerValueImpl::CompareToZeroSigned() const {
+ if (IsMonostate()) {
+ llvm_unreachable("uncomparable ints");
+ return Ordering::Equal;
+ }
+ return withWord([](const auto &x) { return x.CompareToZeroSigned(); });
+}
+
+typename IntegerValueImpl::ValueWithOverflow IntegerValueImpl::Negate() const {
+ if (IsMonostate()) {
+ return ValueWithOverflow{}; // negation of uninitialized int 0 is zero
+ }
+ return withWord([](const auto &x) -> ValueWithOverflow {
+ auto r{x.Negate()};
+ return {FromWord(r.value), r.overflow};
+ });
+}
+
+typename IntegerValueImpl::ValueWithOverflow IntegerValueImpl::ABS() const {
+ if (IsMonostate()) {
+ return ValueWithOverflow{}; // absolute of uninitialized int 0 is zero
+ }
+ return withWord([](const auto &x) -> ValueWithOverflow {
+ auto r{x.ABS()};
+ return {FromWord(r.value), r.overflow};
+ });
+}
+
+typename IntegerValueImpl::ValueWithCarry IntegerValueImpl::AddUnsigned(
+ const IntegerValueImpl &y, bool carryIn) const {
+ if (IsMonostate()) {
+ llvm_unreachable("incompatiable ints");
+ return ValueWithCarry{};
+ }
+ return withWord([&](const auto &x) -> ValueWithCarry {
+ using T = std::decay_t<decltype(x)>;
+ auto r{x.AddUnsigned(Coerce<T>(y), carryIn)};
+ return {FromWord(r.value), r.carry};
+ });
+}
+
+typename IntegerValueImpl::ValueWithOverflow IntegerValueImpl::AddSigned(
+ const IntegerValueImpl &y) const {
+ if (IsMonostate()) {
+ llvm_unreachable("incompatible ints");
+ return ValueWithOverflow{};
+ }
+ return withWord([&](const auto &x) -> ValueWithOverflow {
+ using T = std::decay_t<decltype(x)>;
+ auto r{x.AddSigned(Coerce<T>(y))};
+ return {FromWord(r.value), r.overflow};
+ });
+}
+
+typename IntegerValueImpl::ValueWithOverflow IntegerValueImpl::SubtractSigned(
+ const IntegerValueImpl &y) const {
+ if (IsMonostate()) {
+ llvm_unreachable("incompatiable ints");
+ return ValueWithOverflow{};
+ }
+ return withWord([&](const auto &x) -> ValueWithOverflow {
+ using T = std::decay_t<decltype(x)>;
+ auto r{x.SubtractSigned(Coerce<T>(y))};
+ return {FromWord(r.value), r.overflow};
+ });
+}
+
+typename IntegerValueImpl::ValueWithOverflow IntegerValueImpl::DIM(
+ const IntegerValueImpl &y) const {
+ if (IsMonostate()) {
+ llvm_unreachable("incompatiable ints");
+ return ValueWithOverflow{};
+ }
+ // DIM(X,Y) = MAX(X-Y, 0)
+ if (CompareSigned(y) != Ordering::Greater) {
+ return {Zero(kind()), false};
+ }
+ return SubtractSigned(y);
+}
+
+typename IntegerValueImpl::ValueWithOverflow IntegerValueImpl::SIGN(
+ const IntegerValueImpl &sign) const {
+ if (IsMonostate()) {
+ llvm_unreachable("incompatiable ints");
+ return ValueWithOverflow{};
+ }
+ bool toNegative{sign.IsNegative()};
+ if (toNegative == IsNegative()) {
+ return {*this, false};
+ }
+ if (toNegative) {
+ return Negate();
+ }
+ return ABS();
+}
+
+typename IntegerValueImpl::Product IntegerValueImpl::MultiplySigned(
+ const IntegerValueImpl &y) const {
+ if (IsMonostate()) {
+ llvm_unreachable("incompatiable ints");
+ return Product{};
+ }
+ return withWord([&](const auto &x) -> Product {
+ using T = std::decay_t<decltype(x)>;
+ auto r{x.MultiplySigned(Coerce<T>(y))};
+ return {FromWord(r.upper), FromWord(r.lower),
+ r.SignedMultiplicationOverflowed()};
+ });
+}
+
+typename IntegerValueImpl::Product IntegerValueImpl::MultiplyUnsigned(
+ const IntegerValueImpl &y) const {
+ if (IsMonostate()) {
+ llvm_unreachable("incompatiable ints");
+ return Product{};
+ }
+ return withWord([&](const auto &x) -> Product {
+ using T = std::decay_t<decltype(x)>;
+ auto r{x.MultiplyUnsigned(Coerce<T>(y))};
+ return {FromWord(r.upper), FromWord(r.lower), false};
+ });
+}
+
+typename IntegerValueImpl::QuotientWithRemainder IntegerValueImpl::DivideSigned(
+ const IntegerValueImpl &y) const {
+ if (IsMonostate()) {
+ llvm_unreachable("incompatiable ints");
+ return QuotientWithRemainder{};
+ }
+ return withWord([&](const auto &x) -> QuotientWithRemainder {
+ using T = std::decay_t<decltype(x)>;
+ auto r{x.DivideSigned(Coerce<T>(y))};
+ return {FromWord(r.quotient), FromWord(r.remainder), r.divisionByZero,
+ r.overflow};
+ });
+}
+
+typename IntegerValueImpl::QuotientWithRemainder
+IntegerValueImpl::DivideUnsigned(const IntegerValueImpl &y) const {
+ if (IsMonostate()) {
+ llvm_unreachable("incompatiable ints");
+ return QuotientWithRemainder{};
+ }
+ return withWord([&](const auto &x) -> QuotientWithRemainder {
+ using T = std::decay_t<decltype(x)>;
+ auto r{x.DivideUnsigned(Coerce<T>(y))};
+ return {FromWord(r.quotient), FromWord(r.remainder), r.divisionByZero,
+ r.overflow};
+ });
+}
+
+typename IntegerValueImpl::ValueWithOverflow IntegerValueImpl::MODULO(
+ const IntegerValueImpl &y) const {
+ if (IsMonostate()) {
+ llvm_unreachable("incompatiable ints");
+ return ValueWithOverflow{};
+ }
+ return withWord([&](const auto &x) -> ValueWithOverflow {
+ using T = std::decay_t<decltype(x)>;
+ auto r{x.MODULO(Coerce<T>(y))};
+ return {FromWord(r.value), r.overflow};
+ });
+}
+
+typename IntegerValueImpl::PowerWithErrors IntegerValueImpl::Power(
+ const IntegerValueImpl &e) const {
+ if (IsMonostate()) {
+ llvm_unreachable("incompatiable ints");
+ return PowerWithErrors{};
+ }
+ return withWord([&](const auto &x) -> PowerWithErrors {
+ using T = std::decay_t<decltype(x)>;
+ auto r{x.Power(Coerce<T>(e))};
+ return {FromWord(r.power), r.divisionByZero, r.overflow, r.zeroToZero};
+ });
+}
+
+IntegerValueImpl IntegerValueImpl::NOT() const {
+ if (IsMonostate()) {
+ llvm_unreachable("incompatible ints");
+ return IntegerValueImpl{};
+ }
+ return withWord([](const auto &x) { return FromWord(x.NOT()); });
+}
+
+IntegerValueImpl IntegerValueImpl::IAND(const IntegerValueImpl &y) const {
+ if (IsMonostate()) {
+ llvm_unreachable("incompatiable ints");
+ return IntegerValueImpl{};
+ }
+ return withWord([&](const auto &x) {
+ using T = std::decay_t<decltype(x)>;
+ return FromWord(x.IAND(Coerce<T>(y)));
+ });
+}
+
+IntegerValueImpl IntegerValueImpl::IOR(const IntegerValueImpl &y) const {
+ if (IsMonostate()) {
+ llvm_unreachable("incompatible ints");
+ return IntegerValueImpl{};
+ }
+ return withWord([&](const auto &x) {
+ using T = std::decay_t<decltype(x)>;
+ return FromWord(x.IOR(Coerce<T>(y)));
+ });
+}
+
+IntegerValueImpl IntegerValueImpl::IEOR(const IntegerValueImpl &y) const {
+ if (IsMonostate()) {
+ llvm_unreachable("incompatible ints");
+ return IntegerValueImpl{};
+ }
+ return withWord([&](const auto &x) {
+ using T = std::decay_t<decltype(x)>;
+ return FromWord(x.IEOR(Coerce<T>(y)));
+ });
+}
+
+IntegerValueImpl IntegerValueImpl::MERGE_BITS(
+ const IntegerValueImpl &y, const IntegerValueImpl &mask) const {
+ if (IsMonostate()) {
+ llvm_unreachable("incompatible ints");
+ return IntegerValueImpl{};
+ }
+ return withWord([&](const auto &x) {
+ using T = std::decay_t<decltype(x)>;
+ return FromWord(x.MERGE_BITS(Coerce<T>(y), Coerce<T>(mask)));
+ });
+}
+
+IntegerValueImpl IntegerValueImpl::SHIFTL(int count) const {
+ if (IsMonostate()) {
+ llvm_unreachable("incompatible ints");
+ return IntegerValueImpl{};
+ }
+ return withWord([&](const auto &x) { return FromWord(x.SHIFTL(count)); });
+}
+
+IntegerValueImpl IntegerValueImpl::SHIFTR(int count) const {
+ if (IsMonostate()) {
+ llvm_unreachable("incompatible ints");
+ return IntegerValueImpl{};
+ }
+ return withWord([&](const auto &x) { return FromWord(x.SHIFTR(count)); });
+}
+
+IntegerValueImpl IntegerValueImpl::SHIFTA(int count) const {
+ if (IsMonostate()) {
+ llvm_unreachable("incompatible ints");
+ return IntegerValueImpl{};
+ }
+ return withWord([&](const auto &x) { return FromWord(x.SHIFTA(count)); });
+}
+
+IntegerValueImpl IntegerValueImpl::ISHFTC(int count, int size) const {
+ if (IsMonostate()) {
+ llvm_unreachable("incompatible ints");
+ return IntegerValueImpl{};
+ }
+ return withWord([&](const auto &x) {
+ using T = std::decay_t<decltype(x)>;
+ return FromWord(x.ISHFTC(count, size <= 0 ? T::bits : size));
+ });
+}
+
+IntegerValueImpl IntegerValueImpl::IBITS(int pos, int size) const {
+ if (IsMonostate()) {
+ llvm_unreachable("incompatible ints");
+ return IntegerValueImpl{};
+ }
+ return withWord([&](const auto &x) { return FromWord(x.IBITS(pos, size)); });
+}
+
+IntegerValueImpl IntegerValueImpl::IBSET(int pos) const {
+ if (IsMonostate()) {
+ llvm_unreachable("incompatible ints");
+ return IntegerValueImpl{};
+ }
+ return withWord([&](const auto &x) { return FromWord(x.IBSET(pos)); });
+}
+
+IntegerValueImpl IntegerValueImpl::IBCLR(int pos) const {
+ if (IsMonostate()) {
+ llvm_unreachable("incompatible ints");
+ return IntegerValueImpl{};
+ }
+ return withWord([&](const auto &x) { return FromWord(x.IBCLR(pos)); });
+}
+
+IntegerValueImpl IntegerValueImpl::DSHIFTL(
+ const IntegerValueImpl &fill, int count) const {
+ if (IsMonostate()) {
+ llvm_unreachable("incompatible ints");
+ return IntegerValueImpl{};
+ }
+ // DSHIFTL(I,J) shifts I:J left; the second argument is the right fill.
+ return withWord([&](const auto &x) {
+ using T = std::decay_t<decltype(x)>;
+ return FromWord(x.SHIFTLWithFill(Coerce<T>(fill), count));
+ });
+}
+
+IntegerValueImpl IntegerValueImpl::DSHIFTR(
+ const IntegerValueImpl &v2, int count) const {
+ if (IsMonostate()) {
+ llvm_unreachable("incompatible ints");
+ return IntegerValueImpl{};
+ }
+ // DSHIFTR(I,J) shifts I:J right; the *first* argument (this) is the left
+ // fill, and the receiver of the shift is v2 (mirrors value::Integer's
+ // DSHIFTR, whose *this is the shifted operand and whose argument is the
+ // fill).
+ return v2.withWord([&](const auto &x2) {
+ using T = std::decay_t<decltype(x2)>;
+ return FromWord(x2.SHIFTRWithFill(Coerce<T>(*this), count));
+ });
+}
+
+bool IntegerValueImpl::BTEST(int pos) const {
+ if (IsMonostate()) {
+ return false; // uninitialized int representing 0 has no bits set
+ }
+ return withWord([&](const auto &x) { return x.BTEST(pos); });
+}
+
+int IntegerValueImpl::LEADZ() const {
+ if (IsMonostate()) {
+ llvm_unreachable("incompatible ints");
+ return 0;
+ }
+ return withWord([](const auto &x) { return x.LEADZ(); });
+}
+
+int IntegerValueImpl::TRAILZ() const {
+ if (IsMonostate()) {
+ llvm_unreachable("incompatible ints");
+ return 0;
+ }
+ return withWord([](const auto &x) { return x.TRAILZ(); });
+}
+
+int IntegerValueImpl::POPCNT() const {
+ if (IsMonostate()) {
+ return 0; // uninitialized int representing 0 has no bits set
+ }
+ return withWord([](const auto &x) { return x.POPCNT(); });
+}
+
+bool IntegerValueImpl::POPPAR() const {
+ if (IsMonostate()) {
+ llvm_unreachable("incompatible ints");
+ return false;
+ }
+ return withWord([](const auto &x) { return x.POPPAR(); });
+}
+
+typename IntegerValueImpl::ValueWithOverflow IntegerValueImpl::ConvertSigned(
+ const IntegerValueImpl &from, int toBits) {
+ if (from.IsMonostate()) {
+ return {};
+ }
+ return from.withWord([&](const auto &x) -> ValueWithOverflow {
+ using S = std::decay_t<decltype(x)>;
+ return withWordProto(toBits / 8, [&](auto proto) -> ValueWithOverflow {
+ using T = decltype(proto);
+ auto r{T::template ConvertSigned<S>(x)};
+ return {FromWord(r.value), r.overflow};
+ });
+ });
+}
+
+typename IntegerValueImpl::ValueWithOverflow IntegerValueImpl::ConvertUnsigned(
+ const IntegerValueImpl &from, int toBits) {
+ if (from.IsMonostate()) {
+ return {};
+ }
+ return from.withWord([&](const auto &x) -> ValueWithOverflow {
+ using S = std::decay_t<decltype(x)>;
+ return withWordProto(toBits / 8, [&](auto proto) -> ValueWithOverflow {
+ using T = decltype(proto);
+ auto r{T::template ConvertUnsigned<S>(x)};
+ return {FromWord(r.value), r.overflow};
+ });
+ });
+}
+
+typename IntegerValueImpl::ValueWithOverflow IntegerValueImpl::Read(
+ int kind, const char *&pp, int base, bool isSigned) {
+ return withWordProto(kind, [&](auto proto) -> ValueWithOverflow {
+ using T = decltype(proto);
+ auto r{T::Read(pp, base, isSigned)};
+ return {FromWord(r.value), r.overflow};
+ });
+}
+
+std::string IntegerValueImpl::SignedDecimal() const {
+ if (IsMonostate()) {
+ return "0";
+ }
+ return withWord([](const auto &x) { return x.SignedDecimal(); });
+}
+
+std::string IntegerValueImpl::UnsignedDecimal() const {
+ if (IsMonostate()) {
+ return "0";
+ }
+ return withWord([](const auto &x) { return x.UnsignedDecimal(); });
+}
+
+std::string IntegerValueImpl::Hexadecimal() const {
+ if (IsMonostate()) {
+ return "0";
+ }
+ return withWord([](const auto &x) { return x.Hexadecimal(); });
+}
+
+void IntegerValueImpl::StoreRawBytes(
+ void *dst, size_t expectedSize, bool *changed) const {
+ CHECK(expectedSize == bytesStored());
+
+ withWord([dst, changed, bytesStored = bytesStored()](auto w) {
+ assert(bytesStored == sizeof(w));
+
+ if (changed) {
+ if (std::memcmp(dst, &w, bytesStored) == 0) {
+ return;
+ }
+ *changed = true;
+ }
+ std::memcpy(dst, &w, bytesStored);
+ });
+}
+
+} // namespace Fortran::evaluate::value
diff --git a/flang/lib/Evaluate/integer-value-impl.h b/flang/lib/Evaluate/integer-value-impl.h
new file mode 100644
index 0000000000000..d05ef8a485519
--- /dev/null
+++ b/flang/lib/Evaluate/integer-value-impl.h
@@ -0,0 +1,308 @@
+//===-- lib/Evaluate/integer-value-impl.h ----------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef FORTRAN_EVALUATE_INTEGER_VALUE_IMPL_H_
+#define FORTRAN_EVALUATE_INTEGER_VALUE_IMPL_H_
+
+#include "flang/Evaluate/integer.h"
+#include "llvm/Support/ErrorHandling.h"
+#include <cstdint>
+#include <cstring>
+#include <string>
+#include <type_traits>
+#include <variant>
+
+// Some environments, viz. glibc 2.17 and *BSD, allow the macro HUGE
+// to leak out of <math.h>.
+#undef HUGE
+
+namespace Fortran::evaluate::value {
+
+class IntegerValueImpl {
+public:
+ // Per-KIND fixed-width backing formats. I80 (X87IntegerContainer) is not
+ // itself a Fortran INTEGER kind, but used as REAL(10) storage. While
+ // RealValue has its own RealValueImpl, IntegerValue still needs to able to
+ // hold it with conversions such as RealValue::IntegerValue().
+ using I8 = Integer<8>;
+ using I16 = Integer<16>;
+ using I32 = Integer<32>;
+ using I64 = Integer<64>;
+ using I80 = X87IntegerContainer;
+ using I128 = Integer<128>;
+ using Storage = std::variant<std::monostate, I8, I16, I32, I64, I80, I128>;
+
+ struct ValueWithOverflow;
+ struct ValueWithCarry;
+ struct Product;
+ struct QuotientWithRemainder;
+ struct PowerWithErrors;
+
+ // rule-of-five
+ ~IntegerValueImpl() = default;
+ IntegerValueImpl(const IntegerValueImpl &) = default;
+ IntegerValueImpl(IntegerValueImpl &&) = default;
+ IntegerValueImpl &operator=(const IntegerValueImpl &) = default;
+ IntegerValueImpl &operator=(IntegerValueImpl &&) = default;
+
+ IntegerValueImpl() = default;
+ IntegerValueImpl(int kind, const IntegerValueImpl &x) : IntegerValueImpl(x) {
+ CHECK(x.kind() == kind);
+ }
+
+ static IntegerValueImpl Zero(int kind);
+
+ template <typename INT, typename = std::enable_if_t<std::is_integral_v<INT>>>
+ IntegerValueImpl(int kind, INT n) {
+ withWordProto(kind, [&](auto proto) {
+ using T = decltype(proto);
+ storage_ = T{n};
+ });
+ }
+
+ template <typename T> static IntegerValueImpl FromWord(const T &n) {
+ IntegerValueImpl v;
+ v.storage_ = n;
+ return v;
+ }
+
+ static IntegerValueImpl FromRawBytes(
+ int kind, const void *raw, std::size_t expectedSize);
+
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+ LLVM_DUMP_METHOD void dump() const;
+#endif
+
+ bool IsMonostate() const { return storage_.index() == 0; }
+ int kind() const;
+
+ int bits() const;
+
+ std::size_t bytesStored() const { return bytesStored(kind()); }
+ static constexpr std::size_t bytesStored(int kind) {
+ switch (kind) {
+ case 3:
+ return 2;
+ case 10:
+ return 16;
+ default:
+ return kind;
+ }
+ }
+
+ bool IsZero() const;
+
+ // Comparison operators
+ bool operator<(const IntegerValueImpl &y) const {
+ return CompareSigned(y) == Ordering::Less;
+ }
+ bool operator<=(const IntegerValueImpl &y) const { return !(y < *this); }
+ bool operator==(const IntegerValueImpl &y) const;
+ bool operator!=(const IntegerValueImpl &y) const { return !(*this == y); }
+ bool operator>=(const IntegerValueImpl &y) const { return !(*this < y); }
+ bool operator>(const IntegerValueImpl &y) const { return y < *this; }
+
+ /// Left-justified mask (e.g., MASKL(1) has only its sign bit set)
+ static IntegerValueImpl MASKL(int kind, int places);
+ /// Right-justified mask (e.g., MASKR(1) == 1, MASKR(2) == 3, &c.)
+ static IntegerValueImpl MASKR(int kind, int places);
+ static IntegerValueImpl HUGE(int kind);
+ static IntegerValueImpl Least(int kind);
+
+ bool IsNegative() const;
+
+ std::uint64_t ToUInt64() const;
+ std::int64_t ToInt64() const;
+
+ // Signed/unsigned comparisons
+ Ordering CompareSigned(const IntegerValueImpl &y) const;
+ Ordering CompareUnsigned(const IntegerValueImpl &y) const;
+ Ordering CompareToZeroSigned() const;
+
+ // Arithmetic
+ ValueWithOverflow Negate() const;
+ ValueWithOverflow ABS() const;
+
+ ValueWithCarry AddUnsigned(
+ const IntegerValueImpl &y, bool carryIn = false) const;
+ ValueWithOverflow AddSigned(const IntegerValueImpl &y) const;
+ ValueWithOverflow SubtractSigned(const IntegerValueImpl &y) const;
+ ValueWithOverflow DIM(const IntegerValueImpl &y) const;
+ ValueWithOverflow SIGN(const IntegerValueImpl &sign) const;
+
+ Product MultiplySigned(const IntegerValueImpl &y) const;
+ Product MultiplyUnsigned(const IntegerValueImpl &y) const;
+ QuotientWithRemainder DivideSigned(const IntegerValueImpl &y) const;
+ QuotientWithRemainder DivideUnsigned(const IntegerValueImpl &y) const;
+ ValueWithOverflow MODULO(const IntegerValueImpl &y) const;
+ PowerWithErrors Power(const IntegerValueImpl &e) const;
+
+ // Bitwise operations
+ IntegerValueImpl NOT() const;
+ IntegerValueImpl IAND(const IntegerValueImpl &y) const;
+ IntegerValueImpl IOR(const IntegerValueImpl &y) const;
+ IntegerValueImpl IEOR(const IntegerValueImpl &y) const;
+ IntegerValueImpl MERGE_BITS(
+ const IntegerValueImpl &y, const IntegerValueImpl &mask) const;
+ IntegerValueImpl MAX(const IntegerValueImpl &y) const {
+ return CompareSigned(y) == Ordering::Less ? y : *this;
+ }
+ IntegerValueImpl MIN(const IntegerValueImpl &y) const {
+ return CompareSigned(y) == Ordering::Less ? *this : y;
+ }
+
+ // Shift operations
+ IntegerValueImpl ISHFT(int count) const {
+ return count < 0 ? SHIFTR(-count) : SHIFTL(count);
+ }
+ IntegerValueImpl SHIFTL(int count) const;
+ IntegerValueImpl SHIFTR(int count) const;
+ IntegerValueImpl SHIFTA(int count) const;
+ IntegerValueImpl ISHFTC(int count, int size) const;
+ IntegerValueImpl ISHFTC(int count) const { return ISHFTC(count, bits()); }
+ IntegerValueImpl IBITS(int pos, int size) const;
+ IntegerValueImpl IBSET(int pos) const;
+ IntegerValueImpl IBCLR(int pos) const;
+ IntegerValueImpl DSHIFTL(const IntegerValueImpl &fill, int count) const;
+ IntegerValueImpl DSHIFTR(const IntegerValueImpl &v2, int count) const;
+ bool BTEST(int pos) const;
+ int LEADZ() const;
+ int TRAILZ() const;
+ int POPCNT() const;
+ bool POPPAR() const;
+
+ static ValueWithOverflow ConvertSigned(
+ const IntegerValueImpl &from, int toBits);
+ static ValueWithOverflow ConvertUnsigned(
+ const IntegerValueImpl &from, int toBits);
+
+ static ValueWithOverflow Read(
+ int kind, const char *&pp, int base, bool isSigned);
+
+ // Formatting
+ std::string SignedDecimal() const;
+ std::string UnsignedDecimal() const;
+ std::string Hexadecimal() const;
+
+ // y converted (sign-preserving) to T, so that binary operations operate on
+ // operands of equal width. A monostate operand is treated as a zero of
+ // that width.
+ template <typename T> static T Coerce(const IntegerValueImpl &y) {
+ if (y.IsMonostate()) {
+ return T{};
+ }
+ return y.withWord([](const auto &yv) -> T {
+ using S = std::decay_t<decltype(yv)>;
+ if constexpr (std::is_same_v<S, T>) {
+ return yv;
+ } else {
+ return T::template ConvertSigned<S>(yv).value;
+ }
+ });
+ }
+
+ // Same as Coerce, but zero-extending rather than sign-extending.
+ template <typename T> static T CoerceUnsigned(const IntegerValueImpl &y) {
+ if (y.IsMonostate()) {
+ return T{};
+ }
+ return y.withWord([](const auto &yv) -> T {
+ using S = std::decay_t<decltype(yv)>;
+ if constexpr (std::is_same_v<S, T>) {
+ return yv;
+ } else {
+ return T::template ConvertUnsigned<S>(yv).value;
+ }
+ });
+ }
+
+ void StoreRawBytes(void *dst, size_t size, bool *changed) const;
+
+ // Compile-time dispatchers to current/specified kind
+
+ template <typename F>
+ auto withWordProto(F &&f) const
+ -> decltype(std::declval<F>()(std::declval<I64>())) {
+ return withWordProto(kind(), std::forward<F>(f));
+ }
+
+ template <typename F>
+ static auto withWordProto(int kind, F &&f)
+ -> decltype(std::declval<F>()(std::declval<I64>())) {
+ switch (kind) {
+ case 1:
+ return f(I8{});
+ case 2:
+ case 3:
+ return f(I16{});
+ case 4:
+ return f(I32{});
+ case 8:
+ return f(I64{});
+ case 10:
+ return f(I80{});
+ case 16:
+ return f(I128{});
+ default:
+ llvm_unreachable("unsupported integer width");
+ }
+ }
+
+ template <typename F>
+ auto withWord(F &&f) const
+ -> decltype(std::declval<F>()(std::declval<I64>())) {
+ switch (storage_.index()) {
+ case 1:
+ return f(std::get<I8>(storage_));
+ case 2:
+ return f(std::get<I16>(storage_));
+ case 3:
+ return f(std::get<I32>(storage_));
+ case 4:
+ return f(std::get<I64>(storage_));
+ case 5:
+ return f(std::get<I80>(storage_));
+ case 6:
+ return f(std::get<I128>(storage_));
+ default:
+ llvm_unreachable("operation on uninitialized IntegerValueImpl");
+ }
+ }
+
+private:
+ Storage storage_;
+};
+
+struct IntegerValueImpl::ValueWithOverflow {
+ IntegerValueImpl value;
+ bool overflow{false};
+};
+
+struct IntegerValueImpl::ValueWithCarry {
+ IntegerValueImpl value;
+ bool carry{false};
+};
+
+struct IntegerValueImpl::Product {
+ IntegerValueImpl upper, lower;
+ bool SignedMultiplicationOverflowed() const { return overflow; }
+ bool overflow{false};
+};
+
+struct IntegerValueImpl::QuotientWithRemainder {
+ IntegerValueImpl quotient, remainder;
+ bool divisionByZero{false}, overflow{false};
+};
+
+struct IntegerValueImpl::PowerWithErrors {
+ IntegerValueImpl power;
+ bool divisionByZero{false}, overflow{false}, zeroToZero{false};
+};
+
+} // namespace Fortran::evaluate::value
+#endif // FORTRAN_EVALUATE_INTEGER_VALUE_IMPL_H_
diff --git a/flang/lib/Evaluate/integer-value.cpp b/flang/lib/Evaluate/integer-value.cpp
new file mode 100644
index 0000000000000..e0a0031af3bfc
--- /dev/null
+++ b/flang/lib/Evaluate/integer-value.cpp
@@ -0,0 +1,304 @@
+//===-- lib/Evaluate/integer-value.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/integer-value.h"
+#include "integer-value-impl.h"
+#include <new>
+
+namespace Fortran::evaluate::value {
+static_assert(sizeof(IntegerValueImpl) == detail::kIntegerObjectSize);
+static_assert(alignof(IntegerValueImpl) == detail::kIntegerObjectAlign);
+static_assert(sizeof(IntegerValue) == sizeof(IntegerValueImpl));
+static_assert(alignof(IntegerValue) == alignof(IntegerValueImpl));
+
+IntegerValue::IntegerValue() { new (this) IntegerValueImpl(); }
+
+IntegerValue::~IntegerValue() { impl().~IntegerValueImpl(); }
+
+IntegerValue::IntegerValue(const IntegerValue &x) {
+ new (this) IntegerValueImpl(x.impl());
+}
+
+IntegerValue::IntegerValue(IntegerValue &&x) {
+ new (this) IntegerValueImpl(std::move(x.impl()));
+}
+
+IntegerValue &IntegerValue::operator=(const IntegerValue &x) {
+ impl() = x.impl();
+ return *this;
+}
+
+IntegerValue &IntegerValue::operator=(IntegerValue &&x) {
+ impl() = std::move(x.impl());
+ return *this;
+}
+
+IntegerValue IntegerValue::Zero(int kind) {
+ return FromImpl(IntegerValueImpl::Zero(kind));
+}
+
+bool IntegerValue::IsMonostate() const { return impl().IsMonostate(); }
+
+int IntegerValue::kind() const { return impl().kind(); }
+
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+LLVM_DUMP_METHOD void IntegerValue::dump() const { impl().dump(); }
+#endif
+
+bool IntegerValue::operator==(const IntegerValue &y) const {
+ return impl() == y.impl();
+}
+
+IntegerValue IntegerValue::MASKL(int kind, int places) {
+ return FromImpl(IntegerValueImpl::MASKL(kind, places));
+}
+
+IntegerValue IntegerValue::MASKR(int kind, int places) {
+ return FromImpl(IntegerValueImpl::MASKR(kind, places));
+}
+
+IntegerValue::ValueWithOverflow IntegerValue::Read(
+ int kind, const char *&pp, int base, bool isSigned) {
+ auto r{IntegerValueImpl::Read(kind, pp, base, isSigned)};
+ return {FromImpl(std::move(r.value)), r.overflow};
+}
+
+IntegerValue::ValueWithOverflow IntegerValue::ConvertUnsigned(
+ const IntegerValue &from, int toBits) {
+ auto r{IntegerValueImpl::ConvertUnsigned(from.impl(), toBits)};
+ return {FromImpl(std::move(r.value)), r.overflow};
+}
+
+typename IntegerValue::ValueWithOverflow IntegerValue::ConvertSigned(
+ const IntegerValue &from, int toBits) {
+ auto r{IntegerValueImpl::ConvertSigned(from.impl(), toBits)};
+ return {FromImpl(std::move(r.value)), r.overflow};
+}
+
+std::string IntegerValue::UnsignedDecimal() const {
+ return impl().UnsignedDecimal();
+}
+
+std::string IntegerValue::SignedDecimal() const {
+ return impl().SignedDecimal();
+}
+
+std::string IntegerValue::Hexadecimal() const { return impl().Hexadecimal(); }
+
+IntegerValue IntegerValue::HUGE(int kind) {
+ return FromImpl(IntegerValueImpl::HUGE(kind));
+}
+
+IntegerValue IntegerValue::Least(int kind) {
+ return FromImpl(IntegerValueImpl::Least(kind));
+}
+
+int IntegerValue::RANGE(int kind) { return DecimalRange(kind * 8 - 1); }
+
+int IntegerValue::UnsignedRANGE(int kind) { return DecimalRange(kind * 8); }
+
+bool IntegerValue::IsZero() const { return impl().IsZero(); }
+
+bool IntegerValue::IsNegative() const { return impl().IsNegative(); }
+
+Ordering IntegerValue::CompareToZeroSigned() const {
+ return impl().CompareToZeroSigned();
+}
+
+int IntegerValue::LEADZ() const { return impl().LEADZ(); }
+
+int IntegerValue::POPCNT() const { return impl().POPCNT(); }
+
+bool IntegerValue::POPPAR() const { return impl().POPPAR(); }
+
+int IntegerValue::TRAILZ() const { return impl().TRAILZ(); }
+
+bool IntegerValue::BTEST(int pos) const { return impl().BTEST(pos); }
+
+Ordering IntegerValue::CompareUnsigned(const IntegerValue &y) const {
+ return impl().CompareUnsigned(y.impl());
+}
+
+Ordering IntegerValue::CompareSigned(const IntegerValue &y) const {
+ return impl().CompareSigned(y.impl());
+}
+
+std::uint64_t IntegerValue::ToUInt64() const { return impl().ToUInt64(); }
+
+std::int64_t IntegerValue::ToInt64() const { return impl().ToInt64(); }
+
+IntegerValue IntegerValue::NOT() const { return FromImpl(impl().NOT()); }
+
+typename IntegerValue::ValueWithOverflow IntegerValue::Negate() const {
+ auto r{impl().Negate()};
+ return {FromImpl(std::move(r.value)), r.overflow};
+}
+typename IntegerValue::ValueWithOverflow IntegerValue::ABS() const {
+ auto r{impl().ABS()};
+ return {FromImpl(std::move(r.value)), r.overflow};
+}
+
+IntegerValue IntegerValue::SHIFTL(int count) const {
+ return FromImpl(impl().SHIFTL(count));
+}
+
+IntegerValue IntegerValue::ISHFTC(int count, int size) const {
+ return FromImpl(impl().ISHFTC(count, size));
+}
+
+IntegerValue IntegerValue::ISHFTC(int count) const {
+ return FromImpl(impl().ISHFTC(count));
+}
+
+IntegerValue IntegerValue::DSHIFTL(const IntegerValue &fill, int count) const {
+ return FromImpl(impl().DSHIFTL(fill.impl(), count));
+}
+
+IntegerValue IntegerValue::DSHIFTR(const IntegerValue &v2, int count) const {
+ return FromImpl(impl().DSHIFTR(v2.impl(), count));
+}
+
+IntegerValue IntegerValue::SHIFTR(int count) const {
+ return FromImpl(impl().SHIFTR(count));
+}
+
+IntegerValue IntegerValue::SHIFTA(int count) const {
+ return FromImpl(impl().SHIFTA(count));
+}
+
+IntegerValue IntegerValue::IBCLR(int pos) const {
+ return FromImpl(impl().IBCLR(pos));
+}
+
+IntegerValue IntegerValue::IBSET(int pos) const {
+ return FromImpl(impl().IBSET(pos));
+}
+
+IntegerValue IntegerValue::IBITS(int pos, int size) const {
+ return FromImpl(impl().IBITS(pos, size));
+}
+
+IntegerValue IntegerValue::IAND(const IntegerValue &y) const {
+ return FromImpl(impl().IAND(y.impl()));
+}
+
+IntegerValue IntegerValue::IOR(const IntegerValue &y) const {
+ return FromImpl(impl().IOR(y.impl()));
+}
+
+IntegerValue IntegerValue::IEOR(const IntegerValue &y) const {
+ return FromImpl(impl().IEOR(y.impl()));
+}
+
+IntegerValue IntegerValue::MERGE_BITS(
+ const IntegerValue &y, const IntegerValue &mask) const {
+ return FromImpl(impl().MERGE_BITS(y.impl(), mask.impl()));
+}
+
+typename IntegerValue::ValueWithCarry IntegerValue::AddUnsigned(
+ const IntegerValue &y, bool carryIn) const {
+ auto r{impl().AddUnsigned(y.impl(), carryIn)};
+ return {FromImpl(std::move(r.value)), r.carry};
+}
+
+typename IntegerValue::ValueWithOverflow IntegerValue::AddSigned(
+ const IntegerValue &y) const {
+ auto r{impl().AddSigned(y.impl())};
+ return {FromImpl(std::move(r.value)), r.overflow};
+}
+
+typename IntegerValue::ValueWithOverflow IntegerValue::SubtractSigned(
+ const IntegerValue &y) const {
+ auto r{impl().SubtractSigned(y.impl())};
+ return {FromImpl(std::move(r.value)), r.overflow};
+}
+
+typename IntegerValue::ValueWithOverflow IntegerValue::DIM(
+ const IntegerValue &y) const {
+ auto r{impl().DIM(y.impl())};
+ return {FromImpl(std::move(r.value)), r.overflow};
+}
+
+typename IntegerValue::ValueWithOverflow IntegerValue::SIGN(
+ const IntegerValue &sign) const {
+ auto r{impl().SIGN(sign.impl())};
+ return {FromImpl(std::move(r.value)), r.overflow};
+}
+
+typename IntegerValue::Product IntegerValue::MultiplyUnsigned(
+ const IntegerValue &y) const {
+ auto r{impl().MultiplyUnsigned(y.impl())};
+ return {
+ FromImpl(std::move(r.upper)), FromImpl(std::move(r.lower)), r.overflow};
+}
+
+typename IntegerValue::Product IntegerValue::MultiplySigned(
+ const IntegerValue &y) const {
+ auto r{impl().MultiplySigned(y.impl())};
+ return {
+ FromImpl(std::move(r.upper)), FromImpl(std::move(r.lower)), r.overflow};
+}
+
+typename IntegerValue::QuotientWithRemainder IntegerValue::DivideUnsigned(
+ const IntegerValue &y) const {
+ auto r{impl().DivideUnsigned(y.impl())};
+ return {FromImpl(std::move(r.quotient)), FromImpl(std::move(r.remainder)),
+ r.divisionByZero, r.overflow};
+}
+
+typename IntegerValue::QuotientWithRemainder IntegerValue::DivideSigned(
+ const IntegerValue &y) const {
+ auto r{impl().DivideSigned(y.impl())};
+ return {FromImpl(std::move(r.quotient)), FromImpl(std::move(r.remainder)),
+ r.divisionByZero, r.overflow};
+}
+
+typename IntegerValue::ValueWithOverflow IntegerValue::MODULO(
+ const IntegerValue &y) const {
+ auto r{impl().MODULO(y.impl())};
+ return {FromImpl(std::move(r.value)), r.overflow};
+}
+
+typename IntegerValue::PowerWithErrors IntegerValue::Power(
+ const IntegerValue &e) const {
+ auto r{impl().Power(e.impl())};
+ return {
+ FromImpl(std::move(r.power)), r.divisionByZero, r.overflow, r.zeroToZero};
+}
+
+IntegerValue IntegerValue::FromRawBytes(
+ int kind, const void *raw, std::size_t expectedSize) {
+ return FromImpl(IntegerValueImpl::FromRawBytes(kind, raw, expectedSize));
+}
+
+void IntegerValue::StoreRawBytes(void *dst, size_t size, bool *changed) const {
+ impl().StoreRawBytes(dst, size, changed);
+}
+
+void IntegerValue::ConstructFromIntegral(
+ int kind, std::uint64_t n, bool isSigned) {
+ if (isSigned) {
+ new (this) IntegerValueImpl(kind, static_cast<std::int64_t>(n));
+ } else {
+ new (this) IntegerValueImpl(kind, n);
+ }
+}
+
+IntegerValue IntegerValue::FromImpl(const IntegerValueImpl &x) {
+ IntegerValue r;
+ r.impl() = x;
+ return r;
+}
+
+IntegerValue IntegerValue::FromImpl(IntegerValueImpl &&x) {
+ IntegerValue r;
+ r.impl() = std::move(x);
+ return r;
+}
+
+} // namespace Fortran::evaluate::value
diff --git a/flang/lib/Evaluate/intrinsics-library.cpp b/flang/lib/Evaluate/intrinsics-library.cpp
index f2c1a7bfaf50b..adfc295b97a2c 100644
--- a/flang/lib/Evaluate/intrinsics-library.cpp
+++ b/flang/lib/Evaluate/intrinsics-library.cpp
@@ -152,12 +152,13 @@ template <typename FuncType, typename TR, typename... TA, size_t... I>
static Expr<SomeType> ApplyHostFunctionHelper(FuncType func,
FoldingContext &context, std::vector<Expr<SomeType>> &&args,
std::index_sequence<I...>) {
+ const int kind{TR::kind};
host::HostFloatingPointEnvironment hostFPE;
hostFPE.SetUpHostFloatingPointEnvironment(context);
host::HostType<TR> hostResult{};
Scalar<TR> result{};
std::tuple<Scalar<TA>...> scalarArgs{
- GetScalarConstantValue<TA>(args[I]).value()...};
+ GetScalarConstantValue<typename TA::FortranType>(args[I]).value()...};
if (context.targetCharacteristics().areSubnormalsFlushedToZero() &&
!hostFPE.hasSubnormalFlushingHardwareControl()) {
hostResult = func(host::CastFortranToHost<TA>(
@@ -171,7 +172,8 @@ static Expr<SomeType> ApplyHostFunctionHelper(FuncType func,
CheckFloatingPointIssues<TR>(hostFPE, result);
}
hostFPE.CheckAndRestoreFloatingPointEnvironment(context);
- return AsGenericExpr(Constant<TR>(std::move(result)));
+ return AsGenericExpr(
+ Constant<typename TR::FortranType>(kind, std::move(result)));
}
template <typename HostTR, typename... HostTA>
Expr<SomeType> ApplyHostFunction(FuncPointer<HostTR, HostTA...> func,
@@ -823,9 +825,12 @@ static const Expr<SomeType> &GetArg(
template <typename T>
static bool IsInRange(const Expr<T> &expr, int lb, int ub) {
+ const int kind{expr.kind()};
if (auto scalar{GetScalarConstantValue<T>(expr)}) {
- auto lbValue{Scalar<T>::FromInteger(value::Integer<8>{lb}).value};
- auto ubValue{Scalar<T>::FromInteger(value::Integer<8>{ub}).value};
+ auto lbValue{
+ Scalar<T>::FromInteger(kind, value::IntegerValue{1, lb}).value};
+ auto ubValue{
+ Scalar<T>::FromInteger(kind, value::IntegerValue{1, ub}).value};
return Satisfies(RelationalOperator::LE, lbValue.Compare(*scalar)) &&
Satisfies(RelationalOperator::LE, scalar->Compare(ubValue));
}
@@ -859,9 +864,10 @@ static bool VerifyStrictlyPositiveIfReal(
const bool isStrictlyPositive{std::visit(
[&](const auto &x) -> bool {
using T = typename std::decay_t<decltype(x)>::Result;
+ const int kind{x.kind()};
auto scalar{GetScalarConstantValue<T>(x)};
return Satisfies(
- RelationalOperator::LT, Scalar<T>{}.Compare(*scalar));
+ RelationalOperator::LT, Scalar<T>::Zero(kind).Compare(*scalar));
},
someReal->u)};
if (!isStrictlyPositive) {
diff --git a/flang/lib/Evaluate/logical-value.cpp b/flang/lib/Evaluate/logical-value.cpp
new file mode 100644
index 0000000000000..1b15ae502b26c
--- /dev/null
+++ b/flang/lib/Evaluate/logical-value.cpp
@@ -0,0 +1,25 @@
+//===-- lib/Evaluate/logical-value.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/logical-value.h"
+
+namespace Fortran::evaluate::value {
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+LLVM_DUMP_METHOD void LogicalValue::dump() const {
+ if (!IsCanonical()) {
+ llvm::errs() << "transfer(" << word().ToInt64() << "_8,.false._" << kind()
+ << ")\n";
+ } else if (IsTrue()) {
+ llvm::errs() << ".true." << '_' << kind() << '\n';
+ } else {
+ llvm::errs() << ".false." << '_' << kind() << '\n';
+ }
+}
+#endif
+
+} // namespace Fortran::evaluate::value
diff --git a/flang/lib/Evaluate/logical.cpp b/flang/lib/Evaluate/logical.cpp
deleted file mode 100644
index 520d11fb967f4..0000000000000
--- a/flang/lib/Evaluate/logical.cpp
+++ /dev/null
@@ -1,17 +0,0 @@
-//===-- lib/Evaluate/logical.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/logical.h"
-
-namespace Fortran::evaluate::value {
-
-template class Logical<8>;
-template class Logical<16>;
-template class Logical<32>;
-template class Logical<64>;
-} // namespace Fortran::evaluate::value
diff --git a/flang/lib/Evaluate/real-value-impl.cpp b/flang/lib/Evaluate/real-value-impl.cpp
new file mode 100644
index 0000000000000..88847352fa6bf
--- /dev/null
+++ b/flang/lib/Evaluate/real-value-impl.cpp
@@ -0,0 +1,538 @@
+//===-- lib/Evaluate/real-value-impl.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 "real-value-impl.h"
+#include "integer-value-impl.h"
+#include "flang/Common/idioms.h"
+#include "flang/Decimal/decimal.h"
+#include "flang/Evaluate/real-value.h"
+#include "flang/Evaluate/rounding-bits.h"
+#include "llvm/Support/raw_ostream.h"
+#include <cstring>
+#include <new>
+#include <string>
+
+namespace Fortran::evaluate::value {
+
+RealValueImpl::RealValueImpl(int kind, const Word &w) {
+ withWordProto(kind, [&](auto proto) {
+ using R = decltype(proto);
+ if (w.IsMonostate()) {
+ storage_ = R{};
+ } else {
+ storage_ = R{FixedIntegerFromValue<typename R::Word>(w)};
+ }
+ });
+}
+
+RealValueImpl RealValueImpl::Zero(int kind) {
+ RealValueImpl result;
+ withWordProto(kind, [&](auto proto) { result.storage_ = decltype(proto){}; });
+ return result;
+}
+
+RealValueImpl RealValueImpl::FromRawBytes(
+ int kind, const void *raw, std::size_t expectedSize) {
+ return RealValueImpl{
+ kind, IntegerValue::FromRawBytes(kind, raw, expectedSize)};
+}
+
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+LLVM_DUMP_METHOD void RealValueImpl::dump() const {
+ AsFortran(llvm::errs(), kind()) << '\n';
+}
+#endif
+
+int RealValueImpl::kind() const {
+ if (IsMonostate()) {
+ llvm_unreachable("uninitialized value has not a defined kind");
+ }
+
+ return withWord([](const auto &v) -> int {
+ using R = std::decay_t<decltype(v)>;
+ if constexpr (std::is_same_v<R, R3>) {
+ return 3;
+ }
+ return R::bits / 8;
+ });
+}
+
+int RealValueImpl::bits() const {
+ if (IsMonostate()) {
+ return 0;
+ }
+
+ return withWord(
+ [](const auto &v) -> int { return std::decay_t<decltype(v)>::bits; });
+}
+
+bool RealValueImpl::IsZero() const {
+ if (IsMonostate()) {
+ return true;
+ }
+ return withWord([](const auto &v) { return v.IsZero(); });
+}
+
+bool RealValueImpl::operator==(const RealValueImpl &y) const {
+ return withWord([&y](const auto &v1) -> bool {
+ return y.withWord([&v1](const auto &v2) -> bool {
+ if constexpr (std::is_same_v<std::decay_t<decltype(v1)>,
+ std::decay_t<decltype(v2)>>) {
+ return v1 == v2;
+ }
+ llvm_unreachable("Uncomparable reals");
+ });
+ });
+}
+
+int RealValueImpl::DIGITS(int kind) {
+ return withWordProto(kind, [](auto p) { return decltype(p)::DIGITS; });
+}
+
+int RealValueImpl::PRECISION(int kind) {
+ return withWordProto(kind, [](auto p) { return decltype(p)::PRECISION; });
+}
+
+int RealValueImpl::RANGE(int kind) {
+ return withWordProto(kind, [](auto p) { return decltype(p)::RANGE; });
+}
+
+int RealValueImpl::MAXEXPONENT(int kind) {
+ return withWordProto(kind, [](auto p) { return decltype(p)::MAXEXPONENT; });
+}
+
+int RealValueImpl::MINEXPONENT(int kind) {
+ return withWordProto(kind, [](auto p) { return decltype(p)::MINEXPONENT; });
+}
+
+RealValueImpl RealValueImpl::HUGE(int kind) {
+ return withWordProto(
+ kind, [](auto p) { return FromWord(decltype(p)::HUGE()); });
+}
+
+RealValueImpl RealValueImpl::EPSILON(int kind) {
+ return withWordProto(
+ kind, [](auto p) { return FromWord(decltype(p)::EPSILON()); });
+}
+
+RealValueImpl RealValueImpl::TINY(int kind) {
+ return withWordProto(
+ kind, [](auto p) { return FromWord(decltype(p)::TINY()); });
+}
+
+RealValueImpl RealValueImpl::NotANumber(int kind) {
+ return withWordProto(
+ kind, [](auto p) { return FromWord(decltype(p)::NotANumber()); });
+}
+
+bool RealValueImpl::IsNegative() const {
+ if (IsMonostate()) {
+ return false;
+ }
+ return withWord([](const auto &v) { return v.IsNegative(); });
+}
+
+bool RealValueImpl::IsNotANumber() const {
+ if (IsMonostate()) {
+ return false;
+ }
+ return withWord([](const auto &v) { return v.IsNotANumber(); });
+}
+
+bool RealValueImpl::IsSignalingNaN() const {
+ if (IsMonostate()) {
+ return false;
+ }
+ return withWord([](const auto &v) { return v.IsSignalingNaN(); });
+}
+
+bool RealValueImpl::IsInfinite() const {
+ if (IsMonostate()) {
+ return false;
+ }
+ return withWord([](const auto &v) { return v.IsInfinite(); });
+}
+
+bool RealValueImpl::IsFinite() const {
+ if (IsMonostate()) {
+ return true;
+ }
+ return withWord([](const auto &v) { return v.IsFinite(); });
+}
+
+bool RealValueImpl::IsNormal() const {
+ if (IsMonostate()) {
+ return true;
+ }
+ return withWord([](const auto &v) { return v.IsNormal(); });
+}
+
+int RealValueImpl::Exponent() const {
+ if (IsMonostate()) {
+ return 0;
+ }
+ return withWord([](const auto &v) { return v.Exponent(); });
+}
+
+void RealValueImpl::StoreRawBytes(
+ void *dst, size_t expectedSize, bool *changed) const {
+ CHECK(bytesStored() == expectedSize);
+ withWord([=](const auto &v) {
+ auto data{v.RawBits()};
+ CHECK(sizeof(data) == expectedSize);
+ if (std::memcmp(dst, &data, sizeof(data))) {
+ std::memcpy(dst, &data, sizeof(data));
+ if (changed)
+ *changed = true;
+ }
+ });
+}
+
+IntegerValue RealValueImpl::RawBits() const {
+ if (IsMonostate()) {
+ return {};
+ }
+
+ return withWord(
+ [](const auto &v) { return IntegerValueFromFixed(v.RawBits()); });
+}
+
+Relation RealValueImpl::Compare(const RealValueImpl &y) const {
+ if (IsMonostate()) {
+ llvm_unreachable("uncomparable value");
+ }
+ return withWord([&](const auto &v) {
+ using R = std::decay_t<decltype(v)>;
+ return v.Compare(AsWord<R>(y));
+ });
+}
+
+RealValueImpl RealValueImpl::ABS() const {
+ if (IsMonostate()) {
+ return RealValueImpl{};
+ }
+ return withWord([](const auto &v) { return FromWord(v.ABS()); });
+}
+
+RealValueImpl RealValueImpl::Negate() const {
+ if (IsMonostate()) {
+ return RealValueImpl{};
+ }
+ return withWord([](const auto &v) { return FromWord(v.Negate()); });
+}
+
+RealValueImpl RealValueImpl::SIGN(const RealValueImpl &x) const {
+ if (IsMonostate()) {
+ llvm_unreachable("unsupported operation over uninitialized value");
+ }
+ return withWord([&](const auto &v) {
+ using R = std::decay_t<decltype(v)>;
+ return FromWord(v.SIGN(AsWord<R>(x)));
+ });
+}
+
+RealValueImpl RealValueImpl::SetSign(bool toNegative) const {
+ if (IsMonostate()) {
+ llvm_unreachable("unsupported operation over uninitialized value");
+ }
+ return withWord(
+ [&](const auto &v) { return FromWord(v.SetSign(toNegative)); });
+}
+
+RealValueImpl RealValueImpl::FlushSubnormalToZero() const {
+ if (IsMonostate()) {
+ llvm_unreachable("unsupported operation over uninitialized value");
+ }
+ return withWord(
+ [](const auto &v) { return FromWord(v.FlushSubnormalToZero()); });
+}
+
+ValueWithRealFlags<RealValueImpl> RealValueImpl::Add(
+ const RealValueImpl &y, Rounding rounding) const {
+ if (IsMonostate()) {
+ llvm_unreachable("unsupported operation over uninitialized value");
+ }
+ return withWord([&](const auto &v) {
+ using R = std::decay_t<decltype(v)>;
+ return FromWord(v.Add(AsWord<R>(y), rounding));
+ });
+}
+
+ValueWithRealFlags<RealValueImpl> RealValueImpl::Subtract(
+ const RealValueImpl &y, Rounding rounding) const {
+ if (IsMonostate()) {
+ llvm_unreachable("unsupported operation over uninitialized value");
+ }
+ return withWord([&](const auto &v) {
+ using R = std::decay_t<decltype(v)>;
+ return FromWord(v.Subtract(AsWord<R>(y), rounding));
+ });
+}
+
+ValueWithRealFlags<RealValueImpl> RealValueImpl::Multiply(
+ const RealValueImpl &y, Rounding rounding) const {
+ if (IsMonostate()) {
+ llvm_unreachable("unsupported operation over uninitialized value");
+ }
+ return withWord([&](const auto &v) {
+ using R = std::decay_t<decltype(v)>;
+ return FromWord(v.Multiply(AsWord<R>(y), rounding));
+ });
+}
+
+ValueWithRealFlags<RealValueImpl> RealValueImpl::Divide(
+ const RealValueImpl &y, Rounding rounding) const {
+ if (IsMonostate()) {
+ llvm_unreachable("unsupported operation over uninitialized value");
+ }
+ return withWord([&](const auto &v) {
+ using R = std::decay_t<decltype(v)>;
+ return FromWord(v.Divide(AsWord<R>(y), rounding));
+ });
+}
+
+ValueWithRealFlags<RealValueImpl> RealValueImpl::SQRT(Rounding rounding) const {
+ if (IsMonostate()) {
+ llvm_unreachable("unsupported operation over uninitialized value");
+ }
+ return withWord([&](const auto &v) { return FromWord(v.SQRT(rounding)); });
+}
+
+ValueWithRealFlags<RealValueImpl> RealValueImpl::HYPOT(
+ const RealValueImpl &y, Rounding rounding) const {
+ if (IsMonostate()) {
+ llvm_unreachable("unsupported operation over uninitialized value");
+ }
+ return withWord([&](const auto &v) {
+ using R = std::decay_t<decltype(v)>;
+ return FromWord(v.HYPOT(AsWord<R>(y), rounding));
+ });
+}
+
+ValueWithRealFlags<RealValueImpl> RealValueImpl::MOD(
+ const RealValueImpl &y, Rounding rounding) const {
+ if (IsMonostate()) {
+ llvm_unreachable("unsupported operation over uninitialized value");
+ }
+ return withWord([&](const auto &v) {
+ using R = std::decay_t<decltype(v)>;
+ return FromWord(v.MOD(AsWord<R>(y), rounding));
+ });
+}
+
+ValueWithRealFlags<RealValueImpl> RealValueImpl::MODULO(
+ const RealValueImpl &y, Rounding rounding) const {
+ if (IsMonostate()) {
+ llvm_unreachable("unsupported operation over uninitialized value");
+ }
+ return withWord([&](const auto &v) {
+ using R = std::decay_t<decltype(v)>;
+ return FromWord(v.MODULO(AsWord<R>(y), rounding));
+ });
+}
+
+ValueWithRealFlags<RealValueImpl> RealValueImpl::DIM(
+ const RealValueImpl &y, Rounding rounding) const {
+ if (IsMonostate()) {
+ llvm_unreachable("unsupported operation over uninitialized value");
+ }
+ return withWord([&](const auto &v) {
+ using R = std::decay_t<decltype(v)>;
+ return FromWord(v.DIM(AsWord<R>(y), rounding));
+ });
+}
+
+RealValueImpl RealValueImpl::FRACTION() const {
+ if (IsMonostate()) {
+ llvm_unreachable("unsupported operation over uninitialized value");
+ }
+ return withWord([](const auto &v) { return FromWord(v.FRACTION()); });
+}
+
+RealValueImpl RealValueImpl::RRSPACING() const {
+ if (IsMonostate()) {
+ llvm_unreachable("unsupported operation over uninitialized value");
+ }
+ return withWord([](const auto &v) { return FromWord(v.RRSPACING()); });
+}
+
+RealValueImpl RealValueImpl::SPACING() const {
+ if (IsMonostate()) {
+ llvm_unreachable("unsupported operation over uninitialized value");
+ }
+ return withWord([](const auto &v) { return FromWord(v.SPACING()); });
+}
+
+RealValueImpl RealValueImpl::SET_EXPONENT(std::int64_t e) const {
+ if (IsMonostate()) {
+ llvm_unreachable("unsupported operation over uninitialized value");
+ }
+ return withWord([&](const auto &v) { return FromWord(v.SET_EXPONENT(e)); });
+}
+
+ValueWithRealFlags<RealValueImpl> RealValueImpl::NEAREST(bool upward) const {
+ if (IsMonostate()) {
+ llvm_unreachable("unsupported operation over uninitialized value");
+ }
+ return withWord([&](const auto &v) { return FromWord(v.NEAREST(upward)); });
+}
+
+ValueWithRealFlags<RealValueImpl> RealValueImpl::ToWholeNumber(
+ common::RoundingMode mode) const {
+ if (IsMonostate()) {
+ llvm_unreachable("unsupported operation over uninitialized value");
+ }
+ return withWord(
+ [&](const auto &v) { return FromWord(v.ToWholeNumber(mode)); });
+}
+
+ValueWithRealFlags<IntegerValue> RealValueImpl::ToInteger(
+ common::RoundingMode mode, int toBits) const {
+ if (IsMonostate()) {
+ llvm_unreachable("unsupported operation over uninitialized value");
+ }
+ return withWord([&](const auto &v) -> ValueWithRealFlags<IntegerValue> {
+ auto pick{[&](auto target) -> ValueWithRealFlags<IntegerValue> {
+ using W = decltype(target);
+ auto r{v.template ToInteger<W>(mode)};
+ ValueWithRealFlags<IntegerValue> result;
+ result.value = IntegerValueFromFixed(r.value);
+ result.flags = r.flags;
+ return result;
+ }};
+ switch (toBits) {
+ case 8:
+ return pick(Integer<8>{});
+ case 16:
+ return pick(Integer<16>{});
+ case 32:
+ return pick(Integer<32>{});
+ case 64:
+ return pick(Integer<64>{});
+ case 128:
+ return pick(Integer<128>{});
+ default:
+ return pick(Integer<64>{});
+ }
+ });
+}
+
+ValueWithRealFlags<RealValueImpl> RealValueImpl::SCALE(
+ const IntegerValue &by, Rounding rounding) const {
+ if (IsMonostate()) {
+ llvm_unreachable("unsupported operation over uninitialized value");
+ }
+ return withWord([&](const auto &v) -> ValueWithRealFlags<RealValueImpl> {
+ return FromWord(v.SCALE(Integer<64>{by.ToInt64()}, rounding));
+ });
+}
+
+ValueWithRealFlags<RealValueImpl> RealValueImpl::KahanSummation(
+ const RealValueImpl &y, RealValueImpl &correction,
+ Rounding rounding) const {
+ if (IsMonostate()) {
+ llvm_unreachable("unsupported operation over uninitialized value");
+ }
+ return withWord([&](const auto &v) {
+ using R = std::decay_t<decltype(v)>;
+ R corr{AsWord<R>(correction)};
+ auto r{v.KahanSummation(AsWord<R>(y), corr, rounding)};
+ correction = FromWord(corr);
+ return FromWord(r);
+ });
+}
+
+IntegerValue RealValueImpl::EXPONENT() const {
+ if (IsMonostate()) {
+ llvm_unreachable("unsupported operation over uninitialized value");
+ }
+ return withWord([](const auto &v) -> IntegerValue {
+ return IntegerValueFromFixed(v.template EXPONENT<Integer<32>>());
+ });
+}
+
+ValueWithRealFlags<RealValueImpl> RealValueImpl::FromInteger(
+ int kind, const IntegerValue &n, bool isUnsigned, Rounding rounding) {
+ if (n.IsMonostate()) {
+ return ValueWithRealFlags<RealValueImpl>{};
+ }
+ return withWordProto(
+ kind, [&](auto proto) -> ValueWithRealFlags<RealValueImpl> {
+ auto r{FromIntegerValue<decltype(proto)>(n, isUnsigned, rounding)};
+ return {FromWord(r.value), r.flags};
+ });
+}
+
+ValueWithRealFlags<RealValueImpl> RealValueImpl::Convert(
+ int kind, const RealValueImpl &from, Rounding rounding) {
+ return withWordProto(
+ kind, [&](auto proto) -> ValueWithRealFlags<RealValueImpl> {
+ using R = decltype(proto);
+ if (from.IsMonostate()) {
+ return FromWord(R::Convert(R{}, rounding));
+ }
+ return from.withWord(
+ [&](const auto &v) -> ValueWithRealFlags<RealValueImpl> {
+ return FromWord(R::Convert(v, rounding));
+ });
+ });
+}
+
+ValueWithRealFlags<RealValueImpl> RealValueImpl::Read(
+ int kind, const char *&pp, Rounding rounding) {
+ return withWordProto(
+ kind, [&](auto proto) -> ValueWithRealFlags<RealValueImpl> {
+ auto r{decltype(proto)::Read(pp, rounding)};
+ ValueWithRealFlags<RealValueImpl> result;
+ result.value = FromWord(r.value);
+ result.flags = r.flags;
+ return result;
+ });
+}
+
+std::string RealValueImpl::DumpHexadecimal() const {
+ if (IsMonostate()) {
+ llvm_unreachable("unsupported operation over uninitialized value");
+ }
+ return withWord([](const auto &v) { return v.DumpHexadecimal(); });
+}
+
+llvm::raw_ostream &RealValueImpl::AsFortran(
+ llvm::raw_ostream &o, int kind, bool minimal) const {
+ if (IsMonostate()) {
+ o << "0";
+ return o;
+ }
+ withWord([&](const auto &v) {
+ v.AsFortran(o, kind, minimal);
+ return 0;
+ });
+ return o;
+}
+
+template <typename INT>
+IntegerValue RealValueImpl::IntegerValueFromFixed(const INT &n) {
+ IntegerValue result;
+ result.impl() = IntegerValueImpl::FromWord(n);
+ return result;
+}
+
+template <typename INT>
+INT RealValueImpl::FixedIntegerFromValue(const IntegerValue &v) {
+ return IntegerValueImpl::CoerceUnsigned<INT>(v.impl());
+}
+
+template <typename R>
+ValueWithRealFlags<R> RealValueImpl::FromIntegerValue(
+ const IntegerValue &n, bool isUnsigned, Rounding rounding) {
+ return n.impl().withWord([&](const auto &concrete) {
+ return R::FromInteger(concrete, isUnsigned, rounding);
+ });
+}
+
+} // namespace Fortran::evaluate::value
diff --git a/flang/lib/Evaluate/real-value-impl.h b/flang/lib/Evaluate/real-value-impl.h
new file mode 100644
index 0000000000000..be56259cdb4e0
--- /dev/null
+++ b/flang/lib/Evaluate/real-value-impl.h
@@ -0,0 +1,266 @@
+//===-- lib/Evaluate/real-value-impl.h ------------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef FORTRAN_EVALUATE_REAL_VALUE_IMPL_H_
+#define FORTRAN_EVALUATE_REAL_VALUE_IMPL_H_
+
+#include "flang/Evaluate/real.h"
+#include "llvm/Support/ErrorHandling.h"
+#include <type_traits>
+#include <utility>
+#include <variant>
+
+// Some environments, viz. glibc 2.17 and *BSD, allow the macro HUGE
+// to leak out of <math.h>.
+#undef HUGE
+
+namespace llvm {
+class raw_ostream;
+}
+
+namespace Fortran::evaluate::value {
+class IntegerValue;
+
+class RealValueImpl {
+public:
+ using R2 = Real<Integer<16>, 11>; // IEEE half
+ using R3 = Real<Integer<16>, 8>; // bfloat16
+ using R4 = Real<Integer<32>, 24>; // IEEE single
+ using R8 = Real<Integer<64>, 53>; // IEEE double
+ using R10 = Real<X87IntegerContainer, 64>; // 80387 extended precision
+ using R16 = Real<Integer<128>, 113>; // IEEE quad
+ using Storage = std::variant<std::monostate, R2, R3, R4, R8, R10, R16>;
+ using Word = IntegerValue;
+
+ // rule-of-five
+ ~RealValueImpl() = default;
+ RealValueImpl(const RealValueImpl &) = default;
+ RealValueImpl(RealValueImpl &&) = default;
+ RealValueImpl &operator=(const RealValueImpl &) = default;
+ RealValueImpl &operator=(RealValueImpl &&) = default;
+
+ RealValueImpl() = default;
+
+ // Interpret w as the raw bit pattern of a value of the given runtime kind.
+ RealValueImpl(int kind, const Word &w);
+
+ static RealValueImpl Zero(int kind);
+
+ template <typename T> static RealValueImpl FromWord(const T &r) {
+ RealValueImpl v;
+ v.storage_ = r;
+ return v;
+ }
+
+ template <typename T>
+ static ValueWithRealFlags<RealValueImpl> FromWord(
+ const ValueWithRealFlags<T> &x) {
+ ValueWithRealFlags<RealValueImpl> r;
+ r.value = FromWord(x.value);
+ r.flags = x.flags;
+ return r;
+ }
+
+ static RealValueImpl FromRawBytes(
+ int kind, const void *raw, std::size_t expectedSize);
+
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+ LLVM_DUMP_METHOD void dump() const;
+#endif
+
+ bool IsMonostate() const { return storage_.index() == 0; }
+ int kind() const;
+
+ int bits() const;
+
+ std::size_t bytesStored() const { return bytesStored(kind()); }
+ static constexpr std::size_t bytesStored(int kind) {
+ switch (kind) {
+ case 3:
+ return 2;
+ case 10:
+ return 16;
+ default:
+ return kind;
+ }
+ }
+
+ bool IsZero() const;
+
+ // Comparison operators
+ bool operator==(const RealValueImpl &y) const;
+ bool operator!=(const RealValueImpl &y) const { return !(*this == y); }
+
+ // Kind-property inquiries, formerly compile-time constants derived from the
+ // PREC template parameter; now selected by the runtime KIND.
+ static int DIGITS(int kind);
+ static int PRECISION(int kind);
+ static int RANGE(int kind);
+ static int MAXEXPONENT(int kind);
+ static int MINEXPONENT(int kind);
+
+ static RealValueImpl HUGE(int kind);
+ static RealValueImpl EPSILON(int kind);
+ static RealValueImpl TINY(int kind);
+ static RealValueImpl NotANumber(int kind);
+
+ // Runtime kind / width accessors
+ bool IsNegative() const;
+ bool IsNotANumber() const;
+ bool IsSignalingNaN() const;
+ bool IsInfinite() const;
+ bool IsFinite() const;
+ bool IsNormal() const;
+ int Exponent() const;
+ void StoreRawBytes(void *dst, size_t size, bool *changed) const;
+
+ // The raw bit pattern at the value's runtime width.
+ IntegerValue RawBits() const;
+
+ // Comparisons
+ Relation Compare(const RealValueImpl &y) const;
+
+ // Unary operations
+ RealValueImpl ABS() const;
+ RealValueImpl Negate() const;
+ RealValueImpl SIGN(const RealValueImpl &x) const;
+ RealValueImpl SetSign(bool toNegative) const;
+ RealValueImpl FlushSubnormalToZero() const;
+
+ // Binary arithmetic
+ ValueWithRealFlags<RealValueImpl> Add(const RealValueImpl &y,
+ Rounding rounding = TargetCharacteristics::defaultRounding) const;
+ ValueWithRealFlags<RealValueImpl> Subtract(const RealValueImpl &y,
+ Rounding rounding = TargetCharacteristics::defaultRounding) const;
+ ValueWithRealFlags<RealValueImpl> Multiply(const RealValueImpl &y,
+ Rounding rounding = TargetCharacteristics::defaultRounding) const;
+ ValueWithRealFlags<RealValueImpl> Divide(const RealValueImpl &y,
+ Rounding rounding = TargetCharacteristics::defaultRounding) const;
+ ValueWithRealFlags<RealValueImpl> SQRT(
+ Rounding rounding = TargetCharacteristics::defaultRounding) const;
+ ValueWithRealFlags<RealValueImpl> HYPOT(const RealValueImpl &y,
+ Rounding rounding = TargetCharacteristics::defaultRounding) const;
+ ValueWithRealFlags<RealValueImpl> MOD(const RealValueImpl &y,
+ Rounding rounding = TargetCharacteristics::defaultRounding) const;
+ ValueWithRealFlags<RealValueImpl> MODULO(const RealValueImpl &y,
+ Rounding rounding = TargetCharacteristics::defaultRounding) const;
+ ValueWithRealFlags<RealValueImpl> DIM(const RealValueImpl &y,
+ Rounding rounding = TargetCharacteristics::defaultRounding) const;
+
+ RealValueImpl FRACTION() const;
+ RealValueImpl RRSPACING() const;
+ RealValueImpl SPACING() const;
+ RealValueImpl SET_EXPONENT(std::int64_t e) const;
+
+ ValueWithRealFlags<RealValueImpl> NEAREST(bool upward) const;
+ ValueWithRealFlags<RealValueImpl> ToWholeNumber(
+ common::RoundingMode mode = common::RoundingMode::ToZero) const;
+ // Convert this real to an integer of the given bit width.
+ ValueWithRealFlags<IntegerValue> ToInteger(
+ common::RoundingMode mode = common::RoundingMode::ToZero,
+ int toBits = 0) const;
+
+ ValueWithRealFlags<RealValueImpl> SCALE(const IntegerValue &by,
+ Rounding rounding = TargetCharacteristics::defaultRounding) const;
+
+ ValueWithRealFlags<RealValueImpl> KahanSummation(const RealValueImpl &y,
+ RealValueImpl &correction,
+ Rounding rounding = TargetCharacteristics::defaultRounding) const;
+
+ IntegerValue EXPONENT() const;
+
+ // Conversion from an integer facade (REAL()).
+ static ValueWithRealFlags<RealValueImpl> FromInteger(int kind,
+ const IntegerValue &n, bool isUnsigned = false,
+ Rounding rounding = TargetCharacteristics::defaultRounding);
+
+ // Conversion between real kinds.
+ static ValueWithRealFlags<RealValueImpl> Convert(int kind,
+ const RealValueImpl &from,
+ Rounding rounding = TargetCharacteristics::defaultRounding);
+
+ static ValueWithRealFlags<RealValueImpl> Read(int kind, const char *&pp,
+ Rounding rounding = TargetCharacteristics::defaultRounding);
+
+ std::string DumpHexadecimal() const;
+ llvm::raw_ostream &AsFortran(
+ llvm::raw_ostream &o, int kind, bool minimal = false) const;
+
+ template <typename V> static std::decay_t<V> AsWord(const RealValueImpl &y) {
+ using R = std::decay_t<V>;
+ if (y.IsMonostate()) {
+ return R{};
+ }
+
+ return y.withWord([](const auto &yv) -> R {
+ using YR = std::decay_t<decltype(yv)>;
+ if constexpr (std::is_same_v<YR, R>) {
+ return yv;
+ } else {
+ return R::Convert(yv).value;
+ }
+ });
+ }
+
+ // Compile-time dispatchers to current/specified kind
+
+ template <typename F> static inline auto withWordProto(int kind, F &&f) {
+ using namespace Fortran::evaluate::value;
+ switch (kind) {
+ case 2:
+ return f(RealValueImpl::R2{});
+ case 3:
+ return f(RealValueImpl::R3{});
+ case 4:
+ return f(RealValueImpl::R4{});
+ case 8:
+ return f(RealValueImpl::R8{});
+ case 10:
+ return f(RealValueImpl::R10{});
+ case 16:
+ return f(RealValueImpl::R16{});
+ default:
+ llvm_unreachable("arbitrary bits not yet supported");
+ }
+ }
+
+ template <typename F> auto withWord(F &&f) const {
+ switch (storage_.index()) {
+ case 1:
+ return f(std::get<R2>(storage_));
+ case 2:
+ return f(std::get<R3>(storage_));
+ case 3:
+ return f(std::get<R4>(storage_));
+ case 4:
+ return f(std::get<R8>(storage_));
+ case 5:
+ return f(std::get<R10>(storage_));
+ case 6:
+ return f(std::get<R16>(storage_));
+ default:
+ llvm_unreachable("operation on uninitialized RealValueImpl");
+ }
+ }
+
+private:
+ template <typename INT>
+ static IntegerValue IntegerValueFromFixed(const INT &);
+
+ template <typename INT>
+ static INT FixedIntegerFromValue(const IntegerValue &);
+
+ template <typename R>
+ static ValueWithRealFlags<R> FromIntegerValue(
+ const IntegerValue &v, bool isUnsigned, Rounding rounding);
+
+ Storage storage_;
+};
+
+} // namespace Fortran::evaluate::value
+#endif // FORTRAN_EVALUATE_REAL_VALUE_IMPL_H_
diff --git a/flang/lib/Evaluate/real-value.cpp b/flang/lib/Evaluate/real-value.cpp
new file mode 100644
index 0000000000000..b6bbdad9170ab
--- /dev/null
+++ b/flang/lib/Evaluate/real-value.cpp
@@ -0,0 +1,264 @@
+//===-- lib/Evaluate/real-value.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/real-value.h"
+#include "integer-value-impl.h"
+#include "real-value-impl.h"
+#include "flang/Common/idioms.h"
+#include "flang/Decimal/decimal.h"
+#include "flang/Evaluate/rounding-bits.h"
+#include "llvm/Support/raw_ostream.h"
+#include <cstring>
+#include <new>
+#include <string>
+
+namespace Fortran::evaluate::value {
+static_assert(sizeof(RealValueImpl) == detail::kRealObjectSize);
+static_assert(alignof(RealValueImpl) == detail::kRealObjectAlign);
+static_assert(sizeof(RealValue) == sizeof(RealValueImpl));
+static_assert(alignof(RealValue) == alignof(RealValueImpl));
+
+RealValue::RealValue() { new (this) RealValueImpl(); }
+
+RealValue::~RealValue() { impl().~RealValueImpl(); }
+
+RealValue::RealValue(const RealValue &x) { new (this) RealValueImpl(x.impl()); }
+
+RealValue::RealValue(RealValue &&x) {
+ new (this) RealValueImpl(std::move(x.impl()));
+}
+
+RealValue &RealValue::operator=(const RealValue &x) {
+ impl() = x.impl();
+ return *this;
+}
+
+RealValue &RealValue::operator=(RealValue &&x) {
+ impl() = std::move(x.impl());
+ return *this;
+}
+
+RealValue::RealValue(int kind, const Word &w) {
+ new (this) RealValueImpl(kind, w);
+}
+
+RealValue RealValue::Zero(int kind) {
+ return FromImpl(RealValueImpl::Zero(kind));
+}
+
+bool RealValue::IsMonostate() const { return impl().IsMonostate(); }
+
+int RealValue::kind() const { return impl().kind(); }
+
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+LLVM_DUMP_METHOD void RealValue::dump() const { impl().dump(); }
+#endif
+
+bool RealValue::operator==(const RealValue &y) const {
+ return impl() == y.impl();
+}
+
+bool RealValue::IsNegative() const { return impl().IsNegative(); }
+
+bool RealValue::IsNotANumber() const { return impl().IsNotANumber(); }
+
+bool RealValue::IsSignalingNaN() const { return impl().IsSignalingNaN(); }
+
+bool RealValue::IsInfinite() const { return impl().IsInfinite(); }
+
+bool RealValue::IsFinite() const { return impl().IsFinite(); }
+
+bool RealValue::IsZero() const { return impl().IsZero(); }
+
+bool RealValue::IsNormal() const { return impl().IsNormal(); }
+
+RealValue RealValue::ABS() const { return FromImpl(impl().ABS()); }
+
+RealValue RealValue::SetSign(bool toNegative) const {
+ return FromImpl(impl().SetSign(toNegative));
+}
+
+RealValue RealValue::SIGN(const RealValue &x) const {
+ return FromImpl(impl().SIGN(x.impl()));
+}
+
+RealValue RealValue::Negate() const { return FromImpl(impl().Negate()); }
+
+Relation RealValue::Compare(const RealValue &y) const {
+ return impl().Compare(y.impl());
+}
+
+ValueWithRealFlags<RealValue> RealValue::Add(
+ const RealValue &y, Rounding rounding) const {
+ return FromImpl(impl().Add(y.impl(), rounding));
+}
+ValueWithRealFlags<RealValue> RealValue::Subtract(
+ const RealValue &y, Rounding rounding) const {
+ return FromImpl(impl().Subtract(y.impl(), rounding));
+}
+ValueWithRealFlags<RealValue> RealValue::Multiply(
+ const RealValue &y, Rounding rounding) const {
+ return FromImpl(impl().Multiply(y.impl(), rounding));
+}
+ValueWithRealFlags<RealValue> RealValue::Divide(
+ const RealValue &y, Rounding rounding) const {
+ return FromImpl(impl().Divide(y.impl(), rounding));
+}
+ValueWithRealFlags<RealValue> RealValue::SQRT(Rounding rounding) const {
+ return FromImpl(impl().SQRT(rounding));
+}
+ValueWithRealFlags<RealValue> RealValue::NEAREST(bool upward) const {
+ return FromImpl(impl().NEAREST(upward));
+}
+ValueWithRealFlags<RealValue> RealValue::HYPOT(
+ const RealValue &y, Rounding rounding) const {
+ return FromImpl(impl().HYPOT(y.impl(), rounding));
+}
+ValueWithRealFlags<RealValue> RealValue::DIM(
+ const RealValue &y, Rounding rounding) const {
+ return FromImpl(impl().DIM(y.impl(), rounding));
+}
+ValueWithRealFlags<RealValue> RealValue::MOD(
+ const RealValue &y, Rounding rounding) const {
+ return FromImpl(impl().MOD(y.impl(), rounding));
+}
+ValueWithRealFlags<RealValue> RealValue::MODULO(
+ const RealValue &y, Rounding rounding) const {
+ return FromImpl(impl().MODULO(y.impl(), rounding));
+}
+ValueWithRealFlags<RealValue> RealValue::KahanSummation(
+ const RealValue &y, RealValue &correction, Rounding rounding) const {
+ return FromImpl(impl().KahanSummation(y.impl(), correction.impl(), rounding));
+}
+
+IntegerValue RealValue::EXPONENT() const { return impl().EXPONENT(); }
+
+RealValue RealValue::EPSILON(int kind) {
+ return FromImpl(RealValueImpl::EPSILON(kind));
+}
+
+RealValue RealValue::HUGE(int kind) {
+ return FromImpl(RealValueImpl::HUGE(kind));
+}
+
+RealValue RealValue::TINY(int kind) {
+ return FromImpl(RealValueImpl::TINY(kind));
+}
+
+int RealValue::DIGITS(int kind) { return RealValueImpl::DIGITS(kind); }
+
+int RealValue::PRECISION(int kind) { return RealValueImpl::PRECISION(kind); }
+
+int RealValue::RANGE(int kind) { return RealValueImpl::RANGE(kind); }
+
+int RealValue::MAXEXPONENT(int kind) {
+ return RealValueImpl::MAXEXPONENT(kind);
+}
+
+int RealValue::MINEXPONENT(int kind) {
+ return RealValueImpl::MINEXPONENT(kind);
+}
+
+RealValue RealValue::RRSPACING() const { return FromImpl(impl().RRSPACING()); }
+
+RealValue RealValue::SPACING() const { return FromImpl(impl().SPACING()); }
+
+RealValue RealValue::SET_EXPONENT(std::int64_t e) const {
+ return FromImpl(impl().SET_EXPONENT(e));
+}
+
+RealValue RealValue::FRACTION() const { return FromImpl(impl().FRACTION()); }
+
+ValueWithRealFlags<RealValue> RealValue::SCALE(
+ const IntegerValue &by, Rounding rounding) const {
+ return FromImpl(impl().SCALE(by, rounding));
+}
+
+RealValue RealValue::FlushSubnormalToZero() const {
+ return FromImpl(impl().FlushSubnormalToZero());
+}
+
+RealValue RealValue::NotANumber(int kind) {
+ return FromImpl(RealValueImpl::NotANumber(kind));
+}
+
+ValueWithRealFlags<RealValue> RealValue::FromInteger(
+ int kind, const IntegerValue &n, bool isUnsigned, Rounding rounding) {
+ return FromImpl(RealValueImpl::FromInteger(kind, n, isUnsigned, rounding));
+}
+
+ValueWithRealFlags<RealValue> RealValue::ToWholeNumber(
+ common::RoundingMode mode) const {
+ return FromImpl(impl().ToWholeNumber(mode));
+}
+ValueWithRealFlags<IntegerValue> RealValue::ToInteger(
+ common::RoundingMode mode, int toBits) const {
+ return impl().ToInteger(mode, toBits);
+}
+
+ValueWithRealFlags<RealValue> RealValue::Convert(
+ int kind, const RealValue &from, Rounding rounding) {
+ return FromImpl(RealValueImpl::Convert(kind, from.impl(), rounding));
+}
+
+IntegerValue RealValue::RawBits() const { return impl().RawBits(); }
+
+int RealValue::Exponent() const { return impl().Exponent(); }
+
+ValueWithRealFlags<RealValue> RealValue::Read(
+ int kind, const char *&pp, Rounding rounding) {
+ return FromImpl(RealValueImpl::Read(kind, pp, rounding));
+}
+
+std::string RealValue::DumpHexadecimal() const {
+ return impl().DumpHexadecimal();
+}
+
+llvm::raw_ostream &RealValue::AsFortran(
+ llvm::raw_ostream &o, int kind, bool minimal) const {
+ return impl().AsFortran(o, kind, minimal);
+}
+
+RealValue RealValue::FromRawBytes(
+ int kind, const void *raw, std::size_t expectedSize) {
+ return FromImpl(RealValueImpl::FromRawBytes(kind, raw, expectedSize));
+}
+
+void RealValue::StoreRawBytes(void *dst, size_t size, bool *changed) const {
+ impl().StoreRawBytes(dst, size, changed);
+}
+
+RealValue RealValue::FromImpl(const RealValueImpl &x) {
+ RealValue r;
+ r.impl() = x;
+ return r;
+}
+
+RealValue RealValue::FromImpl(RealValueImpl &&x) {
+ RealValue r;
+ r.impl() = std::move(x);
+ return r;
+}
+
+ValueWithRealFlags<RealValue> RealValue::FromImpl(
+ const ValueWithRealFlags<RealValueImpl> &x) {
+ ValueWithRealFlags<RealValue> r;
+ r.value.impl() = std::move(x.value);
+ r.flags = x.flags;
+ return r;
+}
+
+ValueWithRealFlags<RealValue> RealValue::FromImpl(
+ ValueWithRealFlags<RealValueImpl> &&x) {
+ ValueWithRealFlags<RealValue> r;
+ r.value.impl() = x.value;
+ r.flags = x.flags;
+ return r;
+}
+
+} // namespace Fortran::evaluate::value
diff --git a/flang/lib/Evaluate/shape.cpp b/flang/lib/Evaluate/shape.cpp
index 924b6cbdddd5e..4d10951fe6501 100644
--- a/flang/lib/Evaluate/shape.cpp
+++ b/flang/lib/Evaluate/shape.cpp
@@ -53,7 +53,7 @@ Shape GetShapeHelper::ConstantShape(const Constant<ExtentType> &arrayConstant) {
std::size_t dimensions{arrayConstant.size()};
for (std::size_t j{0}; j < dimensions; ++j) {
Scalar<ExtentType> extent{arrayConstant.values().at(j)};
- result.emplace_back(MaybeExtentExpr{ExtentExpr{std::move(extent)}});
+ result.emplace_back(MakeExtentExpr(std::move(extent)));
}
return result;
}
@@ -98,7 +98,8 @@ std::optional<ExtentExpr> AsExtentArrayExpr(const Shape &shape) {
return std::nullopt;
}
}
- return ExtentExpr{ArrayConstructor<ExtentType>{std::move(values)}};
+ return ExtentExpr{
+ ArrayConstructor<ExtentType>{SubscriptIntegerKind, std::move(values)}};
}
std::optional<Constant<ExtentType>> AsConstantShape(
@@ -116,9 +117,10 @@ Constant<SubscriptInteger> AsConstantShape(const ConstantSubscripts &shape) {
using IntType = Scalar<SubscriptInteger>;
std::vector<IntType> result;
for (auto dim : shape) {
- result.emplace_back(dim);
+ result.emplace_back(SubscriptIntegerKind, dim);
}
- return {std::move(result), ConstantSubscripts{GetRank(shape)}};
+ return {SubscriptIntegerKind, std::move(result),
+ ConstantSubscripts{GetRank(shape)}};
}
ConstantSubscripts AsConstantExtents(const Constant<ExtentType> &shape) {
@@ -141,7 +143,7 @@ std::optional<ConstantSubscripts> AsConstantExtents(
Shape AsShape(const ConstantSubscripts &shape) {
Shape result;
for (const auto &extent : shape) {
- result.emplace_back(ExtentExpr{extent});
+ result.emplace_back(MakeExtentExpr(extent));
}
return result;
}
@@ -176,8 +178,8 @@ static ExtentExpr ComputeTripCount(
ExtentExpr span{
(std::move(upper) - std::move(lower) + std::move(strideCopy)) /
std::move(stride)};
- return ExtentExpr{
- Extremum<ExtentType>{Ordering::Greater, std::move(span), ExtentExpr{0}}};
+ return ExtentExpr{Extremum<ExtentType>{
+ Ordering::Greater, std::move(span), MakeExtentExpr(0)}};
}
ExtentExpr CountTrips(
@@ -201,7 +203,7 @@ MaybeExtentExpr CountTrips(MaybeExtentExpr &&lower, MaybeExtentExpr &&upper,
}
MaybeExtentExpr GetSize(Shape &&shape) {
- ExtentExpr extent{1};
+ ExtentExpr extent{MakeExtentExpr(1)};
for (auto &&dim : std::move(shape)) {
if (dim) {
extent = std::move(extent) * std::move(*dim);
@@ -245,10 +247,10 @@ class GetLowerBoundHelper
int d, FoldingContext *context, bool invariantOnly)
: Base{*this}, dimension_{d}, context_{context},
invariantOnly_{invariantOnly} {}
- static Result Default() { return Result{1}; }
+ static Result Default() { return Result{MakeExtentExpr(1)}; }
static Result Combine(Result &&, Result &&) {
// Operator results and array references always have lower bounds == 1
- return Result{1};
+ return MakeExtentExpr(1);
}
Result GetLowerBound(const Symbol &symbol0, NamedEntity &&base) const {
@@ -281,10 +283,10 @@ class GetLowerBoundHelper
if (context_) {
auto extent{ToInt64(Fold(*context_,
ExtentExpr{*ubound} - ExtentExpr{*lbound} +
- ExtentExpr{1}))};
+ MakeExtentExpr(1)))};
if (extent) {
if (extent <= 0) {
- return Result{1};
+ return MakeExtentExpr(1);
}
ok = true;
} else {
@@ -294,7 +296,7 @@ class GetLowerBoundHelper
auto ubValue{ToInt64(*ubound)};
if (lbValue && ubValue) {
if (*lbValue > *ubValue) {
- return Result{1};
+ return MakeExtentExpr(1);
}
ok = true;
} else {
@@ -307,7 +309,7 @@ class GetLowerBoundHelper
return *lbound;
}
} else {
- return Result{1};
+ return MakeExtentExpr(1);
}
}
if (IsDescriptor(symbol)) {
@@ -318,7 +320,7 @@ class GetLowerBoundHelper
} else if (const auto *assoc{
symbol.detailsIf<semantics::AssocEntityDetails>()}) {
if (assoc->IsAssumedSize()) { // RANK(*)
- return Result{1};
+ return MakeExtentExpr(1);
} else if (assoc->IsAssumedRank()) { // RANK DEFAULT
} else if (assoc->rank()) { // RANK(n)
const Symbol &resolved{ResolveAssociations(symbol)};
@@ -343,7 +345,7 @@ class GetLowerBoundHelper
if constexpr (LBOUND_SEMANTICS) {
return Result{};
} else {
- return Result{1};
+ return MakeExtentExpr(1);
}
}
@@ -356,7 +358,7 @@ class GetLowerBoundHelper
return GetLowerBound(
component.GetLastSymbol(), NamedEntity{common::Clone(component)});
}
- return Result{1};
+ return MakeExtentExpr(1);
}
template <typename T> Result operator()(const Expr<T> &expr) const {
@@ -366,10 +368,10 @@ class GetLowerBoundHelper
if (const auto *con{std::get_if<Constant<T>>(&expr.u)}) {
ConstantSubscripts lb{con->lbounds()};
if (dimension_ < GetRank(lb)) {
- return Result{lb[dimension_]};
+ return MakeExtentExpr(lb[dimension_]);
}
} else { // operation
- return Result{1};
+ return MakeExtentExpr(1);
}
} else {
return (*this)(expr.u);
@@ -377,7 +379,7 @@ class GetLowerBoundHelper
if constexpr (LBOUND_SEMANTICS) {
return Result{};
} else {
- return Result{1};
+ return MakeExtentExpr(1);
}
}
@@ -462,9 +464,9 @@ static MaybeExtentExpr GetNonNegativeExtent(
std::optional<ConstantSubscript> lval{ToInt64(lbound)};
if (uval && lval) {
if (*uval < *lval) {
- return ExtentExpr{0};
+ return MakeExtentExpr(0);
} else {
- return ExtentExpr{*uval - *lval + 1};
+ return MakeExtentExpr(*uval - *lval + 1);
}
} else if (lbound && ubound && lbound->Rank() == 0 && ubound->Rank() == 0 &&
(!invariantOnly ||
@@ -473,11 +475,11 @@ static MaybeExtentExpr GetNonNegativeExtent(
// result is never negative
if (lval.value_or(0) == 1) {
return ExtentExpr{Extremum<SubscriptInteger>{
- Ordering::Greater, ExtentExpr{0}, common::Clone(*ubound)}};
+ Ordering::Greater, MakeExtentExpr(0), common::Clone(*ubound)}};
} else {
- return ExtentExpr{
- Extremum<SubscriptInteger>{Ordering::Greater, ExtentExpr{0},
- common::Clone(*ubound) - common::Clone(*lbound) + ExtentExpr{1}}};
+ return ExtentExpr{Extremum<SubscriptInteger>{Ordering::Greater,
+ MakeExtentExpr(0),
+ common::Clone(*ubound) - common::Clone(*lbound) + MakeExtentExpr(1)}};
}
} else {
return std::nullopt;
@@ -598,7 +600,7 @@ MaybeExtentExpr ComputeUpperBound(
if (ToInt64(lower).value_or(0) == 1) {
return std::move(*extent);
} else {
- return std::move(*extent) + std::move(lower) - ExtentExpr{1};
+ return std::move(*extent) + std::move(lower) - MakeExtentExpr(1);
}
} else {
return std::nullopt;
@@ -658,7 +660,7 @@ static MaybeExtentExpr GetExplicitUBOUND(FoldingContext *context,
if (cstExtent > 0) {
return *ubound;
} else if (cstExtent == 0) {
- return ExtentExpr{0};
+ return MakeExtentExpr(0);
}
}
}
@@ -992,8 +994,7 @@ auto GetShapeHelper::operator()(const ProcedureRef &call) const -> Result {
if (semantics::IsAssumedRank(*call.arguments().front())) {
return Shape{MaybeExtentExpr{}};
} else {
- return Shape{
- MaybeExtentExpr{ExtentExpr{call.arguments().front()->Rank()}}};
+ return Shape{MakeExtentExpr(call.arguments().front()->Rank())};
}
}
} else if (intrinsic->name == "all" || intrinsic->name == "any" ||
@@ -1033,7 +1034,7 @@ auto GetShapeHelper::operator()(const ProcedureRef &call) const -> Result {
}
} else {
// xxxLOC(no DIM=) result is vector(1:RANK(ARRAY=))
- return Shape{ExtentExpr{rank}};
+ return Shape{MakeExtentExpr(rank)};
}
}
}
@@ -1043,7 +1044,7 @@ auto GetShapeHelper::operator()(const ProcedureRef &call) const -> Result {
}
} else if (intrinsic->name == "lcobound" || intrinsic->name == "ucobound") {
if (call.arguments().size() == 3 && !call.arguments().at(1).has_value()) {
- return Shape(1, ExtentExpr{GetCorank(call.arguments().at(0))});
+ return Shape(1, MakeExtentExpr(GetCorank(call.arguments().at(0))));
}
} else if (intrinsic->name == "matmul") {
if (call.arguments().size() == 2) {
@@ -1074,12 +1075,12 @@ auto GetShapeHelper::operator()(const ProcedureRef &call) const -> Result {
if (auto arraySize{GetSize(std::move(*arrayShape))}) {
ActualArguments toMerge{
ActualArgument{AsGenericExpr(std::move(*arraySize))},
- ActualArgument{AsGenericExpr(ExtentExpr{0})},
+ ActualArgument{AsGenericExpr(MakeExtentExpr(0))},
common::Clone(call.arguments().at(1))};
auto specific{context_->intrinsics().Probe(
CallCharacteristics{"merge"}, toMerge, *context_)};
CHECK(specific);
- return Shape{ExtentExpr{FunctionRef<ExtentType>{
+ return Shape{ExtentExpr{FunctionRef<ExtentType>{ExtentIntKind,
ProcedureDesignator{std::move(specific->specificIntrinsic)},
std::move(specific->arguments)}}};
}
@@ -1087,7 +1088,7 @@ auto GetShapeHelper::operator()(const ProcedureRef &call) const -> Result {
} else {
// Non-scalar MASK= -> [COUNT(mask, KIND=extent_kind)]
ActualArgument kindArg{
- AsGenericExpr(Constant<ExtentType>{ExtentType::kind})};
+ AsGenericExpr(MakeExtentConstant(ExtentIntKind))};
kindArg.set_keyword(context_->SaveTempName("kind"));
ActualArguments toCount{
ActualArgument{common::Clone(
@@ -1096,7 +1097,7 @@ auto GetShapeHelper::operator()(const ProcedureRef &call) const -> Result {
auto specific{context_->intrinsics().Probe(
CallCharacteristics{"count"}, toCount, *context_)};
CHECK(specific);
- return Shape{ExtentExpr{FunctionRef<ExtentType>{
+ return Shape{ExtentExpr{FunctionRef<ExtentType>{ExtentIntKind,
ProcedureDesignator{std::move(specific->specificIntrinsic)},
std::move(specific->arguments)}}};
}
@@ -1108,8 +1109,8 @@ auto GetShapeHelper::operator()(const ProcedureRef &call) const -> Result {
if (const auto *shapeExpr{
call.arguments().at(1).value().UnwrapExpr()}) {
auto shapeArg{std::get<Expr<SomeInteger>>(shapeExpr->u)};
- if (auto result{AsShapeResult(
- ConvertToType<ExtentType>(std::move(shapeArg)))}) {
+ if (auto result{AsShapeResult(ConvertToType<ExtentType>(
+ ExtentIntKind, std::move(shapeArg)))}) {
return result;
}
}
@@ -1128,8 +1129,10 @@ auto GetShapeHelper::operator()(const ProcedureRef &call) const -> Result {
if (*dim >= 1 &&
static_cast<std::size_t>(*dim) <= arrayShape->size() + 1) {
arrayShape->emplace(arrayShape->begin() + *dim - 1,
- Extremum<SubscriptInteger>{Ordering::Greater, ExtentExpr{0},
- ConvertToType<ExtentType>(common::Clone(*nCopies))});
+ Extremum<SubscriptInteger>{Ordering::Greater,
+ MakeExtentExpr(0),
+ ConvertToType<ExtentType>(
+ ExtentIntKind, common::Clone(*nCopies))});
return std::move(*arrayShape);
}
}
@@ -1140,8 +1143,8 @@ auto GetShapeHelper::operator()(const ProcedureRef &call) const -> Result {
// SIZE= is present; shape is vector [SIZE=]
if (const auto *size{
UnwrapExpr<Expr<SomeInteger>>(call.arguments().at(2))}) {
- return Shape{
- MaybeExtentExpr{ConvertToType<ExtentType>(common::Clone(*size))}};
+ return Shape{MaybeExtentExpr{
+ ConvertToType<ExtentType>(ExtentIntKind, common::Clone(*size))}};
}
} else if (context_) {
if (auto moldTypeAndShape{characteristics::TypeAndShape::Characterize(
@@ -1163,7 +1166,7 @@ auto GetShapeHelper::operator()(const ProcedureRef &call) const -> Result {
*sourceBytes = Fold(*context_, std::move(*sourceBytes));
if (auto sourceBytesConst{ToInt64(*sourceBytes)}) {
if (*sourceBytesConst == 0) {
- return Shape{ExtentExpr{0}};
+ return Shape{MakeExtentExpr(0)};
}
}
if (auto moldElementBytes{
@@ -1175,7 +1178,8 @@ auto GetShapeHelper::operator()(const ProcedureRef &call) const -> Result {
if (moldElementBytesConst && *moldElementBytesConst != 0) {
ExtentExpr extent{Fold(*context_,
(std::move(*sourceBytes) +
- common::Clone(*moldElementBytes) - ExtentExpr{1}) /
+ common::Clone(*moldElementBytes) -
+ MakeExtentExpr(1)) /
common::Clone(*moldElementBytes))};
return Shape{MaybeExtentExpr{std::move(extent)}};
}
@@ -1188,7 +1192,7 @@ auto GetShapeHelper::operator()(const ProcedureRef &call) const -> Result {
} else if (intrinsic->name == "this_image") {
if (call.arguments().size() == 2) {
// THIS_IMAGE(coarray, no DIM, [TEAM])
- return Shape(1, ExtentExpr{GetCorank(call.arguments().at(0))});
+ return Shape(1, MakeExtentExpr(GetCorank(call.arguments().at(0))));
}
} else if (intrinsic->name == "transpose") {
if (call.arguments().size() >= 1) {
diff --git a/flang/lib/Evaluate/static-data.cpp b/flang/lib/Evaluate/static-data.cpp
index 9063de0f3a938..bcc19913bd543 100644
--- a/flang/lib/Evaluate/static-data.cpp
+++ b/flang/lib/Evaluate/static-data.cpp
@@ -54,6 +54,12 @@ StaticDataObject &StaticDataObject::Push(
return *this;
}
+StaticDataObject &StaticDataObject::Push(
+ const value::CharacterValue &v, bool bigEndian) {
+ return v.withStdString(
+ [&](const auto &s) -> StaticDataObject & { return Push(s, bigEndian); });
+}
+
std::optional<std::string> StaticDataObject::AsString() const {
if (itemBytes_ <= 1) {
std::string result;
diff --git a/flang/lib/Evaluate/target.cpp b/flang/lib/Evaluate/target.cpp
index c443278148304..47442634355c2 100644
--- a/flang/lib/Evaluate/target.cpp
+++ b/flang/lib/Evaluate/target.cpp
@@ -162,10 +162,10 @@ class SelectedIntKindVisitor {
: targetCharacteristics_{targetCharacteristics}, precision_{p} {}
using Result = std::optional<int>;
using Types = IntegerTypes;
- template <typename T> Result Test() const {
- if (Scalar<T>::RANGE >= precision_ &&
- targetCharacteristics_.IsTypeEnabled(T::category, T::kind)) {
- return T::kind;
+ template <typename T> Result Test(int kind) const {
+ if (Scalar<T>::RANGE(kind) >= precision_ &&
+ targetCharacteristics_.IsTypeEnabled(T::category, kind)) {
+ return kind;
} else {
return std::nullopt;
}
@@ -177,8 +177,7 @@ class SelectedIntKindVisitor {
};
int TargetCharacteristics::SelectedIntKind(std::int64_t precision) const {
- if (auto kind{
- common::SearchTypes(SelectedIntKindVisitor{*this, precision})}) {
+ if (auto kind{SearchTypes(SelectedIntKindVisitor{*this, precision})}) {
return *kind;
} else {
return -1;
@@ -193,10 +192,10 @@ class SelectedLogicalKindVisitor {
: targetCharacteristics_{targetCharacteristics}, bits_{bits} {}
using Result = std::optional<int>;
using Types = LogicalTypes;
- template <typename T> Result Test() const {
- if (Scalar<T>::bits >= bits_ &&
- targetCharacteristics_.IsTypeEnabled(T::category, T::kind)) {
- return T::kind;
+ template <typename T> Result Test(int kind) const {
+ if (Scalar<T>::bits(kind) >= bits_ &&
+ targetCharacteristics_.IsTypeEnabled(T::category, kind)) {
+ return kind;
} else {
return std::nullopt;
}
@@ -208,7 +207,7 @@ class SelectedLogicalKindVisitor {
};
int TargetCharacteristics::SelectedLogicalKind(std::int64_t bits) const {
- if (auto kind{common::SearchTypes(SelectedLogicalKindVisitor{*this, bits})}) {
+ if (auto kind{SearchTypes(SelectedLogicalKindVisitor{*this, bits})}) {
return *kind;
} else {
return -1;
@@ -224,10 +223,11 @@ class SelectedRealKindVisitor {
r} {}
using Result = std::optional<int>;
using Types = RealTypes;
- template <typename T> Result Test() const {
- if (Scalar<T>::PRECISION >= precision_ && Scalar<T>::RANGE >= range_ &&
- targetCharacteristics_.IsTypeEnabled(T::category, T::kind)) {
- return {T::kind};
+ template <typename T> Result Test(int kind) const {
+ if (Scalar<T>::PRECISION(kind) >= precision_ &&
+ Scalar<T>::RANGE(kind) >= range_ &&
+ targetCharacteristics_.IsTypeEnabled(T::category, kind)) {
+ return {kind};
} else {
return std::nullopt;
}
@@ -243,15 +243,15 @@ int TargetCharacteristics::SelectedRealKind(
if (radix != 2) {
return -5;
}
- if (auto kind{common::SearchTypes(
- SelectedRealKindVisitor{*this, precision, range})}) {
+ if (auto kind{
+ SearchTypes(SelectedRealKindVisitor{*this, precision, range})}) {
return *kind;
}
// No kind has both sufficient precision and sufficient range.
// The negative return value encodes whether any kinds exist that
// could satisfy either constraint independently.
- bool pOK{common::SearchTypes(SelectedRealKindVisitor{*this, precision, 0})};
- bool rOK{common::SearchTypes(SelectedRealKindVisitor{*this, 0, range})};
+ bool pOK{SearchTypes(SelectedRealKindVisitor{*this, precision, 0})};
+ bool rOK{SearchTypes(SelectedRealKindVisitor{*this, 0, range})};
if (pOK) {
if (rOK) {
return -4;
diff --git a/flang/lib/Evaluate/tools.cpp b/flang/lib/Evaluate/tools.cpp
index 589aab5132a65..5e2cfd474fb19 100644
--- a/flang/lib/Evaluate/tools.cpp
+++ b/flang/lib/Evaluate/tools.cpp
@@ -155,23 +155,27 @@ ConvertRealOperandsResult ConvertRealOperands(
},
[&](Expr<SomeInteger> &&ix,
Expr<SomeReal> &&ry) -> ConvertRealOperandsResult {
+ auto converted{ConvertTo(ry, std::move(ix))};
return {AsSameKindExprs<TypeCategory::Real>(
- ConvertTo(ry, std::move(ix)), std::move(ry))};
+ std::move(converted), std::move(ry))};
},
[&](Expr<SomeUnsigned> &&ix,
Expr<SomeReal> &&ry) -> ConvertRealOperandsResult {
+ auto converted{ConvertTo(ry, std::move(ix))};
return {AsSameKindExprs<TypeCategory::Real>(
- ConvertTo(ry, std::move(ix)), std::move(ry))};
+ std::move(converted), std::move(ry))};
},
[&](Expr<SomeReal> &&rx,
Expr<SomeInteger> &&iy) -> ConvertRealOperandsResult {
+ auto converted{ConvertTo(rx, std::move(iy))};
return {AsSameKindExprs<TypeCategory::Real>(
- std::move(rx), ConvertTo(rx, std::move(iy)))};
+ std::move(rx), std::move(converted))};
},
[&](Expr<SomeReal> &&rx,
Expr<SomeUnsigned> &&iy) -> ConvertRealOperandsResult {
+ auto converted{ConvertTo(rx, std::move(iy))};
return {AsSameKindExprs<TypeCategory::Real>(
- std::move(rx), ConvertTo(rx, std::move(iy)))};
+ std::move(rx), std::move(converted))};
},
[&](Expr<SomeReal> &&rx,
Expr<SomeReal> &&ry) -> ConvertRealOperandsResult {
@@ -212,13 +216,15 @@ ConvertRealOperandsResult ConvertRealOperands(
},
[&](Expr<SomeReal> &&rx,
BOZLiteralConstant &&by) -> ConvertRealOperandsResult {
+ auto converted{ConvertTo(rx, std::move(by))};
return {AsSameKindExprs<TypeCategory::Real>(
- std::move(rx), ConvertTo(rx, std::move(by)))};
+ std::move(rx), std::move(converted))};
},
[&](BOZLiteralConstant &&bx,
Expr<SomeReal> &&ry) -> ConvertRealOperandsResult {
+ auto converted{ConvertTo(ry, std::move(bx))};
return {AsSameKindExprs<TypeCategory::Real>(
- ConvertTo(ry, std::move(bx)), std::move(ry))};
+ std::move(converted), std::move(ry))};
},
[&](BOZLiteralConstant &&,
BOZLiteralConstant &&) -> ConvertRealOperandsResult {
@@ -259,22 +265,23 @@ std::optional<Expr<SomeType>> MixedRealLeft(
return Package(common::visit(
[&](auto &&rxk) -> Expr<SomeReal> {
using resultType = ResultType<decltype(rxk)>;
+ const int resultKind = rxk.kind();
if constexpr (std::is_same_v<OPR<resultType>, Power<resultType>>) {
- return AsCategoryExpr(
- RealToIntPower<resultType>{std::move(rxk), std::move(iy)});
+ return AsCategoryExpr(RealToIntPower<resultType>{
+ resultKind, std::move(rxk), std::move(iy)});
}
// G++ 8.1.0 emits bogus warnings about missing return statements if
// this statement is wrapped in an "else", as it should be.
- return AsCategoryExpr(OPR<resultType>{
- std::move(rxk), ConvertToType<resultType>(std::move(iy))});
+ auto converted{ConvertToType<resultType>(resultKind, std::move(iy))};
+ return AsCategoryExpr(
+ OPR<resultType>{resultKind, std::move(rxk), std::move(converted)});
},
std::move(rx.u)));
}
-template <int KIND>
-Expr<SomeComplex> MakeComplex(Expr<Type<TypeCategory::Real, KIND>> &&re,
- Expr<Type<TypeCategory::Real, KIND>> &&im) {
- return AsCategoryExpr(ComplexConstructor<KIND>{std::move(re), std::move(im)});
+static Expr<SomeComplex> MakeComplex(
+ Expr<Type<TypeCategory::Real>> &&re, Expr<Type<TypeCategory::Real>> &&im) {
+ return AsCategoryExpr(ComplexConstructor{std::move(re), std::move(im)});
}
std::optional<Expr<SomeComplex>> ConstructComplex(
@@ -304,83 +311,17 @@ std::optional<Expr<SomeComplex>> ConstructComplex(
// Extracts the real or imaginary part of the result of a COMPLEX
// expression, when that expression is simple enough to be duplicated.
template <bool GET_IMAGINARY> struct ComplexPartExtractor {
+ // NOTE: The the code in this class was dead code; a std/common::withStdString
+ // was forgotten such that the overload resolution was looking for a
+ // std::variant<...> overload instead for the runtime content of the
+ // std::variant. There was no specialization for std::variant so this fallback
+ // overload was matched unconditionally. The intended overloads were broken
+ // due to never been checked by the compiler, because they were never
+ // instantiated. As a result, the complex expression is just never considered
+ // "simple enough".
template <typename A> static std::optional<Expr<SomeReal>> Get(const A &) {
return std::nullopt;
}
-
- template <int KIND>
- static std::optional<Expr<SomeReal>> Get(
- const Parentheses<Type<TypeCategory::Complex, KIND>> &kz) {
- if (auto x{Get(kz.left())}) {
- return AsGenericExpr(AsSpecificExpr(
- Parentheses<Type<TypeCategory::Real, KIND>>{std::move(*x)}));
- } else {
- return std::nullopt;
- }
- }
-
- template <int KIND>
- static std::optional<Expr<SomeReal>> Get(
- const Negate<Type<TypeCategory::Complex, KIND>> &kz) {
- if (auto x{Get(kz.left())}) {
- return AsGenericExpr(AsSpecificExpr(
- Negate<Type<TypeCategory::Real, KIND>>{std::move(*x)}));
- } else {
- return std::nullopt;
- }
- }
-
- template <int KIND>
- static std::optional<Expr<SomeReal>> Get(
- const Convert<Type<TypeCategory::Complex, KIND>, TypeCategory::Complex>
- &kz) {
- if (auto x{Get(kz.left())}) {
- return AsGenericExpr(AsSpecificExpr(
- Convert<Type<TypeCategory::Real, KIND>, TypeCategory::Real>{
- AsGenericExpr(std::move(*x))}));
- } else {
- return std::nullopt;
- }
- }
-
- template <int KIND>
- static std::optional<Expr<SomeReal>> Get(const ComplexConstructor<KIND> &kz) {
- return GET_IMAGINARY ? Get(kz.right()) : Get(kz.left());
- }
-
- template <int KIND>
- static std::optional<Expr<SomeReal>> Get(
- const Constant<Type<TypeCategory::Complex, KIND>> &kz) {
- if (auto cz{kz.GetScalarValue()}) {
- return AsGenericExpr(
- AsSpecificExpr(GET_IMAGINARY ? cz->AIMAG() : cz->REAL()));
- } else {
- return std::nullopt;
- }
- }
-
- template <int KIND>
- static std::optional<Expr<SomeReal>> Get(
- const Designator<Type<TypeCategory::Complex, KIND>> &kz) {
- if (const auto *symbolRef{std::get_if<SymbolRef>(&kz.u)}) {
- return AsGenericExpr(AsSpecificExpr(
- Designator<Type<TypeCategory::Complex, KIND>>{ComplexPart{
- DataRef{*symbolRef},
- GET_IMAGINARY ? ComplexPart::Part::IM : ComplexPart::Part::RE}}));
- } else {
- return std::nullopt;
- }
- }
-
- template <int KIND>
- static std::optional<Expr<SomeReal>> Get(
- const Expr<Type<TypeCategory::Complex, KIND>> &kz) {
- return Get(kz.u);
- }
-
- static std::optional<Expr<SomeReal>> Get(const Expr<SomeComplex> &z) {
- return Get(z.u);
- }
};
// Convert REAL to COMPLEX of the same kind. Preserving the real operand kind
@@ -391,8 +332,10 @@ Expr<SomeComplex> PromoteRealToComplex(Expr<SomeReal> &&someX) {
return common::visit(
[](auto &&x) {
using RT = ResultType<decltype(x)>;
- return AsCategoryExpr(ComplexConstructor<RT::kind>{
- std::move(x), AsExpr(Constant<RT>{Scalar<RT>{}})});
+ int rtKind{x.kind()};
+ return AsCategoryExpr(ComplexConstructor{std::move(x),
+ AsExpr(
+ Constant<RT>{rtKind, Scalar<RT>::Zero(rtKind), RT{rtKind}})});
},
std::move(someX.u));
}
@@ -409,9 +352,10 @@ std::optional<Expr<SomeType>> MixedComplexLeft(
// COMPLEX**INTEGER is a special case that doesn't convert the exponent.
return Package(common::visit(
[&](const auto &zxk) {
+ const int zxkKind{zxk.kind()};
using Ty = ResultType<decltype(zxk)>;
- return AsCategoryExpr(AsExpr(
- RealToIntPower<Ty>{common::Clone(zxk), common::Clone(iry)}));
+ return AsCategoryExpr(AsExpr(RealToIntPower<Ty>{
+ zxkKind, common::Clone(zxk), common::Clone(iry)}));
},
zx.u));
}
@@ -488,12 +432,12 @@ Expr<SomeComplex> PromoteMixedComplexReal(
static_assert(XCAT == TypeCategory::Real || YCAT == TypeCategory::Real);
return common::visit(
[&](const auto &kx, const auto &ky) {
- constexpr int maxKind{std::max(
- ResultType<decltype(kx)>::kind, ResultType<decltype(ky)>::kind)};
- using ZTy = Type<TypeCategory::Complex, maxKind>;
+ int maxKind{std::max(kx.kind(), ky.kind())};
+ using ZTy = Type<TypeCategory::Complex>;
+ auto cx{ConvertToType<ZTy>(maxKind, std::move(x))};
+ auto cy{ConvertToType<ZTy>(maxKind, std::move(y))};
return Expr<SomeComplex>{
- Expr<ZTy>{OPR<ZTy>{ConvertToType<ZTy>(std::move(x)),
- ConvertToType<ZTy>(std::move(y))}}};
+ Expr<ZTy>{OPR<ZTy>{maxKind, std::move(cx), std::move(cy)}}};
},
x.u, y.u);
}
@@ -527,9 +471,11 @@ std::optional<Expr<SomeType>> NumericOperation(
return Package(common::visit(
[&](auto &&ryk) -> Expr<SomeReal> {
using resultType = ResultType<decltype(ryk)>;
- return AsCategoryExpr(
- OPR<resultType>{ConvertToType<resultType>(std::move(ix)),
- std::move(ryk)});
+ const int resultKind{ryk.kind()};
+ auto converted{
+ ConvertToType<resultType>(resultKind, std::move(ix))};
+ return AsCategoryExpr(OPR<resultType>{
+ resultKind, std::move(converted), std::move(ryk)});
},
std::move(ry.u)));
},
@@ -543,8 +489,9 @@ std::optional<Expr<SomeType>> NumericOperation(
MixedComplexLeft<OPR>(messages, zx, iy, defaultRealKind)}) {
return result;
} else {
+ auto converted{ConvertTo(zx, std::move(iy))};
return Package(PromoteAndCombine<OPR, TypeCategory::Complex>(
- std::move(zx), ConvertTo(zx, std::move(iy))));
+ std::move(zx), std::move(converted)));
}
},
[&](Expr<SomeComplex> &&zx, Expr<SomeReal> &&ry) {
@@ -561,8 +508,9 @@ std::optional<Expr<SomeType>> NumericOperation(
messages, ix, zy, defaultRealKind)}) {
return result;
} else {
+ auto converted{ConvertTo(zy, std::move(ix))};
return Package(PromoteAndCombine<OPR, TypeCategory::Complex>(
- ConvertTo(zy, std::move(ix)), std::move(zy)));
+ std::move(converted), std::move(zy)));
}
},
[&](Expr<SomeReal> &&rx, Expr<SomeComplex> &&zy) {
@@ -576,31 +524,39 @@ std::optional<Expr<SomeType>> NumericOperation(
},
// Operations with one typeless operand
[&](BOZLiteralConstant &&bx, Expr<SomeInteger> &&iy) {
+ // iy aliases y, so sequence the conversion before std::move(y)
+ // (argument order is unspecified)
+ auto converted{ConvertTo(iy, std::move(bx))};
return NumericOperation<OPR>(messages,
- AsGenericExpr(ConvertTo(iy, std::move(bx))), std::move(y),
+ AsGenericExpr(std::move(converted)), std::move(y),
defaultRealKind);
},
[&](BOZLiteralConstant &&bx, Expr<SomeUnsigned> &&iy) {
+ auto converted{ConvertTo(iy, std::move(bx))};
return NumericOperation<OPR>(messages,
- AsGenericExpr(ConvertTo(iy, std::move(bx))), std::move(y),
+ AsGenericExpr(std::move(converted)), std::move(y),
defaultRealKind);
},
[&](BOZLiteralConstant &&bx, Expr<SomeReal> &&ry) {
+ auto converted{ConvertTo(ry, std::move(bx))};
return NumericOperation<OPR>(messages,
- AsGenericExpr(ConvertTo(ry, std::move(bx))), std::move(y),
+ AsGenericExpr(std::move(converted)), std::move(y),
defaultRealKind);
},
[&](Expr<SomeInteger> &&ix, BOZLiteralConstant &&by) {
+ auto cvt{ConvertTo(ix, std::move(by))};
return NumericOperation<OPR>(messages, std::move(x),
- AsGenericExpr(ConvertTo(ix, std::move(by))), defaultRealKind);
+ AsGenericExpr(std::move(cvt)), defaultRealKind);
},
[&](Expr<SomeUnsigned> &&ix, BOZLiteralConstant &&by) {
+ auto converted{ConvertTo(ix, std::move(by))};
return NumericOperation<OPR>(messages, std::move(x),
- AsGenericExpr(ConvertTo(ix, std::move(by))), defaultRealKind);
+ AsGenericExpr(std::move(converted)), defaultRealKind);
},
[&](Expr<SomeReal> &&rx, BOZLiteralConstant &&by) {
+ auto converted{ConvertTo(rx, std::move(by))};
return NumericOperation<OPR>(messages, std::move(x),
- AsGenericExpr(ConvertTo(rx, std::move(by))), defaultRealKind);
+ AsGenericExpr(std::move(converted)), defaultRealKind);
},
// Error cases
[&](Expr<SomeUnsigned> &&, auto &&) {
@@ -734,12 +690,16 @@ std::optional<Expr<LogicalResult>> Relate(parser::ContextualMessages &messages,
return PromoteAndRelate(opr, std::move(rx), std::move(ry));
},
[&](Expr<SomeReal> &&rx, Expr<SomeInteger> &&iy) {
+ // rx aliases x, so sequence the conversion before std::move(x)
+ // (argument order is unspecified)
+ auto converted{ConvertTo(rx, std::move(iy))};
return Relate(messages, opr, std::move(x),
- AsGenericExpr(ConvertTo(rx, std::move(iy))));
+ AsGenericExpr(std::move(converted)));
},
[&](Expr<SomeInteger> &&ix, Expr<SomeReal> &&ry) {
- return Relate(messages, opr,
- AsGenericExpr(ConvertTo(ry, std::move(ix))), std::move(y));
+ auto converted{ConvertTo(ry, std::move(ix))};
+ return Relate(messages, opr, AsGenericExpr(std::move(converted)),
+ std::move(y));
},
[&](Expr<SomeComplex> &&zx,
Expr<SomeComplex> &&zy) -> std::optional<Expr<LogicalResult>> {
@@ -753,20 +713,24 @@ std::optional<Expr<LogicalResult>> Relate(parser::ContextualMessages &messages,
}
},
[&](Expr<SomeComplex> &&zx, Expr<SomeInteger> &&iy) {
+ auto converted{ConvertTo(zx, std::move(iy))};
return Relate(messages, opr, std::move(x),
- AsGenericExpr(ConvertTo(zx, std::move(iy))));
+ AsGenericExpr(std::move(converted)));
},
[&](Expr<SomeComplex> &&zx, Expr<SomeReal> &&ry) {
+ auto converted{ConvertTo(zx, std::move(ry))};
return Relate(messages, opr, std::move(x),
- AsGenericExpr(ConvertTo(zx, std::move(ry))));
+ AsGenericExpr(std::move(converted)));
},
[&](Expr<SomeInteger> &&ix, Expr<SomeComplex> &&zy) {
- return Relate(messages, opr,
- AsGenericExpr(ConvertTo(zy, std::move(ix))), std::move(y));
+ auto converted{ConvertTo(zy, std::move(ix))};
+ return Relate(messages, opr, AsGenericExpr(std::move(converted)),
+ std::move(y));
},
[&](Expr<SomeReal> &&rx, Expr<SomeComplex> &&zy) {
- return Relate(messages, opr,
- AsGenericExpr(ConvertTo(zy, std::move(rx))), std::move(y));
+ auto converted{ConvertTo(zy, std::move(rx))};
+ return Relate(messages, opr, AsGenericExpr(std::move(converted)),
+ std::move(y));
},
[&](Expr<SomeCharacter> &&cx, Expr<SomeCharacter> &&cy) {
return common::visit(
@@ -806,7 +770,8 @@ std::optional<Expr<LogicalResult>> Relate(parser::ContextualMessages &messages,
// operand representing INT(enumExpr).
auto makeIntCall =
[&](Expr<SomeDerived> &&operand) -> Expr<SomeType> {
- using IntType = Type<TypeCategory::Integer, 4>;
+ using IntType = Type<TypeCategory::Integer>;
+ constexpr int intKind{4};
DynamicType enumType{*xDerived};
DynamicType intResultType{TypeCategory::Integer, 4};
characteristics::DummyDataObject ddo{
@@ -825,7 +790,7 @@ std::optional<Expr<LogicalResult>> Relate(parser::ContextualMessages &messages,
intArgs.emplace_back(AsGenericExpr(std::move(operand)));
return AsGenericExpr(
Expr<SomeInteger>(Expr<IntType>(FunctionRef<IntType>{
- ProcedureDesignator{std::move(intSpec)},
+ intKind, ProcedureDesignator{std::move(intSpec)},
std::move(intArgs)})));
};
return Relate(messages, opr, makeIntCall(std::move(dx)),
@@ -849,9 +814,8 @@ Expr<SomeLogical> BinaryLogicalOperation(
CHECK(opr != LogicalOperator::Not);
return common::visit(
[=](auto &&xy) {
- using Ty = ResultType<decltype(xy[0])>;
- return Expr<SomeLogical>{BinaryLogicalOperation<Ty::kind>(
- opr, std::move(xy[0]), std::move(xy[1]))};
+ return Expr<SomeLogical>{
+ BinaryLogicalOperation(opr, std::move(xy[0]), std::move(xy[1]))};
},
AsSameKindExprs(std::move(x), std::move(y)));
}
@@ -912,9 +876,9 @@ std::optional<Expr<SomeType>> ConvertToType(
converted = common::visit(
[&](auto &&x) {
using CharacterType = ResultType<decltype(x)>;
- return Expr<SomeCharacter>{
- Expr<CharacterType>{SetLength<CharacterType::kind>{
- std::move(x), std::move(*length)}}};
+ const int characterKind{x.kind()};
+ return Expr<SomeCharacter>{Expr<CharacterType>{
+ SetLength{characterKind, std::move(x), std::move(*length)}}};
},
std::move(converted.u));
}
@@ -1382,79 +1346,84 @@ bool HasVolatileOrAsynchronousSymbol(const Expr<SomeType> &expr) {
namespace {
-template <common::TypeCategory CAT, int KIND> using Numeric = Type<CAT, KIND>;
+template <common::TypeCategory CAT> using Numeric = Type<CAT>;
-template <common::TypeCategory CAT, int KIND>
-using NumericExpr = Expr<Numeric<CAT, KIND>>;
+template <common::TypeCategory CAT> using NumericExpr = Expr<Numeric<CAT>>;
-template <common::TypeCategory CAT, int KIND> struct SignedNumericTerm {
- NumericExpr<CAT, KIND> expr;
+template <common::TypeCategory CAT> struct SignedNumericTerm {
+ int kind() const { return expr.kind(); };
+
+ NumericExpr<CAT> expr;
bool isPositive;
};
-template <common::TypeCategory CAT, int KIND> struct SignedNumericExpr {
- NumericExpr<CAT, KIND> expr;
+template <common::TypeCategory CAT> struct SignedNumericExpr {
+ int kind() const { return expr.kind(); };
+
+ NumericExpr<CAT> expr;
bool isPositive;
};
-template <common::TypeCategory CAT, int KIND>
-static void flattenTopLevelAddSubtract(const NumericExpr<CAT, KIND> &expr,
- llvm::SmallVectorImpl<SignedNumericTerm<CAT, KIND>> &terms,
+template <common::TypeCategory CAT>
+static void flattenTopLevelAddSubtract(const NumericExpr<CAT> &expr,
+ llvm::SmallVectorImpl<SignedNumericTerm<CAT>> &terms,
bool isPositive = true) {
// Only flatten Add and Subtract nodes. Every other node, including
// Parentheses, is one opaque signed term whose tree is preserved.
- if (const auto *add = std::get_if<Add<Numeric<CAT, KIND>>>(&expr.u)) {
+ if (const auto *add = std::get_if<Add<Numeric<CAT>>>(&expr.u)) {
flattenTopLevelAddSubtract(add->left(), terms, isPositive);
flattenTopLevelAddSubtract(add->right(), terms, isPositive);
return;
}
- if (const auto *subtract =
- std::get_if<Subtract<Numeric<CAT, KIND>>>(&expr.u)) {
+ if (const auto *subtract = std::get_if<Subtract<Numeric<CAT>>>(&expr.u)) {
flattenTopLevelAddSubtract(subtract->left(), terms, isPositive);
flattenTopLevelAddSubtract(subtract->right(), terms, !isPositive);
return;
}
- terms.push_back(SignedNumericTerm<CAT, KIND>{expr, isPositive});
+ terms.push_back(SignedNumericTerm<CAT>{expr, isPositive});
}
-template <common::TypeCategory CAT, int KIND>
-static SignedNumericExpr<CAT, KIND> buildRightAssociatedSignedFold(
- llvm::MutableArrayRef<SignedNumericTerm<CAT, KIND>> terms) {
+template <common::TypeCategory CAT>
+static SignedNumericExpr<CAT> buildRightAssociatedSignedFold(
+ llvm::MutableArrayRef<SignedNumericTerm<CAT>> terms) {
assert(!terms.empty() && "cannot build empty signed fold");
+ const int kind{terms.front().kind()};
const bool isPositive{terms.front().isPositive};
- NumericExpr<CAT, KIND> result{std::move(terms.back().expr)};
+ NumericExpr<CAT> result{std::move(terms.back().expr)};
for (std::size_t i{terms.size() - 1}; i > 0; --i) {
- SignedNumericTerm<CAT, KIND> &term{terms[i - 1]};
+ SignedNumericTerm<CAT> &term{terms[i - 1]};
const bool useAdd{term.isPositive == terms[i].isPositive};
if (useAdd)
- result = NumericExpr<CAT, KIND>{
- Add<Numeric<CAT, KIND>>{std::move(term.expr), std::move(result)}};
+ result = NumericExpr<CAT>{
+ Add<Numeric<CAT>>{kind, std::move(term.expr), std::move(result)}};
else
- result = NumericExpr<CAT, KIND>{Subtract<Numeric<CAT, KIND>>{
- std::move(term.expr), std::move(result)}};
+ result = NumericExpr<CAT>{Subtract<Numeric<CAT>>{
+ kind, std::move(term.expr), std::move(result)}};
}
- return SignedNumericExpr<CAT, KIND>{std::move(result), isPositive};
+ return SignedNumericExpr<CAT>{std::move(result), isPositive};
}
-template <common::TypeCategory CAT, int KIND>
-static SignedNumericExpr<CAT, KIND> buildSignedAdd(
- SignedNumericExpr<CAT, KIND> left, SignedNumericExpr<CAT, KIND> right) {
+template <common::TypeCategory CAT>
+static SignedNumericExpr<CAT> buildSignedAdd(
+ SignedNumericExpr<CAT> left, SignedNumericExpr<CAT> right) {
+ CHECK(left.kind() == right.kind());
+ const int kind{left.kind()};
if (left.isPositive == right.isPositive) {
- return SignedNumericExpr<CAT, KIND>{
- NumericExpr<CAT, KIND>{Add<Numeric<CAT, KIND>>{
- std::move(left.expr), std::move(right.expr)}},
+ return SignedNumericExpr<CAT>{
+ NumericExpr<CAT>{Add<Numeric<CAT>>{
+ kind, std::move(left.expr), std::move(right.expr)}},
left.isPositive};
}
if (left.isPositive) {
- return SignedNumericExpr<CAT, KIND>{
- NumericExpr<CAT, KIND>{Subtract<Numeric<CAT, KIND>>{
- std::move(left.expr), std::move(right.expr)}},
+ return SignedNumericExpr<CAT>{
+ NumericExpr<CAT>{Subtract<Numeric<CAT>>{
+ kind, std::move(left.expr), std::move(right.expr)}},
true};
}
// Prefer Y-X to introducing a unary negation for -X+Y.
- return SignedNumericExpr<CAT, KIND>{
- NumericExpr<CAT, KIND>{Subtract<Numeric<CAT, KIND>>{
- std::move(right.expr), std::move(left.expr)}},
+ return SignedNumericExpr<CAT>{
+ NumericExpr<CAT>{Subtract<Numeric<CAT>>{
+ kind, std::move(right.expr), std::move(left.expr)}},
true};
}
@@ -1463,24 +1432,24 @@ static std::optional<Expr<SomeType>> tryBuildSplitSumExpressionTree(const T &) {
return std::nullopt;
}
-template <common::TypeCategory CAT, int KIND>
+template <common::TypeCategory CAT>
static std::optional<Expr<SomeType>> tryBuildSplitSumExpressionTree(
- const NumericExpr<CAT, KIND> &expr) {
- if (!std::get_if<Add<Numeric<CAT, KIND>>>(&expr.u) &&
- !std::get_if<Subtract<Numeric<CAT, KIND>>>(&expr.u))
+ const NumericExpr<CAT> &expr) {
+ if (!std::get_if<Add<Numeric<CAT>>>(&expr.u) &&
+ !std::get_if<Subtract<Numeric<CAT>>>(&expr.u))
return std::nullopt;
- llvm::SmallVector<SignedNumericTerm<CAT, KIND>, 8> terms;
+ llvm::SmallVector<SignedNumericTerm<CAT>, 8> terms;
flattenTopLevelAddSubtract(expr, terms);
if (terms.size() <= 2)
return std::nullopt;
- llvm::MutableArrayRef<SignedNumericTerm<CAT, KIND>> head{terms.data(), 2};
- llvm::MutableArrayRef<SignedNumericTerm<CAT, KIND>> tail{
+ llvm::MutableArrayRef<SignedNumericTerm<CAT>> head{terms.data(), 2};
+ llvm::MutableArrayRef<SignedNumericTerm<CAT>> tail{
terms.data() + 2, terms.size() - 2};
- SignedNumericExpr<CAT, KIND> headExpr = buildRightAssociatedSignedFold(head);
- SignedNumericExpr<CAT, KIND> tailExpr = buildRightAssociatedSignedFold(tail);
- SignedNumericExpr<CAT, KIND> result =
+ SignedNumericExpr<CAT> headExpr = buildRightAssociatedSignedFold(head);
+ SignedNumericExpr<CAT> tailExpr = buildRightAssociatedSignedFold(tail);
+ SignedNumericExpr<CAT> result =
buildSignedAdd(std::move(tailExpr), std::move(headExpr));
assert(result.isPositive &&
"the first flattened term and therefore the split sum are positive");
@@ -1771,11 +1740,10 @@ static std::optional<Expr<SomeType>> DataConstantConversionHelper(
return common::visit(
[](const auto &w) -> std::optional<Expr<SomeType>> {
using FromType = ResultType<decltype(w)>;
- static constexpr int kind{FromType::kind};
- if constexpr (IsValidKindOfIntrinsicType(TO, kind)) {
+ const int kind{w.kind()};
+ if (IsValidKindOfIntrinsicType(TO, kind)) {
if (const auto *fromConst{UnwrapExpr<Constant<FromType>>(w)}) {
- using FromWordType = typename FromType::Scalar;
- using LogicalType = value::Logical<FromWordType::bits>;
+ using LogicalType = value::LogicalValue;
using ElementType =
std::conditional_t<TO == TypeCategory::Logical, LogicalType,
typename LogicalType::Word>;
@@ -1786,13 +1754,13 @@ static std::optional<Expr<SomeType>> DataConstantConversionHelper(
fromConst->IncrementSubscripts(at)) {
auto elt{fromConst->At(at)};
if constexpr (TO == TypeCategory::Logical) {
- values.emplace_back(std::move(elt));
+ values.emplace_back(kind, elt);
} else {
values.emplace_back(elt.word());
}
}
- return {AsGenericExpr(AsExpr(Constant<Type<TO, kind>>{
- std::move(values), std::move(shape)}))};
+ return {AsGenericExpr(AsExpr(Constant<Type<TO>>{kind,
+ std::move(values), std::move(shape), Type<TO>{kind}}))};
}
}
return std::nullopt;
@@ -1851,17 +1819,27 @@ bool MayBePassedAsAbsentOptional(const Expr<SomeType> &expr) {
IsAllocatableOrPointerObject(expr);
}
+static std::optional<std::string> GetScalarConstantValueAsStdString(
+ const Expr<SomeType> &expr) {
+ if (std::optional<value::CharacterValue> chValue{
+ GetScalarConstantValue<Ascii>(expr)}) {
+ return chValue->AsStdString();
+ }
+ return std::nullopt;
+}
+
std::optional<Expr<SomeType>> HollerithToBOZ(FoldingContext &context,
const Expr<SomeType> &expr, const DynamicType &type) {
- if (std::optional<std::string> chValue{GetScalarConstantValue<Ascii>(expr)}) {
+ if (std::optional<std::string> chValue{
+ GetScalarConstantValueAsStdString(expr)}) {
// Pad on the right with spaces when short, truncate the right if long.
auto bytes{static_cast<std::size_t>(
ToInt64(type.MeasureSizeInBytes(context, false)).value())};
- BOZLiteralConstant bits{0};
+ BOZLiteralConstant bits{LargestRealKind, 0};
for (std::size_t j{0}; j < bytes; ++j) {
auto idx{isHostLittleEndian ? j : bytes - j - 1};
char ch{idx >= chValue->size() ? ' ' : chValue->at(idx)};
- BOZLiteralConstant chBOZ{static_cast<unsigned char>(ch)};
+ BOZLiteralConstant chBOZ{LargestRealKind, static_cast<unsigned char>(ch)};
bits = bits.IOR(chBOZ.SHIFTL(8 * j));
}
return ConvertToType(type, Expr<SomeType>{bits});
@@ -1874,10 +1852,9 @@ std::optional<Expr<SomeType>> HollerithToBOZ(FoldingContext &context,
// possibly wrapped with parentheses or MAX(0, ...).
// Works with any integer expression.
template <typename T> const Symbol *GetBoundSymbol(const Expr<T> &);
-template <int KIND>
-const Symbol *GetBoundSymbol(
- const Expr<Type<TypeCategory::Integer, KIND>> &expr) {
- using T = Type<TypeCategory::Integer, KIND>;
+const Symbol *GetBoundSymbol(const Expr<Type<TypeCategory::Integer>> &expr) {
+ using T = Type<TypeCategory::Integer>;
+ const int kind{expr.kind()};
return common::visit(
common::visitors{
[](const Extremum<T> &max) -> const Symbol * {
@@ -1895,12 +1872,11 @@ const Symbol *GetBoundSymbol(
}
return nullptr;
},
- [](const Convert<T, TypeCategory::Integer> &x) {
+ [kind](const Convert<T, TypeCategory::Integer> &x) {
return common::visit(
- [](const auto &y) -> const Symbol * {
- using yType = std::decay_t<decltype(y)>;
- using yResult = typename yType::Result;
- if constexpr (yResult::kind <= KIND) {
+ [kind](const auto &y) -> const Symbol * {
+ int yKind{y.GetType() ? y.GetType()->kind() : 0};
+ if (yKind <= kind) {
return GetBoundSymbol(y);
} else {
return nullptr;
@@ -2007,8 +1983,7 @@ struct ArgumentExtractor
using Base::operator();
- template <int Kind>
- Result operator()(const Constant<Type<Logical, Kind>> &x) const {
+ Result operator()(const Constant<Type<Logical>> &x) const {
if (const auto &val{x.GetScalarValue()}) {
return val->IsTrue()
? std::make_pair(operation::Operator::True, Arguments{})
@@ -2268,24 +2243,18 @@ struct ConvertCollector
}
template <typename T> struct is_convert {
- static constexpr bool value{false};
+ // ComplexComponent is a conversion from complex to real.
+ static constexpr bool value{std::is_same_v<T, ComplexComponent>};
};
template <typename T, common::TypeCategory C>
struct is_convert<Convert<T, C>> {
static constexpr bool value{true};
};
- template <int K> struct is_convert<ComplexComponent<K>> {
- // Conversion from complex to real.
- static constexpr bool value{true};
- };
template <typename T>
static constexpr bool is_convert_v{is_convert<T>::value};
template <typename T> struct is_complex_constructor {
- static constexpr bool value{false};
- };
- template <int K> struct is_complex_constructor<ComplexConstructor<K>> {
- static constexpr bool value{true};
+ static constexpr bool value{std::is_same_v<T, ComplexConstructor>};
};
template <typename T>
static constexpr bool is_complex_constructor_v{
diff --git a/flang/lib/Evaluate/type.cpp b/flang/lib/Evaluate/type.cpp
index 3913bd394fde0..b9ed5305ad75c 100644
--- a/flang/lib/Evaluate/type.cpp
+++ b/flang/lib/Evaluate/type.cpp
@@ -142,10 +142,11 @@ bool DynamicType::operator==(const DynamicType &that) const {
std::optional<Expr<SubscriptInteger>> DynamicType::GetCharLength() const {
if (category_ == TypeCategory::Character) {
if (knownLength()) {
- return AsExpr(Constant<SubscriptInteger>(*knownLength()));
+ return MakeSubscriptIntExpr(*knownLength());
} else if (charLengthParamValue_) {
if (auto length{charLengthParamValue_->GetExplicit()}) {
- return ConvertToType<SubscriptInteger>(std::move(*length));
+ return ConvertToType<SubscriptInteger>(
+ SubscriptIntegerKind, std::move(*length));
}
}
}
@@ -194,15 +195,14 @@ std::optional<Expr<SubscriptInteger>> DynamicType::MeasureSizeInBytes(
case TypeCategory::Real:
case TypeCategory::Complex:
case TypeCategory::Logical:
- return Expr<SubscriptInteger>{
- context.targetCharacteristics().GetByteSize(category_, kind())};
+ return MakeSubscriptIntExpr(
+ context.targetCharacteristics().GetByteSize(category_, kind()));
case TypeCategory::Character:
- if (auto len{charLength ? Expr<SubscriptInteger>{Constant<SubscriptInteger>{
- *charLength}}
- : GetCharLength()}) {
+ if (auto len{
+ charLength ? MakeSubscriptIntExpr(*charLength) : GetCharLength()}) {
return Fold(context,
- Expr<SubscriptInteger>{
- context.targetCharacteristics().GetByteSize(category_, kind())} *
+ MakeSubscriptIntExpr(
+ context.targetCharacteristics().GetByteSize(category_, kind())) *
std::move(*len));
}
break;
@@ -218,16 +218,14 @@ std::optional<Expr<SubscriptInteger>> DynamicType::MeasureSizeInBytes(
auto size{derived_->GetScope()->size()};
auto align{aligned ? derived_->GetScope()->alignment().value_or(0) : 0};
auto alignedSize{align > 0 ? ((size + align - 1) / align) * align : size};
- return Expr<SubscriptInteger>{
- static_cast<ConstantSubscript>(alignedSize)};
+ return MakeSubscriptIntExpr(alignedSize);
}
// Regular derived type path.
if (!IsPolymorphic() && derived_ && derived_->scope()) {
auto size{derived_->scope()->size()};
auto align{aligned ? derived_->scope()->alignment().value_or(0) : 0};
auto alignedSize{align > 0 ? ((size + align - 1) / align) * align : size};
- return Expr<SubscriptInteger>{
- static_cast<ConstantSubscript>(alignedSize)};
+ return MakeSubscriptIntExpr(alignedSize);
}
break;
}
@@ -890,7 +888,7 @@ std::optional<DynamicType> ComparisonType(
case TypeCategory::Logical:
switch (t2.category()) {
case TypeCategory::Logical:
- return DynamicType{TypeCategory::Logical, LogicalResult::kind};
+ return DynamicType{TypeCategory::Logical, LogicalResultKind};
default:
return std::nullopt;
}
diff --git a/flang/lib/Evaluate/variable.cpp b/flang/lib/Evaluate/variable.cpp
index 409fd66f81c2b..7ea509efdd5ca 100644
--- a/flang/lib/Evaluate/variable.cpp
+++ b/flang/lib/Evaluate/variable.cpp
@@ -22,12 +22,12 @@ namespace Fortran::evaluate {
// Constructors, accessors, mutators
-Triplet::Triplet() : stride_{Expr<SubscriptInteger>{1}} {}
+Triplet::Triplet() : stride_{MakeSubscriptIntExpr(1)} {}
Triplet::Triplet(std::optional<Expr<SubscriptInteger>> &&l,
std::optional<Expr<SubscriptInteger>> &&u,
std::optional<Expr<SubscriptInteger>> &&s)
- : stride_{s ? std::move(*s) : Expr<SubscriptInteger>{1}} {
+ : stride_{s ? std::move(*s) : MakeSubscriptIntExpr(1)} {
if (l) {
lower_.emplace(std::move(*l));
}
@@ -133,7 +133,7 @@ Expr<SubscriptInteger> Substring::lower() const {
if (lower_) {
return lower_.value().value();
} else {
- return AsExpr(Constant<SubscriptInteger>{1});
+ return MakeSubscriptIntExpr(1);
}
}
@@ -151,7 +151,7 @@ std::optional<Expr<SubscriptInteger>> Substring::upper() const {
[](const DataRef &dataRef) { return dataRef.LEN(); },
[](const StaticDataObject::Pointer &object)
-> std::optional<Expr<SubscriptInteger>> {
- return AsExpr(Constant<SubscriptInteger>{object->data().size()});
+ return MakeSubscriptIntExpr(object->data().size());
},
},
parent_);
@@ -176,7 +176,7 @@ std::optional<Expr<SomeCharacter>> Substring::Fold(FoldingContext &context) {
return std::nullopt;
}
if (!lower_) {
- lower_ = AsExpr(Constant<SubscriptInteger>{1});
+ lower_ = MakeSubscriptIntExpr(1);
}
lower_.value() = evaluate::Fold(context, std::move(lower_.value().value()));
std::optional<ConstantSubscript> lbi{ToInt64(lower_.value().value())};
@@ -186,16 +186,15 @@ std::optional<Expr<SomeCharacter>> Substring::Fold(FoldingContext &context) {
if (*lbi > *ubi) { // empty result; canonicalize
*lbi = 1;
*ubi = 0;
- lower_ = AsExpr(Constant<SubscriptInteger>{*lbi});
- upper_ = AsExpr(Constant<SubscriptInteger>{*ubi});
+ lower_ = MakeSubscriptIntExpr(*lbi);
+ upper_ = MakeSubscriptIntExpr(*ubi);
}
std::optional<ConstantSubscript> length;
std::optional<Expr<SomeCharacter>> strings; // a Constant<Character>
if (const auto *literal{std::get_if<StaticDataObject::Pointer>(&parent_)}) {
length = (*literal)->data().size();
if (auto str{(*literal)->AsString()}) {
- strings =
- Expr<SomeCharacter>(Expr<Ascii>(Constant<Ascii>{std::move(*str)}));
+ strings = Expr<SomeCharacter>(MakeAsciiExpr(*str));
}
} else if (const auto *dataRef{std::get_if<DataRef>(&parent_)}) {
if (auto expr{AsGenericExpr(DataRef{*dataRef})}) {
@@ -227,7 +226,7 @@ std::optional<Expr<SomeCharacter>> Substring::Fold(FoldingContext &context) {
"Lower bound (%jd) on substring is less than one"_warn_en_US,
static_cast<std::intmax_t>(*lbi));
*lbi = 1;
- lower_ = AsExpr(Constant<SubscriptInteger>{1});
+ lower_ = MakeSubscriptIntExpr(1);
}
if (length && *ubi > *length) {
context.Warn(common::UsageWarning::Bounds,
@@ -235,7 +234,7 @@ std::optional<Expr<SomeCharacter>> Substring::Fold(FoldingContext &context) {
static_cast<std::intmax_t>(*ubi),
static_cast<std::intmax_t>(*length));
*ubi = *length;
- upper_ = AsExpr(Constant<SubscriptInteger>{*ubi});
+ upper_ = MakeSubscriptIntExpr(*ubi);
}
}
return result;
@@ -283,11 +282,11 @@ static std::optional<Expr<SubscriptInteger>> SymbolLEN(const Symbol &symbol) {
}
if (len) {
if (auto constLen{ToInt64(*len)}) {
- return Expr<SubscriptInteger>{std::max<std::int64_t>(*constLen, 0)};
+ return MakeSubscriptIntExpr(std::max<std::int64_t>(*constLen, 0));
} else if (ultimate.owner().IsDerivedType() ||
IsScopeInvariantExpr(*len)) {
return AsExpr(Extremum<SubscriptInteger>{
- Ordering::Greater, Expr<SubscriptInteger>{0}, std::move(*len)});
+ Ordering::Greater, MakeSubscriptIntExpr(0), std::move(*len)});
}
}
}
@@ -304,7 +303,7 @@ std::optional<Expr<SubscriptInteger>> BaseObject::LEN() const {
[](const Symbol &symbol) { return SymbolLEN(symbol); },
[](const StaticDataObject::Pointer &object)
-> std::optional<Expr<SubscriptInteger>> {
- return AsExpr(Constant<SubscriptInteger>{object->data().size()});
+ return MakeSubscriptIntExpr(object->data().size());
},
},
u);
@@ -336,9 +335,9 @@ std::optional<Expr<SubscriptInteger>> DataRef::LEN() const {
std::optional<Expr<SubscriptInteger>> Substring::LEN() const {
if (auto top{upper()}) {
- return AsExpr(Extremum<SubscriptInteger>{Ordering::Greater,
- AsExpr(Constant<SubscriptInteger>{0}),
- *std::move(top) - lower() + AsExpr(Constant<SubscriptInteger>{1})});
+ return AsExpr(
+ Extremum<SubscriptInteger>{Ordering::Greater, MakeSubscriptIntExpr(0),
+ *std::move(top) - lower() + MakeSubscriptIntExpr(1)});
} else {
return std::nullopt;
}
@@ -648,16 +647,16 @@ template <typename T> const Symbol *Designator<T>::GetLastSymbol() const {
template <typename T>
std::optional<DynamicType> Designator<T>::GetType() const {
if constexpr (IsLengthlessIntrinsicType<Result>) {
- return Result::GetType();
+ return DynamicType{Result::category, kind()};
}
if constexpr (Result::category == TypeCategory::Character) {
if (std::holds_alternative<Substring>(u)) {
if (auto len{LEN()}) {
if (auto n{ToInt64(*len)}) {
- return DynamicType{T::kind, *n};
+ return DynamicType{kind(), *n};
}
}
- return DynamicType{TypeCategory::Character, T::kind};
+ return DynamicType{TypeCategory::Character, kind()};
}
}
if (const Symbol * symbol{GetLastSymbol()}) {
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index a8e3e4a0aea1a..a8dddffdbb3a3 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -6721,8 +6721,7 @@ class FirConverter : public Fortran::lower::AbstractConverter {
return Fortran::common::visit(
[&](const auto &someKind) -> std::string {
using T = std::decay_t<decltype(someKind)>;
- using TK = Fortran::evaluate::Type<T::Result::category,
- T::Result::kind>;
+ using TK = typename T::Result;
if (const auto *constant =
std::get_if<Fortran::evaluate::Constant<TK>>(
&someKind.u)) {
diff --git a/flang/lib/Lower/CallInterface.cpp b/flang/lib/Lower/CallInterface.cpp
index 64960b5e09a98..4d1f8c29dc9f7 100644
--- a/flang/lib/Lower/CallInterface.cpp
+++ b/flang/lib/Lower/CallInterface.cpp
@@ -8,6 +8,7 @@
#include "flang/Lower/CallInterface.h"
#include "flang/Evaluate/fold.h"
+#include "flang/Evaluate/shape.h"
#include "flang/Lower/Bridge.h"
#include "flang/Lower/ConvertCall.h"
#include "flang/Lower/Mangler.h"
@@ -426,12 +427,12 @@ static Fortran::evaluate::ExtentExpr
getExtentExpr(const Fortran::semantics::ShapeSpec &shapeSpec) {
if (shapeSpec.ubound().isStar())
// F'2023 18.5.3 point 5.
- return Fortran::evaluate::ExtentExpr{-1};
+ return Fortran::evaluate::MakeExtentExpr(-1);
const auto &ubound = shapeSpec.ubound().GetExplicit();
const auto &lbound = shapeSpec.lbound().GetExplicit();
assert(lbound && ubound && "shape must be explicit");
return Fortran::common::Clone(*ubound) - Fortran::common::Clone(*lbound) +
- Fortran::evaluate::ExtentExpr{1};
+ Fortran::evaluate::MakeExtentExpr(1);
}
static void
diff --git a/flang/lib/Lower/ConvertArrayConstructor.cpp b/flang/lib/Lower/ConvertArrayConstructor.cpp
index 1d22da615ba07..a76527b4e7454 100644
--- a/flang/lib/Lower/ConvertArrayConstructor.cpp
+++ b/flang/lib/Lower/ConvertArrayConstructor.cpp
@@ -503,15 +503,16 @@ namespace {
/// evaluating an ac-value.
template <typename T>
struct LengthAndTypeCollector {
- static mlir::Type collect(mlir::Location,
- Fortran::lower::AbstractConverter &converter,
- const Fortran::evaluate::ArrayConstructor<T> &,
- Fortran::lower::SymMap &,
- Fortran::lower::StatementContext &,
- mlir::SmallVectorImpl<mlir::Value> &) {
+ static mlir::Type
+ collect(mlir::Location, Fortran::lower::AbstractConverter &converter,
+ const Fortran::evaluate::ArrayConstructor<T> &arrayCtorExpr,
+ Fortran::lower::SymMap &, Fortran::lower::StatementContext &,
+ mlir::SmallVectorImpl<mlir::Value> &) {
+ const int kind{arrayCtorExpr.kind()};
// Numerical and Logical types.
return Fortran::lower::getFIRType(&converter.getMLIRContext(), T::category,
- T::kind, /*lenParams*/ {});
+ kind,
+ /*lenParams*/ {});
}
};
@@ -530,16 +531,17 @@ struct LengthAndTypeCollector<Fortran::evaluate::SomeDerived> {
}
};
-template <int Kind>
using Character =
- Fortran::evaluate::Type<Fortran::common::TypeCategory::Character, Kind>;
-template <int Kind>
-struct LengthAndTypeCollector<Character<Kind>> {
- static mlir::Type collect(
- mlir::Location loc, Fortran::lower::AbstractConverter &converter,
- const Fortran::evaluate::ArrayConstructor<Character<Kind>> &arrayCtorExpr,
- Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx,
- mlir::SmallVectorImpl<mlir::Value> &lengths) {
+ Fortran::evaluate::Type<Fortran::common::TypeCategory::Character>;
+template <>
+struct LengthAndTypeCollector<Character> {
+ static mlir::Type
+ collect(mlir::Location loc, Fortran::lower::AbstractConverter &converter,
+ const Fortran::evaluate::ArrayConstructor<Character> &arrayCtorExpr,
+ Fortran::lower::SymMap &symMap,
+ Fortran::lower::StatementContext &stmtCtx,
+ mlir::SmallVectorImpl<mlir::Value> &lengths) {
+ const int kind{arrayCtorExpr.kind()};
llvm::SmallVector<Fortran::lower::LenParameterTy> typeLengths;
if (const Fortran::evaluate::ExtentExpr *lenExpr = arrayCtorExpr.LEN()) {
lengths.push_back(
@@ -550,7 +552,7 @@ struct LengthAndTypeCollector<Character<Kind>> {
}
return Fortran::lower::getFIRType(&converter.getMLIRContext(),
Fortran::common::TypeCategory::Character,
- Kind, typeLengths);
+ kind, typeLengths);
}
};
} // namespace
diff --git a/flang/lib/Lower/ConvertConstant.cpp b/flang/lib/Lower/ConvertConstant.cpp
index 70dc4c77ab869..31f706803de3e 100644
--- a/flang/lib/Lower/ConvertConstant.cpp
+++ b/flang/lib/Lower/ConvertConstant.cpp
@@ -49,18 +49,19 @@ static llvm::APFloat consAPFloat(const llvm::fltSemantics &fsem,
//===----------------------------------------------------------------------===//
/// Generate an mlir attribute from a literal value
-template <Fortran::common::TypeCategory TC, int KIND>
+template <Fortran::common::TypeCategory TC>
static mlir::Attribute convertToAttribute(
fir::FirOpBuilder &builder,
- const Fortran::evaluate::Scalar<Fortran::evaluate::Type<TC, KIND>> &value,
+ const Fortran::evaluate::Scalar<Fortran::evaluate::Type<TC>> &value,
mlir::Type type) {
+ const int kind{value.kind()};
if constexpr (TC == Fortran::common::TypeCategory::Integer) {
- if constexpr (KIND <= 8)
+ if (kind <= 8)
return builder.getIntegerAttr(type, value.ToInt64());
else {
- static_assert(KIND <= 16, "integers with KIND > 16 are not supported");
+ CHECK_MSG(kind <= 16, "integers with KIND > 16 are not supported");
return builder.getIntegerAttr(
- type, llvm::APInt(KIND * 8,
+ type, llvm::APInt(kind * 8,
{value.ToUInt64(), value.SHIFTR(64).ToUInt64()}));
}
} else if constexpr (TC == Fortran::common::TypeCategory::Logical) {
@@ -72,7 +73,7 @@ static mlir::Attribute convertToAttribute(
auto getFloatAttr = [&](const auto &value, mlir::Type type) {
std::string str = value.DumpHexadecimal();
auto floatVal =
- consAPFloat(builder.getKindMap().getFloatSemantics(KIND), str);
+ consAPFloat(builder.getKindMap().getFloatSemantics(kind), str);
return builder.getFloatAttr(type, floatVal);
};
@@ -127,12 +128,11 @@ class DenseGlobalBuilder {
setDefaultAlignment);
}
- template <Fortran::common::TypeCategory TC, int KIND>
+ template <Fortran::common::TypeCategory TC>
static fir::GlobalOp tryCreating(
fir::FirOpBuilder &builder, mlir::Location loc, mlir::Type symTy,
llvm::StringRef globalName, mlir::StringAttr linkage, bool isConst,
- const Fortran::evaluate::Constant<Fortran::evaluate::Type<TC, KIND>>
- &constant,
+ const Fortran::evaluate::Constant<Fortran::evaluate::Type<TC>> &constant,
cuf::DataAttributeAttr dataAttr, bool setDefaultAlignment = true) {
DenseGlobalBuilder globalBuilder;
globalBuilder.tryConvertingToAttributes(builder, constant);
@@ -145,21 +145,21 @@ class DenseGlobalBuilder {
DenseGlobalBuilder() = default;
/// Try converting an evaluate::Constant to a list of MLIR attributes.
- template <Fortran::common::TypeCategory TC, int KIND>
+ template <Fortran::common::TypeCategory TC>
void tryConvertingToAttributes(
fir::FirOpBuilder &builder,
- const Fortran::evaluate::Constant<Fortran::evaluate::Type<TC, KIND>>
+ const Fortran::evaluate::Constant<Fortran::evaluate::Type<TC>>
&constant) {
- using Element =
- Fortran::evaluate::Scalar<Fortran::evaluate::Type<TC, KIND>>;
+ using Element = Fortran::evaluate::Scalar<Fortran::evaluate::Type<TC>>;
static_assert(TC != Fortran::common::TypeCategory::Character,
"must be numerical or logical");
+ const int kind = constant.kind();
auto attrTc = TC == Fortran::common::TypeCategory::Logical
? Fortran::common::TypeCategory::Integer
: TC;
attributeElementType =
- Fortran::lower::getFIRType(builder.getContext(), attrTc, KIND, {});
+ Fortran::lower::getFIRType(builder.getContext(), attrTc, kind, {});
const std::vector<Element> &values = constant.values();
auto sameElements = [&]() -> bool {
@@ -171,15 +171,15 @@ class DenseGlobalBuilder {
};
if (sameElements()) {
- auto attr = convertToAttribute<TC, KIND>(builder, values.front(),
- attributeElementType);
+ auto attr =
+ convertToAttribute<TC>(builder, values.front(), attributeElementType);
attributes.assign(values.size(), attr);
return;
}
for (auto element : values)
attributes.push_back(
- convertToAttribute<TC, KIND>(builder, element, attributeElementType));
+ convertToAttribute<TC>(builder, element, attributeElementType));
}
/// Try converting an evaluate::Expr to a list of MLIR attributes.
@@ -191,8 +191,7 @@ class DenseGlobalBuilder {
using TR = Fortran::evaluate::ResultType<decltype(x)>;
if (const auto *constant =
std::get_if<Fortran::evaluate::Constant<TR>>(&x.u))
- tryConvertingToAttributes<TR::category, TR::kind>(builder,
- *constant);
+ tryConvertingToAttributes<TR::category>(builder, *constant);
},
expr.u);
}
@@ -242,25 +241,25 @@ fir::GlobalOp Fortran::lower::tryCreatingDenseGlobal(
//===----------------------------------------------------------------------===//
/// Generate a real constant with a value `value`.
-template <int KIND>
-static mlir::Value genRealConstant(fir::FirOpBuilder &builder,
+static mlir::Value genRealConstant(int kind, fir::FirOpBuilder &builder,
mlir::Location loc,
const llvm::APFloat &value) {
- mlir::Type fltTy = Fortran::lower::convertReal(builder.getContext(), KIND);
+ mlir::Type fltTy = Fortran::lower::convertReal(builder.getContext(), kind);
return builder.createRealConstant(loc, fltTy, value);
}
/// Convert a scalar literal constant to IR.
-template <Fortran::common::TypeCategory TC, int KIND>
+template <Fortran::common::TypeCategory TC>
static mlir::Value genScalarLit(
fir::FirOpBuilder &builder, mlir::Location loc,
- const Fortran::evaluate::Scalar<Fortran::evaluate::Type<TC, KIND>> &value) {
+ const Fortran::evaluate::Scalar<Fortran::evaluate::Type<TC>> &value) {
+ int kind = value.kind();
if constexpr (TC == Fortran::common::TypeCategory::Integer ||
TC == Fortran::common::TypeCategory::Unsigned) {
// MLIR requires constants to be signless
mlir::Type ty = Fortran::lower::getFIRType(
- builder.getContext(), Fortran::common::TypeCategory::Integer, KIND, {});
- if (KIND == 16) {
+ builder.getContext(), Fortran::common::TypeCategory::Integer, kind, {});
+ if (kind == 16) {
auto bigInt = llvm::APInt(ty.getIntOrFloatBitWidth(),
TC == Fortran::common::TypeCategory::Unsigned
? value.UnsignedDecimal()
@@ -274,38 +273,45 @@ static mlir::Value genScalarLit(
if (value.IsCanonical())
return builder.createBool(loc, value.IsTrue());
mlir::Type logicalType = Fortran::lower::getFIRType(
- builder.getContext(), Fortran::common::TypeCategory::Logical, KIND, {});
+ builder.getContext(), Fortran::common::TypeCategory::Logical, kind, {});
mlir::Type intType = Fortran::lower::getFIRType(
- builder.getContext(), Fortran::common::TypeCategory::Integer, KIND, {});
+ builder.getContext(), Fortran::common::TypeCategory::Integer, kind, {});
mlir::Value integer =
builder.createIntegerConstant(loc, intType, value.word().ToInt64());
return fir::BitcastOp::create(builder, loc, logicalType, integer);
} else if constexpr (TC == Fortran::common::TypeCategory::Real) {
std::string str = value.DumpHexadecimal();
- if constexpr (KIND == 2) {
+ switch (kind) {
+ case 2: {
auto floatVal = consAPFloat(llvm::APFloatBase::IEEEhalf(), str);
- return genRealConstant<KIND>(builder, loc, floatVal);
- } else if constexpr (KIND == 3) {
+ return genRealConstant(kind, builder, loc, floatVal);
+ }
+ case 3: {
auto floatVal = consAPFloat(llvm::APFloatBase::BFloat(), str);
- return genRealConstant<KIND>(builder, loc, floatVal);
- } else if constexpr (KIND == 4) {
+ return genRealConstant(kind, builder, loc, floatVal);
+ }
+ case 4: {
auto floatVal = consAPFloat(llvm::APFloatBase::IEEEsingle(), str);
- return genRealConstant<KIND>(builder, loc, floatVal);
- } else if constexpr (KIND == 10) {
+ return genRealConstant(kind, builder, loc, floatVal);
+ }
+ case 10: {
auto floatVal = consAPFloat(llvm::APFloatBase::x87DoubleExtended(), str);
- return genRealConstant<KIND>(builder, loc, floatVal);
- } else if constexpr (KIND == 16) {
+ return genRealConstant(kind, builder, loc, floatVal);
+ }
+ case 16: {
auto floatVal = consAPFloat(llvm::APFloatBase::IEEEquad(), str);
- return genRealConstant<KIND>(builder, loc, floatVal);
- } else {
+ return genRealConstant(kind, builder, loc, floatVal);
+ }
+ default: {
// convert everything else to double
auto floatVal = consAPFloat(llvm::APFloatBase::IEEEdouble(), str);
- return genRealConstant<KIND>(builder, loc, floatVal);
+ return genRealConstant(kind, builder, loc, floatVal);
+ }
}
} else if constexpr (TC == Fortran::common::TypeCategory::Complex) {
- mlir::Value real = genScalarLit<Fortran::common::TypeCategory::Real, KIND>(
+ mlir::Value real = genScalarLit<Fortran::common::TypeCategory::Real>(
builder, loc, value.REAL());
- mlir::Value imag = genScalarLit<Fortran::common::TypeCategory::Real, KIND>(
+ mlir::Value imag = genScalarLit<Fortran::common::TypeCategory::Real>(
builder, loc, value.AIMAG());
return fir::factory::Complex{builder, loc}.createComplex(real, imag);
} else /*constexpr*/ {
@@ -314,70 +320,72 @@ static mlir::Value genScalarLit(
}
/// Create fir::string_lit from a scalar character constant.
-template <int KIND>
static fir::StringLitOp
createStringLitOp(fir::FirOpBuilder &builder, mlir::Location loc,
const Fortran::evaluate::Scalar<Fortran::evaluate::Type<
- Fortran::common::TypeCategory::Character, KIND>> &value,
+ Fortran::common::TypeCategory::Character>> &value,
[[maybe_unused]] int64_t len) {
- if constexpr (KIND == 1) {
+ int kind = value.kind();
+ if (kind == 1) {
assert(value.size() == static_cast<std::uint64_t>(len));
- return builder.createStringLitOp(loc, value);
+ return builder.createStringLitOp(loc, *value.AsStringRef());
} else {
- using ET = typename std::decay_t<decltype(value)>::value_type;
- fir::CharacterType type =
- fir::CharacterType::get(builder.getContext(), KIND, len);
- mlir::MLIRContext *context = builder.getContext();
- std::int64_t size = static_cast<std::int64_t>(value.size());
- mlir::ShapedType shape = mlir::RankedTensorType::get(
- llvm::ArrayRef<std::int64_t>{size},
- mlir::IntegerType::get(builder.getContext(), sizeof(ET) * 8));
- auto denseAttr = mlir::DenseElementsAttr::get(
- shape, llvm::ArrayRef<ET>{value.data(), value.size()});
- auto denseTag = mlir::StringAttr::get(context, fir::StringLitOp::xlist());
- mlir::NamedAttribute dataAttr(denseTag, denseAttr);
- auto sizeTag = mlir::StringAttr::get(context, fir::StringLitOp::size());
- mlir::NamedAttribute sizeAttr(sizeTag, builder.getI64IntegerAttr(len));
- llvm::SmallVector<mlir::NamedAttribute> attrs = {dataAttr, sizeAttr};
- return fir::StringLitOp::create(builder, loc,
- llvm::ArrayRef<mlir::Type>{type},
- mlir::ValueRange{}, attrs);
+ return value.withStdString([&](const auto &value) -> fir::StringLitOp {
+ using ET = typename std::decay_t<decltype(value)>::value_type;
+ fir::CharacterType type =
+ fir::CharacterType::get(builder.getContext(), kind, len);
+ mlir::MLIRContext *context = builder.getContext();
+ std::int64_t size = static_cast<std::int64_t>(value.size());
+ mlir::ShapedType shape = mlir::RankedTensorType::get(
+ llvm::ArrayRef<std::int64_t>{size},
+ mlir::IntegerType::get(builder.getContext(), sizeof(ET) * 8));
+ auto denseAttr = mlir::DenseElementsAttr::get(
+ shape, llvm::ArrayRef<ET>{value.data(), value.size()});
+ auto denseTag = mlir::StringAttr::get(context, fir::StringLitOp::xlist());
+ mlir::NamedAttribute dataAttr(denseTag, denseAttr);
+ auto sizeTag = mlir::StringAttr::get(context, fir::StringLitOp::size());
+ mlir::NamedAttribute sizeAttr(sizeTag, builder.getI64IntegerAttr(len));
+ llvm::SmallVector<mlir::NamedAttribute> attrs = {dataAttr, sizeAttr};
+ return fir::StringLitOp::create(builder, loc,
+ llvm::ArrayRef<mlir::Type>{type},
+ mlir::ValueRange{}, attrs);
+ });
}
}
/// Convert a scalar literal CHARACTER to IR.
-template <int KIND>
static mlir::Value
genScalarLit(fir::FirOpBuilder &builder, mlir::Location loc,
const Fortran::evaluate::Scalar<Fortran::evaluate::Type<
- Fortran::common::TypeCategory::Character, KIND>> &value,
+ Fortran::common::TypeCategory::Character>> &value,
int64_t len, bool outlineInReadOnlyMemory) {
+ int kind = value.kind();
// When in an initializer context, construct the literal op itself and do
// not construct another constant object in rodata.
if (!outlineInReadOnlyMemory)
- return createStringLitOp<KIND>(builder, loc, value, len);
+ return createStringLitOp(builder, loc, value, len);
// Otherwise, the string is in a plain old expression so "outline" the value
// in read only data by hash consing it to a constant literal object.
// ASCII global constants are created using an mlir string attribute.
- if constexpr (KIND == 1) {
- return fir::getBase(fir::factory::createStringLiteral(builder, loc, value));
+ if (kind == 1) {
+ return fir::getBase(
+ fir::factory::createStringLiteral(builder, loc, *value.AsStringRef()));
}
- auto size = builder.getKindMap().getCharacterBitsize(KIND) / 8 * value.size();
- llvm::StringRef strVal(reinterpret_cast<const char *>(value.c_str()), size);
+ auto size = builder.getKindMap().getCharacterBitsize(kind) / 8 * value.size();
+ llvm::StringRef strVal(reinterpret_cast<const char *>(value.data()), size);
std::string globalName = fir::factory::uniqueCGIdent(
- KIND == 1 ? "cl"s : "cl"s + std::to_string(KIND), strVal);
+ kind == 1 ? "cl"s : "cl"s + std::to_string(kind), strVal);
fir::GlobalOp global = builder.getNamedGlobal(globalName);
fir::CharacterType type =
- fir::CharacterType::get(builder.getContext(), KIND, len);
+ fir::CharacterType::get(builder.getContext(), kind, len);
if (!global)
global = builder.createGlobalConstant(
loc, type, globalName,
[&](fir::FirOpBuilder &builder) {
- fir::StringLitOp str =
- createStringLitOp<KIND>(builder, loc, value, len);
+ fir::StringLitOp str = createStringLitOp(builder, loc, value, len);
fir::HasValueOp::create(builder, loc, str);
},
builder.createLinkOnceLinkage());
@@ -630,8 +638,8 @@ genInlinedArrayLit(Fortran::lower::AbstractConverter &converter,
if constexpr (T::category == Fortran::common::TypeCategory::Character) {
do {
mlir::Value elementVal =
- genScalarLit<T::kind>(builder, loc, con.At(subscripts), con.LEN(),
- /*outlineInReadOnlyMemory=*/false);
+ genScalarLit(builder, loc, con.At(subscripts), con.LEN(),
+ /*outlineInReadOnlyMemory=*/false);
array =
fir::InsertValueOp::create(builder, loc, arrayTy, array, elementVal,
builder.getArrayAttr(createIdx()));
@@ -653,9 +661,9 @@ genInlinedArrayLit(Fortran::lower::AbstractConverter &converter,
mlir::Type eleTy = mlir::cast<fir::SequenceType>(arrayTy).getElementType();
do {
auto getElementVal = [&]() {
- return builder.createConvert(loc, eleTy,
- genScalarLit<T::category, T::kind>(
- builder, loc, con.At(subscripts)));
+ return builder.createConvert(
+ loc, eleTy,
+ genScalarLit<T::category>(builder, loc, con.At(subscripts)));
};
Fortran::evaluate::ConstantSubscripts nextSubscripts = subscripts;
bool nextIsSame = con.IncrementSubscripts(nextSubscripts) &&
@@ -743,6 +751,7 @@ static fir::ExtendedValue
genArrayLit(Fortran::lower::AbstractConverter &converter, mlir::Location loc,
const Fortran::evaluate::Constant<T> &con,
bool outlineInReadOnlyMemory) {
+ const int kind{con.kind()};
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
Fortran::evaluate::ConstantSubscript size =
Fortran::evaluate::GetSize(con.shape());
@@ -758,8 +767,8 @@ genArrayLit(Fortran::lower::AbstractConverter &converter, mlir::Location loc,
eleTy = Fortran::lower::translateDerivedTypeToFIRType(
converter, con.GetType().GetDerivedTypeSpec());
else
- eleTy = Fortran::lower::getFIRType(builder.getContext(), T::category,
- T::kind, typeParams);
+ eleTy = Fortran::lower::getFIRType(builder.getContext(), T::category, kind,
+ typeParams);
auto arrayTy = fir::SequenceType::get(shape, eleTy);
mlir::Value array = outlineInReadOnlyMemory
? genOutlineArrayLit(converter, loc, arrayTy, con)
@@ -795,8 +804,7 @@ fir::ExtendedValue Fortran::lower::ConstantBuilder<T>::gen(
assert(opt.has_value() && "constant has no value");
if constexpr (T::category == Fortran::common::TypeCategory::Character) {
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
- auto value =
- genScalarLit<T::kind>(builder, loc, opt.value(), constant.LEN(),
+ auto value = genScalarLit(builder, loc, opt.value(), constant.LEN(),
outlineBigConstantsInReadOnlyMemory);
mlir::Value len = builder.createIntegerConstant(
loc, builder.getCharacterLengthType(), constant.LEN());
@@ -807,8 +815,8 @@ fir::ExtendedValue Fortran::lower::ConstantBuilder<T>::gen(
return genScalarLit(converter, loc, *opt, eleTy,
outlineBigConstantsInReadOnlyMemory);
} else {
- return genScalarLit<T::category, T::kind>(converter.getFirOpBuilder(), loc,
- opt.value());
+ return genScalarLit<T::category>(converter.getFirOpBuilder(), loc,
+ opt.value());
}
}
@@ -843,12 +851,11 @@ genConstantValue(Fortran::lower::AbstractConverter &converter,
constantExpr.AsFortran());
}
-template <Fortran::common::TypeCategory TC, int KIND>
+template <Fortran::common::TypeCategory TC>
static fir::ExtendedValue genConstantValue(
Fortran::lower::AbstractConverter &converter, mlir::Location loc,
- const Fortran::evaluate::Expr<Fortran::evaluate::Type<TC, KIND>>
- &constantExpr) {
- using T = Fortran::evaluate::Type<TC, KIND>;
+ const Fortran::evaluate::Expr<Fortran::evaluate::Type<TC>> &constantExpr) {
+ using T = Fortran::evaluate::Type<TC>;
// Initializer folding preserves parentheses around a scalar constant (e.g.
// "integer :: i = (42)"). UnwrapConstantValue looks through them.
if (const auto *constant =
diff --git a/flang/lib/Lower/ConvertExprToHLFIR.cpp b/flang/lib/Lower/ConvertExprToHLFIR.cpp
index 59ef7143914b2..31d3d802b2384 100644
--- a/flang/lib/Lower/ConvertExprToHLFIR.cpp
+++ b/flang/lib/Lower/ConvertExprToHLFIR.cpp
@@ -87,7 +87,7 @@ class HlfirDesignatorBuilder {
// Character designators variant contains substrings
using CharacterDesignators =
decltype(Fortran::evaluate::Designator<Fortran::evaluate::Type<
- Fortran::evaluate::TypeCategory::Character, 1>>::u);
+ Fortran::evaluate::TypeCategory::Character>>::u);
hlfir::EntityWithAttributes
gen(const CharacterDesignators &designatorVariant,
bool vectorSubscriptDesignatorToValue = true) {
@@ -100,7 +100,7 @@ class HlfirDesignatorBuilder {
// Character designators variant contains complex parts
using RealDesignators =
decltype(Fortran::evaluate::Designator<Fortran::evaluate::Type<
- Fortran::evaluate::TypeCategory::Real, 4>>::u);
+ Fortran::evaluate::TypeCategory::Real>>::u);
hlfir::EntityWithAttributes
gen(const RealDesignators &designatorVariant,
bool vectorSubscriptDesignatorToValue = true) {
@@ -113,7 +113,7 @@ class HlfirDesignatorBuilder {
// All other designators are similar
using OtherDesignators =
decltype(Fortran::evaluate::Designator<Fortran::evaluate::Type<
- Fortran::evaluate::TypeCategory::Integer, 4>>::u);
+ Fortran::evaluate::TypeCategory::Integer>>::u);
hlfir::EntityWithAttributes
gen(const OtherDesignators &designatorVariant,
bool vectorSubscriptDesignatorToValue = true) {
@@ -1056,11 +1056,11 @@ struct BinaryOp {};
#undef GENBIN
#define GENBIN(GenBinEvOp, GenBinTyCat, GenBinFirOp) \
- template <int KIND> \
- struct BinaryOp<Fortran::evaluate::GenBinEvOp<Fortran::evaluate::Type< \
- Fortran::common::TypeCategory::GenBinTyCat, KIND>>> { \
- using Op = Fortran::evaluate::GenBinEvOp<Fortran::evaluate::Type< \
- Fortran::common::TypeCategory::GenBinTyCat, KIND>>; \
+ template <> \
+ struct BinaryOp<Fortran::evaluate::GenBinEvOp< \
+ Fortran::evaluate::Type<Fortran::common::TypeCategory::GenBinTyCat>>> { \
+ using Op = Fortran::evaluate::GenBinEvOp< \
+ Fortran::evaluate::Type<Fortran::common::TypeCategory::GenBinTyCat>>; \
static hlfir::EntityWithAttributes gen(mlir::Location loc, \
fir::FirOpBuilder &builder, \
const Op &, hlfir::Entity lhs, \
@@ -1093,16 +1093,18 @@ GENBIN(Divide, Integer, mlir::arith::DivSIOp)
GENBIN(Divide, Unsigned, mlir::arith::DivUIOp)
GENBIN(Divide, Real, mlir::arith::DivFOp)
-template <int KIND>
+template <>
struct BinaryOp<Fortran::evaluate::Divide<
- Fortran::evaluate::Type<Fortran::common::TypeCategory::Complex, KIND>>> {
+ Fortran::evaluate::Type<Fortran::common::TypeCategory::Complex>>> {
using Op = Fortran::evaluate::Divide<
- Fortran::evaluate::Type<Fortran::common::TypeCategory::Complex, KIND>>;
+ Fortran::evaluate::Type<Fortran::common::TypeCategory::Complex>>;
static hlfir::EntityWithAttributes gen(mlir::Location loc,
- fir::FirOpBuilder &builder, const Op &,
- hlfir::Entity lhs, hlfir::Entity rhs) {
+ fir::FirOpBuilder &builder,
+ const Op &op, hlfir::Entity lhs,
+ hlfir::Entity rhs) {
+ const int kind = op.kind();
mlir::Type ty = Fortran::lower::getFIRType(
- builder.getContext(), Fortran::common::TypeCategory::Complex, KIND,
+ builder.getContext(), Fortran::common::TypeCategory::Complex, kind,
/*params=*/{});
// TODO: Ideally, complex number division operations should always be
@@ -1118,36 +1120,38 @@ struct BinaryOp<Fortran::evaluate::Divide<
}
};
-template <Fortran::common::TypeCategory TC, int KIND>
-struct BinaryOp<Fortran::evaluate::Power<Fortran::evaluate::Type<TC, KIND>>> {
- using Op = Fortran::evaluate::Power<Fortran::evaluate::Type<TC, KIND>>;
+template <Fortran::common::TypeCategory TC>
+struct BinaryOp<Fortran::evaluate::Power<Fortran::evaluate::Type<TC>>> {
+ using Op = Fortran::evaluate::Power<Fortran::evaluate::Type<TC>>;
static hlfir::EntityWithAttributes gen(mlir::Location loc,
- fir::FirOpBuilder &builder, const Op &,
- hlfir::Entity lhs, hlfir::Entity rhs) {
- mlir::Type ty = Fortran::lower::getFIRType(builder.getContext(), TC, KIND,
+ fir::FirOpBuilder &builder,
+ const Op &op, hlfir::Entity lhs,
+ hlfir::Entity rhs) {
+ const int kind = op.kind();
+ mlir::Type ty = Fortran::lower::getFIRType(builder.getContext(), TC, kind,
/*params=*/{});
return hlfir::EntityWithAttributes{fir::genPow(builder, loc, ty, lhs, rhs)};
}
};
-template <Fortran::common::TypeCategory TC, int KIND>
+template <Fortran::common::TypeCategory TC>
struct BinaryOp<
- Fortran::evaluate::RealToIntPower<Fortran::evaluate::Type<TC, KIND>>> {
- using Op =
- Fortran::evaluate::RealToIntPower<Fortran::evaluate::Type<TC, KIND>>;
+ Fortran::evaluate::RealToIntPower<Fortran::evaluate::Type<TC>>> {
+ using Op = Fortran::evaluate::RealToIntPower<Fortran::evaluate::Type<TC>>;
static hlfir::EntityWithAttributes gen(mlir::Location loc,
- fir::FirOpBuilder &builder, const Op &,
- hlfir::Entity lhs, hlfir::Entity rhs) {
- mlir::Type ty = Fortran::lower::getFIRType(builder.getContext(), TC, KIND,
+ fir::FirOpBuilder &builder,
+ const Op &op, hlfir::Entity lhs,
+ hlfir::Entity rhs) {
+ const int kind = op.kind();
+ mlir::Type ty = Fortran::lower::getFIRType(builder.getContext(), TC, kind,
/*params=*/{});
return hlfir::EntityWithAttributes{fir::genPow(builder, loc, ty, lhs, rhs)};
}
};
-template <Fortran::common::TypeCategory TC, int KIND>
-struct BinaryOp<
- Fortran::evaluate::Extremum<Fortran::evaluate::Type<TC, KIND>>> {
- using Op = Fortran::evaluate::Extremum<Fortran::evaluate::Type<TC, KIND>>;
+template <Fortran::common::TypeCategory TC>
+struct BinaryOp<Fortran::evaluate::Extremum<Fortran::evaluate::Type<TC>>> {
+ using Op = Fortran::evaluate::Extremum<Fortran::evaluate::Type<TC>>;
static hlfir::EntityWithAttributes gen(mlir::Location loc,
fir::FirOpBuilder &builder,
const Op &op, hlfir::Entity lhs,
@@ -1165,11 +1169,11 @@ struct BinaryOp<
// MIN and MAX are represented as evaluate::ProcedureRef and are not going
// through here. So far the frontend does not generate character Extremum so
// there is no way to test it.
-template <int KIND>
+template <>
struct BinaryOp<Fortran::evaluate::Extremum<
- Fortran::evaluate::Type<Fortran::common::TypeCategory::Character, KIND>>> {
+ Fortran::evaluate::Type<Fortran::common::TypeCategory::Character>>> {
using Op = Fortran::evaluate::Extremum<
- Fortran::evaluate::Type<Fortran::common::TypeCategory::Character, KIND>>;
+ Fortran::evaluate::Type<Fortran::common::TypeCategory::Character>>;
static hlfir::EntityWithAttributes gen(mlir::Location loc,
fir::FirOpBuilder &, const Op &,
hlfir::Entity, hlfir::Entity) {
@@ -1202,11 +1206,11 @@ translateSignedRelational(Fortran::common::RelationalOperator rop) {
llvm_unreachable("unhandled INTEGER relational operator");
}
-template <int KIND>
+template <>
struct BinaryOp<Fortran::evaluate::Relational<
- Fortran::evaluate::Type<Fortran::common::TypeCategory::Integer, KIND>>> {
+ Fortran::evaluate::Type<Fortran::common::TypeCategory::Integer>>> {
using Op = Fortran::evaluate::Relational<
- Fortran::evaluate::Type<Fortran::common::TypeCategory::Integer, KIND>>;
+ Fortran::evaluate::Type<Fortran::common::TypeCategory::Integer>>;
static hlfir::EntityWithAttributes gen(mlir::Location loc,
fir::FirOpBuilder &builder,
const Op &op, hlfir::Entity lhs,
@@ -1218,17 +1222,16 @@ struct BinaryOp<Fortran::evaluate::Relational<
}
};
-template <int KIND>
+template <>
struct BinaryOp<Fortran::evaluate::Relational<
- Fortran::evaluate::Type<Fortran::common::TypeCategory::Unsigned, KIND>>> {
+ Fortran::evaluate::Type<Fortran::common::TypeCategory::Unsigned>>> {
using Op = Fortran::evaluate::Relational<
- Fortran::evaluate::Type<Fortran::common::TypeCategory::Unsigned, KIND>>;
+ Fortran::evaluate::Type<Fortran::common::TypeCategory::Unsigned>>;
static hlfir::EntityWithAttributes gen(mlir::Location loc,
fir::FirOpBuilder &builder,
const Op &op, hlfir::Entity lhs,
hlfir::Entity rhs) {
- int bits = Fortran::evaluate::Type<Fortran::common::TypeCategory::Integer,
- KIND>::Scalar::bits;
+ int bits = 8 * op.left().GetType().value().kind();
auto signlessType = mlir::IntegerType::get(
builder.getContext(), bits,
mlir::IntegerType::SignednessSemantics::Signless);
@@ -1241,11 +1244,11 @@ struct BinaryOp<Fortran::evaluate::Relational<
}
};
-template <int KIND>
+template <>
struct BinaryOp<Fortran::evaluate::Relational<
- Fortran::evaluate::Type<Fortran::common::TypeCategory::Real, KIND>>> {
+ Fortran::evaluate::Type<Fortran::common::TypeCategory::Real>>> {
using Op = Fortran::evaluate::Relational<
- Fortran::evaluate::Type<Fortran::common::TypeCategory::Real, KIND>>;
+ Fortran::evaluate::Type<Fortran::common::TypeCategory::Real>>;
static hlfir::EntityWithAttributes gen(mlir::Location loc,
fir::FirOpBuilder &builder,
const Op &op, hlfir::Entity lhs,
@@ -1257,11 +1260,11 @@ struct BinaryOp<Fortran::evaluate::Relational<
}
};
-template <int KIND>
+template <>
struct BinaryOp<Fortran::evaluate::Relational<
- Fortran::evaluate::Type<Fortran::common::TypeCategory::Complex, KIND>>> {
+ Fortran::evaluate::Type<Fortran::common::TypeCategory::Complex>>> {
using Op = Fortran::evaluate::Relational<
- Fortran::evaluate::Type<Fortran::common::TypeCategory::Complex, KIND>>;
+ Fortran::evaluate::Type<Fortran::common::TypeCategory::Complex>>;
static hlfir::EntityWithAttributes gen(mlir::Location loc,
fir::FirOpBuilder &builder,
const Op &op, hlfir::Entity lhs,
@@ -1273,11 +1276,11 @@ struct BinaryOp<Fortran::evaluate::Relational<
}
};
-template <int KIND>
+template <>
struct BinaryOp<Fortran::evaluate::Relational<
- Fortran::evaluate::Type<Fortran::common::TypeCategory::Character, KIND>>> {
+ Fortran::evaluate::Type<Fortran::common::TypeCategory::Character>>> {
using Op = Fortran::evaluate::Relational<
- Fortran::evaluate::Type<Fortran::common::TypeCategory::Character, KIND>>;
+ Fortran::evaluate::Type<Fortran::common::TypeCategory::Character>>;
static hlfir::EntityWithAttributes gen(mlir::Location loc,
fir::FirOpBuilder &builder,
const Op &op, hlfir::Entity lhs,
@@ -1288,9 +1291,9 @@ struct BinaryOp<Fortran::evaluate::Relational<
}
};
-template <int KIND>
-struct BinaryOp<Fortran::evaluate::LogicalOperation<KIND>> {
- using Op = Fortran::evaluate::LogicalOperation<KIND>;
+template <>
+struct BinaryOp<Fortran::evaluate::LogicalOperation> {
+ using Op = Fortran::evaluate::LogicalOperation;
static hlfir::EntityWithAttributes gen(mlir::Location loc,
fir::FirOpBuilder &builder,
const Op &op, hlfir::Entity lhs,
@@ -1345,9 +1348,9 @@ struct BinaryOp<Fortran::evaluate::LogicalOperation<KIND>> {
}
};
-template <int KIND>
-struct BinaryOp<Fortran::evaluate::ComplexConstructor<KIND>> {
- using Op = Fortran::evaluate::ComplexConstructor<KIND>;
+template <>
+struct BinaryOp<Fortran::evaluate::ComplexConstructor> {
+ using Op = Fortran::evaluate::ComplexConstructor;
static hlfir::EntityWithAttributes gen(mlir::Location loc,
fir::FirOpBuilder &builder, const Op &,
hlfir::Entity lhs, hlfir::Entity rhs) {
@@ -1357,9 +1360,9 @@ struct BinaryOp<Fortran::evaluate::ComplexConstructor<KIND>> {
}
};
-template <int KIND>
-struct BinaryOp<Fortran::evaluate::SetLength<KIND>> {
- using Op = Fortran::evaluate::SetLength<KIND>;
+template <>
+struct BinaryOp<Fortran::evaluate::SetLength> {
+ using Op = Fortran::evaluate::SetLength;
static hlfir::EntityWithAttributes gen(mlir::Location loc,
fir::FirOpBuilder &builder, const Op &,
hlfir::Entity string,
@@ -1378,9 +1381,9 @@ struct BinaryOp<Fortran::evaluate::SetLength<KIND>> {
}
};
-template <int KIND>
-struct BinaryOp<Fortran::evaluate::Concat<KIND>> {
- using Op = Fortran::evaluate::Concat<KIND>;
+template <>
+struct BinaryOp<Fortran::evaluate::Concat> {
+ using Op = Fortran::evaluate::Concat;
hlfir::EntityWithAttributes gen(mlir::Location loc,
fir::FirOpBuilder &builder, const Op &,
hlfir::Entity lhs, hlfir::Entity rhs) {
@@ -1415,9 +1418,9 @@ struct BinaryOp<Fortran::evaluate::Concat<KIND>> {
template <typename T>
struct UnaryOp {};
-template <int KIND>
-struct UnaryOp<Fortran::evaluate::Not<KIND>> {
- using Op = Fortran::evaluate::Not<KIND>;
+template <>
+struct UnaryOp<Fortran::evaluate::Not> {
+ using Op = Fortran::evaluate::Not;
static hlfir::EntityWithAttributes gen(mlir::Location loc,
fir::FirOpBuilder &builder, const Op &,
hlfir::Entity lhs) {
@@ -1428,17 +1431,18 @@ struct UnaryOp<Fortran::evaluate::Not<KIND>> {
}
};
-template <int KIND>
+template <>
struct UnaryOp<Fortran::evaluate::Negate<
- Fortran::evaluate::Type<Fortran::common::TypeCategory::Integer, KIND>>> {
+ Fortran::evaluate::Type<Fortran::common::TypeCategory::Integer>>> {
using Op = Fortran::evaluate::Negate<
- Fortran::evaluate::Type<Fortran::common::TypeCategory::Integer, KIND>>;
+ Fortran::evaluate::Type<Fortran::common::TypeCategory::Integer>>;
static hlfir::EntityWithAttributes gen(mlir::Location loc,
- fir::FirOpBuilder &builder, const Op &,
- hlfir::Entity lhs) {
+ fir::FirOpBuilder &builder,
+ const Op &op, hlfir::Entity lhs) {
+ const int kind = op.kind();
// Like LLVM, integer negation is the binary op "0 - value"
mlir::Type type = Fortran::lower::getFIRType(
- builder.getContext(), Fortran::common::TypeCategory::Integer, KIND,
+ builder.getContext(), Fortran::common::TypeCategory::Integer, kind,
/*params=*/{});
mlir::Value zero = builder.createIntegerConstant(loc, type, 0);
return hlfir::EntityWithAttributes{
@@ -1446,16 +1450,17 @@ struct UnaryOp<Fortran::evaluate::Negate<
}
};
-template <int KIND>
+template <>
struct UnaryOp<Fortran::evaluate::Negate<
- Fortran::evaluate::Type<Fortran::common::TypeCategory::Unsigned, KIND>>> {
+ Fortran::evaluate::Type<Fortran::common::TypeCategory::Unsigned>>> {
using Op = Fortran::evaluate::Negate<
- Fortran::evaluate::Type<Fortran::common::TypeCategory::Unsigned, KIND>>;
+ Fortran::evaluate::Type<Fortran::common::TypeCategory::Unsigned>>;
static hlfir::EntityWithAttributes gen(mlir::Location loc,
- fir::FirOpBuilder &builder, const Op &,
- hlfir::Entity lhs) {
- int bits = Fortran::evaluate::Type<Fortran::common::TypeCategory::Integer,
- KIND>::Scalar::bits;
+ fir::FirOpBuilder &builder,
+ const Op &op, hlfir::Entity lhs) {
+ const int kind = op.kind();
+ int bits = Fortran::evaluate::Type<
+ Fortran::common::TypeCategory::Integer>::Scalar::bits(kind);
mlir::Type signlessType = mlir::IntegerType::get(
builder.getContext(), bits,
mlir::IntegerType::SignednessSemantics::Signless);
@@ -1468,11 +1473,11 @@ struct UnaryOp<Fortran::evaluate::Negate<
}
};
-template <int KIND>
+template <>
struct UnaryOp<Fortran::evaluate::Negate<
- Fortran::evaluate::Type<Fortran::common::TypeCategory::Real, KIND>>> {
+ Fortran::evaluate::Type<Fortran::common::TypeCategory::Real>>> {
using Op = Fortran::evaluate::Negate<
- Fortran::evaluate::Type<Fortran::common::TypeCategory::Real, KIND>>;
+ Fortran::evaluate::Type<Fortran::common::TypeCategory::Real>>;
static hlfir::EntityWithAttributes gen(mlir::Location loc,
fir::FirOpBuilder &builder, const Op &,
hlfir::Entity lhs) {
@@ -1481,11 +1486,11 @@ struct UnaryOp<Fortran::evaluate::Negate<
}
};
-template <int KIND>
+template <>
struct UnaryOp<Fortran::evaluate::Negate<
- Fortran::evaluate::Type<Fortran::common::TypeCategory::Complex, KIND>>> {
+ Fortran::evaluate::Type<Fortran::common::TypeCategory::Complex>>> {
using Op = Fortran::evaluate::Negate<
- Fortran::evaluate::Type<Fortran::common::TypeCategory::Complex, KIND>>;
+ Fortran::evaluate::Type<Fortran::common::TypeCategory::Complex>>;
static hlfir::EntityWithAttributes gen(mlir::Location loc,
fir::FirOpBuilder &builder, const Op &,
hlfir::Entity lhs) {
@@ -1493,9 +1498,9 @@ struct UnaryOp<Fortran::evaluate::Negate<
}
};
-template <int KIND>
-struct UnaryOp<Fortran::evaluate::ComplexComponent<KIND>> {
- using Op = Fortran::evaluate::ComplexComponent<KIND>;
+template <>
+struct UnaryOp<Fortran::evaluate::ComplexComponent> {
+ using Op = Fortran::evaluate::ComplexComponent;
static hlfir::EntityWithAttributes gen(mlir::Location loc,
fir::FirOpBuilder &builder,
const Op &op, hlfir::Entity lhs) {
@@ -1526,21 +1531,19 @@ struct UnaryOp<Fortran::evaluate::Parentheses<T>> {
}
};
-template <Fortran::common::TypeCategory TC1, int KIND,
- Fortran::common::TypeCategory TC2>
-struct UnaryOp<
- Fortran::evaluate::Convert<Fortran::evaluate::Type<TC1, KIND>, TC2>> {
- using Op =
- Fortran::evaluate::Convert<Fortran::evaluate::Type<TC1, KIND>, TC2>;
+template <Fortran::common::TypeCategory TC1, Fortran::common::TypeCategory TC2>
+struct UnaryOp<Fortran::evaluate::Convert<Fortran::evaluate::Type<TC1>, TC2>> {
+ using Op = Fortran::evaluate::Convert<Fortran::evaluate::Type<TC1>, TC2>;
static hlfir::EntityWithAttributes gen(mlir::Location loc,
- fir::FirOpBuilder &builder, const Op &,
- hlfir::Entity lhs) {
+ fir::FirOpBuilder &builder,
+ const Op &op, hlfir::Entity lhs) {
+ int kind = op.kind();
if constexpr (TC1 == Fortran::common::TypeCategory::Character &&
TC2 == TC1) {
- return hlfir::convertCharacterKind(loc, builder, lhs, KIND);
+ return hlfir::convertCharacterKind(loc, builder, lhs, kind);
}
mlir::Type type = Fortran::lower::getFIRType(builder.getContext(), TC1,
- KIND, /*params=*/{});
+ kind, /*params=*/{});
mlir::Value res = builder.convertWithSemantics(loc, type, lhs);
return hlfir::EntityWithAttributes{res};
}
@@ -1617,7 +1620,7 @@ class HlfirBuilder {
// least one operand is not constant (otherwise folds). For all other
// intrinsics, semantics converts BOZ to the expected type before lowering.
Fortran::evaluate::Constant<Fortran::evaluate::LargestInt> intConstant{
- expr};
+ Fortran::evaluate::LargestIntKind, expr};
return gen(intConstant);
}
@@ -1696,6 +1699,7 @@ class HlfirBuilder {
template <typename D, typename R, typename O>
hlfir::EntityWithAttributes
gen(const Fortran::evaluate::Operation<D, R, O> &op) {
+ const int rKind = op.kind();
auto &builder = getBuilder();
mlir::Location loc = getLoc();
const int rank = op.Rank();
@@ -1718,7 +1722,7 @@ class HlfirBuilder {
getConverter(), op.derived().GetType().GetDerivedTypeSpec());
} else {
elementType =
- Fortran::lower::getFIRType(builder.getContext(), R::category, R::kind,
+ Fortran::lower::getFIRType(builder.getContext(), R::category, rKind,
/*params=*/{});
}
mlir::Value shape = hlfir::genShape(loc, builder, left);
@@ -1741,6 +1745,7 @@ class HlfirBuilder {
template <typename D, typename R, typename LO, typename RO>
hlfir::EntityWithAttributes
gen(const Fortran::evaluate::Operation<D, R, LO, RO> &op) {
+ const int rKind = op.kind();
auto &builder = getBuilder();
mlir::Location loc = getLoc();
const int rank = op.Rank();
@@ -1771,7 +1776,7 @@ class HlfirBuilder {
// Elemental expression.
mlir::Type elementType =
- Fortran::lower::getFIRType(builder.getContext(), R::category, R::kind,
+ Fortran::lower::getFIRType(builder.getContext(), R::category, rKind,
/*params=*/{});
// TODO: "merge" shape, get cst shape from front-end if possible.
// Prefer a compile-time constant shape to get a statically shaped result.
@@ -1839,8 +1844,8 @@ class HlfirBuilder {
getStmtCtx())
.genNamedEntity(desc.base());
using ResTy = Fortran::evaluate::DescriptorInquiry::Result;
- mlir::Type resultType =
- getConverter().genType(ResTy::category, ResTy::kind);
+ const int resKind = Fortran::evaluate::DescriptorInquiry::kind();
+ mlir::Type resultType = getConverter().genType(ResTy::category, resKind);
auto castResult = [&](mlir::Value v) {
return hlfir::EntityWithAttributes{
builder.createConvert(loc, resultType, v)};
diff --git a/flang/lib/Lower/ConvertType.cpp b/flang/lib/Lower/ConvertType.cpp
index 0fdbdfcc74424..7dd56a41354e8 100644
--- a/flang/lib/Lower/ConvertType.cpp
+++ b/flang/lib/Lower/ConvertType.cpp
@@ -53,10 +53,9 @@ static mlir::Type genRealType(mlir::MLIRContext *context, int kind) {
llvm_unreachable("REAL type translation not implemented");
}
-template <int KIND>
-int getIntegerBits() {
- return Fortran::evaluate::Type<Fortran::common::TypeCategory::Integer,
- KIND>::Scalar::bits;
+static int getIntegerBits(int kind) {
+ return Fortran::evaluate::Type<
+ Fortran::common::TypeCategory::Integer>::Scalar::bits(kind);
}
static mlir::Type genIntegerType(mlir::MLIRContext *context, int kind,
bool isUnsigned = false) {
@@ -66,18 +65,7 @@ static mlir::Type genIntegerType(mlir::MLIRContext *context, int kind,
(isUnsigned ? mlir::IntegerType::SignednessSemantics::Unsigned
: mlir::IntegerType::SignednessSemantics::Signless);
- switch (kind) {
- case 1:
- return mlir::IntegerType::get(context, getIntegerBits<1>(), signedness);
- case 2:
- return mlir::IntegerType::get(context, getIntegerBits<2>(), signedness);
- case 4:
- return mlir::IntegerType::get(context, getIntegerBits<4>(), signedness);
- case 8:
- return mlir::IntegerType::get(context, getIntegerBits<8>(), signedness);
- case 16:
- return mlir::IntegerType::get(context, getIntegerBits<16>(), signedness);
- }
+ return mlir::IntegerType::get(context, getIntegerBits(kind), signedness);
}
llvm_unreachable("INTEGER or UNSIGNED kind not translated");
}
@@ -493,14 +481,14 @@ struct TypeBuilderImpl {
// To get the character length from a symbol, make an fold a designator for
// the symbol to cover the case where the symbol is an assumed length named
// constant and its length comes from its init expression length.
- template <int Kind>
fir::SequenceType::Extent
- getCharacterLengthHelper(const Fortran::semantics::Symbol &symbol) {
+ getCharacterLengthHelper(int kind, const Fortran::semantics::Symbol &symbol) {
using TC =
- Fortran::evaluate::Type<Fortran::common::TypeCategory::Character, Kind>;
+ Fortran::evaluate::Type<Fortran::common::TypeCategory::Character>;
auto designator = Fortran::evaluate::Fold(
converter.getFoldingContext(),
- Fortran::evaluate::Expr<TC>{Fortran::evaluate::Designator<TC>{symbol}});
+ Fortran::evaluate::Expr<TC>{
+ Fortran::evaluate::Designator<TC>{kind, symbol}});
if (auto len = toInt64(std::move(designator.LEN())))
return *len;
return fir::SequenceType::getUnknownExtent();
@@ -524,15 +512,7 @@ struct TypeBuilderImpl {
llvm::report_fatal_error("not a character symbol");
int kind =
toInt64(Fortran::common::Clone(type->AsIntrinsic()->kind())).value();
- switch (kind) {
- case 1:
- return getCharacterLengthHelper<1>(symbol);
- case 2:
- return getCharacterLengthHelper<2>(symbol);
- case 4:
- return getCharacterLengthHelper<4>(symbol);
- }
- llvm_unreachable("unknown character kind");
+ return getCharacterLengthHelper(kind, symbol);
}
template <typename A>
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index 1a6819cf10ee7..0732354997cbd 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -6481,7 +6481,7 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
// Map standard OpenMP foreign-runtime identifier strings to
// their well-known integer values (OpenMP 5.1, Table 22.2).
auto frId = llvm::StringSwitch<std::optional<int64_t>>(
- llvm::StringRef(*str).lower())
+ str->AsStringRef()->lower())
.Case("cuda", 1)
.Case("cuda_driver", 2)
.Case("opencl", 3)
@@ -6771,7 +6771,7 @@ static void genErrorDirective(lower::AbstractConverter &converter,
if (args.message) {
if (auto expr = semantics::omp::GetEvaluateExpr(*args.message)) {
if (auto val = evaluate::GetScalarConstantValue<evaluate::Ascii>(*expr))
- message = *val;
+ message = val->AsStdString();
else
messageExpr = expr;
}
diff --git a/flang/lib/Lower/Support/Utils.cpp b/flang/lib/Lower/Support/Utils.cpp
index 9431daaddf1aa..be139d5fc775b 100644
--- a/flang/lib/Lower/Support/Utils.cpp
+++ b/flang/lib/Lower/Support/Utils.cpp
@@ -97,65 +97,72 @@ class HashEvaluateExpr {
static unsigned getHashValue(const Fortran::evaluate::ComplexPart &x) {
return getHashValue(x.complex()) - static_cast<unsigned>(x.part());
}
- template <Fortran::common::TypeCategory TC1, int KIND,
+ template <Fortran::common::TypeCategory TC1,
Fortran::common::TypeCategory TC2>
static unsigned getHashValue(
- const Fortran::evaluate::Convert<Fortran::evaluate::Type<TC1, KIND>, TC2>
- &x) {
+ const Fortran::evaluate::Convert<Fortran::evaluate::Type<TC1>, TC2> &x) {
+ const int kind{x.kind()};
return getHashValue(x.left()) - (static_cast<unsigned>(TC1) + 2u) -
- (static_cast<unsigned>(KIND) + 5u);
+ (static_cast<unsigned>(kind) + 5u);
}
- template <int KIND>
- static unsigned
- getHashValue(const Fortran::evaluate::ComplexComponent<KIND> &x) {
+ static unsigned getHashValue(const Fortran::evaluate::ComplexComponent &x) {
+ const int kind{x.kind()};
return getHashValue(x.left()) -
- (static_cast<unsigned>(x.isImaginaryPart) + 1u) * 3u;
+ (static_cast<unsigned>(x.isImaginaryPart) + 1u) * 3u +
+ static_cast<unsigned>(kind);
}
template <typename T>
static unsigned getHashValue(const Fortran::evaluate::Parentheses<T> &x) {
return getHashValue(x.left()) * 17u;
}
- template <Fortran::common::TypeCategory TC, int KIND>
+ template <Fortran::common::TypeCategory TC>
static unsigned getHashValue(
- const Fortran::evaluate::Negate<Fortran::evaluate::Type<TC, KIND>> &x) {
+ const Fortran::evaluate::Negate<Fortran::evaluate::Type<TC>> &x) {
+ const int kind{x.kind()};
return getHashValue(x.left()) - (static_cast<unsigned>(TC) + 5u) -
- (static_cast<unsigned>(KIND) + 7u);
+ (static_cast<unsigned>(kind) + 7u);
}
- template <Fortran::common::TypeCategory TC, int KIND>
- static unsigned getHashValue(
- const Fortran::evaluate::Add<Fortran::evaluate::Type<TC, KIND>> &x) {
+ template <Fortran::common::TypeCategory TC>
+ static unsigned
+ getHashValue(const Fortran::evaluate::Add<Fortran::evaluate::Type<TC>> &x) {
+ const int kind{x.kind()};
return (getHashValue(x.left()) + getHashValue(x.right())) * 23u +
- static_cast<unsigned>(TC) + static_cast<unsigned>(KIND);
+ static_cast<unsigned>(TC) + static_cast<unsigned>(kind);
}
- template <Fortran::common::TypeCategory TC, int KIND>
+ template <Fortran::common::TypeCategory TC>
static unsigned getHashValue(
- const Fortran::evaluate::Subtract<Fortran::evaluate::Type<TC, KIND>> &x) {
+ const Fortran::evaluate::Subtract<Fortran::evaluate::Type<TC>> &x) {
+ const int kind{x.kind()};
return (getHashValue(x.left()) - getHashValue(x.right())) * 19u +
- static_cast<unsigned>(TC) + static_cast<unsigned>(KIND);
+ static_cast<unsigned>(TC) + static_cast<unsigned>(kind);
}
- template <Fortran::common::TypeCategory TC, int KIND>
+ template <Fortran::common::TypeCategory TC>
static unsigned getHashValue(
- const Fortran::evaluate::Multiply<Fortran::evaluate::Type<TC, KIND>> &x) {
+ const Fortran::evaluate::Multiply<Fortran::evaluate::Type<TC>> &x) {
+ const int kind{x.kind()};
return (getHashValue(x.left()) + getHashValue(x.right())) * 29u +
- static_cast<unsigned>(TC) + static_cast<unsigned>(KIND);
+ static_cast<unsigned>(TC) + static_cast<unsigned>(kind);
}
- template <Fortran::common::TypeCategory TC, int KIND>
+ template <Fortran::common::TypeCategory TC>
static unsigned getHashValue(
- const Fortran::evaluate::Divide<Fortran::evaluate::Type<TC, KIND>> &x) {
+ const Fortran::evaluate::Divide<Fortran::evaluate::Type<TC>> &x) {
+ const int kind{x.kind()};
return (getHashValue(x.left()) - getHashValue(x.right())) * 31u +
- static_cast<unsigned>(TC) + static_cast<unsigned>(KIND);
+ static_cast<unsigned>(TC) + static_cast<unsigned>(kind);
}
- template <Fortran::common::TypeCategory TC, int KIND>
- static unsigned getHashValue(
- const Fortran::evaluate::Power<Fortran::evaluate::Type<TC, KIND>> &x) {
+ template <Fortran::common::TypeCategory TC>
+ static unsigned
+ getHashValue(const Fortran::evaluate::Power<Fortran::evaluate::Type<TC>> &x) {
+ const int kind{x.kind()};
return (getHashValue(x.left()) - getHashValue(x.right())) * 37u +
- static_cast<unsigned>(TC) + static_cast<unsigned>(KIND);
+ static_cast<unsigned>(TC) + static_cast<unsigned>(kind);
}
- template <Fortran::common::TypeCategory TC, int KIND>
+ template <Fortran::common::TypeCategory TC>
static unsigned getHashValue(
- const Fortran::evaluate::Extremum<Fortran::evaluate::Type<TC, KIND>> &x) {
+ const Fortran::evaluate::Extremum<Fortran::evaluate::Type<TC>> &x) {
+ const int kind{x.kind()};
return (getHashValue(x.left()) + getHashValue(x.right())) * 41u +
- static_cast<unsigned>(TC) + static_cast<unsigned>(KIND) +
+ static_cast<unsigned>(TC) + static_cast<unsigned>(kind) +
static_cast<unsigned>(x.ordering) * 7u;
}
template <typename T>
@@ -163,28 +170,27 @@ class HashEvaluateExpr {
return getHashValue(x.condition()) * 151u -
getHashValue(x.thenValue()) * 3u + getHashValue(x.elseValue());
}
- template <Fortran::common::TypeCategory TC, int KIND>
+ template <Fortran::common::TypeCategory TC>
static unsigned getHashValue(
- const Fortran::evaluate::RealToIntPower<Fortran::evaluate::Type<TC, KIND>>
- &x) {
+ const Fortran::evaluate::RealToIntPower<Fortran::evaluate::Type<TC>> &x) {
+ const int kind{x.kind()};
return (getHashValue(x.left()) - getHashValue(x.right())) * 43u +
- static_cast<unsigned>(TC) + static_cast<unsigned>(KIND);
+ static_cast<unsigned>(TC) + static_cast<unsigned>(kind);
}
- template <int KIND>
- static unsigned
- getHashValue(const Fortran::evaluate::ComplexConstructor<KIND> &x) {
+ static unsigned getHashValue(const Fortran::evaluate::ComplexConstructor &x) {
+ const int kind{x.kind()};
return (getHashValue(x.left()) - getHashValue(x.right())) * 47u +
- static_cast<unsigned>(KIND);
+ static_cast<unsigned>(kind);
}
- template <int KIND>
- static unsigned getHashValue(const Fortran::evaluate::Concat<KIND> &x) {
+ static unsigned getHashValue(const Fortran::evaluate::Concat &x) {
+ const int kind{x.kind()};
return (getHashValue(x.left()) - getHashValue(x.right())) * 53u +
- static_cast<unsigned>(KIND);
+ static_cast<unsigned>(kind);
}
- template <int KIND>
- static unsigned getHashValue(const Fortran::evaluate::SetLength<KIND> &x) {
+ static unsigned getHashValue(const Fortran::evaluate::SetLength &x) {
+ const int kind{x.kind()};
return (getHashValue(x.left()) - getHashValue(x.right())) * 59u +
- static_cast<unsigned>(KIND);
+ static_cast<unsigned>(kind);
}
static unsigned getHashValue(const Fortran::semantics::SymbolRef &sym) {
return getHashValue(sym.get());
@@ -251,22 +257,19 @@ class HashEvaluateExpr {
// FIXME: hash the contents.
return 149u;
}
- template <int KIND>
- static unsigned getHashValue(const Fortran::evaluate::Not<KIND> &x) {
- return getHashValue(x.left()) * 61u + static_cast<unsigned>(KIND);
+ static unsigned getHashValue(const Fortran::evaluate::Not &x) {
+ return getHashValue(x.left()) * 61u;
}
- template <int KIND>
- static unsigned
- getHashValue(const Fortran::evaluate::LogicalOperation<KIND> &x) {
+ static unsigned getHashValue(const Fortran::evaluate::LogicalOperation &x) {
unsigned result = getHashValue(x.left()) + getHashValue(x.right());
return result * 67u + static_cast<unsigned>(x.logicalOperator) * 5u;
}
- template <Fortran::common::TypeCategory TC, int KIND>
+ template <Fortran::common::TypeCategory TC>
static unsigned getHashValue(
- const Fortran::evaluate::Relational<Fortran::evaluate::Type<TC, KIND>>
- &x) {
+ const Fortran::evaluate::Relational<Fortran::evaluate::Type<TC>> &x) {
+ const int kind{x.kind()};
return (getHashValue(x.left()) + getHashValue(x.right())) * 71u +
- static_cast<unsigned>(TC) + static_cast<unsigned>(KIND) +
+ static_cast<unsigned>(TC) + static_cast<unsigned>(kind) +
static_cast<unsigned>(x.opr) * 11u;
}
template <typename A>
@@ -284,9 +287,8 @@ class HashEvaluateExpr {
return Fortran::common::visit(
[&](const auto &v) { return getHashValue(v); }, x.u);
}
- template <int BITS>
static unsigned
- getHashValue(const Fortran::evaluate::value::Integer<BITS> &x) {
+ getHashValue(const Fortran::evaluate::value::IntegerValue &x) {
return static_cast<unsigned>(x.ToSInt());
}
static unsigned getHashValue(const Fortran::evaluate::NullPointer &x) {
@@ -374,84 +376,81 @@ class IsEqualEvaluateExpr {
template <typename A, Fortran::common::TypeCategory TC2>
static bool isEqual(const Fortran::evaluate::Convert<A, TC2> &x,
const Fortran::evaluate::Convert<A, TC2> &y) {
- return isEqual(x.left(), y.left());
+ return x.kind() == y.kind() && isEqual(x.left(), y.left());
}
- template <int KIND>
- static bool isEqual(const Fortran::evaluate::ComplexComponent<KIND> &x,
- const Fortran::evaluate::ComplexComponent<KIND> &y) {
- return isEqual(x.left(), y.left()) &&
+ static bool isEqual(const Fortran::evaluate::ComplexComponent &x,
+ const Fortran::evaluate::ComplexComponent &y) {
+ return x.kind() == y.kind() && isEqual(x.left(), y.left()) &&
x.isImaginaryPart == y.isImaginaryPart;
}
template <typename T>
static bool isEqual(const Fortran::evaluate::Parentheses<T> &x,
const Fortran::evaluate::Parentheses<T> &y) {
- return isEqual(x.left(), y.left());
+ return x.kind() == y.kind() && isEqual(x.left(), y.left());
}
template <typename A>
static bool isEqual(const Fortran::evaluate::Negate<A> &x,
const Fortran::evaluate::Negate<A> &y) {
- return isEqual(x.left(), y.left());
+ return x.kind() == y.kind() && isEqual(x.left(), y.left());
}
template <typename A>
static bool isBinaryEqual(const A &x, const A &y) {
- return isEqual(x.left(), y.left()) && isEqual(x.right(), y.right());
+ return x.kind() == y.kind() && isEqual(x.left(), y.left()) &&
+ isEqual(x.right(), y.right());
}
template <typename A>
static bool isEqual(const Fortran::evaluate::Add<A> &x,
const Fortran::evaluate::Add<A> &y) {
- return isBinaryEqual(x, y);
+ return x.kind() == y.kind() && isBinaryEqual(x, y);
}
template <typename A>
static bool isEqual(const Fortran::evaluate::Subtract<A> &x,
const Fortran::evaluate::Subtract<A> &y) {
- return isBinaryEqual(x, y);
+ return x.kind() == y.kind() && isBinaryEqual(x, y);
}
template <typename A>
static bool isEqual(const Fortran::evaluate::Multiply<A> &x,
const Fortran::evaluate::Multiply<A> &y) {
- return isBinaryEqual(x, y);
+ return x.kind() == y.kind() && isBinaryEqual(x, y);
}
template <typename A>
static bool isEqual(const Fortran::evaluate::Divide<A> &x,
const Fortran::evaluate::Divide<A> &y) {
- return isBinaryEqual(x, y);
+ return x.kind() == y.kind() && isBinaryEqual(x, y);
}
template <typename A>
static bool isEqual(const Fortran::evaluate::Power<A> &x,
const Fortran::evaluate::Power<A> &y) {
- return isBinaryEqual(x, y);
+ return x.kind() == y.kind() && isBinaryEqual(x, y);
}
template <typename A>
static bool isEqual(const Fortran::evaluate::Extremum<A> &x,
const Fortran::evaluate::Extremum<A> &y) {
- return isBinaryEqual(x, y);
+ return x.kind() == y.kind() && isBinaryEqual(x, y);
}
template <typename T>
static bool isEqual(const Fortran::evaluate::ConditionalExpr<T> &x,
const Fortran::evaluate::ConditionalExpr<T> &y) {
- return isEqual(x.condition(), y.condition()) &&
+ return x.kind() == y.kind() && isEqual(x.condition(), y.condition()) &&
isEqual(x.thenValue(), y.thenValue()) &&
isEqual(x.elseValue(), y.elseValue());
}
template <typename A>
static bool isEqual(const Fortran::evaluate::RealToIntPower<A> &x,
const Fortran::evaluate::RealToIntPower<A> &y) {
- return isBinaryEqual(x, y);
+ return x.kind() == y.kind() && isBinaryEqual(x, y);
}
- template <int KIND>
- static bool isEqual(const Fortran::evaluate::ComplexConstructor<KIND> &x,
- const Fortran::evaluate::ComplexConstructor<KIND> &y) {
- return isBinaryEqual(x, y);
+ static bool isEqual(const Fortran::evaluate::ComplexConstructor &x,
+ const Fortran::evaluate::ComplexConstructor &y) {
+ return x.kind() == y.kind() && isBinaryEqual(x, y);
}
- template <int KIND>
- static bool isEqual(const Fortran::evaluate::Concat<KIND> &x,
- const Fortran::evaluate::Concat<KIND> &y) {
- return isBinaryEqual(x, y);
+ static bool isEqual(const Fortran::evaluate::Concat &x,
+ const Fortran::evaluate::Concat &y) {
+ return x.kind() == y.kind() && isBinaryEqual(x, y);
}
- template <int KIND>
- static bool isEqual(const Fortran::evaluate::SetLength<KIND> &x,
- const Fortran::evaluate::SetLength<KIND> &y) {
- return isBinaryEqual(x, y);
+ static bool isEqual(const Fortran::evaluate::SetLength &x,
+ const Fortran::evaluate::SetLength &y) {
+ return x.kind() == y.kind() && isBinaryEqual(x, y);
}
static bool isEqual(const Fortran::semantics::SymbolRef &x,
const Fortran::semantics::SymbolRef &y) {
@@ -475,7 +474,7 @@ class IsEqualEvaluateExpr {
template <typename A>
static bool isEqual(const Fortran::evaluate::Constant<A> &x,
const Fortran::evaluate::Constant<A> &y) {
- return x == y;
+ return x.kind() == y.kind() && x == y;
}
static bool isEqual(const Fortran::evaluate::ActualArgument &x,
const Fortran::evaluate::ActualArgument &y) {
@@ -526,11 +525,13 @@ class IsEqualEvaluateExpr {
}
static bool isEqual(const Fortran::evaluate::SubscriptInteger &x,
const Fortran::evaluate::SubscriptInteger &y) {
- return x == y;
+ return x.kind() == y.kind() && x == y;
}
template <typename A>
static bool isEqual(const Fortran::evaluate::ArrayConstructor<A> &x,
const Fortran::evaluate::ArrayConstructor<A> &y) {
+ if (x.kind() != y.kind())
+ return false;
bool checkCharacterType = true;
if constexpr (A::category == Fortran::common::TypeCategory::Character) {
checkCharacterType = isEqual(*x.LEN(), *y.LEN());
@@ -541,16 +542,17 @@ class IsEqualEvaluateExpr {
}
static bool isEqual(const Fortran::evaluate::ImpliedDoIndex &x,
const Fortran::evaluate::ImpliedDoIndex &y) {
- return toStringRef(x.name) == toStringRef(y.name);
+ return x.kind() == y.kind() && toStringRef(x.name) == toStringRef(y.name);
}
static bool isEqual(const Fortran::evaluate::TypeParamInquiry &x,
const Fortran::evaluate::TypeParamInquiry &y) {
- return isEqual(x.base(), y.base()) && isEqual(x.parameter(), y.parameter());
+ return x.kind() == y.kind() && isEqual(x.base(), y.base()) &&
+ isEqual(x.parameter(), y.parameter());
}
static bool isEqual(const Fortran::evaluate::DescriptorInquiry &x,
const Fortran::evaluate::DescriptorInquiry &y) {
- return isEqual(x.base(), y.base()) && x.field() == y.field() &&
- x.dimension() == y.dimension();
+ return x.kind() == y.kind() && isEqual(x.base(), y.base()) &&
+ x.field() == y.field() && x.dimension() == y.dimension();
}
static bool isEqual(const Fortran::evaluate::RankOneBoundElement &x,
const Fortran::evaluate::RankOneBoundElement &y) {
@@ -558,6 +560,8 @@ class IsEqualEvaluateExpr {
}
static bool isEqual(const Fortran::evaluate::StructureConstructor &x,
const Fortran::evaluate::StructureConstructor &y) {
+ if (x.kind() != y.kind())
+ return false;
const auto &xValues = x.values();
const auto &yValues = y.values();
if (xValues.size() != yValues.size())
@@ -575,47 +579,52 @@ class IsEqualEvaluateExpr {
}
return true;
}
- template <int KIND>
- static bool isEqual(const Fortran::evaluate::Not<KIND> &x,
- const Fortran::evaluate::Not<KIND> &y) {
- return isEqual(x.left(), y.left());
+ static bool isEqual(const Fortran::evaluate::Not &x,
+ const Fortran::evaluate::Not &y) {
+ return x.kind() == y.kind() && isEqual(x.left(), y.left());
}
- template <int KIND>
- static bool isEqual(const Fortran::evaluate::LogicalOperation<KIND> &x,
- const Fortran::evaluate::LogicalOperation<KIND> &y) {
- return isEqual(x.left(), y.left()) && isEqual(x.right(), y.right());
+ static bool isEqual(const Fortran::evaluate::LogicalOperation &x,
+ const Fortran::evaluate::LogicalOperation &y) {
+ return x.kind() == y.kind() && isEqual(x.left(), y.left()) &&
+ isEqual(x.right(), y.right());
}
template <typename A>
static bool isEqual(const Fortran::evaluate::Relational<A> &x,
const Fortran::evaluate::Relational<A> &y) {
- return isEqual(x.left(), y.left()) && isEqual(x.right(), y.right());
+ return x.kind() == y.kind() && isEqual(x.left(), y.left()) &&
+ isEqual(x.right(), y.right());
}
template <typename A>
static bool isEqual(const Fortran::evaluate::Expr<A> &x,
const Fortran::evaluate::Expr<A> &y) {
- return Fortran::common::visit(
- [&](const auto &v, const auto &w) { return isEqual(v, w); }, x.u, y.u);
+ return x.kind() == y.kind() &&
+ Fortran::common::visit(
+ [&](const auto &v, const auto &w) { return isEqual(v, w); }, x.u,
+ y.u);
}
static bool
isEqual(const Fortran::evaluate::Relational<Fortran::evaluate::SomeType> &x,
const Fortran::evaluate::Relational<Fortran::evaluate::SomeType> &y) {
- return Fortran::common::visit(
- [&](const auto &v, const auto &w) { return isEqual(v, w); }, x.u, y.u);
+ return x.kind() == y.kind() &&
+ Fortran::common::visit(
+ [&](const auto &v, const auto &w) { return isEqual(v, w); }, x.u,
+ y.u);
}
template <typename A>
static bool isEqual(const Fortran::evaluate::Designator<A> &x,
const Fortran::evaluate::Designator<A> &y) {
- return Fortran::common::visit(
- [&](const auto &v, const auto &w) { return isEqual(v, w); }, x.u, y.u);
+ return x.kind() == y.kind() &&
+ Fortran::common::visit(
+ [&](const auto &v, const auto &w) { return isEqual(v, w); }, x.u,
+ y.u);
}
- template <int BITS>
- static bool isEqual(const Fortran::evaluate::value::Integer<BITS> &x,
- const Fortran::evaluate::value::Integer<BITS> &y) {
- return x == y;
+ static bool isEqual(const Fortran::evaluate::value::IntegerValue &x,
+ const Fortran::evaluate::value::IntegerValue &y) {
+ return x.kind() == y.kind() && x == y;
}
static bool isEqual(const Fortran::evaluate::NullPointer &x,
const Fortran::evaluate::NullPointer &y) {
- return true;
+ return x.kind() == y.kind() && true;
}
template <typename A, typename B,
std::enable_if_t<!std::is_same_v<A, B>, bool> = true>
diff --git a/flang/lib/Semantics/check-call.cpp b/flang/lib/Semantics/check-call.cpp
index 6309b3617f84b..21cb1fcfb2ffa 100644
--- a/flang/lib/Semantics/check-call.cpp
+++ b/flang/lib/Semantics/check-call.cpp
@@ -271,7 +271,7 @@ static void CheckCharacterActual(evaluate::Expr<evaluate::SomeType> &actual,
ConvertToType(dummy.type.type(), std::move(actual))};
CHECK(converted);
actual = std::move(*converted);
- actualType.set_LEN(SubscriptIntExpr{*dummyLength});
+ actualType.set_LEN(evaluate::MakeSubscriptIntExpr(*dummyLength));
}
}
}
diff --git a/flang/lib/Semantics/check-case.cpp b/flang/lib/Semantics/check-case.cpp
index 7f227cbab618a..38eab344ed48f 100644
--- a/flang/lib/Semantics/check-case.cpp
+++ b/flang/lib/Semantics/check-case.cpp
@@ -22,8 +22,8 @@ namespace Fortran::semantics {
template <typename T> class CaseValues {
public:
- CaseValues(SemanticsContext &c, const evaluate::DynamicType &t)
- : context_{c}, caseExprType_{t} {}
+ CaseValues(int kind, SemanticsContext &c, const evaluate::DynamicType &t)
+ : kind_{kind}, context_{c}, caseExprType_{t} {}
void Check(const std::list<parser::CaseConstruct::Case> &cases) {
for (const parser::CaseConstruct::Case &c : cases) {
@@ -61,13 +61,13 @@ template <typename T> class CaseValues {
"CASE range is not allowed for LOGICAL"_err_en_US);
}
}
- cases_.emplace_back(stmt);
+ cases_.emplace_back(kind_, stmt);
cases_.back().lower = std::move(pair.first);
cases_.back().upper = std::move(pair.second);
}
}
},
- [&](const parser::Default &) { cases_.emplace_front(stmt); },
+ [&](const parser::Default &) { cases_.emplace_front(kind_, stmt); },
},
selector.u);
}
@@ -86,7 +86,8 @@ template <typename T> class CaseValues {
context_.foldingContext(), foldingMessages};
auto folded{evaluate::Fold(foldingContext, SomeExpr{*x->v})};
if (auto converted{evaluate::Fold(foldingContext,
- evaluate::ConvertToType(T::GetType(), SomeExpr{folded}))}) {
+ evaluate::ConvertToType(
+ {T::category, kind_}, SomeExpr{folded}))}) {
if (auto value{evaluate::GetScalarConstantValue<T>(*converted)}) {
auto back{evaluate::Fold(foldingContext,
evaluate::ConvertToType(*type, SomeExpr{*converted}))};
@@ -143,22 +144,23 @@ template <typename T> class CaseValues {
}
struct Case {
- explicit Case(const parser::Statement<parser::CaseStmt> &s) : stmt{s} {}
+ explicit Case(int kind, const parser::Statement<parser::CaseStmt> &s)
+ : kind_{kind}, stmt{s} {}
bool IsDefault() const { return !lower && !upper; }
std::string AsFortran() const {
std::string result;
{
llvm::raw_string_ostream bs{result};
if (lower) {
- evaluate::Constant<T>{*lower}.AsFortran(bs << '(');
+ evaluate::Constant<T>{kind_, *lower}.AsFortran(bs << '(');
if (!upper) {
bs << ':';
} else if (*lower != *upper) {
- evaluate::Constant<T>{*upper}.AsFortran(bs << ':');
+ evaluate::Constant<T>{kind_, *upper}.AsFortran(bs << ':');
}
bs << ')';
} else if (upper) {
- evaluate::Constant<T>{*upper}.AsFortran(bs << "(:") << ')';
+ evaluate::Constant<T>{kind_, *upper}.AsFortran(bs << "(:") << ')';
} else {
bs << "DEFAULT";
}
@@ -166,6 +168,7 @@ template <typename T> class CaseValues {
return result;
}
+ int kind_;
const parser::Statement<parser::CaseStmt> &stmt;
std::optional<Value> lower, upper;
};
@@ -215,6 +218,7 @@ template <typename T> class CaseValues {
}
}
+ int kind_;
SemanticsContext &context_;
const evaluate::DynamicType &caseExprType_;
std::list<Case> cases_;
@@ -224,9 +228,9 @@ template <typename T> class CaseValues {
template <TypeCategory CAT> struct TypeVisitor {
using Result = bool;
using Types = evaluate::CategoryTypes<CAT>;
- template <typename T> Result Test() {
- if (T::kind == exprType.kind()) {
- CaseValues<T>(context, exprType).Check(caseList);
+ template <typename T> Result Test(int kind) {
+ if (kind == exprType.kind()) {
+ CaseValues<T>(kind, context, exprType).Check(caseList);
return true;
} else {
return false;
@@ -341,19 +345,19 @@ void CaseChecker::Enter(const parser::CaseConstruct &construct) {
std::get<std::list<parser::CaseConstruct::Case>>(construct.t)};
switch (exprType->category()) {
case TypeCategory::Integer:
- common::SearchTypes(
+ evaluate::SearchTypes(
TypeVisitor<TypeCategory::Integer>{context_, *exprType, caseList});
return;
case TypeCategory::Unsigned:
- common::SearchTypes(
+ evaluate::SearchTypes(
TypeVisitor<TypeCategory::Unsigned>{context_, *exprType, caseList});
return;
case TypeCategory::Logical:
- CaseValues<evaluate::Type<TypeCategory::Logical, 1>>{context_, *exprType}
+ CaseValues<evaluate::Type<TypeCategory::Logical>>{1, context_, *exprType}
.Check(caseList);
return;
case TypeCategory::Character:
- common::SearchTypes(
+ evaluate::SearchTypes(
TypeVisitor<TypeCategory::Character>{context_, *exprType, caseList});
return;
case TypeCategory::Derived:
@@ -361,8 +365,8 @@ void CaseChecker::Enter(const parser::CaseConstruct &construct) {
if (derived->IsEnumerationType()) {
if (ConvertEnumCaseValues(context_, caseList, *derived)) {
evaluate::DynamicType intType{TypeCategory::Integer, 4};
- CaseValues<evaluate::Type<TypeCategory::Integer, 4>>{
- context_, intType}
+ CaseValues<evaluate::Type<TypeCategory::Integer>>{
+ 4, context_, intType}
.Check(caseList);
}
return;
diff --git a/flang/lib/Semantics/check-coarray.cpp b/flang/lib/Semantics/check-coarray.cpp
index 00b97162ff0d4..a80dbed3f9206 100644
--- a/flang/lib/Semantics/check-coarray.cpp
+++ b/flang/lib/Semantics/check-coarray.cpp
@@ -209,7 +209,7 @@ void CoarrayChecker::Leave(const parser::SyncImagesStmt &x) {
someInt && evaluate::IsActuallyConstant(*someInt)) {
auto converted{evaluate::Fold(context_.foldingContext(),
evaluate::ConvertToType<evaluate::SubscriptInteger>(
- common::Clone(*someInt)))};
+ evaluate::SubscriptIntegerKind, common::Clone(*someInt)))};
if (const auto *cst{
evaluate::UnwrapConstantValue<evaluate::SubscriptInteger>(
converted)}) {
diff --git a/flang/lib/Semantics/check-data.cpp b/flang/lib/Semantics/check-data.cpp
index e8b7ea2eda63e..8adf758e319af 100644
--- a/flang/lib/Semantics/check-data.cpp
+++ b/flang/lib/Semantics/check-data.cpp
@@ -25,7 +25,7 @@ namespace Fortran::semantics {
void DataChecker::Enter(const parser::DataImpliedDo &x) {
const auto &name{parser::UnwrapRef<parser::Name>(
std::get<parser::DataImpliedDo::Bounds>(x.t).Name())};
- int kind{evaluate::ResultType<evaluate::ImpliedDoIndex>::kind};
+ int kind{evaluate::ResultKind<evaluate::ImpliedDoIndex>};
if (const auto dynamicType{evaluate::DynamicType::From(DEREF(name.symbol))}) {
if (dynamicType->category() == TypeCategory::Integer) {
kind = dynamicType->kind();
diff --git a/flang/lib/Semantics/check-io.h b/flang/lib/Semantics/check-io.h
index 96a07ce13f7bb..58714b39352d8 100644
--- a/flang/lib/Semantics/check-io.h
+++ b/flang/lib/Semantics/check-io.h
@@ -91,8 +91,11 @@ class IoChecker : public virtual BaseChecker {
const auto foldExpr{
evaluate::Fold(context_.foldingContext(), common::Clone(*expr))};
if constexpr (std::is_same_v<R, std::string>) {
- return evaluate::GetScalarConstantValue<DefaultCharConstantType>(
- foldExpr);
+ if (auto charVal{
+ evaluate::GetScalarConstantValue<DefaultCharConstantType>(
+ foldExpr)}) {
+ return charVal->AsStdString();
+ }
} else {
static_assert(std::is_same_v<R, std::int64_t>, "unexpected type");
return evaluate::ToInt64(foldExpr);
diff --git a/flang/lib/Semantics/check-omp-atomic.cpp b/flang/lib/Semantics/check-omp-atomic.cpp
index 810d91a54d710..93508ceb866ca 100644
--- a/flang/lib/Semantics/check-omp-atomic.cpp
+++ b/flang/lib/Semantics/check-omp-atomic.cpp
@@ -59,8 +59,7 @@ template <typename...> struct IsIntegral {
static constexpr bool value{false};
};
-template <common::TypeCategory C, int K>
-struct IsIntegral<evaluate::Type<C, K>> {
+template <common::TypeCategory C> struct IsIntegral<evaluate::Type<C>> {
static constexpr bool value{//
C == common::TypeCategory::Integer ||
C == common::TypeCategory::Unsigned};
@@ -72,8 +71,7 @@ template <typename...> struct IsFloatingPoint {
static constexpr bool value{false};
};
-template <common::TypeCategory C, int K>
-struct IsFloatingPoint<evaluate::Type<C, K>> {
+template <common::TypeCategory C> struct IsFloatingPoint<evaluate::Type<C>> {
static constexpr bool value{//
C == common::TypeCategory::Real || C == common::TypeCategory::Complex};
};
@@ -88,8 +86,7 @@ template <typename...> struct IsLogical {
static constexpr bool value{false};
};
-template <common::TypeCategory C, int K>
-struct IsLogical<evaluate::Type<C, K>> {
+template <common::TypeCategory C> struct IsLogical<evaluate::Type<C>> {
static constexpr bool value{C == common::TypeCategory::Logical};
};
@@ -160,7 +157,7 @@ struct ReassocRewriter : public evaluate::rewrite::Identity {
// Since this works with clang, MSVC and at least GCC 8.5, I'm assuming
// that this is some kind of a GCC issue.
using MatchTypes = std::tuple<evaluate::Add<T>, evaluate::Multiply<T>,
- evaluate::LogicalOperation<T::kind>>;
+ evaluate::LogicalOperation>;
#else
using MatchTypes = typename decltype(outer1)::MatchTypes;
#endif
@@ -205,6 +202,7 @@ struct ReassocRewriter : public evaluate::rewrite::Identity {
evaluate::Expr<T> Reconstruct(const S &op, evaluate::Expr<T> atom,
evaluate::Expr<T> op1, evaluate::Expr<T> op2) {
using TypeS = llvm::remove_cvref_t<decltype(op)>;
+ const int KindS{op.kind()};
// This function has to be semantically correct for all possible types
// of S even though at runtime s will only be one of the matched types.
// Limit the construction to the operation types that we tried to match
@@ -212,8 +210,10 @@ struct ReassocRewriter : public evaluate::rewrite::Identity {
if constexpr (!common::HasMember<TypeS, MatchTypes>) {
return evaluate::Expr<T>(TypeS(op));
} else if constexpr (is_logical_v<T>) {
- constexpr int K{T::kind};
- if constexpr (std::is_same_v<TypeS, evaluate::LogicalOperation<K>>) {
+ CHECK(op1.kind() == op2.kind());
+ const int K{op1.kind()};
+ if constexpr (std::is_same_v<TypeS, evaluate::LogicalOperation>) {
+ CHECK(K == KindS);
// Logical operators take an extra argument in their constructor,
// so they need their own reconstruction code.
common::LogicalOperator opCode{op.logicalOperator};
@@ -224,9 +224,9 @@ struct ReassocRewriter : public evaluate::rewrite::Identity {
}
} else {
// Generic reconstruction.
- return evaluate::Expr<T>(TypeS( //
+ return evaluate::Expr<T>(TypeS(KindS, //
std::move(atom),
- evaluate::Expr<T>(TypeS( //
+ evaluate::Expr<T>(TypeS(KindS, //
std::move(op1), std::move(op2)))));
}
}
@@ -1722,6 +1722,7 @@ struct MinMaxRewriter : public evaluate::rewrite::Identity {
template <typename T>
evaluate::Expr<T> operator()(
evaluate::Expr<T> &&x, const evaluate::FunctionRef<T> &f) {
+ const int kind{f.kind()};
const evaluate::ProcedureDesignator &proc = f.proc();
if (!IsMinMax(proc) || f.arguments().size() <= 2) {
return Id::operator()(std::move(x), f);
@@ -1777,10 +1778,10 @@ struct MinMaxRewriter : public evaluate::rewrite::Identity {
}
SomeExpr tmp = evaluate::AsGenericExpr(
- evaluate::FunctionRef<T>(AsRvalue(proc), AsRvalue(nonAtoms)));
+ evaluate::FunctionRef<T>{kind, AsRvalue(proc), AsRvalue(nonAtoms)});
- return evaluate::Expr<T>(evaluate::FunctionRef<T>(
- AsRvalue(proc), {AsActual(*atomArg), AsActual(tmp)}));
+ return evaluate::Expr<T>{evaluate::FunctionRef<T>{
+ kind, AsRvalue(proc), {AsActual(*atomArg), AsActual(tmp)}}};
}
private:
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index 7bd5f1720fb3c..124b659113e84 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -2752,7 +2752,7 @@ void OmpStructureChecker::Enter(const parser::OmpErrorDirective &x) {
if (args.message) {
if (auto expr{GetEvaluateExpr(*args.message)}) {
if (auto val{evaluate::GetScalarConstantValue<evaluate::Ascii>(*expr)}) {
- message = *val;
+ message = val->AsStdString();
}
}
}
diff --git a/flang/lib/Semantics/data-to-inits.cpp b/flang/lib/Semantics/data-to-inits.cpp
index cd9ec3059afec..ad893735eae6c 100644
--- a/flang/lib/Semantics/data-to-inits.cpp
+++ b/flang/lib/Semantics/data-to-inits.cpp
@@ -207,7 +207,7 @@ bool DataInitializationCompiler<DSV>::Scan(const parser::DataImpliedDo &ido) {
auto foldedUpper{evaluate::Fold(context, SomeExpr{*upperExpr})};
auto upper{ToInt64(foldedUpper)};
if (lower && upper) {
- int kind{evaluate::ResultType<evaluate::ImpliedDoIndex>::kind};
+ int kind{evaluate::ResultKind<evaluate::ImpliedDoIndex>};
if (const auto dynamicType{evaluate::DynamicType::From(*name.symbol)}) {
if (dynamicType->category() == TypeCategory::Integer) {
kind = dynamicType->kind();
@@ -829,7 +829,7 @@ static bool CombineEquivalencedInitialization(
minElementBytes = 1;
}
const DeclTypeSpec &typeSpec{scope.MakeNumericType(
- TypeCategory::Integer, KindExpr{minElementBytes})};
+ TypeCategory::Integer, MakeKindExpr(minElementBytes))};
details.set_type(typeSpec);
ArraySpec arraySpec;
arraySpec.emplace_back(ShapeSpec::MakeExplicit(Bound{
diff --git a/flang/lib/Semantics/dump-expr.cpp b/flang/lib/Semantics/dump-expr.cpp
index 44c7d5a4058cf..9a581896a8914 100644
--- a/flang/lib/Semantics/dump-expr.cpp
+++ b/flang/lib/Semantics/dump-expr.cpp
@@ -224,12 +224,7 @@ void DumpEvaluateExpr::Outdent() {
void DumpEvExpr(const SomeExpr &x) { DumpEvaluateExpr::Dump(x); }
void DumpEvExpr(
- const evaluate::Expr<evaluate::Type<common::TypeCategory::Integer, 4>> &x) {
- DumpEvaluateExpr::Dump(x);
-}
-
-void DumpEvExpr(
- const evaluate::Expr<evaluate::Type<common::TypeCategory::Integer, 8>> &x) {
+ const evaluate::Expr<evaluate::Type<common::TypeCategory::Integer>> &x) {
DumpEvaluateExpr::Dump(x);
}
@@ -240,7 +235,7 @@ void DumpEvExpr(const evaluate::DataRef &x) { DumpEvaluateExpr::Dump(x); }
void DumpEvExpr(const evaluate::Substring &x) { DumpEvaluateExpr::Dump(x); }
void DumpEvExpr(
- const evaluate::Designator<evaluate::Type<common::TypeCategory::Integer, 4>>
+ const evaluate::Designator<evaluate::Type<common::TypeCategory::Integer>>
&x) {
DumpEvaluateExpr::Dump(x);
}
diff --git a/flang/lib/Semantics/expression.cpp b/flang/lib/Semantics/expression.cpp
index fc57cc43e981c..68dcb5721d37a 100644
--- a/flang/lib/Semantics/expression.cpp
+++ b/flang/lib/Semantics/expression.cpp
@@ -87,7 +87,7 @@ static std::optional<DynamicTypeWithLength> AnalyzeTypeSpec(
if (auto lenExpr{type.LEN()}) {
type.length = Fold(context,
AsExpr(Extremum<SubscriptInteger>{Ordering::Greater,
- Expr<SubscriptInteger>{0}, std::move(*lenExpr)}));
+ MakeSubscriptIntExpr(0), std::move(*lenExpr)}));
}
return type;
} else {
@@ -728,24 +728,25 @@ int ExpressionAnalyzer::AnalyzeKindParam(
template <typename TYPES, TypeCategory CAT> struct IntTypeVisitor {
using Result = MaybeExpr;
using Types = TYPES;
- template <typename T> Result Test() {
- if (T::kind >= kind) {
+ template <typename T> Result Test(int testKind) {
+ if (testKind >= kind) {
const char *p{digits.begin()};
using Int = typename T::Scalar;
- typename Int::ValueWithOverflow num{0, false};
+ typename Int::ValueWithOverflow num{Int::Zero(testKind), false};
const char *typeName{
CAT == TypeCategory::Integer ? "INTEGER" : "UNSIGNED"};
if (isNegated) {
- auto unsignedNum{Int::Read(p, 10, false /*unsigned*/)};
+ auto unsignedNum{Int::Read(testKind, p, 10, false /*unsigned*/)};
num.value = unsignedNum.value.Negate().value;
num.overflow = unsignedNum.overflow ||
- (CAT == TypeCategory::Integer && num.value > Int{0});
+ (CAT == TypeCategory::Integer && num.value > Int{testKind, 0});
if (!num.overflow && num.value.Negate().overflow) {
analyzer.Warn(LanguageFeature::BigIntLiterals, digits,
- "negated maximum INTEGER(KIND=%d) literal"_port_en_US, T::kind);
+ "negated maximum INTEGER(KIND=%d) literal"_port_en_US, testKind);
}
} else {
- num = Int::Read(p, 10, /*isSigned=*/CAT == TypeCategory::Integer);
+ num = Int::Read(
+ testKind, p, 10, /*isSigned=*/CAT == TypeCategory::Integer);
}
if (num.overflow) {
if constexpr (CAT == TypeCategory::Unsigned) {
@@ -753,10 +754,10 @@ template <typename TYPES, TypeCategory CAT> struct IntTypeVisitor {
"Unsigned literal too large for UNSIGNED(KIND=%d); truncated"_warn_en_US,
kind);
return Expr<SomeType>{
- Expr<SomeKind<CAT>>{Expr<T>{Constant<T>{std::move(num.value)}}}};
+ MakeConstantExpr<T>(testKind, std::move(num.value))};
}
} else {
- if (T::kind > kind) {
+ if (testKind > kind) {
if (!isDefaultKind ||
!analyzer.context().IsEnabled(LanguageFeature::BigIntLiterals)) {
return std::nullopt;
@@ -764,11 +765,11 @@ template <typename TYPES, TypeCategory CAT> struct IntTypeVisitor {
analyzer.Warn(LanguageFeature::BigIntLiterals, digits,
"Integer literal is too large for default %s(KIND=%d); "
"assuming %s(KIND=%d)"_port_en_US,
- typeName, kind, typeName, T::kind);
+ typeName, kind, typeName, testKind);
}
}
return Expr<SomeType>{
- Expr<SomeKind<CAT>>{Expr<T>{Constant<T>{std::move(num.value)}}}};
+ MakeConstantExpr<T>(testKind, std::move(num.value))};
}
}
return std::nullopt;
@@ -789,7 +790,7 @@ MaybeExpr ExpressionAnalyzer::IntLiteralConstant(
const char *typeName{CAT == TypeCategory::Integer ? "INTEGER" : "UNSIGNED"};
if (CheckIntrinsicKind(CAT, kind)) {
auto digits{std::get<parser::CharBlock>(x.t)};
- if (MaybeExpr result{common::SearchTypes(IntTypeVisitor<TYPES, CAT>{
+ if (MaybeExpr result{SearchTypes(IntTypeVisitor<TYPES, CAT>{
*this, digits, kind, isDefaultKind, isNegated})}) {
return result;
} else if (isDefaultKind) {
@@ -830,21 +831,21 @@ MaybeExpr ExpressionAnalyzer::Analyze(
}
template <typename TYPE>
-Constant<TYPE> ReadRealLiteral(
- parser::CharBlock source, FoldingContext &context, bool isDefaultKind) {
+Constant<TYPE> ReadRealLiteral(int kind, parser::CharBlock source,
+ FoldingContext &context, bool isDefaultKind) {
const char *p{source.begin()};
- auto valWithFlags{
- Scalar<TYPE>::Read(p, context.targetCharacteristics().roundingMode())};
+ auto valWithFlags{Scalar<TYPE>::Read(
+ kind, p, context.targetCharacteristics().roundingMode())};
CHECK(p == source.end());
context.RealFlagWarnings(valWithFlags.flags, "conversion of REAL literal");
auto value{valWithFlags.value};
if (context.targetCharacteristics().areSubnormalsFlushedToZero()) {
value = value.FlushSubnormalToZero();
}
- typename Constant<TYPE>::Result resultInfo;
+ typename Constant<TYPE>::Result resultInfo{kind};
resultInfo.set_isFromInexactLiteralConversion(
isDefaultKind && valWithFlags.flags.test(RealFlag::Inexact));
- return {value, resultInfo};
+ return {kind, value, resultInfo};
}
struct RealTypeVisitor {
@@ -855,10 +856,10 @@ struct RealTypeVisitor {
int k, parser::CharBlock lit, FoldingContext &ctx, bool isDeftKind)
: kind{k}, literal{lit}, context{ctx}, isDefaultKind{isDeftKind} {}
- template <typename T> Result Test() {
- if (kind == T::kind) {
- return {
- AsCategoryExpr(ReadRealLiteral<T>(literal, context, isDefaultKind))};
+ template <typename T> Result Test(int testKind) {
+ if (kind == testKind) {
+ return {AsCategoryExpr(
+ ReadRealLiteral<T>(testKind, literal, context, isDefaultKind))};
}
return std::nullopt;
}
@@ -920,7 +921,7 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::RealLiteralConstant &x) {
}
}
bool isDefaultKind{!xkind && letterKind.value_or('e') == 'e'};
- auto result{common::SearchTypes(
+ auto result{SearchTypes(
RealTypeVisitor{kind, xreal.source, GetFoldingContext(), isDefaultKind})};
if (!result) { // C717
Say("Unsupported REAL(KIND=%d)"_err_en_US, kind);
@@ -970,17 +971,17 @@ MaybeExpr ExpressionAnalyzer::AnalyzeString(std::string &&string, int kind) {
}
switch (kind) {
case 1:
- return AsGenericExpr(Constant<Type<TypeCategory::Character, 1>>{
+ return AsGenericExpr(MakeConstant<Type<TypeCategory::Character>>(1,
parser::DecodeString<std::string, parser::Encoding::LATIN_1>(
- string, true)});
+ string, true)));
case 2:
- return AsGenericExpr(Constant<Type<TypeCategory::Character, 2>>{
+ return AsGenericExpr(MakeConstant<Type<TypeCategory::Character>>(2,
parser::DecodeString<std::u16string, parser::Encoding::UTF_8>(
- string, true)});
+ string, true)));
case 4:
- return AsGenericExpr(Constant<Type<TypeCategory::Character, 4>>{
+ return AsGenericExpr(MakeConstant<Type<TypeCategory::Character>>(4,
parser::DecodeString<std::u32string, parser::Encoding::UTF_8>(
- string, true)});
+ string, true)));
default:
CRASH_NO_CASE;
}
@@ -1008,9 +1009,9 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::LogicalLiteralConstant &x) {
auto kind{AnalyzeKindParam(std::get<std::optional<parser::KindParam>>(x.t),
GetDefaultKind(TypeCategory::Logical))};
bool value{std::get<bool>(x.t)};
- auto result{common::SearchTypes(
- TypeKindVisitor<TypeCategory::Logical, Constant, bool>{
- kind, std::move(value)})};
+ auto result{SearchTypes(
+ TypeKindVisitor<TypeCategory::Logical, Constant, value::LogicalValue>{
+ kind, value::LogicalValue{kind, value}})};
if (!result) {
Say("unsupported LOGICAL(KIND=%d)"_err_en_US, kind); // C728
}
@@ -1037,7 +1038,8 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::BOZLiteralConstant &x) {
}
CHECK(*p == '"');
++p;
- auto value{BOZLiteralConstant::Read(p, base, false /*unsigned*/)};
+ auto value{BOZLiteralConstant::Read(
+ BOZLiteralConstantKind, p, base, false /*unsigned*/)};
if (*p != '"') {
Say("Invalid digit ('%c') in BOZ literal '%s'"_err_en_US, *p,
x.v); // C7107, C7108
@@ -1072,7 +1074,7 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::Name &n) {
// while processing other specification expressions in the PDT
// definition; the right kind value will be used later in each of its
// instantiations.
- int kind{SubscriptInteger::kind};
+ int kind{SubscriptIntegerKind};
if (const auto *typeSpec{ultimate.GetType()}) {
if (const semantics::IntrinsicTypeSpec *
intrinType{typeSpec->AsIntrinsic()}) {
@@ -1179,12 +1181,13 @@ std::optional<Expr<SubscriptInteger>> ExpressionAnalyzer::GetSubstringBound(
Say("substring bound expression has rank %d"_err_en_US, expr->Rank());
}
if (auto *intExpr{std::get_if<Expr<SomeInteger>>(&expr->u)}) {
- if (auto *ssIntExpr{std::get_if<Expr<SubscriptInteger>>(&intExpr->u)}) {
+ if (auto *ssIntExpr{std::get_if<Expr<SubscriptInteger>>(&intExpr->u)};
+ ssIntExpr && ssIntExpr->kind() == SubscriptIntegerKind) {
return {std::move(*ssIntExpr)};
}
return {Expr<SubscriptInteger>{
Convert<SubscriptInteger, TypeCategory::Integer>{
- std::move(*intExpr)}}};
+ SubscriptIntegerKind, std::move(*intExpr)}}};
} else {
Say("substring bound expression is not INTEGER"_err_en_US);
}
@@ -1260,26 +1263,26 @@ MaybeExpr ExpressionAnalyzer::Analyze(
common::visit([](const auto &ckExpr) { return ckExpr.LEN().value(); },
charExpr->u)};
if (!lower) {
- lower = Expr<SubscriptInteger>{1};
+ lower = MakeSubscriptIntExpr(1);
}
if (!upper) {
- upper = Expr<SubscriptInteger>{
- static_cast<std::int64_t>(ToInt64(length).value())};
+ upper = MakeSubscriptIntExpr(ToInt64(length).value());
}
return common::visit(
[&](auto &&ckExpr) -> MaybeExpr {
using Result = ResultType<decltype(ckExpr)>;
+ const int resultKind{ckExpr.kind()};
auto *cp{std::get_if<Constant<Result>>(&ckExpr.u)};
CHECK(DEREF(cp).size() == 1);
StaticDataObject::Pointer staticData{StaticDataObject::Create()};
- staticData->set_alignment(Result::kind)
- .set_itemBytes(Result::kind)
+ staticData->set_alignment(resultKind)
+ .set_itemBytes(resultKind)
.Push(cp->GetScalarValue().value(),
foldingContext_.targetCharacteristics().isBigEndian());
Substring substring{std::move(staticData), std::move(lower.value()),
std::move(upper.value())};
- return AsGenericExpr(
- Expr<Result>{Designator<Result>{std::move(substring)}});
+ return AsGenericExpr(Expr<Result>{
+ Designator<Result>{resultKind, std::move(substring)}});
},
std::move(charExpr->u));
}
@@ -1311,12 +1314,13 @@ std::optional<Expr<SubscriptInteger>> ExpressionAnalyzer::AsSubscript(
expr->Rank());
}
if (auto *intExpr{std::get_if<Expr<SomeInteger>>(&expr->u)}) {
- if (auto *ssIntExpr{std::get_if<Expr<SubscriptInteger>>(&intExpr->u)}) {
+ if (auto *ssIntExpr{std::get_if<Expr<SubscriptInteger>>(&intExpr->u)};
+ ssIntExpr && ssIntExpr->kind() == SubscriptIntegerKind) {
return std::move(*ssIntExpr);
} else {
return Expr<SubscriptInteger>{
Convert<SubscriptInteger, TypeCategory::Integer>{
- std::move(*intExpr)}};
+ SubscriptIntegerKind, std::move(*intExpr)}};
}
} else {
Say("Subscript expression is not INTEGER"_err_en_US);
@@ -1549,7 +1553,7 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::StructureComponent &sc) {
? ComplexPart::Part::RE
: ComplexPart::Part::IM};
return AsCategoryExpr(Designator<PartType>{
- ComplexPart{std::move(*dataRef), part}});
+ z.kind(), ComplexPart{std::move(*dataRef), part}});
},
zExpr->u)};
return AsGenericExpr(std::move(realExpr));
@@ -1581,8 +1585,8 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::CoindexedNamedObject &x) {
std::get<std::list<parser::Cosubscript>>(selector.t)) {
MaybeExpr coex{Analyze(cosub)};
if (auto *intExpr{UnwrapExpr<Expr<SomeInteger>>(coex)}) {
- cosubscripts.push_back(
- ConvertToType<SubscriptInteger>(std::move(*intExpr)));
+ cosubscripts.push_back(ConvertToType<SubscriptInteger>(
+ SubscriptIntegerKind, std::move(*intExpr)));
} else {
cosubsOk = false;
}
@@ -1709,15 +1713,15 @@ class ArrayConstructorContext {
// expression in ToExpr().
using Result = MaybeExpr;
using Types = AllTypes;
- template <typename T> Result Test() {
+ template <typename T> Result Test(int kind) {
if (type_ && type_->category() == T::category) {
if constexpr (T::category == TypeCategory::Derived) {
if (!type_->IsUnlimitedPolymorphic()) {
return AsMaybeExpr(ArrayConstructor<T>{type_->GetDerivedTypeSpec(),
MakeSpecific<T>(std::move(values_))});
}
- } else if (type_->kind() == T::kind) {
- ArrayConstructor<T> result{MakeSpecific<T>(std::move(values_))};
+ } else if (type_->kind() == kind) {
+ ArrayConstructor<T> result{kind, MakeSpecific<T>(std::move(values_))};
if constexpr (T::category == TypeCategory::Character) {
if (auto len{LengthIfGood()}) {
// The ac-do-variables may be treated as constant expressions,
@@ -1744,6 +1748,7 @@ class ArrayConstructorContext {
private:
using ImpliedDoIntType = ResultType<ImpliedDoIndex>;
+ static constexpr int ImpliedDoIntKind = ResultKind<ImpliedDoIndex>;
std::optional<Expr<SubscriptInteger>> LengthIfGood() const {
if (type_) {
@@ -1769,23 +1774,20 @@ class ArrayConstructorContext {
parser::CharBlock name, std::int64_t lower, std::int64_t upper,
std::int64_t stride);
- template <int KIND>
- std::optional<Expr<Type<TypeCategory::Integer, KIND>>> ToSpecificInt(
- MaybeExpr &&y) {
+ std::optional<Expr<ImpliedDoIntType>> ToSpecificInt(MaybeExpr &&y) {
if (y) {
Expr<SomeInteger> *intExpr{UnwrapExpr<Expr<SomeInteger>>(*y)};
return Fold(exprAnalyzer_.GetFoldingContext(),
- ConvertToType<Type<TypeCategory::Integer, KIND>>(
- std::move(DEREF(intExpr))));
+ ConvertToType<ImpliedDoIntType>(
+ SubscriptIntegerKind, std::move(DEREF(intExpr))));
} else {
return std::nullopt;
}
}
- template <int KIND, typename A>
- std::optional<Expr<Type<TypeCategory::Integer, KIND>>> GetSpecificIntExpr(
- const A &x) {
- return ToSpecificInt<KIND>(exprAnalyzer_.Analyze(x));
+ template <typename A>
+ std::optional<Expr<ImpliedDoIntType>> GetSpecificIntExpr(const A &x) {
+ return ToSpecificInt(exprAnalyzer_.Analyze(x));
}
// Nested array constructors all reference the same ExpressionAnalyzer,
@@ -1953,13 +1955,12 @@ void ArrayConstructorContext::Add(const parser::AcValue::Triplet &triplet) {
if (strideType->kind() > kind) {
kind = strideType->kind();
}
- auto lower{ToSpecificInt<ImpliedDoIntType::kind>(std::move(lowerExpr))};
- auto upper{ToSpecificInt<ImpliedDoIntType::kind>(std::move(upperExpr))};
+ auto lower{ToSpecificInt(std::move(lowerExpr))};
+ auto upper{ToSpecificInt(std::move(upperExpr))};
if (lower && upper) {
- auto stride{
- ToSpecificInt<ImpliedDoIntType::kind>(std::move(strideExpr))};
+ auto stride{ToSpecificInt(std::move(strideExpr))};
if (!stride) {
- stride = Expr<ImpliedDoIntType>{1};
+ stride = MakeConstantExpr<ImpliedDoIntType>(ImpliedDoIntKind, 1);
}
DynamicType type{TypeCategory::Integer, kind};
if (!type_) {
@@ -1993,7 +1994,7 @@ void ArrayConstructorContext::Add(const parser::AcImpliedDo &impliedDo) {
exprAnalyzer_.Analyze(bounds.Name());
const auto &parsedName{parser::UnwrapRef<parser::Name>(bounds.Name())};
parser::CharBlock name{parsedName.source};
- int kind{ImpliedDoIntType::kind};
+ int kind{ImpliedDoIntKind};
if (const Symbol *symbol{parsedName.symbol}) {
if (auto dynamicType{DynamicType::From(symbol)}) {
if (dynamicType->category() == TypeCategory::Integer) {
@@ -2002,14 +2003,14 @@ void ArrayConstructorContext::Add(const parser::AcImpliedDo &impliedDo) {
}
}
std::optional<Expr<ImpliedDoIntType>> lower{
- GetSpecificIntExpr<ImpliedDoIntType::kind>(bounds.Lower())};
+ GetSpecificIntExpr(bounds.Lower())};
std::optional<Expr<ImpliedDoIntType>> upper{
- GetSpecificIntExpr<ImpliedDoIntType::kind>(bounds.Upper())};
+ GetSpecificIntExpr(bounds.Upper())};
if (lower && upper) {
std::optional<Expr<ImpliedDoIntType>> stride{
- GetSpecificIntExpr<ImpliedDoIntType::kind>(bounds.Step())};
+ GetSpecificIntExpr(bounds.Step())};
if (!stride) {
- stride = Expr<ImpliedDoIntType>{1};
+ stride = MakeConstantExpr<ImpliedDoIntType>(ImpliedDoIntKind, 1);
}
if (exprAnalyzer_.AddImpliedDo(name, kind)) {
// Check for constant bounds; the loop may require complete unrolling
@@ -2094,7 +2095,7 @@ void ArrayConstructorContext::UnrollConstantImpliedDo(
}
MaybeExpr ArrayConstructorContext::ToExpr() {
- return common::SearchTypes(std::move(*this));
+ return SearchTypes(std::move(*this));
}
MaybeExpr ExpressionAnalyzer::Analyze(const parser::ArrayConstructor &array) {
@@ -3661,8 +3662,7 @@ std::optional<Chevrons> ExpressionAnalyzer::AnalyzeChevrons(
return std::nullopt;
}
} else {
- result.emplace_back(
- AsGenericExpr(evaluate::Constant<evaluate::CInteger>{-1}));
+ result.emplace_back(AsGenericExpr(MakeCIntegerConstant(-1)));
}
if (auto expr{Analyze(std::get<1>(chevrons->t))};
expr && checkLaunchArg(*expr, "block")) {
@@ -4207,8 +4207,9 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::ConditionalExpr &x) {
return common::visit(
common::visitors{
[&](Expr<SomeDerived> &&elseVal) -> MaybeExpr {
- Expr<LogicalResult> cond{ConvertToType<LogicalResult>(
- std::move(std::get<Expr<SomeLogical>>(condExpr->u)))};
+ Expr<LogicalResult> cond{
+ ConvertToType<LogicalResult>(LogicalResultKind,
+ std::move(std::get<Expr<SomeLogical>>(condExpr->u)))};
Expr<SomeDerived> thenVal{
std::move(std::get<Expr<SomeDerived>>(thenExpr->u))};
return AsGenericExpr(
@@ -4228,6 +4229,7 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::ConditionalExpr &x) {
using T =
typename std::decay_t<decltype(elseKindExpr)>::Result;
Expr<LogicalResult> cond{ConvertToType<LogicalResult>(
+ LogicalResultKind,
std::move(std::get<Expr<SomeLogical>>(condExpr->u)))};
Expr<T> thenVal{std::move(std::get<Expr<T>>(
std::get<CategoryType>(thenExpr->u).u))};
@@ -4314,7 +4316,7 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::Concat &x) {
[&](auto &&x, auto &&y) -> MaybeExpr {
using T = ResultType<decltype(x)>;
if constexpr (std::is_same_v<T, ResultType<decltype(y)>>) {
- return AsGenericExpr(Concat<T::kind>{std::move(x), std::move(y)});
+ return AsGenericExpr(Concat{std::move(x), std::move(y)});
} else {
DIE("different types for intrinsic concat");
}
@@ -4719,7 +4721,7 @@ Expr<SubscriptInteger> ExpressionAnalyzer::AnalyzeKindSelector(
const std::optional<parser::KindSelector> &selector) {
int defaultKind{GetDefaultKind(category)};
if (!selector) {
- return Expr<SubscriptInteger>{defaultKind};
+ return MakeSubscriptIntExpr(defaultKind);
}
return common::visit(
common::visitors{
@@ -4727,13 +4729,14 @@ Expr<SubscriptInteger> ExpressionAnalyzer::AnalyzeKindSelector(
if (MaybeExpr kind{Analyze(x)}) {
if (std::optional<std::int64_t> code{ToInt64(*kind)}) {
if (CheckIntrinsicKind(category, *code)) {
- return Expr<SubscriptInteger>{*code};
+ return MakeSubscriptIntExpr(*code);
}
} else if (auto *intExpr{UnwrapExpr<Expr<SomeInteger>>(*kind)}) {
- return ConvertToType<SubscriptInteger>(std::move(*intExpr));
+ return ConvertToType<SubscriptInteger>(
+ SubscriptIntegerKind, std::move(*intExpr));
}
}
- return Expr<SubscriptInteger>{defaultKind};
+ return MakeSubscriptIntExpr(defaultKind);
},
[&](const parser::KindSelector::StarSize &x) {
std::intmax_t size = x.v;
@@ -4742,7 +4745,7 @@ Expr<SubscriptInteger> ExpressionAnalyzer::AnalyzeKindSelector(
} else if (category == TypeCategory::Complex) {
size /= 2;
}
- return Expr<SubscriptInteger>{size};
+ return MakeSubscriptIntExpr(size);
},
},
selector->u);
@@ -5728,11 +5731,11 @@ std::optional<ActualArgument> ArgumentAnalyzer::AnalyzeExpr(
// (or a smaller numeric type) by legacy code.
if (auto hollerith{UnwrapExpr<Constant<Ascii>>(*argExpr)};
hollerith && hollerith->wasHollerith()) {
- std::string bytes{hollerith->values()};
+ auto bytes{hollerith->values()};
while ((bytes.size() % 8) != 0) {
bytes += ' ';
}
- Constant<Ascii> c{std::move(bytes)};
+ Constant<Ascii> c{AsciiKind, std::move(bytes)};
c.set_wasHollerith(true);
argExpr = AsGenericExpr(std::move(c));
}
@@ -6019,7 +6022,7 @@ bool ExprChecker::Pre(const parser::DataImpliedDo &ido) {
parser::Walk(std::get<parser::DataImpliedDo::Bounds>(ido.t), *this);
const auto &bounds{std::get<parser::DataImpliedDo::Bounds>(ido.t)};
const auto &name{parser::UnwrapRef<parser::Name>(bounds.Name())};
- int kind{evaluate::ResultType<evaluate::ImpliedDoIndex>::kind};
+ int kind{evaluate::ResultKind<evaluate::ImpliedDoIndex>};
if (const auto dynamicType{evaluate::DynamicType::From(DEREF(name.symbol))}) {
if (dynamicType->category() == TypeCategory::Integer) {
kind = dynamicType->kind();
diff --git a/flang/lib/Semantics/openmp-utils.cpp b/flang/lib/Semantics/openmp-utils.cpp
index 731d98f1103a5..1b84ca1a5f111 100644
--- a/flang/lib/Semantics/openmp-utils.cpp
+++ b/flang/lib/Semantics/openmp-utils.cpp
@@ -1139,8 +1139,8 @@ struct ArrayExpressionRecognizer {
return common::visit([](auto &&s) { return isArrayExpression(s); }, x.u);
}
- template <TypeCategory C, int K>
- static bool isArrayExpression(const evaluate::Expr<evaluate::Type<C, K>> &x) {
+ template <TypeCategory C>
+ static bool isArrayExpression(const evaluate::Expr<evaluate::Type<C>> &x) {
return common::visit([](auto &&s) { return isArrayExpression(s); },
evaluate::match::deparen(x).u);
}
diff --git a/flang/lib/Semantics/pointer-assignment.cpp b/flang/lib/Semantics/pointer-assignment.cpp
index 7425f831b51b4..7152bb8342c05 100644
--- a/flang/lib/Semantics/pointer-assignment.cpp
+++ b/flang/lib/Semantics/pointer-assignment.cpp
@@ -533,11 +533,11 @@ static bool CheckPointerBounds(
},
[&](const evaluate::Assignment::BoundsRemapping &bounds) {
isBoundsRemapping = true;
- evaluate::ExtentExpr lhsSizeExpr{1};
+ evaluate::ExtentExpr lhsSizeExpr{evaluate::MakeExtentConstant(1)};
for (const auto &bound : bounds) {
lhsSizeExpr = std::move(lhsSizeExpr) *
(common::Clone(bound.second) - common::Clone(bound.first) +
- evaluate::ExtentExpr{1});
+ evaluate::MakeExtentExpr(1));
}
if (std::optional<std::int64_t> lhsSize{evaluate::ToInt64(
evaluate::Fold(context, std::move(lhsSizeExpr)))}) {
diff --git a/flang/lib/Semantics/resolve-names-utils.cpp b/flang/lib/Semantics/resolve-names-utils.cpp
index 6580c7e31abdb..deb11bdeb96fb 100644
--- a/flang/lib/Semantics/resolve-names-utils.cpp
+++ b/flang/lib/Semantics/resolve-names-utils.cpp
@@ -465,7 +465,7 @@ ArraySpecAnalyzer::CheckExplicitShapeBoundsSpec(
CHECK(someInt);
auto asSI{evaluate::Fold(context_.foldingContext(),
evaluate::ConvertToType<evaluate::SubscriptInteger>(
- common::Clone(*someInt)))};
+ evaluate::SubscriptIntegerKind, common::Clone(*someInt)))};
if (folded.Rank() == 0) {
// Scalar bound: broadcasts to every dimension.
return std::make_pair(Bound{MaybeSubscriptIntExpr{std::move(asSI)}},
@@ -599,7 +599,7 @@ void ArraySpecAnalyzer::Analyze(const parser::ExplicitShapeBoundsSpec &x) {
}
}
Bound lb{lbExpr ? std::move(lbExpr)
- : MaybeSubscriptIntExpr{SubscriptIntExpr{1}}};
+ : MaybeSubscriptIntExpr{evaluate::MakeSubscriptIntExpr(1)}};
Bound ub{std::move(ubExpr)};
arraySpec_.push_back(ShapeSpec::MakeExplicit(std::move(lb), std::move(ub)));
}
@@ -640,7 +640,7 @@ Bound ArraySpecAnalyzer::GetBound(const parser::SpecificationExpr &x) {
if (auto *intExpr{evaluate::UnwrapExpr<SomeIntExpr>(*maybeExpr)}) {
expr = evaluate::Fold(context_.foldingContext(),
evaluate::ConvertToType<evaluate::SubscriptInteger>(
- std::move(*intExpr)));
+ evaluate::SubscriptIntegerKind, std::move(*intExpr)));
}
}
return Bound{std::move(expr)};
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index 27c4e96d269aa..bd3a6222710b4 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -220,7 +220,7 @@ class BaseVisitor {
MaybeSubscriptIntExpr EvaluateSubscriptIntExpr(const T &expr) {
if (MaybeIntExpr maybeIntExpr{EvaluateIntExpr(expr)}) {
return FoldExpr(evaluate::ConvertToType<evaluate::SubscriptInteger>(
- std::move(*maybeIntExpr)));
+ evaluate::SubscriptIntegerKind, std::move(*maybeIntExpr)));
} else {
return std::nullopt;
}
@@ -2599,8 +2599,11 @@ void AttrsVisitor::SetBindNameOn(Symbol &symbol) {
return;
}
symbol.SetIsCDefined(isCDefined_);
- std::optional<std::string> label{
- evaluate::GetScalarConstantValue<evaluate::Ascii>(bindName_)};
+ std::optional<std::string> label;
+ if (auto charVal{
+ evaluate::GetScalarConstantValue<evaluate::Ascii>(bindName_)}) {
+ label = charVal->AsStdString();
+ }
// 18.9.2(2): discard leading and trailing blanks
if (label) {
symbol.SetIsExplicitBindName(true);
@@ -6215,7 +6218,7 @@ bool DeclarationVisitor::Pre(const parser::Enumerator &enumerator) {
// Enumerators are treated as PARAMETER (section 7.6 paragraph (4))
symbol = &MakeSymbol(name, Attrs{Attr::PARAMETER}, ObjectEntityDetails{});
symbol->SetType(context().MakeNumericType(
- TypeCategory::Integer, evaluate::CInteger::kind));
+ TypeCategory::Integer, evaluate::CIntegerKind));
}
if (auto &init{std::get<std::optional<parser::ScalarIntConstantExpr>>(
@@ -6226,7 +6229,7 @@ bool DeclarationVisitor::Pre(const parser::Enumerator &enumerator) {
// F2023 7.6.1 errata f23/013: a BOZ enumerator initializer
// has the value specified by INT(boz-literal-constant, C_INT).
const evaluate::DynamicType cIntType{
- TypeCategory::Integer, evaluate::CInteger::kind};
+ TypeCategory::Integer, evaluate::CIntegerKind};
if (MaybeExpr maybeExpr{EvaluateExpr(expr)}) {
if (auto converted{
evaluate::ConvertToType(cIntType, std::move(*maybeExpr))}) {
@@ -6252,8 +6255,8 @@ bool DeclarationVisitor::Pre(const parser::Enumerator &enumerator) {
if (symbol) {
if (enumerationState_.value) {
- symbol->get<ObjectEntityDetails>().set_init(SomeExpr{
- evaluate::Expr<evaluate::CInteger>{*enumerationState_.value}});
+ symbol->get<ObjectEntityDetails>().set_init(
+ SomeExpr{evaluate::MakeCIntegerExpr(*enumerationState_.value)});
} else {
context().SetError(*symbol);
}
@@ -6324,7 +6327,7 @@ void DeclarationVisitor::Post(const parser::EnumerationTypeStmt &x) {
Symbol &ordinalSym{MakeSymbol(currScope(), ordinalName, Attrs{})};
ordinalSym.set_details(ObjectEntityDetails{});
ordinalSym.SetType(
- currScope().MakeNumericType(TypeCategory::Integer, KindExpr{4}));
+ currScope().MakeNumericType(TypeCategory::Integer, MakeKindExpr(4)));
ordinalSym.set(Symbol::Flag::CompilerCreated);
symbol.get<DerivedTypeDetails>().add_component(ordinalSym);
}
@@ -6362,7 +6365,7 @@ bool DeclarationVisitor::Pre(const parser::EnumerationEnumeratorStmt &x) {
CHECK(ordinalIter != currScope().end());
const Symbol &ordinalSym{*ordinalIter->second};
enumCtor.Add(ordinalSym,
- evaluate::AsGenericExpr(evaluate::Expr<evaluate::CInteger>{ordinal}));
+ evaluate::AsGenericExpr(evaluate::MakeCIntegerExpr(ordinal)));
enumerator.get<ObjectEntityDetails>().set_init(
SomeExpr{evaluate::Expr<evaluate::SomeDerived>{
evaluate::Constant<evaluate::SomeDerived>{std::move(enumCtor)}}});
@@ -6759,7 +6762,7 @@ void DeclarationVisitor::Post(const parser::IntrinsicTypeSpec::Character &) {
}
if (!charInfo_.kind) {
charInfo_.kind =
- KindExpr{context().GetDefaultKind(TypeCategory::Character)};
+ MakeKindExpr(context().GetDefaultKind(TypeCategory::Character));
}
SetDeclTypeSpec(currScope().MakeCharacterType(
std::move(*charInfo_.length), std::move(*charInfo_.kind)));
@@ -9216,11 +9219,11 @@ const DeclTypeSpec &ConstructVisitor::ToDeclTypeSpec(
if (length) {
return currScope().MakeCharacterType(
ParamValue{SomeIntExpr{*std::move(length)}, common::TypeParamAttr::Len},
- KindExpr{type.kind()});
+ MakeKindExpr(type.kind()));
} else {
return currScope().MakeCharacterType(
ParamValue::Deferred(common::TypeParamAttr::Len),
- KindExpr{type.kind()});
+ MakeKindExpr(type.kind()));
}
}
@@ -9432,12 +9435,14 @@ class ExecutionPartAsyncIOSkimmer : public ExecutionPartSkimmerBase {
bool Pre(const parser::IoControlSpec::Asynchronous &async) {
if (auto folded{evaluate::Fold(
context_.foldingContext(), AnalyzeExpr(context_, async.v))}) {
- if (auto str{
+ if (auto charVal{
evaluate::GetScalarConstantValue<evaluate::Ascii>(*folded)}) {
- for (char ch : *str) {
- if (ch != ' ') {
- inAsyncIO_ = ch == 'y' || ch == 'Y';
- break;
+ if (auto str{charVal->AsStdString()}) {
+ for (char ch : *str) {
+ if (ch != ' ') {
+ inAsyncIO_ = ch == 'y' || ch == 'Y';
+ break;
+ }
}
}
}
diff --git a/flang/lib/Semantics/runtime-type-info.cpp b/flang/lib/Semantics/runtime-type-info.cpp
index 24f8f571439a3..ef56ce90ff0a7 100644
--- a/flang/lib/Semantics/runtime-type-info.cpp
+++ b/flang/lib/Semantics/runtime-type-info.cpp
@@ -296,7 +296,7 @@ static evaluate::StructureConstructorValues &AddValue(
}
static SomeExpr IntToExpr(std::int64_t n) {
- return evaluate::AsGenericExpr(evaluate::ExtentExpr{n});
+ return evaluate::AsGenericExpr(evaluate::MakeExtentExpr(n));
}
static evaluate::StructureConstructor Structure(
@@ -320,31 +320,31 @@ static int GetIntegerKind(const Symbol &symbol, bool canBeUninstantiated) {
// Save a rank-1 array constant of some numeric type as an
// initialized data object in a scope.
template <typename T>
-static SomeExpr SaveNumericPointerTarget(
- Scope &scope, SourceName name, std::vector<typename T::Scalar> &&x) {
+static SomeExpr SaveNumericPointerTarget(int kind, Scope &scope,
+ SourceName name, std::vector<typename T::Scalar> &&x) {
if (x.empty()) {
return SomeExpr{evaluate::NullPointer{}};
} else {
ObjectEntityDetails object;
if (const auto *spec{scope.FindType(
- DeclTypeSpec{NumericTypeSpec{T::category, KindExpr{T::kind}}})}) {
+ DeclTypeSpec{NumericTypeSpec{T::category, MakeKindExpr(kind)}})}) {
object.set_type(*spec);
} else {
- object.set_type(scope.MakeNumericType(T::category, KindExpr{T::kind}));
+ object.set_type(scope.MakeNumericType(T::category, MakeKindExpr(kind)));
}
auto elements{static_cast<evaluate::ConstantSubscript>(x.size())};
ArraySpec arraySpec;
arraySpec.push_back(ShapeSpec::MakeExplicit(Bound{0}, Bound{elements - 1}));
object.set_shape(arraySpec);
object.set_init(evaluate::AsGenericExpr(evaluate::Constant<T>{
- std::move(x), evaluate::ConstantSubscripts{elements}}));
+ kind, std::move(x), evaluate::ConstantSubscripts{elements}}));
Symbol &symbol{*scope
.try_emplace(name, Attrs{Attr::TARGET, Attr::SAVE},
std::move(object))
.first->second};
SetReadOnlyCompilerCreatedFlags(symbol);
return evaluate::AsGenericExpr(
- evaluate::Expr<T>{evaluate::Designator<T>{symbol}});
+ evaluate::Expr<T>{evaluate::Designator<T>{kind, symbol}});
}
}
@@ -357,12 +357,12 @@ static SomeExpr SaveObjectInit(
CHECK(symbol.get<ObjectEntityDetails>().init().has_value());
SetReadOnlyCompilerCreatedFlags(symbol);
return evaluate::AsGenericExpr(
- evaluate::Designator<evaluate::SomeDerived>{symbol});
+ evaluate::Designator<evaluate::SomeDerived>{0, symbol});
}
-template <int KIND> static SomeExpr IntExpr(std::int64_t n) {
+static SomeExpr IntExpr(int kind, std::int64_t n) {
return evaluate::AsGenericExpr(
- evaluate::Constant<evaluate::Type<TypeCategory::Integer, KIND>>{n});
+ evaluate::MakeConstant<evaluate::Type<TypeCategory::Integer>>(kind, n));
}
static std::optional<std::string> GetSuffixIfTypeKindParameters(
@@ -490,8 +490,8 @@ const Symbol *RuntimeTableBuilder::DescribeType(
AddValue(dtValues, derivedTypeSchema_, "uninstantiated"s,
SomeExpr{evaluate::NullPointer{}});
}
- using Int8 = evaluate::Type<TypeCategory::Integer, 8>;
- using Int1 = evaluate::Type<TypeCategory::Integer, 1>;
+ using Int8 = evaluate::Type<TypeCategory::Integer>;
+ using Int1 = evaluate::Type<TypeCategory::Integer>;
std::vector<Int8::Scalar> kinds;
std::vector<Int1::Scalar> lenKinds;
if (parameters) {
@@ -514,20 +514,20 @@ const Symbol *RuntimeTableBuilder::DescribeType(
}
}
}
- kinds.emplace_back(value);
+ kinds.emplace_back(8, value);
} else { // LEN= parameter
lenKinds.emplace_back(
- GetIntegerKind(*inst, isPDTDefinitionWithKindParameters));
+ 1, GetIntegerKind(*inst, isPDTDefinitionWithKindParameters));
}
}
}
}
AddValue(dtValues, derivedTypeSchema_, "kindparameter"s,
- SaveNumericPointerTarget<Int8>(scope,
+ SaveNumericPointerTarget<Int8>(8, scope,
SaveObjectName((fir::kKindParameterSeparator + distinctName).str()),
std::move(kinds)));
AddValue(dtValues, derivedTypeSchema_, "lenparameterkind"s,
- SaveNumericPointerTarget<Int1>(scope,
+ SaveNumericPointerTarget<Int1>(1, scope,
SaveObjectName((fir::kLenKindSeparator + distinctName).str()),
std::move(lenKinds)));
// Traverse the components of the derived type
@@ -645,27 +645,28 @@ const Symbol *RuntimeTableBuilder::DescribeType(
static_cast<evaluate::ConstantSubscript>(specials.size())}));
}
AddValue(dtValues, derivedTypeSchema_, "specialbitset"s,
- IntExpr<4>(specialBitSet));
+ IntExpr(4, specialBitSet));
// Note the presence/absence of a parent component
AddValue(dtValues, derivedTypeSchema_, "hasparent"s,
- IntExpr<1>(dtScope.GetDerivedTypeParent() != nullptr));
+ IntExpr(1, dtScope.GetDerivedTypeParent() != nullptr));
// To avoid wasting run time attempting to initialize derived type
// instances without any initialized components, analyze the type
// and set a flag if there's nothing to do for it at run time.
AddValue(dtValues, derivedTypeSchema_, "noinitializationneeded"s,
- IntExpr<1>(derivedTypeSpec &&
- !derivedTypeSpec->HasDefaultInitialization(false, false)));
+ IntExpr(1,
+ derivedTypeSpec &&
+ !derivedTypeSpec->HasDefaultInitialization(false, false)));
// Similarly, a flag to short-circuit destruction when not needed.
AddValue(dtValues, derivedTypeSchema_, "nodestructionneeded"s,
- IntExpr<1>(derivedTypeSpec && !derivedTypeSpec->HasDestruction()));
+ IntExpr(1, derivedTypeSpec && !derivedTypeSpec->HasDestruction()));
// Similarly, a flag to short-circuit finalization when not needed.
AddValue(dtValues, derivedTypeSchema_, "nofinalizationneeded"s,
- IntExpr<1>(
- derivedTypeSpec && !MayRequireFinalization(*derivedTypeSpec)));
+ IntExpr(
+ 1, derivedTypeSpec && !MayRequireFinalization(*derivedTypeSpec)));
// Similarly, a flag to enable optimized runtime assignment.
AddValue(dtValues, derivedTypeSchema_, "nodefinedassignment"s,
- IntExpr<1>(
- derivedTypeSpec && !MayHaveDefinedAssignment(*derivedTypeSpec)));
+ IntExpr(
+ 1, derivedTypeSpec && !MayHaveDefinedAssignment(*derivedTypeSpec)));
}
dtObject.get<ObjectEntityDetails>().set_init(MaybeExpr{
StructureExpr(Structure(derivedTypeSchema_, std::move(dtValues)))});
@@ -715,7 +716,7 @@ SomeExpr RuntimeTableBuilder::GetEnumValue(const char *name) const {
const Symbol &symbol{GetSchemaSymbol(name)};
auto value{evaluate::ToInt64(symbol.get<ObjectEntityDetails>().init())};
CHECK(value.has_value());
- return IntExpr<1>(*value);
+ return IntExpr(1, *value);
}
Symbol &RuntimeTableBuilder::CreateObject(
@@ -741,15 +742,16 @@ SomeExpr RuntimeTableBuilder::SaveNameAsPointerTarget(
ObjectEntityDetails object;
auto len{static_cast<common::ConstantSubscript>(name.size())};
if (const auto *spec{scope.FindType(DeclTypeSpec{CharacterTypeSpec{
- ParamValue{len, common::TypeParamAttr::Len}, KindExpr{1}}})}) {
+ ParamValue{len, common::TypeParamAttr::Len}, MakeKindExpr(1)}})}) {
object.set_type(*spec);
} else {
object.set_type(scope.MakeCharacterType(
- ParamValue{len, common::TypeParamAttr::Len}, KindExpr{1}));
+ ParamValue{len, common::TypeParamAttr::Len}, MakeKindExpr(1)));
}
- using evaluate::Ascii;
+ using evaluate::Ascii, evaluate::AsciiKind, evaluate::MakeConstantExpr;
using AsciiExpr = evaluate::Expr<Ascii>;
- object.set_init(evaluate::AsGenericExpr(AsciiExpr{name}));
+ object.set_init(
+ evaluate::AsGenericExpr(MakeConstantExpr<Ascii>(AsciiKind, name)));
Symbol &symbol{
*scope
.try_emplace(
@@ -758,7 +760,7 @@ SomeExpr RuntimeTableBuilder::SaveNameAsPointerTarget(
.first->second};
SetReadOnlyCompilerCreatedFlags(symbol);
return evaluate::AsGenericExpr(
- AsciiExpr{evaluate::Designator<Ascii>{symbol}});
+ AsciiExpr{evaluate::Designator<Ascii>{AsciiKind, symbol}});
}
evaluate::StructureConstructor RuntimeTableBuilder::DescribeComponent(
@@ -781,14 +783,14 @@ evaluate::StructureConstructor RuntimeTableBuilder::DescribeComponent(
AddValue(values, componentSchema_, "name"s,
SaveNameAsPointerTarget(scope, symbol.name().ToString()));
AddValue(values, componentSchema_, "category"s,
- IntExpr<1>(static_cast<int>(dyType.category())));
+ IntExpr(1, static_cast<int>(dyType.category())));
if (dyType.IsUnlimitedPolymorphic() ||
dyType.category() == TypeCategory::Derived) {
- AddValue(values, componentSchema_, "kind"s, IntExpr<1>(0));
+ AddValue(values, componentSchema_, "kind"s, IntExpr(1, 0));
} else {
- AddValue(values, componentSchema_, "kind"s, IntExpr<1>(dyType.kind()));
+ AddValue(values, componentSchema_, "kind"s, IntExpr(1, dyType.kind()));
}
- AddValue(values, componentSchema_, "offset"s, IntExpr<8>(symbol.offset()));
+ AddValue(values, componentSchema_, "offset"s, IntExpr(8, symbol.offset()));
// CHARACTER length
auto len{typeAndShape->LEN()};
if (const semantics::DerivedTypeSpec *
@@ -801,7 +803,7 @@ evaluate::StructureConstructor RuntimeTableBuilder::DescribeComponent(
if (const auto *clamped{evaluate::UnwrapExpr<
evaluate::Extremum<evaluate::SubscriptInteger>>(*len)}) {
if (clamped->ordering == evaluate::Ordering::Greater &&
- clamped->left() == evaluate::Expr<evaluate::SubscriptInteger>{0}) {
+ clamped->left() == evaluate::MakeSubscriptIntExpr(0)) {
len = common::Clone(clamped->right());
}
}
@@ -862,7 +864,7 @@ evaluate::StructureConstructor RuntimeTableBuilder::DescribeComponent(
SomeExpr{evaluate::NullPointer{}});
}
// Shape information
- AddValue(values, componentSchema_, "rank"s, IntExpr<1>(rank));
+ AddValue(values, componentSchema_, "rank"s, IntExpr(1, rank));
if (rank > 0 && !IsAllocatable(symbol) && !IsPointer(symbol)) {
std::vector<evaluate::StructureConstructor> bounds;
evaluate::NamedEntity entity{symbol};
@@ -927,7 +929,7 @@ evaluate::StructureConstructor RuntimeTableBuilder::DescribeComponent(
evaluate::StructureConstructorValues values;
AddValue(values, procPtrSchema_, "name"s,
SaveNameAsPointerTarget(scope, symbol.name().ToString()));
- AddValue(values, procPtrSchema_, "offset"s, IntExpr<8>(symbol.offset()));
+ AddValue(values, procPtrSchema_, "offset"s, IntExpr(8, symbol.offset()));
if (auto init{proc.init()}; init && *init) {
AddValue(values, procPtrSchema_, "initialization"s,
SomeExpr{evaluate::ProcedureDesignator{**init}});
@@ -1202,7 +1204,7 @@ void RuntimeTableBuilder::DescribeSpecialProc(
} else {
which = scalarFinalEnum_;
if (int rank{typeAndShape.Rank()}; rank > 0) {
- which = IntExpr<1>(ToInt64(which).value() + rank);
+ which = IntExpr(1, ToInt64(which).value() + rank);
if (dummyData.IsPassedByDescriptor(proc->IsBindC())) {
argThatMightBeDescriptor = 1;
}
@@ -1264,7 +1266,7 @@ void RuntimeTableBuilder::DescribeSpecialProc(
AddValue(
values, specialSchema_, "which"s, SomeExpr{std::move(which.value())});
AddValue(values, specialSchema_, "isargdescriptorset"s,
- IntExpr<1>(isArgDescriptorSet));
+ IntExpr(1, isArgDescriptorSet));
int bindingIndex{0};
if (bindings) {
int j{0};
@@ -1277,9 +1279,9 @@ void RuntimeTableBuilder::DescribeSpecialProc(
}
}
CHECK(bindingIndex <= 255);
- AddValue(values, specialSchema_, "istypebound"s, IntExpr<1>(bindingIndex));
+ AddValue(values, specialSchema_, "istypebound"s, IntExpr(1, bindingIndex));
AddValue(values, specialSchema_, "specialcaseflag"s,
- IntExpr<1>(specialCaseFlag));
+ IntExpr(1, specialCaseFlag));
AddValue(values, specialSchema_, procCompName,
SomeExpr{evaluate::ProcedureDesignator{specific}});
// index might already be present in the case of an override
diff --git a/flang/lib/Semantics/scope.cpp b/flang/lib/Semantics/scope.cpp
index c000bb9038cf5..7c9e2286c797b 100644
--- a/flang/lib/Semantics/scope.cpp
+++ b/flang/lib/Semantics/scope.cpp
@@ -257,11 +257,12 @@ const DeclTypeSpec *Scope::GetType(const SomeExpr &expr) {
case TypeCategory::Unsigned:
case TypeCategory::Real:
case TypeCategory::Complex:
- return &MakeNumericType(dyType->category(), KindExpr{dyType->kind()});
+ return &MakeNumericType(
+ dyType->category(), MakeKindExpr(dyType->kind()));
case TypeCategory::Character:
if (const ParamValue * lenParam{dyType->charLengthParamValue()}) {
return &MakeCharacterType(
- ParamValue{*lenParam}, KindExpr{dyType->kind()});
+ ParamValue{*lenParam}, MakeKindExpr(dyType->kind()));
} else {
auto lenExpr{dyType->GetCharLength()};
if (!lenExpr) {
@@ -272,12 +273,12 @@ const DeclTypeSpec *Scope::GetType(const SomeExpr &expr) {
return &MakeCharacterType(
ParamValue{SomeIntExpr{std::move(*lenExpr)},
common::TypeParamAttr::Len},
- KindExpr{dyType->kind()});
+ MakeKindExpr(dyType->kind()));
}
}
break;
case TypeCategory::Logical:
- return &MakeLogicalType(KindExpr{dyType->kind()});
+ return &MakeLogicalType(MakeKindExpr(dyType->kind()));
case TypeCategory::Derived:
return &MakeDerivedType(dyType->IsPolymorphic()
? DeclTypeSpec::ClassDerived
diff --git a/flang/lib/Semantics/semantics.cpp b/flang/lib/Semantics/semantics.cpp
index 33c54c81f8abd..1ccbf79750be2 100644
--- a/flang/lib/Semantics/semantics.cpp
+++ b/flang/lib/Semantics/semantics.cpp
@@ -406,13 +406,13 @@ const DeclTypeSpec &SemanticsContext::MakeNumericType(
if (kind == 0) {
kind = GetDefaultKind(category);
}
- return globalScope_.MakeNumericType(category, KindExpr{kind});
+ return globalScope_.MakeNumericType(category, MakeKindExpr(kind));
}
const DeclTypeSpec &SemanticsContext::MakeLogicalType(int kind) {
if (kind == 0) {
kind = GetDefaultKind(TypeCategory::Logical);
}
- return globalScope_.MakeLogicalType(KindExpr{kind});
+ return globalScope_.MakeLogicalType(MakeKindExpr(kind));
}
bool SemanticsContext::AnyFatalError() const {
diff --git a/flang/lib/Semantics/type.cpp b/flang/lib/Semantics/type.cpp
index 678bae83ba68e..d8bafc2d26aa6 100644
--- a/flang/lib/Semantics/type.cpp
+++ b/flang/lib/Semantics/type.cpp
@@ -23,6 +23,8 @@
namespace Fortran::semantics {
+KindExpr MakeKindExpr(int v) { return evaluate::MakeSubscriptIntExpr(v); }
+
DerivedTypeSpec::DerivedTypeSpec(SourceName name, const Symbol &typeSymbol)
: name_{name}, originalTypeSymbol_{typeSymbol},
typeSymbol_{typeSymbol.GetUltimate()} {
@@ -114,7 +116,7 @@ void DerivedTypeSpec::EvaluateParameters(SemanticsContext &context) {
auto &messages{foldingContext.messages()};
for (const Symbol &symbol : OrderParameterDeclarations(typeSymbol_)) {
SourceName name{symbol.name()};
- int parameterKind{evaluate::TypeParamInquiry::Result::kind};
+ int parameterKind{evaluate::TypeParamInquiry::ResultKind};
// Compute the integer kind value of the type parameter,
// which may depend on the values of earlier ones.
if (const auto *typeSpec{symbol.GetType()}) {
@@ -436,7 +438,7 @@ void DerivedTypeSpec::Instantiate(Scope &containingScope) {
std::move(DEREF(evaluate::UnwrapExpr<SomeIntExpr>(*expr))));
if (auto dyType{expr->GetType()}) {
instanceDetails.set_type(newScope.MakeNumericType(
- TypeCategory::Integer, KindExpr{dyType->kind()}));
+ TypeCategory::Integer, MakeKindExpr(dyType->kind())));
}
}
if (!instanceDetails.type()) {
@@ -661,7 +663,7 @@ const DeclTypeSpec &InstantiateHelper::InstantiateIntrinsicType(
if (MaybeExpr analyzed{AnalyzeExpr(scope_.context(), *originalKindExpr)}) {
if (auto *intExpr{evaluate::UnwrapExpr<SomeIntExpr>(*analyzed)}) {
kindExpr = evaluate::ConvertToType<evaluate::SubscriptInteger>(
- std::move(*intExpr));
+ evaluate::SubscriptIntegerKind, std::move(*intExpr));
}
}
}
@@ -692,13 +694,13 @@ const DeclTypeSpec &InstantiateHelper::InstantiateIntrinsicType(
}
switch (spec.category()) {
case DeclTypeSpec::Numeric:
- return scope_.MakeNumericType(intrinsic.category(), KindExpr{kind});
+ return scope_.MakeNumericType(intrinsic.category(), MakeKindExpr(kind));
case DeclTypeSpec::Logical:
- return scope_.MakeLogicalType(KindExpr{kind});
+ return scope_.MakeLogicalType(MakeKindExpr(kind));
case DeclTypeSpec::Character:
return scope_.MakeCharacterType(
FoldCharacterLength(foldingContext(), spec.characterTypeSpec()),
- KindExpr{kind});
+ MakeKindExpr(kind));
default:
CRASH_NO_CASE;
}
@@ -831,7 +833,8 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &o, const DerivedTypeSpec &x) {
return o << x.AsFortran();
}
-Bound::Bound(common::ConstantSubscript bound) : expr_{bound} {}
+Bound::Bound(common::ConstantSubscript bound)
+ : expr_{evaluate::MakeSubscriptIntExpr(bound)} {}
llvm::raw_ostream &operator<<(llvm::raw_ostream &o, const Bound &x) {
if (x.isStar()) {
@@ -881,8 +884,7 @@ ParamValue::ParamValue(SomeIntExpr &&expr, common::TypeParamAttr attr)
: attr_{attr}, expr_{std::move(expr)} {}
ParamValue::ParamValue(
common::ConstantSubscript value, common::TypeParamAttr attr)
- : ParamValue(SomeIntExpr{evaluate::Expr<evaluate::SubscriptInteger>{value}},
- attr) {}
+ : ParamValue(SomeIntExpr{evaluate::MakeSubscriptIntExpr(value)}, attr) {}
void ParamValue::SetExplicit(SomeIntExpr &&x) {
category_ = Category::Explicit;
diff --git a/flang/test/Evaluate/fold-ibits.f90 b/flang/test/Evaluate/fold-ibits.f90
index b49fce9f7af96..69052b0bcd588 100644
--- a/flang/test/Evaluate/fold-ibits.f90
+++ b/flang/test/Evaluate/fold-ibits.f90
@@ -11,3 +11,32 @@ module m1
integer, parameter :: expect3(*) = [((iand(shiftr(mess,pos),maskr(len)),len=0,31-pos),pos=0,31)]
logical, parameter :: test3 = all(res3 == expect3)
end module
+
+! IBITS must be folded at the kind of its first argument. Folding it at the
+! default integer kind truncates arguments of a wider kind.
+module m2
+ implicit integer(a-z)
+ integer(1), parameter :: mess1 = int(z'5a', 1)
+ integer(2), parameter :: mess2 = int(z'5a5a', 2)
+ integer(8), parameter :: mess8 = int(z'5a5a5a5a5a5a5a5a', 8)
+ integer(16), parameter :: mess16 = int(z'5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a', 16)
+ logical, parameter :: test_kind1 = all( &
+ [((ibits(mess1,pos,len),len=0,7-pos),pos=0,7)] == &
+ [((iand(shiftr(mess1,pos),maskr(len,1)),len=0,7-pos),pos=0,7)])
+ logical, parameter :: test_kind2 = all( &
+ [((ibits(mess2,pos,len),len=0,15-pos),pos=0,15)] == &
+ [((iand(shiftr(mess2,pos),maskr(len,2)),len=0,15-pos),pos=0,15)])
+ logical, parameter :: test_kind8 = all( &
+ [((ibits(mess8,pos,len),len=0,63-pos),pos=0,63)] == &
+ [((iand(shiftr(mess8,pos),maskr(len,8)),len=0,63-pos),pos=0,63)])
+ logical, parameter :: test_kind16 = all( &
+ [((ibits(mess16,pos,len),len=0,127-pos),pos=0,127)] == &
+ [((iand(shiftr(mess16,pos),maskr(len,16)),len=0,127-pos),pos=0,127)])
+ ! Bit fields that extend past bit 31 must survive.
+ logical, parameter :: test_wide8 = ibits(1234567890123_8, 0, 40) == 135056262347_8
+ logical, parameter :: test_wide16 = &
+ ibits(1234567890123456789_16, 8, 120) == 4822530820794753_16
+ ! Folding must not narrow the first argument, which would overflow.
+ integer(8), parameter :: nonarrow8 = ibits(-1_8, 0, 64)
+ logical, parameter :: test_nonarrow8 = nonarrow8 == -1_8
+end module
diff --git a/flang/test/Evaluate/fold-real-storage-size.f90 b/flang/test/Evaluate/fold-real-storage-size.f90
new file mode 100644
index 0000000000000..460e5a6beea11
--- /dev/null
+++ b/flang/test/Evaluate/fold-real-storage-size.f90
@@ -0,0 +1,24 @@
+! RUN: %flang_fc1 -fdebug-unparse %s 2>&1 | FileCheck %s
+
+! REAL(3) is bfloat16: its raw bits occupy 2 bytes, not 3. Constant
+! initialization and TRANSFER round-trip values through their raw bytes, so
+! the number of bytes stored must not be conflated with the kind number.
+! See fold-real10-storage-size.f90 for the REAL(10) counterpart.
+
+subroutine data_init
+ real(3) :: x
+ data x/1.5_3/
+ print *, x
+end subroutine
+! CHECK-LABEL: SUBROUTINE data_init
+! CHECK: DATA x/1.5_3/
+
+subroutine transfers
+ print *, transfer(1.5_3, 0.0_3)
+ print *, transfer(1.5_3, 0_2)
+ print *, transfer(16320_2, 0.0_3)
+end subroutine
+! CHECK-LABEL: SUBROUTINE transfers
+! CHECK: PRINT *, 1.5_3
+! CHECK: PRINT *, 16320_2
+! CHECK: PRINT *, 1.5_3
diff --git a/flang/test/Evaluate/fold-real10-storage-size.f90 b/flang/test/Evaluate/fold-real10-storage-size.f90
new file mode 100644
index 0000000000000..6def77bd9fa06
--- /dev/null
+++ b/flang/test/Evaluate/fold-real10-storage-size.f90
@@ -0,0 +1,41 @@
+! RUN: %flang_fc1 -fdebug-unparse %s 2>&1 | FileCheck %s
+! REQUIRES: target=x86_64{{.*}}
+
+! REAL(10) is the x87 extended format: 80 significant bits held in a 16-byte
+! container, so its raw bits occupy 16 bytes rather than 10. Constant
+! initialization, TRANSFER and host-library folding all round-trip values
+! through their raw bytes, so the number of bytes stored must not be
+! conflated with the kind number.
+
+subroutine data_init
+ real(10) :: x
+ complex(10) :: y
+ data x/1.5_10/
+ data y/(1.5_10, 2.5_10)/
+ print *, x, y
+end subroutine
+! CHECK-LABEL: SUBROUTINE data_init
+! CHECK: DATA x/1.5_10/
+! CHECK: DATA y/(1.5_10,2.5_10)/
+
+subroutine transfers
+ ! A bit pattern of 1 reinterpreted as REAL(10) is the smallest subnormal,
+ ! so the low-order bytes must survive the round trip.
+ print *, transfer(1_8, 0.0_10)
+ print *, transfer(1.5_10, 0.0_10)
+ print *, transfer(1.5_10, 0_2, 8)
+end subroutine
+! CHECK-LABEL: SUBROUTINE transfers
+! CHECK: PRINT *, {{.*}}e-4951_10
+! CHECK: PRINT *, 1.5_10
+! CHECK: PRINT *, [INTEGER(2)::0_2,0_2,0_2,-16384_2,16383_2,0_2,0_2,0_2]
+
+subroutine host_folding
+ ! Folding these casts to and from the host long double, whose size differs
+ ! from the number of significant bytes.
+ real(10), parameter :: s = sin(1.0_10)
+ real(10), parameter :: e = exp(1.0_10)
+ print *, s, e
+end subroutine
+! CHECK-LABEL: SUBROUTINE host_folding
+! CHECK: PRINT *, 8.414709848078965048756572286947630345821380615234375e-1_10, 2.718281828459045090795598298427648842334747314453125_10
diff --git a/flang/test/Evaluate/fold-transfer-partial.f90 b/flang/test/Evaluate/fold-transfer-partial.f90
new file mode 100644
index 0000000000000..4e01635641c71
--- /dev/null
+++ b/flang/test/Evaluate/fold-transfer-partial.f90
@@ -0,0 +1,71 @@
+! RUN: %python %S/test_folding.py %s %flang_fc1
+! Tests folding of TRANSFER(...) when the physical representation of the
+! result is longer than that of SOURCE. F2023 16.9.212 p.5 requires the
+! leading part of the result's physical representation to be that of
+! SOURCE, and requires TRANSFER(TRANSFER(E, D), E) to have the value of E
+! for scalar D and E (and likewise TRANSFER(TRANSFER(E, D), E, SIZE(E))
+! when D is an array and E has rank one); the Examples paragraph's
+! Case (ii) shows a trailing array element only partially covered by
+! SOURCE. The remainder of the result beyond SOURCE's representation is
+! processor dependent; flang zero-fills it (as already pinned for
+! CHARACTER by fold-transfer.f90's test_i2c_s).
+! Same-size and mold-shorter values are covered by fold-transfer.f90;
+! this file pins the mold-longer cases. All checks are byte-order
+! independent: the round trips prove the leading-part byte placement,
+! and the two-endian .or. checks (idiom precedent: fold-transfer.f90's
+! test_c2i_s) are portable value/zero-fill pins, not placement proofs.
+
+module m
+ ! Scalar MOLD longer than SOURCE: round trips (16.9.212 p.5), ...
+ logical, parameter :: test_rt_scalar = transfer(transfer(1_4, 0_8), 0_4) == 1_4
+ logical, parameter :: test_rt_neg = transfer(transfer(-1_4, 0_8), 0_4) == -1_4
+ logical, parameter :: test_rt_real = transfer(transfer(1.5, 0._8), 0.0) == 1.5
+ ! ... and a portable leading-part + zero-fill value pin (either
+ ! byte order's correct value; placement is proven by the round trips)
+ integer(8), parameter :: w1 = transfer(1_4, 0_8)
+ logical, parameter :: test_lead_zfill = w1 == 1_8 .or. w1 == 4294967296_8
+
+ ! Rank-one results whose trailing element is only partially covered
+ ! by SOURCE, with and without SIZE=
+ integer(8), parameter :: via8(2) = transfer([1_4, 2_4, 3_4], 0_8, 2)
+ logical, parameter :: test_rt_array = all(transfer(via8, 0_4, 3) == [1_4, 2_4, 3_4])
+ logical, parameter :: test_elem2_zfill = via8(2) == 3_8 .or. via8(2) == 12884901888_8
+ integer(8), parameter :: via8b(*) = transfer([1_4, 2_4, 3_4], [0_8])
+ logical, parameter :: test_rt_array2 = all(transfer(via8b, 0_4, 3) == [1_4, 2_4, 3_4])
+ real(8), parameter :: rvia8(2) = transfer([1.5, 2.5, 3.5], 0._8, 2)
+ logical, parameter :: test_rt_real_arr = all(transfer(rvia8, 0.0, 3) == [1.5, 2.5, 3.5])
+
+ ! The standard's own Case (ii) example (16.9.212 p.6): the second
+ ! element's real part has the value 3.3; its imaginary part is
+ ! processor dependent
+ complex, parameter :: cx(2) = transfer([1.1, 2.2, 3.3], [(0.0, 0.0)])
+ logical, parameter :: test_case_ii = cx(1) == (1.1, 2.2) .and. real(cx(2)) == 3.3
+
+ ! Derived-type MOLD longer than SOURCE: the leading part is preserved
+ ! (observed portably via round trips); components at or beyond the end
+ ! of SOURCE's representation are zero-filled
+ type t1
+ integer(8) :: a, b
+ end type
+ type(t1), parameter :: x1 = transfer([1_4, 2_4, 3_4], t1(0, 0)) ! b partially covered
+ logical, parameter :: test_derived_rt = all(transfer(x1, 0_4, 3) == [1_4, 2_4, 3_4])
+ type(t1), parameter :: x2 = transfer(7_4, t1(-1, -1)) ! a partial, b wholly beyond
+ logical, parameter :: test_derived_lead = transfer(x2, 0_4) == 7_4
+ logical, parameter :: test_derived_zero = x2%b == 0_8
+ type(t1), parameter :: x4 = transfer(1_8, t1(-1, -1)) ! b exactly at the end
+ logical, parameter :: test_at_end = x4%a == 1_8 .and. x4%b == 0_8
+ type t2
+ integer(4) :: x
+ integer(4) :: y ! keeps c beyond a 4-byte SOURCE even where integer(8) has 4-byte alignment
+ integer(8) :: c(4) ! wholly beyond SOURCE's representation
+ end type
+ type(t2), parameter :: x3 = transfer(9_4, t2(0, 0, [0_8, 0_8, 0_8, 0_8]))
+ logical, parameter :: test_beyond = x3%x == 9_4 .and. x3%y == 0_4 .and. all(x3%c == 0_8)
+
+ ! CHARACTER MOLD with elements beyond SOURCE: NUL fill
+ character(1), parameter :: ch(50) = transfer(1_8, 'x', 50)
+ logical, parameter :: test_char_rt = transfer(ch(1:8), 0_8) == 1_8
+ logical, parameter :: test_char_zero = ichar(ch(9)) == 0 .and. ichar(ch(50)) == 0
+ character(8), parameter :: c8 = transfer('AB', 'xxxxxxxx')
+ logical, parameter :: test_char_scalar = c8(1:2) == 'AB' .and. ichar(c8(3:3)) == 0 .and. ichar(c8(8:8)) == 0
+end module
diff --git a/flang/test/Lower/constant-literal-kinds.f90 b/flang/test/Lower/constant-literal-kinds.f90
new file mode 100644
index 0000000000000..76498b91ce9df
--- /dev/null
+++ b/flang/test/Lower/constant-literal-kinds.f90
@@ -0,0 +1,63 @@
+! RUN: bbc -emit-fir -o - %s | FileCheck %s
+
+! Constant array literals are hoisted into globals that are shared between
+! equivalent literals. Every constant expression hashes to the same bucket,
+! so the equality predicate in Fortran::lower::isEqual() is what keeps them
+! apart: literals that differ only in the kind of their elements must not be
+! given the same global, or the global would be emitted with the element type
+! of whichever literal was lowered first.
+
+subroutine integer_kinds
+ interface
+ subroutine i1(x)
+ integer(1) :: x(3)
+ end subroutine
+ subroutine i2(x)
+ integer(2) :: x(3)
+ end subroutine
+ subroutine i4(x)
+ integer(4) :: x(3)
+ end subroutine
+ subroutine i8(x)
+ integer(8) :: x(3)
+ end subroutine
+ end interface
+ call i1([1_1, 2_1, 3_1])
+ call i2([1_2, 2_2, 3_2])
+ call i4([1_4, 2_4, 3_4])
+ call i8([1_8, 2_8, 3_8])
+end subroutine
+! CHECK-DAG: fir.global internal @_QQro.3xi1.{{[0-9]+}}(dense<[1, 2, 3]> : tensor<3xi8>) {{.*}} : !fir.array<3xi8>
+! CHECK-DAG: fir.global internal @_QQro.3xi2.{{[0-9]+}}(dense<[1, 2, 3]> : tensor<3xi16>) {{.*}} : !fir.array<3xi16>
+! CHECK-DAG: fir.global internal @_QQro.3xi4.{{[0-9]+}}(dense<[1, 2, 3]> : tensor<3xi32>) {{.*}} : !fir.array<3xi32>
+! CHECK-DAG: fir.global internal @_QQro.3xi8.{{[0-9]+}}(dense<[1, 2, 3]> : tensor<3xi64>) {{.*}} : !fir.array<3xi64>
+
+subroutine real_kinds
+ interface
+ subroutine r4(x)
+ real(4) :: x(3)
+ end subroutine
+ subroutine r8(x)
+ real(8) :: x(3)
+ end subroutine
+ end interface
+ call r4([1.0_4, 2.0_4, 3.0_4])
+ call r8([1.0_8, 2.0_8, 3.0_8])
+end subroutine
+! CHECK-DAG: fir.global internal @_QQro.3xr4.{{[0-9]+}}({{.*}} : tensor<3xf32>) {{.*}} : !fir.array<3xf32>
+! CHECK-DAG: fir.global internal @_QQro.3xr8.{{[0-9]+}}({{.*}} : tensor<3xf64>) {{.*}} : !fir.array<3xf64>
+
+subroutine logical_kinds
+ interface
+ subroutine l1(x)
+ logical(1) :: x(2)
+ end subroutine
+ subroutine l4(x)
+ logical(4) :: x(2)
+ end subroutine
+ end interface
+ call l1([.true._1, .false._1])
+ call l4([.true._4, .false._4])
+end subroutine
+! CHECK-DAG: fir.global internal @_QQro.2xl1.{{[0-9]+}}({{.*}} : tensor<2xi8>) {{.*}} : !fir.array<2x!fir.logical<1>>
+! CHECK-DAG: fir.global internal @_QQro.2xl4.{{[0-9]+}}({{.*}} : tensor<2xi32>) {{.*}} : !fir.array<2x!fir.logical<4>>
diff --git a/flang/tools/CMakeLists.txt b/flang/tools/CMakeLists.txt
index 975eaa29343fc..9f919ffe6e5d3 100644
--- a/flang/tools/CMakeLists.txt
+++ b/flang/tools/CMakeLists.txt
@@ -16,3 +16,4 @@ add_subdirectory(tco)
add_subdirectory(f18-parse-demo)
add_subdirectory(fir-opt)
add_subdirectory(fir-lsp-server)
+add_subdirectory(object-size-probe)
diff --git a/flang/tools/object-size-probe/CMakeLists.txt b/flang/tools/object-size-probe/CMakeLists.txt
new file mode 100644
index 0000000000000..6203eafa2ff91
--- /dev/null
+++ b/flang/tools/object-size-probe/CMakeLists.txt
@@ -0,0 +1,42 @@
+#===-- tools/object-size-probe/CMakeLists.txt ------------------------------===#
+#
+# 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
+#
+#===------------------------------------------------------------------------===#
+
+if (CMAKE_CROSSCOMPILING)
+ # Cannot execute the probe when cross-compiling
+ # Unlike tblgen, building it separately is no solution because we need the
+ # sizeof(...)/alignof(...) to be processed for the target, not the host.
+ # include/flang/Evaluate/object-sizes.h contains backup values when the
+ # generated header does not exist
+ return ()
+endif ()
+
+
+set(LLVM_LINK_COMPONENTS
+ Support
+ )
+
+add_llvm_executable(flang-object-size-probe object-size-probe.cpp)
+target_include_directories(flang-object-size-probe PRIVATE
+ "${FLANG_SOURCE_DIR}/lib/Evaluate"
+ )
+
+set(_object_sizes_dir "${FLANG_BINARY_DIR}/include/object-sizes/$<CONFIG>/flang/Evaluate")
+set(_object_sizes_h "${_object_sizes_dir}/object-sizes-generated.h")
+
+add_custom_command(
+ OUTPUT "${_object_sizes_h}"
+ COMMAND "${CMAKE_COMMAND}" -E make_directory "${_object_sizes_dir}"
+ COMMAND "$<TARGET_FILE:flang-object-size-probe>" "${_object_sizes_h}"
+ DEPENDS flang-object-size-probe
+ COMMENT "Deducing IntegerValueImpl/RealValueImpl/CharacterValueImpl object size and alignment ($<CONFIG>)"
+ VERBATIM)
+add_custom_target(flang-generated-object-sizes DEPENDS "${_object_sizes_h}")
+
+add_dependencies(FortranEvaluate flang-generated-object-sizes)
+add_dependencies(FortranSemantics flang-generated-object-sizes)
+add_dependencies(FortranLower flang-generated-object-sizes)
diff --git a/flang/tools/object-size-probe/object-size-probe.cpp b/flang/tools/object-size-probe/object-size-probe.cpp
new file mode 100644
index 0000000000000..58f485b423d4b
--- /dev/null
+++ b/flang/tools/object-size-probe/object-size-probe.cpp
@@ -0,0 +1,99 @@
+//===-- tools/object-size-probe/object-size-probe.cpp -----------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Automatic deduction of the opaque object size/alignment used by the
+// IntegerValue, RealValue, and CharacterValue facades (integer-value.h,
+// real-value.h, character-value.h).
+//
+// These are similar to the pImpl-idiom, except that instead of the facade
+// storing a pointer to the implementation-object (IntegerValueImpl,
+// RealValueImpl, CharacterValueImpl), it is reinterpret-casted over the facade
+// object. This requires both to have the same object sizes. A `sizeof(*Impl)`
+// would defeat the purpose of hiding the implementation. Instead, we probe the
+// object size at build time.
+//
+// This program is compiled and executed to generate a header file containing
+// sizes of the implementation objects.
+//
+//===----------------------------------------------------------------------===//
+
+#define FLANG_OBJECT_SIZE_PROBE
+
+#include "character-value-impl.h"
+#include "integer-value-impl.h"
+#include "real-value-impl.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Support/raw_ostream.h"
+#include <cstddef>
+#include <cstdlib>
+
+using Fortran::evaluate::value::CharacterValueImpl;
+using Fortran::evaluate::value::IntegerValueImpl;
+using Fortran::evaluate::value::RealValueImpl;
+
+int main(int argc, char **argv) {
+ if (argc != 2) {
+ llvm::errs() << "usage: " << argv[0] << " <object-sizes-generated.h>\n";
+ return EXIT_FAILURE;
+ }
+
+ std::error_code ec;
+ llvm::ToolOutputFile out(argv[1], ec, llvm::sys::fs::OF_Text);
+ if (ec) {
+ llvm::errs() << "object-size-probe: cannot open " << argv[1]
+ << " for writing: " << ec.message() << '\n';
+ return EXIT_FAILURE;
+ }
+
+ out.os() << llvm::format(
+ R"(
+//===-- object-sizes-generated.h --------------------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Generated at build time by flang-object-size-probe.
+// Do not edit; edit flang/tools/object-size-probe/object-size-probe.cpp instead.
+// Included by flang/Evaluate/object-sizes.h when present on the path.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef FORTRAN_EVALUATE_OBJECT_SIZES_GENERATED_H_
+#define FORTRAN_EVALUATE_OBJECT_SIZES_GENERATED_H_
+
+#include <cstddef>
+
+namespace Fortran::evaluate::value::detail {
+
+// Object size/alignment for IntegerValue / IntegerValueImpl
+inline constexpr std::size_t kIntegerObjectSize{%zu};
+inline constexpr std::size_t kIntegerObjectAlign{%zu};
+
+// Object size/alignment for RealValue / RealValueImpl
+inline constexpr std::size_t kRealObjectSize{%zu};
+inline constexpr std::size_t kRealObjectAlign{%zu};
+
+// Object size/alignment for CharacterValue / CharacterValueImpl
+inline constexpr std::size_t kCharacterObjectSize{%zu};
+inline constexpr std::size_t kCharacterObjectAlign{%zu};
+
+} // namespace Fortran::evaluate::value::detail
+#endif // FORTRAN_EVALUATE_OBJECT_SIZES_GENERATED_H_
+)",
+ sizeof(IntegerValueImpl), alignof(IntegerValueImpl),
+ sizeof(RealValueImpl), alignof(RealValueImpl), sizeof(CharacterValueImpl),
+ alignof(CharacterValueImpl));
+
+ out.keep();
+ return EXIT_SUCCESS;
+}
diff --git a/flang/unittests/CMakeLists.txt b/flang/unittests/CMakeLists.txt
index 64cd5c0446474..db83c4a98e615 100644
--- a/flang/unittests/CMakeLists.txt
+++ b/flang/unittests/CMakeLists.txt
@@ -15,6 +15,10 @@ function(add_flang_unittest test_dirname)
if (FLANG_PARALLEL_COMPILE_JOBS)
set_property(TARGET ${test_dirname} PROPERTY JOB_POOL_COMPILE flang_compile_job_pool)
endif ()
+
+ if(TARGET flang-generated-object-sizes)
+ add_dependencies(${test_dirname} flang-generated-object-sizes)
+ endif()
endfunction()
if (CXX_SUPPORTS_SUGGEST_OVERRIDE_FLAG)
@@ -53,6 +57,10 @@ function(add_flang_nongtest_unittest test_name)
if(NOT ARG_SLOW_TEST)
add_dependencies(FlangUnitTests ${test_name}${suffix})
endif()
+
+ if(TARGET flang-generated-object-sizes)
+ add_dependencies(${test_name}${suffix} flang-generated-object-sizes)
+ endif()
endfunction()
add_subdirectory(Optimizer)
diff --git a/flang/unittests/Evaluate/expression.cpp b/flang/unittests/Evaluate/expression.cpp
index d575f36def287..8d570f517bc1f 100644
--- a/flang/unittests/Evaluate/expression.cpp
+++ b/flang/unittests/Evaluate/expression.cpp
@@ -11,13 +11,17 @@
using namespace Fortran::evaluate;
+static Expr<Type<TypeCategory::Integer>> MakeDefaultIntegerExpr(int32_t v) {
+ return MakeConstantExpr<Type<TypeCategory::Integer>>(4, v);
+}
+
int main() {
- using DefaultIntegerExpr = Expr<Type<TypeCategory::Integer, 4>>;
- TEST(DefaultIntegerExpr::Result::AsFortran() == "INTEGER(4)");
- MATCH("666_4", DefaultIntegerExpr{666}.AsFortran());
- MATCH("-1_4", (-DefaultIntegerExpr{1}).AsFortran());
- auto ex1{
- DefaultIntegerExpr{2} + DefaultIntegerExpr{3} * -DefaultIntegerExpr{4}};
+ using DefaultIntegerExpr = Expr<Type<TypeCategory::Integer>>;
+ TEST(DefaultIntegerExpr::Result{4}.AsFortran() == "INTEGER(4)");
+ MATCH("666_4", MakeDefaultIntegerExpr(666).AsFortran());
+ MATCH("-1_4", (-MakeDefaultIntegerExpr(1)).AsFortran());
+ auto ex1{MakeDefaultIntegerExpr(2) +
+ MakeDefaultIntegerExpr(3) * -MakeDefaultIntegerExpr(4)};
MATCH("2_4+3_4*(-4_4)", ex1.AsFortran());
Fortran::common::IntrinsicTypeDefaultKinds defaults;
auto intrinsics{Fortran::evaluate::IntrinsicProcTable::Configure(defaults)};
@@ -28,9 +32,10 @@ int main() {
intrinsics, targetCharacteristics, languageFeatures, tempNames};
ex1 = Fold(context, std::move(ex1));
MATCH("-10_4", ex1.AsFortran());
- MATCH("1_4/2_4", (DefaultIntegerExpr{1} / DefaultIntegerExpr{2}).AsFortran());
- DefaultIntegerExpr a{1};
- DefaultIntegerExpr b{2};
+ MATCH("1_4/2_4",
+ (MakeDefaultIntegerExpr(1) / MakeDefaultIntegerExpr(2)).AsFortran());
+ DefaultIntegerExpr a{MakeDefaultIntegerExpr(1)};
+ DefaultIntegerExpr b{MakeDefaultIntegerExpr(2)};
MATCH("1_4", a.AsFortran());
a = b;
MATCH("2_4", a.AsFortran());
diff --git a/flang/unittests/Evaluate/folding.cpp b/flang/unittests/Evaluate/folding.cpp
index 832e55d44316d..31b33c51af57b 100644
--- a/flang/unittests/Evaluate/folding.cpp
+++ b/flang/unittests/Evaluate/folding.cpp
@@ -15,13 +15,19 @@ using namespace Fortran::evaluate;
template <typename... T> struct RunOnTypes {};
template <typename Test, typename... T>
struct RunOnTypes<Test, std::tuple<T...>> {
- static void Run() { (..., Test::template Run<T>()); }
+ template <typename U> static void RunOnKinds() {
+ for (int kind : KindsByType<U::category>::kinds) {
+ Test::template Run<U>(kind);
+ }
+ }
+
+ static void Run() { (..., RunOnKinds<T>()); }
};
// test for fold.h GetScalarConstantValue function
struct TestGetScalarConstantValue {
- template <typename T> static void Run() {
- Expr<T> exprFullyTyped{Constant<T>{Scalar<T>{}}};
+ template <typename T> static void Run(int kind) {
+ Expr<T> exprFullyTyped{MakeZeroExpr<T>(kind)};
Expr<SomeKind<T::category>> exprSomeKind{exprFullyTyped};
Expr<SomeType> exprSomeType{exprSomeKind};
TEST(GetScalarConstantValue<T>(exprFullyTyped).has_value());
@@ -33,13 +39,15 @@ struct TestGetScalarConstantValue {
template <typename T>
Scalar<T> CallHostRt(
HostRuntimeWrapper func, FoldingContext &context, Scalar<T> x) {
+ const int kind{x.kind()};
return GetScalarConstantValue<T>(
- func(context, {AsGenericExpr(Constant<T>{x})}))
+ func(context, {AsGenericExpr(Constant<T>{kind, x})}))
.value();
}
void TestHostRuntimeSubnormalFlushing() {
- using R4 = Type<TypeCategory::Real, 4>;
+ using R4 = host::TypeKind<TypeCategory::Real, 4>;
+ using FR4 = R4::FortranType;
if constexpr (std::is_same_v<host::HostType<R4>, float>) {
Fortran::parser::CharBlock src;
Fortran::parser::ContextualMessages messages{src, nullptr};
@@ -56,13 +64,14 @@ void TestHostRuntimeSubnormalFlushing() {
FoldingContext noFlushingContext{messages, defaults, intrinsics,
noFlushingTargetCharacteristics, languageFeatures, tempNames};
- DynamicType r4{R4{}.GetType()};
+ DynamicType r4{R4::GetType()};
// Test subnormal argument flushing
if (auto callable{GetHostRuntimeWrapper("log", r4, {r4})}) {
// Biggest IEEE 32bits subnormal power of two
- const Scalar<R4> x1{Scalar<R4>::Word{0x00400000}};
- Scalar<R4> y1Flushing{CallHostRt<R4>(*callable, flushingContext, x1)};
- Scalar<R4> y1NoFlushing{CallHostRt<R4>(*callable, noFlushingContext, x1)};
+ const Scalar<FR4> x1{4, Scalar<FR4>::Word{4, 0x00400000}};
+ Scalar<FR4> y1Flushing{CallHostRt<FR4>(*callable, flushingContext, x1)};
+ Scalar<FR4> y1NoFlushing{
+ CallHostRt<FR4>(*callable, noFlushingContext, x1)};
// We would expect y1Flushing to be NaN, but some libc logf implementation
// "workaround" subnormal flushing by returning a constant negative
// results for all subnormal values (-1.03972076416015625e2_4). In case of
diff --git a/flang/unittests/Evaluate/intrinsics.cpp b/flang/unittests/Evaluate/intrinsics.cpp
index cca2f8c30247e..1abb41ae392ac 100644
--- a/flang/unittests/Evaluate/intrinsics.cpp
+++ b/flang/unittests/Evaluate/intrinsics.cpp
@@ -44,7 +44,8 @@ class CookedStrings {
};
template <typename A> auto Const(A &&x) -> Constant<TypeOf<A>> {
- return Constant<TypeOf<A>>{std::move(x)};
+ const int kind{x.kind()};
+ return Constant<TypeOf<A>>{kind, std::move(x)};
}
template <typename A> struct NamedArg {
@@ -152,64 +153,73 @@ void TestIntrinsics() {
IntrinsicProcTable table{IntrinsicProcTable::Configure(defaults)};
table.Dump(llvm::outs());
- using Int1 = Type<TypeCategory::Integer, 1>;
- using Int4 = Type<TypeCategory::Integer, 4>;
- using Int8 = Type<TypeCategory::Integer, 8>;
- using Real4 = Type<TypeCategory::Real, 4>;
- using Real8 = Type<TypeCategory::Real, 8>;
- using Complex4 = Type<TypeCategory::Complex, 4>;
- using Complex8 = Type<TypeCategory::Complex, 8>;
- using Char = Type<TypeCategory::Character, 1>;
- using Log4 = Type<TypeCategory::Logical, 4>;
+ using Int1 = Type<TypeCategory::Integer>;
+ Int1 int1{1};
+ using Int4 = Type<TypeCategory::Integer>;
+ Int4 int4{4};
+ using Int8 = Type<TypeCategory::Integer>;
+ Int8 int8{8};
+ using Real4 = Type<TypeCategory::Real>;
+ Real4 real4{4};
+ using Real8 = Type<TypeCategory::Real>;
+ Real8 real8{8};
+ using Complex4 = Type<TypeCategory::Complex>;
+ Complex4 complex4{4};
+ using Complex8 = Type<TypeCategory::Complex>;
+ Complex8 complex8{8};
+ using Char = Type<TypeCategory::Character>;
+ Char ascii{1};
+ using Log4 = Type<TypeCategory::Logical>;
+ Log4 log4{4};
TestCall{defaults, table, "bad"}
- .Push(Const(Scalar<Int4>{}))
+ .Push(Const(Scalar<Int4>::Zero(4)))
.DoCall(); // bad intrinsic name
TestCall{defaults, table, "abs"}
- .Push(Named("a", Const(Scalar<Int4>{})))
- .DoCall(Int4::GetType());
+ .Push(Named("a", Const(Scalar<Int4>::Zero(4))))
+ .DoCall(int4.GetType());
TestCall{defaults, table, "abs"}
- .Push(Const(Scalar<Int4>{}))
- .DoCall(Int4::GetType());
+ .Push(Const(Scalar<Int4>::Zero(4)))
+ .DoCall(int4.GetType());
TestCall{defaults, table, "abs"}
- .Push(Named("bad", Const(Scalar<Int4>{})))
+ .Push(Named("bad", Const(Scalar<Int4>::Zero(4))))
.DoCall(); // bad keyword
TestCall{defaults, table, "abs"}.DoCall(); // insufficient args
TestCall{defaults, table, "abs"}
- .Push(Const(Scalar<Int4>{}))
- .Push(Const(Scalar<Int4>{}))
+ .Push(Const(Scalar<Int4>::Zero(4)))
+ .Push(Const(Scalar<Int4>::Zero(4)))
.DoCall(); // too many args
TestCall{defaults, table, "abs"}
- .Push(Const(Scalar<Int4>{}))
- .Push(Named("a", Const(Scalar<Int4>{})))
+ .Push(Const(Scalar<Int4>::Zero(4)))
+ .Push(Named("a", Const(Scalar<Int4>::Zero(4))))
.DoCall();
TestCall{defaults, table, "abs"}
- .Push(Named("a", Const(Scalar<Int4>{})))
- .Push(Const(Scalar<Int4>{}))
+ .Push(Named("a", Const(Scalar<Int4>::Zero(4))))
+ .Push(Const(Scalar<Int4>::Zero(4)))
.DoCall();
TestCall{defaults, table, "abs"}
- .Push(Const(Scalar<Int1>{}))
- .DoCall(Int1::GetType());
+ .Push(Const(Scalar<Int1>::Zero(1)))
+ .DoCall(int1.GetType());
TestCall{defaults, table, "abs"}
- .Push(Const(Scalar<Int4>{}))
- .DoCall(Int4::GetType());
+ .Push(Const(Scalar<Int4>::Zero(4)))
+ .DoCall(int4.GetType());
TestCall{defaults, table, "abs"}
- .Push(Const(Scalar<Int8>{}))
- .DoCall(Int8::GetType());
+ .Push(Const(Scalar<Int8>::Zero(8)))
+ .DoCall(int8.GetType());
TestCall{defaults, table, "abs"}
- .Push(Const(Scalar<Real4>{}))
- .DoCall(Real4::GetType());
+ .Push(Const(Scalar<Real4>::Zero(4)))
+ .DoCall(real4.GetType());
TestCall{defaults, table, "abs"}
- .Push(Const(Scalar<Real8>{}))
- .DoCall(Real8::GetType());
+ .Push(Const(Scalar<Real8>::Zero(8)))
+ .DoCall(real8.GetType());
TestCall{defaults, table, "abs"}
- .Push(Const(Scalar<Complex4>{}))
- .DoCall(Real4::GetType());
+ .Push(Const(Scalar<Complex4>::Zero(4)))
+ .DoCall(real4.GetType());
TestCall{defaults, table, "abs"}
- .Push(Const(Scalar<Complex8>{}))
- .DoCall(Real8::GetType());
- TestCall{defaults, table, "abs"}.Push(Const(Scalar<Char>{})).DoCall();
- TestCall{defaults, table, "abs"}.Push(Const(Scalar<Log4>{})).DoCall();
+ .Push(Const(Scalar<Complex8>::Zero(8)))
+ .DoCall(real8.GetType());
+ TestCall{defaults, table, "abs"}.Push(Const(Scalar<Char>::Zero(1))).DoCall();
+ TestCall{defaults, table, "abs"}.Push(Const(Scalar<Log4>::Zero(4))).DoCall();
// "Ext" in names for calls allowed as extensions
TestCall maxCallR{defaults, table, "max"}, maxCallI{defaults, table, "min"},
@@ -218,110 +228,114 @@ void TestIntrinsics() {
max0ExtCall{defaults, table, "max0"},
amin1ExtCall{defaults, table, "amin1"};
for (int j{0}; j < 10; ++j) {
- maxCallR.Push(Const(Scalar<Real4>{}));
- maxCallI.Push(Const(Scalar<Int4>{}));
- max0Call.Push(Const(Scalar<Int4>{}));
- max0ExtCall.Push(Const(Scalar<Real4>{}));
- max1Call.Push(Const(Scalar<Real4>{}));
- amin0Call.Push(Const(Scalar<Int4>{}));
- amin1ExtCall.Push(Const(Scalar<Int4>{}));
- amin1Call.Push(Const(Scalar<Real4>{}));
+ maxCallR.Push(Const(Scalar<Real4>::Zero(4)));
+ maxCallI.Push(Const(Scalar<Int4>::Zero(4)));
+ max0Call.Push(Const(Scalar<Int4>::Zero(4)));
+ max0ExtCall.Push(Const(Scalar<Real4>::Zero(4)));
+ max1Call.Push(Const(Scalar<Real4>::Zero(4)));
+ amin0Call.Push(Const(Scalar<Int4>::Zero(4)));
+ amin1ExtCall.Push(Const(Scalar<Int4>::Zero(4)));
+ amin1Call.Push(Const(Scalar<Real4>::Zero(4)));
}
- maxCallR.DoCall(Real4::GetType());
- maxCallI.DoCall(Int4::GetType());
- max0Call.DoCall(Int4::GetType());
- max0ExtCall.DoCall(Int4::GetType());
- max1Call.DoCall(Int4::GetType());
- amin0Call.DoCall(Real4::GetType());
- amin1Call.DoCall(Real4::GetType());
- amin1ExtCall.DoCall(Real4::GetType());
+ maxCallR.DoCall(real4.GetType());
+ maxCallI.DoCall(int4.GetType());
+ max0Call.DoCall(int4.GetType());
+ max0ExtCall.DoCall(int4.GetType());
+ max1Call.DoCall(int4.GetType());
+ amin0Call.DoCall(real4.GetType());
+ amin1Call.DoCall(real4.GetType());
+ amin1ExtCall.DoCall(real4.GetType());
TestCall{defaults, table, "conjg"}
- .Push(Const(Scalar<Complex4>{}))
- .DoCall(Complex4::GetType());
+ .Push(Const(Scalar<Complex4>::Zero(4)))
+ .DoCall(complex4.GetType());
TestCall{defaults, table, "conjg"}
- .Push(Const(Scalar<Complex8>{}))
- .DoCall(Complex8::GetType());
+ .Push(Const(Scalar<Complex8>::Zero(8)))
+ .DoCall(complex8.GetType());
TestCall{defaults, table, "dconjg"}
- .Push(Const(Scalar<Complex8>{}))
- .DoCall(Complex8::GetType());
+ .Push(Const(Scalar<Complex8>::Zero(8)))
+ .DoCall(complex8.GetType());
- TestCall{defaults, table, "float"}.Push(Const(Scalar<Real4>{})).DoCall();
TestCall{defaults, table, "float"}
- .Push(Const(Scalar<Int4>{}))
- .DoCall(Real4::GetType());
- TestCall{defaults, table, "idint"}.Push(Const(Scalar<Int4>{})).DoCall();
+ .Push(Const(Scalar<Real4>::Zero(4)))
+ .DoCall();
+ TestCall{defaults, table, "float"}
+ .Push(Const(Scalar<Int4>::Zero(4)))
+ .DoCall(real4.GetType());
+ TestCall{defaults, table, "idint"}
+ .Push(Const(Scalar<Int4>::Zero(4)))
+ .DoCall();
TestCall{defaults, table, "idint"}
- .Push(Const(Scalar<Real8>{}))
- .DoCall(Int4::GetType());
+ .Push(Const(Scalar<Real8>::Zero(8)))
+ .DoCall(int4.GetType());
// Allowed as extensions
TestCall{defaults, table, "float"}
- .Push(Const(Scalar<Int8>{}))
- .DoCall(Real4::GetType());
+ .Push(Const(Scalar<Int8>::Zero(8)))
+ .DoCall(real4.GetType());
TestCall{defaults, table, "idint"}
- .Push(Const(Scalar<Real4>{}))
- .DoCall(Int4::GetType());
+ .Push(Const(Scalar<Real4>::Zero(4)))
+ .DoCall(int4.GetType());
- TestCall{defaults, table, "num_images"}.DoCall(Int4::GetType());
+ TestCall{defaults, table, "num_images"}.DoCall(int4.GetType());
TestCall{defaults, table, "num_images"}
- .Push(Const(Scalar<Int1>{}))
- .DoCall(Int4::GetType());
+ .Push(Const(Scalar<Int1>::Zero(1)))
+ .DoCall(int4.GetType());
TestCall{defaults, table, "num_images"}
- .Push(Const(Scalar<Int4>{}))
- .DoCall(Int4::GetType());
+ .Push(Const(Scalar<Int4>::Zero(4)))
+ .DoCall(int4.GetType());
TestCall{defaults, table, "num_images"}
- .Push(Const(Scalar<Int8>{}))
- .DoCall(Int4::GetType());
+ .Push(Const(Scalar<Int8>::Zero(8)))
+ .DoCall(int4.GetType());
TestCall{defaults, table, "num_images"}
- .Push(Named("team_number", Const(Scalar<Int4>{})))
- .DoCall(Int4::GetType());
+ .Push(Named("team_number", Const(Scalar<Int4>::Zero(4))))
+ .DoCall(int4.GetType());
TestCall{defaults, table, "num_images"}
- .Push(Const(Scalar<Int4>{}))
- .Push(Const(Scalar<Int4>{}))
+ .Push(Const(Scalar<Int4>::Zero(4)))
+ .Push(Const(Scalar<Int4>::Zero(4)))
.DoCall(); // too many args
TestCall{defaults, table, "num_images"}
- .Push(Named("bad", Const(Scalar<Int4>{})))
+ .Push(Named("bad", Const(Scalar<Int4>::Zero(4))))
.DoCall(); // bad keyword
TestCall{defaults, table, "num_images"}
- .Push(Const(Scalar<Char>{}))
+ .Push(Const(Scalar<Char>::Zero(1)))
.DoCall(); // bad type
TestCall{defaults, table, "num_images"}
- .Push(Const(Scalar<Log4>{}))
+ .Push(Const(Scalar<Log4>::Zero(4)))
.DoCall(); // bad type
TestCall{defaults, table, "num_images"}
- .Push(Const(Scalar<Complex8>{}))
+ .Push(Const(Scalar<Complex8>::Zero(8)))
.DoCall(); // bad type
TestCall{defaults, table, "num_images"}
- .Push(Const(Scalar<Real4>{}))
+ .Push(Const(Scalar<Real4>::Zero(4)))
.DoCall(); // bad type
// This test temporarily removed because it requires access to
// the ISO_FORTRAN_ENV intrinsic module. This module should to
// be loaded (somehow) and the following test reinstated.
- // TestCall{defaults, table, "team_number"}.DoCall(Int4::GetType());
+ // TestCall{defaults, table, "team_number"}.DoCall(int4.GetType());
TestCall{defaults, table, "team_number"}
- .Push(Const(Scalar<Int4>{}))
- .Push(Const(Scalar<Int4>{}))
+ .Push(Const(Scalar<Int4>::Zero(4)))
+ .Push(Const(Scalar<Int4>::Zero(4)))
.DoCall(); // too many args
TestCall{defaults, table, "team_number"}
- .Push(Named("bad", Const(Scalar<Int4>{})))
+ .Push(Named("bad", Const(Scalar<Int4>::Zero(4))))
.DoCall(); // bad keyword
TestCall{defaults, table, "team_number"}
- .Push(Const(Scalar<Int4>{}))
+ .Push(Const(Scalar<Int4>::Zero(4)))
.DoCall(); // bad type
TestCall{defaults, table, "team_number"}
- .Push(Const(Scalar<Char>{}))
+ .Push(Const(Scalar<Char>::Zero(1)))
.DoCall(); // bad type
TestCall{defaults, table, "team_number"}
- .Push(Const(Scalar<Log4>{}))
+ .Push(Const(Scalar<Log4>::Zero(4)))
.DoCall(); // bad type
TestCall{defaults, table, "team_number"}
- .Push(Const(Scalar<Complex8>{}))
+ .Push(Const(Scalar<Complex8>::Zero(4)))
.DoCall(); // bad type
TestCall{defaults, table, "team_number"}
- .Push(Const(Scalar<Real4>{}))
+ .Push(Const(Scalar<Real4>::Zero(4)))
.DoCall(); // bad type
// TODO: test other intrinsics
diff --git a/flang/unittests/Evaluate/logical.cpp b/flang/unittests/Evaluate/logical.cpp
index ba7d0d8d0c0e3..c8bf850d6c08e 100644
--- a/flang/unittests/Evaluate/logical.cpp
+++ b/flang/unittests/Evaluate/logical.cpp
@@ -2,41 +2,40 @@
#include "flang/Testing/testing.h"
#include <cstdio>
-template <int KIND> void testKind() {
- using Type =
- Fortran::evaluate::Type<Fortran::common::TypeCategory::Logical, KIND>;
+static void testKind(int kind) {
+ using Type = Fortran::evaluate::Type<Fortran::common::TypeCategory::Logical>;
TEST(Fortran::evaluate::IsSpecificIntrinsicType<Type>);
TEST(Type::category == Fortran::common::TypeCategory::Logical);
- TEST(Type::kind == KIND);
+ TEST(Type{kind}.kind() == kind);
using Value = Fortran::evaluate::Scalar<Type>;
- MATCH(8 * KIND, Value::bits);
+ MATCH(8 * kind, Value::Zero(kind).bits());
TEST(!Value{}.IsTrue());
- TEST(!Value{false}.IsTrue());
- TEST(Value{true}.IsTrue());
- TEST(Value{false}.NOT().IsTrue());
- TEST(!Value{true}.NOT().IsTrue());
- TEST(!Value{false}.AND(Value{false}).IsTrue());
- TEST(!Value{false}.AND(Value{true}).IsTrue());
- TEST(!Value{true}.AND(Value{false}).IsTrue());
- TEST(Value{true}.AND(Value{true}).IsTrue());
- TEST(!Value{false}.OR(Value{false}).IsTrue());
- TEST(Value{false}.OR(Value{true}).IsTrue());
- TEST(Value{true}.OR(Value{false}).IsTrue());
- TEST(Value{true}.OR(Value{true}).IsTrue());
- TEST(Value{false}.EQV(Value{false}).IsTrue());
- TEST(!Value{false}.EQV(Value{true}).IsTrue());
- TEST(!Value{true}.EQV(Value{false}).IsTrue());
- TEST(Value{true}.EQV(Value{true}).IsTrue());
- TEST(!Value{false}.NEQV(Value{false}).IsTrue());
- TEST(Value{false}.NEQV(Value{true}).IsTrue());
- TEST(Value{true}.NEQV(Value{false}).IsTrue());
- TEST(!Value{true}.NEQV(Value{true}).IsTrue());
+ TEST(!Value(kind, false).IsTrue());
+ TEST(Value(kind, true).IsTrue());
+ TEST(Value(kind, false).NOT().IsTrue());
+ TEST(!Value(kind, true).NOT().IsTrue());
+ TEST(!Value(kind, false).AND(Value(kind, false)).IsTrue());
+ TEST(!Value(kind, false).AND(Value(kind, true)).IsTrue());
+ TEST(!Value(kind, true).AND(Value(kind, false)).IsTrue());
+ TEST(Value(kind, true).AND(Value(kind, true)).IsTrue());
+ TEST(!Value(kind, false).OR(Value(kind, false)).IsTrue());
+ TEST(Value(kind, false).OR(Value(kind, true)).IsTrue());
+ TEST(Value(kind, true).OR(Value(kind, false)).IsTrue());
+ TEST(Value(kind, true).OR(Value(kind, true)).IsTrue());
+ TEST(Value(kind, false).EQV(Value(kind, false)).IsTrue());
+ TEST(!Value(kind, false).EQV(Value(kind, true)).IsTrue());
+ TEST(!Value(kind, true).EQV(Value(kind, false)).IsTrue());
+ TEST(Value(kind, true).EQV(Value(kind, true)).IsTrue());
+ TEST(!Value(kind, false).NEQV(Value(kind, false)).IsTrue());
+ TEST(Value(kind, false).NEQV(Value(kind, true)).IsTrue());
+ TEST(Value(kind, true).NEQV(Value(kind, false)).IsTrue());
+ TEST(!Value(kind, true).NEQV(Value(kind, true)).IsTrue());
}
int main() {
- testKind<1>();
- testKind<2>();
- testKind<4>();
- testKind<8>();
+ testKind(1);
+ testKind(2);
+ testKind(4);
+ testKind(8);
return testing::Complete();
}
diff --git a/flang/unittests/Evaluate/real.cpp b/flang/unittests/Evaluate/real.cpp
index a28da5c3273ce..9599303b6b566 100644
--- a/flang/unittests/Evaluate/real.cpp
+++ b/flang/unittests/Evaluate/real.cpp
@@ -1,3 +1,4 @@
+#include "flang/Evaluate/real.h"
#include "flang/Evaluate/type.h"
#include "flang/Testing/fp-testing.h"
#include "flang/Testing/testing.h"
@@ -10,16 +11,16 @@
using namespace Fortran::evaluate;
using namespace Fortran::common;
-using Real2 = Scalar<Type<TypeCategory::Real, 2>>;
-using Real3 = Scalar<Type<TypeCategory::Real, 3>>;
-using Real4 = Scalar<Type<TypeCategory::Real, 4>>;
-using Real8 = Scalar<Type<TypeCategory::Real, 8>>;
+using Real2 = value::Real<value::Integer<16>, 11>;
+using Real3 = value::Real<value::Integer<16>, 8>;
+using Real4 = value::Real<value::Integer<32>, 24>;
+using Real8 = value::Real<value::Integer<64>, 53>;
#ifdef __x86_64__
-using Real10 = Scalar<Type<TypeCategory::Real, 10>>;
+using Real10 = value::Real<value::X87IntegerContainer, 64>;
#endif
-using Real16 = Scalar<Type<TypeCategory::Real, 16>>;
-using Integer4 = Scalar<Type<TypeCategory::Integer, 4>>;
-using Integer8 = Scalar<Type<TypeCategory::Integer, 8>>;
+using Real16 = value::Real<value::Integer<128>, 113>;
+using Integer4 = value::Integer<32>;
+using Integer8 = value::Integer<64>;
void dumpTest() {
struct {
>From 373bbaf6cd5bbc304ed67a5f73fe31b48b748c56 Mon Sep 17 00:00:00 2001
From: Michael Kruse <llvm-project at meinersbur.de>
Date: Tue, 11 Aug 2026 09:42:54 +0200
Subject: [PATCH 2/2] Fix (unrealted) Werror fail
---
flang/lib/Lower/ConvertType.cpp | 2 +-
flang/unittests/Evaluate/intrinsics.cpp | 2 --
2 files changed, 1 insertion(+), 3 deletions(-)
diff --git a/flang/lib/Lower/ConvertType.cpp b/flang/lib/Lower/ConvertType.cpp
index 7dd56a41354e8..a2c10f0bd821f 100644
--- a/flang/lib/Lower/ConvertType.cpp
+++ b/flang/lib/Lower/ConvertType.cpp
@@ -489,7 +489,7 @@ struct TypeBuilderImpl {
converter.getFoldingContext(),
Fortran::evaluate::Expr<TC>{
Fortran::evaluate::Designator<TC>{kind, symbol}});
- if (auto len = toInt64(std::move(designator.LEN())))
+ if (auto len = toInt64(designator.LEN()))
return *len;
return fir::SequenceType::getUnknownExtent();
}
diff --git a/flang/unittests/Evaluate/intrinsics.cpp b/flang/unittests/Evaluate/intrinsics.cpp
index 1abb41ae392ac..69c8aa4246950 100644
--- a/flang/unittests/Evaluate/intrinsics.cpp
+++ b/flang/unittests/Evaluate/intrinsics.cpp
@@ -168,9 +168,7 @@ void TestIntrinsics() {
using Complex8 = Type<TypeCategory::Complex>;
Complex8 complex8{8};
using Char = Type<TypeCategory::Character>;
- Char ascii{1};
using Log4 = Type<TypeCategory::Logical>;
- Log4 log4{4};
TestCall{defaults, table, "bad"}
.Push(Const(Scalar<Int4>::Zero(4)))
More information about the flang-commits
mailing list