170ad73b6Ssameeran joshi //===-- lib/Semantics/check-select-type.cpp -------------------------------===//
270ad73b6Ssameeran joshi //
370ad73b6Ssameeran joshi // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
470ad73b6Ssameeran joshi // See https://llvm.org/LICENSE.txt for license information.
570ad73b6Ssameeran joshi // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
670ad73b6Ssameeran joshi //
770ad73b6Ssameeran joshi //===----------------------------------------------------------------------===//
870ad73b6Ssameeran joshi 
970ad73b6Ssameeran joshi #include "check-select-type.h"
1070ad73b6Ssameeran joshi #include "flang/Common/idioms.h"
1170ad73b6Ssameeran joshi #include "flang/Common/reference.h"
1270ad73b6Ssameeran joshi #include "flang/Evaluate/fold.h"
1370ad73b6Ssameeran joshi #include "flang/Evaluate/type.h"
1470ad73b6Ssameeran joshi #include "flang/Parser/parse-tree.h"
1570ad73b6Ssameeran joshi #include "flang/Semantics/semantics.h"
1670ad73b6Ssameeran joshi #include "flang/Semantics/tools.h"
1770ad73b6Ssameeran joshi #include <optional>
1870ad73b6Ssameeran joshi 
1970ad73b6Ssameeran joshi namespace Fortran::semantics {
2070ad73b6Ssameeran joshi 
2170ad73b6Ssameeran joshi class TypeCaseValues {
2270ad73b6Ssameeran joshi public:
TypeCaseValues(SemanticsContext & c,const evaluate::DynamicType & t)2370ad73b6Ssameeran joshi   TypeCaseValues(SemanticsContext &c, const evaluate::DynamicType &t)
2470ad73b6Ssameeran joshi       : context_{c}, selectorType_{t} {}
Check(const std::list<parser::SelectTypeConstruct::TypeCase> & cases)2570ad73b6Ssameeran joshi   void Check(const std::list<parser::SelectTypeConstruct::TypeCase> &cases) {
2670ad73b6Ssameeran joshi     for (const auto &c : cases) {
2770ad73b6Ssameeran joshi       AddTypeCase(c);
2870ad73b6Ssameeran joshi     }
2970ad73b6Ssameeran joshi     if (!hasErrors_) {
3070ad73b6Ssameeran joshi       ReportConflictingTypeCases();
3170ad73b6Ssameeran joshi     }
3270ad73b6Ssameeran joshi   }
3370ad73b6Ssameeran joshi 
3470ad73b6Ssameeran joshi private:
AddTypeCase(const parser::SelectTypeConstruct::TypeCase & c)3570ad73b6Ssameeran joshi   void AddTypeCase(const parser::SelectTypeConstruct::TypeCase &c) {
3670ad73b6Ssameeran joshi     const auto &stmt{std::get<parser::Statement<parser::TypeGuardStmt>>(c.t)};
3770ad73b6Ssameeran joshi     const parser::TypeGuardStmt &typeGuardStmt{stmt.statement};
3870ad73b6Ssameeran joshi     const auto &guard{std::get<parser::TypeGuardStmt::Guard>(typeGuardStmt.t)};
3970ad73b6Ssameeran joshi     if (std::holds_alternative<parser::Default>(guard.u)) {
4070ad73b6Ssameeran joshi       typeCases_.emplace_back(stmt, std::nullopt);
4170ad73b6Ssameeran joshi     } else if (std::optional<evaluate::DynamicType> type{GetGuardType(guard)}) {
42594dec28Scompinder       if (PassesChecksOnGuard(stmt, *type)) {
4370ad73b6Ssameeran joshi         typeCases_.emplace_back(stmt, *type);
4470ad73b6Ssameeran joshi       } else {
4570ad73b6Ssameeran joshi         hasErrors_ = true;
4670ad73b6Ssameeran joshi       }
4770ad73b6Ssameeran joshi     } else {
4870ad73b6Ssameeran joshi       hasErrors_ = true;
4970ad73b6Ssameeran joshi     }
5070ad73b6Ssameeran joshi   }
5170ad73b6Ssameeran joshi 
GetGuardType(const parser::TypeGuardStmt::Guard & guard)5270ad73b6Ssameeran joshi   std::optional<evaluate::DynamicType> GetGuardType(
5370ad73b6Ssameeran joshi       const parser::TypeGuardStmt::Guard &guard) {
54*cd03e96fSPeter Klausler     return common::visit(
5570ad73b6Ssameeran joshi         common::visitors{
5670ad73b6Ssameeran joshi             [](const parser::Default &)
5770ad73b6Ssameeran joshi                 -> std::optional<evaluate::DynamicType> {
5870ad73b6Ssameeran joshi               return std::nullopt;
5970ad73b6Ssameeran joshi             },
6070ad73b6Ssameeran joshi             [](const parser::TypeSpec &typeSpec) {
6170ad73b6Ssameeran joshi               return evaluate::DynamicType::From(typeSpec.declTypeSpec);
6270ad73b6Ssameeran joshi             },
6370ad73b6Ssameeran joshi             [](const parser::DerivedTypeSpec &spec)
6470ad73b6Ssameeran joshi                 -> std::optional<evaluate::DynamicType> {
6570ad73b6Ssameeran joshi               if (const auto *derivedTypeSpec{spec.derivedTypeSpec}) {
6670ad73b6Ssameeran joshi                 return evaluate::DynamicType(*derivedTypeSpec);
6770ad73b6Ssameeran joshi               }
6870ad73b6Ssameeran joshi               return std::nullopt;
6970ad73b6Ssameeran joshi             },
7070ad73b6Ssameeran joshi         },
7170ad73b6Ssameeran joshi         guard.u);
7270ad73b6Ssameeran joshi   }
7370ad73b6Ssameeran joshi 
PassesChecksOnGuard(const parser::Statement<parser::TypeGuardStmt> & stmt,const evaluate::DynamicType & guardDynamicType)74594dec28Scompinder   bool PassesChecksOnGuard(const parser::Statement<parser::TypeGuardStmt> &stmt,
7570ad73b6Ssameeran joshi       const evaluate::DynamicType &guardDynamicType) {
76594dec28Scompinder     const parser::TypeGuardStmt &typeGuardStmt{stmt.statement};
77594dec28Scompinder     const auto &guard{std::get<parser::TypeGuardStmt::Guard>(typeGuardStmt.t)};
78*cd03e96fSPeter Klausler     return common::visit(
7970ad73b6Ssameeran joshi         common::visitors{
8070ad73b6Ssameeran joshi             [](const parser::Default &) { return true; },
8170ad73b6Ssameeran joshi             [&](const parser::TypeSpec &typeSpec) {
82594dec28Scompinder               const DeclTypeSpec *spec{typeSpec.declTypeSpec};
83594dec28Scompinder               CHECK(spec);
84594dec28Scompinder               CHECK(spec->AsIntrinsic() || spec->AsDerived());
85594dec28Scompinder               bool typeSpecRetVal{false};
86594dec28Scompinder               if (spec->AsIntrinsic()) {
87594dec28Scompinder                 typeSpecRetVal = true;
88594dec28Scompinder                 if (!selectorType_.IsUnlimitedPolymorphic()) { // C1162
89594dec28Scompinder                   context_.Say(stmt.source,
90594dec28Scompinder                       "If selector is not unlimited polymorphic, "
91594dec28Scompinder                       "an intrinsic type specification must not be specified "
92594dec28Scompinder                       "in the type guard statement"_err_en_US);
93594dec28Scompinder                   typeSpecRetVal = false;
94594dec28Scompinder                 }
9570ad73b6Ssameeran joshi                 if (spec->category() == DeclTypeSpec::Character &&
9670ad73b6Ssameeran joshi                     !guardDynamicType.IsAssumedLengthCharacter()) { // C1160
9770ad73b6Ssameeran joshi                   context_.Say(parser::FindSourceLocation(typeSpec),
9870ad73b6Ssameeran joshi                       "The type specification statement must have "
9970ad73b6Ssameeran joshi                       "LEN type parameter as assumed"_err_en_US);
100594dec28Scompinder                   typeSpecRetVal = false;
10170ad73b6Ssameeran joshi                 }
102594dec28Scompinder               } else {
103594dec28Scompinder                 const DerivedTypeSpec *derived{spec->AsDerived()};
104594dec28Scompinder                 typeSpecRetVal = PassesDerivedTypeChecks(
10570ad73b6Ssameeran joshi                     *derived, parser::FindSourceLocation(typeSpec));
10670ad73b6Ssameeran joshi               }
107594dec28Scompinder               return typeSpecRetVal;
10870ad73b6Ssameeran joshi             },
10970ad73b6Ssameeran joshi             [&](const parser::DerivedTypeSpec &x) {
110594dec28Scompinder               CHECK(x.derivedTypeSpec);
111594dec28Scompinder               const semantics::DerivedTypeSpec *derived{x.derivedTypeSpec};
11270ad73b6Ssameeran joshi               return PassesDerivedTypeChecks(
11370ad73b6Ssameeran joshi                   *derived, parser::FindSourceLocation(x));
11470ad73b6Ssameeran joshi             },
11570ad73b6Ssameeran joshi         },
11670ad73b6Ssameeran joshi         guard.u);
11770ad73b6Ssameeran joshi   }
11870ad73b6Ssameeran joshi 
PassesDerivedTypeChecks(const semantics::DerivedTypeSpec & derived,parser::CharBlock sourceLoc) const11970ad73b6Ssameeran joshi   bool PassesDerivedTypeChecks(const semantics::DerivedTypeSpec &derived,
12070ad73b6Ssameeran joshi       parser::CharBlock sourceLoc) const {
12170ad73b6Ssameeran joshi     for (const auto &pair : derived.parameters()) {
12270ad73b6Ssameeran joshi       if (pair.second.isLen() && !pair.second.isAssumed()) { // C1160
12370ad73b6Ssameeran joshi         context_.Say(sourceLoc,
12470ad73b6Ssameeran joshi             "The type specification statement must have "
12570ad73b6Ssameeran joshi             "LEN type parameter as assumed"_err_en_US);
12670ad73b6Ssameeran joshi         return false;
12770ad73b6Ssameeran joshi       }
12870ad73b6Ssameeran joshi     }
12970ad73b6Ssameeran joshi     if (!IsExtensibleType(&derived)) { // C1161
13070ad73b6Ssameeran joshi       context_.Say(sourceLoc,
13170ad73b6Ssameeran joshi           "The type specification statement must not specify "
13270ad73b6Ssameeran joshi           "a type with a SEQUENCE attribute or a BIND attribute"_err_en_US);
13370ad73b6Ssameeran joshi       return false;
13470ad73b6Ssameeran joshi     }
13570ad73b6Ssameeran joshi     if (!selectorType_.IsUnlimitedPolymorphic()) { // C1162
13670ad73b6Ssameeran joshi       if (const semantics::Scope * guardScope{derived.typeSymbol().scope()}) {
13770ad73b6Ssameeran joshi         if (const auto *selDerivedTypeSpec{
13870ad73b6Ssameeran joshi                 evaluate::GetDerivedTypeSpec(selectorType_)}) {
139142cbd50SPeter Klausler           if (!derived.Match(*selDerivedTypeSpec) &&
14070ad73b6Ssameeran joshi               !guardScope->FindComponent(selDerivedTypeSpec->name())) {
14170ad73b6Ssameeran joshi             context_.Say(sourceLoc,
14270ad73b6Ssameeran joshi                 "Type specification '%s' must be an extension"
14370ad73b6Ssameeran joshi                 " of TYPE '%s'"_err_en_US,
14470ad73b6Ssameeran joshi                 derived.AsFortran(), selDerivedTypeSpec->AsFortran());
14570ad73b6Ssameeran joshi             return false;
14670ad73b6Ssameeran joshi           }
14770ad73b6Ssameeran joshi         }
14870ad73b6Ssameeran joshi       }
14970ad73b6Ssameeran joshi     }
15070ad73b6Ssameeran joshi     return true;
15170ad73b6Ssameeran joshi   }
15270ad73b6Ssameeran joshi 
15370ad73b6Ssameeran joshi   struct TypeCase {
TypeCaseFortran::semantics::TypeCaseValues::TypeCase15470ad73b6Ssameeran joshi     explicit TypeCase(const parser::Statement<parser::TypeGuardStmt> &s,
15570ad73b6Ssameeran joshi         std::optional<evaluate::DynamicType> guardTypeDynamic)
15670ad73b6Ssameeran joshi         : stmt{s} {
15770ad73b6Ssameeran joshi       SetGuardType(guardTypeDynamic);
15870ad73b6Ssameeran joshi     }
15970ad73b6Ssameeran joshi 
SetGuardTypeFortran::semantics::TypeCaseValues::TypeCase16070ad73b6Ssameeran joshi     void SetGuardType(std::optional<evaluate::DynamicType> guardTypeDynamic) {
16170ad73b6Ssameeran joshi       const auto &guard{GetGuardFromStmt(stmt)};
162*cd03e96fSPeter Klausler       common::visit(common::visitors{
16370ad73b6Ssameeran joshi                         [&](const parser::Default &) {},
16470ad73b6Ssameeran joshi                         [&](const auto &) { guardType_ = *guardTypeDynamic; },
16570ad73b6Ssameeran joshi                     },
16670ad73b6Ssameeran joshi           guard.u);
16770ad73b6Ssameeran joshi     }
16870ad73b6Ssameeran joshi 
IsDefaultFortran::semantics::TypeCaseValues::TypeCase16970ad73b6Ssameeran joshi     bool IsDefault() const {
17070ad73b6Ssameeran joshi       const auto &guard{GetGuardFromStmt(stmt)};
17170ad73b6Ssameeran joshi       return std::holds_alternative<parser::Default>(guard.u);
17270ad73b6Ssameeran joshi     }
17370ad73b6Ssameeran joshi 
IsTypeSpecFortran::semantics::TypeCaseValues::TypeCase17470ad73b6Ssameeran joshi     bool IsTypeSpec() const {
17570ad73b6Ssameeran joshi       const auto &guard{GetGuardFromStmt(stmt)};
17670ad73b6Ssameeran joshi       return std::holds_alternative<parser::TypeSpec>(guard.u);
17770ad73b6Ssameeran joshi     }
17870ad73b6Ssameeran joshi 
IsDerivedTypeSpecFortran::semantics::TypeCaseValues::TypeCase17970ad73b6Ssameeran joshi     bool IsDerivedTypeSpec() const {
18070ad73b6Ssameeran joshi       const auto &guard{GetGuardFromStmt(stmt)};
18170ad73b6Ssameeran joshi       return std::holds_alternative<parser::DerivedTypeSpec>(guard.u);
18270ad73b6Ssameeran joshi     }
18370ad73b6Ssameeran joshi 
GetGuardFromStmtFortran::semantics::TypeCaseValues::TypeCase18470ad73b6Ssameeran joshi     const parser::TypeGuardStmt::Guard &GetGuardFromStmt(
18570ad73b6Ssameeran joshi         const parser::Statement<parser::TypeGuardStmt> &stmt) const {
18670ad73b6Ssameeran joshi       const parser::TypeGuardStmt &typeGuardStmt{stmt.statement};
18770ad73b6Ssameeran joshi       return std::get<parser::TypeGuardStmt::Guard>(typeGuardStmt.t);
18870ad73b6Ssameeran joshi     }
18970ad73b6Ssameeran joshi 
guardTypeFortran::semantics::TypeCaseValues::TypeCase19070ad73b6Ssameeran joshi     std::optional<evaluate::DynamicType> guardType() const {
19170ad73b6Ssameeran joshi       return guardType_;
19270ad73b6Ssameeran joshi     }
19370ad73b6Ssameeran joshi 
AsFortranFortran::semantics::TypeCaseValues::TypeCase19470ad73b6Ssameeran joshi     std::string AsFortran() const {
19570ad73b6Ssameeran joshi       std::string result;
19670ad73b6Ssameeran joshi       if (this->guardType()) {
19770ad73b6Ssameeran joshi         auto type{*this->guardType()};
19870ad73b6Ssameeran joshi         result += type.AsFortran();
19970ad73b6Ssameeran joshi       } else {
20070ad73b6Ssameeran joshi         result += "DEFAULT";
20170ad73b6Ssameeran joshi       }
20270ad73b6Ssameeran joshi       return result;
20370ad73b6Ssameeran joshi     }
20470ad73b6Ssameeran joshi     const parser::Statement<parser::TypeGuardStmt> &stmt;
20570ad73b6Ssameeran joshi     std::optional<evaluate::DynamicType> guardType_; // is this POD?
20670ad73b6Ssameeran joshi   };
20770ad73b6Ssameeran joshi 
20870ad73b6Ssameeran joshi   // Returns true if and only if the values are different
20970ad73b6Ssameeran joshi   // Does apple to apple comparision, in case of TypeSpec or DerivedTypeSpec
21070ad73b6Ssameeran joshi   // checks for kinds as well.
TypesAreDifferent(const TypeCase & x,const TypeCase & y)21170ad73b6Ssameeran joshi   static bool TypesAreDifferent(const TypeCase &x, const TypeCase &y) {
21270ad73b6Ssameeran joshi     if (x.IsDefault()) { // C1164
21370ad73b6Ssameeran joshi       return !y.IsDefault();
21470ad73b6Ssameeran joshi     } else if (x.IsTypeSpec() && y.IsTypeSpec()) { // C1163
21570ad73b6Ssameeran joshi       return !AreTypeKindCompatible(x, y);
21670ad73b6Ssameeran joshi     } else if (x.IsDerivedTypeSpec() && y.IsDerivedTypeSpec()) { // C1163
21770ad73b6Ssameeran joshi       return !AreTypeKindCompatible(x, y);
21870ad73b6Ssameeran joshi     }
21970ad73b6Ssameeran joshi     return true;
22070ad73b6Ssameeran joshi   }
22170ad73b6Ssameeran joshi 
AreTypeKindCompatible(const TypeCase & x,const TypeCase & y)22270ad73b6Ssameeran joshi   static bool AreTypeKindCompatible(const TypeCase &x, const TypeCase &y) {
22370ad73b6Ssameeran joshi     return (*x.guardType()).IsTkCompatibleWith((*y.guardType()));
22470ad73b6Ssameeran joshi   }
22570ad73b6Ssameeran joshi 
ReportConflictingTypeCases()22670ad73b6Ssameeran joshi   void ReportConflictingTypeCases() {
22770ad73b6Ssameeran joshi     for (auto iter{typeCases_.begin()}; iter != typeCases_.end(); ++iter) {
22870ad73b6Ssameeran joshi       parser::Message *msg{nullptr};
22970ad73b6Ssameeran joshi       for (auto p{typeCases_.begin()}; p != typeCases_.end(); ++p) {
23070ad73b6Ssameeran joshi         if (p->stmt.source.begin() < iter->stmt.source.begin() &&
23170ad73b6Ssameeran joshi             !TypesAreDifferent(*p, *iter)) {
23270ad73b6Ssameeran joshi           if (!msg) {
23370ad73b6Ssameeran joshi             msg = &context_.Say(iter->stmt.source,
23470ad73b6Ssameeran joshi                 "Type specification '%s' conflicts with "
23570ad73b6Ssameeran joshi                 "previous type specification"_err_en_US,
23670ad73b6Ssameeran joshi                 iter->AsFortran());
23770ad73b6Ssameeran joshi           }
23870ad73b6Ssameeran joshi           msg->Attach(p->stmt.source,
23970ad73b6Ssameeran joshi               "Conflicting type specification '%s'"_en_US, p->AsFortran());
24070ad73b6Ssameeran joshi         }
24170ad73b6Ssameeran joshi       }
24270ad73b6Ssameeran joshi     }
24370ad73b6Ssameeran joshi   }
24470ad73b6Ssameeran joshi 
24570ad73b6Ssameeran joshi   SemanticsContext &context_;
24670ad73b6Ssameeran joshi   const evaluate::DynamicType &selectorType_;
24770ad73b6Ssameeran joshi   std::list<TypeCase> typeCases_;
24870ad73b6Ssameeran joshi   bool hasErrors_{false};
24970ad73b6Ssameeran joshi };
25070ad73b6Ssameeran joshi 
Enter(const parser::SelectTypeConstruct & construct)25170ad73b6Ssameeran joshi void SelectTypeChecker::Enter(const parser::SelectTypeConstruct &construct) {
25270ad73b6Ssameeran joshi   const auto &selectTypeStmt{
25370ad73b6Ssameeran joshi       std::get<parser::Statement<parser::SelectTypeStmt>>(construct.t)};
25470ad73b6Ssameeran joshi   const auto &selectType{selectTypeStmt.statement};
25570ad73b6Ssameeran joshi   const auto &unResolvedSel{std::get<parser::Selector>(selectType.t)};
25670ad73b6Ssameeran joshi   const auto *selector{GetExprFromSelector(unResolvedSel)};
25770ad73b6Ssameeran joshi 
25870ad73b6Ssameeran joshi   if (!selector) {
25970ad73b6Ssameeran joshi     return; // expression semantics failed on Selector
26070ad73b6Ssameeran joshi   }
26170ad73b6Ssameeran joshi   if (auto exprType{selector->GetType()}) {
26270ad73b6Ssameeran joshi     const auto &typeCaseList{
26370ad73b6Ssameeran joshi         std::get<std::list<parser::SelectTypeConstruct::TypeCase>>(
26470ad73b6Ssameeran joshi             construct.t)};
26570ad73b6Ssameeran joshi     TypeCaseValues{context_, *exprType}.Check(typeCaseList);
26670ad73b6Ssameeran joshi   }
26770ad73b6Ssameeran joshi }
26870ad73b6Ssameeran joshi 
GetExprFromSelector(const parser::Selector & selector)26970ad73b6Ssameeran joshi const SomeExpr *SelectTypeChecker::GetExprFromSelector(
27070ad73b6Ssameeran joshi     const parser::Selector &selector) {
271*cd03e96fSPeter Klausler   return common::visit([](const auto &x) { return GetExpr(x); }, selector.u);
27270ad73b6Ssameeran joshi }
27370ad73b6Ssameeran joshi } // namespace Fortran::semantics
274