[flang] Roll up fixes for semantic analysis of SPEC CPU codes
authorpeter klausler <pklausler@nvidia.com>
Thu, 19 Dec 2019 01:06:13 +0000 (17:06 -0800)
committerpeter klausler <pklausler@nvidia.com>
Fri, 20 Dec 2019 02:05:58 +0000 (18:05 -0800)
- Add cases to IsDescriptor
- Fix NULLIFY in PURE checking
- Fix fotonik3d mutually-referencing derived type bug
- Fix spurious I/O error message on ALLOCATE

Original-commit: flang-compiler/f18@34b64db7da9e90109b1dfec5347b344eae7dd8ad
Reviewed-on: https://github.com/flang-compiler/f18/pull/883

13 files changed:
flang/lib/evaluate/fold.cc
flang/lib/evaluate/formatting.cc
flang/lib/evaluate/type.cc
flang/lib/semantics/assignment.cc
flang/lib/semantics/assignment.h
flang/lib/semantics/check-io.cc
flang/lib/semantics/check-io.h
flang/lib/semantics/check-nullify.cc
flang/lib/semantics/expression.h
flang/lib/semantics/resolve-names.cc
flang/lib/semantics/scope.h
flang/lib/semantics/type.cc
flang/test/semantics/call12.f90

index de06b2c..8af32c3 100644 (file)
@@ -2130,6 +2130,9 @@ Expr<TO> FoldOperation(
               Operand::category == TypeCategory::Logical) {
             return Expr<TO>{value->IsTrue()};
           }
+        } else if constexpr (std::is_same_v<Operand, TO> &&
+            FROMCAT != TypeCategory::Character) {
+          return std::move(kindExpr);  // remove needless conversion
         }
         return Expr<TO>{std::move(convert)};
       },
@@ -2143,8 +2146,12 @@ Expr<T> FoldOperation(FoldingContext &context, Parentheses<T> &&x) {
   if (auto value{GetScalarConstantValue<T>(operand)}) {
     // Preserve parentheses, even around constants.
     return Expr<T>{Parentheses<T>{Expr<T>{Constant<T>{*value}}}};
+  } else if (std::holds_alternative<Parentheses<T>>(operand.u)) {
+    // ((x)) -> (x)
+    return std::move(operand);
+  } else {
+    return Expr<T>{Parentheses<T>{std::move(operand)}};
   }
-  return Expr<T>{Parentheses<T>{std::move(operand)}};
 }
 
 template<typename T>
index 6cde8a9..d60b236 100644 (file)
@@ -226,6 +226,10 @@ template<typename T> static Precedence ToPrecedence(const Constant<T> &x) {
 template<typename T> constexpr Precedence ToPrecedence(const Parentheses<T> &) {
   return Precedence::Parenthesize;
 }
+template<int KIND>
+constexpr Precedence ToPrecedence(const ComplexConstructor<KIND> &) {
+  return Precedence::Parenthesize;
+}
 
 template<typename T> static Precedence ToPrecedence(const Expr<T> &expr) {
   return std::visit([](const auto &x) { return ToPrecedence(x); }, expr.u);
@@ -260,7 +264,25 @@ constexpr OperatorSpelling SpellOperator(const Negate<A> &) {
 }
 template<int KIND>
 static OperatorSpelling SpellOperator(const ComplexComponent<KIND> &x) {
-  return OperatorSpelling{x.isImaginaryPart ? "AIMAG(" : "REAL(", "", ")"};
+  if (x.isImaginaryPart) {
+    return {"aimag(", "", ")"};
+  } else if constexpr (KIND == 2) {
+    return {"real(", "", ",kind=2)"};
+  } else if constexpr (KIND == 3) {
+    return {"real(", "", ",kind=3)"};
+  } else if constexpr (KIND == 4) {
+    return {"real(", "", ",kind=4)"};
+  } else if constexpr (KIND == 8) {
+    return {"real(", "", ",kind=8)"};
+  } else if constexpr (KIND == 10) {
+    return {"real(", "", ",kind=10)"};
+  } else if constexpr (KIND == 16) {
+    return {"real(", "", ",kind=16)"};
+  } else {
+    static_assert(KIND == 2 || KIND == 3 || KIND == 4 || KIND == 8 ||
+            KIND == 10 || KIND == 16,
+        "bad KIND");
+  }
 }
 template<int KIND> constexpr OperatorSpelling SpellOperator(const Not<KIND> &) {
   return OperatorSpelling{".NOT.", "", ""};
@@ -299,7 +321,7 @@ constexpr OperatorSpelling SpellOperator(const RealToIntPower<A> &) {
 template<typename A>
 static OperatorSpelling SpellOperator(const Extremum<A> &x) {
   return OperatorSpelling{
-      x.ordering == Ordering::Less ? "MIN(" : "MAX(", ",", ")"};
+      x.ordering == Ordering::Less ? "min(" : "max(", ",", ")"};
 }
 template<int KIND>
 constexpr OperatorSpelling SpellOperator(const Concat<KIND> &) {
index 621d8c1..58271b7 100644 (file)
@@ -54,28 +54,38 @@ static bool IsDescriptor(const ProcEntityDetails &details) {
   return details.HasExplicitInterface();
 }
 
-bool IsDescriptor(const Symbol &symbol0) {
-  const Symbol &symbol{evaluate::ResolveAssociations(symbol0)};
-  if (const auto *objectDetails{symbol.detailsIf<ObjectEntityDetails>()}) {
-    return IsAllocatableOrPointer(symbol) || IsDescriptor(*objectDetails);
-  } else if (const auto *procDetails{symbol.detailsIf<ProcEntityDetails>()}) {
-    if (symbol.attrs().test(Attr::POINTER) ||
-        symbol.attrs().test(Attr::EXTERNAL)) {
-      return IsDescriptor(*procDetails);
-    }
-  } else if (const auto *assocDetails{symbol.detailsIf<AssocEntityDetails>()}) {
-    if (const auto &expr{assocDetails->expr()}) {
-      if (expr->Rank() > 0) {
-        return true;
-      }
-      if (const auto dynamicType{expr->GetType()}) {
-        if (dynamicType->RequiresDescriptor()) {
-          return true;
-        }
-      }
-    }
-  }
-  return false;
+bool IsDescriptor(const Symbol &symbol) {
+  return std::visit(
+      common::visitors{
+          [&](const ObjectEntityDetails &d) {
+            return IsAllocatableOrPointer(symbol) || IsDescriptor(d);
+          },
+          [&](const ProcEntityDetails &d) {
+            return (symbol.attrs().test(Attr::POINTER) ||
+                       symbol.attrs().test(Attr::EXTERNAL)) &&
+                IsDescriptor(d);
+          },
+          [](const AssocEntityDetails &d) {
+            if (const auto &expr{d.expr()}) {
+              if (expr->Rank() > 0) {
+                return true;
+              }
+              if (const auto dynamicType{expr->GetType()}) {
+                if (dynamicType->RequiresDescriptor()) {
+                  return true;
+                }
+              }
+            }
+            return false;
+          },
+          [](const SubprogramDetails &d) {
+            return d.isFunction() && IsDescriptor(d.result());
+          },
+          [](const UseDetails &d) { return IsDescriptor(d.symbol()); },
+          [](const HostAssocDetails &d) { return IsDescriptor(d.symbol()); },
+          [](const auto &) { return false; },
+      },
+      symbol.details());
 }
 }
 
index 526fe47..38b01bf 100644 (file)
@@ -586,11 +586,13 @@ static const char *WhyBaseObjectIsSuspicious(
 
 // Checks C1594(1,2)
 void CheckDefinabilityInPureScope(parser::ContextualMessages &messages,
-    const Symbol &lhs, const Scope &scope) {
-  if (const char *why{WhyBaseObjectIsSuspicious(lhs, scope)}) {
-    evaluate::SayWithDeclaration(messages, lhs,
-        "A PURE subprogram may not define '%s' because it is %s"_err_en_US,
-        lhs.name(), why);
+    const Symbol &lhs, const Scope &context, const Scope &pure) {
+  if (pure.symbol()) {
+    if (const char *why{WhyBaseObjectIsSuspicious(lhs, context)}) {
+      evaluate::SayWithDeclaration(messages, lhs,
+          "PURE subprogram '%s' may not define '%s' because it is %s"_err_en_US,
+          pure.symbol()->name(), lhs.name(), why);
+    }
   }
 }
 
@@ -624,13 +626,13 @@ void CheckCopyabilityInPureScope(parser::ContextualMessages &messages,
 void AssignmentContext::CheckForPureContext(const SomeExpr &lhs,
     const SomeExpr &rhs, parser::CharBlock source, bool isPointerAssignment) {
   const Scope &scope{context_.FindScope(source)};
-  if (FindPureProcedureContaining(scope)) {
+  if (const Scope * pure{FindPureProcedureContaining(scope)}) {
     parser::ContextualMessages messages{at_, &context_.messages()};
     if (evaluate::ExtractCoarrayRef(lhs)) {
       messages.Say(
           "A PURE subprogram may not define a coindexed object"_err_en_US);
     } else if (const Symbol * base{GetFirstSymbol(lhs)}) {
-      CheckDefinabilityInPureScope(messages, *base, scope);
+      CheckDefinabilityInPureScope(messages, *base, scope, *pure);
     }
     if (isPointerAssignment) {
       if (const Symbol * base{GetFirstSymbol(rhs)}) {
index 30af8a2..75a2196 100644 (file)
@@ -55,8 +55,8 @@ extern template class Fortran::common::Indirection<
 
 namespace Fortran::semantics {
 // Applies checks from C1594(1-2) on definitions in PURE subprograms
-void CheckDefinabilityInPureScope(
-    parser::ContextualMessages &, const Symbol &, const Scope &);
+void CheckDefinabilityInPureScope(parser::ContextualMessages &, const Symbol &,
+    const Scope &context, const Scope &pure);
 // Applies checks from C1594(5-6) on copying pointers in PURE subprograms
 void CheckCopyabilityInPureScope(parser::ContextualMessages &,
     const evaluate::Expr<evaluate::SomeType> &, const Scope &);
index 2caa74a..3c3e20b 100644 (file)
@@ -425,35 +425,39 @@ void IoChecker::Enter(const parser::StatusExpr &spec) {
 }
 
 void IoChecker::Enter(const parser::StatVariable &) {
-  SetSpecifier(IoSpecKind::Iostat);
+  if (stmt_ == IoStmtKind::None) {
+    // ALLOCATE & DEALLOCATE
+  } else {
+    SetSpecifier(IoSpecKind::Iostat);
+  }
 }
 
 void IoChecker::Leave(const parser::BackspaceStmt &) {
   CheckForPureSubprogram();
   CheckForRequiredSpecifier(
       flags_.test(Flag::NumberUnit), "UNIT number");  // C1240
-  stmt_ = IoStmtKind::None;
+  Done();
 }
 
 void IoChecker::Leave(const parser::CloseStmt &) {
   CheckForPureSubprogram();
   CheckForRequiredSpecifier(
       flags_.test(Flag::NumberUnit), "UNIT number");  // C1208
-  stmt_ = IoStmtKind::None;
+  Done();
 }
 
 void IoChecker::Leave(const parser::EndfileStmt &) {
   CheckForPureSubprogram();
   CheckForRequiredSpecifier(
       flags_.test(Flag::NumberUnit), "UNIT number");  // C1240
-  stmt_ = IoStmtKind::None;
+  Done();
 }
 
 void IoChecker::Leave(const parser::FlushStmt &) {
   CheckForPureSubprogram();
   CheckForRequiredSpecifier(
       flags_.test(Flag::NumberUnit), "UNIT number");  // C1243
-  stmt_ = IoStmtKind::None;
+  Done();
 }
 
 void IoChecker::Leave(const parser::InquireStmt &stmt) {
@@ -466,7 +470,7 @@ void IoChecker::Leave(const parser::InquireStmt &stmt) {
     CheckForProhibitedSpecifier(IoSpecKind::File, IoSpecKind::Unit);  // C1246
     CheckForRequiredSpecifier(IoSpecKind::Id, IoSpecKind::Pending);  // C1248
   }
-  stmt_ = IoStmtKind::None;
+  Done();
 }
 
 void IoChecker::Leave(const parser::OpenStmt &) {
@@ -499,12 +503,12 @@ void IoChecker::Leave(const parser::OpenStmt &) {
     CheckForProhibitedSpecifier(flags_.test(Flag::AccessStream),
         "STATUS='STREAM'", IoSpecKind::Recl);  // 12.5.6.15
   }
-  stmt_ = IoStmtKind::None;
+  Done();
 }
 
 void IoChecker::Leave(const parser::PrintStmt &) {
   CheckForPureSubprogram();
-  stmt_ = IoStmtKind::None;
+  Done();
 }
 
 void IoChecker::Leave(const parser::ReadStmt &) {
@@ -512,6 +516,7 @@ void IoChecker::Leave(const parser::ReadStmt &) {
     CheckForPureSubprogram();
   }
   if (!flags_.test(Flag::IoControlList)) {
+    Done();
     return;
   }
   LeaveReadWrite();
@@ -525,21 +530,21 @@ void IoChecker::Leave(const parser::ReadStmt &) {
       "FMT or NML");  // C1227
   CheckForRequiredSpecifier(
       IoSpecKind::Pad, flags_.test(Flag::FmtOrNml), "FMT or NML");  // C1227
-  stmt_ = IoStmtKind::None;
+  Done();
 }
 
 void IoChecker::Leave(const parser::RewindStmt &) {
   CheckForRequiredSpecifier(
       flags_.test(Flag::NumberUnit), "UNIT number");  // C1240
   CheckForPureSubprogram();
-  stmt_ = IoStmtKind::None;
+  Done();
 }
 
 void IoChecker::Leave(const parser::WaitStmt &) {
   CheckForRequiredSpecifier(
       flags_.test(Flag::NumberUnit), "UNIT number");  // C1237
   CheckForPureSubprogram();
-  stmt_ = IoStmtKind::None;
+  Done();
 }
 
 void IoChecker::Leave(const parser::WriteStmt &) {
@@ -557,7 +562,7 @@ void IoChecker::Leave(const parser::WriteStmt &) {
   CheckForRequiredSpecifier(IoSpecKind::Delim,
       flags_.test(Flag::StarFmt) || specifierSet_.test(IoSpecKind::Nml),
       "FMT=* or NML");  // C1228
-  stmt_ = IoStmtKind::None;
+  Done();
 }
 
 void IoChecker::LeaveReadWrite() const {
index fafbc53..f501128 100644 (file)
@@ -134,10 +134,12 @@ private:
     flags_.reset();
   }
 
+  void Done() { stmt_ = IoStmtKind::None; }
+
   void CheckForPureSubprogram() const;
 
   SemanticsContext &context_;
-  IoStmtKind stmt_ = IoStmtKind::None;
+  IoStmtKind stmt_{IoStmtKind::None};
   common::EnumSet<IoSpecKind, common::IoSpecKind_enumSize> specifierSet_;
   common::EnumSet<Flag, Flag_enumSize> flags_;
 };
index 2b55616..f7e1ca3 100644 (file)
@@ -25,7 +25,7 @@ namespace Fortran::semantics {
 void NullifyChecker::Leave(const parser::NullifyStmt &nullifyStmt) {
   CHECK(context_.location());
   const Scope &scope{context_.FindScope(*context_.location())};
-  bool isPure{FindPureProcedureContaining(scope)};
+  const Scope *pure{FindPureProcedureContaining(scope)};
   parser::ContextualMessages messages{
       *context_.location(), &context_.messages()};
   for (const parser::PointerObject &pointerObject : nullifyStmt.v) {
@@ -41,8 +41,8 @@ void NullifyChecker::Leave(const parser::NullifyStmt &nullifyStmt) {
               } else if (!IsPointer(symbol)) {  // C951
                 messages.Say(name.source,
                     "name in NULLIFY statement must have the POINTER attribute"_err_en_US);
-              } else if (isPure) {
-                CheckDefinabilityInPureScope(messages, symbol, scope);
+              } else if (pure) {
+                CheckDefinabilityInPureScope(messages, symbol, scope, *pure);
               }
             },
             [&](const parser::StructureComponent &structureComponent) {
@@ -51,8 +51,11 @@ void NullifyChecker::Leave(const parser::NullifyStmt &nullifyStmt) {
                 if (!IsPointer(*structureComponent.component.symbol)) {  // C951
                   messages.Say(structureComponent.component.source,
                       "component in NULLIFY statement must have the POINTER attribute"_err_en_US);
-                } else if (const Symbol * symbol{GetFirstSymbol(checked)}) {
-                  CheckDefinabilityInPureScope(messages, *symbol, scope);
+                } else if (pure) {
+                  if (const Symbol * symbol{GetFirstSymbol(checked)}) {
+                    CheckDefinabilityInPureScope(
+                        messages, *symbol, scope, *pure);
+                  }
                 }
               }
             },
@@ -67,4 +70,4 @@ void NullifyChecker::Leave(const parser::NullifyStmt &nullifyStmt) {
   // Some dependencies can be found compile time or at
   // runtime, but for now we choose to skip such checks.
 }
-}  // namespace Fortran::semantics
+}
index f1b16c0..33ed02d 100644 (file)
@@ -101,8 +101,8 @@ struct SetExprHelper {
   template<typename T> void Set(const T &x) {
     if constexpr (ConstraintTrait<T>) {
       Set(x.thing);
-    } else {
-      static_assert("bad type");
+    } else if constexpr (WrapperTrait<T>) {
+      Set(x.v);
     }
   }
 
index b67008a..75a6706 100644 (file)
@@ -5916,7 +5916,7 @@ bool ResolveNamesVisitor::BeginScope(const ProgramTree &node) {
 }
 
 // Some analyses and checks, such as the processing of initializers of
-// pointers, is deferred until all of the pertinent specification parts
+// pointers, are deferred until all of the pertinent specification parts
 // have been visited.  This deferred processing enables the use of forward
 // references in these circumstances.
 class DeferredCheckVisitor {
@@ -6020,7 +6020,8 @@ void ResolveNamesVisitor::FinishSpecificationParts(const ProgramTree &node) {
 // type parameter values of a particular instantiation.
 void ResolveNamesVisitor::FinishDerivedTypeInstantiation(Scope &scope) {
   CHECK(scope.IsDerivedType() && !scope.symbol());
-  if (const DerivedTypeSpec * spec{scope.derivedTypeSpec()}) {
+  if (DerivedTypeSpec * spec{scope.derivedTypeSpec()}) {
+    spec->Instantiate(currScope(), context());
     const Symbol &origTypeSymbol{spec->typeSymbol()};
     if (const Scope * origTypeScope{origTypeSymbol.scope()}) {
       CHECK(origTypeScope->IsDerivedType() &&
index 846ee6b..6cff736 100644 (file)
@@ -197,9 +197,8 @@ public:
   void add_importName(const SourceName &);
 
   const DerivedTypeSpec *derivedTypeSpec() const { return derivedTypeSpec_; }
-  void set_derivedTypeSpec(const DerivedTypeSpec &spec) {
-    derivedTypeSpec_ = &spec;
-  }
+  DerivedTypeSpec *derivedTypeSpec() { return derivedTypeSpec_; }
+  void set_derivedTypeSpec(DerivedTypeSpec &spec) { derivedTypeSpec_ = &spec; }
 
   // The range of the source of this and nested scopes.
   const parser::CharBlock &sourceRange() const { return sourceRange_; }
@@ -234,7 +233,7 @@ private:
   std::string chars_;
   std::optional<ImportKind> importKind_;
   std::set<SourceName> importNames_;
-  const DerivedTypeSpec *derivedTypeSpec_{nullptr};  // dTS->scope() == this
+  DerivedTypeSpec *derivedTypeSpec_{nullptr};  // dTS->scope() == this
   // When additional data members are added to Scope, remember to
   // copy them, if appropriate, in InstantiateDerivedType().
 
index 40289e1..1c92bb8 100644 (file)
@@ -204,8 +204,11 @@ void DerivedTypeSpec::Instantiate(
       const Symbol &symbol{*pair.second};
       if (const DeclTypeSpec * type{symbol.GetType()}) {
         if (const DerivedTypeSpec * derived{type->AsDerived()}) {
-          auto &instantiatable{*const_cast<DerivedTypeSpec *>(derived)};
-          instantiatable.Instantiate(containingScope, context);
+          if (!(derived->IsForwardReferenced() &&
+                  IsAllocatableOrPointer(symbol))) {
+            auto &instantiatable{*const_cast<DerivedTypeSpec *>(derived)};
+            instantiatable.Instantiate(containingScope, context);
+          }
         }
       }
     }
index cbf61f3..817868f 100644 (file)
@@ -42,21 +42,21 @@ module m
     type(hasCoarray), pointer :: hcp
     integer :: n
     common /block/ y
-    !ERROR: A PURE subprogram may not define 'x' because it is host-associated
+    !ERROR: PURE subprogram 'test' may not define 'x' because it is host-associated
     x%a = 0.
-    !ERROR: A PURE subprogram may not define 'y' because it is in a COMMON block
+    !ERROR: PURE subprogram 'test' may not define 'y' because it is in a COMMON block
     y%a = 0. ! C1594(1)
-    !ERROR: A PURE subprogram may not define 'useassociated' because it is USE-associated
+    !ERROR: PURE subprogram 'test' may not define 'useassociated' because it is USE-associated
     useassociated = 0.  ! C1594(1)
-    !ERROR: A PURE subprogram may not define 'ptr' because it is a POINTER dummy argument of a PURE function
+    !ERROR: PURE subprogram 'test' may not define 'ptr' because it is a POINTER dummy argument of a PURE function
     ptr%a = 0. ! C1594(1)
-    !ERROR: A PURE subprogram may not define 'in' because it is an INTENT(IN) dummy argument
+    !ERROR: PURE subprogram 'test' may not define 'in' because it is an INTENT(IN) dummy argument
     in%a = 0. ! C1594(1)
     !ERROR: A PURE subprogram may not define a coindexed object
     hcp%co[1] = 0. ! C1594(1)
-    !ERROR: A PURE subprogram may not define 'ptr' because it is a POINTER dummy argument of a PURE function
+    !ERROR: PURE subprogram 'test' may not define 'ptr' because it is a POINTER dummy argument of a PURE function
     ptr => z ! C1594(2)
-    !ERROR: A PURE subprogram may not define 'ptr' because it is a POINTER dummy argument of a PURE function
+    !ERROR: PURE subprogram 'test' may not define 'ptr' because it is a POINTER dummy argument of a PURE function
     nullify(ptr) ! C1594(2), 19.6.8
     !ERROR: A PURE subprogram may not use 'ptr' as the target of pointer assignment because it is a POINTER dummy argument of a PURE function
     ptr2 => ptr ! C1594(3)
@@ -79,7 +79,7 @@ module m
    contains
     pure subroutine internal
       type(hasPtr) :: localhp
-      !ERROR: A PURE subprogram may not define 'z' because it is host-associated
+      !ERROR: PURE subprogram 'internal' may not define 'z' because it is host-associated
       z%a = 0.
       !ERROR: Externally visible object 'z' may not be associated with pointer component 'p' in a PURE procedure
       localhp = hasPtr(z%a)