1 //===-- lib/Semantics/check-select-type.cpp -------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "check-select-type.h"
10 #include "flang/Common/idioms.h"
11 #include "flang/Common/reference.h"
12 #include "flang/Evaluate/fold.h"
13 #include "flang/Evaluate/type.h"
14 #include "flang/Parser/parse-tree.h"
15 #include "flang/Semantics/semantics.h"
16 #include "flang/Semantics/tools.h"
19 namespace Fortran::semantics
{
21 class TypeCaseValues
{
23 TypeCaseValues(SemanticsContext
&c
, const evaluate::DynamicType
&t
)
24 : context_
{c
}, selectorType_
{t
} {}
25 void Check(const std::list
<parser::SelectTypeConstruct::TypeCase
> &cases
) {
26 for (const auto &c
: cases
) {
30 ReportConflictingTypeCases();
35 void AddTypeCase(const parser::SelectTypeConstruct::TypeCase
&c
) {
36 const auto &stmt
{std::get
<parser::Statement
<parser::TypeGuardStmt
>>(c
.t
)};
37 const parser::TypeGuardStmt
&typeGuardStmt
{stmt
.statement
};
38 const auto &guard
{std::get
<parser::TypeGuardStmt::Guard
>(typeGuardStmt
.t
)};
39 if (std::holds_alternative
<parser::Default
>(guard
.u
)) {
40 typeCases_
.emplace_back(stmt
, std::nullopt
);
41 } else if (std::optional
<evaluate::DynamicType
> type
{GetGuardType(guard
)}) {
42 if (PassesChecksOnGuard(stmt
, *type
)) {
43 typeCases_
.emplace_back(stmt
, *type
);
52 std::optional
<evaluate::DynamicType
> GetGuardType(
53 const parser::TypeGuardStmt::Guard
&guard
) {
56 [](const parser::Default
&)
57 -> std::optional
<evaluate::DynamicType
> {
60 [](const parser::TypeSpec
&typeSpec
) {
61 return evaluate::DynamicType::From(typeSpec
.declTypeSpec
);
63 [](const parser::DerivedTypeSpec
&spec
)
64 -> std::optional
<evaluate::DynamicType
> {
65 if (const auto *derivedTypeSpec
{spec
.derivedTypeSpec
}) {
66 return evaluate::DynamicType(*derivedTypeSpec
);
74 bool PassesChecksOnGuard(const parser::Statement
<parser::TypeGuardStmt
> &stmt
,
75 const evaluate::DynamicType
&guardDynamicType
) {
76 const parser::TypeGuardStmt
&typeGuardStmt
{stmt
.statement
};
77 const auto &guard
{std::get
<parser::TypeGuardStmt::Guard
>(typeGuardStmt
.t
)};
80 [](const parser::Default
&) { return true; },
81 [&](const parser::TypeSpec
&typeSpec
) {
82 const DeclTypeSpec
*spec
{typeSpec
.declTypeSpec
};
84 CHECK(spec
->AsIntrinsic() || spec
->AsDerived());
85 bool typeSpecRetVal
{false};
86 if (spec
->AsIntrinsic()) {
87 typeSpecRetVal
= true;
88 if (!selectorType_
.IsUnlimitedPolymorphic()) { // C1162
89 context_
.Say(stmt
.source
,
90 "If selector is not unlimited polymorphic, "
91 "an intrinsic type specification must not be specified "
92 "in the type guard statement"_err_en_US
);
93 typeSpecRetVal
= false;
95 if (spec
->category() == DeclTypeSpec::Character
&&
96 !guardDynamicType
.IsAssumedLengthCharacter()) { // C1160
97 auto location
{parser::FindSourceLocation(typeSpec
)};
98 context_
.Say(location
.empty() ? stmt
.source
: location
,
99 "The type specification statement must have "
100 "LEN type parameter as assumed"_err_en_US
);
101 typeSpecRetVal
= false;
104 const DerivedTypeSpec
*derived
{spec
->AsDerived()};
105 typeSpecRetVal
= PassesDerivedTypeChecks(
106 *derived
, parser::FindSourceLocation(typeSpec
));
108 return typeSpecRetVal
;
110 [&](const parser::DerivedTypeSpec
&x
) {
111 CHECK(x
.derivedTypeSpec
);
112 const semantics::DerivedTypeSpec
*derived
{x
.derivedTypeSpec
};
113 return PassesDerivedTypeChecks(
114 *derived
, parser::FindSourceLocation(x
));
120 bool PassesDerivedTypeChecks(const semantics::DerivedTypeSpec
&derived
,
121 parser::CharBlock sourceLoc
) const {
122 for (const auto &pair
: derived
.parameters()) {
123 if (pair
.second
.isLen() && !pair
.second
.isAssumed()) { // F'2023 C1165
124 context_
.Say(sourceLoc
,
125 "The type specification statement must have LEN type parameter as assumed"_err_en_US
);
129 if (!IsExtensibleType(&derived
)) { // F'2023 C1166
130 context_
.Say(sourceLoc
,
131 "The type specification statement must not specify a type with a SEQUENCE attribute or a BIND attribute"_err_en_US
);
134 if (!selectorType_
.IsUnlimitedPolymorphic()) { // F'2023 C1167
135 if (const auto *selDerivedTypeSpec
{
136 evaluate::GetDerivedTypeSpec(selectorType_
)}) {
137 if (!derived
.MatchesOrExtends(*selDerivedTypeSpec
)) {
138 context_
.Say(sourceLoc
,
139 "Type specification '%s' must be an extension of TYPE '%s'"_err_en_US
,
140 derived
.AsFortran(), selDerivedTypeSpec
->AsFortran());
149 explicit TypeCase(const parser::Statement
<parser::TypeGuardStmt
> &s
,
150 std::optional
<evaluate::DynamicType
> guardTypeDynamic
)
152 SetGuardType(guardTypeDynamic
);
155 void SetGuardType(std::optional
<evaluate::DynamicType
> guardTypeDynamic
) {
156 const auto &guard
{GetGuardFromStmt(stmt
)};
157 common::visit(common::visitors
{
158 [&](const parser::Default
&) {},
159 [&](const auto &) { guardType_
= *guardTypeDynamic
; },
164 bool IsDefault() const {
165 const auto &guard
{GetGuardFromStmt(stmt
)};
166 return std::holds_alternative
<parser::Default
>(guard
.u
);
169 bool IsTypeSpec() const {
170 const auto &guard
{GetGuardFromStmt(stmt
)};
171 return std::holds_alternative
<parser::TypeSpec
>(guard
.u
);
174 bool IsDerivedTypeSpec() const {
175 const auto &guard
{GetGuardFromStmt(stmt
)};
176 return std::holds_alternative
<parser::DerivedTypeSpec
>(guard
.u
);
179 const parser::TypeGuardStmt::Guard
&GetGuardFromStmt(
180 const parser::Statement
<parser::TypeGuardStmt
> &stmt
) const {
181 const parser::TypeGuardStmt
&typeGuardStmt
{stmt
.statement
};
182 return std::get
<parser::TypeGuardStmt::Guard
>(typeGuardStmt
.t
);
185 std::optional
<evaluate::DynamicType
> guardType() const {
189 std::string
AsFortran() const {
191 if (this->guardType()) {
192 auto type
{*this->guardType()};
193 result
+= type
.AsFortran();
199 const parser::Statement
<parser::TypeGuardStmt
> &stmt
;
200 std::optional
<evaluate::DynamicType
> guardType_
; // is this POD?
203 // Returns true if and only if the values are different
204 // Does apple to apple comparision, in case of TypeSpec or DerivedTypeSpec
205 // checks for kinds as well.
206 static bool TypesAreDifferent(const TypeCase
&x
, const TypeCase
&y
) {
207 if (x
.IsDefault()) { // C1164
208 return !y
.IsDefault();
209 } else if (x
.IsTypeSpec() && y
.IsTypeSpec()) { // C1163
210 return !AreTypeKindCompatible(x
, y
);
211 } else if (x
.IsDerivedTypeSpec() && y
.IsDerivedTypeSpec()) { // C1163
212 return !AreTypeKindCompatible(x
, y
);
217 static bool AreTypeKindCompatible(const TypeCase
&x
, const TypeCase
&y
) {
218 return (*x
.guardType()).IsTkCompatibleWith((*y
.guardType()));
221 void ReportConflictingTypeCases() {
222 for (auto iter
{typeCases_
.begin()}; iter
!= typeCases_
.end(); ++iter
) {
223 parser::Message
*msg
{nullptr};
224 for (auto p
{typeCases_
.begin()}; p
!= typeCases_
.end(); ++p
) {
225 if (p
->stmt
.source
.begin() < iter
->stmt
.source
.begin() &&
226 !TypesAreDifferent(*p
, *iter
)) {
228 msg
= &context_
.Say(iter
->stmt
.source
,
229 "Type specification '%s' conflicts with "
230 "previous type specification"_err_en_US
,
233 msg
->Attach(p
->stmt
.source
,
234 "Conflicting type specification '%s'"_en_US
, p
->AsFortran());
240 SemanticsContext
&context_
;
241 const evaluate::DynamicType
&selectorType_
;
242 std::list
<TypeCase
> typeCases_
;
243 bool hasErrors_
{false};
246 void SelectTypeChecker::Enter(const parser::SelectTypeConstruct
&construct
) {
247 const auto &selectTypeStmt
{
248 std::get
<parser::Statement
<parser::SelectTypeStmt
>>(construct
.t
)};
249 const auto &selectType
{selectTypeStmt
.statement
};
250 const auto &unResolvedSel
{std::get
<parser::Selector
>(selectType
.t
)};
251 if (const auto *selector
{GetExprFromSelector(unResolvedSel
)}) {
252 if (IsProcedure(*selector
)) {
254 selectTypeStmt
.source
, "Selector may not be a procedure"_err_en_US
);
255 } else if (evaluate::IsAssumedRank(*selector
)) {
256 context_
.Say(selectTypeStmt
.source
,
257 "Assumed-rank variable may only be used as actual argument"_err_en_US
);
258 } else if (auto exprType
{selector
->GetType()}) {
259 const auto &typeCaseList
{
260 std::get
<std::list
<parser::SelectTypeConstruct::TypeCase
>>(
262 TypeCaseValues
{context_
, *exprType
}.Check(typeCaseList
);
267 const SomeExpr
*SelectTypeChecker::GetExprFromSelector(
268 const parser::Selector
&selector
) {
269 return common::visit([](const auto &x
) { return GetExpr(x
); }, selector
.u
);
271 } // namespace Fortran::semantics