//===-- lib/Semantics/check-select-type.cpp -------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "check-select-type.h" #include "flang/Common/idioms.h" #include "flang/Common/reference.h" #include "flang/Evaluate/fold.h" #include "flang/Evaluate/type.h" #include "flang/Parser/parse-tree.h" #include "flang/Semantics/semantics.h" #include "flang/Semantics/tools.h" #include namespace Fortran::semantics { class TypeCaseValues { public: TypeCaseValues(SemanticsContext &c, const evaluate::DynamicType &t) : context_{c}, selectorType_{t} {} void Check(const std::list &cases) { for (const auto &c : cases) { AddTypeCase(c); } if (!hasErrors_) { ReportConflictingTypeCases(); } } private: void AddTypeCase(const parser::SelectTypeConstruct::TypeCase &c) { const auto &stmt{std::get>(c.t)}; const parser::TypeGuardStmt &typeGuardStmt{stmt.statement}; const auto &guard{std::get(typeGuardStmt.t)}; if (std::holds_alternative(guard.u)) { typeCases_.emplace_back(stmt, std::nullopt); } else if (std::optional type{GetGuardType(guard)}) { if (PassesChecksOnGuard(stmt, *type)) { typeCases_.emplace_back(stmt, *type); } else { hasErrors_ = true; } } else { hasErrors_ = true; } } std::optional GetGuardType( const parser::TypeGuardStmt::Guard &guard) { return common::visit( common::visitors{ [](const parser::Default &) -> std::optional { return std::nullopt; }, [](const parser::TypeSpec &typeSpec) { return evaluate::DynamicType::From(typeSpec.declTypeSpec); }, [](const parser::DerivedTypeSpec &spec) -> std::optional { if (const auto *derivedTypeSpec{spec.derivedTypeSpec}) { return evaluate::DynamicType(*derivedTypeSpec); } return std::nullopt; }, }, guard.u); } bool PassesChecksOnGuard(const parser::Statement &stmt, const evaluate::DynamicType &guardDynamicType) { const parser::TypeGuardStmt &typeGuardStmt{stmt.statement}; const auto &guard{std::get(typeGuardStmt.t)}; return common::visit( common::visitors{ [](const parser::Default &) { return true; }, [&](const parser::TypeSpec &typeSpec) { const DeclTypeSpec *spec{typeSpec.declTypeSpec}; CHECK(spec); CHECK(spec->AsIntrinsic() || spec->AsDerived()); bool typeSpecRetVal{false}; if (spec->AsIntrinsic()) { typeSpecRetVal = true; if (!selectorType_.IsUnlimitedPolymorphic()) { // C1162 context_.Say(stmt.source, "If selector is not unlimited polymorphic, " "an intrinsic type specification must not be specified " "in the type guard statement"_err_en_US); typeSpecRetVal = false; } if (spec->category() == DeclTypeSpec::Character && !guardDynamicType.IsAssumedLengthCharacter()) { // C1160 auto location{parser::FindSourceLocation(typeSpec)}; context_.Say(location.empty() ? stmt.source : location, "The type specification statement must have " "LEN type parameter as assumed"_err_en_US); typeSpecRetVal = false; } } else { const DerivedTypeSpec *derived{spec->AsDerived()}; typeSpecRetVal = PassesDerivedTypeChecks( *derived, parser::FindSourceLocation(typeSpec)); } return typeSpecRetVal; }, [&](const parser::DerivedTypeSpec &x) { CHECK(x.derivedTypeSpec); const semantics::DerivedTypeSpec *derived{x.derivedTypeSpec}; return PassesDerivedTypeChecks( *derived, parser::FindSourceLocation(x)); }, }, guard.u); } bool PassesDerivedTypeChecks(const semantics::DerivedTypeSpec &derived, parser::CharBlock sourceLoc) const { for (const auto &pair : derived.parameters()) { if (pair.second.isLen() && !pair.second.isAssumed()) { // F'2023 C1165 context_.Say(sourceLoc, "The type specification statement must have LEN type parameter as assumed"_err_en_US); return false; } } if (!IsExtensibleType(&derived)) { // F'2023 C1166 context_.Say(sourceLoc, "The type specification statement must not specify a type with a SEQUENCE attribute or a BIND attribute"_err_en_US); return false; } if (!selectorType_.IsUnlimitedPolymorphic()) { // F'2023 C1167 if (const auto *selDerivedTypeSpec{ evaluate::GetDerivedTypeSpec(selectorType_)}) { if (!derived.MatchesOrExtends(*selDerivedTypeSpec)) { context_.Say(sourceLoc, "Type specification '%s' must be an extension of TYPE '%s'"_err_en_US, derived.AsFortran(), selDerivedTypeSpec->AsFortran()); return false; } } } return true; } struct TypeCase { explicit TypeCase(const parser::Statement &s, std::optional guardTypeDynamic) : stmt{s} { SetGuardType(guardTypeDynamic); } void SetGuardType(std::optional guardTypeDynamic) { const auto &guard{GetGuardFromStmt(stmt)}; common::visit(common::visitors{ [&](const parser::Default &) {}, [&](const auto &) { guardType_ = *guardTypeDynamic; }, }, guard.u); } bool IsDefault() const { const auto &guard{GetGuardFromStmt(stmt)}; return std::holds_alternative(guard.u); } bool IsTypeSpec() const { const auto &guard{GetGuardFromStmt(stmt)}; return std::holds_alternative(guard.u); } bool IsDerivedTypeSpec() const { const auto &guard{GetGuardFromStmt(stmt)}; return std::holds_alternative(guard.u); } const parser::TypeGuardStmt::Guard &GetGuardFromStmt( const parser::Statement &stmt) const { const parser::TypeGuardStmt &typeGuardStmt{stmt.statement}; return std::get(typeGuardStmt.t); } std::optional guardType() const { return guardType_; } std::string AsFortran() const { std::string result; if (this->guardType()) { auto type{*this->guardType()}; result += type.AsFortran(); } else { result += "DEFAULT"; } return result; } const parser::Statement &stmt; std::optional guardType_; // is this POD? }; // Returns true if and only if the values are different // Does apple to apple comparision, in case of TypeSpec or DerivedTypeSpec // checks for kinds as well. static bool TypesAreDifferent(const TypeCase &x, const TypeCase &y) { if (x.IsDefault()) { // C1164 return !y.IsDefault(); } else if (x.IsTypeSpec() && y.IsTypeSpec()) { // C1163 return !AreTypeKindCompatible(x, y); } else if (x.IsDerivedTypeSpec() && y.IsDerivedTypeSpec()) { // C1163 return !AreTypeKindCompatible(x, y); } return true; } static bool AreTypeKindCompatible(const TypeCase &x, const TypeCase &y) { return (*x.guardType()).IsTkCompatibleWith((*y.guardType())); } void ReportConflictingTypeCases() { for (auto iter{typeCases_.begin()}; iter != typeCases_.end(); ++iter) { parser::Message *msg{nullptr}; for (auto p{typeCases_.begin()}; p != typeCases_.end(); ++p) { if (p->stmt.source.begin() < iter->stmt.source.begin() && !TypesAreDifferent(*p, *iter)) { if (!msg) { msg = &context_.Say(iter->stmt.source, "Type specification '%s' conflicts with " "previous type specification"_err_en_US, iter->AsFortran()); } msg->Attach(p->stmt.source, "Conflicting type specification '%s'"_en_US, p->AsFortran()); } } } } SemanticsContext &context_; const evaluate::DynamicType &selectorType_; std::list typeCases_; bool hasErrors_{false}; }; void SelectTypeChecker::Enter(const parser::SelectTypeConstruct &construct) { const auto &selectTypeStmt{ std::get>(construct.t)}; const auto &selectType{selectTypeStmt.statement}; const auto &unResolvedSel{std::get(selectType.t)}; if (const auto *selector{GetExprFromSelector(unResolvedSel)}) { if (IsProcedure(*selector)) { context_.Say( selectTypeStmt.source, "Selector may not be a procedure"_err_en_US); } else if (evaluate::IsAssumedRank(*selector)) { context_.Say(selectTypeStmt.source, "Assumed-rank variable may only be used as actual argument"_err_en_US); } else if (auto exprType{selector->GetType()}) { const auto &typeCaseList{ std::get>( construct.t)}; TypeCaseValues{context_, *exprType}.Check(typeCaseList); } } } const SomeExpr *SelectTypeChecker::GetExprFromSelector( const parser::Selector &selector) { return common::visit([](const auto &x) { return GetExpr(x); }, selector.u); } } // namespace Fortran::semantics