[flang] Handle SAVE attribute and statement
authorTim Keith <tkeith@nvidia.com>
Thu, 21 Feb 2019 01:45:39 +0000 (17:45 -0800)
committerTim Keith <tkeith@nvidia.com>
Thu, 21 Feb 2019 16:59:38 +0000 (08:59 -0800)
As with COMMON blocks, we can't completely check SAVE statements and
attributes until the end of the specification part when we have seen
full declarations of entities. So when SAVE is specified, add it to one
of the two sets in `saveInfo_`. At the end of the specification part,
check that those entities can have SAVE applied and set it if it is
not already implicitly set (e.g. due to being in a module). Also apply
the "global" SAVE if present (i.e. setting it on every applicable
entity).

Add `IsDummy()` and `IsFuncResult()` to `Symbol` to simplify some of
the checks. Also detect attempts to put a function result in a common
block.

Original-commit: flang-compiler/f18@af19c02baca371ffff13722fa17792a1f26e7bcd
Reviewed-on: https://github.com/flang-compiler/f18/pull/298

flang/lib/semantics/resolve-names.cc
flang/lib/semantics/symbol.cc
flang/lib/semantics/symbol.h
flang/test/semantics/CMakeLists.txt
flang/test/semantics/resolve42.f90
flang/test/semantics/resolve45.f90 [new file with mode: 0644]

index 3adadbb..14929fb 100644 (file)
@@ -697,6 +697,7 @@ public:
   void Post(const parser::CommonStmt::Block &);
   bool Pre(const parser::CommonBlockObject &);
   void Post(const parser::CommonBlockObject &);
+  bool Pre(const parser::SaveStmt &);
 
 protected:
   bool BeginDecl();
@@ -716,6 +717,7 @@ protected:
   bool CheckAccessibleComponent(const SourceName &, const Symbol &);
   void CheckScalarIntegerType(const parser::Name &);
   void CheckCommonBlocks();
+  void CheckSaveStmts();
 
 private:
   // The attribute corresponding to the statement containing an ObjectDecl
@@ -739,6 +741,12 @@ private:
     Symbol *curr{nullptr};  // common block currently being processed
     std::set<SourceName> names;  // names in any common block of scope
   } commonBlockInfo_;
+  // Info about about SAVE statements and attributes in current scope
+  struct {
+    const SourceName *saveAll{nullptr};  // "SAVE" without entity list
+    std::set<SourceName> entities;  // names of entities with save attr
+    std::set<SourceName> commons;  // names of common blocks with save attr
+  } saveInfo_;
   // In a ProcedureDeclarationStmt or ProcComponentDefStmt, this is
   // the interface name, if any.
   const parser::Name *interfaceName_{nullptr};
@@ -757,6 +765,10 @@ private:
   ParamValue GetParamValue(const parser::TypeParamValue &);
   Symbol &MakeCommonBlockSymbol(const parser::Name &);
   void CheckCommonBlockDerivedType(const SourceName &, const Symbol &);
+  std::optional<MessageFixedText> CheckSaveAttr(const Symbol &);
+  Attrs HandleSaveName(const SourceName &, Attrs);
+  void AddSaveName(std::set<SourceName> &, const SourceName &);
+  void SetSaveAttr(Symbol &);
 
   // Declare an object or procedure entity.
   // T is one of: EntityDetails, ObjectEntityDetails, ProcEntityDetails
@@ -2235,6 +2247,7 @@ void SubprogramVisitor::Post(const parser::FunctionStmt &stmt) {
   }
   // add function result to function scope
   EntityDetails funcResultDetails;
+  funcResultDetails.set_funcResult(true);
   if (auto *type{GetDeclTypeSpec()}) {
     funcResultDetails.set_type(*type);
   }
@@ -2456,7 +2469,7 @@ void DeclarationVisitor::Post(const parser::EntityDecl &x) {
   // TODO: may be under StructureStmt
   const auto &name{std::get<parser::ObjectName>(x.t)};
   // TODO: CoarraySpec, CharLength, Initialization
-  Attrs attrs{attrs_ ? *attrs_ : Attrs{}};
+  Attrs attrs{attrs_ ? HandleSaveName(name.source, *attrs_) : Attrs{}};
   Symbol &symbol{DeclareUnknownEntity(name, attrs)};
   if (auto &init{std::get<std::optional<parser::Initialization>>(x.t)}) {
     if (ConvertToObjectEntity(symbol)) {
@@ -2559,7 +2572,7 @@ bool DeclarationVisitor::HandleAttributeStmt(
 }
 Symbol &DeclarationVisitor::HandleAttributeStmt(
     Attr attr, const parser::Name &name) {
-  auto *symbol{FindSymbol(name)};
+  auto *symbol{FindInScope(currScope(), name)};
   if (symbol) {
     // symbol was already there: set attribute on it
     if (attr == Attr::ASYNCHRONOUS || attr == Attr::VOLATILE) {
@@ -2573,6 +2586,7 @@ Symbol &DeclarationVisitor::HandleAttributeStmt(
     symbol = &MakeSymbol(name, EntityDetails{});
   }
   symbol->attrs().set(attr);
+  symbol->attrs() = HandleSaveName(name.source, symbol->attrs());
   return *symbol;
 }
 
@@ -3016,6 +3030,7 @@ void DeclarationVisitor::Post(const parser::ProcInterface &x) {
 }
 
 void DeclarationVisitor::Post(const parser::ProcDecl &x) {
+  const auto &name{std::get<parser::Name>(x.t)};
   ProcInterface interface;
   if (interfaceName_) {
     if (auto *symbol{FindExplicitInterface(*interfaceName_)}) {
@@ -3024,11 +3039,10 @@ void DeclarationVisitor::Post(const parser::ProcDecl &x) {
   } else if (auto *type{GetDeclTypeSpec()}) {
     interface.set_type(*type);
   }
-  auto attrs{GetAttrs()};
+  auto attrs{HandleSaveName(name.source, GetAttrs())};
   if (currScope().kind() != Scope::Kind::DerivedType) {
     attrs.set(Attr::EXTERNAL);
   }
-  const auto &name{std::get<parser::Name>(x.t)};
   DeclareProcEntity(name, attrs, interface);
 }
 
@@ -3279,6 +3293,116 @@ void DeclarationVisitor::Post(const parser::CommonBlockObject &x) {
   }
 }
 
+bool DeclarationVisitor::Pre(const parser::SaveStmt &x) {
+  if (x.v.empty()) {
+    saveInfo_.saveAll = currStmtSource();
+  } else {
+    for (const parser::SavedEntity &y : x.v) {
+      auto kind{std::get<parser::SavedEntity::Kind>(y.t)};
+      const auto &name{std::get<parser::Name>(y.t)};
+      if (kind == parser::SavedEntity::Kind::Common) {
+        MakeCommonBlockSymbol(name);
+        AddSaveName(saveInfo_.commons, name.source);
+      } else {
+        HandleAttributeStmt(Attr::SAVE, name);
+      }
+    }
+  }
+  return false;
+}
+
+void DeclarationVisitor::CheckSaveStmts() {
+  for (const SourceName &name : saveInfo_.entities) {
+    auto *symbol{FindInScope(currScope(), name)};
+    if (!symbol) {
+      // error was reported
+    } else if (saveInfo_.saveAll) {
+      // C889 - note that pgi, ifort, xlf do not enforce this constraint
+      Say2(name,
+          "Explicit SAVE of '%s' is redundant due to global SAVE statement"_err_en_US,
+          *saveInfo_.saveAll, "Global SAVE statement"_en_US);
+    } else if (auto msg{CheckSaveAttr(*symbol)}) {
+      Say(name, *msg);
+    } else {
+      SetSaveAttr(*symbol);
+    }
+  }
+  for (const SourceName &name : saveInfo_.commons) {
+    if (auto *symbol{currScope().FindCommonBlock(name)}) {
+      auto &objects{symbol->get<CommonBlockDetails>().objects()};
+      if (objects.empty()) {
+        Say(name,
+            "'%s' appears as a COMMON block in a SAVE statement but not in"
+            " a COMMON statement"_err_en_US);
+      } else {
+        for (Symbol *object : symbol->get<CommonBlockDetails>().objects()) {
+          SetSaveAttr(*object);
+        }
+      }
+    }
+  }
+  if (saveInfo_.saveAll) {
+    // Apply SAVE attribute to applicable symbols
+    for (auto pair : currScope()) {
+      auto &symbol{*pair.second};
+      if (!CheckSaveAttr(symbol)) {
+        SetSaveAttr(symbol);
+      }
+    }
+  }
+  saveInfo_ = {};
+}
+
+// If SAVE attribute can't be set on symbol, return error message.
+std::optional<MessageFixedText> DeclarationVisitor::CheckSaveAttr(
+    const Symbol &symbol) {
+  std::optional<MessageFixedText> msg;
+  if (symbol.IsDummy()) {
+    return "SAVE attribute may not be applied to dummy argument '%s'"_err_en_US;
+  } else if (symbol.IsFuncResult()) {
+    return "SAVE attribute may not be applied to function result '%s'"_err_en_US;
+  } else if (symbol.has<ProcEntityDetails>() &&
+      !symbol.attrs().test(Attr::POINTER)) {
+    return "Procedure '%s' with SAVE attribute must also have POINTER attribute"_err_en_US;
+  } else {
+    return std::nullopt;
+  }
+}
+
+// Instead of setting SAVE attribute, record the name in saveInfo_.entities.
+Attrs DeclarationVisitor::HandleSaveName(const SourceName &name, Attrs attrs) {
+  if (attrs.test(Attr::SAVE)) {
+    attrs.set(Attr::SAVE, false);
+    AddSaveName(saveInfo_.entities, name);
+  }
+  return attrs;
+}
+
+// Record a name in a set of those to be saved.
+void DeclarationVisitor::AddSaveName(
+    std::set<SourceName> &set, const SourceName &name) {
+  auto pair{set.insert(name)};
+  if (!pair.second) {
+    Say2(name, "SAVE attribute was already specified on '%s'"_err_en_US,
+        *pair.first, "Previous specification of SAVE attribute"_en_US);
+  }
+}
+
+// Set the SAVE attribute on symbol unless it is implicitly saved anyway.
+void DeclarationVisitor::SetSaveAttr(Symbol &symbol) {
+  auto scopeKind{symbol.owner().kind()};
+  if (scopeKind == Scope::Kind::MainProgram ||
+      scopeKind == Scope::Kind::Module) {
+    return;
+  }
+  if (const auto *details{symbol.detailsIf<ObjectEntityDetails>()}) {
+    if (details->init()) {
+      return;
+    }
+  }
+  symbol.attrs().set(Attr::SAVE);
+}
+
 // Check types of common block objects, now that they are known.
 void DeclarationVisitor::CheckCommonBlocks() {
   // check for empty common blocks
@@ -3304,11 +3428,13 @@ void DeclarationVisitor::CheckCommonBlocks() {
     } else if (attrs.test(Attr::BIND_C)) {
       Say(name,
           "Variable '%s' with BIND attribute may not appear in a COMMON block"_err_en_US);
-    } else if (const auto &details{symbol->get<ObjectEntityDetails>()};
-               details.isDummy()) {
+    } else if (symbol->IsDummy()) {
       Say(name,
           "Dummy argument '%s' may not appear in a COMMON block"_err_en_US);
-    } else if (const DeclTypeSpec * type{details.type()}) {
+    } else if (symbol->IsFuncResult()) {
+      Say(name,
+          "Function result '%s' may not appear in a COMMON block"_err_en_US);
+    } else if (const DeclTypeSpec * type{symbol->GetType()}) {
       if (type->category() == DeclTypeSpec::ClassStar) {
         Say(name,
             "Unlimited polymorphic pointer '%s' may not appear in a COMMON block"_err_en_US);
@@ -4106,7 +4232,8 @@ void ResolveNamesVisitor::Post(const parser::ProcedureDesignator &x) {
       }
       symbol->attrs().set(Attr::EXTERNAL);
       if (!symbol->has<ProcEntityDetails>()) {
-        symbol->set_details(ProcEntityDetails{});
+        // symbol->set_details(ProcEntityDetails{});
+        ConvertToProcEntity(*symbol);
       }
       if (const auto type{GetImplicitType(*symbol)}) {
         symbol->get<ProcEntityDetails>().interface().set_type(*type);
@@ -4251,6 +4378,7 @@ void ResolveNamesVisitor::Post(const parser::SpecificationPart &) {
       symbol.set(Symbol::Flag::Subroutine);
     }
   }
+  CheckSaveStmts();
   CheckCommonBlocks();
 }
 
index 6e978cf..de708f9 100644 (file)
@@ -215,6 +215,26 @@ void Symbol::SetType(const DeclTypeSpec &type) {
       details_);
 }
 
+bool Symbol::IsDummy() const {
+  return std::visit(
+      common::visitors{[](const EntityDetails &x) { return x.isDummy(); },
+          [](const ObjectEntityDetails &x) { return x.isDummy(); },
+          [](const ProcEntityDetails &x) { return x.isDummy(); },
+          [](const HostAssocDetails &x) { return x.symbol().IsDummy(); },
+          [](const auto &) { return false; }},
+      details_);
+}
+
+bool Symbol::IsFuncResult() const {
+  return std::visit(
+      common::visitors{[](const EntityDetails &x) { return x.isFuncResult(); },
+          [](const ObjectEntityDetails &x) { return x.isFuncResult(); },
+          [](const ProcEntityDetails &x) { return x.isFuncResult(); },
+          [](const HostAssocDetails &x) { return x.symbol().IsFuncResult(); },
+          [](const auto &) { return false; }},
+      details_);
+}
+
 bool Symbol::IsObjectArray() const {
   const auto *details{std::get_if<ObjectEntityDetails>(&details_)};
   return details && details->IsArray();
@@ -245,6 +265,12 @@ ObjectEntityDetails::ObjectEntityDetails(EntityDetails &&d)
   : EntityDetails(d) {}
 
 std::ostream &operator<<(std::ostream &os, const EntityDetails &x) {
+  if (x.isDummy()) {
+    os << " dummy";
+  }
+  if (x.isFuncResult()) {
+    os << " funcResult";
+  }
   if (x.type()) {
     os << " type: " << *x.type();
   }
index 18c0479..aa32129 100644 (file)
@@ -110,11 +110,14 @@ public:
   void set_type(const DeclTypeSpec &);
   void ReplaceType(const DeclTypeSpec &);
   bool isDummy() const { return isDummy_; }
+  bool isFuncResult() const { return isFuncResult_; }
+  void set_funcResult(bool x) { isFuncResult_ = x; }
   MaybeExpr bindName() const { return bindName_; }
   void set_bindName(MaybeExpr &&expr) { bindName_ = std::move(expr); }
 
 private:
   bool isDummy_;
+  bool isFuncResult_{false};
   const DeclTypeSpec *type_{nullptr};
   MaybeExpr bindName_;
   friend std::ostream &operator<<(std::ostream &, const EntityDetails &);
@@ -276,13 +279,14 @@ private:
 
 class CommonBlockDetails {
 public:
-  SymbolList objects() const { return objects_; }
+  std::list<Symbol *> &objects() { return objects_; }
+  const std::list<Symbol *> &objects() const { return objects_; }
   void add_object(Symbol &object) { objects_.push_back(&object); }
   MaybeExpr bindName() const { return bindName_; }
   void set_bindName(MaybeExpr &&expr) { bindName_ = std::move(expr); }
 
 private:
-  SymbolList objects_;
+  std::list<Symbol *> objects_;
   MaybeExpr bindName_;
 };
 
@@ -480,6 +484,8 @@ public:
 
   void SetType(const DeclTypeSpec &);
 
+  bool IsDummy() const;
+  bool IsFuncResult() const;
   bool IsObjectArray() const;
   bool IsSubprogram() const;
   bool IsSeparateModuleProc() const;
index 016ac62..e6934d3 100644 (file)
@@ -70,6 +70,7 @@ set(ERROR_TESTS
   resolve42.f90
   resolve43.f90
   resolve44.f90
+  resolve45.f90
   structconst01.f90
 )
 
index 22461c9..b44e2a5 100644 (file)
@@ -40,11 +40,13 @@ subroutine s5
   real y(2)
 end
 
-subroutine s6(x)
+function f6(x) result(r)
   !ERROR: Dummy argument 'x' may not appear in a COMMON block
   !ERROR: ALLOCATABLE object 'y' may not appear in a COMMON block
   common x,y,z
   allocatable y
+  !ERROR: Function result 'r' may not appear in a COMMON block
+  common r
 end
 
 module m7
diff --git a/flang/test/semantics/resolve45.f90 b/flang/test/semantics/resolve45.f90
new file mode 100644 (file)
index 0000000..0084948
--- /dev/null
@@ -0,0 +1,76 @@
+! Copyright (c) 2019, NVIDIA CORPORATION.  All rights reserved.
+!
+! Licensed under the Apache License, Version 2.0 (the "License");
+! you may not use this file except in compliance with the License.
+! You may obtain a copy of the License at
+!
+!     http://www.apache.org/licenses/LICENSE-2.0
+!
+! Unless required by applicable law or agreed to in writing, software
+! distributed under the License is distributed on an "AS IS" BASIS,
+! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+! See the License for the specific language governing permissions and
+! limitations under the License.
+
+function f1(x, y)
+  integer x
+  !ERROR: SAVE attribute may not be applied to dummy argument 'x'
+  !ERROR: SAVE attribute may not be applied to dummy argument 'y'
+  save x,y
+  integer y
+  !ERROR: SAVE attribute may not be applied to function result 'f1'
+  save f1
+end
+
+function f2(x, y)
+  !ERROR: SAVE attribute may not be applied to function result 'f2'
+  real, save :: f2
+  !ERROR: SAVE attribute may not be applied to dummy argument 'x'
+  complex, save :: x
+  allocatable :: y
+  !ERROR: SAVE attribute may not be applied to dummy argument 'y'
+  integer, save :: y
+end
+
+subroutine s3(x)
+  !ERROR: SAVE attribute may not be applied to dummy argument 'x'
+  procedure(integer), pointer, save :: x
+  !ERROR: Procedure 'y' with SAVE attribute must also have POINTER attribute
+  procedure(integer), save :: y
+end
+
+subroutine s4
+  !ERROR: Explicit SAVE of 'z' is redundant due to global SAVE statement
+  save z
+  save
+  procedure(integer), pointer :: x
+  !ERROR: Explicit SAVE of 'x' is redundant due to global SAVE statement
+  save :: x
+  !ERROR: Explicit SAVE of 'y' is redundant due to global SAVE statement
+  integer, save :: y
+end
+
+subroutine s5
+  implicit none
+  integer x
+  block
+    !ERROR: No explicit type declared for 'x'
+    save x
+  end block
+end
+
+subroutine s6
+  save x
+  save y
+  !ERROR: SAVE attribute was already specified on 'y'
+  integer, save :: y
+  integer, save :: z
+  !ERROR: SAVE attribute was already specified on 'x'
+  !ERROR: SAVE attribute was already specified on 'z'
+  save x,z
+end
+
+subroutine s7
+  !ERROR: 'x' appears as a COMMON block in a SAVE statement but not in a COMMON statement
+  save /x/
+end