1 //===-- lib/Semantics/resolve-names-utils.cpp -----------------------------===//
2 //
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
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "resolve-names-utils.h"
10 #include "flang/Common/Fortran-features.h"
11 #include "flang/Common/idioms.h"
12 #include "flang/Common/indirection.h"
13 #include "flang/Evaluate/fold.h"
14 #include "flang/Evaluate/tools.h"
15 #include "flang/Evaluate/type.h"
16 #include "flang/Parser/char-block.h"
17 #include "flang/Parser/parse-tree.h"
18 #include "flang/Semantics/expression.h"
19 #include "flang/Semantics/semantics.h"
20 #include "flang/Semantics/tools.h"
21 #include <initializer_list>
22 #include <variant>
23
24 namespace Fortran::semantics {
25
26 using common::LanguageFeature;
27 using common::LogicalOperator;
28 using common::NumericOperator;
29 using common::RelationalOperator;
30 using IntrinsicOperator = parser::DefinedOperator::IntrinsicOperator;
31
32 static constexpr const char *operatorPrefix{"operator("};
33
34 static GenericKind MapIntrinsicOperator(IntrinsicOperator);
35
Resolve(const parser::Name & name,Symbol * symbol)36 Symbol *Resolve(const parser::Name &name, Symbol *symbol) {
37 if (symbol && !name.symbol) {
38 name.symbol = symbol;
39 }
40 return symbol;
41 }
Resolve(const parser::Name & name,Symbol & symbol)42 Symbol &Resolve(const parser::Name &name, Symbol &symbol) {
43 return *Resolve(name, &symbol);
44 }
45
WithIsFatal(const parser::MessageFixedText & msg,bool isFatal)46 parser::MessageFixedText WithIsFatal(
47 const parser::MessageFixedText &msg, bool isFatal) {
48 return parser::MessageFixedText{
49 msg.text().begin(), msg.text().size(), isFatal};
50 }
51
IsIntrinsicOperator(const SemanticsContext & context,const SourceName & name)52 bool IsIntrinsicOperator(
53 const SemanticsContext &context, const SourceName &name) {
54 std::string str{name.ToString()};
55 for (int i{0}; i != common::LogicalOperator_enumSize; ++i) {
56 auto names{context.languageFeatures().GetNames(LogicalOperator{i})};
57 if (std::find(names.begin(), names.end(), str) != names.end()) {
58 return true;
59 }
60 }
61 for (int i{0}; i != common::RelationalOperator_enumSize; ++i) {
62 auto names{context.languageFeatures().GetNames(RelationalOperator{i})};
63 if (std::find(names.begin(), names.end(), str) != names.end()) {
64 return true;
65 }
66 }
67 return false;
68 }
69
70 template <typename E>
GetOperatorNames(const SemanticsContext & context,E opr)71 std::forward_list<std::string> GetOperatorNames(
72 const SemanticsContext &context, E opr) {
73 std::forward_list<std::string> result;
74 for (const char *name : context.languageFeatures().GetNames(opr)) {
75 result.emplace_front(std::string{operatorPrefix} + name + ')');
76 }
77 return result;
78 }
79
GetAllNames(const SemanticsContext & context,const SourceName & name)80 std::forward_list<std::string> GetAllNames(
81 const SemanticsContext &context, const SourceName &name) {
82 std::string str{name.ToString()};
83 if (!name.empty() && name.end()[-1] == ')' &&
84 name.ToString().rfind(std::string{operatorPrefix}, 0) == 0) {
85 for (int i{0}; i != common::LogicalOperator_enumSize; ++i) {
86 auto names{GetOperatorNames(context, LogicalOperator{i})};
87 if (std::find(names.begin(), names.end(), str) != names.end()) {
88 return names;
89 }
90 }
91 for (int i{0}; i != common::RelationalOperator_enumSize; ++i) {
92 auto names{GetOperatorNames(context, RelationalOperator{i})};
93 if (std::find(names.begin(), names.end(), str) != names.end()) {
94 return names;
95 }
96 }
97 }
98 return {str};
99 }
100
IsLogicalConstant(const SemanticsContext & context,const SourceName & name)101 bool IsLogicalConstant(
102 const SemanticsContext &context, const SourceName &name) {
103 std::string str{name.ToString()};
104 return str == ".true." || str == ".false." ||
105 (context.IsEnabled(LanguageFeature::LogicalAbbreviations) &&
106 (str == ".t" || str == ".f."));
107 }
108
Resolve(Symbol * symbol) const109 void GenericSpecInfo::Resolve(Symbol *symbol) const {
110 if (symbol) {
111 if (auto *details{symbol->detailsIf<GenericDetails>()}) {
112 details->set_kind(kind_);
113 }
114 if (parseName_) {
115 semantics::Resolve(*parseName_, symbol);
116 }
117 }
118 }
119
Analyze(const parser::DefinedOpName & name)120 void GenericSpecInfo::Analyze(const parser::DefinedOpName &name) {
121 kind_ = GenericKind::OtherKind::DefinedOp;
122 parseName_ = &name.v;
123 symbolName_ = name.v.source;
124 }
125
Analyze(const parser::GenericSpec & x)126 void GenericSpecInfo::Analyze(const parser::GenericSpec &x) {
127 symbolName_ = x.source;
128 kind_ = std::visit(
129 common::visitors{
130 [&](const parser::Name &y) -> GenericKind {
131 parseName_ = &y;
132 symbolName_ = y.source;
133 return GenericKind::OtherKind::Name;
134 },
135 [&](const parser::DefinedOperator &y) {
136 return std::visit(
137 common::visitors{
138 [&](const parser::DefinedOpName &z) -> GenericKind {
139 Analyze(z);
140 return GenericKind::OtherKind::DefinedOp;
141 },
142 [&](const IntrinsicOperator &z) {
143 return MapIntrinsicOperator(z);
144 },
145 },
146 y.u);
147 },
148 [&](const parser::GenericSpec::Assignment &) -> GenericKind {
149 return GenericKind::OtherKind::Assignment;
150 },
151 [&](const parser::GenericSpec::ReadFormatted &) -> GenericKind {
152 return GenericKind::DefinedIo::ReadFormatted;
153 },
154 [&](const parser::GenericSpec::ReadUnformatted &) -> GenericKind {
155 return GenericKind::DefinedIo::ReadUnformatted;
156 },
157 [&](const parser::GenericSpec::WriteFormatted &) -> GenericKind {
158 return GenericKind::DefinedIo::WriteFormatted;
159 },
160 [&](const parser::GenericSpec::WriteUnformatted &) -> GenericKind {
161 return GenericKind::DefinedIo::WriteUnformatted;
162 },
163 },
164 x.u);
165 }
166
operator <<(llvm::raw_ostream & os,const GenericSpecInfo & info)167 llvm::raw_ostream &operator<<(
168 llvm::raw_ostream &os, const GenericSpecInfo &info) {
169 os << "GenericSpecInfo: kind=" << info.kind_.ToString();
170 os << " parseName="
171 << (info.parseName_ ? info.parseName_->ToString() : "null");
172 os << " symbolName="
173 << (info.symbolName_ ? info.symbolName_->ToString() : "null");
174 return os;
175 }
176
177 // parser::DefinedOperator::IntrinsicOperator -> GenericKind
MapIntrinsicOperator(IntrinsicOperator op)178 static GenericKind MapIntrinsicOperator(IntrinsicOperator op) {
179 switch (op) {
180 SWITCH_COVERS_ALL_CASES
181 case IntrinsicOperator::Concat:
182 return GenericKind::OtherKind::Concat;
183 case IntrinsicOperator::Power:
184 return NumericOperator::Power;
185 case IntrinsicOperator::Multiply:
186 return NumericOperator::Multiply;
187 case IntrinsicOperator::Divide:
188 return NumericOperator::Divide;
189 case IntrinsicOperator::Add:
190 return NumericOperator::Add;
191 case IntrinsicOperator::Subtract:
192 return NumericOperator::Subtract;
193 case IntrinsicOperator::AND:
194 return LogicalOperator::And;
195 case IntrinsicOperator::OR:
196 return LogicalOperator::Or;
197 case IntrinsicOperator::EQV:
198 return LogicalOperator::Eqv;
199 case IntrinsicOperator::NEQV:
200 return LogicalOperator::Neqv;
201 case IntrinsicOperator::NOT:
202 return LogicalOperator::Not;
203 case IntrinsicOperator::LT:
204 return RelationalOperator::LT;
205 case IntrinsicOperator::LE:
206 return RelationalOperator::LE;
207 case IntrinsicOperator::EQ:
208 return RelationalOperator::EQ;
209 case IntrinsicOperator::NE:
210 return RelationalOperator::NE;
211 case IntrinsicOperator::GE:
212 return RelationalOperator::GE;
213 case IntrinsicOperator::GT:
214 return RelationalOperator::GT;
215 }
216 }
217
218 class ArraySpecAnalyzer {
219 public:
ArraySpecAnalyzer(SemanticsContext & context)220 ArraySpecAnalyzer(SemanticsContext &context) : context_{context} {}
221 ArraySpec Analyze(const parser::ArraySpec &);
222 ArraySpec AnalyzeDeferredShapeSpecList(const parser::DeferredShapeSpecList &);
223 ArraySpec Analyze(const parser::ComponentArraySpec &);
224 ArraySpec Analyze(const parser::CoarraySpec &);
225
226 private:
227 SemanticsContext &context_;
228 ArraySpec arraySpec_;
229
Analyze(const std::list<T> & list)230 template <typename T> void Analyze(const std::list<T> &list) {
231 for (const auto &elem : list) {
232 Analyze(elem);
233 }
234 }
235 void Analyze(const parser::AssumedShapeSpec &);
236 void Analyze(const parser::ExplicitShapeSpec &);
237 void Analyze(const parser::AssumedImpliedSpec &);
238 void Analyze(const parser::DeferredShapeSpecList &);
239 void Analyze(const parser::AssumedRankSpec &);
240 void MakeExplicit(const std::optional<parser::SpecificationExpr> &,
241 const parser::SpecificationExpr &);
242 void MakeImplied(const std::optional<parser::SpecificationExpr> &);
243 void MakeDeferred(int);
244 Bound GetBound(const std::optional<parser::SpecificationExpr> &);
245 Bound GetBound(const parser::SpecificationExpr &);
246 };
247
AnalyzeArraySpec(SemanticsContext & context,const parser::ArraySpec & arraySpec)248 ArraySpec AnalyzeArraySpec(
249 SemanticsContext &context, const parser::ArraySpec &arraySpec) {
250 return ArraySpecAnalyzer{context}.Analyze(arraySpec);
251 }
AnalyzeArraySpec(SemanticsContext & context,const parser::ComponentArraySpec & arraySpec)252 ArraySpec AnalyzeArraySpec(
253 SemanticsContext &context, const parser::ComponentArraySpec &arraySpec) {
254 return ArraySpecAnalyzer{context}.Analyze(arraySpec);
255 }
AnalyzeDeferredShapeSpecList(SemanticsContext & context,const parser::DeferredShapeSpecList & deferredShapeSpecs)256 ArraySpec AnalyzeDeferredShapeSpecList(SemanticsContext &context,
257 const parser::DeferredShapeSpecList &deferredShapeSpecs) {
258 return ArraySpecAnalyzer{context}.AnalyzeDeferredShapeSpecList(
259 deferredShapeSpecs);
260 }
AnalyzeCoarraySpec(SemanticsContext & context,const parser::CoarraySpec & coarraySpec)261 ArraySpec AnalyzeCoarraySpec(
262 SemanticsContext &context, const parser::CoarraySpec &coarraySpec) {
263 return ArraySpecAnalyzer{context}.Analyze(coarraySpec);
264 }
265
Analyze(const parser::ComponentArraySpec & x)266 ArraySpec ArraySpecAnalyzer::Analyze(const parser::ComponentArraySpec &x) {
267 std::visit([this](const auto &y) { Analyze(y); }, x.u);
268 CHECK(!arraySpec_.empty());
269 return arraySpec_;
270 }
Analyze(const parser::ArraySpec & x)271 ArraySpec ArraySpecAnalyzer::Analyze(const parser::ArraySpec &x) {
272 std::visit(common::visitors{
273 [&](const parser::AssumedSizeSpec &y) {
274 Analyze(std::get<std::list<parser::ExplicitShapeSpec>>(y.t));
275 Analyze(std::get<parser::AssumedImpliedSpec>(y.t));
276 },
277 [&](const parser::ImpliedShapeSpec &y) { Analyze(y.v); },
278 [&](const auto &y) { Analyze(y); },
279 },
280 x.u);
281 CHECK(!arraySpec_.empty());
282 return arraySpec_;
283 }
AnalyzeDeferredShapeSpecList(const parser::DeferredShapeSpecList & x)284 ArraySpec ArraySpecAnalyzer::AnalyzeDeferredShapeSpecList(
285 const parser::DeferredShapeSpecList &x) {
286 Analyze(x);
287 CHECK(!arraySpec_.empty());
288 return arraySpec_;
289 }
Analyze(const parser::CoarraySpec & x)290 ArraySpec ArraySpecAnalyzer::Analyze(const parser::CoarraySpec &x) {
291 std::visit(
292 common::visitors{
293 [&](const parser::DeferredCoshapeSpecList &y) { MakeDeferred(y.v); },
294 [&](const parser::ExplicitCoshapeSpec &y) {
295 Analyze(std::get<std::list<parser::ExplicitShapeSpec>>(y.t));
296 MakeImplied(
297 std::get<std::optional<parser::SpecificationExpr>>(y.t));
298 },
299 },
300 x.u);
301 CHECK(!arraySpec_.empty());
302 return arraySpec_;
303 }
304
Analyze(const parser::AssumedShapeSpec & x)305 void ArraySpecAnalyzer::Analyze(const parser::AssumedShapeSpec &x) {
306 arraySpec_.push_back(ShapeSpec::MakeAssumed(GetBound(x.v)));
307 }
Analyze(const parser::ExplicitShapeSpec & x)308 void ArraySpecAnalyzer::Analyze(const parser::ExplicitShapeSpec &x) {
309 MakeExplicit(std::get<std::optional<parser::SpecificationExpr>>(x.t),
310 std::get<parser::SpecificationExpr>(x.t));
311 }
Analyze(const parser::AssumedImpliedSpec & x)312 void ArraySpecAnalyzer::Analyze(const parser::AssumedImpliedSpec &x) {
313 MakeImplied(x.v);
314 }
Analyze(const parser::DeferredShapeSpecList & x)315 void ArraySpecAnalyzer::Analyze(const parser::DeferredShapeSpecList &x) {
316 MakeDeferred(x.v);
317 }
Analyze(const parser::AssumedRankSpec &)318 void ArraySpecAnalyzer::Analyze(const parser::AssumedRankSpec &) {
319 arraySpec_.push_back(ShapeSpec::MakeAssumedRank());
320 }
321
MakeExplicit(const std::optional<parser::SpecificationExpr> & lb,const parser::SpecificationExpr & ub)322 void ArraySpecAnalyzer::MakeExplicit(
323 const std::optional<parser::SpecificationExpr> &lb,
324 const parser::SpecificationExpr &ub) {
325 arraySpec_.push_back(ShapeSpec::MakeExplicit(GetBound(lb), GetBound(ub)));
326 }
MakeImplied(const std::optional<parser::SpecificationExpr> & lb)327 void ArraySpecAnalyzer::MakeImplied(
328 const std::optional<parser::SpecificationExpr> &lb) {
329 arraySpec_.push_back(ShapeSpec::MakeImplied(GetBound(lb)));
330 }
MakeDeferred(int n)331 void ArraySpecAnalyzer::MakeDeferred(int n) {
332 for (int i = 0; i < n; ++i) {
333 arraySpec_.push_back(ShapeSpec::MakeDeferred());
334 }
335 }
336
GetBound(const std::optional<parser::SpecificationExpr> & x)337 Bound ArraySpecAnalyzer::GetBound(
338 const std::optional<parser::SpecificationExpr> &x) {
339 return x ? GetBound(*x) : Bound{1};
340 }
GetBound(const parser::SpecificationExpr & x)341 Bound ArraySpecAnalyzer::GetBound(const parser::SpecificationExpr &x) {
342 MaybeSubscriptIntExpr expr;
343 if (MaybeExpr maybeExpr{AnalyzeExpr(context_, x.v)}) {
344 if (auto *intExpr{evaluate::UnwrapExpr<SomeIntExpr>(*maybeExpr)}) {
345 expr = evaluate::Fold(context_.foldingContext(),
346 evaluate::ConvertToType<evaluate::SubscriptInteger>(
347 std::move(*intExpr)));
348 }
349 }
350 return Bound{std::move(expr)};
351 }
352
353 // If SAVE is set on src, set it on all members of dst
PropagateSaveAttr(const EquivalenceObject & src,EquivalenceSet & dst)354 static void PropagateSaveAttr(
355 const EquivalenceObject &src, EquivalenceSet &dst) {
356 if (src.symbol.attrs().test(Attr::SAVE)) {
357 for (auto &obj : dst) {
358 obj.symbol.attrs().set(Attr::SAVE);
359 }
360 }
361 }
PropagateSaveAttr(const EquivalenceSet & src,EquivalenceSet & dst)362 static void PropagateSaveAttr(const EquivalenceSet &src, EquivalenceSet &dst) {
363 if (!src.empty()) {
364 PropagateSaveAttr(src.front(), dst);
365 }
366 }
367
AddToSet(const parser::Designator & designator)368 void EquivalenceSets::AddToSet(const parser::Designator &designator) {
369 if (CheckDesignator(designator)) {
370 Symbol &symbol{*currObject_.symbol};
371 if (!currSet_.empty()) {
372 // check this symbol against first of set for compatibility
373 Symbol &first{currSet_.front().symbol};
374 CheckCanEquivalence(designator.source, first, symbol) &&
375 CheckCanEquivalence(designator.source, symbol, first);
376 }
377 auto subscripts{currObject_.subscripts};
378 if (subscripts.empty() && symbol.IsObjectArray()) {
379 // record a whole array as its first element
380 for (const ShapeSpec &spec : symbol.get<ObjectEntityDetails>().shape()) {
381 auto &lbound{spec.lbound().GetExplicit().value()};
382 subscripts.push_back(evaluate::ToInt64(lbound).value());
383 }
384 }
385 auto substringStart{currObject_.substringStart};
386 currSet_.emplace_back(
387 symbol, subscripts, substringStart, designator.source);
388 PropagateSaveAttr(currSet_.back(), currSet_);
389 }
390 currObject_ = {};
391 }
392
FinishSet(const parser::CharBlock & source)393 void EquivalenceSets::FinishSet(const parser::CharBlock &source) {
394 std::set<std::size_t> existing; // indices of sets intersecting this one
395 for (auto &obj : currSet_) {
396 auto it{objectToSet_.find(obj)};
397 if (it != objectToSet_.end()) {
398 existing.insert(it->second); // symbol already in this set
399 }
400 }
401 if (existing.empty()) {
402 sets_.push_back({}); // create a new equivalence set
403 MergeInto(source, currSet_, sets_.size() - 1);
404 } else {
405 auto it{existing.begin()};
406 std::size_t dstIndex{*it};
407 MergeInto(source, currSet_, dstIndex);
408 while (++it != existing.end()) {
409 MergeInto(source, sets_[*it], dstIndex);
410 }
411 }
412 currSet_.clear();
413 }
414
415 // Report an error if sym1 and sym2 cannot be in the same equivalence set.
CheckCanEquivalence(const parser::CharBlock & source,const Symbol & sym1,const Symbol & sym2)416 bool EquivalenceSets::CheckCanEquivalence(
417 const parser::CharBlock &source, const Symbol &sym1, const Symbol &sym2) {
418 std::optional<parser::MessageFixedText> msg;
419 const DeclTypeSpec *type1{sym1.GetType()};
420 const DeclTypeSpec *type2{sym2.GetType()};
421 bool isNum1{IsNumericSequenceType(type1)};
422 bool isNum2{IsNumericSequenceType(type2)};
423 bool isChar1{IsCharacterSequenceType(type1)};
424 bool isChar2{IsCharacterSequenceType(type2)};
425 if (sym1.attrs().test(Attr::PROTECTED) &&
426 !sym2.attrs().test(Attr::PROTECTED)) { // C8114
427 msg = "Equivalence set cannot contain '%s'"
428 " with PROTECTED attribute and '%s' without"_err_en_US;
429 } else if (isNum1) {
430 if (isChar2) {
431 if (context_.ShouldWarn(
432 LanguageFeature::EquivalenceNumericWithCharacter)) {
433 msg = "Equivalence set contains '%s' that is numeric sequence "
434 "type and '%s' that is character"_en_US;
435 }
436 } else if (!isNum2) { // C8110
437 msg = "Equivalence set cannot contain '%s'"
438 " that is numeric sequence type and '%s' that is not"_err_en_US;
439 }
440 } else if (isChar1) {
441 if (isNum2) {
442 if (context_.ShouldWarn(
443 LanguageFeature::EquivalenceNumericWithCharacter)) {
444 msg = "Equivalence set contains '%s' that is character sequence "
445 "type and '%s' that is numeric"_en_US;
446 }
447 } else if (!isChar2) { // C8111
448 msg = "Equivalence set cannot contain '%s'"
449 " that is character sequence type and '%s' that is not"_err_en_US;
450 }
451 } else if (!isNum2 && !isChar2 && *type1 != *type2) { // C8112, C8113
452 msg = "Equivalence set cannot contain '%s' and '%s' with different types"
453 " that are neither numeric nor character sequence types"_err_en_US;
454 }
455 if (msg) {
456 context_.Say(source, std::move(*msg), sym1.name(), sym2.name());
457 return false;
458 }
459 return true;
460 }
461
462 // Move objects from src to sets_[dstIndex]
MergeInto(const parser::CharBlock & source,EquivalenceSet & src,std::size_t dstIndex)463 void EquivalenceSets::MergeInto(const parser::CharBlock &source,
464 EquivalenceSet &src, std::size_t dstIndex) {
465 EquivalenceSet &dst{sets_[dstIndex]};
466 PropagateSaveAttr(dst, src);
467 for (const auto &obj : src) {
468 dst.push_back(obj);
469 objectToSet_[obj] = dstIndex;
470 }
471 PropagateSaveAttr(src, dst);
472 src.clear();
473 }
474
475 // If set has an object with this symbol, return it.
Find(const EquivalenceSet & set,const Symbol & symbol)476 const EquivalenceObject *EquivalenceSets::Find(
477 const EquivalenceSet &set, const Symbol &symbol) {
478 for (const auto &obj : set) {
479 if (obj.symbol == symbol) {
480 return &obj;
481 }
482 }
483 return nullptr;
484 }
485
CheckDesignator(const parser::Designator & designator)486 bool EquivalenceSets::CheckDesignator(const parser::Designator &designator) {
487 return std::visit(
488 common::visitors{
489 [&](const parser::DataRef &x) {
490 return CheckDataRef(designator.source, x);
491 },
492 [&](const parser::Substring &x) {
493 const auto &dataRef{std::get<parser::DataRef>(x.t)};
494 const auto &range{std::get<parser::SubstringRange>(x.t)};
495 bool ok{CheckDataRef(designator.source, dataRef)};
496 if (const auto &lb{std::get<0>(range.t)}) {
497 ok &= CheckSubstringBound(lb->thing.thing.value(), true);
498 } else {
499 currObject_.substringStart = 1;
500 }
501 if (const auto &ub{std::get<1>(range.t)}) {
502 ok &= CheckSubstringBound(ub->thing.thing.value(), false);
503 }
504 return ok;
505 },
506 },
507 designator.u);
508 }
509
CheckDataRef(const parser::CharBlock & source,const parser::DataRef & x)510 bool EquivalenceSets::CheckDataRef(
511 const parser::CharBlock &source, const parser::DataRef &x) {
512 return std::visit(
513 common::visitors{
514 [&](const parser::Name &name) { return CheckObject(name); },
515 [&](const common::Indirection<parser::StructureComponent> &) {
516 context_.Say(source, // C8107
517 "Derived type component '%s' is not allowed in an equivalence set"_err_en_US,
518 source);
519 return false;
520 },
521 [&](const common::Indirection<parser::ArrayElement> &elem) {
522 bool ok{CheckDataRef(source, elem.value().base)};
523 for (const auto &subscript : elem.value().subscripts) {
524 ok &= std::visit(
525 common::visitors{
526 [&](const parser::SubscriptTriplet &) {
527 context_.Say(source, // C924, R872
528 "Array section '%s' is not allowed in an equivalence set"_err_en_US,
529 source);
530 return false;
531 },
532 [&](const parser::IntExpr &y) {
533 return CheckArrayBound(y.thing.value());
534 },
535 },
536 subscript.u);
537 }
538 return ok;
539 },
540 [&](const common::Indirection<parser::CoindexedNamedObject> &) {
541 context_.Say(source, // C924 (R872)
542 "Coindexed object '%s' is not allowed in an equivalence set"_err_en_US,
543 source);
544 return false;
545 },
546 },
547 x.u);
548 }
549
InCommonWithBind(const Symbol & symbol)550 static bool InCommonWithBind(const Symbol &symbol) {
551 if (const auto *details{symbol.detailsIf<ObjectEntityDetails>()}) {
552 const Symbol *commonBlock{details->commonBlock()};
553 return commonBlock && commonBlock->attrs().test(Attr::BIND_C);
554 } else {
555 return false;
556 }
557 }
558
559 // If symbol can't be in equivalence set report error and return false;
CheckObject(const parser::Name & name)560 bool EquivalenceSets::CheckObject(const parser::Name &name) {
561 if (!name.symbol) {
562 return false; // an error has already occurred
563 }
564 currObject_.symbol = name.symbol;
565 parser::MessageFixedText msg{"", 0};
566 const Symbol &symbol{*name.symbol};
567 if (symbol.owner().IsDerivedType()) { // C8107
568 msg = "Derived type component '%s'"
569 " is not allowed in an equivalence set"_err_en_US;
570 } else if (IsDummy(symbol)) { // C8106
571 msg = "Dummy argument '%s' is not allowed in an equivalence set"_err_en_US;
572 } else if (symbol.IsFuncResult()) { // C8106
573 msg = "Function result '%s' is not allow in an equivalence set"_err_en_US;
574 } else if (IsPointer(symbol)) { // C8106
575 msg = "Pointer '%s' is not allowed in an equivalence set"_err_en_US;
576 } else if (IsAllocatable(symbol)) { // C8106
577 msg = "Allocatable variable '%s'"
578 " is not allowed in an equivalence set"_err_en_US;
579 } else if (symbol.Corank() > 0) { // C8106
580 msg = "Coarray '%s' is not allowed in an equivalence set"_err_en_US;
581 } else if (symbol.has<UseDetails>()) { // C8115
582 msg = "Use-associated variable '%s'"
583 " is not allowed in an equivalence set"_err_en_US;
584 } else if (symbol.attrs().test(Attr::BIND_C)) { // C8106
585 msg = "Variable '%s' with BIND attribute"
586 " is not allowed in an equivalence set"_err_en_US;
587 } else if (symbol.attrs().test(Attr::TARGET)) { // C8108
588 msg = "Variable '%s' with TARGET attribute"
589 " is not allowed in an equivalence set"_err_en_US;
590 } else if (IsNamedConstant(symbol)) { // C8106
591 msg = "Named constant '%s' is not allowed in an equivalence set"_err_en_US;
592 } else if (InCommonWithBind(symbol)) { // C8106
593 msg = "Variable '%s' in common block with BIND attribute"
594 " is not allowed in an equivalence set"_err_en_US;
595 } else if (const auto *type{symbol.GetType()}) {
596 if (const auto *derived{type->AsDerived()}) {
597 if (const auto *comp{FindUltimateComponent(
598 *derived, IsAllocatableOrPointer)}) { // C8106
599 msg = IsPointer(*comp)
600 ? "Derived type object '%s' with pointer ultimate component"
601 " is not allowed in an equivalence set"_err_en_US
602 : "Derived type object '%s' with allocatable ultimate component"
603 " is not allowed in an equivalence set"_err_en_US;
604 } else if (!derived->typeSymbol().get<DerivedTypeDetails>().sequence()) {
605 msg = "Nonsequence derived type object '%s'"
606 " is not allowed in an equivalence set"_err_en_US;
607 }
608 } else if (IsAutomaticObject(symbol)) {
609 msg = "Automatic object '%s'"
610 " is not allowed in an equivalence set"_err_en_US;
611 }
612 }
613 if (!msg.text().empty()) {
614 context_.Say(name.source, std::move(msg), name.source);
615 return false;
616 }
617 return true;
618 }
619
CheckArrayBound(const parser::Expr & bound)620 bool EquivalenceSets::CheckArrayBound(const parser::Expr &bound) {
621 MaybeExpr expr{
622 evaluate::Fold(context_.foldingContext(), AnalyzeExpr(context_, bound))};
623 if (!expr) {
624 return false;
625 }
626 if (expr->Rank() > 0) {
627 context_.Say(bound.source, // C924, R872
628 "Array with vector subscript '%s' is not allowed in an equivalence set"_err_en_US,
629 bound.source);
630 return false;
631 }
632 auto subscript{evaluate::ToInt64(*expr)};
633 if (!subscript) {
634 context_.Say(bound.source, // C8109
635 "Array with nonconstant subscript '%s' is not allowed in an equivalence set"_err_en_US,
636 bound.source);
637 return false;
638 }
639 currObject_.subscripts.push_back(*subscript);
640 return true;
641 }
642
CheckSubstringBound(const parser::Expr & bound,bool isStart)643 bool EquivalenceSets::CheckSubstringBound(
644 const parser::Expr &bound, bool isStart) {
645 MaybeExpr expr{
646 evaluate::Fold(context_.foldingContext(), AnalyzeExpr(context_, bound))};
647 if (!expr) {
648 return false;
649 }
650 auto subscript{evaluate::ToInt64(*expr)};
651 if (!subscript) {
652 context_.Say(bound.source, // C8109
653 "Substring with nonconstant bound '%s' is not allowed in an equivalence set"_err_en_US,
654 bound.source);
655 return false;
656 }
657 if (!isStart) {
658 auto start{currObject_.substringStart};
659 if (*subscript < (start ? *start : 1)) {
660 context_.Say(bound.source, // C8116
661 "Substring with zero length is not allowed in an equivalence set"_err_en_US);
662 return false;
663 }
664 } else if (*subscript != 1) {
665 currObject_.substringStart = *subscript;
666 }
667 return true;
668 }
669
IsCharacterSequenceType(const DeclTypeSpec * type)670 bool EquivalenceSets::IsCharacterSequenceType(const DeclTypeSpec *type) {
671 return IsSequenceType(type, [&](const IntrinsicTypeSpec &type) {
672 auto kind{evaluate::ToInt64(type.kind())};
673 return type.category() == TypeCategory::Character && kind &&
674 kind.value() == context_.GetDefaultKind(TypeCategory::Character);
675 });
676 }
677
678 // Numeric or logical type of default kind or DOUBLE PRECISION or DOUBLE COMPLEX
IsDefaultKindNumericType(const IntrinsicTypeSpec & type)679 bool EquivalenceSets::IsDefaultKindNumericType(const IntrinsicTypeSpec &type) {
680 if (auto kind{evaluate::ToInt64(type.kind())}) {
681 auto category{type.category()};
682 auto defaultKind{context_.GetDefaultKind(category)};
683 switch (category) {
684 case TypeCategory::Integer:
685 case TypeCategory::Logical:
686 return *kind == defaultKind;
687 case TypeCategory::Real:
688 case TypeCategory::Complex:
689 return *kind == defaultKind || *kind == context_.doublePrecisionKind();
690 default:
691 return false;
692 }
693 }
694 return false;
695 }
696
IsNumericSequenceType(const DeclTypeSpec * type)697 bool EquivalenceSets::IsNumericSequenceType(const DeclTypeSpec *type) {
698 return IsSequenceType(type, [&](const IntrinsicTypeSpec &type) {
699 return IsDefaultKindNumericType(type);
700 });
701 }
702
703 // Is type an intrinsic type that satisfies predicate or a sequence type
704 // whose components do.
IsSequenceType(const DeclTypeSpec * type,std::function<bool (const IntrinsicTypeSpec &)> predicate)705 bool EquivalenceSets::IsSequenceType(const DeclTypeSpec *type,
706 std::function<bool(const IntrinsicTypeSpec &)> predicate) {
707 if (!type) {
708 return false;
709 } else if (const IntrinsicTypeSpec * intrinsic{type->AsIntrinsic()}) {
710 return predicate(*intrinsic);
711 } else if (const DerivedTypeSpec * derived{type->AsDerived()}) {
712 for (const auto &pair : *derived->typeSymbol().scope()) {
713 const Symbol &component{*pair.second};
714 if (IsAllocatableOrPointer(component) ||
715 !IsSequenceType(component.GetType(), predicate)) {
716 return false;
717 }
718 }
719 return true;
720 } else {
721 return false;
722 }
723 }
724
725 } // namespace Fortran::semantics
726