[flang-commits] [flang] dfecbca - [flang] CheckConformance: tristate-ify result

peter klausler via flang-commits flang-commits at lists.llvm.org
Fri Jun 4 11:19:30 PDT 2021


Author: peter klausler
Date: 2021-06-04T11:19:14-07:00
New Revision: dfecbcae0d6434fa1daafe66ba5d90f816d4268b

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

LOG: [flang] CheckConformance: tristate-ify result

To ensure that errors are emitted by CheckConformance and
its callers in all situations, it's necessary for the returned result
of that function to distinguish between three possible
outcomes: the arrays are known to conform at compilation time,
the arrays are known to not conform (and a message has been
produced), and an indeterminate result in which is not possible
to determine conformance.  So convert CheckConformance's
result into an optional<bool>, and convert its confusing
Boolean flag arguments into a bit-set of named flags too.

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

Added: 
    

Modified: 
    flang/include/flang/Evaluate/characteristics.h
    flang/include/flang/Evaluate/shape.h
    flang/lib/Evaluate/characteristics.cpp
    flang/lib/Evaluate/check-expression.cpp
    flang/lib/Evaluate/fold-implementation.h
    flang/lib/Evaluate/shape.cpp
    flang/lib/Semantics/check-call.cpp
    flang/lib/Semantics/expression.cpp
    flang/lib/Semantics/pointer-assignment.cpp

Removed: 
    


################################################################################
diff  --git a/flang/include/flang/Evaluate/characteristics.h b/flang/include/flang/Evaluate/characteristics.h
index e9fed59e393a3..8006f7a09c048 100644
--- a/flang/include/flang/Evaluate/characteristics.h
+++ b/flang/include/flang/Evaluate/characteristics.h
@@ -144,8 +144,8 @@ class TypeAndShape {
   int Rank() const { return GetRank(shape_); }
   bool IsCompatibleWith(parser::ContextualMessages &, const TypeAndShape &that,
       const char *thisIs = "pointer", const char *thatIs = "target",
-      bool isElemental = false, bool thisIsDeferredShape = false,
-      bool thatIsDeferredShape = false) const;
+      bool isElemental = false,
+      enum CheckConformanceFlags::Flags = CheckConformanceFlags::None) const;
   std::optional<Expr<SubscriptInteger>> MeasureElementSizeInBytes(
       FoldingContext &, bool align) const;
   std::optional<Expr<SubscriptInteger>> MeasureSizeInBytes(

diff  --git a/flang/include/flang/Evaluate/shape.h b/flang/include/flang/Evaluate/shape.h
index 507a710f380c6..5df3eeff73815 100644
--- a/flang/include/flang/Evaluate/shape.h
+++ b/flang/include/flang/Evaluate/shape.h
@@ -239,12 +239,30 @@ std::optional<ConstantSubscripts> GetConstantExtents(
 }
 
 // Compilation-time shape conformance checking, when corresponding extents
-// are known.
-bool CheckConformance(parser::ContextualMessages &, const Shape &left,
-    const Shape &right, const char *leftIs = "left operand",
-    const char *rightIs = "right operand", bool leftScalarExpandable = true,
-    bool rightScalarExpandable = true, bool leftIsDeferredShape = false,
-    bool rightIsDeferredShape = false);
+// are or should be known.  The result is an optional Boolean:
+//  - nullopt: no error found or reported, but conformance cannot
+//    be guaranteed during compilation; this result is possible only
+//    when one or both arrays are allowed to have deferred shape
+//  - true: no error found or reported, arrays conform
+//  - false: errors found and reported
+// Use "CheckConformance(...).value_or()" to specify a default result
+// when you don't care whether messages have been emitted.
+struct CheckConformanceFlags {
+  enum Flags {
+    None = 0,
+    LeftScalarExpandable = 1,
+    RightScalarExpandable = 2,
+    LeftIsDeferredShape = 4,
+    RightIsDeferredShape = 8,
+    EitherScalarExpandable = LeftScalarExpandable | RightScalarExpandable,
+    BothDeferredShape = LeftIsDeferredShape | RightIsDeferredShape,
+    RightIsExpandableDeferred = RightScalarExpandable | RightIsDeferredShape,
+  };
+};
+std::optional<bool> CheckConformance(parser::ContextualMessages &,
+    const Shape &left, const Shape &right,
+    CheckConformanceFlags::Flags flags = CheckConformanceFlags::None,
+    const char *leftIs = "left operand", const char *rightIs = "right operand");
 
 // Increments one-based subscripts in element order (first varies fastest)
 // and returns true when they remain in range; resets them all to one and

diff  --git a/flang/lib/Evaluate/characteristics.cpp b/flang/lib/Evaluate/characteristics.cpp
index 4930f936fbaf3..80f5f23fa53be 100644
--- a/flang/lib/Evaluate/characteristics.cpp
+++ b/flang/lib/Evaluate/characteristics.cpp
@@ -149,8 +149,7 @@ std::optional<TypeAndShape> TypeAndShape::Characterize(
 
 bool TypeAndShape::IsCompatibleWith(parser::ContextualMessages &messages,
     const TypeAndShape &that, const char *thisIs, const char *thatIs,
-    bool isElemental, bool thisIsDeferredShape,
-    bool thatIsDeferredShape) const {
+    bool isElemental, enum CheckConformanceFlags::Flags flags) const {
   if (!type_.IsTkCompatibleWith(that.type_)) {
     messages.Say(
         "%1$s type '%2$s' is not compatible with %3$s type '%4$s'"_err_en_US,
@@ -158,9 +157,8 @@ bool TypeAndShape::IsCompatibleWith(parser::ContextualMessages &messages,
     return false;
   }
   return isElemental ||
-      CheckConformance(messages, shape_, that.shape_, thisIs, thatIs, false,
-          false /* no scalar expansion */, thisIsDeferredShape,
-          thatIsDeferredShape);
+      CheckConformance(messages, shape_, that.shape_, flags, thisIs, thatIs)
+          .value_or(true /*fail only when nonconformance is known now*/);
 }
 
 std::optional<Expr<SubscriptInteger>> TypeAndShape::MeasureElementSizeInBytes(

diff  --git a/flang/lib/Evaluate/check-expression.cpp b/flang/lib/Evaluate/check-expression.cpp
index 418d161053657..4f634cb63903b 100644
--- a/flang/lib/Evaluate/check-expression.cpp
+++ b/flang/lib/Evaluate/check-expression.cpp
@@ -390,8 +390,9 @@ std::optional<Expr<SomeType>> NonPointerInitializationExpr(const Symbol &symbol,
                 .Expand(std::move(folded));
           } else if (auto resultShape{GetShape(context, folded)}) {
             if (CheckConformance(context.messages(), symTS->shape(),
-                    *resultShape, "initialized object",
-                    "initialization expression", false, false)) {
+                    *resultShape, CheckConformanceFlags::None,
+                    "initialized object", "initialization expression")
+                    .value_or(false /*fail if not known now to conform*/)) {
               // make a constant array with adjusted lower bounds
               return ArrayConstantBoundChanger{
                   std::move(*AsConstantExtents(

diff  --git a/flang/lib/Evaluate/fold-implementation.h b/flang/lib/Evaluate/fold-implementation.h
index 94cef9bd4ba99..4dadebd47d056 100644
--- a/flang/lib/Evaluate/fold-implementation.h
+++ b/flang/lib/Evaluate/fold-implementation.h
@@ -1030,8 +1030,9 @@ auto ApplyElementwise(FoldingContext &context,
         if (rightExpr.Rank() > 0) {
           if (std::optional<Shape> rightShape{GetShape(context, rightExpr)}) {
             if (auto right{AsFlatArrayConstructor(rightExpr)}) {
-              if (CheckConformance(
-                      context.messages(), *leftShape, *rightShape)) {
+              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,
                     std::move(*left), std::move(*right));
               } else {

diff  --git a/flang/lib/Evaluate/shape.cpp b/flang/lib/Evaluate/shape.cpp
index 217270ec8b9f9..fc7dd6dfea72c 100644
--- a/flang/lib/Evaluate/shape.cpp
+++ b/flang/lib/Evaluate/shape.cpp
@@ -759,18 +759,16 @@ auto GetShapeHelper::operator()(const ProcedureRef &call) const -> Result {
   return std::nullopt;
 }
 
-// Check conformance of the passed shapes.  Only return true if we can verify
-// that they conform
-bool CheckConformance(parser::ContextualMessages &messages, const Shape &left,
-    const Shape &right, const char *leftIs, const char *rightIs,
-    bool leftScalarExpandable, bool rightScalarExpandable,
-    bool leftIsDeferredShape, bool rightIsDeferredShape) {
+// Check conformance of the passed shapes.
+std::optional<bool> CheckConformance(parser::ContextualMessages &messages,
+    const Shape &left, const Shape &right, CheckConformanceFlags::Flags flags,
+    const char *leftIs, const char *rightIs) {
   int n{GetRank(left)};
-  if (n == 0 && leftScalarExpandable) {
+  if (n == 0 && (flags & CheckConformanceFlags::LeftScalarExpandable)) {
     return true;
   }
   int rn{GetRank(right)};
-  if (rn == 0 && rightScalarExpandable) {
+  if (rn == 0 && (flags & CheckConformanceFlags::RightScalarExpandable)) {
     return true;
   }
   if (n != rn) {
@@ -787,11 +785,11 @@ bool CheckConformance(parser::ContextualMessages &messages, const Shape &left,
               j + 1, leftIs, *leftDim, rightIs, *rightDim);
           return false;
         }
-      } else if (!rightIsDeferredShape) {
-        return false;
+      } else if (!(flags & CheckConformanceFlags::RightIsDeferredShape)) {
+        return std::nullopt;
       }
-    } else if (!leftIsDeferredShape) {
-      return false;
+    } else if (!(flags & CheckConformanceFlags::LeftIsDeferredShape)) {
+      return std::nullopt;
     }
   }
   return true;

diff  --git a/flang/lib/Semantics/check-call.cpp b/flang/lib/Semantics/check-call.cpp
index bf04091d7648b..5a358c46f31f6 100644
--- a/flang/lib/Semantics/check-call.cpp
+++ b/flang/lib/Semantics/check-call.cpp
@@ -160,7 +160,8 @@ static void CheckExplicitDataArg(const characteristics::DummyDataObject &dummy,
       // Let CheckConformance accept scalars; storage association
       // cases are checked here below.
       CheckConformance(messages, dummy.type.shape(), actualType.shape(),
-          "dummy argument", "actual argument", true, true);
+          evaluate::CheckConformanceFlags::EitherScalarExpandable,
+          "dummy argument", "actual argument");
     }
   } else {
     const auto &len{actualType.LEN()};

diff  --git a/flang/lib/Semantics/expression.cpp b/flang/lib/Semantics/expression.cpp
index 80a180442498c..62cb0f1b87f3e 100644
--- a/flang/lib/Semantics/expression.cpp
+++ b/flang/lib/Semantics/expression.cpp
@@ -1655,17 +1655,21 @@ MaybeExpr ExpressionAnalyzer::Analyze(
                         "Rank-%d array value is not compatible with scalar component '%s'"_err_en_US,
                         GetRank(*valueShape), symbol->name()),
                     *symbol);
-              } else if (CheckConformance(messages, *componentShape,
-                             *valueShape, "component", "value", false,
-                             true /* can expand scalar value */)) {
-                if (GetRank(*componentShape) > 0 && GetRank(*valueShape) == 0 &&
+              } else {
+                auto checked{
+                    CheckConformance(messages, *componentShape, *valueShape,
+                        CheckConformanceFlags::RightIsExpandableDeferred,
+                        "component", "value")};
+                if (checked && *checked && GetRank(*componentShape) > 0 &&
+                    GetRank(*valueShape) == 0 &&
                     !IsExpandableScalar(*converted)) {
                   AttachDeclaration(
                       Say(expr.source,
                           "Scalar value cannot be expanded to shape of array component '%s'"_err_en_US,
                           symbol->name()),
                       *symbol);
-                } else {
+                }
+                if (checked.value_or(true)) {
                   result.Add(*symbol, std::move(*converted));
                 }
               }
@@ -3146,8 +3150,9 @@ bool ArgumentAnalyzer::CheckConformance() const {
       auto rhShape{GetShape(foldingContext, *rhs)};
       if (lhShape && rhShape) {
         return evaluate::CheckConformance(foldingContext.messages(), *lhShape,
-            *rhShape, "left operand", "right operand", true,
-            true /* scalar expansion is allowed */);
+            *rhShape, CheckConformanceFlags::EitherScalarExpandable,
+            "left operand", "right operand")
+            .value_or(false /*fail when conformance is not known now*/);
       }
     }
   }

diff  --git a/flang/lib/Semantics/pointer-assignment.cpp b/flang/lib/Semantics/pointer-assignment.cpp
index 8cf46f5a5cda7..171e2ba02477c 100644
--- a/flang/lib/Semantics/pointer-assignment.cpp
+++ b/flang/lib/Semantics/pointer-assignment.cpp
@@ -171,7 +171,7 @@ bool PointerAssignmentChecker::Check(const evaluate::FunctionRef<T> &f) {
     CHECK(frTypeAndShape);
     if (!lhsType_->IsCompatibleWith(context_.messages(), *frTypeAndShape,
             "pointer", "function result", false /*elemental*/,
-            true /*left: deferred shape*/, true /*right: deferred shape*/)) {
+            evaluate::CheckConformanceFlags::BothDeferredShape)) {
       msg = "%s is associated with the result of a reference to function '%s'"
             " whose pointer result has an incompatible type or shape"_err_en_US;
     }


        


More information about the flang-commits mailing list