diff --git a/flang/lib/Semantics/CMakeLists.txt b/flang/lib/Semantics/CMakeLists.txt --- a/flang/lib/Semantics/CMakeLists.txt +++ b/flang/lib/Semantics/CMakeLists.txt @@ -20,6 +20,7 @@ check-omp-structure.cpp check-purity.cpp check-return.cpp + check-select-type.cpp check-stop.cpp expression.cpp mod-file.cpp diff --git a/flang/lib/Semantics/check-select-type.h b/flang/lib/Semantics/check-select-type.h new file mode 100644 --- /dev/null +++ b/flang/lib/Semantics/check-select-type.h @@ -0,0 +1,31 @@ +//===-- lib/Semantics/check-select-type.h -----------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef FORTRAN_SEMANTICS_CHECK_SELECT_TYPE_H_ +#define FORTRAN_SEMANTICS_CHECK_SELECT_TYPE_H_ + +#include "flang/Semantics/semantics.h" + +namespace Fortran::parser { +struct SelectTypeConstruct; +struct Selector; +} // namespace Fortran::parser + +namespace Fortran::semantics { + +class SelectTypeChecker : public virtual BaseChecker { +public: + explicit SelectTypeChecker(SemanticsContext &context) : context_{context} {}; + void Enter(const parser::SelectTypeConstruct &); + +private: + const SomeExpr *GetExprFromSelector(const parser::Selector &); + SemanticsContext &context_; +}; +} // namespace Fortran::semantics +#endif // FORTRAN_SEMANTICS_CHECK_SELECT_TYPE_H_ diff --git a/flang/lib/Semantics/check-select-type.cpp b/flang/lib/Semantics/check-select-type.cpp new file mode 100644 --- /dev/null +++ b/flang/lib/Semantics/check-select-type.cpp @@ -0,0 +1,262 @@ +//===-- 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)}) { + auto success{PerformChecksOnGuard(guard, *type)}; + if (success) { + typeCases_.emplace_back(stmt, *type); + } else { + hasErrors_ = true; + } + } else { + hasErrors_ = true; + } + } + + std::optional GetGuardType( + const parser::TypeGuardStmt::Guard &guard) { + return std::visit( + common::visitors{ + [&](const parser::Default &) + -> std::optional { + return std::nullopt; + }, + [&](const parser::TypeSpec &typeSpec) + -> std::optional { + if (const DeclTypeSpec * declTypeSpec{typeSpec.declTypeSpec}) { + if (std::optional guardTypeDynamic{ + evaluate::DynamicType::From(declTypeSpec)}) { + return *guardTypeDynamic; + } + } + return std::nullopt; + }, + [&](const parser::DerivedTypeSpec &spec) + -> std::optional { + if (const auto *derivedTypeSpec{spec.derivedTypeSpec}) { + const semantics::Symbol &typeSymbol{ + derivedTypeSpec->typeSymbol()}; + if (const semantics::Scope * scope{typeSymbol.scope()}) { + auto &mutablederivedTypeSpec{ + *const_cast( + derivedTypeSpec)}; + auto &mutableConstScope{ + *const_cast(scope)}; + auto &declTypeSpec{mutableConstScope.MakeDerivedType( + DeclTypeSpec::TypeDerived, + std::move(mutablederivedTypeSpec))}; + if (auto guardTypeDynamic{ + evaluate::DynamicType::From(declTypeSpec)}) { + return *guardTypeDynamic; + } + } + } + return std::nullopt; + }, + }, + guard.u); + } + + bool PerformChecksOnGuard(const parser::TypeGuardStmt::Guard &guard, + const evaluate::DynamicType &guardDynamicType) { + return std::visit( + common::visitors{ + [&](const parser::Default &) { return true; }, + [&](const parser::TypeSpec &typeSpec) { + if (const DeclTypeSpec * spec{typeSpec.declTypeSpec}) { + if (spec->category() == DeclTypeSpec::Character && + !guardDynamicType.IsAssumedLengthCharacter()) { // C1160 + context_.Say(parser::FindSourceLocation(typeSpec), + "The type specification statement must have LEN type parameter as assumed"_err_en_US); + return false; + } + if (const DerivedTypeSpec * derived{spec->AsDerived()}) { + return ChecksOnDerivedTypeSpec( + derived, parser::FindSourceLocation(typeSpec)); + } + return false; + } + return false; + }, + [&](const parser::DerivedTypeSpec &x) { + if (const semantics::DerivedTypeSpec * + derived{x.derivedTypeSpec}) { + return ChecksOnDerivedTypeSpec( + derived, parser::FindSourceLocation(x)); + } + return false; + }, + }, + guard.u); + } + bool ChecksOnDerivedTypeSpec(const semantics::DerivedTypeSpec *derived, + parser::CharBlock sourceLoc) const { + if (!IsExtensibleType(derived)) { // C1161 + context_.Say(sourceLoc, + "The type specification statement must not specify " + "a type with SEQUENCE attribute or a BIND attribute"_err_en_US); + return false; + } + // TODO: C1162 + 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)}; + std::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 { // TODO:needs more type details on output + std::string result; + llvm::raw_string_ostream bs{result}; + if (this->guardType()) { + auto type{*this->guardType()}; + bs << type.AsFortran(); + } else { + bs << "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. + struct Comparator { + bool operator()(const TypeCase &x, const TypeCase &y) const { + if (x.IsDefault()) { // C1164 + return !y.IsDefault(); + } else if (x.IsTypeSpec() && y.IsTypeSpec()) { // C1163 + return !CheckTypeKindCompatibility(x, y); + } else if (x.IsDerivedTypeSpec() && y.IsDerivedTypeSpec()) { // C1163 + return !CheckTypeKindCompatibility(x, y); + } + return true; + } + + bool CheckTypeKindCompatibility( + const TypeCase &x, const TypeCase &y) const { + if (x.guardType() && y.guardType()) { + auto typex{*x.guardType()}; + auto typey{*y.guardType()}; + return typex.IsTkCompatibleWith(typey); + } + return false; + } + }; + + // // This has quadratic time, but only runs in non-error cases + 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() && + !Comparator{}(*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}; +}; // namespace Fortran::semantics + +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)}; + const auto *Selector{GetExprFromSelector(unResolvedSel)}; + if (!Selector) { + return; // expression semantics failed on Selector + } + 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 std::visit([](const auto &x) { return GetExpr(x); }, selector.u); +} +} // namespace Fortran::semantics diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp --- a/flang/lib/Semantics/resolve-names.cpp +++ b/flang/lib/Semantics/resolve-names.cpp @@ -5090,6 +5090,19 @@ "Selector is not a variable"_err_en_US); association = {}; } + if (const DeclTypeSpec * type{whole->GetType()}) { + if (!type->IsPolymorphic()) { // C1159 + Say(association.selector.source, + "Selector '%s' in SELECT TYPE statements must be " + "polymorphic value"_err_en_US); + } + } + + } else if (evaluate::HasVectorSubscript( + *association.selector.expr)) { // C1158 + Say(association.selector.source, + "Neither 'associate-name =>' " + "nor any subobject must appear in selector"_err_en_US); } else { Say(association.selector.source, // C1157 "Selector is not a named variable: 'associate-name =>' is required"_err_en_US); diff --git a/flang/lib/Semantics/semantics.cpp b/flang/lib/Semantics/semantics.cpp --- a/flang/lib/Semantics/semantics.cpp +++ b/flang/lib/Semantics/semantics.cpp @@ -25,6 +25,7 @@ #include "check-omp-structure.h" #include "check-purity.h" #include "check-return.h" +#include "check-select-type.h" #include "check-stop.h" #include "mod-file.h" #include "resolve-labels.h" @@ -155,7 +156,7 @@ ArithmeticIfStmtChecker, AssignmentChecker, CaseChecker, CoarrayChecker, DataChecker, DeallocateChecker, DoForallChecker, IfStmtChecker, IoChecker, MiscChecker, NamelistChecker, NullifyChecker, OmpStructureChecker, - PurityChecker, ReturnStmtChecker, StopChecker>; + PurityChecker, ReturnStmtChecker, SelectTypeChecker, StopChecker>; static bool PerformStatementSemantics( SemanticsContext &context, parser::Program &program) { diff --git a/flang/test/Semantics/selecttype01.f90 b/flang/test/Semantics/selecttype01.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Semantics/selecttype01.f90 @@ -0,0 +1,214 @@ +! RUN: %B/test/Semantics/test_errors.sh %s %flang %t +! Test for checking select type constraints, +module m1 + use ISO_C_BINDING + type shape + integer :: color + logical :: filled + integer :: x + integer :: y + end type shape + type, extends(shape) :: rectangle + integer :: length + integer :: width + end type rectangle + type, extends(rectangle) :: square + end type square + type :: unrelated + logical :: some_logical + end type + type withSequence + SEQUENCE + integer :: x + end type + type, BIND(C) :: withBind + INTEGER(c_int) ::int_in_c + end type + + TYPE(shape), TARGET :: shape_obj + TYPE(rectangle), TARGET :: rect_obj + TYPE(square), TARGET :: squr_obj + !define polymorphic objects + class(*), pointer :: unlim_polymorphic + class(shape), pointer :: shape_lim_polymorphic +end +module m + type :: t(n) + integer, len :: n + end type +contains + subroutine CheckC1160( a ) + class(*), intent(in) :: a + select type ( a ) +!ERROR: The type specification statement must have LEN type parameter as assumed + type is ( character(len=10) ) !<-- assumed length-type + ! OK + type is ( character(len=*) ) +!ERROR: Derived type 'character' not found + class is ( character(len=10) ) !<-- assumed length-type + end select + end subroutine +end module + +subroutine CheckC1157 + use m1 + integer, parameter :: const_var=10 +!ERROR: Selector is not a named variable: 'associate-name =>' is required + select type(10) + end select +!ERROR: Selector is not a named variable: 'associate-name =>' is required + select type(const_var) + end select +!ERROR: Selector is not a named variable: 'associate-name =>' is required + select type (4.999) + end select +!ERROR: Selector is not a named variable: 'associate-name =>' is required + select type (shape_obj%x) + end select +end subroutine + +subroutine CheckC1158 + integer, parameter :: i(4) = [1, 2, 3, 4] + integer :: array (10) + !ERROR: Neither 'associate-name =>' nor any subobject must appear in selector + select type(array(i)) + end select +end subroutine + +!CheckPloymorphicSelectorType +subroutine CheckC1159 + integer :: int_variable + real :: real_variable + complex :: complex_var = cmplx(3.0, 4.0) + logical :: log_variable + character (len=10) :: char_variable = "OM" + !ERROR: Selector 'int_variable' in SELECT TYPE statements must be polymorphic value + select type (int_variable) + end select + !ERROR: Selector 'real_variable' in SELECT TYPE statements must be polymorphic value + select type (real_variable) + end select + !ERROR: Selector 'complex_var' in SELECT TYPE statements must be polymorphic value + select type(complex_var) + end select + !ERROR: Selector 'logical_variable' in SELECT TYPE statements must be polymorphic value + select type(logical_variable) + end select + !ERROR: Selector 'char_variable' in SELECT TYPE statements must be polymorphic value + select type(char_variable) + end select +end + +subroutine CheckC1161 + use m1 + shape_lim_polymorphic => rect_obj + select type(shape_lim_polymorphic) + !ERROR: The type specification statement must not specify a type with SEQUENCE attribute or a BIND attribute + type is (withSequence) + !ERROR: The type specification statement must not specify a type with SEQUENCE attribute or a BIND attribute + type is (withBind) + end select +end + +!!Trying to point from lower type object to higher object and then some unrelated object +subroutine CheckC1162 + use m1 + class(rectangle), pointer :: rectangle_polymorphic + !not unlimited polymorphic objects + select type (rectangle_polymorphic) + !![TODO]ERROR: the type is/class is should have an object of type which is derivable from child_polymorphic + type is (shape) + !![TODO]ERROR: the type is/class is should have an object of type which is derivable from child_polymorphic + type is (unrelated) + end select + + !!This check should not catch unlimited polymorphic objects. + unlim_polymorphic => rect_obj + select type (unlim_polymorphic) + type is (shape) + type is (unrelated) + end select +end +subroutine CheckC1163 + use m1 + !assign dynamically + shape_lim_polymorphic => rect_obj + unlim_polymorphic => shape_obj + select type (shape_lim_polymorphic) + type is (shape) +!ERROR: Type specification shape conflicts with previous type specification + type is (shape) + class is (square) +!ERROR: Type specification square conflicts with previous type specification + class is (square) +! ![FIXME]: figure out whats allowed and not allowed +! type is (integer) +! class is (integer) + + end select + +! select type (unlim_polymorphic) +! ![FIXME] figure out whats allowed and not allowed +! class is (integer) +! type is (integer) +! end select +end +subroutine CheckC1164 + use m1 + shape_lim_polymorphic => rect_obj + unlim_polymorphic => shape_obj + select type (shape_lim_polymorphic) + CLASS DEFAULT +!ERROR: Type specification DEFAULT conflicts with previous type specification + CLASS DEFAULT + TYPE IS (shape) + TYPE IS (rectangle) +!ERROR: Type specification DEFAULT conflicts with previous type specification + CLASS DEFAULT + end select + + !Saving computation if some error in guard by not computing RepeatingCases + select type (shape_lim_polymorphic) + CLASS DEFAULT + CLASS DEFAULT + !ERROR: The type specification statement must not specify a type with SEQUENCE attribute or a BIND attribute + TYPE IS(withSequence) + end select +end subroutine +subroutine WorkingPolymorphism + use m1 + + !assign dynamically + shape_lim_polymorphic => rect_obj + unlim_polymorphic => shape_obj + select type (shape_lim_polymorphic) + type is (shape) + print *, "hello shape" + type is (rectangle) + print *, "hello rect" + type is (square) + print *, "hello square" + CLASS DEFAULT + print *, "default" + end select + + print *, "unlim polymorphism" + + select type (unlim_polymorphic) + type is (shape) + print *, "hello shape" + type is (rectangle) + print *, "hello rect" + type is (square) + print *, "hello square" + CLASS DEFAULT + print *, "default" + end select +end + +program main + use m, only : CheckC1160 + call CheckC1160( "Hello!" ) + call CheckC1160( 1.0 ) +end program +