[flang-commits] [flang] bd859cb - [flasg] Debug folding of substring references

Peter Klausler via flang-commits flang-commits at lists.llvm.org
Fri Jan 14 16:09:12 PST 2022


Author: Peter Klausler
Date: 2022-01-14T16:09:06-08:00
New Revision: bd859cb4def4f1b2362babab602f7f194de13928

URL: https://github.com/llvm/llvm-project/commit/bd859cb4def4f1b2362babab602f7f194de13928
DIFF: https://github.com/llvm/llvm-project/commit/bd859cb4def4f1b2362babab602f7f194de13928.diff

LOG: [flasg] Debug folding of substring references

Character substrings weren't being folded correctly;
add tests and rework the implementation so that substrings
of literals and named constant character scalars & arrays
are properly folded for use in constant expressions.

Differential Revision: https://reviews.llvm.org/D117343

Added: 
    flang/test/Evaluate/fold-substr.f90

Modified: 
    flang/include/flang/Evaluate/constant.h
    flang/lib/Evaluate/constant.cpp
    flang/lib/Evaluate/fold-implementation.h
    flang/lib/Evaluate/variable.cpp

Removed: 
    


################################################################################
diff  --git a/flang/include/flang/Evaluate/constant.h b/flang/include/flang/Evaluate/constant.h
index 34453d5a2370f..97445649b8c41 100644
--- a/flang/include/flang/Evaluate/constant.h
+++ b/flang/include/flang/Evaluate/constant.h
@@ -183,6 +183,9 @@ class Constant<Type<TypeCategory::Character, KIND>> : public ConstantBounds {
   // Apply subscripts, if any.
   Scalar<Result> At(const ConstantSubscripts &) const;
 
+  // Extract substring(s); returns nullopt for errors.
+  std::optional<Constant> Substring(ConstantSubscript, ConstantSubscript) const;
+
   Constant Reshape(ConstantSubscripts &&) const;
   llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const;
   static constexpr DynamicType GetType() {

diff  --git a/flang/lib/Evaluate/constant.cpp b/flang/lib/Evaluate/constant.cpp
index efc02316d92dc..199b19003cf7f 100644
--- a/flang/lib/Evaluate/constant.cpp
+++ b/flang/lib/Evaluate/constant.cpp
@@ -221,6 +221,28 @@ auto Constant<Type<TypeCategory::Character, KIND>>::At(
   return values_.substr(offset * length_, length_);
 }
 
+template <int KIND>
+auto Constant<Type<TypeCategory::Character, KIND>>::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(); // ""
+    }
+  } else if (lo < 1 || hi > length_) {
+    return std::nullopt;
+  } else {
+    newLength = hi - lo + 1;
+    for (ConstantSubscripts at{lbounds()}; n-- > 0; IncrementSubscripts(at)) {
+      elements.emplace_back(At(at).substr(lo - 1, newLength));
+    }
+  }
+  return Constant{newLength, std::move(elements), ConstantSubscripts{shape()}};
+}
+
 template <int KIND>
 auto Constant<Type<TypeCategory::Character, KIND>>::Reshape(
     ConstantSubscripts &&dims) const -> Constant<Result> {

diff  --git a/flang/lib/Evaluate/fold-implementation.h b/flang/lib/Evaluate/fold-implementation.h
index 4b1ea9be7ff5a..43b7f931826ab 100644
--- a/flang/lib/Evaluate/fold-implementation.h
+++ b/flang/lib/Evaluate/fold-implementation.h
@@ -331,8 +331,8 @@ template <typename T> Expr<T> Folder<T>::Folding(Designator<T> &&designator) {
     if (auto *substring{common::Unwrap<Substring>(designator.u)}) {
       if (std::optional<Expr<SomeCharacter>> folded{
               substring->Fold(context_)}) {
-        if (auto value{GetScalarConstantValue<T>(*folded)}) {
-          return Expr<T>{*value};
+        if (const auto *specific{std::get_if<Expr<T>>(&folded->u)}) {
+          return std::move(*specific);
         }
       }
       if (auto length{ToInt64(Fold(context_, substring->LEN()))}) {

diff  --git a/flang/lib/Evaluate/variable.cpp b/flang/lib/Evaluate/variable.cpp
index 5885a41705f8e..b33a77f351319 100644
--- a/flang/lib/Evaluate/variable.cpp
+++ b/flang/lib/Evaluate/variable.cpp
@@ -163,81 +163,81 @@ Substring &Substring::set_upper(Expr<SubscriptInteger> &&expr) {
 }
 
 std::optional<Expr<SomeCharacter>> Substring::Fold(FoldingContext &context) {
+  if (!upper_) {
+    upper_ = upper();
+    if (!upper_) {
+      return std::nullopt;
+    }
+  }
+  upper_.value() = evaluate::Fold(context, std::move(upper_.value().value()));
+  std::optional<ConstantSubscript> ubi{ToInt64(upper_.value().value())};
+  if (!ubi) {
+    return std::nullopt;
+  }
   if (!lower_) {
     lower_ = AsExpr(Constant<SubscriptInteger>{1});
   }
   lower_.value() = evaluate::Fold(context, std::move(lower_.value().value()));
   std::optional<ConstantSubscript> lbi{ToInt64(lower_.value().value())};
-  if (lbi && *lbi < 1) {
-    context.messages().Say(
-        "Lower bound (%jd) on substring is less than one"_en_US, *lbi);
+  if (!lbi) {
+    return std::nullopt;
+  }
+  if (*lbi > *ubi) { // empty result; canonicalize
     *lbi = 1;
-    lower_ = AsExpr(Constant<SubscriptInteger>{1});
+    *ubi = 0;
+    lower_ = AsExpr(Constant<SubscriptInteger>{*lbi});
+    upper_ = AsExpr(Constant<SubscriptInteger>{*ubi});
   }
-  if (!upper_) {
-    upper_ = upper();
-    if (!upper_) {
-      return std::nullopt;
+  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)}));
     }
-  }
-  upper_.value() = evaluate::Fold(context, std::move(upper_.value().value()));
-  if (std::optional<ConstantSubscript> ubi{ToInt64(upper_.value().value())}) {
-    auto *literal{std::get_if<StaticDataObject::Pointer>(&parent_)};
-    std::optional<ConstantSubscript> length;
-    if (literal) {
-      length = (*literal)->data().size();
-    } else if (const Symbol * symbol{GetLastSymbol()}) {
-      if (const semantics::DeclTypeSpec * type{symbol->GetType()}) {
-        if (type->category() == semantics::DeclTypeSpec::Character) {
-          length = ToInt64(type->characterTypeSpec().length().GetExplicit());
+  } else if (const auto *dataRef{std::get_if<DataRef>(&parent_)}) {
+    if (auto expr{AsGenericExpr(DataRef{*dataRef})}) {
+      auto folded{evaluate::Fold(context, std::move(*expr))};
+      if (IsActuallyConstant(folded)) {
+        if (const auto *value{UnwrapExpr<Expr<SomeCharacter>>(folded)}) {
+          strings = *value;
         }
       }
     }
-    if (*ubi < 1 || (lbi && *ubi < *lbi)) {
-      // Zero-length string: canonicalize
-      *lbi = 1, *ubi = 0;
-      lower_ = AsExpr(Constant<SubscriptInteger>{*lbi});
-      upper_ = AsExpr(Constant<SubscriptInteger>{*ubi});
-    } else if (length && *ubi > *length) {
-      context.messages().Say("Upper bound (%jd) on substring is greater "
-                             "than character length (%jd)"_en_US,
-          *ubi, *length);
-      *ubi = *length;
-    }
-    if (lbi && literal) {
-      auto newStaticData{StaticDataObject::Create()};
-      auto items{0}; // If the lower bound is greater, the length is 0
-      if (*ubi >= *lbi) {
-        items = *ubi - *lbi + 1;
-      }
-      auto width{(*literal)->itemBytes()};
-      auto bytes{items * width};
-      auto startByte{(*lbi - 1) * width};
-      const auto *from{&(*literal)->data()[0] + startByte};
-      for (auto j{0}; j < bytes; ++j) {
-        newStaticData->data().push_back(from[j]);
-      }
-      parent_ = newStaticData;
+  }
+  std::optional<Expr<SomeCharacter>> result;
+  if (strings) {
+    result = std::visit(
+        [&](const auto &expr) -> std::optional<Expr<SomeCharacter>> {
+          using Type = typename std::decay_t<decltype(expr)>::Result;
+          if (const auto *cc{std::get_if<Constant<Type>>(&expr.u)}) {
+            if (auto substr{cc->Substring(*lbi, *ubi)}) {
+              return Expr<SomeCharacter>{Expr<Type>{*substr}};
+            }
+          }
+          return std::nullopt;
+        },
+        strings->u);
+  }
+  if (!result) { // error cases
+    if (*lbi < 1) {
+      context.messages().Say(
+          "Lower bound (%jd) on substring is less than one"_en_US,
+          static_cast<std::intmax_t>(*lbi));
+      *lbi = 1;
       lower_ = AsExpr(Constant<SubscriptInteger>{1});
-      ConstantSubscript length = newStaticData->data().size();
-      upper_ = AsExpr(Constant<SubscriptInteger>{length});
-      switch (width) {
-      case 1:
-        return {
-            AsCategoryExpr(AsExpr(Constant<Type<TypeCategory::Character, 1>>{
-                *newStaticData->AsString()}))};
-      case 2:
-        return {AsCategoryExpr(Constant<Type<TypeCategory::Character, 2>>{
-            *newStaticData->AsU16String()})};
-      case 4:
-        return {AsCategoryExpr(Constant<Type<TypeCategory::Character, 4>>{
-            *newStaticData->AsU32String()})};
-      default:
-        CRASH_NO_CASE;
-      }
+    }
+    if (length && *ubi > *length) {
+      context.messages().Say(
+          "Upper bound (%jd) on substring is greater than character length (%jd)"_en_US,
+          static_cast<std::intmax_t>(*ubi),
+          static_cast<std::intmax_t>(*length));
+      *ubi = *length;
+      upper_ = AsExpr(Constant<SubscriptInteger>{*ubi});
     }
   }
-  return std::nullopt;
+  return result;
 }
 
 DescriptorInquiry::DescriptorInquiry(

diff  --git a/flang/test/Evaluate/fold-substr.f90 b/flang/test/Evaluate/fold-substr.f90
new file mode 100644
index 0000000000000..c2b73c2affc91
--- /dev/null
+++ b/flang/test/Evaluate/fold-substr.f90
@@ -0,0 +1,17 @@
+! RUN: %python %S/test_folding.py %s %flang_fc1
+! Test folding of substrings
+module m
+  logical, parameter :: test_01a = "abc"(1:3) == "abc"
+  logical, parameter :: test_01b = len("abc"(1:3)) == 3
+  logical, parameter :: test_02a = "abc"(-1:-2) == ""
+  logical, parameter :: test_02b = len("abc"(-1:-2)) == 0
+  logical, parameter :: test_03a = "abc"(9999:4) == ""
+  logical, parameter :: test_03b = len("abc"(9999:4)) == 0
+  character(4), parameter :: ca(3) = ["abcd", "efgh", "ijkl"]
+  logical, parameter :: test_04a = ca(2)(2:4) == "fgh"
+  logical, parameter :: test_04b = len(ca(2)(2:4)) == 3
+  logical, parameter :: test_05a = all(ca(:)(2:4) == ["bcd", "fgh", "jkl"])
+  logical, parameter :: test_05b = len(ca(:)(2:4)) == 3
+  logical, parameter :: test_06a = ca(1)(1:2)//ca(2)(2:3)//ca(3)(3:4) == "abfgkl"
+  logical, parameter :: test_06b = len(ca(1)(1:2)//ca(2)(2:3)//ca(3)(3:4)) == 6
+end module


        


More information about the flang-commits mailing list