2020-02-25 23:11:52 +08:00
|
|
|
//===-- lib/Semantics/tools.cpp -------------------------------------------===//
|
2019-03-05 02:13:12 +08:00
|
|
|
//
|
2019-12-21 04:52:07 +08:00
|
|
|
// 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
|
2019-03-05 02:13:12 +08:00
|
|
|
//
|
2020-01-11 04:12:03 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2019-03-05 02:13:12 +08:00
|
|
|
|
2020-02-25 23:11:52 +08:00
|
|
|
#include "flang/Parser/tools.h"
|
|
|
|
#include "flang/Common/Fortran.h"
|
|
|
|
#include "flang/Common/indirection.h"
|
|
|
|
#include "flang/Parser/dump-parse-tree.h"
|
|
|
|
#include "flang/Parser/message.h"
|
|
|
|
#include "flang/Parser/parse-tree.h"
|
|
|
|
#include "flang/Semantics/scope.h"
|
|
|
|
#include "flang/Semantics/semantics.h"
|
|
|
|
#include "flang/Semantics/symbol.h"
|
|
|
|
#include "flang/Semantics/tools.h"
|
|
|
|
#include "flang/Semantics/type.h"
|
2020-02-28 23:11:03 +08:00
|
|
|
#include "llvm/Support/raw_ostream.h"
|
2019-03-05 02:13:12 +08:00
|
|
|
#include <algorithm>
|
|
|
|
#include <set>
|
|
|
|
#include <variant>
|
|
|
|
|
|
|
|
namespace Fortran::semantics {
|
|
|
|
|
2020-03-03 08:43:01 +08:00
|
|
|
// Find this or containing scope that matches predicate
|
|
|
|
static const Scope *FindScopeContaining(
|
|
|
|
const Scope &start, std::function<bool(const Scope &)> predicate) {
|
|
|
|
for (const Scope *scope{&start};; scope = &scope->parent()) {
|
|
|
|
if (predicate(*scope)) {
|
|
|
|
return scope;
|
|
|
|
}
|
|
|
|
if (scope->IsGlobal()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const Scope *FindModuleContaining(const Scope &start) {
|
|
|
|
return FindScopeContaining(
|
|
|
|
start, [](const Scope &scope) { return scope.IsModule(); });
|
|
|
|
}
|
|
|
|
|
2019-03-05 02:13:12 +08:00
|
|
|
const Symbol *FindCommonBlockContaining(const Symbol &object) {
|
2020-01-15 07:08:37 +08:00
|
|
|
if (const auto *details{object.detailsIf<ObjectEntityDetails>()}) {
|
|
|
|
return details->commonBlock();
|
|
|
|
} else {
|
|
|
|
return nullptr;
|
2019-03-05 02:13:12 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const Scope *FindProgramUnitContaining(const Scope &start) {
|
2020-03-03 08:43:01 +08:00
|
|
|
return FindScopeContaining(start, [](const Scope &scope) {
|
|
|
|
switch (scope.kind()) {
|
2019-03-05 02:13:12 +08:00
|
|
|
case Scope::Kind::Module:
|
|
|
|
case Scope::Kind::MainProgram:
|
2020-01-10 09:12:46 +08:00
|
|
|
case Scope::Kind::Subprogram:
|
2020-03-28 05:17:25 +08:00
|
|
|
case Scope::Kind::BlockData:
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
return false;
|
2019-03-05 02:13:12 +08:00
|
|
|
}
|
2020-03-03 08:43:01 +08:00
|
|
|
});
|
2019-03-05 02:13:12 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
const Scope *FindProgramUnitContaining(const Symbol &symbol) {
|
|
|
|
return FindProgramUnitContaining(symbol.owner());
|
|
|
|
}
|
|
|
|
|
2019-11-20 11:10:02 +08:00
|
|
|
const Scope *FindPureProcedureContaining(const Scope &start) {
|
|
|
|
// N.B. We only need to examine the innermost containing program unit
|
2019-12-24 09:12:53 +08:00
|
|
|
// because an internal subprogram of a pure subprogram must also
|
|
|
|
// be pure (C1592).
|
2019-11-22 05:35:20 +08:00
|
|
|
if (const Scope * scope{FindProgramUnitContaining(start)}) {
|
2019-07-20 06:17:14 +08:00
|
|
|
if (IsPureProcedure(*scope)) {
|
2019-03-05 02:13:12 +08:00
|
|
|
return scope;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2019-11-23 08:46:11 +08:00
|
|
|
Tristate IsDefinedAssignment(
|
|
|
|
const std::optional<evaluate::DynamicType> &lhsType, int lhsRank,
|
|
|
|
const std::optional<evaluate::DynamicType> &rhsType, int rhsRank) {
|
|
|
|
if (!lhsType || !rhsType) {
|
2020-03-28 05:17:25 +08:00
|
|
|
return Tristate::No; // error or rhs is untyped
|
2019-11-23 08:46:11 +08:00
|
|
|
}
|
|
|
|
TypeCategory lhsCat{lhsType->category()};
|
|
|
|
TypeCategory rhsCat{rhsType->category()};
|
|
|
|
if (rhsRank > 0 && lhsRank != rhsRank) {
|
|
|
|
return Tristate::Yes;
|
|
|
|
} else if (lhsCat != TypeCategory::Derived) {
|
|
|
|
return ToTristate(lhsCat != rhsCat &&
|
|
|
|
(!IsNumericTypeCategory(lhsCat) || !IsNumericTypeCategory(rhsCat)));
|
|
|
|
} else {
|
2019-12-17 06:28:23 +08:00
|
|
|
const auto *lhsDerived{evaluate::GetDerivedTypeSpec(lhsType)};
|
|
|
|
const auto *rhsDerived{evaluate::GetDerivedTypeSpec(rhsType)};
|
|
|
|
if (lhsDerived && rhsDerived && *lhsDerived == *rhsDerived) {
|
2020-03-28 05:17:25 +08:00
|
|
|
return Tristate::Maybe; // TYPE(t) = TYPE(t) can be defined or
|
|
|
|
// intrinsic
|
2019-12-17 06:28:23 +08:00
|
|
|
} else {
|
|
|
|
return Tristate::Yes;
|
|
|
|
}
|
2019-11-23 08:46:11 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-03 00:55:44 +08:00
|
|
|
bool IsIntrinsicRelational(common::RelationalOperator opr,
|
|
|
|
const evaluate::DynamicType &type0, int rank0,
|
|
|
|
const evaluate::DynamicType &type1, int rank1) {
|
|
|
|
if (!evaluate::AreConformable(rank0, rank1)) {
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
auto cat0{type0.category()};
|
|
|
|
auto cat1{type1.category()};
|
|
|
|
if (IsNumericTypeCategory(cat0) && IsNumericTypeCategory(cat1)) {
|
|
|
|
// numeric types: EQ/NE always ok, others ok for non-complex
|
|
|
|
return opr == common::RelationalOperator::EQ ||
|
|
|
|
opr == common::RelationalOperator::NE ||
|
|
|
|
(cat0 != TypeCategory::Complex && cat1 != TypeCategory::Complex);
|
|
|
|
} else {
|
|
|
|
// not both numeric: only Character is ok
|
|
|
|
return cat0 == TypeCategory::Character && cat1 == TypeCategory::Character;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsIntrinsicNumeric(const evaluate::DynamicType &type0) {
|
|
|
|
return IsNumericTypeCategory(type0.category());
|
|
|
|
}
|
|
|
|
bool IsIntrinsicNumeric(const evaluate::DynamicType &type0, int rank0,
|
|
|
|
const evaluate::DynamicType &type1, int rank1) {
|
|
|
|
return evaluate::AreConformable(rank0, rank1) &&
|
|
|
|
IsNumericTypeCategory(type0.category()) &&
|
|
|
|
IsNumericTypeCategory(type1.category());
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsIntrinsicLogical(const evaluate::DynamicType &type0) {
|
|
|
|
return type0.category() == TypeCategory::Logical;
|
|
|
|
}
|
|
|
|
bool IsIntrinsicLogical(const evaluate::DynamicType &type0, int rank0,
|
|
|
|
const evaluate::DynamicType &type1, int rank1) {
|
|
|
|
return evaluate::AreConformable(rank0, rank1) &&
|
|
|
|
type0.category() == TypeCategory::Logical &&
|
|
|
|
type1.category() == TypeCategory::Logical;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsIntrinsicConcat(const evaluate::DynamicType &type0, int rank0,
|
|
|
|
const evaluate::DynamicType &type1, int rank1) {
|
|
|
|
return evaluate::AreConformable(rank0, rank1) &&
|
|
|
|
type0.category() == TypeCategory::Character &&
|
|
|
|
type1.category() == TypeCategory::Character &&
|
|
|
|
type0.kind() == type1.kind();
|
|
|
|
}
|
|
|
|
|
2019-10-23 00:31:33 +08:00
|
|
|
bool IsGenericDefinedOp(const Symbol &symbol) {
|
2020-01-03 01:55:03 +08:00
|
|
|
const Symbol &ultimate{symbol.GetUltimate()};
|
|
|
|
if (const auto *generic{ultimate.detailsIf<GenericDetails>()}) {
|
|
|
|
return generic->kind().IsDefinedOperator();
|
|
|
|
} else if (const auto *misc{ultimate.detailsIf<MiscDetails>()}) {
|
|
|
|
return misc->kind() == MiscDetails::Kind::TypeBoundDefinedOp;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
2019-10-23 00:31:33 +08:00
|
|
|
}
|
|
|
|
|
2019-03-05 02:13:12 +08:00
|
|
|
bool IsCommonBlockContaining(const Symbol &block, const Symbol &object) {
|
|
|
|
const auto &objects{block.get<CommonBlockDetails>().objects()};
|
2019-11-05 03:08:13 +08:00
|
|
|
auto found{std::find(objects.begin(), objects.end(), object)};
|
2019-03-05 02:13:12 +08:00
|
|
|
return found != objects.end();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsUseAssociated(const Symbol &symbol, const Scope &scope) {
|
|
|
|
const Scope *owner{FindProgramUnitContaining(symbol.GetUltimate().owner())};
|
2019-11-10 01:29:31 +08:00
|
|
|
return owner && owner->kind() == Scope::Kind::Module &&
|
2019-03-05 02:13:12 +08:00
|
|
|
owner != FindProgramUnitContaining(scope);
|
|
|
|
}
|
|
|
|
|
2019-03-26 15:33:03 +08:00
|
|
|
bool DoesScopeContain(
|
|
|
|
const Scope *maybeAncestor, const Scope &maybeDescendent) {
|
2020-03-03 08:43:01 +08:00
|
|
|
return maybeAncestor && !maybeDescendent.IsGlobal() &&
|
|
|
|
FindScopeContaining(maybeDescendent.parent(),
|
|
|
|
[&](const Scope &scope) { return &scope == maybeAncestor; });
|
2019-03-05 02:13:12 +08:00
|
|
|
}
|
|
|
|
|
2019-03-06 05:11:57 +08:00
|
|
|
bool DoesScopeContain(const Scope *maybeAncestor, const Symbol &symbol) {
|
|
|
|
return DoesScopeContain(maybeAncestor, symbol.owner());
|
|
|
|
}
|
|
|
|
|
2019-03-05 02:13:12 +08:00
|
|
|
bool IsHostAssociated(const Symbol &symbol, const Scope &scope) {
|
2019-12-24 09:12:53 +08:00
|
|
|
const Scope *subprogram{FindProgramUnitContaining(scope)};
|
|
|
|
return subprogram &&
|
|
|
|
DoesScopeContain(FindProgramUnitContaining(symbol), *subprogram);
|
2019-03-05 02:13:12 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
bool IsDummy(const Symbol &symbol) {
|
|
|
|
if (const auto *details{symbol.detailsIf<ObjectEntityDetails>()}) {
|
|
|
|
return details->isDummy();
|
|
|
|
} else if (const auto *details{symbol.detailsIf<ProcEntityDetails>()}) {
|
|
|
|
return details->isDummy();
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-27 12:19:48 +08:00
|
|
|
bool IsStmtFunction(const Symbol &symbol) {
|
|
|
|
const auto *subprogram{symbol.detailsIf<SubprogramDetails>()};
|
2020-02-28 00:49:40 +08:00
|
|
|
return subprogram && subprogram->stmtFunction();
|
2020-02-27 12:19:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
bool IsInStmtFunction(const Symbol &symbol) {
|
|
|
|
if (const Symbol * function{symbol.owner().symbol()}) {
|
|
|
|
return IsStmtFunction(*function);
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsStmtFunctionDummy(const Symbol &symbol) {
|
|
|
|
return IsDummy(symbol) && IsInStmtFunction(symbol);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsStmtFunctionResult(const Symbol &symbol) {
|
|
|
|
return IsFunctionResult(symbol) && IsInStmtFunction(symbol);
|
|
|
|
}
|
|
|
|
|
2019-03-05 02:13:12 +08:00
|
|
|
bool IsPointerDummy(const Symbol &symbol) {
|
2019-04-08 02:29:48 +08:00
|
|
|
return IsPointer(symbol) && IsDummy(symbol);
|
|
|
|
}
|
|
|
|
|
|
|
|
// variable-name
|
|
|
|
bool IsVariableName(const Symbol &symbol) {
|
2019-07-20 06:17:14 +08:00
|
|
|
if (const Symbol * root{GetAssociationRoot(symbol)}) {
|
2019-08-26 17:28:08 +08:00
|
|
|
return root->has<ObjectEntityDetails>() && !IsNamedConstant(*root);
|
2019-07-20 06:17:14 +08:00
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
2019-04-08 02:29:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// proc-name
|
|
|
|
bool IsProcName(const Symbol &symbol) {
|
2019-06-11 04:30:29 +08:00
|
|
|
return symbol.GetUltimate().has<ProcEntityDetails>();
|
2019-03-05 02:13:12 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
bool IsFunction(const Symbol &symbol) {
|
2019-04-17 22:42:16 +08:00
|
|
|
return std::visit(
|
|
|
|
common::visitors{
|
|
|
|
[](const SubprogramDetails &x) { return x.isFunction(); },
|
2019-08-16 04:50:27 +08:00
|
|
|
[&](const SubprogramNameDetails &) {
|
2019-04-17 22:42:16 +08:00
|
|
|
return symbol.test(Symbol::Flag::Function);
|
|
|
|
},
|
|
|
|
[](const ProcEntityDetails &x) {
|
|
|
|
const auto &ifc{x.interface()};
|
|
|
|
return ifc.type() || (ifc.symbol() && IsFunction(*ifc.symbol()));
|
|
|
|
},
|
2019-07-17 05:37:56 +08:00
|
|
|
[](const ProcBindingDetails &x) { return IsFunction(x.symbol()); },
|
2019-04-17 22:42:16 +08:00
|
|
|
[](const UseDetails &x) { return IsFunction(x.symbol()); },
|
|
|
|
[](const auto &) { return false; },
|
|
|
|
},
|
|
|
|
symbol.details());
|
2019-03-05 02:13:12 +08:00
|
|
|
}
|
|
|
|
|
2019-07-20 06:17:14 +08:00
|
|
|
bool IsPureProcedure(const Symbol &symbol) {
|
2019-10-24 06:56:22 +08:00
|
|
|
if (const auto *procDetails{symbol.detailsIf<ProcEntityDetails>()}) {
|
2019-10-23 07:53:29 +08:00
|
|
|
if (const Symbol * procInterface{procDetails->interface().symbol()}) {
|
2019-12-24 09:12:53 +08:00
|
|
|
// procedure component with a pure interface
|
2019-10-24 06:56:22 +08:00
|
|
|
return IsPureProcedure(*procInterface);
|
|
|
|
}
|
2019-12-04 00:43:05 +08:00
|
|
|
} else if (const auto *details{symbol.detailsIf<ProcBindingDetails>()}) {
|
|
|
|
return IsPureProcedure(details->symbol());
|
|
|
|
} else if (!IsProcedure(symbol)) {
|
|
|
|
return false;
|
2019-10-24 06:56:22 +08:00
|
|
|
}
|
2019-12-24 09:12:53 +08:00
|
|
|
return symbol.attrs().test(Attr::PURE) ||
|
|
|
|
(symbol.attrs().test(Attr::ELEMENTAL) &&
|
|
|
|
!symbol.attrs().test(Attr::IMPURE));
|
2019-03-05 02:13:12 +08:00
|
|
|
}
|
|
|
|
|
2019-07-20 06:17:14 +08:00
|
|
|
bool IsPureProcedure(const Scope &scope) {
|
2019-03-05 02:13:12 +08:00
|
|
|
if (const Symbol * symbol{scope.GetSymbol()}) {
|
2019-07-20 06:17:14 +08:00
|
|
|
return IsPureProcedure(*symbol);
|
2019-03-05 02:13:12 +08:00
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-30 03:46:25 +08:00
|
|
|
bool IsBindCProcedure(const Symbol &symbol) {
|
|
|
|
if (const auto *procDetails{symbol.detailsIf<ProcEntityDetails>()}) {
|
|
|
|
if (const Symbol * procInterface{procDetails->interface().symbol()}) {
|
|
|
|
// procedure component with a BIND(C) interface
|
|
|
|
return IsBindCProcedure(*procInterface);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return symbol.attrs().test(Attr::BIND_C) && IsProcedure(symbol);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsBindCProcedure(const Scope &scope) {
|
|
|
|
if (const Symbol * symbol{scope.GetSymbol()}) {
|
|
|
|
return IsBindCProcedure(*symbol);
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-17 22:42:16 +08:00
|
|
|
bool IsProcedure(const Symbol &symbol) {
|
|
|
|
return std::visit(
|
|
|
|
common::visitors{
|
|
|
|
[](const SubprogramDetails &) { return true; },
|
|
|
|
[](const SubprogramNameDetails &) { return true; },
|
2019-04-26 04:18:33 +08:00
|
|
|
[](const ProcEntityDetails &) { return true; },
|
|
|
|
[](const GenericDetails &) { return true; },
|
2019-05-22 07:58:46 +08:00
|
|
|
[](const ProcBindingDetails &) { return true; },
|
2019-04-17 22:42:16 +08:00
|
|
|
[](const UseDetails &x) { return IsProcedure(x.symbol()); },
|
2019-11-13 07:43:09 +08:00
|
|
|
// TODO: FinalProcDetails?
|
2019-04-17 22:42:16 +08:00
|
|
|
[](const auto &) { return false; },
|
|
|
|
},
|
|
|
|
symbol.details());
|
|
|
|
}
|
|
|
|
|
2019-04-19 06:07:40 +08:00
|
|
|
bool IsProcedurePointer(const Symbol &symbol) {
|
|
|
|
return symbol.has<ProcEntityDetails>() && IsPointer(symbol);
|
|
|
|
}
|
|
|
|
|
2019-03-05 08:28:35 +08:00
|
|
|
static const Symbol *FindPointerComponent(
|
2019-03-05 02:13:12 +08:00
|
|
|
const Scope &scope, std::set<const Scope *> &visited) {
|
2019-07-12 00:27:25 +08:00
|
|
|
if (!scope.IsDerivedType()) {
|
2019-03-05 08:28:35 +08:00
|
|
|
return nullptr;
|
2019-03-05 02:13:12 +08:00
|
|
|
}
|
|
|
|
if (!visited.insert(&scope).second) {
|
2019-03-05 08:28:35 +08:00
|
|
|
return nullptr;
|
2019-03-05 02:13:12 +08:00
|
|
|
}
|
2019-03-05 08:28:35 +08:00
|
|
|
// If there's a top-level pointer component, return it for clearer error
|
|
|
|
// messaging.
|
2019-03-05 02:13:12 +08:00
|
|
|
for (const auto &pair : scope) {
|
|
|
|
const Symbol &symbol{*pair.second};
|
2019-04-19 06:07:40 +08:00
|
|
|
if (IsPointer(symbol)) {
|
2019-03-05 08:28:35 +08:00
|
|
|
return &symbol;
|
2019-03-05 02:13:12 +08:00
|
|
|
}
|
2019-03-05 08:28:35 +08:00
|
|
|
}
|
|
|
|
for (const auto &pair : scope) {
|
|
|
|
const Symbol &symbol{*pair.second};
|
2019-03-05 02:13:12 +08:00
|
|
|
if (const auto *details{symbol.detailsIf<ObjectEntityDetails>()}) {
|
|
|
|
if (const DeclTypeSpec * type{details->type()}) {
|
|
|
|
if (const DerivedTypeSpec * derived{type->AsDerived()}) {
|
|
|
|
if (const Scope * nested{derived->scope()}) {
|
2019-03-05 08:28:35 +08:00
|
|
|
if (const Symbol *
|
|
|
|
pointer{FindPointerComponent(*nested, visited)}) {
|
|
|
|
return pointer;
|
2019-03-05 02:13:12 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-03-05 08:28:35 +08:00
|
|
|
return nullptr;
|
2019-03-05 02:13:12 +08:00
|
|
|
}
|
|
|
|
|
2019-03-05 08:28:35 +08:00
|
|
|
const Symbol *FindPointerComponent(const Scope &scope) {
|
2019-03-05 02:13:12 +08:00
|
|
|
std::set<const Scope *> visited;
|
2019-03-05 08:28:35 +08:00
|
|
|
return FindPointerComponent(scope, visited);
|
2019-03-05 02:13:12 +08:00
|
|
|
}
|
|
|
|
|
2019-03-05 08:28:35 +08:00
|
|
|
const Symbol *FindPointerComponent(const DerivedTypeSpec &derived) {
|
2019-03-05 02:13:12 +08:00
|
|
|
if (const Scope * scope{derived.scope()}) {
|
2019-03-05 08:28:35 +08:00
|
|
|
return FindPointerComponent(*scope);
|
2019-03-05 02:13:12 +08:00
|
|
|
} else {
|
2019-03-05 08:28:35 +08:00
|
|
|
return nullptr;
|
2019-03-05 02:13:12 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-05 08:28:35 +08:00
|
|
|
const Symbol *FindPointerComponent(const DeclTypeSpec &type) {
|
2019-03-05 02:13:12 +08:00
|
|
|
if (const DerivedTypeSpec * derived{type.AsDerived()}) {
|
2019-03-05 08:28:35 +08:00
|
|
|
return FindPointerComponent(*derived);
|
2019-03-05 02:13:12 +08:00
|
|
|
} else {
|
2019-03-05 08:28:35 +08:00
|
|
|
return nullptr;
|
2019-03-05 02:13:12 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-05 08:28:35 +08:00
|
|
|
const Symbol *FindPointerComponent(const DeclTypeSpec *type) {
|
|
|
|
return type ? FindPointerComponent(*type) : nullptr;
|
2019-03-05 02:13:12 +08:00
|
|
|
}
|
|
|
|
|
2019-03-05 08:28:35 +08:00
|
|
|
const Symbol *FindPointerComponent(const Symbol &symbol) {
|
2019-04-19 06:07:40 +08:00
|
|
|
return IsPointer(symbol) ? &symbol : FindPointerComponent(symbol.GetType());
|
2019-03-05 02:13:12 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// C1594 specifies several ways by which an object might be globally visible.
|
2019-03-05 08:28:35 +08:00
|
|
|
const Symbol *FindExternallyVisibleObject(
|
|
|
|
const Symbol &object, const Scope &scope) {
|
|
|
|
// TODO: Storage association with any object for which this predicate holds,
|
|
|
|
// once EQUIVALENCE is supported.
|
|
|
|
if (IsUseAssociated(object, scope) || IsHostAssociated(object, scope) ||
|
2019-07-20 06:17:14 +08:00
|
|
|
(IsPureProcedure(scope) && IsPointerDummy(object)) ||
|
2019-07-24 07:34:55 +08:00
|
|
|
(IsIntentIn(object) && IsDummy(object))) {
|
2019-03-05 08:28:35 +08:00
|
|
|
return &object;
|
|
|
|
} else if (const Symbol * block{FindCommonBlockContaining(object)}) {
|
|
|
|
return block;
|
|
|
|
} else {
|
|
|
|
return nullptr;
|
|
|
|
}
|
2019-03-05 02:13:12 +08:00
|
|
|
}
|
2019-03-26 15:33:03 +08:00
|
|
|
|
2019-04-19 23:22:28 +08:00
|
|
|
bool ExprHasTypeCategory(
|
|
|
|
const SomeExpr &expr, const common::TypeCategory &type) {
|
|
|
|
auto dynamicType{expr.GetType()};
|
2019-11-10 01:29:31 +08:00
|
|
|
return dynamicType && dynamicType->category() == type;
|
2019-03-26 15:33:03 +08:00
|
|
|
}
|
2019-03-19 00:19:41 +08:00
|
|
|
|
2019-04-12 04:25:45 +08:00
|
|
|
bool ExprTypeKindIsDefault(
|
2019-04-19 23:22:28 +08:00
|
|
|
const SomeExpr &expr, const SemanticsContext &context) {
|
|
|
|
auto dynamicType{expr.GetType()};
|
2019-11-10 01:29:31 +08:00
|
|
|
return dynamicType &&
|
2019-05-14 00:33:18 +08:00
|
|
|
dynamicType->category() != common::TypeCategory::Derived &&
|
2019-06-12 09:26:48 +08:00
|
|
|
dynamicType->kind() == context.GetDefaultKind(dynamicType->category());
|
2019-04-12 04:25:45 +08:00
|
|
|
}
|
2019-04-19 06:07:40 +08:00
|
|
|
|
2020-01-15 09:39:29 +08:00
|
|
|
// If an analyzed expr or assignment is missing, dump the node and die.
|
2020-03-28 05:17:25 +08:00
|
|
|
template <typename T>
|
|
|
|
static void CheckMissingAnalysis(bool absent, const T &x) {
|
2020-01-15 09:39:29 +08:00
|
|
|
if (absent) {
|
2020-02-28 23:11:03 +08:00
|
|
|
std::string buf;
|
|
|
|
llvm::raw_string_ostream ss{buf};
|
2020-01-15 09:39:29 +08:00
|
|
|
ss << "node has not been analyzed:\n";
|
|
|
|
parser::DumpTree(ss, x);
|
|
|
|
common::die(ss.str().c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const SomeExpr *GetExprHelper::Get(const parser::Expr &x) {
|
|
|
|
CheckMissingAnalysis(!x.typedExpr, x);
|
|
|
|
return common::GetPtrFromOptional(x.typedExpr->v);
|
|
|
|
}
|
|
|
|
const SomeExpr *GetExprHelper::Get(const parser::Variable &x) {
|
|
|
|
CheckMissingAnalysis(!x.typedExpr, x);
|
|
|
|
return common::GetPtrFromOptional(x.typedExpr->v);
|
|
|
|
}
|
|
|
|
|
2019-11-23 08:04:56 +08:00
|
|
|
const evaluate::Assignment *GetAssignment(const parser::AssignmentStmt &x) {
|
2020-01-15 09:39:29 +08:00
|
|
|
CheckMissingAnalysis(!x.typedAssignment, x);
|
|
|
|
return common::GetPtrFromOptional(x.typedAssignment->v);
|
2019-11-23 08:04:56 +08:00
|
|
|
}
|
2020-01-04 02:38:51 +08:00
|
|
|
const evaluate::Assignment *GetAssignment(
|
|
|
|
const parser::PointerAssignmentStmt &x) {
|
2020-01-15 09:39:29 +08:00
|
|
|
CheckMissingAnalysis(!x.typedAssignment, x);
|
|
|
|
return common::GetPtrFromOptional(x.typedAssignment->v);
|
2020-01-04 02:38:51 +08:00
|
|
|
}
|
2019-11-23 08:04:56 +08:00
|
|
|
|
2019-07-17 05:37:56 +08:00
|
|
|
const Symbol *FindInterface(const Symbol &symbol) {
|
|
|
|
return std::visit(
|
|
|
|
common::visitors{
|
|
|
|
[](const ProcEntityDetails &details) {
|
|
|
|
return details.interface().symbol();
|
|
|
|
},
|
|
|
|
[](const ProcBindingDetails &details) { return &details.symbol(); },
|
|
|
|
[](const auto &) -> const Symbol * { return nullptr; },
|
|
|
|
},
|
|
|
|
symbol.details());
|
|
|
|
}
|
|
|
|
|
|
|
|
const Symbol *FindSubprogram(const Symbol &symbol) {
|
|
|
|
return std::visit(
|
|
|
|
common::visitors{
|
|
|
|
[&](const ProcEntityDetails &details) -> const Symbol * {
|
|
|
|
if (const Symbol * interface{details.interface().symbol()}) {
|
|
|
|
return FindSubprogram(*interface);
|
|
|
|
} else {
|
|
|
|
return &symbol;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
[](const ProcBindingDetails &details) {
|
|
|
|
return FindSubprogram(details.symbol());
|
|
|
|
},
|
|
|
|
[&](const SubprogramDetails &) { return &symbol; },
|
|
|
|
[](const UseDetails &details) {
|
|
|
|
return FindSubprogram(details.symbol());
|
|
|
|
},
|
|
|
|
[](const HostAssocDetails &details) {
|
|
|
|
return FindSubprogram(details.symbol());
|
|
|
|
},
|
|
|
|
[](const auto &) -> const Symbol * { return nullptr; },
|
|
|
|
},
|
|
|
|
symbol.details());
|
|
|
|
}
|
|
|
|
|
2019-04-19 06:07:40 +08:00
|
|
|
const Symbol *FindFunctionResult(const Symbol &symbol) {
|
2019-07-17 05:37:56 +08:00
|
|
|
if (const Symbol * subp{FindSubprogram(symbol)}) {
|
2019-07-23 05:05:07 +08:00
|
|
|
if (const auto &subpDetails{subp->detailsIf<SubprogramDetails>()}) {
|
|
|
|
if (subpDetails->isFunction()) {
|
|
|
|
return &subpDetails->result();
|
|
|
|
}
|
2019-04-19 06:07:40 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
[flang] Allocate semantic checks (second part)
Implement semantic checks and realted tests for constraints:
C937, C938, C939, C940, C941, C942, C945 (second part),
C946, C947, C948, C949 and C950.
Original-commit: flang-compiler/f18@b4965d272b1749d554e3d1388c0a7856591741e8
Tree-same-pre-rewrite: false
2019-04-26 16:10:04 +08:00
|
|
|
|
2019-11-16 06:26:10 +08:00
|
|
|
const Symbol *FindOverriddenBinding(const Symbol &symbol) {
|
|
|
|
if (symbol.has<ProcBindingDetails>()) {
|
|
|
|
if (const DeclTypeSpec * parentType{FindParentTypeSpec(symbol.owner())}) {
|
|
|
|
if (const DerivedTypeSpec * parentDerived{parentType->AsDerived()}) {
|
|
|
|
if (const Scope * parentScope{parentDerived->typeSymbol().scope()}) {
|
|
|
|
return parentScope->FindComponent(symbol.name());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
const DeclTypeSpec *FindParentTypeSpec(const DerivedTypeSpec &derived) {
|
|
|
|
return FindParentTypeSpec(derived.typeSymbol());
|
|
|
|
}
|
|
|
|
|
|
|
|
const DeclTypeSpec *FindParentTypeSpec(const DeclTypeSpec &decl) {
|
|
|
|
if (const DerivedTypeSpec * derived{decl.AsDerived()}) {
|
|
|
|
return FindParentTypeSpec(*derived);
|
|
|
|
} else {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const DeclTypeSpec *FindParentTypeSpec(const Scope &scope) {
|
|
|
|
if (scope.kind() == Scope::Kind::DerivedType) {
|
|
|
|
if (const auto *symbol{scope.symbol()}) {
|
|
|
|
return FindParentTypeSpec(*symbol);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
const DeclTypeSpec *FindParentTypeSpec(const Symbol &symbol) {
|
|
|
|
if (const Scope * scope{symbol.scope()}) {
|
|
|
|
if (const auto *details{symbol.detailsIf<DerivedTypeDetails>()}) {
|
|
|
|
if (const Symbol * parent{details->GetParentComponent(*scope)}) {
|
|
|
|
return parent->GetType();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2020-01-28 06:12:35 +08:00
|
|
|
// When a construct association maps to a variable, and that variable
|
2019-10-11 04:09:35 +08:00
|
|
|
// is not an array with a vector-valued subscript, return the base
|
|
|
|
// Symbol of that variable, else nullptr. Descends into other construct
|
|
|
|
// associations when one associations maps to another.
|
2019-07-26 03:54:11 +08:00
|
|
|
static const Symbol *GetAssociatedVariable(const AssocEntityDetails &details) {
|
2019-07-24 07:34:55 +08:00
|
|
|
if (const MaybeExpr & expr{details.expr()}) {
|
2019-10-11 04:09:35 +08:00
|
|
|
if (evaluate::IsVariable(*expr) && !evaluate::HasVectorSubscript(*expr)) {
|
|
|
|
if (const Symbol * varSymbol{evaluate::GetFirstSymbol(*expr)}) {
|
2019-07-24 07:34:55 +08:00
|
|
|
return GetAssociationRoot(*varSymbol);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return the Symbol of the variable of a construct association, if it exists
|
2019-07-20 06:17:14 +08:00
|
|
|
// Return nullptr if the name is associated with an expression
|
|
|
|
const Symbol *GetAssociationRoot(const Symbol &symbol) {
|
|
|
|
const Symbol &ultimate{symbol.GetUltimate()};
|
2019-07-26 03:54:11 +08:00
|
|
|
if (const auto *details{ultimate.detailsIf<AssocEntityDetails>()}) {
|
|
|
|
// We have a construct association
|
|
|
|
return GetAssociatedVariable(*details);
|
2019-07-24 07:34:55 +08:00
|
|
|
} else {
|
|
|
|
return &ultimate;
|
2019-07-20 06:17:14 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-24 01:59:32 +08:00
|
|
|
bool IsExtensibleType(const DerivedTypeSpec *derived) {
|
|
|
|
return derived && !IsIsoCType(derived) &&
|
|
|
|
!derived->typeSymbol().attrs().test(Attr::BIND_C) &&
|
|
|
|
!derived->typeSymbol().get<DerivedTypeDetails>().sequence();
|
|
|
|
}
|
|
|
|
|
2019-12-26 04:29:50 +08:00
|
|
|
bool IsBuiltinDerivedType(const DerivedTypeSpec *derived, const char *name) {
|
[flang] Allocate semantic checks (second part)
Implement semantic checks and realted tests for constraints:
C937, C938, C939, C940, C941, C942, C945 (second part),
C946, C947, C948, C949 and C950.
Original-commit: flang-compiler/f18@b4965d272b1749d554e3d1388c0a7856591741e8
Tree-same-pre-rewrite: false
2019-04-26 16:10:04 +08:00
|
|
|
if (!derived) {
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
const auto &symbol{derived->typeSymbol()};
|
2019-12-26 04:29:50 +08:00
|
|
|
return symbol.owner().IsModule() &&
|
|
|
|
symbol.owner().GetName().value() == "__fortran_builtins" &&
|
|
|
|
symbol.name() == "__builtin_"s + name;
|
[flang] Allocate semantic checks (second part)
Implement semantic checks and realted tests for constraints:
C937, C938, C939, C940, C941, C942, C945 (second part),
C946, C947, C948, C949 and C950.
Original-commit: flang-compiler/f18@b4965d272b1749d554e3d1388c0a7856591741e8
Tree-same-pre-rewrite: false
2019-04-26 16:10:04 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-22 08:32:11 +08:00
|
|
|
bool IsIsoCType(const DerivedTypeSpec *derived) {
|
2019-12-26 04:29:50 +08:00
|
|
|
return IsBuiltinDerivedType(derived, "c_ptr") ||
|
|
|
|
IsBuiltinDerivedType(derived, "c_funptr");
|
2019-06-22 08:32:11 +08:00
|
|
|
}
|
|
|
|
|
[flang] Allocate semantic checks (second part)
Implement semantic checks and realted tests for constraints:
C937, C938, C939, C940, C941, C942, C945 (second part),
C946, C947, C948, C949 and C950.
Original-commit: flang-compiler/f18@b4965d272b1749d554e3d1388c0a7856591741e8
Tree-same-pre-rewrite: false
2019-04-26 16:10:04 +08:00
|
|
|
bool IsTeamType(const DerivedTypeSpec *derived) {
|
2019-12-26 04:29:50 +08:00
|
|
|
return IsBuiltinDerivedType(derived, "team_type");
|
[flang] Allocate semantic checks (second part)
Implement semantic checks and realted tests for constraints:
C937, C938, C939, C940, C941, C942, C945 (second part),
C946, C947, C948, C949 and C950.
Original-commit: flang-compiler/f18@b4965d272b1749d554e3d1388c0a7856591741e8
Tree-same-pre-rewrite: false
2019-04-26 16:10:04 +08:00
|
|
|
}
|
|
|
|
|
2019-08-16 02:54:51 +08:00
|
|
|
bool IsEventTypeOrLockType(const DerivedTypeSpec *derivedTypeSpec) {
|
2019-12-26 04:29:50 +08:00
|
|
|
return IsBuiltinDerivedType(derivedTypeSpec, "event_type") ||
|
|
|
|
IsBuiltinDerivedType(derivedTypeSpec, "lock_type");
|
[flang] Allocate semantic checks (second part)
Implement semantic checks and realted tests for constraints:
C937, C938, C939, C940, C941, C942, C945 (second part),
C946, C947, C948, C949 and C950.
Original-commit: flang-compiler/f18@b4965d272b1749d554e3d1388c0a7856591741e8
Tree-same-pre-rewrite: false
2019-04-26 16:10:04 +08:00
|
|
|
}
|
|
|
|
|
2019-07-20 06:17:14 +08:00
|
|
|
bool IsOrContainsEventOrLockComponent(const Symbol &symbol) {
|
|
|
|
if (const Symbol * root{GetAssociationRoot(symbol)}) {
|
|
|
|
if (const auto *details{root->detailsIf<ObjectEntityDetails>()}) {
|
|
|
|
if (const DeclTypeSpec * type{details->type()}) {
|
|
|
|
if (const DerivedTypeSpec * derived{type->AsDerived()}) {
|
2019-07-26 03:54:11 +08:00
|
|
|
return IsEventTypeOrLockType(derived) ||
|
2019-07-31 22:19:22 +08:00
|
|
|
FindEventOrLockPotentialComponent(*derived);
|
2019-07-20 06:17:14 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-08-07 02:49:47 +08:00
|
|
|
bool IsSaved(const Symbol &symbol) {
|
|
|
|
auto scopeKind{symbol.owner().kind()};
|
2020-03-14 03:19:44 +08:00
|
|
|
if (scopeKind == Scope::Kind::Module || scopeKind == Scope::Kind::BlockData) {
|
2019-08-07 02:49:47 +08:00
|
|
|
return true;
|
|
|
|
} else if (scopeKind == Scope::Kind::DerivedType) {
|
2020-03-28 05:17:25 +08:00
|
|
|
return false; // this is a component
|
2019-11-16 06:26:10 +08:00
|
|
|
} else if (IsNamedConstant(symbol)) {
|
|
|
|
return false;
|
2019-08-07 02:49:47 +08:00
|
|
|
} else if (symbol.attrs().test(Attr::SAVE)) {
|
|
|
|
return true;
|
|
|
|
} else {
|
2019-11-13 07:43:09 +08:00
|
|
|
if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
|
|
|
|
if (object->init()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
} else if (IsProcedurePointer(symbol)) {
|
|
|
|
if (symbol.get<ProcEntityDetails>().init()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (const Symbol * block{FindCommonBlockContaining(symbol)}) {
|
|
|
|
if (block->attrs().test(Attr::SAVE)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2019-08-07 02:49:47 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-09 06:06:51 +08:00
|
|
|
// Check this symbol suitable as a type-bound procedure - C769
|
|
|
|
bool CanBeTypeBoundProc(const Symbol *symbol) {
|
2019-11-10 01:29:31 +08:00
|
|
|
if (!symbol || IsDummy(*symbol) || IsProcedurePointer(*symbol)) {
|
2019-08-09 06:06:51 +08:00
|
|
|
return false;
|
|
|
|
} else if (symbol->has<SubprogramNameDetails>()) {
|
|
|
|
return symbol->owner().kind() == Scope::Kind::Module;
|
|
|
|
} else if (auto *details{symbol->detailsIf<SubprogramDetails>()}) {
|
|
|
|
return symbol->owner().kind() == Scope::Kind::Module ||
|
|
|
|
details->isInterface();
|
|
|
|
} else if (const auto *proc{symbol->detailsIf<ProcEntityDetails>()}) {
|
|
|
|
return !symbol->attrs().test(Attr::INTRINSIC) &&
|
|
|
|
proc->HasExplicitInterface();
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-10 09:12:46 +08:00
|
|
|
bool IsInitialized(const Symbol &symbol) {
|
|
|
|
if (symbol.test(Symbol::Flag::InDataStmt)) {
|
|
|
|
return true;
|
|
|
|
} else if (IsNamedConstant(symbol)) {
|
|
|
|
return false;
|
|
|
|
} else if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
|
|
|
|
if (IsAllocatable(symbol) || object->init()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (!IsPointer(symbol) && object->type()) {
|
|
|
|
if (const auto *derived{object->type()->AsDerived()}) {
|
|
|
|
if (derived->HasDefaultInitialization()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (const auto *proc{symbol.detailsIf<ProcEntityDetails>()}) {
|
|
|
|
return proc->init().has_value();
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-03-20 11:07:01 +08:00
|
|
|
bool HasIntrinsicTypeName(const Symbol &symbol) {
|
|
|
|
std::string name{symbol.name().ToString()};
|
|
|
|
if (name == "doubleprecision") {
|
|
|
|
return true;
|
|
|
|
} else if (name == "derived") {
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
for (int i{0}; i != common::TypeCategory_enumSize; ++i) {
|
|
|
|
if (name == parser::ToLowerCaseLetters(EnumToString(TypeCategory{i}))) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-20 07:31:10 +08:00
|
|
|
bool IsSeparateModuleProcedureInterface(const Symbol *symbol) {
|
|
|
|
if (symbol && symbol->attrs().test(Attr::MODULE)) {
|
|
|
|
if (auto *details{symbol->detailsIf<SubprogramDetails>()}) {
|
|
|
|
return details->isInterface();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-07-03 03:10:09 +08:00
|
|
|
bool IsFinalizable(const Symbol &symbol) {
|
2019-07-03 11:34:27 +08:00
|
|
|
if (const DeclTypeSpec * type{symbol.GetType()}) {
|
|
|
|
if (const DerivedTypeSpec * derived{type->AsDerived()}) {
|
2019-11-13 07:43:09 +08:00
|
|
|
return IsFinalizable(*derived);
|
2019-07-03 03:10:09 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-11-13 07:43:09 +08:00
|
|
|
bool IsFinalizable(const DerivedTypeSpec &derived) {
|
|
|
|
ScopeComponentIterator components{derived};
|
|
|
|
return std::find_if(components.begin(), components.end(),
|
|
|
|
[](const Symbol &x) { return x.has<FinalProcDetails>(); }) !=
|
|
|
|
components.end();
|
|
|
|
}
|
|
|
|
|
2020-01-28 06:12:35 +08:00
|
|
|
// TODO The following function returns true for all types with FINAL procedures
|
|
|
|
// This is because we don't yet fill in the data for FinalProcDetails
|
2019-11-13 07:43:09 +08:00
|
|
|
bool HasImpureFinal(const DerivedTypeSpec &derived) {
|
|
|
|
ScopeComponentIterator components{derived};
|
|
|
|
return std::find_if(
|
|
|
|
components.begin(), components.end(), [](const Symbol &x) {
|
|
|
|
return x.has<FinalProcDetails>() && !x.attrs().test(Attr::PURE);
|
|
|
|
}) != components.end();
|
|
|
|
}
|
|
|
|
|
2019-07-11 09:20:27 +08:00
|
|
|
bool IsCoarray(const Symbol &symbol) { return symbol.Corank() > 0; }
|
2019-07-03 03:10:09 +08:00
|
|
|
|
2019-09-10 08:01:06 +08:00
|
|
|
bool IsAssumedLengthCharacter(const Symbol &symbol) {
|
|
|
|
if (const DeclTypeSpec * type{symbol.GetType()}) {
|
|
|
|
return type->category() == DeclTypeSpec::Character &&
|
|
|
|
type->characterTypeSpec().length().isAssumed();
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-27 12:19:48 +08:00
|
|
|
// C722 and C723: For a function to be assumed length, it must be external and
|
|
|
|
// of CHARACTER type
|
2020-03-20 07:31:10 +08:00
|
|
|
bool IsExternal(const Symbol &symbol) {
|
|
|
|
return (symbol.has<SubprogramDetails>() && symbol.owner().IsGlobal()) ||
|
|
|
|
symbol.attrs().test(Attr::EXTERNAL);
|
2019-09-10 08:01:06 +08:00
|
|
|
}
|
|
|
|
|
2019-11-13 07:43:09 +08:00
|
|
|
const Symbol *IsExternalInPureContext(
|
|
|
|
const Symbol &symbol, const Scope &scope) {
|
2020-03-03 08:43:01 +08:00
|
|
|
if (const auto *pureProc{FindPureProcedureContaining(scope)}) {
|
2019-07-20 06:17:14 +08:00
|
|
|
if (const Symbol * root{GetAssociationRoot(symbol)}) {
|
2019-11-13 07:43:09 +08:00
|
|
|
if (const Symbol *
|
|
|
|
visible{FindExternallyVisibleObject(*root, *pureProc)}) {
|
|
|
|
return visible;
|
2019-07-20 06:17:14 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-11-13 07:43:09 +08:00
|
|
|
return nullptr;
|
2019-07-20 06:17:14 +08:00
|
|
|
}
|
|
|
|
|
[flang] Changes to check for constraint C1140
This constraint prohibits deallocation of polymorphic entities in a DO
CONCURRENT.
Section 9.7.3.2 specifies the situations that might cause deallocation
of a polymorphic entity. The ones that are applicable to a DO CONCURRENT
are exiting from a block that declares such variables, intrinsic
assignment, and an actual DEALLOCATE statement. This section also
specifies (paragraph 8) that deallocation of a derived type causes
deallocation of all of its allocatable subobjects.
Section 10.2.1.3 specifies what happens during intrinsic assignment.
Paragraph 3 states If the variable is an allocated allocatable variable,
it is deallocated if expr is an array of different shape, any
corresponding length type parameter values of the variable and expr
differ, or the variable is polymorphic and the dynamic type or any
corresponding kind type parameter values of the variable and expr
differ." Thus, an allocatable polymorphic variable on the left hand side
of an assignment statement gets deallocated. Paragraph 13 states that
"For a noncoarray allocatable component the following sequence of
operations is applied.
(1) If the component of the variable is allocated, it is deallocated."
Thus, a variable on the left-hand side of an assignment statement might have noncorray allocatable components. Such components will be deallocated.
Deallocation can be caused by exiting from a block where the entity is
declared, from an assignment, and from direct deallocation.
Original-commit: flang-compiler/f18@7d1932d344308d8266503268a7534532cebe6087
Reviewed-on: https://github.com/flang-compiler/f18/pull/814
2019-11-06 02:18:33 +08:00
|
|
|
PotentialComponentIterator::const_iterator FindPolymorphicPotentialComponent(
|
|
|
|
const DerivedTypeSpec &derived) {
|
|
|
|
PotentialComponentIterator potentials{derived};
|
|
|
|
return std::find_if(
|
|
|
|
potentials.begin(), potentials.end(), [](const Symbol &component) {
|
|
|
|
if (const auto *details{component.detailsIf<ObjectEntityDetails>()}) {
|
|
|
|
const DeclTypeSpec *type{details->type()};
|
|
|
|
return type && type->IsPolymorphic();
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsOrContainsPolymorphicComponent(const Symbol &symbol) {
|
|
|
|
if (const Symbol * root{GetAssociationRoot(symbol)}) {
|
|
|
|
if (const auto *details{root->detailsIf<ObjectEntityDetails>()}) {
|
|
|
|
if (const DeclTypeSpec * type{details->type()}) {
|
|
|
|
if (type->IsPolymorphic()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (const DerivedTypeSpec * derived{type->AsDerived()}) {
|
|
|
|
return (bool)FindPolymorphicPotentialComponent(*derived);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-07-20 06:17:14 +08:00
|
|
|
bool InProtectedContext(const Symbol &symbol, const Scope ¤tScope) {
|
|
|
|
return IsProtected(symbol) && !IsHostAssociated(symbol, currentScope);
|
|
|
|
}
|
|
|
|
|
2019-07-26 03:54:11 +08:00
|
|
|
// C1101 and C1158
|
2019-10-11 04:09:35 +08:00
|
|
|
// TODO Need to check for a coindexed object (why? C1103?)
|
2019-07-24 07:34:55 +08:00
|
|
|
std::optional<parser::MessageFixedText> WhyNotModifiable(
|
|
|
|
const Symbol &symbol, const Scope &scope) {
|
|
|
|
const Symbol *root{GetAssociationRoot(symbol)};
|
|
|
|
if (!root) {
|
|
|
|
return "'%s' is construct associated with an expression"_en_US;
|
|
|
|
} else if (InProtectedContext(*root, scope)) {
|
|
|
|
return "'%s' is protected in this scope"_en_US;
|
|
|
|
} else if (IsExternalInPureContext(*root, scope)) {
|
2019-12-24 09:12:53 +08:00
|
|
|
return "'%s' is externally visible and referenced in a pure"
|
2019-07-26 03:54:11 +08:00
|
|
|
" procedure"_en_US;
|
2019-07-24 07:34:55 +08:00
|
|
|
} else if (IsOrContainsEventOrLockComponent(*root)) {
|
|
|
|
return "'%s' is an entity with either an EVENT_TYPE or LOCK_TYPE"_en_US;
|
2019-07-26 03:54:11 +08:00
|
|
|
} else if (IsIntentIn(*root)) {
|
2019-07-24 07:34:55 +08:00
|
|
|
return "'%s' is an INTENT(IN) dummy argument"_en_US;
|
|
|
|
} else if (!IsVariableName(*root)) {
|
|
|
|
return "'%s' is not a variable"_en_US;
|
2019-07-20 06:17:14 +08:00
|
|
|
} else {
|
2019-07-24 07:34:55 +08:00
|
|
|
return std::nullopt;
|
2019-07-20 06:17:14 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-06 04:56:30 +08:00
|
|
|
std::optional<parser::Message> WhyNotModifiable(parser::CharBlock at,
|
2019-10-11 07:06:05 +08:00
|
|
|
const SomeExpr &expr, const Scope &scope, bool vectorSubscriptIsOk) {
|
2020-03-06 04:56:30 +08:00
|
|
|
if (!evaluate::IsVariable(expr)) {
|
|
|
|
return parser::Message{at, "Expression is not a variable"_en_US};
|
2020-03-28 05:17:25 +08:00
|
|
|
} else if (auto dataRef{evaluate::ExtractDataRef(expr, true)}) {
|
2020-03-06 04:56:30 +08:00
|
|
|
if (!vectorSubscriptIsOk && evaluate::HasVectorSubscript(expr)) {
|
|
|
|
return parser::Message{at, "Variable has a vector subscript"_en_US};
|
|
|
|
}
|
|
|
|
const Symbol &symbol{dataRef->GetFirstSymbol()};
|
|
|
|
if (auto maybeWhy{WhyNotModifiable(symbol, scope)}) {
|
|
|
|
return parser::Message{symbol.name(),
|
|
|
|
parser::MessageFormattedText{std::move(*maybeWhy), symbol.name()}};
|
2019-10-11 04:09:35 +08:00
|
|
|
}
|
|
|
|
} else {
|
2020-03-06 04:56:30 +08:00
|
|
|
// reference to function returning POINTER
|
2019-10-11 04:09:35 +08:00
|
|
|
}
|
2020-03-06 04:56:30 +08:00
|
|
|
return std::nullopt;
|
2019-10-11 04:09:35 +08:00
|
|
|
}
|
|
|
|
|
2019-11-13 07:43:09 +08:00
|
|
|
class ImageControlStmtHelper {
|
2019-10-12 05:39:33 +08:00
|
|
|
using ImageControlStmts = std::variant<parser::ChangeTeamConstruct,
|
|
|
|
parser::CriticalConstruct, parser::EventPostStmt, parser::EventWaitStmt,
|
|
|
|
parser::FormTeamStmt, parser::LockStmt, parser::StopStmt,
|
|
|
|
parser::SyncAllStmt, parser::SyncImagesStmt, parser::SyncMemoryStmt,
|
|
|
|
parser::SyncTeamStmt, parser::UnlockStmt>;
|
2019-11-13 07:43:09 +08:00
|
|
|
|
|
|
|
public:
|
2020-03-28 05:17:25 +08:00
|
|
|
template <typename T> bool operator()(const T &) {
|
2019-10-12 05:39:33 +08:00
|
|
|
return common::HasMember<T, ImageControlStmts>;
|
|
|
|
}
|
2020-03-28 05:17:25 +08:00
|
|
|
template <typename T> bool operator()(const common::Indirection<T> &x) {
|
2019-10-12 05:39:33 +08:00
|
|
|
return (*this)(x.value());
|
|
|
|
}
|
|
|
|
bool operator()(const parser::AllocateStmt &stmt) {
|
|
|
|
const auto &allocationList{std::get<std::list<parser::Allocation>>(stmt.t)};
|
|
|
|
for (const auto &allocation : allocationList) {
|
|
|
|
const auto &allocateObject{
|
|
|
|
std::get<parser::AllocateObject>(allocation.t)};
|
|
|
|
if (IsCoarrayObject(allocateObject)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
bool operator()(const parser::DeallocateStmt &stmt) {
|
|
|
|
const auto &allocateObjectList{
|
|
|
|
std::get<std::list<parser::AllocateObject>>(stmt.t)};
|
|
|
|
for (const auto &allocateObject : allocateObjectList) {
|
|
|
|
if (IsCoarrayObject(allocateObject)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
bool operator()(const parser::CallStmt &stmt) {
|
|
|
|
const auto &procedureDesignator{
|
|
|
|
std::get<parser::ProcedureDesignator>(stmt.v.t)};
|
|
|
|
if (auto *name{std::get_if<parser::Name>(&procedureDesignator.u)}) {
|
|
|
|
// TODO: also ensure that the procedure is, in fact, an intrinsic
|
|
|
|
if (name->source == "move_alloc") {
|
|
|
|
const auto &args{std::get<std::list<parser::ActualArgSpec>>(stmt.v.t)};
|
|
|
|
if (!args.empty()) {
|
|
|
|
const parser::ActualArg &actualArg{
|
|
|
|
std::get<parser::ActualArg>(args.front().t)};
|
|
|
|
if (const auto *argExpr{
|
|
|
|
std::get_if<common::Indirection<parser::Expr>>(
|
|
|
|
&actualArg.u)}) {
|
|
|
|
return HasCoarray(argExpr->value());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
bool operator()(const parser::Statement<parser::ActionStmt> &stmt) {
|
|
|
|
return std::visit(*this, stmt.statement.u);
|
|
|
|
}
|
2019-11-13 07:43:09 +08:00
|
|
|
|
|
|
|
private:
|
|
|
|
bool IsCoarrayObject(const parser::AllocateObject &allocateObject) {
|
|
|
|
const parser::Name &name{GetLastName(allocateObject)};
|
|
|
|
return name.symbol && IsCoarray(*name.symbol);
|
|
|
|
}
|
2019-10-12 05:39:33 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
bool IsImageControlStmt(const parser::ExecutableConstruct &construct) {
|
|
|
|
return std::visit(ImageControlStmtHelper{}, construct.u);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::optional<parser::MessageFixedText> GetImageControlStmtCoarrayMsg(
|
|
|
|
const parser::ExecutableConstruct &construct) {
|
|
|
|
if (const auto *actionStmt{
|
|
|
|
std::get_if<parser::Statement<parser::ActionStmt>>(&construct.u)}) {
|
|
|
|
return std::visit(
|
|
|
|
common::visitors{
|
|
|
|
[](const common::Indirection<parser::AllocateStmt> &)
|
|
|
|
-> std::optional<parser::MessageFixedText> {
|
|
|
|
return "ALLOCATE of a coarray is an image control"
|
|
|
|
" statement"_en_US;
|
|
|
|
},
|
|
|
|
[](const common::Indirection<parser::DeallocateStmt> &)
|
|
|
|
-> std::optional<parser::MessageFixedText> {
|
|
|
|
return "DEALLOCATE of a coarray is an image control"
|
|
|
|
" statement"_en_US;
|
|
|
|
},
|
|
|
|
[](const common::Indirection<parser::CallStmt> &)
|
|
|
|
-> std::optional<parser::MessageFixedText> {
|
|
|
|
return "MOVE_ALLOC of a coarray is an image control"
|
|
|
|
" statement "_en_US;
|
|
|
|
},
|
|
|
|
[](const auto &) -> std::optional<parser::MessageFixedText> {
|
|
|
|
return std::nullopt;
|
|
|
|
},
|
|
|
|
},
|
|
|
|
actionStmt->statement.u);
|
|
|
|
}
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
2019-11-13 07:43:09 +08:00
|
|
|
parser::CharBlock GetImageControlStmtLocation(
|
2019-10-12 05:39:33 +08:00
|
|
|
const parser::ExecutableConstruct &executableConstruct) {
|
|
|
|
return std::visit(
|
|
|
|
common::visitors{
|
|
|
|
[](const common::Indirection<parser::ChangeTeamConstruct>
|
|
|
|
&construct) {
|
|
|
|
return std::get<parser::Statement<parser::ChangeTeamStmt>>(
|
|
|
|
construct.value().t)
|
|
|
|
.source;
|
|
|
|
},
|
|
|
|
[](const common::Indirection<parser::CriticalConstruct> &construct) {
|
|
|
|
return std::get<parser::Statement<parser::CriticalStmt>>(
|
|
|
|
construct.value().t)
|
|
|
|
.source;
|
|
|
|
},
|
|
|
|
[](const parser::Statement<parser::ActionStmt> &actionStmt) {
|
|
|
|
return actionStmt.source;
|
|
|
|
},
|
|
|
|
[](const auto &) { return parser::CharBlock{}; },
|
|
|
|
},
|
|
|
|
executableConstruct.u);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HasCoarray(const parser::Expr &expression) {
|
|
|
|
if (const auto *expr{GetExpr(expression)}) {
|
2019-10-23 07:53:29 +08:00
|
|
|
for (const Symbol &symbol : evaluate::CollectSymbols(*expr)) {
|
|
|
|
if (const Symbol * root{GetAssociationRoot(symbol)}) {
|
2019-10-12 05:39:33 +08:00
|
|
|
if (IsCoarray(*root)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
[flang] Changes to check for constraint C1140
This constraint prohibits deallocation of polymorphic entities in a DO
CONCURRENT.
Section 9.7.3.2 specifies the situations that might cause deallocation
of a polymorphic entity. The ones that are applicable to a DO CONCURRENT
are exiting from a block that declares such variables, intrinsic
assignment, and an actual DEALLOCATE statement. This section also
specifies (paragraph 8) that deallocation of a derived type causes
deallocation of all of its allocatable subobjects.
Section 10.2.1.3 specifies what happens during intrinsic assignment.
Paragraph 3 states If the variable is an allocated allocatable variable,
it is deallocated if expr is an array of different shape, any
corresponding length type parameter values of the variable and expr
differ, or the variable is polymorphic and the dynamic type or any
corresponding kind type parameter values of the variable and expr
differ." Thus, an allocatable polymorphic variable on the left hand side
of an assignment statement gets deallocated. Paragraph 13 states that
"For a noncoarray allocatable component the following sequence of
operations is applied.
(1) If the component of the variable is allocated, it is deallocated."
Thus, a variable on the left-hand side of an assignment statement might have noncorray allocatable components. Such components will be deallocated.
Deallocation can be caused by exiting from a block where the entity is
declared, from an assignment, and from direct deallocation.
Original-commit: flang-compiler/f18@7d1932d344308d8266503268a7534532cebe6087
Reviewed-on: https://github.com/flang-compiler/f18/pull/814
2019-11-06 02:18:33 +08:00
|
|
|
bool IsPolymorphic(const Symbol &symbol) {
|
|
|
|
if (const DeclTypeSpec * type{symbol.GetType()}) {
|
|
|
|
return type->IsPolymorphic();
|
2019-11-13 07:43:09 +08:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
[flang] Changes to check for constraint C1140
This constraint prohibits deallocation of polymorphic entities in a DO
CONCURRENT.
Section 9.7.3.2 specifies the situations that might cause deallocation
of a polymorphic entity. The ones that are applicable to a DO CONCURRENT
are exiting from a block that declares such variables, intrinsic
assignment, and an actual DEALLOCATE statement. This section also
specifies (paragraph 8) that deallocation of a derived type causes
deallocation of all of its allocatable subobjects.
Section 10.2.1.3 specifies what happens during intrinsic assignment.
Paragraph 3 states If the variable is an allocated allocatable variable,
it is deallocated if expr is an array of different shape, any
corresponding length type parameter values of the variable and expr
differ, or the variable is polymorphic and the dynamic type or any
corresponding kind type parameter values of the variable and expr
differ." Thus, an allocatable polymorphic variable on the left hand side
of an assignment statement gets deallocated. Paragraph 13 states that
"For a noncoarray allocatable component the following sequence of
operations is applied.
(1) If the component of the variable is allocated, it is deallocated."
Thus, a variable on the left-hand side of an assignment statement might have noncorray allocatable components. Such components will be deallocated.
Deallocation can be caused by exiting from a block where the entity is
declared, from an assignment, and from direct deallocation.
Original-commit: flang-compiler/f18@7d1932d344308d8266503268a7534532cebe6087
Reviewed-on: https://github.com/flang-compiler/f18/pull/814
2019-11-06 02:18:33 +08:00
|
|
|
bool IsPolymorphicAllocatable(const Symbol &symbol) {
|
|
|
|
return IsAllocatable(symbol) && IsPolymorphic(symbol);
|
|
|
|
}
|
|
|
|
|
2020-03-03 08:43:01 +08:00
|
|
|
std::optional<parser::MessageFormattedText> CheckAccessibleComponent(
|
|
|
|
const Scope &scope, const Symbol &symbol) {
|
2020-03-28 05:17:25 +08:00
|
|
|
CHECK(symbol.owner().IsDerivedType()); // symbol must be a component
|
2020-03-03 08:43:01 +08:00
|
|
|
if (symbol.attrs().test(Attr::PRIVATE)) {
|
|
|
|
if (const Scope * moduleScope{FindModuleContaining(symbol.owner())}) {
|
2020-03-11 06:31:02 +08:00
|
|
|
if (!moduleScope->Contains(scope)) {
|
2020-03-03 08:43:01 +08:00
|
|
|
return parser::MessageFormattedText{
|
|
|
|
"PRIVATE component '%s' is only accessible within module '%s'"_err_en_US,
|
|
|
|
symbol.name(), moduleScope->GetName().value()};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
2019-07-11 21:29:31 +08:00
|
|
|
std::list<SourceName> OrderParameterNames(const Symbol &typeSymbol) {
|
|
|
|
std::list<SourceName> result;
|
|
|
|
if (const DerivedTypeSpec * spec{typeSymbol.GetParentTypeSpec()}) {
|
|
|
|
result = OrderParameterNames(spec->typeSymbol());
|
|
|
|
}
|
|
|
|
const auto ¶mNames{typeSymbol.get<DerivedTypeDetails>().paramNames()};
|
|
|
|
result.insert(result.end(), paramNames.begin(), paramNames.end());
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
SymbolVector OrderParameterDeclarations(const Symbol &typeSymbol) {
|
|
|
|
SymbolVector result;
|
|
|
|
if (const DerivedTypeSpec * spec{typeSymbol.GetParentTypeSpec()}) {
|
|
|
|
result = OrderParameterDeclarations(spec->typeSymbol());
|
|
|
|
}
|
|
|
|
const auto ¶mDecls{typeSymbol.get<DerivedTypeDetails>().paramDecls()};
|
|
|
|
result.insert(result.end(), paramDecls.begin(), paramDecls.end());
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2019-07-11 09:20:27 +08:00
|
|
|
const DeclTypeSpec &FindOrInstantiateDerivedType(Scope &scope,
|
|
|
|
DerivedTypeSpec &&spec, SemanticsContext &semanticsContext,
|
|
|
|
DeclTypeSpec::Category category) {
|
2019-11-23 00:15:02 +08:00
|
|
|
spec.CookParameters(semanticsContext.foldingContext());
|
|
|
|
spec.EvaluateParameters(semanticsContext.foldingContext());
|
2019-07-11 09:20:27 +08:00
|
|
|
if (const DeclTypeSpec *
|
2019-11-23 00:15:02 +08:00
|
|
|
type{scope.FindInstantiatedDerivedType(spec, category)}) {
|
2019-07-11 09:20:27 +08:00
|
|
|
return *type;
|
|
|
|
}
|
|
|
|
// Create a new instantiation of this parameterized derived type
|
|
|
|
// for this particular distinct set of actual parameter values.
|
2019-09-12 09:21:07 +08:00
|
|
|
DeclTypeSpec &type{scope.MakeDerivedType(category, std::move(spec))};
|
2019-11-23 00:15:02 +08:00
|
|
|
type.derivedTypeSpec().Instantiate(scope, semanticsContext);
|
2019-07-11 09:20:27 +08:00
|
|
|
return type;
|
|
|
|
}
|
|
|
|
|
2020-03-20 07:31:10 +08:00
|
|
|
const Symbol *FindSeparateModuleSubprogramInterface(const Symbol *proc) {
|
|
|
|
if (proc) {
|
|
|
|
if (const Symbol * submodule{proc->owner().symbol()}) {
|
|
|
|
if (const auto *details{submodule->detailsIf<ModuleDetails>()}) {
|
|
|
|
if (const Scope * ancestor{details->ancestor()}) {
|
|
|
|
const Symbol *iface{ancestor->FindSymbol(proc->name())};
|
|
|
|
if (IsSeparateModuleProcedureInterface(iface)) {
|
|
|
|
return iface;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2019-07-31 22:19:22 +08:00
|
|
|
// ComponentIterator implementation
|
|
|
|
|
2020-03-28 05:17:25 +08:00
|
|
|
template <ComponentKind componentKind>
|
2019-07-31 22:19:22 +08:00
|
|
|
typename ComponentIterator<componentKind>::const_iterator
|
|
|
|
ComponentIterator<componentKind>::const_iterator::Create(
|
|
|
|
const DerivedTypeSpec &derived) {
|
|
|
|
const_iterator it{};
|
2019-10-23 07:53:29 +08:00
|
|
|
it.componentPath_.emplace_back(derived);
|
2020-03-28 05:17:25 +08:00
|
|
|
it.Increment(); // cue up first relevant component, if any
|
2019-10-23 07:53:29 +08:00
|
|
|
return it;
|
2019-07-31 22:19:22 +08:00
|
|
|
}
|
2019-07-26 21:33:48 +08:00
|
|
|
|
2020-03-28 05:17:25 +08:00
|
|
|
template <ComponentKind componentKind>
|
2019-10-23 07:53:29 +08:00
|
|
|
const DerivedTypeSpec *
|
|
|
|
ComponentIterator<componentKind>::const_iterator::PlanComponentTraversal(
|
|
|
|
const Symbol &component) const {
|
2019-07-31 22:19:22 +08:00
|
|
|
if (const auto *details{component.detailsIf<ObjectEntityDetails>()}) {
|
2019-10-23 07:53:29 +08:00
|
|
|
if (const DeclTypeSpec * type{details->type()}) {
|
|
|
|
if (const auto *derived{type->AsDerived()}) {
|
|
|
|
bool traverse{false};
|
|
|
|
if constexpr (componentKind == ComponentKind::Ordered) {
|
|
|
|
// Order Component (only visit parents)
|
|
|
|
traverse = component.test(Symbol::Flag::ParentComp);
|
|
|
|
} else if constexpr (componentKind == ComponentKind::Direct) {
|
|
|
|
traverse = !IsAllocatableOrPointer(component);
|
|
|
|
} else if constexpr (componentKind == ComponentKind::Ultimate) {
|
|
|
|
traverse = !IsAllocatableOrPointer(component);
|
|
|
|
} else if constexpr (componentKind == ComponentKind::Potential) {
|
|
|
|
traverse = !IsPointer(component);
|
2019-11-13 07:43:09 +08:00
|
|
|
} else if constexpr (componentKind == ComponentKind::Scope) {
|
|
|
|
traverse = !IsAllocatableOrPointer(component);
|
2019-10-23 07:53:29 +08:00
|
|
|
}
|
|
|
|
if (traverse) {
|
|
|
|
const Symbol &newTypeSymbol{derived->typeSymbol()};
|
|
|
|
// Avoid infinite loop if the type is already part of the types
|
|
|
|
// being visited. It is possible to have "loops in type" because
|
|
|
|
// C744 does not forbid to use not yet declared type for
|
|
|
|
// ALLOCATABLE or POINTER components.
|
|
|
|
for (const auto &node : componentPath_) {
|
|
|
|
if (&newTypeSymbol == &node.GetTypeSymbol()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
2019-07-31 22:19:22 +08:00
|
|
|
}
|
2019-10-23 07:53:29 +08:00
|
|
|
return derived;
|
2019-07-31 22:19:22 +08:00
|
|
|
}
|
|
|
|
}
|
2020-03-28 05:17:25 +08:00
|
|
|
} // intrinsic & unlimited polymorphic not traversable
|
2019-07-26 21:33:48 +08:00
|
|
|
}
|
2019-10-23 07:53:29 +08:00
|
|
|
return nullptr;
|
2019-07-31 22:19:22 +08:00
|
|
|
}
|
2019-07-26 21:33:48 +08:00
|
|
|
|
2020-03-28 05:17:25 +08:00
|
|
|
template <ComponentKind componentKind>
|
2019-07-31 22:19:22 +08:00
|
|
|
static bool StopAtComponentPre(const Symbol &component) {
|
|
|
|
if constexpr (componentKind == ComponentKind::Ordered) {
|
|
|
|
// Parent components need to be iterated upon after their
|
|
|
|
// sub-components in structure constructor analysis.
|
|
|
|
return !component.test(Symbol::Flag::ParentComp);
|
|
|
|
} else if constexpr (componentKind == ComponentKind::Direct) {
|
|
|
|
return true;
|
|
|
|
} else if constexpr (componentKind == ComponentKind::Ultimate) {
|
|
|
|
return component.has<ProcEntityDetails>() ||
|
|
|
|
IsAllocatableOrPointer(component) ||
|
|
|
|
(component.get<ObjectEntityDetails>().type() &&
|
|
|
|
component.get<ObjectEntityDetails>().type()->AsIntrinsic());
|
|
|
|
} else if constexpr (componentKind == ComponentKind::Potential) {
|
|
|
|
return !IsPointer(component);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-28 05:17:25 +08:00
|
|
|
template <ComponentKind componentKind>
|
2019-07-31 22:19:22 +08:00
|
|
|
static bool StopAtComponentPost(const Symbol &component) {
|
2019-10-23 07:53:29 +08:00
|
|
|
return componentKind == ComponentKind::Ordered &&
|
|
|
|
component.test(Symbol::Flag::ParentComp);
|
2019-07-31 22:19:22 +08:00
|
|
|
}
|
2019-07-26 21:33:48 +08:00
|
|
|
|
2020-03-28 05:17:25 +08:00
|
|
|
template <ComponentKind componentKind>
|
2019-07-31 22:19:22 +08:00
|
|
|
void ComponentIterator<componentKind>::const_iterator::Increment() {
|
2019-10-23 07:53:29 +08:00
|
|
|
while (!componentPath_.empty()) {
|
|
|
|
ComponentPathNode &deepest{componentPath_.back()};
|
|
|
|
if (deepest.component()) {
|
|
|
|
if (!deepest.descended()) {
|
|
|
|
deepest.set_descended(true);
|
|
|
|
if (const DerivedTypeSpec *
|
|
|
|
derived{PlanComponentTraversal(*deepest.component())}) {
|
|
|
|
componentPath_.emplace_back(*derived);
|
|
|
|
continue;
|
2019-07-26 21:33:48 +08:00
|
|
|
}
|
2019-10-23 07:53:29 +08:00
|
|
|
} else if (!deepest.visited()) {
|
|
|
|
deepest.set_visited(true);
|
2020-03-28 05:17:25 +08:00
|
|
|
return; // this is the next component to visit, after descending
|
2019-07-31 22:19:22 +08:00
|
|
|
}
|
|
|
|
}
|
2019-10-23 07:53:29 +08:00
|
|
|
auto &nameIterator{deepest.nameIterator()};
|
|
|
|
if (nameIterator == deepest.nameEnd()) {
|
2019-07-31 22:19:22 +08:00
|
|
|
componentPath_.pop_back();
|
2019-11-13 07:43:09 +08:00
|
|
|
} else if constexpr (componentKind == ComponentKind::Scope) {
|
|
|
|
deepest.set_component(*nameIterator++->second);
|
|
|
|
deepest.set_descended(false);
|
|
|
|
deepest.set_visited(true);
|
2020-03-28 05:17:25 +08:00
|
|
|
return; // this is the next component to visit, before descending
|
2019-10-23 07:53:29 +08:00
|
|
|
} else {
|
|
|
|
const Scope &scope{deepest.GetScope()};
|
|
|
|
auto scopeIter{scope.find(*nameIterator++)};
|
|
|
|
if (scopeIter != scope.cend()) {
|
|
|
|
const Symbol &component{*scopeIter->second};
|
|
|
|
deepest.set_component(component);
|
|
|
|
deepest.set_descended(false);
|
|
|
|
if (StopAtComponentPre<componentKind>(component)) {
|
|
|
|
deepest.set_visited(true);
|
2020-03-28 05:17:25 +08:00
|
|
|
return; // this is the next component to visit, before descending
|
2019-10-23 07:53:29 +08:00
|
|
|
} else {
|
|
|
|
deepest.set_visited(!StopAtComponentPost<componentKind>(component));
|
|
|
|
}
|
|
|
|
}
|
2019-07-31 22:19:22 +08:00
|
|
|
}
|
|
|
|
}
|
2019-07-26 21:33:48 +08:00
|
|
|
}
|
|
|
|
|
2020-03-28 05:17:25 +08:00
|
|
|
template <ComponentKind componentKind>
|
2019-07-31 22:19:22 +08:00
|
|
|
std::string
|
|
|
|
ComponentIterator<componentKind>::const_iterator::BuildResultDesignatorName()
|
|
|
|
const {
|
|
|
|
std::string designator{""};
|
|
|
|
for (const auto &node : componentPath_) {
|
2019-10-23 07:53:29 +08:00
|
|
|
designator += "%" + DEREF(node.component()).name().ToString();
|
2019-07-31 22:19:22 +08:00
|
|
|
}
|
|
|
|
return designator;
|
2019-07-26 21:33:48 +08:00
|
|
|
}
|
|
|
|
|
2019-07-31 22:19:22 +08:00
|
|
|
template class ComponentIterator<ComponentKind::Ordered>;
|
|
|
|
template class ComponentIterator<ComponentKind::Direct>;
|
|
|
|
template class ComponentIterator<ComponentKind::Ultimate>;
|
|
|
|
template class ComponentIterator<ComponentKind::Potential>;
|
2019-11-13 07:43:09 +08:00
|
|
|
template class ComponentIterator<ComponentKind::Scope>;
|
2019-07-26 21:33:48 +08:00
|
|
|
|
2019-07-31 22:19:22 +08:00
|
|
|
UltimateComponentIterator::const_iterator FindCoarrayUltimateComponent(
|
2019-07-26 23:11:59 +08:00
|
|
|
const DerivedTypeSpec &derived) {
|
2019-07-31 22:19:22 +08:00
|
|
|
UltimateComponentIterator ultimates{derived};
|
2019-11-13 07:43:09 +08:00
|
|
|
return std::find_if(ultimates.begin(), ultimates.end(), IsCoarray);
|
2019-07-26 23:11:59 +08:00
|
|
|
}
|
|
|
|
|
2019-09-14 03:32:43 +08:00
|
|
|
UltimateComponentIterator::const_iterator FindPointerUltimateComponent(
|
|
|
|
const DerivedTypeSpec &derived) {
|
|
|
|
UltimateComponentIterator ultimates{derived};
|
2019-11-13 07:43:09 +08:00
|
|
|
return std::find_if(ultimates.begin(), ultimates.end(), IsPointer);
|
2019-09-14 03:32:43 +08:00
|
|
|
}
|
|
|
|
|
2019-07-31 22:19:22 +08:00
|
|
|
PotentialComponentIterator::const_iterator FindEventOrLockPotentialComponent(
|
|
|
|
const DerivedTypeSpec &derived) {
|
|
|
|
PotentialComponentIterator potentials{derived};
|
|
|
|
return std::find_if(
|
2019-10-23 07:53:29 +08:00
|
|
|
potentials.begin(), potentials.end(), [](const Symbol &component) {
|
|
|
|
if (const auto *details{component.detailsIf<ObjectEntityDetails>()}) {
|
2019-07-31 22:19:22 +08:00
|
|
|
const DeclTypeSpec *type{details->type()};
|
|
|
|
return type && IsEventTypeOrLockType(type->AsDerived());
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
});
|
2019-07-26 21:33:48 +08:00
|
|
|
}
|
|
|
|
|
2019-11-13 07:43:09 +08:00
|
|
|
UltimateComponentIterator::const_iterator FindAllocatableUltimateComponent(
|
|
|
|
const DerivedTypeSpec &derived) {
|
|
|
|
UltimateComponentIterator ultimates{derived};
|
|
|
|
return std::find_if(ultimates.begin(), ultimates.end(), IsAllocatable);
|
|
|
|
}
|
|
|
|
|
|
|
|
UltimateComponentIterator::const_iterator
|
|
|
|
FindPolymorphicAllocatableUltimateComponent(const DerivedTypeSpec &derived) {
|
|
|
|
UltimateComponentIterator ultimates{derived};
|
|
|
|
return std::find_if(
|
|
|
|
ultimates.begin(), ultimates.end(), IsPolymorphicAllocatable);
|
|
|
|
}
|
|
|
|
|
2019-11-16 06:26:10 +08:00
|
|
|
UltimateComponentIterator::const_iterator
|
|
|
|
FindPolymorphicAllocatableNonCoarrayUltimateComponent(
|
|
|
|
const DerivedTypeSpec &derived) {
|
|
|
|
UltimateComponentIterator ultimates{derived};
|
|
|
|
return std::find_if(ultimates.begin(), ultimates.end(), [](const Symbol &x) {
|
|
|
|
return IsPolymorphicAllocatable(x) && !IsCoarray(x);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-07-26 23:11:59 +08:00
|
|
|
const Symbol *FindUltimateComponent(const DerivedTypeSpec &derived,
|
2019-10-18 07:15:20 +08:00
|
|
|
const std::function<bool(const Symbol &)> &predicate) {
|
2019-07-31 22:19:22 +08:00
|
|
|
UltimateComponentIterator ultimates{derived};
|
|
|
|
if (auto it{std::find_if(ultimates.begin(), ultimates.end(),
|
2019-10-23 07:53:29 +08:00
|
|
|
[&predicate](const Symbol &component) -> bool {
|
|
|
|
return predicate(component);
|
2019-07-31 22:19:22 +08:00
|
|
|
})}) {
|
2019-10-23 07:53:29 +08:00
|
|
|
return &*it;
|
2019-07-31 22:19:22 +08:00
|
|
|
}
|
|
|
|
return nullptr;
|
2019-07-26 23:11:59 +08:00
|
|
|
}
|
|
|
|
|
2019-10-18 07:15:20 +08:00
|
|
|
const Symbol *FindUltimateComponent(const Symbol &symbol,
|
|
|
|
const std::function<bool(const Symbol &)> &predicate) {
|
|
|
|
if (predicate(symbol)) {
|
|
|
|
return &symbol;
|
|
|
|
} else if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
|
|
|
|
if (const auto *type{object->type()}) {
|
|
|
|
if (const auto *derived{type->AsDerived()}) {
|
|
|
|
return FindUltimateComponent(*derived, predicate);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2019-10-18 06:29:26 +08:00
|
|
|
const Symbol *FindImmediateComponent(const DerivedTypeSpec &type,
|
|
|
|
const std::function<bool(const Symbol &)> &predicate) {
|
|
|
|
if (const Scope * scope{type.scope()}) {
|
|
|
|
const Symbol *parent{nullptr};
|
|
|
|
for (const auto &pair : *scope) {
|
2019-10-23 07:53:29 +08:00
|
|
|
const Symbol *symbol{&*pair.second};
|
|
|
|
if (predicate(*symbol)) {
|
|
|
|
return symbol;
|
|
|
|
}
|
|
|
|
if (symbol->test(Symbol::Flag::ParentComp)) {
|
|
|
|
parent = symbol;
|
2019-10-18 06:29:26 +08:00
|
|
|
}
|
|
|
|
}
|
2019-11-10 01:29:31 +08:00
|
|
|
if (parent) {
|
2019-10-18 06:29:26 +08:00
|
|
|
if (const auto *object{parent->detailsIf<ObjectEntityDetails>()}) {
|
|
|
|
if (const auto *type{object->type()}) {
|
|
|
|
if (const auto *derived{type->AsDerived()}) {
|
|
|
|
return FindImmediateComponent(*derived, predicate);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2019-08-08 18:46:14 +08:00
|
|
|
bool IsFunctionResult(const Symbol &symbol) {
|
2020-03-03 08:43:01 +08:00
|
|
|
return (symbol.has<ObjectEntityDetails>() &&
|
|
|
|
symbol.get<ObjectEntityDetails>().isFuncResult()) ||
|
|
|
|
(symbol.has<ProcEntityDetails>() &&
|
|
|
|
symbol.get<ProcEntityDetails>().isFuncResult());
|
2019-08-08 18:46:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
bool IsFunctionResultWithSameNameAsFunction(const Symbol &symbol) {
|
|
|
|
if (IsFunctionResult(symbol)) {
|
2019-08-07 18:51:13 +08:00
|
|
|
if (const Symbol * function{symbol.owner().symbol()}) {
|
|
|
|
return symbol.name() == function->name();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2019-12-26 13:18:47 +08:00
|
|
|
|
|
|
|
void LabelEnforce::Post(const parser::GotoStmt &gotoStmt) {
|
|
|
|
checkLabelUse(gotoStmt.v);
|
|
|
|
}
|
|
|
|
void LabelEnforce::Post(const parser::ComputedGotoStmt &computedGotoStmt) {
|
|
|
|
for (auto &i : std::get<std::list<parser::Label>>(computedGotoStmt.t)) {
|
|
|
|
checkLabelUse(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void LabelEnforce::Post(const parser::ArithmeticIfStmt &arithmeticIfStmt) {
|
|
|
|
checkLabelUse(std::get<1>(arithmeticIfStmt.t));
|
|
|
|
checkLabelUse(std::get<2>(arithmeticIfStmt.t));
|
|
|
|
checkLabelUse(std::get<3>(arithmeticIfStmt.t));
|
|
|
|
}
|
|
|
|
|
|
|
|
void LabelEnforce::Post(const parser::AssignStmt &assignStmt) {
|
|
|
|
checkLabelUse(std::get<parser::Label>(assignStmt.t));
|
|
|
|
}
|
|
|
|
|
|
|
|
void LabelEnforce::Post(const parser::AssignedGotoStmt &assignedGotoStmt) {
|
|
|
|
for (auto &i : std::get<std::list<parser::Label>>(assignedGotoStmt.t)) {
|
|
|
|
checkLabelUse(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void LabelEnforce::Post(const parser::AltReturnSpec &altReturnSpec) {
|
|
|
|
checkLabelUse(altReturnSpec.v);
|
|
|
|
}
|
|
|
|
|
|
|
|
void LabelEnforce::Post(const parser::ErrLabel &errLabel) {
|
|
|
|
checkLabelUse(errLabel.v);
|
|
|
|
}
|
|
|
|
void LabelEnforce::Post(const parser::EndLabel &endLabel) {
|
|
|
|
checkLabelUse(endLabel.v);
|
|
|
|
}
|
|
|
|
void LabelEnforce::Post(const parser::EorLabel &eorLabel) {
|
|
|
|
checkLabelUse(eorLabel.v);
|
|
|
|
}
|
|
|
|
|
|
|
|
void LabelEnforce::checkLabelUse(const parser::Label &labelUsed) {
|
|
|
|
if (labels_.find(labelUsed) == labels_.end()) {
|
|
|
|
SayWithConstruct(context_, currentStatementSourcePosition_,
|
|
|
|
parser::MessageFormattedText{
|
|
|
|
"Control flow escapes from %s"_err_en_US, construct_},
|
|
|
|
constructSourcePosition_);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
parser::MessageFormattedText LabelEnforce::GetEnclosingConstructMsg() {
|
|
|
|
return {"Enclosing %s statement"_en_US, construct_};
|
|
|
|
}
|
|
|
|
|
|
|
|
void LabelEnforce::SayWithConstruct(SemanticsContext &context,
|
|
|
|
parser::CharBlock stmtLocation, parser::MessageFormattedText &&message,
|
|
|
|
parser::CharBlock constructLocation) {
|
|
|
|
context.Say(stmtLocation, message)
|
|
|
|
.Attach(constructLocation, GetEnclosingConstructMsg());
|
|
|
|
}
|
2020-03-28 05:17:25 +08:00
|
|
|
} // namespace Fortran::semantics
|