[flang-commits] [flang] 75f9b18 - [flang] Compile-time checks for shape conformance on assignments

Peter Klausler via flang-commits flang-commits at lists.llvm.org
Thu Aug 18 14:57:24 PDT 2022


Author: Peter Klausler
Date: 2022-08-18T14:52:38-07:00
New Revision: 75f9b189889aae31de209e0554b3ba20998cf659

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

LOG: [flang] Compile-time checks for shape conformance on assignments

Assignment statements need to check for array shape conformance
errors that are discernable at compilation time.

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

Added: 
    flang/test/Semantics/assign10.f90

Modified: 
    flang/lib/Semantics/expression.cpp
    flang/test/Lower/Intrinsics/matmul.f90
    flang/test/Lower/array-constructor-2.f90
    flang/test/Semantics/array-constr-values.f90

Removed: 
    


################################################################################
diff  --git a/flang/lib/Semantics/expression.cpp b/flang/lib/Semantics/expression.cpp
index dacfe796f3627..39f3fb8bfc0b6 100644
--- a/flang/lib/Semantics/expression.cpp
+++ b/flang/lib/Semantics/expression.cpp
@@ -150,6 +150,7 @@ class ArgumentAnalyzer {
   bool IsIntrinsicConcat() const;
 
   bool CheckConformance();
+  bool CheckAssignmentConformance();
   bool CheckForNullPointer(const char *where = "as an operand here");
 
   // Find and return a user-defined operator or report an error.
@@ -2558,10 +2559,12 @@ void ExpressionAnalyzer::Analyze(const parser::CallStmt &callStmt) {
 const Assignment *ExpressionAnalyzer::Analyze(const parser::AssignmentStmt &x) {
   if (!x.typedAssignment) {
     ArgumentAnalyzer analyzer{*this};
-    analyzer.Analyze(std::get<parser::Variable>(x.t));
+    const auto &variable{std::get<parser::Variable>(x.t)};
+    analyzer.Analyze(variable);
     analyzer.Analyze(std::get<parser::Expr>(x.t));
     std::optional<Assignment> assignment;
     if (!analyzer.fatalErrors()) {
+      auto restorer{GetContextualMessages().SetLocation(variable.GetSource())};
       std::optional<ProcedureRef> procRef{analyzer.TryDefinedAssignment()};
       if (!procRef) {
         analyzer.CheckForNullPointer(
@@ -3478,6 +3481,28 @@ bool ArgumentAnalyzer::CheckConformance() {
   return true; // no proven problem
 }
 
+bool ArgumentAnalyzer::CheckAssignmentConformance() {
+  if (actuals_.size() == 2) {
+    const auto *lhs{actuals_.at(0).value().UnwrapExpr()};
+    const auto *rhs{actuals_.at(1).value().UnwrapExpr()};
+    if (lhs && rhs) {
+      auto &foldingContext{context_.GetFoldingContext()};
+      auto lhShape{GetShape(foldingContext, *lhs)};
+      auto rhShape{GetShape(foldingContext, *rhs)};
+      if (lhShape && rhShape) {
+        if (!evaluate::CheckConformance(foldingContext.messages(), *lhShape,
+                *rhShape, CheckConformanceFlags::RightScalarExpandable,
+                "left-hand side", "right-hand side")
+                 .value_or(true /*ok when conformance is not known now*/)) {
+          fatalErrors_ = true;
+          return false;
+        }
+      }
+    }
+  }
+  return true; // no proven problem
+}
+
 bool ArgumentAnalyzer::CheckForNullPointer(const char *where) {
   for (const std::optional<ActualArgument> &arg : actuals_) {
     if (arg) {
@@ -3579,6 +3604,9 @@ std::optional<ProcedureRef> ArgumentAnalyzer::TryDefinedAssignment() {
     if (lhsType && rhsType) {
       AddAssignmentConversion(*lhsType, *rhsType);
     }
+    if (!fatalErrors_) {
+      CheckAssignmentConformance();
+    }
     return std::nullopt; // user-defined assignment not allowed for these args
   }
   auto restorer{context_.GetContextualMessages().SetLocation(source_)};

diff  --git a/flang/test/Lower/Intrinsics/matmul.f90 b/flang/test/Lower/Intrinsics/matmul.f90
index dedd09cae99fe..2c1211d10e1d9 100644
--- a/flang/test/Lower/Intrinsics/matmul.f90
+++ b/flang/test/Lower/Intrinsics/matmul.f90
@@ -4,13 +4,13 @@
 ! Test matmul intrinsic
 
 ! CHECK-LABEL: matmul_test
-! CHECK-SAME: (%[[X:.*]]: !fir.ref<!fir.array<3x1xf32>>{{.*}}, %[[Y:.*]]: !fir.ref<!fir.array<1x3xf32>>{{.*}}, %[[Z:.*]]: !fir.ref<!fir.array<2x2xf32>>{{.*}})
+! CHECK-SAME: (%[[X:.*]]: !fir.ref<!fir.array<3x1xf32>>{{.*}}, %[[Y:.*]]: !fir.ref<!fir.array<1x3xf32>>{{.*}}, %[[Z:.*]]: !fir.ref<!fir.array<3x3xf32>>{{.*}})
 ! CHECK:  %[[RESULT_BOX_ADDR:.*]] = fir.alloca !fir.box<!fir.heap<!fir.array<?x?xf32>>>
 ! CHECK:  %[[C3:.*]] = arith.constant 3 : index
 ! CHECK:  %[[C1:.*]] = arith.constant 1 : index
 ! CHECK:  %[[C1_0:.*]] = arith.constant 1 : index
 ! CHECK:  %[[C3_1:.*]] = arith.constant 3 : index
-! CHECK:  %[[Z_BOX:.*]] = fir.array_load %[[Z]]({{.*}}) : (!fir.ref<!fir.array<2x2xf32>>, !fir.shape<2>) -> !fir.array<2x2xf32>
+! CHECK:  %[[Z_BOX:.*]] = fir.array_load %[[Z]]({{.*}}) : (!fir.ref<!fir.array<3x3xf32>>, !fir.shape<2>) -> !fir.array<3x3xf32>
 ! CHECK:  %[[X_SHAPE:.*]] = fir.shape %[[C3]], %[[C1]] : (index, index) -> !fir.shape<2>
 ! CHECK:  %[[X_BOX:.*]] = fir.embox %[[X]](%[[X_SHAPE]]) : (!fir.ref<!fir.array<3x1xf32>>, !fir.shape<2>) -> !fir.box<!fir.array<3x1xf32>>
 ! CHECK:  %[[Y_SHAPE:.*]] = fir.shape %[[C1_0]], %[[C3_1]] : (index, index) -> !fir.shape<2>
@@ -31,10 +31,10 @@
 ! CHECK:    {{.*}}fir.array_update
 ! CHECK:    fir.result
 ! CHECK:  }
-! CHECK:  fir.array_merge_store %[[Z_BOX]], %[[Z_COPY_FROM_RESULT]] to %[[Z]] : !fir.array<2x2xf32>, !fir.array<2x2xf32>, !fir.ref<!fir.array<2x2xf32>>
+! CHECK:  fir.array_merge_store %[[Z_BOX]], %[[Z_COPY_FROM_RESULT]] to %[[Z]] : !fir.array<3x3xf32>, !fir.array<3x3xf32>, !fir.ref<!fir.array<3x3xf32>>
 ! CHECK:  fir.freemem %[[RESULT_TMP]] : !fir.heap<!fir.array<?x?xf32>>
 subroutine matmul_test(x,y,z)
-  real :: x(3,1), y(1,3), z(2,2)
+  real :: x(3,1), y(1,3), z(3,3)
   z = matmul(x,y)
 end subroutine
 

diff  --git a/flang/test/Lower/array-constructor-2.f90 b/flang/test/Lower/array-constructor-2.f90
index b99b97e96213d..a54ec5483d532 100644
--- a/flang/test/Lower/array-constructor-2.f90
+++ b/flang/test/Lower/array-constructor-2.f90
@@ -146,7 +146,7 @@ end subroutine test5
 
 ! CHECK-LABEL: func @_QPtest6(
 subroutine test6(c, d, e)
-  character(5) :: c(3)
+  character(5) :: c(2)
   character(5) :: d, e
   ! CHECK: = fir.allocmem !fir.array<2x!fir.char<1,5>>
   ! CHECK: fir.call @realloc

diff  --git a/flang/test/Semantics/array-constr-values.f90 b/flang/test/Semantics/array-constr-values.f90
index 2b5198f6dea78..860a3a3d98b32 100644
--- a/flang/test/Semantics/array-constr-values.f90
+++ b/flang/test/Semantics/array-constr-values.f90
@@ -3,7 +3,7 @@
 ! C7110, C7111, C7112, C7113, C7114, C7115
 
 subroutine arrayconstructorvalues()
-  integer :: intarray(5)
+  integer :: intarray(4)
   integer(KIND=8) :: k8 = 20
 
   TYPE EMPLOYEE

diff  --git a/flang/test/Semantics/assign10.f90 b/flang/test/Semantics/assign10.f90
new file mode 100644
index 0000000000000..6b98097bfe62c
--- /dev/null
+++ b/flang/test/Semantics/assign10.f90
@@ -0,0 +1,23 @@
+! RUN: %python %S/test_errors.py %s %flang_fc1
+! Shape conformance checks on assignments
+program test
+  real :: a0, a1a(2), a1b(3), a2a(2,3), a2b(3,2)
+  a0 = 0. ! ok
+  !ERROR: No intrinsic or user-defined ASSIGNMENT(=) matches scalar REAL(4) and rank 1 array of REAL(4)
+  a0 = [0.]
+  a1a = 0. ! ok
+  a1a = [(real(j),j=1,2)] ! ok
+  !ERROR: Dimension 1 of left-hand side has extent 2, but right-hand side has extent 3
+  a1a = [(real(j),j=1,3)]
+  !ERROR: Dimension 1 of left-hand side has extent 3, but right-hand side has extent 2
+  a1b = a1a
+  !ERROR: No intrinsic or user-defined ASSIGNMENT(=) matches rank 1 array of REAL(4) and rank 2 array of REAL(4)
+  a1a = a2a
+  a1a = a2a(:,1) ! ok
+  a2a = 0. ! ok
+  a2a(:,1) = a1a ! ok
+  !ERROR: Dimension 1 of left-hand side has extent 3, but right-hand side has extent 2
+  a2a(1,:) = a1a
+  !ERROR: Dimension 1 of left-hand side has extent 2, but right-hand side has extent 3
+  a2a = a2b
+end


        


More information about the flang-commits mailing list