[flang-commits] [flang] [llvm] [Flang] Introduce *Value classes with unittests (PR #216958)
Michael Kruse via flang-commits
flang-commits at lists.llvm.org
Wed Sep 9 05:58:29 PDT 2026
================
@@ -0,0 +1,615 @@
+//===-- 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 size) {
+ return withCharProto(kind, [kind, raw, size](auto charProto) {
+ using CharT = decltype(charProto);
+ CHECK(size % sizeof(CharT) == 0);
+ std::basic_string<CharT> s;
+ if (size > 0) {
+ s.assign(static_cast<const CharT *>(raw), size / sizeof(CharT));
+ }
+ return CharacterValueImpl{kind, std::move(s)};
+ });
+}
+
+void CharacterValueImpl::print(llvm::raw_ostream &os) const {
+ os << kind() << '_';
+ withStdString(
+ [&](const auto &s) { os << parser::QuoteCharacterLiteral(s, true); });
+}
+
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+LLVM_DUMP_METHOD void CharacterValueImpl::dump() const {
+ print(llvm::errs());
+ llvm::errs() << '\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>) {
+ DIE("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 {
+ DIE("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 {
+ DIE("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 {
+ DIE("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>) {
+ DIE("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>) {
+ DIE("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 {
+ DIE("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 {
+ using StringT = std::decay_t<decltype(s)>;
+ if constexpr (std::is_same_v<StringT, std::monostate>) {
+ DIE("operation not supported on uninitialized value");
+ } else {
+ return CharacterValueImpl{
+ sizeof(typename StringT::value_type), 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 {
+ using StringT = std::decay_t<decltype(s)>;
+ if constexpr (std::is_same_v<std::decay_t<decltype(s)>,
+ std::monostate>) {
+ DIE("operation not supported on uninitialized value");
+ } else {
+ return CharacterValueImpl{
+ sizeof(typename StringT::value_type), 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;
+ }
+}
+
+std::string CharacterValueImpl::ToStdString() const {
+ return common::visit(
+ [](const auto &s) {
+ using StringT = std::decay_t<decltype(s)>;
+ if constexpr (std::is_same_v<StringT, std::monostate>) {
+ return std::string{};
+ } else if constexpr (std::is_same_v<StringT, std::string>) {
+ return s;
+ } else {
+ std::string result(s.size(), '\0');
+ for (auto [i, c] : llvm::enumerate(s)) {
+ result[i] = c;
+ }
+ return result;
+ }
+ },
+ storage_);
+}
+
+CharacterValueImpl CharacterValueImpl::ToAscii(int kind) const {
+ if (IsMonostate()) {
+ return Zero(kind);
----------------
Meinersbur wrote:
Monostate represents zero with unknown kind. This is because Flang's scalar classes default-initialize to zero (0, 0.0, false, empty string), but I cannot pass the kind in the default-initializer which has no argument.
In methods that receive a kind argument we can now insert the actual zero with known kind. This also includes some binary operations (e.g. "some string" + Monostate = "some string") . This saves us some explicit initializations. Monostate behaving more like NaN (propagating whenever used) would also be possible. I chose because it closer resembles the behavior of the current scalar classes.
https://github.com/llvm/llvm-project/pull/216958
More information about the flang-commits
mailing list