[flang] Resolve derived type parameters
authorTim Keith <tkeith@nvidia.com>
Tue, 4 Sep 2018 17:28:27 +0000 (10:28 -0700)
committerTim Keith <tkeith@nvidia.com>
Tue, 4 Sep 2018 17:28:27 +0000 (10:28 -0700)
Add TypeParamDetails for symbols that represent type parameters.
Create such symbols when a type-param-def-stmt is encountered.

At the end of the derived-type-def, check the parameters named
in the type-param-name-list against those encountered in each
type-param-def-stmt.

Original-commit: flang-compiler/f18@c15247bb30311e7166429a34db510cb8c736bd66
Reviewed-on: https://github.com/flang-compiler/f18/pull/173
Tree-same-pre-rewrite: false

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

index 027b7f2..ba7e206 100644 (file)
@@ -43,6 +43,7 @@ static std::string ModFilePath(
 static void PutEntity(std::ostream &, const Symbol &);
 static void PutObjectEntity(std::ostream &, const Symbol &);
 static void PutProcEntity(std::ostream &, const Symbol &);
+static void PutTypeParam(std::ostream &, const Symbol &);
 static void PutEntity(std::ostream &, const Symbol &, std::function<void()>);
 static std::ostream &PutAttrs(
     std::ostream &, Attrs, std::string before = ","s, std::string after = ""s);
@@ -206,8 +207,21 @@ void ModFileWriter::PutSymbol(const Symbol &symbol, bool &didContains) {
 
 void ModFileWriter::PutDerivedType(const Symbol &typeSymbol) {
   PutAttrs(decls_ << "type", typeSymbol.attrs(), ","s, ""s);
-  PutLower(decls_ << "::", typeSymbol) << '\n';
-  PutSymbols(*typeSymbol.scope());
+  PutLower(decls_ << "::", typeSymbol);
+  auto &typeScope{*typeSymbol.scope()};
+  if (typeSymbol.get<DerivedTypeDetails>().hasTypeParams()) {
+    bool first{true};
+    decls_ << '(';
+    for (const auto *symbol : SortSymbols(CollectSymbols(typeScope))) {
+      if (symbol->has<TypeParamDetails>()) {
+        PutLower(first ? decls_ : decls_ << ',', *symbol);
+        first = false;
+      }
+    }
+    decls_ << ')';
+  }
+  decls_ << '\n';
+  PutSymbols(typeScope);
   decls_ << "end type\n";
 }
 
@@ -296,6 +310,7 @@ void PutEntity(std::ostream &os, const Symbol &symbol) {
           [&](const EntityDetails &) { PutObjectEntity(os, symbol); },
           [&](const ObjectEntityDetails &) { PutObjectEntity(os, symbol); },
           [&](const ProcEntityDetails &) { PutProcEntity(os, symbol); },
+          [&](const TypeParamDetails &) { PutTypeParam(os, symbol); },
           [&](const auto &) {
             common::die("PutEntity: unexpected details: %s",
                 DetailsToString(symbol.details()).c_str());
@@ -325,6 +340,16 @@ void PutProcEntity(std::ostream &os, const Symbol &symbol) {
   });
 }
 
+void PutTypeParam(std::ostream &os, const Symbol &symbol) {
+  PutEntity(os, symbol, [&]() {
+    auto *type{symbol.GetType()};
+    CHECK(type);
+    PutLower(os, *type);
+    PutLower(os << ',',
+        common::EnumToString(symbol.get<TypeParamDetails>().kindOrLen()));
+  });
+}
+
 // Write an entity (object or procedure) declaration.
 // writeType is called to write out the type.
 void PutEntity(
index 0a36780..e7dac7b 100644 (file)
@@ -529,7 +529,8 @@ public:
   void Post(const parser::DerivedTypeDef &x);
   bool Pre(const parser::DerivedTypeStmt &x);
   void Post(const parser::DerivedTypeStmt &x);
-  bool Pre(const parser::EndTypeStmt &);
+  bool Pre(const parser::TypeParamDefStmt &x) { return BeginDecl(); }
+  void Post(const parser::TypeParamDefStmt &);
   bool Pre(const parser::TypeAttrSpec::Extends &x);
   bool Pre(const parser::PrivateStmt &x);
   bool Pre(const parser::SequenceStmt &x);
@@ -574,7 +575,7 @@ private:
   const Symbol *ResolveDerivedType(const SourceName &);
   bool CanBeTypeBoundProc(const Symbol &);
   Symbol *FindExplicitInterface(const SourceName &);
-  void MakeTypeSymbol(const SourceName &, const Details &);
+  Symbol &MakeTypeSymbol(const SourceName &, const Details &);
 
   // Declare an object or procedure entity.
   // T is one of: EntityDetails, ObjectEntityDetails, ProcEntityDetails
@@ -638,8 +639,6 @@ public:
 
   bool Pre(const parser::CommonBlockObject &);
   void Post(const parser::CommonBlockObject &);
-  bool Pre(const parser::TypeParamDefStmt &);
-  void Post(const parser::TypeParamDefStmt &);
   bool Pre(const parser::PrefixSpec &);
   void Post(const parser::SpecificationPart &);
   bool Pre(const parser::MainProgram &);
@@ -2043,6 +2042,40 @@ bool DeclarationVisitor::Pre(const parser::DerivedTypeDef &x) {
 }
 void DeclarationVisitor::Post(const parser::DerivedTypeDef &x) {
   derivedTypeData_.reset();
+  std::set<SourceName> paramNames;
+  auto &scope{currScope()};
+  auto &stmt{std::get<parser::Statement<parser::DerivedTypeStmt>>(x.t)};
+  for (auto &name : std::get<std::list<parser::Name>>(stmt.statement.t)) {
+    auto &paramName{name.source};
+    if (auto it{scope.find(paramName)}; it == scope.end()) {
+      Say(paramName,
+          "No definition found for type parameter '%s'"_err_en_US);  // C742
+    } else {
+      auto *symbol{it->second};
+      if (!symbol->has<TypeParamDetails>()) {
+        Say2(paramName, "'%s' is not defined as a type parameter"_err_en_US,
+            symbol->name(),
+            "Definition of '%s'"_en_US);  // C741
+      } else {
+        symbol->add_occurrence(paramName);
+      }
+    }
+    if (!paramNames.insert(paramName).second) {
+      Say(paramName,
+          "Duplicate type parameter name: '%s'"_err_en_US);  // C731
+    }
+  }
+  scope.symbol()->get<DerivedTypeDetails>().set_hasTypeParams(
+      !paramNames.empty());
+  for (const auto &pair : currScope()) {
+    const auto *symbol{pair.second};
+    if (symbol->has<TypeParamDetails>() && !paramNames.count(symbol->name())) {
+      Say2(symbol->name(),
+          "'%s' is not a type parameter of this derived type"_err_en_US,
+          stmt.source, "Derived type statement"_en_US);  // C742
+    }
+  }
+  PopScope();
 }
 bool DeclarationVisitor::Pre(const parser::DerivedTypeStmt &x) {
   return BeginAttrs();
@@ -2053,9 +2086,18 @@ void DeclarationVisitor::Post(const parser::DerivedTypeStmt &x) {
   PushScope(Scope::Kind::DerivedType, &symbol);
   EndAttrs();
 }
-bool DeclarationVisitor::Pre(const parser::EndTypeStmt &) {
-  PopScope();
-  return false;
+void DeclarationVisitor::Post(const parser::TypeParamDefStmt &x) {
+  auto &type{GetDeclTypeSpec()};
+  auto kindOrLen{std::get<common::TypeParamKindOrLen>(x.t)};
+  for (auto &decl : std::get<std::list<parser::TypeParamDecl>>(x.t)) {
+    auto &name{std::get<parser::Name>(decl.t).source};
+    // TODO: initialization
+    // auto &init{
+    //    std::get<std::optional<parser::ScalarIntConstantExpr>>(decl.t)};
+    auto &symbol{MakeTypeSymbol(name, TypeParamDetails{kindOrLen})};
+    SetType(name, symbol, *type);
+  }
+  EndDecl();
 }
 bool DeclarationVisitor::Pre(const parser::TypeAttrSpec::Extends &x) {
   auto &name{x.v.source};
@@ -2225,7 +2267,7 @@ Symbol *DeclarationVisitor::FindExplicitInterface(const SourceName &name) {
 
 // Create a symbol for a type parameter, component, or procedure binding in
 // the current derived type scope.
-void DeclarationVisitor::MakeTypeSymbol(
+Symbol &DeclarationVisitor::MakeTypeSymbol(
     const SourceName &name, const Details &details) {
   Scope &derivedType{currScope()};
   CHECK(derivedType.kind() == Scope::Kind::DerivedType);
@@ -2234,22 +2276,14 @@ void DeclarationVisitor::MakeTypeSymbol(
         "Type parameter, component, or procedure binding '%s'"
         " already defined in this type"_err_en_US,
         it->second->name(), "Previous definition of '%s'"_en_US);
+    return *it->second;
   } else {
-    MakeSymbol(name, GetAttrs(), details);
+    return MakeSymbol(name, GetAttrs(), details);
   }
 }
 
 // ResolveNamesVisitor implementation
 
-bool ResolveNamesVisitor::Pre(const parser::TypeParamDefStmt &x) {
-  BeginDeclTypeSpec();
-  return true;
-}
-void ResolveNamesVisitor::Post(const parser::TypeParamDefStmt &x) {
-  EndDeclTypeSpec();
-  // TODO: TypeParamDefStmt
-}
-
 bool ResolveNamesVisitor::Pre(const parser::CommonBlockObject &x) {
   BeginArraySpec();
   return true;
index 97e5e46..51ec0d6 100644 (file)
@@ -130,6 +130,7 @@ std::string DetailsToString(const Details &details) {
           [](const ProcBindingDetails &) { return "ProcBinding"; },
           [](const GenericBindingDetails &) { return "GenericBinding"; },
           [](const FinalProcDetails &) { return "FinalProc"; },
+          [](const TypeParamDetails &) { return "TypeParam"; },
           [](const auto &) { return "unknown"; },
       },
       details);
@@ -197,6 +198,9 @@ const DeclTypeSpec *Symbol::GetType() const {
             return x.type().has_value() ? &x.type().value() : nullptr;
           },
           [](const ProcEntityDetails &x) { return x.interface().type(); },
+          [](const TypeParamDetails &x) {
+            return x.type().has_value() ? &x.type().value() : nullptr;
+          },
           [](const auto &) {
             return static_cast<const DeclTypeSpec *>(nullptr);
           },
@@ -210,6 +214,7 @@ void Symbol::SetType(const DeclTypeSpec &type) {
           [&](EntityDetails &x) { x.set_type(type); },
           [&](ObjectEntityDetails &x) { x.set_type(type); },
           [&](ProcEntityDetails &x) { x.interface().set_type(type); },
+          [&](TypeParamDetails &x) { x.set_type(type); },
           [](auto &) {},
       },
       details_);
@@ -350,6 +355,12 @@ std::ostream &operator<<(std::ostream &os, const Details &details) {
           },
           [&](const GenericBindingDetails &) { /* TODO */ },
           [&](const FinalProcDetails &) {},
+          [&](const TypeParamDetails &x) {
+            if (x.type()) {
+              os << ' ' << *x.type();
+            }
+            os << ' ' << common::EnumToString(x.kindOrLen());
+          },
       },
       details);
   return os;
index bfb362e..d59d839 100644 (file)
@@ -17,6 +17,7 @@
 
 #include "type.h"
 #include "../common/enum-set.h"
+#include "../common/fortran.h"
 #include <functional>
 #include <memory>
 
@@ -143,8 +144,14 @@ private:
   friend std::ostream &operator<<(std::ostream &, const ProcEntityDetails &);
 };
 
-// A derived type
-class DerivedTypeDetails {};
+class DerivedTypeDetails {
+public:
+  bool hasTypeParams() const { return hasTypeParams_; }
+  void set_hasTypeParams(bool x = true) { hasTypeParams_ = x; }
+
+private:
+  bool hasTypeParams_{false};
+};
 
 class ProcBindingDetails {
 public:
@@ -159,6 +166,22 @@ class GenericBindingDetails {};
 
 class FinalProcDetails {};
 
+class TypeParamDetails {
+public:
+  TypeParamDetails(common::TypeParamKindOrLen kindOrLen)
+    : kindOrLen_{kindOrLen} {}
+  common::TypeParamKindOrLen kindOrLen() const { return kindOrLen_; }
+  const std::optional<DeclTypeSpec> &type() const { return type_; }
+  void set_type(const DeclTypeSpec &type) {
+    CHECK(!type_);
+    type_ = type;
+  }
+
+private:
+  common::TypeParamKindOrLen kindOrLen_;
+  std::optional<DeclTypeSpec> type_;
+};
+
 // Record the USE of a symbol: location is where (USE statement or renaming);
 // symbol is the USEd module.
 class UseDetails {
@@ -235,7 +258,7 @@ using Details = std::variant<UnknownDetails, MainProgramDetails, ModuleDetails,
     SubprogramDetails, SubprogramNameDetails, EntityDetails,
     ObjectEntityDetails, ProcEntityDetails, DerivedTypeDetails, UseDetails,
     UseErrorDetails, GenericDetails, ProcBindingDetails, GenericBindingDetails,
-    FinalProcDetails>;
+    FinalProcDetails, TypeParamDetails>;
 std::ostream &operator<<(std::ostream &, const Details &);
 std::string DetailsToString(const Details &);
 
index f59f7b9..419e856 100644 (file)
@@ -57,6 +57,7 @@ set(ERROR_TESTS
   resolve30.f90
   resolve31.f90
   resolve32.f90
+  resolve33.f90
 )
 
 # These test files have expected symbols in the source
@@ -80,6 +81,7 @@ set(MODFILE_TESTS
   modfile08.f90
   modfile09-*.f90
   modfile10.f90
+  modfile11.f90
 )
 
 foreach(test ${ERROR_TESTS})
diff --git a/flang/test/semantics/modfile11.f90 b/flang/test/semantics/modfile11.f90
new file mode 100644 (file)
index 0000000..8d84742
--- /dev/null
@@ -0,0 +1,31 @@
+! Copyright (c) 2018, 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.
+
+module m
+  type t(a, b, c)
+    integer, kind :: a
+    integer(8), len :: b, c
+    integer :: d
+  end type
+end
+
+!Expect: m.mod
+!module m
+!  type::t(a,b,c)
+!    integer,kind::a
+!    integer(8),len::b
+!    integer(8),len::c
+!    integer::d
+!  end type
+!end
diff --git a/flang/test/semantics/resolve33.f90 b/flang/test/semantics/resolve33.f90
new file mode 100644 (file)
index 0000000..929d9f9
--- /dev/null
@@ -0,0 +1,38 @@
+! Copyright (c) 2018, 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.
+
+! Derived type parameters
+
+module m
+  !ERROR: Duplicate type parameter name: 'a'
+  type t1(a, b, a)
+    integer, kind :: a
+    integer(8), len :: b
+  end type
+  !ERROR: No definition found for type parameter 'b'
+  type t2(a, b, c)
+    integer, kind :: a
+    integer, len :: c
+  end type
+  !ERROR: 'b' is not defined as a type parameter
+  type t3(a, b)
+    integer, kind :: a
+    integer :: b
+  end type
+  type t4(a)
+    integer, kind :: a
+    !ERROR: 'd' is not a type parameter of this derived type
+    integer(8), len :: d
+  end type
+end module