[flang] Resolve misparse of structure constructor as function reference.
authorpeter klausler <pklausler@nvidia.com>
Fri, 8 Mar 2019 20:55:57 +0000 (12:55 -0800)
committerpeter klausler <pklausler@nvidia.com>
Sat, 9 Mar 2019 18:25:06 +0000 (10:25 -0800)
Original-commit: flang-compiler/f18@5b6b2540f6c66b3f2900b24b06d732de3436b4ea
Reviewed-on: https://github.com/flang-compiler/f18/pull/322
Tree-same-pre-rewrite: false

flang/lib/parser/parse-tree.cc
flang/lib/parser/parse-tree.h
flang/lib/semantics/expression.cc
flang/lib/semantics/expression.h
flang/lib/semantics/rewrite-parse-tree.cc

index f4894fa..17b6f57 100644 (file)
@@ -89,34 +89,50 @@ static Designator MakeArrayElementRef(Name &name, std::list<Expr> &subscripts) {
   return Designator{DataRef{common::Indirection{std::move(arrayElement)}}};
 }
 
+static std::optional<Expr> ActualArgToExpr(ActualArgSpec &arg) {
+  return std::visit(
+      common::visitors{
+          [&](common::Indirection<Expr> &y) {
+            return std::make_optional<Expr>(std::move(y.value()));
+          },
+          [&](common::Indirection<Variable> &y) {
+            return std::visit(
+                [&](auto &indirection) {
+                  return std::make_optional<Expr>(
+                      std::move(indirection.value()));
+                },
+                y.value().u);
+          },
+          [&](auto &) -> std::optional<Expr> { return std::nullopt; },
+      },
+      std::get<ActualArg>(arg.t).u);
+}
+
 Designator FunctionReference::ConvertToArrayElementRef() {
   auto &name{std::get<parser::Name>(std::get<ProcedureDesignator>(v.t).u)};
   std::list<Expr> args;
   for (auto &arg : std::get<std::list<ActualArgSpec>>(v.t)) {
-    std::visit(
-        common::visitors{
-            [&](common::Indirection<Expr> &y) {
-              args.push_back(std::move(y.value()));
-            },
-            [&](common::Indirection<Variable> &y) {
-              args.push_back(std::visit(
-                  common::visitors{
-                      [&](common::Indirection<Designator> &z) {
-                        return Expr{std::move(z.value())};
-                      },
-                      [&](common::Indirection<FunctionReference> &z) {
-                        return Expr{std::move(z.value())};
-                      },
-                  },
-                  y.value().u));
-            },
-            [&](auto &) { CHECK(!"unexpected kind of ActualArg"); },
-        },
-        std::get<ActualArg>(arg.t).u);
+    args.emplace_back(std::move(ActualArgToExpr(arg).value()));
   }
   return MakeArrayElementRef(name, args);
 }
 
+StructureConstructor FunctionReference::ConvertToStructureConstructor() {
+  Name name{std::get<parser::Name>(std::get<ProcedureDesignator>(v.t).u)};
+  std::list<ComponentSpec> components;
+  for (auto &arg : std::get<std::list<ActualArgSpec>>(v.t)) {
+    std::optional<Keyword> keyword;
+    if (auto &kw{std::get<std::optional<Keyword>>(arg.t)}) {
+      keyword.emplace(Keyword{Name{kw->v}});
+    }
+    components.emplace_back(
+        std::move(keyword), ComponentDataSource{ActualArgToExpr(arg).value()});
+  }
+  return StructureConstructor{
+      DerivedTypeSpec{std::move(name), std::list<TypeParamSpec>{}},
+      std::move(components)};
+}
+
 // R1544 stmt-function-stmt
 // Convert this stmt-function-stmt to an array element assignment statement.
 Statement<ActionStmt> StmtFunctionStmt::ConvertToAssignment() {
index 2c25c82..5bdff06 100644 (file)
@@ -538,8 +538,6 @@ WRAPPER_CLASS(Program, std::list<ProgramUnit>);
 
 // R603 name -> letter [alphanumeric-character]...
 struct Name {
-  Name() {}
-  COPY_AND_ASSIGN_BOILERPLATE(Name);
   std::string ToString() const { return source.ToString(); }
   CharBlock source;
   mutable semantics::Symbol *symbol{nullptr};  // filled in during semantics
@@ -3094,6 +3092,7 @@ struct Call {
 struct FunctionReference {
   WRAPPER_CLASS_BOILERPLATE(FunctionReference, Call);
   Designator ConvertToArrayElementRef();
+  StructureConstructor ConvertToStructureConstructor();
 };
 
 // R1521 call-stmt -> CALL procedure-designator [( [actual-arg-spec-list] )]
index 87c6363..7be75a5 100644 (file)
@@ -196,7 +196,8 @@ MaybeExpr TypedWrapper(const DynamicType &dyType, WRAPPED &&x) {
 
 // Wraps a data reference in a typed Designator<>.
 static MaybeExpr Designate(DataRef &&ref) {
-  if (std::optional<DynamicType> dyType{GetSymbolType(ref.GetLastSymbol())}) {
+  if (std::optional<DynamicType> dyType{
+          GetSymbolType(ref.GetLastSymbol().GetUltimate())}) {
     return TypedWrapper<Designator, DataRef>(
         std::move(*dyType), std::move(ref));
   }
@@ -205,7 +206,8 @@ static MaybeExpr Designate(DataRef &&ref) {
 }
 
 // Catch and resolve the ambiguous parse of a substring reference
-// that looks like a 1-D array element or section.
+// that looks like a 1-D array element or section.  The parse tree is
+// not adjusted.
 static MaybeExpr ResolveAmbiguousSubstring(ArrayRef &&ref) {
   if (std::optional<DynamicType> dyType{GetSymbolType(ref.GetLastSymbol())}) {
     if (dyType->category == TypeCategory::Character && ref.size() == 1) {
@@ -240,7 +242,7 @@ static MaybeExpr ResolveAmbiguousSubstring(ArrayRef &&ref) {
 // ambiguous parse of a substring reference that looks like a 1-D array
 // element or section.
 MaybeExpr ExpressionAnalyzer::CompleteSubscripts(ArrayRef &&ref) {
-  const Symbol &symbol{ref.GetLastSymbol()};
+  const Symbol &symbol{ref.GetLastSymbol().GetUltimate()};
   int symbolRank{symbol.Rank()};
   int subscripts = ref.size();
   if (subscripts == 0) {
@@ -627,23 +629,24 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::Name &n) {
   if (std::optional<int> kind{IsAcImpliedDo(n.source)}) {
     return AsMaybeExpr(ConvertToKind<TypeCategory::Integer>(
         *kind, AsExpr(ImpliedDoIndex{n.source})));
-  } else if (n.symbol == nullptr) {
-    // error should have been reported in name resolution
-  } else if (n.symbol->attrs().test(semantics::Attr::PARAMETER)) {
-    if (auto *details{n.symbol->detailsIf<semantics::ObjectEntityDetails>()}) {
-      if (auto &init{details->init()}) {
-        return init;
+  } else if (n.symbol != nullptr) {
+    const Symbol &ultimate{n.symbol->GetUltimate()};
+    if (ultimate.attrs().test(semantics::Attr::PARAMETER)) {
+      if (auto *details{ultimate.detailsIf<semantics::ObjectEntityDetails>()}) {
+        if (auto &init{details->init()}) {
+          return init;
+        }
       }
+      // TODO: enumerators, do they have the PARAMETER attribute?
+    } else if (ultimate.detailsIf<semantics::TypeParamDetails>()) {
+      // A bare reference to a derived type parameter (within a parameterized
+      // derived type definition)
+      return AsMaybeExpr(MakeTypeParamInquiry(&ultimate));
+    } else if (MaybeExpr result{Designate(DataRef{*n.symbol})}) {
+      return result;
+    } else {
+      Say(n.source, "not of a supported type and kind"_err_en_US);
     }
-    // TODO: enumerators, do they have the PARAMETER attribute?
-  } else if (n.symbol->detailsIf<semantics::TypeParamDetails>()) {
-    // A bare reference to a derived type parameter (within a parameterized
-    // derived type definition)
-    return AsMaybeExpr(MakeTypeParamInquiry(n.symbol));
-  } else if (MaybeExpr result{Designate(DataRef{*n.symbol})}) {
-    return result;
-  } else {
-    Say(n.source, "not of a supported type and kind"_err_en_US);
   }
   return std::nullopt;
 }
@@ -1382,38 +1385,31 @@ auto ExpressionAnalyzer::Procedure(const parser::ProcedureDesignator &pd,
                   n.ToString().data());
               return std::nullopt;
             }
-            return std::visit(
-                common::visitors{
-                    [&](const semantics::ProcEntityDetails &p)
-                        -> std::optional<CallAndArguments> {
-                      if (p.HasExplicitInterface()) {
-                        // TODO: check actual arguments vs. interface
-                      } else {
-                        CallCharacteristics cc{n.source};
-                        if (std::optional<SpecificCall> specificCall{
-                                context().intrinsics().Probe(
-                                    cc, arguments, &GetContextualMessages())}) {
-                          return {CallAndArguments{
-                              ProcedureDesignator{
-                                  std::move(specificCall->specificIntrinsic)},
-                              std::move(specificCall->arguments)}};
-                        } else {
-                          // TODO: if name is not INTRINSIC, call with implicit
-                          // interface
-                        }
-                      }
-                      return {CallAndArguments{ProcedureDesignator{*n.symbol},
-                          std::move(arguments)}};
-                    },
-                    [&](const auto &) -> std::optional<CallAndArguments> {
-                      // TODO pmk WIP: resolve ambiguous array reference or
-                      // structure constructor usage that reach here
-                      Say("TODO: unimplemented/invalid kind of symbol as procedure designator '%s'"_err_en_US,
-                          n.ToString().data());
-                      return std::nullopt;
-                    },
-                },
-                n.symbol->details());
+            const Symbol &ultimate{n.symbol->GetUltimate()};
+            if (const auto *proc{
+                    ultimate.detailsIf<semantics::ProcEntityDetails>()}) {
+              if (proc->HasExplicitInterface()) {
+                // TODO: check actual arguments vs. interface
+              } else {
+                CallCharacteristics cc{n.source};
+                if (std::optional<SpecificCall> specificCall{
+                        context().intrinsics().Probe(
+                            cc, arguments, &GetContextualMessages())}) {
+                  return {
+                      CallAndArguments{ProcedureDesignator{std::move(
+                                           specificCall->specificIntrinsic)},
+                          std::move(specificCall->arguments)}};
+                } else {
+                  // TODO: if name is not INTRINSIC, call with implicit
+                  // interface
+                }
+              }
+              return {CallAndArguments{
+                  ProcedureDesignator{*n.symbol}, std::move(arguments)}};
+            } else {
+              Say(n.source, "not a procedure"_err_en_US);
+              return std::nullopt;
+            }
           },
           [&](const parser::ProcComponentRef &pcr)
               -> std::optional<CallAndArguments> {
@@ -1756,21 +1752,60 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::DefinedBinary &) {
   return std::nullopt;
 }
 
+// Converts, if appropriate, a misparse of the ambiguous syntax A(1) as
+// a function reference into an array reference or a structure constructor.
+template<typename... A>
+void FixMisparsedFunctionReference(const std::variant<A...> &constU) {
+  // The parse tree is updated in situ when resolving an ambiguous parse.
+  using uType = std::decay_t<decltype(constU)>;
+  auto &u{const_cast<uType &>(constU)};
+  if (auto *func{
+          std::get_if<common::Indirection<parser::FunctionReference>>(&u)}) {
+    parser::FunctionReference &funcRef{func->value()};
+    auto &proc{std::get<parser::ProcedureDesignator>(funcRef.v.t)};
+    if (auto *name{std::get_if<parser::Name>(&proc.u)}) {
+      if (name->symbol == nullptr) {
+        return;
+      }
+      Symbol &symbol{name->symbol->GetUltimate()};
+      if constexpr (common::HasMember<common::Indirection<parser::Designator>,
+                        uType>) {
+        if (symbol.has<semantics::ObjectEntityDetails>()) {
+          u = common::Indirection{funcRef.ConvertToArrayElementRef()};
+          return;
+          // N.B. Expression semantics will reinterpret an array element
+          // reference as a single-character substring elsewhere if necessary.
+        }
+      }
+      if constexpr (common::HasMember<StructureConstructor, uType>) {
+        if (symbol.has<semantics::DerivedTypeDetails>()) {
+          u = funcRef.ConvertToStructureConstructor();
+          return;
+        }
+      }
+    }
+  }
+}
+
 MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr &expr) {
   if (expr.typedExpr.has_value()) {
     // Expression was already checked by ExprChecker
     return std::make_optional<Expr<SomeType>>(expr.typedExpr.value().v);
-  } else if (!expr.source.empty()) {
-    // Analyze the expression in a specified source position context for better
-    // error reporting.
-    auto save{GetFoldingContext().messages().SetLocation(expr.source)};
-    return Analyze(expr.u);
   } else {
-    return Analyze(expr.u);
+    FixMisparsedFunctionReference(expr.u);
+    if (!expr.source.empty()) {
+      // Analyze the expression in a specified source position context for
+      // better error reporting.
+      auto save{GetFoldingContext().messages().SetLocation(expr.source)};
+      return Analyze(expr.u);
+    } else {
+      return Analyze(expr.u);
+    }
   }
 }
 
 MaybeExpr ExpressionAnalyzer::Analyze(const parser::Variable &variable) {
+  FixMisparsedFunctionReference(variable.u);
   return Analyze(variable.u);
 }
 
@@ -1894,4 +1929,19 @@ void ExprChecker::Enter(const parser::Expr &expr) {
     }
   }
 }
+
+void ExprChecker::Enter(const parser::Variable &var) {
+#if PMKDEBUG
+  if (MaybeExpr checked{AnalyzeExpr(context_, var)}) {
+//    checked->AsFortran(std::cout << "checked variable: ") << '\n';
+#else
+  if (AnalyzeExpr(context_, var)) {
+#endif
+  } else {
+#if PMKDEBUG
+    std::cout << "TODO: expression analysis failed for this variable: ";
+    DumpTree(std::cout, var);
+#endif
+  }
+}
 }
index 2728dc7..96cdd74 100644 (file)
@@ -297,6 +297,7 @@ class ExprChecker : public virtual BaseChecker {
 public:
   explicit ExprChecker(SemanticsContext &context) : context_{context} {}
   void Enter(const parser::Expr &);
+  void Enter(const parser::Variable &);
 
 private:
   SemanticsContext &context_;
index 02748ca..1788f11 100644 (file)
@@ -37,8 +37,6 @@ public:
   void Post(parser::Name &);
   void Post(parser::SpecificationPart &);
   bool Pre(parser::ExecutionPart &);
-  void Post(parser::Variable &x) { ConvertFunctionRef(x); }
-  void Post(parser::Expr &x) { ConvertFunctionRef(x); }
 
   // Name resolution yet implemented:
   bool Pre(parser::EquivalenceStmt &) { return false; }
@@ -62,24 +60,6 @@ private:
   bool errorOnUnresolvedName_{true};
   parser::Messages &messages_;
   std::list<stmtFuncType> stmtFuncsToConvert_;
-
-  // For T = Variable or Expr, if x has a function reference that really
-  // should be an array element reference (i.e. the name occurs in an
-  // entity declaration, convert it.
-  template<typename T> void ConvertFunctionRef(T &x) {
-    auto *funcRef{
-        std::get_if<common::Indirection<parser::FunctionReference>>(&x.u)};
-    if (!funcRef) {
-      return;
-    }
-    parser::Name *name{std::get_if<parser::Name>(
-        &std::get<parser::ProcedureDesignator>(funcRef->value().v.t).u)};
-    if (!name || !name->symbol ||
-        !name->symbol->GetUltimate().has<ObjectEntityDetails>()) {
-      return;
-    }
-    x.u = common::Indirection{funcRef->value().ConvertToArrayElementRef()};
-  }
 };
 
 // Check that name has been resolved to a symbol