2020-02-25 23:11:52 +08:00
|
|
|
//===-- lib/Semantics/expression.cpp --------------------------------------===//
|
2018-07-14 07:55:11 +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
|
2018-07-14 07:55:11 +08:00
|
|
|
//
|
2020-01-11 04:12:03 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2018-07-14 07:55:11 +08:00
|
|
|
|
2020-02-25 23:11:52 +08:00
|
|
|
#include "flang/Semantics/expression.h"
|
2019-10-10 07:30:34 +08:00
|
|
|
#include "check-call.h"
|
2020-01-07 01:16:18 +08:00
|
|
|
#include "pointer-assignment.h"
|
2022-05-27 07:56:27 +08:00
|
|
|
#include "resolve-names-utils.h"
|
2020-03-14 03:19:44 +08:00
|
|
|
#include "resolve-names.h"
|
2021-01-05 01:35:15 +08:00
|
|
|
#include "flang/Common/Fortran.h"
|
2020-02-25 23:11:52 +08:00
|
|
|
#include "flang/Common/idioms.h"
|
|
|
|
#include "flang/Evaluate/common.h"
|
|
|
|
#include "flang/Evaluate/fold.h"
|
|
|
|
#include "flang/Evaluate/tools.h"
|
|
|
|
#include "flang/Parser/characters.h"
|
|
|
|
#include "flang/Parser/dump-parse-tree.h"
|
|
|
|
#include "flang/Parser/parse-tree-visitor.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"
|
2020-02-28 23:11:03 +08:00
|
|
|
#include "llvm/Support/raw_ostream.h"
|
2019-03-12 06:39:11 +08:00
|
|
|
#include <algorithm>
|
2018-08-14 04:33:31 +08:00
|
|
|
#include <functional>
|
2018-09-01 04:28:21 +08:00
|
|
|
#include <optional>
|
2019-02-15 04:51:20 +08:00
|
|
|
#include <set>
|
2019-04-13 07:50:58 +08:00
|
|
|
|
2018-12-01 07:23:33 +08:00
|
|
|
// Typedef for optional generic expressions (ubiquitous in this file)
|
2018-12-01 06:03:05 +08:00
|
|
|
using MaybeExpr =
|
|
|
|
std::optional<Fortran::evaluate::Expr<Fortran::evaluate::SomeType>>;
|
|
|
|
|
2018-09-13 02:20:30 +08:00
|
|
|
// Much of the code that implements semantic analysis of expressions is
|
2020-02-25 23:11:52 +08:00
|
|
|
// tightly coupled with their typed representations in lib/Evaluate,
|
2018-09-13 02:20:30 +08:00
|
|
|
// and appears here in namespace Fortran::evaluate for convenience.
|
2018-09-01 04:28:21 +08:00
|
|
|
namespace Fortran::evaluate {
|
2018-07-14 07:55:11 +08:00
|
|
|
|
2019-11-07 07:54:26 +08:00
|
|
|
using common::LanguageFeature;
|
2019-11-03 00:56:46 +08:00
|
|
|
using common::NumericOperator;
|
2018-08-15 05:35:51 +08:00
|
|
|
using common::TypeCategory;
|
|
|
|
|
2019-11-03 00:56:46 +08:00
|
|
|
static inline std::string ToUpperCase(const std::string &str) {
|
|
|
|
return parser::ToUpperCaseLetters(str);
|
|
|
|
}
|
|
|
|
|
2019-01-23 08:30:32 +08:00
|
|
|
struct DynamicTypeWithLength : public DynamicType {
|
2019-03-12 06:39:11 +08:00
|
|
|
explicit DynamicTypeWithLength(const DynamicType &t) : DynamicType{t} {}
|
2019-02-27 07:59:25 +08:00
|
|
|
std::optional<Expr<SubscriptInteger>> LEN() const;
|
2019-01-23 08:30:32 +08:00
|
|
|
std::optional<Expr<SubscriptInteger>> length;
|
|
|
|
};
|
|
|
|
|
2019-02-27 07:59:25 +08:00
|
|
|
std::optional<Expr<SubscriptInteger>> DynamicTypeWithLength::LEN() const {
|
2019-11-10 01:29:31 +08:00
|
|
|
if (length) {
|
2019-02-27 07:59:25 +08:00
|
|
|
return length;
|
2021-06-03 08:13:55 +08:00
|
|
|
} else {
|
|
|
|
return GetCharLength();
|
2019-02-27 07:59:25 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-08 06:46:31 +08:00
|
|
|
static std::optional<DynamicTypeWithLength> AnalyzeTypeSpec(
|
2019-01-23 08:30:32 +08:00
|
|
|
const std::optional<parser::TypeSpec> &spec) {
|
2019-11-10 01:29:31 +08:00
|
|
|
if (spec) {
|
2019-01-23 08:30:32 +08:00
|
|
|
if (const semantics::DeclTypeSpec * typeSpec{spec->declTypeSpec}) {
|
|
|
|
// Name resolution sets TypeSpec::declTypeSpec only when it's valid
|
|
|
|
// (viz., an intrinsic type with valid known kind or a non-polymorphic
|
|
|
|
// & non-ABSTRACT derived type).
|
|
|
|
if (const semantics::IntrinsicTypeSpec *
|
|
|
|
intrinsic{typeSpec->AsIntrinsic()}) {
|
|
|
|
TypeCategory category{intrinsic->category()};
|
2019-02-27 07:59:25 +08:00
|
|
|
if (auto optKind{ToInt64(intrinsic->kind())}) {
|
|
|
|
int kind{static_cast<int>(*optKind)};
|
2019-01-23 08:30:32 +08:00
|
|
|
if (category == TypeCategory::Character) {
|
|
|
|
const semantics::CharacterTypeSpec &cts{
|
|
|
|
typeSpec->characterTypeSpec()};
|
2019-02-27 07:59:25 +08:00
|
|
|
const semantics::ParamValue &len{cts.length()};
|
2019-01-23 08:30:32 +08:00
|
|
|
// N.B. CHARACTER(LEN=*) is allowed in type-specs in ALLOCATE() &
|
|
|
|
// type guards, but not in array constructors.
|
2019-02-27 07:59:25 +08:00
|
|
|
return DynamicTypeWithLength{DynamicType{kind, len}};
|
|
|
|
} else {
|
|
|
|
return DynamicTypeWithLength{DynamicType{category, kind}};
|
2019-01-23 08:30:32 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (const semantics::DerivedTypeSpec *
|
|
|
|
derived{typeSpec->AsDerived()}) {
|
2019-02-27 07:59:25 +08:00
|
|
|
return DynamicTypeWithLength{DynamicType{*derived}};
|
2019-01-23 08:30:32 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
2022-02-12 08:58:01 +08:00
|
|
|
// Utilities to set a source location, if we have one, on an actual argument,
|
|
|
|
// when it is statically present.
|
|
|
|
static void SetArgSourceLocation(ActualArgument &x, parser::CharBlock at) {
|
|
|
|
x.set_sourceLocation(at);
|
|
|
|
}
|
|
|
|
static void SetArgSourceLocation(
|
|
|
|
std::optional<ActualArgument> &x, parser::CharBlock at) {
|
|
|
|
if (x) {
|
|
|
|
x->set_sourceLocation(at);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
static void SetArgSourceLocation(
|
|
|
|
std::optional<ActualArgument> &x, std::optional<parser::CharBlock> at) {
|
|
|
|
if (x && at) {
|
|
|
|
x->set_sourceLocation(*at);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-23 00:31:33 +08:00
|
|
|
class ArgumentAnalyzer {
|
|
|
|
public:
|
2020-01-11 06:09:39 +08:00
|
|
|
explicit ArgumentAnalyzer(ExpressionAnalyzer &context)
|
2020-10-31 04:28:10 +08:00
|
|
|
: context_{context}, source_{context.GetContextualMessages().at()},
|
|
|
|
isProcedureCall_{false} {}
|
2020-01-11 06:09:39 +08:00
|
|
|
ArgumentAnalyzer(ExpressionAnalyzer &context, parser::CharBlock source,
|
2020-08-19 01:47:52 +08:00
|
|
|
bool isProcedureCall = false)
|
|
|
|
: context_{context}, source_{source}, isProcedureCall_{isProcedureCall} {}
|
2019-11-03 00:56:46 +08:00
|
|
|
bool fatalErrors() const { return fatalErrors_; }
|
2019-10-23 00:31:33 +08:00
|
|
|
ActualArguments &&GetActuals() {
|
2019-11-03 00:56:46 +08:00
|
|
|
CHECK(!fatalErrors_);
|
2019-10-23 00:31:33 +08:00
|
|
|
return std::move(actuals_);
|
|
|
|
}
|
2019-11-23 08:46:11 +08:00
|
|
|
const Expr<SomeType> &GetExpr(std::size_t i) const {
|
2019-11-03 00:56:46 +08:00
|
|
|
return DEREF(actuals_.at(i).value().UnwrapExpr());
|
|
|
|
}
|
2019-11-23 08:46:11 +08:00
|
|
|
Expr<SomeType> &&MoveExpr(std::size_t i) {
|
|
|
|
return std::move(DEREF(actuals_.at(i).value().UnwrapExpr()));
|
|
|
|
}
|
2019-11-03 00:56:46 +08:00
|
|
|
void Analyze(const common::Indirection<parser::Expr> &x) {
|
|
|
|
Analyze(x.value());
|
|
|
|
}
|
|
|
|
void Analyze(const parser::Expr &x) {
|
|
|
|
actuals_.emplace_back(AnalyzeExpr(x));
|
2022-02-12 08:58:01 +08:00
|
|
|
SetArgSourceLocation(actuals_.back(), x.source);
|
2019-11-10 01:29:31 +08:00
|
|
|
fatalErrors_ |= !actuals_.back();
|
2019-10-23 00:31:33 +08:00
|
|
|
}
|
2019-11-23 08:46:11 +08:00
|
|
|
void Analyze(const parser::Variable &);
|
2019-10-23 00:31:33 +08:00
|
|
|
void Analyze(const parser::ActualArgSpec &, bool isSubroutine);
|
2021-09-08 03:17:31 +08:00
|
|
|
void ConvertBOZ(std::optional<DynamicType> &thisType, std::size_t i,
|
|
|
|
std::optional<DynamicType> otherType);
|
2019-10-23 00:31:33 +08:00
|
|
|
|
2021-09-08 03:17:31 +08:00
|
|
|
bool IsIntrinsicRelational(
|
|
|
|
RelationalOperator, const DynamicType &, const DynamicType &) const;
|
2019-11-03 00:56:46 +08:00
|
|
|
bool IsIntrinsicLogical() const;
|
2019-11-07 07:54:26 +08:00
|
|
|
bool IsIntrinsicNumeric(NumericOperator) const;
|
2019-11-03 00:56:46 +08:00
|
|
|
bool IsIntrinsicConcat() const;
|
|
|
|
|
2021-09-08 03:17:31 +08:00
|
|
|
bool CheckConformance();
|
2021-10-15 01:32:59 +08:00
|
|
|
bool CheckForNullPointer(const char *where = "as an operand here");
|
2020-09-01 03:06:41 +08:00
|
|
|
|
2019-11-07 07:54:26 +08:00
|
|
|
// Find and return a user-defined operator or report an error.
|
2019-11-03 00:56:46 +08:00
|
|
|
// The provided message is used if there is no such operator.
|
2021-09-08 03:17:31 +08:00
|
|
|
MaybeExpr TryDefinedOp(const char *, parser::MessageFixedText,
|
|
|
|
const Symbol **definedOpSymbolPtr = nullptr, bool isUserOp = false);
|
2020-03-28 05:17:25 +08:00
|
|
|
template <typename E>
|
2021-09-08 03:17:31 +08:00
|
|
|
MaybeExpr TryDefinedOp(E opr, parser::MessageFixedText msg) {
|
2019-11-07 07:54:26 +08:00
|
|
|
return TryDefinedOp(
|
2021-09-08 03:17:31 +08:00
|
|
|
context_.context().languageFeatures().GetNames(opr), msg);
|
2019-11-07 07:54:26 +08:00
|
|
|
}
|
2019-11-23 08:46:11 +08:00
|
|
|
// Find and return a user-defined assignment
|
|
|
|
std::optional<ProcedureRef> TryDefinedAssignment();
|
|
|
|
std::optional<ProcedureRef> GetDefinedAssignmentProc();
|
2020-07-16 07:02:49 +08:00
|
|
|
std::optional<DynamicType> GetType(std::size_t) const;
|
2020-02-28 23:11:03 +08:00
|
|
|
void Dump(llvm::raw_ostream &);
|
2019-11-03 00:56:46 +08:00
|
|
|
|
2019-10-23 00:31:33 +08:00
|
|
|
private:
|
2021-09-08 03:17:31 +08:00
|
|
|
MaybeExpr TryDefinedOp(std::vector<const char *>, parser::MessageFixedText);
|
2019-12-17 03:33:55 +08:00
|
|
|
MaybeExpr TryBoundOp(const Symbol &, int passIndex);
|
2019-11-03 00:56:46 +08:00
|
|
|
std::optional<ActualArgument> AnalyzeExpr(const parser::Expr &);
|
2020-10-02 03:08:04 +08:00
|
|
|
MaybeExpr AnalyzeExprOrWholeAssumedSizeArray(const parser::Expr &);
|
2019-11-03 00:56:46 +08:00
|
|
|
bool AreConformable() const;
|
2021-09-08 03:17:31 +08:00
|
|
|
const Symbol *FindBoundOp(
|
|
|
|
parser::CharBlock, int passIndex, const Symbol *&definedOp);
|
2020-05-10 00:10:08 +08:00
|
|
|
void AddAssignmentConversion(
|
|
|
|
const DynamicType &lhsType, const DynamicType &rhsType);
|
2019-12-17 03:33:55 +08:00
|
|
|
bool OkLogicalIntegerAssignment(TypeCategory lhs, TypeCategory rhs);
|
2019-12-03 00:55:44 +08:00
|
|
|
int GetRank(std::size_t) const;
|
2019-11-07 07:54:26 +08:00
|
|
|
bool IsBOZLiteral(std::size_t i) const {
|
2021-07-28 01:40:34 +08:00
|
|
|
return evaluate::IsBOZLiteral(GetExpr(i));
|
2019-11-07 07:54:26 +08:00
|
|
|
}
|
2019-11-23 08:46:11 +08:00
|
|
|
void SayNoMatch(const std::string &, bool isAssignment = false);
|
2019-11-03 00:56:46 +08:00
|
|
|
std::string TypeAsFortran(std::size_t);
|
2021-01-16 03:52:10 +08:00
|
|
|
bool AnyUntypedOrMissingOperand();
|
2019-10-23 00:31:33 +08:00
|
|
|
|
|
|
|
ExpressionAnalyzer &context_;
|
|
|
|
ActualArguments actuals_;
|
2019-11-03 00:56:46 +08:00
|
|
|
parser::CharBlock source_;
|
|
|
|
bool fatalErrors_{false};
|
2020-08-19 01:47:52 +08:00
|
|
|
const bool isProcedureCall_; // false for user-defined op or assignment
|
2019-10-23 00:31:33 +08:00
|
|
|
};
|
|
|
|
|
2019-03-12 06:39:11 +08:00
|
|
|
// Wraps a data reference in a typed Designator<>, and a procedure
|
|
|
|
// or procedure pointer reference in a ProcedureDesignator.
|
|
|
|
MaybeExpr ExpressionAnalyzer::Designate(DataRef &&ref) {
|
2021-06-16 06:14:16 +08:00
|
|
|
const Symbol &last{ref.GetLastSymbol()};
|
2021-06-26 01:35:03 +08:00
|
|
|
const Symbol &symbol{BypassGeneric(last).GetUltimate()};
|
2019-03-12 06:39:11 +08:00
|
|
|
if (semantics::IsProcedure(symbol)) {
|
|
|
|
if (auto *component{std::get_if<Component>(&ref.u)}) {
|
|
|
|
return Expr<SomeType>{ProcedureDesignator{std::move(*component)}};
|
2020-01-14 08:39:00 +08:00
|
|
|
} else if (!std::holds_alternative<SymbolRef>(ref.u)) {
|
|
|
|
DIE("unexpected alternative in DataRef");
|
|
|
|
} else if (!symbol.attrs().test(semantics::Attr::INTRINSIC)) {
|
2021-06-26 01:35:03 +08:00
|
|
|
if (symbol.has<semantics::GenericDetails>()) {
|
|
|
|
Say("'%s' is not a specific procedure"_err_en_US, symbol.name());
|
|
|
|
} else {
|
|
|
|
return Expr<SomeType>{ProcedureDesignator{symbol}};
|
|
|
|
}
|
2020-01-14 08:39:00 +08:00
|
|
|
} else if (auto interface{context_.intrinsics().IsSpecificIntrinsicFunction(
|
2021-10-26 03:43:17 +08:00
|
|
|
symbol.name().ToString())};
|
|
|
|
interface && !interface->isRestrictedSpecific) {
|
2020-01-14 08:39:00 +08:00
|
|
|
SpecificIntrinsic intrinsic{
|
|
|
|
symbol.name().ToString(), std::move(*interface)};
|
|
|
|
intrinsic.isRestrictedSpecific = interface->isRestrictedSpecific;
|
|
|
|
return Expr<SomeType>{ProcedureDesignator{std::move(intrinsic)}};
|
2019-03-12 06:39:11 +08:00
|
|
|
} else {
|
2021-10-26 03:43:17 +08:00
|
|
|
Say("'%s' is not an unrestricted specific intrinsic procedure"_err_en_US,
|
2020-01-14 08:39:00 +08:00
|
|
|
symbol.name());
|
2019-03-12 06:39:11 +08:00
|
|
|
}
|
2021-05-13 03:10:28 +08:00
|
|
|
return std::nullopt;
|
2021-06-16 06:14:16 +08:00
|
|
|
} else if (MaybeExpr result{AsGenericExpr(std::move(ref))}) {
|
|
|
|
return result;
|
2021-05-13 03:10:28 +08:00
|
|
|
} else {
|
2021-06-16 06:14:16 +08:00
|
|
|
if (!context_.HasError(last) && !context_.HasError(symbol)) {
|
|
|
|
AttachDeclaration(
|
|
|
|
Say("'%s' is not an object that can appear in an expression"_err_en_US,
|
|
|
|
last.name()),
|
|
|
|
symbol);
|
|
|
|
context_.SetError(last);
|
|
|
|
}
|
|
|
|
return std::nullopt;
|
2018-12-04 03:40:53 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Some subscript semantic checks must be deferred until all of the
|
2019-03-09 08:35:39 +08:00
|
|
|
// subscripts are in hand.
|
2019-03-08 06:46:31 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::CompleteSubscripts(ArrayRef &&ref) {
|
2019-03-09 04:55:57 +08:00
|
|
|
const Symbol &symbol{ref.GetLastSymbol().GetUltimate()};
|
2018-12-04 03:40:53 +08:00
|
|
|
int symbolRank{symbol.Rank()};
|
2019-06-26 04:07:32 +08:00
|
|
|
int subscripts{static_cast<int>(ref.size())};
|
2019-02-01 08:04:17 +08:00
|
|
|
if (subscripts == 0) {
|
2020-10-31 05:18:20 +08:00
|
|
|
return std::nullopt; // error recovery
|
2019-07-31 07:51:25 +08:00
|
|
|
} else if (subscripts != symbolRank) {
|
2020-02-28 10:00:45 +08:00
|
|
|
if (symbolRank != 0) {
|
|
|
|
Say("Reference to rank-%d object '%s' has %d subscripts"_err_en_US,
|
|
|
|
symbolRank, symbol.name(), subscripts);
|
|
|
|
}
|
2019-05-22 07:58:46 +08:00
|
|
|
return std::nullopt;
|
2020-10-31 05:18:20 +08:00
|
|
|
} else if (const auto *object{
|
|
|
|
symbol.detailsIf<semantics::ObjectEntityDetails>()}) {
|
2018-12-04 03:40:53 +08:00
|
|
|
// C928 & C1002
|
2019-02-01 08:04:17 +08:00
|
|
|
if (Triplet * last{std::get_if<Triplet>(&ref.subscript().back().u)}) {
|
2019-11-10 01:29:31 +08:00
|
|
|
if (!last->upper() && object->IsAssumedSize()) {
|
2019-03-08 06:46:31 +08:00
|
|
|
Say("Assumed-size array '%s' must have explicit final "
|
|
|
|
"subscript upper bound value"_err_en_US,
|
2019-05-07 00:33:45 +08:00
|
|
|
symbol.name());
|
2019-05-22 07:58:46 +08:00
|
|
|
return std::nullopt;
|
2018-12-04 03:40:53 +08:00
|
|
|
}
|
|
|
|
}
|
2020-10-31 05:18:20 +08:00
|
|
|
} else {
|
|
|
|
// Shouldn't get here from Analyze(ArrayElement) without a valid base,
|
|
|
|
// which, if not an object, must be a construct entity from
|
|
|
|
// SELECT TYPE/RANK or ASSOCIATE.
|
|
|
|
CHECK(symbol.has<semantics::AssocEntityDetails>());
|
2018-12-04 03:40:53 +08:00
|
|
|
}
|
|
|
|
return Designate(DataRef{std::move(ref)});
|
|
|
|
}
|
|
|
|
|
|
|
|
// Applies subscripts to a data reference.
|
2019-03-08 06:46:31 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::ApplySubscripts(
|
2018-12-04 03:40:53 +08:00
|
|
|
DataRef &&dataRef, std::vector<Subscript> &&subscripts) {
|
2020-10-31 05:18:20 +08:00
|
|
|
if (subscripts.empty()) {
|
|
|
|
return std::nullopt; // error recovery
|
|
|
|
}
|
2022-03-24 05:05:50 +08:00
|
|
|
return common::visit(
|
2018-12-04 03:40:53 +08:00
|
|
|
common::visitors{
|
2019-10-23 07:53:29 +08:00
|
|
|
[&](SymbolRef &&symbol) {
|
|
|
|
return CompleteSubscripts(ArrayRef{symbol, std::move(subscripts)});
|
2018-12-04 03:40:53 +08:00
|
|
|
},
|
2019-02-01 08:04:17 +08:00
|
|
|
[&](Component &&c) {
|
|
|
|
return CompleteSubscripts(
|
2019-03-08 06:46:31 +08:00
|
|
|
ArrayRef{std::move(c), std::move(subscripts)});
|
2019-02-01 08:04:17 +08:00
|
|
|
},
|
|
|
|
[&](auto &&) -> MaybeExpr {
|
2019-10-03 03:40:52 +08:00
|
|
|
DIE("bad base for ArrayRef");
|
2018-12-04 03:40:53 +08:00
|
|
|
return std::nullopt;
|
|
|
|
},
|
|
|
|
},
|
|
|
|
std::move(dataRef.u));
|
|
|
|
}
|
|
|
|
|
2022-06-07 14:55:31 +08:00
|
|
|
// C919a - only one part-ref of a data-ref may have rank > 0
|
|
|
|
bool ExpressionAnalyzer::CheckRanks(const DataRef &dataRef) {
|
|
|
|
return common::visit(
|
|
|
|
common::visitors{
|
|
|
|
[this](const Component &component) {
|
|
|
|
const Symbol &symbol{component.GetLastSymbol()};
|
|
|
|
if (int componentRank{symbol.Rank()}; componentRank > 0) {
|
|
|
|
if (int baseRank{component.base().Rank()}; baseRank > 0) {
|
|
|
|
Say("Reference to whole rank-%d component '%s' of rank-%d array of derived type is not allowed"_err_en_US,
|
|
|
|
componentRank, symbol.name(), baseRank);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return CheckRanks(component.base());
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
[this](const ArrayRef &arrayRef) {
|
|
|
|
if (const auto *component{arrayRef.base().UnwrapComponent()}) {
|
|
|
|
int subscriptRank{0};
|
|
|
|
for (const Subscript &subscript : arrayRef.subscript()) {
|
|
|
|
subscriptRank += subscript.Rank();
|
|
|
|
}
|
|
|
|
if (subscriptRank > 0) {
|
|
|
|
if (int componentBaseRank{component->base().Rank()};
|
|
|
|
componentBaseRank > 0) {
|
|
|
|
Say("Subscripts of component '%s' of rank-%d derived type array have rank %d but must all be scalar"_err_en_US,
|
|
|
|
component->GetLastSymbol().name(), componentBaseRank,
|
|
|
|
subscriptRank);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return CheckRanks(component->base());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
[](const SymbolRef &) { return true; },
|
|
|
|
[](const CoarrayRef &) { return true; },
|
|
|
|
},
|
|
|
|
dataRef.u);
|
2018-12-04 03:40:53 +08:00
|
|
|
}
|
|
|
|
|
2019-03-09 08:35:39 +08:00
|
|
|
// Parse tree correction after a substring S(j:k) was misparsed as an
|
|
|
|
// array section. N.B. Fortran substrings have to have a range, not a
|
|
|
|
// single index.
|
|
|
|
static void FixMisparsedSubstring(const parser::Designator &d) {
|
|
|
|
auto &mutate{const_cast<parser::Designator &>(d)};
|
|
|
|
if (auto *dataRef{std::get_if<parser::DataRef>(&mutate.u)}) {
|
|
|
|
if (auto *ae{std::get_if<common::Indirection<parser::ArrayElement>>(
|
|
|
|
&dataRef->u)}) {
|
|
|
|
parser::ArrayElement &arrElement{ae->value()};
|
|
|
|
if (!arrElement.subscripts.empty()) {
|
|
|
|
auto iter{arrElement.subscripts.begin()};
|
|
|
|
if (auto *triplet{std::get_if<parser::SubscriptTriplet>(&iter->u)}) {
|
2019-11-10 01:29:31 +08:00
|
|
|
if (!std::get<2>(triplet->t) /* no stride */ &&
|
2019-03-09 08:35:39 +08:00
|
|
|
++iter == arrElement.subscripts.end() /* one subscript */) {
|
|
|
|
if (Symbol *
|
2022-03-24 05:05:50 +08:00
|
|
|
symbol{common::visit(
|
2019-03-09 08:35:39 +08:00
|
|
|
common::visitors{
|
|
|
|
[](parser::Name &n) { return n.symbol; },
|
|
|
|
[](common::Indirection<parser::StructureComponent>
|
|
|
|
&sc) { return sc.value().component.symbol; },
|
|
|
|
[](auto &) -> Symbol * { return nullptr; },
|
|
|
|
},
|
|
|
|
arrElement.base.u)}) {
|
|
|
|
const Symbol &ultimate{symbol->GetUltimate()};
|
2019-03-15 07:28:06 +08:00
|
|
|
if (const semantics::DeclTypeSpec * type{ultimate.GetType()}) {
|
2019-03-10 02:24:27 +08:00
|
|
|
if (!ultimate.IsObjectArray() &&
|
|
|
|
type->category() == semantics::DeclTypeSpec::Character) {
|
|
|
|
// The ambiguous S(j:k) was parsed as an array section
|
|
|
|
// reference, but it's now clear that it's a substring.
|
|
|
|
// Fix the parse tree in situ.
|
|
|
|
mutate.u = arrElement.ConvertToSubstring();
|
2019-03-09 08:35:39 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-08 06:46:31 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(const parser::Designator &d) {
|
2019-12-12 07:06:24 +08:00
|
|
|
auto restorer{GetContextualMessages().SetLocation(d.source)};
|
2019-03-09 08:35:39 +08:00
|
|
|
FixMisparsedSubstring(d);
|
2018-09-20 05:27:13 +08:00
|
|
|
// These checks have to be deferred to these "top level" data-refs where
|
|
|
|
// we can be sure that there are no following subscripts (yet).
|
2019-03-08 06:46:31 +08:00
|
|
|
if (MaybeExpr result{Analyze(d.u)}) {
|
2019-12-18 07:59:25 +08:00
|
|
|
if (std::optional<DataRef> dataRef{ExtractDataRef(std::move(result))}) {
|
2022-06-07 14:55:31 +08:00
|
|
|
if (!CheckRanks(std::move(*dataRef))) {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
return Designate(std::move(*dataRef));
|
|
|
|
} else if (std::optional<DataRef> dataRef{
|
|
|
|
ExtractDataRef(std::move(result), /*intoSubstring=*/true)}) {
|
|
|
|
if (!CheckRanks(std::move(*dataRef))) {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
} else if (std::optional<DataRef> dataRef{ExtractDataRef(std::move(result),
|
|
|
|
/*intoSubstring=*/false, /*intoComplexPart=*/true)}) {
|
|
|
|
if (!CheckRanks(std::move(*dataRef))) {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
2018-09-19 00:34:59 +08:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
2018-12-01 08:04:51 +08:00
|
|
|
// A utility subroutine to repackage optional expressions of various levels
|
|
|
|
// of type specificity as fully general MaybeExpr values.
|
2020-03-28 05:17:25 +08:00
|
|
|
template <typename A> common::IfNoLvalue<MaybeExpr, A> AsMaybeExpr(A &&x) {
|
2020-06-19 08:17:04 +08:00
|
|
|
return AsGenericExpr(std::move(x));
|
2018-12-01 08:04:51 +08:00
|
|
|
}
|
2020-03-28 05:17:25 +08:00
|
|
|
template <typename A> MaybeExpr AsMaybeExpr(std::optional<A> &&x) {
|
2019-11-10 01:29:31 +08:00
|
|
|
if (x) {
|
2018-12-01 08:04:51 +08:00
|
|
|
return AsMaybeExpr(std::move(*x));
|
|
|
|
}
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
2018-09-19 00:34:59 +08:00
|
|
|
|
2019-01-10 07:06:07 +08:00
|
|
|
// Type kind parameter values for literal constants.
|
2019-03-08 06:46:31 +08:00
|
|
|
int ExpressionAnalyzer::AnalyzeKindParam(
|
2019-06-29 02:16:37 +08:00
|
|
|
const std::optional<parser::KindParam> &kindParam, int defaultKind) {
|
2019-11-10 01:29:31 +08:00
|
|
|
if (!kindParam) {
|
2018-09-01 04:28:21 +08:00
|
|
|
return defaultKind;
|
2018-08-08 03:34:09 +08:00
|
|
|
}
|
2022-03-24 05:05:50 +08:00
|
|
|
return common::visit(
|
2018-11-30 01:27:34 +08:00
|
|
|
common::visitors{
|
|
|
|
[](std::uint64_t k) { return static_cast<int>(k); },
|
2018-09-01 04:28:21 +08:00
|
|
|
[&](const parser::Scalar<
|
|
|
|
parser::Integer<parser::Constant<parser::Name>>> &n) {
|
2019-03-08 06:46:31 +08:00
|
|
|
if (MaybeExpr ie{Analyze(n)}) {
|
2018-10-27 06:10:24 +08:00
|
|
|
if (std::optional<std::int64_t> i64{ToInt64(*ie)}) {
|
|
|
|
int iv = *i64;
|
|
|
|
if (iv == *i64) {
|
|
|
|
return iv;
|
2018-09-01 04:28:21 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return defaultKind;
|
|
|
|
},
|
2018-11-30 01:27:34 +08:00
|
|
|
},
|
2018-09-01 04:28:21 +08:00
|
|
|
kindParam->u);
|
|
|
|
}
|
|
|
|
|
2018-08-11 02:44:43 +08:00
|
|
|
// Common handling of parser::IntLiteralConstant and SignedIntLiteralConstant
|
2019-03-23 05:27:18 +08:00
|
|
|
struct IntTypeVisitor {
|
|
|
|
using Result = MaybeExpr;
|
|
|
|
using Types = IntegerTypes;
|
2020-03-28 05:17:25 +08:00
|
|
|
template <typename T> Result Test() {
|
2019-05-31 04:31:11 +08:00
|
|
|
if (T::kind >= kind) {
|
2019-03-23 05:27:18 +08:00
|
|
|
const char *p{digits.begin()};
|
2022-05-25 06:06:12 +08:00
|
|
|
using Int = typename T::Scalar;
|
|
|
|
typename Int::ValueWithOverflow num{0, false};
|
|
|
|
if (isNegated) {
|
|
|
|
auto unsignedNum{Int::Read(p, 10, false /*unsigned*/)};
|
|
|
|
num.value = unsignedNum.value.Negate().value;
|
|
|
|
num.overflow = unsignedNum.overflow || num.value > Int{0};
|
|
|
|
if (!num.overflow && num.value.Negate().overflow &&
|
|
|
|
!analyzer.context().IsInModuleFile(digits)) {
|
|
|
|
analyzer.Say(digits,
|
|
|
|
"negated maximum INTEGER(KIND=%d) literal"_port_en_US, T::kind);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
num = Int::Read(p, 10, true /*signed*/);
|
|
|
|
}
|
|
|
|
if (!num.overflow) {
|
2019-05-31 04:31:11 +08:00
|
|
|
if (T::kind > kind) {
|
|
|
|
if (!isDefaultKind ||
|
2019-11-07 07:54:26 +08:00
|
|
|
!analyzer.context().IsEnabled(LanguageFeature::BigIntLiterals)) {
|
2019-05-31 04:31:11 +08:00
|
|
|
return std::nullopt;
|
|
|
|
} else if (analyzer.context().ShouldWarn(
|
2019-11-07 07:54:26 +08:00
|
|
|
LanguageFeature::BigIntLiterals)) {
|
2019-05-31 04:31:11 +08:00
|
|
|
analyzer.Say(digits,
|
|
|
|
"Integer literal is too large for default INTEGER(KIND=%d); "
|
2022-03-08 05:57:37 +08:00
|
|
|
"assuming INTEGER(KIND=%d)"_port_en_US,
|
2019-05-31 04:31:11 +08:00
|
|
|
kind, T::kind);
|
|
|
|
}
|
|
|
|
}
|
2019-03-23 05:27:18 +08:00
|
|
|
return Expr<SomeType>{
|
2022-05-25 06:06:12 +08:00
|
|
|
Expr<SomeInteger>{Expr<T>{Constant<T>{std::move(num.value)}}}};
|
2019-03-23 05:27:18 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
2019-05-31 04:31:11 +08:00
|
|
|
ExpressionAnalyzer &analyzer;
|
2019-03-23 05:27:18 +08:00
|
|
|
parser::CharBlock digits;
|
|
|
|
int kind;
|
2019-05-31 04:31:11 +08:00
|
|
|
bool isDefaultKind;
|
2022-05-25 06:06:12 +08:00
|
|
|
bool isNegated;
|
2019-03-23 05:27:18 +08:00
|
|
|
};
|
|
|
|
|
2020-03-28 05:17:25 +08:00
|
|
|
template <typename PARSED>
|
2022-05-25 06:06:12 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::IntLiteralConstant(
|
|
|
|
const PARSED &x, bool isNegated) {
|
2019-05-31 04:31:11 +08:00
|
|
|
const auto &kindParam{std::get<std::optional<parser::KindParam>>(x.t)};
|
2019-11-10 01:29:31 +08:00
|
|
|
bool isDefaultKind{!kindParam};
|
2019-05-31 04:31:11 +08:00
|
|
|
int kind{AnalyzeKindParam(kindParam, GetDefaultKind(TypeCategory::Integer))};
|
2019-03-23 05:27:18 +08:00
|
|
|
if (CheckIntrinsicKind(TypeCategory::Integer, kind)) {
|
|
|
|
auto digits{std::get<parser::CharBlock>(x.t)};
|
2019-05-31 04:31:11 +08:00
|
|
|
if (MaybeExpr result{common::SearchTypes(
|
2022-05-25 06:06:12 +08:00
|
|
|
IntTypeVisitor{*this, digits, kind, isDefaultKind, isNegated})}) {
|
2019-03-23 05:27:18 +08:00
|
|
|
return result;
|
2019-05-31 04:31:11 +08:00
|
|
|
} else if (isDefaultKind) {
|
|
|
|
Say(digits,
|
|
|
|
"Integer literal is too large for any allowable "
|
|
|
|
"kind of INTEGER"_err_en_US);
|
2019-03-23 05:27:18 +08:00
|
|
|
} else {
|
2019-05-31 04:31:11 +08:00
|
|
|
Say(digits, "Integer literal is too large for INTEGER(KIND=%d)"_err_en_US,
|
2019-03-23 05:27:18 +08:00
|
|
|
kind);
|
|
|
|
}
|
2018-07-14 07:55:11 +08:00
|
|
|
}
|
2019-03-23 05:27:18 +08:00
|
|
|
return std::nullopt;
|
2018-07-14 07:55:11 +08:00
|
|
|
}
|
|
|
|
|
2022-05-25 06:06:12 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(
|
|
|
|
const parser::IntLiteralConstant &x, bool isNegated) {
|
2020-03-02 23:59:29 +08:00
|
|
|
auto restorer{
|
|
|
|
GetContextualMessages().SetLocation(std::get<parser::CharBlock>(x.t))};
|
2022-05-25 06:06:12 +08:00
|
|
|
return IntLiteralConstant(x, isNegated);
|
2018-08-11 02:44:43 +08:00
|
|
|
}
|
|
|
|
|
2019-03-08 06:46:31 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(
|
2018-12-04 03:40:53 +08:00
|
|
|
const parser::SignedIntLiteralConstant &x) {
|
2020-03-02 23:59:29 +08:00
|
|
|
auto restorer{GetContextualMessages().SetLocation(x.source)};
|
2019-03-08 06:46:31 +08:00
|
|
|
return IntLiteralConstant(x);
|
2018-08-11 02:44:43 +08:00
|
|
|
}
|
|
|
|
|
2020-03-28 05:17:25 +08:00
|
|
|
template <typename TYPE>
|
2018-09-01 04:28:21 +08:00
|
|
|
Constant<TYPE> ReadRealLiteral(
|
|
|
|
parser::CharBlock source, FoldingContext &context) {
|
2018-08-09 07:30:58 +08:00
|
|
|
const char *p{source.begin()};
|
2022-07-02 02:40:44 +08:00
|
|
|
auto valWithFlags{
|
|
|
|
Scalar<TYPE>::Read(p, context.targetCharacteristics().roundingMode())};
|
2018-08-09 07:30:58 +08:00
|
|
|
CHECK(p == source.end());
|
2018-09-01 04:28:21 +08:00
|
|
|
RealFlagWarnings(context, valWithFlags.flags, "conversion of REAL literal");
|
2018-08-09 07:30:58 +08:00
|
|
|
auto value{valWithFlags.value};
|
2022-07-02 02:40:44 +08:00
|
|
|
if (context.targetCharacteristics().areSubnormalsFlushedToZero()) {
|
2019-01-30 08:47:41 +08:00
|
|
|
value = value.FlushSubnormalToZero();
|
2018-08-09 07:30:58 +08:00
|
|
|
}
|
2018-09-01 04:28:21 +08:00
|
|
|
return {value};
|
2018-08-09 07:30:58 +08:00
|
|
|
}
|
|
|
|
|
2018-09-01 04:28:21 +08:00
|
|
|
struct RealTypeVisitor {
|
|
|
|
using Result = std::optional<Expr<SomeReal>>;
|
2019-01-23 08:30:32 +08:00
|
|
|
using Types = RealTypes;
|
2018-09-01 04:28:21 +08:00
|
|
|
|
|
|
|
RealTypeVisitor(int k, parser::CharBlock lit, FoldingContext &ctx)
|
2020-03-28 05:17:25 +08:00
|
|
|
: kind{k}, literal{lit}, context{ctx} {}
|
2018-09-01 04:28:21 +08:00
|
|
|
|
2020-03-28 05:17:25 +08:00
|
|
|
template <typename T> Result Test() {
|
2019-01-23 08:30:32 +08:00
|
|
|
if (kind == T::kind) {
|
|
|
|
return {AsCategoryExpr(ReadRealLiteral<T>(literal, context))};
|
2018-08-29 06:15:18 +08:00
|
|
|
}
|
2018-09-01 04:28:21 +08:00
|
|
|
return std::nullopt;
|
2018-08-23 04:36:45 +08:00
|
|
|
}
|
2018-08-29 06:15:18 +08:00
|
|
|
|
2018-09-01 04:28:21 +08:00
|
|
|
int kind;
|
2018-08-23 04:36:45 +08:00
|
|
|
parser::CharBlock literal;
|
2018-09-01 04:28:21 +08:00
|
|
|
FoldingContext &context;
|
2018-08-23 04:36:45 +08:00
|
|
|
};
|
|
|
|
|
2018-12-04 03:40:53 +08:00
|
|
|
// Reads a real literal constant and encodes it with the right kind.
|
2019-03-08 06:46:31 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(const parser::RealLiteralConstant &x) {
|
2018-09-01 04:28:21 +08:00
|
|
|
// Use a local message context around the real literal for better
|
|
|
|
// provenance on any messages.
|
2019-12-12 07:06:24 +08:00
|
|
|
auto restorer{GetContextualMessages().SetLocation(x.real.source)};
|
2020-02-27 12:19:48 +08:00
|
|
|
// If a kind parameter appears, it defines the kind of the literal and the
|
|
|
|
// letter used in an exponent part must be 'E' (e.g., the 'E' in
|
|
|
|
// "6.02214E+23"). In the absence of an explicit kind parameter, any
|
|
|
|
// exponent letter determines the kind. Otherwise, defaults apply.
|
2019-06-13 03:38:04 +08:00
|
|
|
auto &defaults{context_.defaultKinds()};
|
|
|
|
int defaultKind{defaults.GetDefaultKind(TypeCategory::Real)};
|
2018-08-09 07:30:58 +08:00
|
|
|
const char *end{x.real.source.end()};
|
2019-07-12 07:09:02 +08:00
|
|
|
char expoLetter{' '};
|
2018-09-01 04:28:21 +08:00
|
|
|
std::optional<int> letterKind;
|
2018-08-09 07:30:58 +08:00
|
|
|
for (const char *p{x.real.source.begin()}; p < end; ++p) {
|
|
|
|
if (parser::IsLetter(*p)) {
|
2019-07-12 07:09:02 +08:00
|
|
|
expoLetter = *p;
|
|
|
|
switch (expoLetter) {
|
2020-03-28 05:17:25 +08:00
|
|
|
case 'e':
|
|
|
|
letterKind = defaults.GetDefaultKind(TypeCategory::Real);
|
|
|
|
break;
|
|
|
|
case 'd':
|
|
|
|
letterKind = defaults.doublePrecisionKind();
|
|
|
|
break;
|
|
|
|
case 'q':
|
|
|
|
letterKind = defaults.quadPrecisionKind();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
Say("Unknown exponent letter '%c'"_err_en_US, expoLetter);
|
2018-08-09 07:30:58 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2019-11-10 01:29:31 +08:00
|
|
|
if (letterKind) {
|
2018-09-01 04:28:21 +08:00
|
|
|
defaultKind = *letterKind;
|
|
|
|
}
|
2022-05-28 06:27:59 +08:00
|
|
|
// C716 requires 'E' as an exponent.
|
|
|
|
// Extension: allow exponent-letter matching the kind-param.
|
2019-03-08 06:46:31 +08:00
|
|
|
auto kind{AnalyzeKindParam(x.kind, defaultKind)};
|
2022-05-28 06:27:59 +08:00
|
|
|
if (letterKind && expoLetter != 'e') {
|
|
|
|
if (kind != *letterKind) {
|
|
|
|
Say("Explicit kind parameter on real constant disagrees with "
|
|
|
|
"exponent letter '%c'"_warn_en_US,
|
|
|
|
expoLetter);
|
|
|
|
} else if (x.kind &&
|
|
|
|
context_.ShouldWarn(
|
|
|
|
common::LanguageFeature::ExponentMatchingKindParam)) {
|
|
|
|
Say("Explicit kind parameter together with non-'E' exponent letter "
|
|
|
|
"is not standard"_port_en_US);
|
|
|
|
}
|
2018-08-09 07:30:58 +08:00
|
|
|
}
|
2019-01-23 08:30:32 +08:00
|
|
|
auto result{common::SearchTypes(
|
2019-03-08 06:46:31 +08:00
|
|
|
RealTypeVisitor{kind, x.real.source, GetFoldingContext()})};
|
2020-03-28 05:17:25 +08:00
|
|
|
if (!result) { // C717
|
2019-05-14 00:33:18 +08:00
|
|
|
Say("Unsupported REAL(KIND=%d)"_err_en_US, kind);
|
2018-09-01 04:28:21 +08:00
|
|
|
}
|
|
|
|
return AsMaybeExpr(std::move(result));
|
2018-08-09 07:30:58 +08:00
|
|
|
}
|
|
|
|
|
2019-03-08 06:46:31 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(
|
2018-12-04 03:40:53 +08:00
|
|
|
const parser::SignedRealLiteralConstant &x) {
|
2019-03-08 06:46:31 +08:00
|
|
|
if (auto result{Analyze(std::get<parser::RealLiteralConstant>(x.t))}) {
|
|
|
|
auto &realExpr{std::get<Expr<SomeReal>>(result->u)};
|
2018-08-11 02:44:43 +08:00
|
|
|
if (auto sign{std::get<std::optional<parser::Sign>>(x.t)}) {
|
|
|
|
if (sign == parser::Sign::Negative) {
|
2020-06-19 08:17:04 +08:00
|
|
|
return AsGenericExpr(-std::move(realExpr));
|
2018-08-11 02:44:43 +08:00
|
|
|
}
|
|
|
|
}
|
2018-08-17 02:46:18 +08:00
|
|
|
return result;
|
2018-08-11 02:44:43 +08:00
|
|
|
}
|
2018-08-17 02:46:18 +08:00
|
|
|
return std::nullopt;
|
2018-08-11 02:44:43 +08:00
|
|
|
}
|
|
|
|
|
2020-03-02 23:59:29 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(
|
|
|
|
const parser::SignedComplexLiteralConstant &x) {
|
|
|
|
auto result{Analyze(std::get<parser::ComplexLiteralConstant>(x.t))};
|
|
|
|
if (!result) {
|
|
|
|
return std::nullopt;
|
|
|
|
} else if (std::get<parser::Sign>(x.t) == parser::Sign::Negative) {
|
|
|
|
return AsGenericExpr(-std::move(std::get<Expr<SomeComplex>>(result->u)));
|
|
|
|
} else {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-08 06:46:31 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(const parser::ComplexPart &x) {
|
|
|
|
return Analyze(x.u);
|
2018-08-08 03:34:09 +08:00
|
|
|
}
|
|
|
|
|
2019-03-08 06:46:31 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(const parser::ComplexLiteralConstant &z) {
|
|
|
|
return AsMaybeExpr(
|
|
|
|
ConstructComplex(GetContextualMessages(), Analyze(std::get<0>(z.t)),
|
|
|
|
Analyze(std::get<1>(z.t)), GetDefaultKind(TypeCategory::Real)));
|
2018-12-04 03:40:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// CHARACTER literal processing.
|
2019-03-08 06:46:31 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::AnalyzeString(std::string &&string, int kind) {
|
|
|
|
if (!CheckIntrinsicKind(TypeCategory::Character, kind)) {
|
2018-11-06 05:48:00 +08:00
|
|
|
return std::nullopt;
|
|
|
|
}
|
2019-06-19 03:34:23 +08:00
|
|
|
switch (kind) {
|
|
|
|
case 1:
|
|
|
|
return AsGenericExpr(Constant<Type<TypeCategory::Character, 1>>{
|
2019-06-29 02:16:37 +08:00
|
|
|
parser::DecodeString<std::string, parser::Encoding::LATIN_1>(
|
|
|
|
string, true)});
|
2019-06-19 03:34:23 +08:00
|
|
|
case 2:
|
2019-06-18 07:12:28 +08:00
|
|
|
return AsGenericExpr(Constant<Type<TypeCategory::Character, 2>>{
|
2019-06-29 02:16:37 +08:00
|
|
|
parser::DecodeString<std::u16string, parser::Encoding::UTF_8>(
|
|
|
|
string, true)});
|
2019-06-19 03:34:23 +08:00
|
|
|
case 4:
|
2019-06-18 07:12:28 +08:00
|
|
|
return AsGenericExpr(Constant<Type<TypeCategory::Character, 4>>{
|
2019-06-29 02:16:37 +08:00
|
|
|
parser::DecodeString<std::u32string, parser::Encoding::UTF_8>(
|
|
|
|
string, true)});
|
2020-03-28 05:17:25 +08:00
|
|
|
default:
|
|
|
|
CRASH_NO_CASE;
|
2018-11-06 05:48:00 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-08 06:46:31 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(const parser::CharLiteralConstant &x) {
|
|
|
|
int kind{
|
|
|
|
AnalyzeKindParam(std::get<std::optional<parser::KindParam>>(x.t), 1)};
|
2018-09-01 04:28:21 +08:00
|
|
|
auto value{std::get<std::string>(x.t)};
|
2019-03-08 06:46:31 +08:00
|
|
|
return AnalyzeString(std::move(value), kind);
|
2018-08-08 03:34:09 +08:00
|
|
|
}
|
|
|
|
|
2019-03-08 06:46:31 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(
|
2018-12-04 03:40:53 +08:00
|
|
|
const parser::HollerithLiteralConstant &x) {
|
2019-03-08 06:46:31 +08:00
|
|
|
int kind{GetDefaultKind(TypeCategory::Character)};
|
2018-12-04 03:40:53 +08:00
|
|
|
auto value{x.v};
|
2019-03-08 06:46:31 +08:00
|
|
|
return AnalyzeString(std::move(value), kind);
|
2018-12-04 03:40:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// .TRUE. and .FALSE. of various kinds
|
2019-03-08 06:46:31 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(const parser::LogicalLiteralConstant &x) {
|
|
|
|
auto kind{AnalyzeKindParam(std::get<std::optional<parser::KindParam>>(x.t),
|
|
|
|
GetDefaultKind(TypeCategory::Logical))};
|
2018-09-01 04:28:21 +08:00
|
|
|
bool value{std::get<bool>(x.t)};
|
2019-01-23 08:30:32 +08:00
|
|
|
auto result{common::SearchTypes(
|
2018-09-12 08:06:44 +08:00
|
|
|
TypeKindVisitor<TypeCategory::Logical, Constant, bool>{
|
2018-09-01 04:28:21 +08:00
|
|
|
kind, std::move(value)})};
|
2019-11-10 01:29:31 +08:00
|
|
|
if (!result) {
|
2020-03-28 05:17:25 +08:00
|
|
|
Say("unsupported LOGICAL(KIND=%d)"_err_en_US, kind); // C728
|
2018-09-01 04:28:21 +08:00
|
|
|
}
|
2018-09-12 08:06:44 +08:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2018-12-04 03:40:53 +08:00
|
|
|
// BOZ typeless literals
|
2019-03-08 06:46:31 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(const parser::BOZLiteralConstant &x) {
|
2019-05-07 00:33:45 +08:00
|
|
|
const char *p{x.v.c_str()};
|
2018-09-13 02:20:30 +08:00
|
|
|
std::uint64_t base{16};
|
|
|
|
switch (*p++) {
|
2020-03-28 05:17:25 +08:00
|
|
|
case 'b':
|
|
|
|
base = 2;
|
|
|
|
break;
|
|
|
|
case 'o':
|
|
|
|
base = 8;
|
|
|
|
break;
|
|
|
|
case 'z':
|
|
|
|
break;
|
|
|
|
case 'x':
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
CRASH_NO_CASE;
|
2018-09-13 02:20:30 +08:00
|
|
|
}
|
2018-12-04 03:40:53 +08:00
|
|
|
CHECK(*p == '"');
|
2019-03-23 05:27:18 +08:00
|
|
|
++p;
|
|
|
|
auto value{BOZLiteralConstant::Read(p, base, false /*unsigned*/)};
|
2018-12-04 03:40:53 +08:00
|
|
|
if (*p != '"') {
|
2020-07-24 16:19:29 +08:00
|
|
|
Say("Invalid digit ('%c') in BOZ literal '%s'"_err_en_US, *p,
|
|
|
|
x.v); // C7107, C7108
|
2018-12-04 03:40:53 +08:00
|
|
|
return std::nullopt;
|
2018-09-20 05:27:13 +08:00
|
|
|
}
|
2018-12-04 03:40:53 +08:00
|
|
|
if (value.overflow) {
|
2019-05-07 00:33:45 +08:00
|
|
|
Say("BOZ literal '%s' too large"_err_en_US, x.v);
|
2018-12-04 03:40:53 +08:00
|
|
|
return std::nullopt;
|
|
|
|
}
|
2019-12-21 05:03:30 +08:00
|
|
|
return AsGenericExpr(std::move(value.value));
|
2018-09-20 05:27:13 +08:00
|
|
|
}
|
|
|
|
|
2018-12-04 03:40:53 +08:00
|
|
|
// Names and named constants
|
2019-03-08 06:46:31 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(const parser::Name &n) {
|
2020-10-31 04:28:10 +08:00
|
|
|
auto restorer{GetContextualMessages().SetLocation(n.source)};
|
Rework DATA statement semantics to use typed expressions
Summary:
Updates recent work on DATA statement semantic checking in
flang/lib/Semantics/check-data.{h,cpp} to use the compiler's
internal representation for typed expressions rather than working
on the raw parse tree. Saves the analyzed expressions for DATA
statement values as parse tree decorations because they'll soon be
needed in lowering. Corrects wording of some error messages.
Fixes a bug in constant expression checking: structure constructors
are not constant expressions if they set an allocatable component
to anything other than NULL.
Includes infrastructure changes to make this work, some renaming
to reflect the fact that the implied DO loop indices tracked by
expression analysis are not (just) from array constructors, remove
some dead code, and improve some comments.
Reviewers: tskeith, sscalpone, jdoerfert, DavidTruby, anchu-rajendran, schweitz
Reviewed By: tskeith, anchu-rajendran, schweitz
Subscribers: llvm-commits, flang-commits
Tags: #flang, #llvm
Differential Revision: https://reviews.llvm.org/D78834
2020-04-25 04:54:11 +08:00
|
|
|
if (std::optional<int> kind{IsImpliedDo(n.source)}) {
|
2019-01-23 08:30:32 +08:00
|
|
|
return AsMaybeExpr(ConvertToKind<TypeCategory::Integer>(
|
|
|
|
*kind, AsExpr(ImpliedDoIndex{n.source})));
|
2022-04-14 01:39:16 +08:00
|
|
|
}
|
|
|
|
if (context_.HasError(n.symbol)) { // includes case of no symbol
|
2019-09-13 00:24:04 +08:00
|
|
|
return std::nullopt;
|
|
|
|
} else {
|
2019-03-09 04:55:57 +08:00
|
|
|
const Symbol &ultimate{n.symbol->GetUltimate()};
|
2019-09-13 04:43:16 +08:00
|
|
|
if (ultimate.has<semantics::TypeParamDetails>()) {
|
2019-03-09 04:55:57 +08:00
|
|
|
// A bare reference to a derived type parameter (within a parameterized
|
|
|
|
// derived type definition)
|
2020-08-26 00:40:20 +08:00
|
|
|
return Fold(ConvertToType(
|
|
|
|
ultimate, AsGenericExpr(TypeParamInquiry{std::nullopt, ultimate})));
|
2019-03-09 04:55:57 +08:00
|
|
|
} else {
|
2019-11-13 07:43:09 +08:00
|
|
|
if (n.symbol->attrs().test(semantics::Attr::VOLATILE)) {
|
|
|
|
if (const semantics::Scope *
|
|
|
|
pure{semantics::FindPureProcedureContaining(
|
2019-11-20 11:10:02 +08:00
|
|
|
context_.FindScope(n.source))}) {
|
2019-11-13 07:43:09 +08:00
|
|
|
SayAt(n,
|
2019-12-24 09:12:53 +08:00
|
|
|
"VOLATILE variable '%s' may not be referenced in pure subprogram '%s'"_err_en_US,
|
2019-11-13 07:43:09 +08:00
|
|
|
n.source, DEREF(pure->symbol()).name());
|
|
|
|
n.symbol->attrs().reset(semantics::Attr::VOLATILE);
|
|
|
|
}
|
|
|
|
}
|
2020-10-02 03:08:04 +08:00
|
|
|
if (!isWholeAssumedSizeArrayOk_ &&
|
|
|
|
semantics::IsAssumedSizeArray(*n.symbol)) { // C1002, C1014, C1231
|
|
|
|
AttachDeclaration(
|
|
|
|
SayAt(n,
|
|
|
|
"Whole assumed-size array '%s' may not appear here without subscripts"_err_en_US,
|
|
|
|
n.source),
|
|
|
|
*n.symbol);
|
|
|
|
}
|
2019-09-13 04:43:16 +08:00
|
|
|
return Designate(DataRef{*n.symbol});
|
2018-11-07 09:18:06 +08:00
|
|
|
}
|
2018-09-01 04:28:21 +08:00
|
|
|
}
|
2018-08-08 03:34:09 +08:00
|
|
|
}
|
|
|
|
|
2019-03-08 06:46:31 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(const parser::NamedConstant &n) {
|
2020-10-31 04:28:10 +08:00
|
|
|
auto restorer{GetContextualMessages().SetLocation(n.v.source)};
|
2019-03-08 06:46:31 +08:00
|
|
|
if (MaybeExpr value{Analyze(n.v)}) {
|
2020-01-04 02:38:51 +08:00
|
|
|
Expr<SomeType> folded{Fold(std::move(*value))};
|
2019-01-08 02:15:27 +08:00
|
|
|
if (IsConstantExpr(folded)) {
|
2020-06-20 00:16:21 +08:00
|
|
|
return folded;
|
2018-09-13 02:20:30 +08:00
|
|
|
}
|
2020-03-28 05:17:25 +08:00
|
|
|
Say(n.v.source, "must be a constant"_err_en_US); // C718
|
2018-12-04 03:40:53 +08:00
|
|
|
}
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
2020-11-18 05:15:34 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(const parser::NullInit &n) {
|
|
|
|
if (MaybeExpr value{Analyze(n.v)}) {
|
|
|
|
// Subtle: when the NullInit is a DataStmtConstant, it might
|
|
|
|
// be a misparse of a structure constructor without parameters
|
|
|
|
// or components (e.g., T()). Checking the result to ensure
|
|
|
|
// that a "=>" data entity initializer actually resolved to
|
|
|
|
// a null pointer has to be done by the caller.
|
|
|
|
return Fold(std::move(*value));
|
|
|
|
}
|
|
|
|
return std::nullopt;
|
Rework DATA statement semantics to use typed expressions
Summary:
Updates recent work on DATA statement semantic checking in
flang/lib/Semantics/check-data.{h,cpp} to use the compiler's
internal representation for typed expressions rather than working
on the raw parse tree. Saves the analyzed expressions for DATA
statement values as parse tree decorations because they'll soon be
needed in lowering. Corrects wording of some error messages.
Fixes a bug in constant expression checking: structure constructors
are not constant expressions if they set an allocatable component
to anything other than NULL.
Includes infrastructure changes to make this work, some renaming
to reflect the fact that the implied DO loop indices tracked by
expression analysis are not (just) from array constructors, remove
some dead code, and improve some comments.
Reviewers: tskeith, sscalpone, jdoerfert, DavidTruby, anchu-rajendran, schweitz
Reviewed By: tskeith, anchu-rajendran, schweitz
Subscribers: llvm-commits, flang-commits
Tags: #flang, #llvm
Differential Revision: https://reviews.llvm.org/D78834
2020-04-25 04:54:11 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(const parser::InitialDataTarget &x) {
|
|
|
|
return Analyze(x.value());
|
|
|
|
}
|
|
|
|
|
2020-06-19 08:17:04 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(const parser::DataStmtValue &x) {
|
|
|
|
if (const auto &repeat{
|
|
|
|
std::get<std::optional<parser::DataStmtRepeat>>(x.t)}) {
|
2020-06-20 00:16:21 +08:00
|
|
|
x.repetitions = -1;
|
2020-06-19 08:17:04 +08:00
|
|
|
if (MaybeExpr expr{Analyze(repeat->u)}) {
|
|
|
|
Expr<SomeType> folded{Fold(std::move(*expr))};
|
|
|
|
if (auto value{ToInt64(folded)}) {
|
|
|
|
if (*value >= 0) { // C882
|
|
|
|
x.repetitions = *value;
|
|
|
|
} else {
|
|
|
|
Say(FindSourceLocation(repeat),
|
|
|
|
"Repeat count (%jd) for data value must not be negative"_err_en_US,
|
|
|
|
*value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return Analyze(std::get<parser::DataStmtConstant>(x.t));
|
|
|
|
}
|
|
|
|
|
2018-12-04 03:40:53 +08:00
|
|
|
// Substring references
|
2019-03-08 06:46:31 +08:00
|
|
|
std::optional<Expr<SubscriptInteger>> ExpressionAnalyzer::GetSubstringBound(
|
2018-12-04 03:40:53 +08:00
|
|
|
const std::optional<parser::ScalarIntExpr> &bound) {
|
2019-11-10 01:29:31 +08:00
|
|
|
if (bound) {
|
2019-03-08 06:46:31 +08:00
|
|
|
if (MaybeExpr expr{Analyze(*bound)}) {
|
2018-12-04 03:40:53 +08:00
|
|
|
if (expr->Rank() > 1) {
|
2019-03-08 06:46:31 +08:00
|
|
|
Say("substring bound expression has rank %d"_err_en_US, expr->Rank());
|
2018-12-04 03:40:53 +08:00
|
|
|
}
|
|
|
|
if (auto *intExpr{std::get_if<Expr<SomeInteger>>(&expr->u)}) {
|
|
|
|
if (auto *ssIntExpr{std::get_if<Expr<SubscriptInteger>>(&intExpr->u)}) {
|
|
|
|
return {std::move(*ssIntExpr)};
|
|
|
|
}
|
|
|
|
return {Expr<SubscriptInteger>{
|
|
|
|
Convert<SubscriptInteger, TypeCategory::Integer>{
|
|
|
|
std::move(*intExpr)}}};
|
|
|
|
} else {
|
2019-03-08 06:46:31 +08:00
|
|
|
Say("substring bound expression is not INTEGER"_err_en_US);
|
2018-12-04 03:40:53 +08:00
|
|
|
}
|
|
|
|
}
|
2018-09-13 02:20:30 +08:00
|
|
|
}
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
2019-03-08 06:46:31 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(const parser::Substring &ss) {
|
|
|
|
if (MaybeExpr baseExpr{Analyze(std::get<parser::DataRef>(ss.t))}) {
|
2018-09-20 05:27:13 +08:00
|
|
|
if (std::optional<DataRef> dataRef{ExtractDataRef(std::move(*baseExpr))}) {
|
2022-06-07 14:55:31 +08:00
|
|
|
if (MaybeExpr newBaseExpr{Designate(std::move(*dataRef))}) {
|
2018-09-20 05:27:13 +08:00
|
|
|
if (std::optional<DataRef> checked{
|
|
|
|
ExtractDataRef(std::move(*newBaseExpr))}) {
|
|
|
|
const parser::SubstringRange &range{
|
|
|
|
std::get<parser::SubstringRange>(ss.t)};
|
|
|
|
std::optional<Expr<SubscriptInteger>> first{
|
2019-03-08 06:46:31 +08:00
|
|
|
GetSubstringBound(std::get<0>(range.t))};
|
2018-09-20 05:27:13 +08:00
|
|
|
std::optional<Expr<SubscriptInteger>> last{
|
2019-03-08 06:46:31 +08:00
|
|
|
GetSubstringBound(std::get<1>(range.t))};
|
2018-11-02 02:18:12 +08:00
|
|
|
const Symbol &symbol{checked->GetLastSymbol()};
|
2019-05-07 00:33:45 +08:00
|
|
|
if (std::optional<DynamicType> dynamicType{
|
|
|
|
DynamicType::From(symbol)}) {
|
2019-05-14 00:33:18 +08:00
|
|
|
if (dynamicType->category() == TypeCategory::Character) {
|
2018-09-21 03:34:29 +08:00
|
|
|
return WrapperHelper<TypeCategory::Character, Designator,
|
2019-05-14 00:33:18 +08:00
|
|
|
Substring>(dynamicType->kind(),
|
2018-12-06 05:03:39 +08:00
|
|
|
Substring{std::move(checked.value()), std::move(first),
|
|
|
|
std::move(last)});
|
2018-09-20 05:27:13 +08:00
|
|
|
}
|
|
|
|
}
|
2019-03-08 06:46:31 +08:00
|
|
|
Say("substring may apply only to CHARACTER"_err_en_US);
|
2018-09-20 05:27:13 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-09-08 01:33:32 +08:00
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
2018-12-04 03:40:53 +08:00
|
|
|
// CHARACTER literal substrings
|
2019-03-08 06:46:31 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(
|
2018-12-04 03:40:53 +08:00
|
|
|
const parser::CharLiteralConstantSubstring &x) {
|
|
|
|
const parser::SubstringRange &range{std::get<parser::SubstringRange>(x.t)};
|
|
|
|
std::optional<Expr<SubscriptInteger>> lower{
|
2019-03-08 06:46:31 +08:00
|
|
|
GetSubstringBound(std::get<0>(range.t))};
|
2018-12-04 03:40:53 +08:00
|
|
|
std::optional<Expr<SubscriptInteger>> upper{
|
2019-03-08 06:46:31 +08:00
|
|
|
GetSubstringBound(std::get<1>(range.t))};
|
|
|
|
if (MaybeExpr string{Analyze(std::get<parser::CharLiteralConstant>(x.t))}) {
|
2018-12-04 03:40:53 +08:00
|
|
|
if (auto *charExpr{std::get_if<Expr<SomeCharacter>>(&string->u)}) {
|
2019-07-04 01:25:43 +08:00
|
|
|
Expr<SubscriptInteger> length{
|
2022-03-24 05:05:50 +08:00
|
|
|
common::visit([](const auto &ckExpr) { return ckExpr.LEN().value(); },
|
2019-07-04 01:25:43 +08:00
|
|
|
charExpr->u)};
|
2019-11-10 01:29:31 +08:00
|
|
|
if (!lower) {
|
2018-12-04 03:40:53 +08:00
|
|
|
lower = Expr<SubscriptInteger>{1};
|
|
|
|
}
|
2019-11-10 01:29:31 +08:00
|
|
|
if (!upper) {
|
2018-12-06 05:03:39 +08:00
|
|
|
upper = Expr<SubscriptInteger>{
|
|
|
|
static_cast<std::int64_t>(ToInt64(length).value())};
|
2018-12-04 03:40:53 +08:00
|
|
|
}
|
2022-03-24 05:05:50 +08:00
|
|
|
return common::visit(
|
2018-12-04 03:40:53 +08:00
|
|
|
[&](auto &&ckExpr) -> MaybeExpr {
|
|
|
|
using Result = ResultType<decltype(ckExpr)>;
|
|
|
|
auto *cp{std::get_if<Constant<Result>>(&ckExpr.u)};
|
2019-07-30 00:12:52 +08:00
|
|
|
CHECK(DEREF(cp).size() == 1);
|
2018-12-04 03:40:53 +08:00
|
|
|
StaticDataObject::Pointer staticData{StaticDataObject::Create()};
|
|
|
|
staticData->set_alignment(Result::kind)
|
|
|
|
.set_itemBytes(Result::kind)
|
2022-07-02 02:40:44 +08:00
|
|
|
.Push(cp->GetScalarValue().value(),
|
|
|
|
foldingContext_.targetCharacteristics().isBigEndian());
|
2018-12-06 05:03:39 +08:00
|
|
|
Substring substring{std::move(staticData), std::move(lower.value()),
|
|
|
|
std::move(upper.value())};
|
2020-06-19 08:17:04 +08:00
|
|
|
return AsGenericExpr(
|
|
|
|
Expr<Result>{Designator<Result>{std::move(substring)}});
|
2018-12-04 03:40:53 +08:00
|
|
|
},
|
|
|
|
std::move(charExpr->u));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Subscripted array references
|
2019-03-08 06:46:31 +08:00
|
|
|
std::optional<Expr<SubscriptInteger>> ExpressionAnalyzer::AsSubscript(
|
|
|
|
MaybeExpr &&expr) {
|
2019-11-10 01:29:31 +08:00
|
|
|
if (expr) {
|
2018-09-20 05:27:13 +08:00
|
|
|
if (expr->Rank() > 1) {
|
2019-10-11 07:06:05 +08:00
|
|
|
Say("Subscript expression has rank %d greater than 1"_err_en_US,
|
|
|
|
expr->Rank());
|
2018-09-20 05:27:13 +08:00
|
|
|
}
|
2018-09-12 08:06:44 +08:00
|
|
|
if (auto *intExpr{std::get_if<Expr<SomeInteger>>(&expr->u)}) {
|
|
|
|
if (auto *ssIntExpr{std::get_if<Expr<SubscriptInteger>>(&intExpr->u)}) {
|
2019-10-11 07:06:05 +08:00
|
|
|
return std::move(*ssIntExpr);
|
|
|
|
} else {
|
|
|
|
return Expr<SubscriptInteger>{
|
|
|
|
Convert<SubscriptInteger, TypeCategory::Integer>{
|
|
|
|
std::move(*intExpr)}};
|
2018-09-12 08:06:44 +08:00
|
|
|
}
|
|
|
|
} else {
|
2019-10-11 07:06:05 +08:00
|
|
|
Say("Subscript expression is not INTEGER"_err_en_US);
|
2018-09-20 05:27:13 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
2019-03-08 06:46:31 +08:00
|
|
|
std::optional<Expr<SubscriptInteger>> ExpressionAnalyzer::TripletPart(
|
2018-09-12 08:06:44 +08:00
|
|
|
const std::optional<parser::Subscript> &s) {
|
2019-11-10 01:29:31 +08:00
|
|
|
if (s) {
|
2019-03-08 06:46:31 +08:00
|
|
|
return AsSubscript(Analyze(*s));
|
2019-10-11 07:06:05 +08:00
|
|
|
} else {
|
|
|
|
return std::nullopt;
|
2018-09-12 08:06:44 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-08 06:46:31 +08:00
|
|
|
std::optional<Subscript> ExpressionAnalyzer::AnalyzeSectionSubscript(
|
|
|
|
const parser::SectionSubscript &ss) {
|
2022-03-24 05:05:50 +08:00
|
|
|
return common::visit(
|
2020-08-11 02:44:04 +08:00
|
|
|
common::visitors{
|
|
|
|
[&](const parser::SubscriptTriplet &t) -> std::optional<Subscript> {
|
|
|
|
const auto &lower{std::get<0>(t.t)};
|
|
|
|
const auto &upper{std::get<1>(t.t)};
|
|
|
|
const auto &stride{std::get<2>(t.t)};
|
|
|
|
auto result{Triplet{
|
|
|
|
TripletPart(lower), TripletPart(upper), TripletPart(stride)}};
|
|
|
|
if ((lower && !result.lower()) || (upper && !result.upper())) {
|
|
|
|
return std::nullopt;
|
|
|
|
} else {
|
|
|
|
return std::make_optional<Subscript>(result);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
[&](const auto &s) -> std::optional<Subscript> {
|
|
|
|
if (auto subscriptExpr{AsSubscript(Analyze(s))}) {
|
|
|
|
return Subscript{std::move(*subscriptExpr)};
|
|
|
|
} else {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
2018-09-12 08:06:44 +08:00
|
|
|
ss.u);
|
|
|
|
}
|
|
|
|
|
2019-04-26 04:18:33 +08:00
|
|
|
// Empty result means an error occurred
|
2019-03-08 06:46:31 +08:00
|
|
|
std::vector<Subscript> ExpressionAnalyzer::AnalyzeSectionSubscripts(
|
2018-09-12 08:06:44 +08:00
|
|
|
const std::list<parser::SectionSubscript> &sss) {
|
2020-01-11 06:09:39 +08:00
|
|
|
bool error{false};
|
2018-09-12 08:06:44 +08:00
|
|
|
std::vector<Subscript> subscripts;
|
|
|
|
for (const auto &s : sss) {
|
2019-03-08 06:46:31 +08:00
|
|
|
if (auto subscript{AnalyzeSectionSubscript(s)}) {
|
2018-09-12 08:06:44 +08:00
|
|
|
subscripts.emplace_back(std::move(*subscript));
|
2019-04-26 04:18:33 +08:00
|
|
|
} else {
|
2020-01-11 06:09:39 +08:00
|
|
|
error = true;
|
2018-09-12 08:06:44 +08:00
|
|
|
}
|
|
|
|
}
|
2020-01-11 06:09:39 +08:00
|
|
|
return !error ? subscripts : std::vector<Subscript>{};
|
2018-09-12 08:06:44 +08:00
|
|
|
}
|
|
|
|
|
2019-03-08 06:46:31 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(const parser::ArrayElement &ae) {
|
2020-10-02 03:08:04 +08:00
|
|
|
MaybeExpr baseExpr;
|
|
|
|
{
|
|
|
|
auto restorer{AllowWholeAssumedSizeArray()};
|
|
|
|
baseExpr = Analyze(ae.base);
|
|
|
|
}
|
|
|
|
if (baseExpr) {
|
2020-02-28 10:00:45 +08:00
|
|
|
if (ae.subscripts.empty()) {
|
|
|
|
// will be converted to function call later or error reported
|
|
|
|
} else if (baseExpr->Rank() == 0) {
|
|
|
|
if (const Symbol * symbol{GetLastSymbol(*baseExpr)}) {
|
2020-07-01 03:03:28 +08:00
|
|
|
if (!context_.HasError(symbol)) {
|
2021-10-26 04:25:34 +08:00
|
|
|
if (inDataStmtConstant_) {
|
|
|
|
// Better error for NULL(X) with a MOLD= argument
|
|
|
|
Say("'%s' must be an array or structure constructor if used with non-empty parentheses as a DATA statement constant"_err_en_US,
|
|
|
|
symbol->name());
|
|
|
|
} else {
|
|
|
|
Say("'%s' is not an array"_err_en_US, symbol->name());
|
|
|
|
}
|
2020-08-29 01:30:23 +08:00
|
|
|
context_.SetError(*symbol);
|
2020-07-01 03:03:28 +08:00
|
|
|
}
|
2018-09-15 06:48:40 +08:00
|
|
|
}
|
2020-02-28 10:00:45 +08:00
|
|
|
} else if (std::optional<DataRef> dataRef{
|
|
|
|
ExtractDataRef(std::move(*baseExpr))}) {
|
|
|
|
return ApplySubscripts(
|
|
|
|
std::move(*dataRef), AnalyzeSectionSubscripts(ae.subscripts));
|
2019-04-26 04:18:33 +08:00
|
|
|
} else {
|
2019-05-14 00:33:18 +08:00
|
|
|
Say("Subscripts may be applied only to an object, component, or array constant"_err_en_US);
|
2018-09-15 06:48:40 +08:00
|
|
|
}
|
|
|
|
}
|
2020-02-28 10:00:45 +08:00
|
|
|
// error was reported: analyze subscripts without reporting more errors
|
|
|
|
auto restorer{GetContextualMessages().DiscardMessages()};
|
|
|
|
AnalyzeSectionSubscripts(ae.subscripts);
|
2018-09-13 02:20:30 +08:00
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
2019-01-10 07:06:07 +08:00
|
|
|
// Type parameter inquiries apply to data references, but don't depend
|
|
|
|
// on any trailing (co)subscripts.
|
2019-06-26 04:07:32 +08:00
|
|
|
static NamedEntity IgnoreAnySubscripts(Designator<SomeDerived> &&designator) {
|
2022-03-24 05:05:50 +08:00
|
|
|
return common::visit(
|
2019-01-10 07:06:07 +08:00
|
|
|
common::visitors{
|
2019-10-23 07:53:29 +08:00
|
|
|
[](SymbolRef &&symbol) { return NamedEntity{symbol}; },
|
2019-06-26 04:07:32 +08:00
|
|
|
[](Component &&component) {
|
|
|
|
return NamedEntity{std::move(component)};
|
|
|
|
},
|
2019-02-01 08:04:17 +08:00
|
|
|
[](ArrayRef &&arrayRef) { return std::move(arrayRef.base()); },
|
2019-01-10 07:06:07 +08:00
|
|
|
[](CoarrayRef &&coarrayRef) {
|
2019-06-26 04:07:32 +08:00
|
|
|
return NamedEntity{coarrayRef.GetLastSymbol()};
|
2019-01-10 07:06:07 +08:00
|
|
|
},
|
|
|
|
},
|
|
|
|
std::move(designator.u));
|
|
|
|
}
|
|
|
|
|
2018-12-05 02:55:32 +08:00
|
|
|
// Components of parent derived types are explicitly represented as such.
|
2021-10-20 04:49:21 +08:00
|
|
|
std::optional<Component> ExpressionAnalyzer::CreateComponent(
|
2018-12-05 02:55:32 +08:00
|
|
|
DataRef &&base, const Symbol &component, const semantics::Scope &scope) {
|
2021-10-20 04:49:21 +08:00
|
|
|
if (IsAllocatableOrPointer(component) && base.Rank() > 0) { // C919b
|
|
|
|
Say("An allocatable or pointer component reference must be applied to a scalar base"_err_en_US);
|
|
|
|
}
|
2018-12-05 02:55:32 +08:00
|
|
|
if (&component.owner() == &scope) {
|
2019-05-14 00:33:18 +08:00
|
|
|
return Component{std::move(base), component};
|
2018-12-05 02:55:32 +08:00
|
|
|
}
|
2022-01-29 00:28:00 +08:00
|
|
|
if (const Symbol * typeSymbol{scope.GetSymbol()}) {
|
|
|
|
if (const Symbol *
|
|
|
|
parentComponent{typeSymbol->GetParentComponent(&scope)}) {
|
|
|
|
if (const auto *object{
|
|
|
|
parentComponent->detailsIf<semantics::ObjectEntityDetails>()}) {
|
|
|
|
if (const auto *parentType{object->type()}) {
|
|
|
|
if (const semantics::Scope *
|
|
|
|
parentScope{parentType->derivedTypeSpec().scope()}) {
|
|
|
|
return CreateComponent(
|
|
|
|
DataRef{Component{std::move(base), *parentComponent}},
|
|
|
|
component, *parentScope);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-12-05 02:55:32 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
2019-01-10 07:06:07 +08:00
|
|
|
// Derived type component references and type parameter inquiries
|
2019-03-08 06:46:31 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(const parser::StructureComponent &sc) {
|
2019-04-26 04:18:33 +08:00
|
|
|
MaybeExpr base{Analyze(sc.base)};
|
|
|
|
Symbol *sym{sc.component.symbol};
|
2020-10-31 04:28:10 +08:00
|
|
|
if (!base || !sym || context_.HasError(sym)) {
|
2019-04-26 04:18:33 +08:00
|
|
|
return std::nullopt;
|
|
|
|
}
|
2019-01-10 07:06:07 +08:00
|
|
|
const auto &name{sc.component.source};
|
2019-04-26 04:18:33 +08:00
|
|
|
if (auto *dtExpr{UnwrapExpr<Expr<SomeDerived>>(*base)}) {
|
2019-12-17 03:33:55 +08:00
|
|
|
const auto *dtSpec{GetDerivedTypeSpec(dtExpr->GetType())};
|
2019-04-26 04:18:33 +08:00
|
|
|
if (sym->detailsIf<semantics::TypeParamDetails>()) {
|
|
|
|
if (auto *designator{UnwrapExpr<Designator<SomeDerived>>(*dtExpr)}) {
|
2019-05-04 02:29:15 +08:00
|
|
|
if (std::optional<DynamicType> dyType{DynamicType::From(*sym)}) {
|
2019-05-14 00:33:18 +08:00
|
|
|
if (dyType->category() == TypeCategory::Integer) {
|
2020-10-31 04:28:10 +08:00
|
|
|
auto restorer{GetContextualMessages().SetLocation(name)};
|
2020-08-26 00:40:20 +08:00
|
|
|
return Fold(ConvertToType(*dyType,
|
|
|
|
AsGenericExpr(TypeParamInquiry{
|
|
|
|
IgnoreAnySubscripts(std::move(*designator)), *sym})));
|
2019-02-01 01:58:40 +08:00
|
|
|
}
|
2018-12-05 02:55:32 +08:00
|
|
|
}
|
2019-05-31 07:14:24 +08:00
|
|
|
Say(name, "Type parameter is not INTEGER"_err_en_US);
|
2018-09-15 06:48:40 +08:00
|
|
|
} else {
|
2019-03-08 06:46:31 +08:00
|
|
|
Say(name,
|
2019-05-31 07:14:24 +08:00
|
|
|
"A type parameter inquiry must be applied to "
|
2019-04-26 04:18:33 +08:00
|
|
|
"a designator"_err_en_US);
|
2018-09-15 06:48:40 +08:00
|
|
|
}
|
2019-11-10 01:29:31 +08:00
|
|
|
} else if (!dtSpec || !dtSpec->scope()) {
|
2019-10-25 05:55:25 +08:00
|
|
|
CHECK(context_.AnyFatalError() || !foldingContext_.messages().empty());
|
2019-03-12 06:39:11 +08:00
|
|
|
return std::nullopt;
|
2019-04-26 04:18:33 +08:00
|
|
|
} else if (std::optional<DataRef> dataRef{
|
|
|
|
ExtractDataRef(std::move(*dtExpr))}) {
|
|
|
|
if (auto component{
|
|
|
|
CreateComponent(std::move(*dataRef), *sym, *dtSpec->scope())}) {
|
|
|
|
return Designate(DataRef{std::move(*component)});
|
2019-01-08 05:31:50 +08:00
|
|
|
} else {
|
2019-05-31 07:14:24 +08:00
|
|
|
Say(name, "Component is not in scope of derived TYPE(%s)"_err_en_US,
|
2019-05-07 00:33:45 +08:00
|
|
|
dtSpec->typeSymbol().name());
|
2018-09-15 06:48:40 +08:00
|
|
|
}
|
|
|
|
} else {
|
2019-04-26 04:18:33 +08:00
|
|
|
Say(name,
|
2019-05-31 07:14:24 +08:00
|
|
|
"Base of component reference must be a data reference"_err_en_US);
|
2018-09-15 06:48:40 +08:00
|
|
|
}
|
2019-04-26 04:18:33 +08:00
|
|
|
} else if (auto *details{sym->detailsIf<semantics::MiscDetails>()}) {
|
|
|
|
// special part-ref: %re, %im, %kind, %len
|
2022-04-13 05:08:04 +08:00
|
|
|
// Type errors on the base of %re/%im/%len are detected and
|
|
|
|
// reported in name resolution.
|
2019-04-26 04:18:33 +08:00
|
|
|
using MiscKind = semantics::MiscDetails::Kind;
|
|
|
|
MiscKind kind{details->kind()};
|
|
|
|
if (kind == MiscKind::ComplexPartRe || kind == MiscKind::ComplexPartIm) {
|
|
|
|
if (auto *zExpr{std::get_if<Expr<SomeComplex>>(&base->u)}) {
|
2022-05-07 09:39:23 +08:00
|
|
|
if (std::optional<DataRef> dataRef{ExtractDataRef(*zExpr)}) {
|
|
|
|
// Represent %RE/%IM as a designator
|
2022-03-24 05:05:50 +08:00
|
|
|
Expr<SomeReal> realExpr{common::visit(
|
2019-04-26 04:18:33 +08:00
|
|
|
[&](const auto &z) {
|
|
|
|
using PartType = typename ResultType<decltype(z)>::Part;
|
|
|
|
auto part{kind == MiscKind::ComplexPartRe
|
|
|
|
? ComplexPart::Part::RE
|
|
|
|
: ComplexPart::Part::IM};
|
|
|
|
return AsCategoryExpr(Designator<PartType>{
|
|
|
|
ComplexPart{std::move(*dataRef), part}});
|
|
|
|
},
|
|
|
|
zExpr->u)};
|
2020-06-19 08:17:04 +08:00
|
|
|
return AsGenericExpr(std::move(realExpr));
|
2019-04-26 04:18:33 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (kind == MiscKind::KindParamInquiry ||
|
|
|
|
kind == MiscKind::LenParamInquiry) {
|
2022-02-12 08:58:01 +08:00
|
|
|
ActualArgument arg{std::move(*base)};
|
|
|
|
SetArgSourceLocation(arg, name);
|
|
|
|
return MakeFunctionRef(name, ActualArguments{std::move(arg)});
|
2019-04-26 04:18:33 +08:00
|
|
|
} else {
|
2019-10-03 03:40:52 +08:00
|
|
|
DIE("unexpected MiscDetails::Kind");
|
2019-04-26 04:18:33 +08:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Say(name, "derived type required before component reference"_err_en_US);
|
2018-09-15 06:48:40 +08:00
|
|
|
}
|
2018-09-08 01:33:32 +08:00
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
2019-09-14 03:32:43 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(const parser::CoindexedNamedObject &x) {
|
2020-03-07 09:05:04 +08:00
|
|
|
if (auto maybeDataRef{ExtractDataRef(Analyze(x.base))}) {
|
|
|
|
DataRef *dataRef{&*maybeDataRef};
|
2019-09-14 03:32:43 +08:00
|
|
|
std::vector<Subscript> subscripts;
|
2019-10-23 07:53:29 +08:00
|
|
|
SymbolVector reversed;
|
2019-09-14 03:32:43 +08:00
|
|
|
if (auto *aRef{std::get_if<ArrayRef>(&dataRef->u)}) {
|
|
|
|
subscripts = std::move(aRef->subscript());
|
2019-10-23 07:53:29 +08:00
|
|
|
reversed.push_back(aRef->GetLastSymbol());
|
2019-09-14 03:32:43 +08:00
|
|
|
if (Component * component{aRef->base().UnwrapComponent()}) {
|
2020-03-07 09:05:04 +08:00
|
|
|
dataRef = &component->base();
|
2019-09-14 03:32:43 +08:00
|
|
|
} else {
|
2020-03-07 09:05:04 +08:00
|
|
|
dataRef = nullptr;
|
2019-09-14 03:32:43 +08:00
|
|
|
}
|
|
|
|
}
|
2019-11-10 01:29:31 +08:00
|
|
|
if (dataRef) {
|
2019-09-14 03:32:43 +08:00
|
|
|
while (auto *component{std::get_if<Component>(&dataRef->u)}) {
|
2019-10-23 07:53:29 +08:00
|
|
|
reversed.push_back(component->GetLastSymbol());
|
2020-03-07 09:05:04 +08:00
|
|
|
dataRef = &component->base();
|
2019-09-14 03:32:43 +08:00
|
|
|
}
|
2019-10-23 07:53:29 +08:00
|
|
|
if (auto *baseSym{std::get_if<SymbolRef>(&dataRef->u)}) {
|
2019-09-14 03:32:43 +08:00
|
|
|
reversed.push_back(*baseSym);
|
|
|
|
} else {
|
|
|
|
Say("Base of coindexed named object has subscripts or cosubscripts"_err_en_US);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
std::vector<Expr<SubscriptInteger>> cosubscripts;
|
|
|
|
bool cosubsOk{true};
|
|
|
|
for (const auto &cosub :
|
|
|
|
std::get<std::list<parser::Cosubscript>>(x.imageSelector.t)) {
|
|
|
|
MaybeExpr coex{Analyze(cosub)};
|
|
|
|
if (auto *intExpr{UnwrapExpr<Expr<SomeInteger>>(coex)}) {
|
|
|
|
cosubscripts.push_back(
|
|
|
|
ConvertToType<SubscriptInteger>(std::move(*intExpr)));
|
|
|
|
} else {
|
|
|
|
cosubsOk = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (cosubsOk && !reversed.empty()) {
|
|
|
|
int numCosubscripts{static_cast<int>(cosubscripts.size())};
|
2019-10-23 07:53:29 +08:00
|
|
|
const Symbol &symbol{reversed.front()};
|
2019-09-14 03:32:43 +08:00
|
|
|
if (numCosubscripts != symbol.Corank()) {
|
|
|
|
Say("'%s' has corank %d, but coindexed reference has %d cosubscripts"_err_en_US,
|
|
|
|
symbol.name(), symbol.Corank(), numCosubscripts);
|
|
|
|
}
|
|
|
|
}
|
2020-07-08 03:15:39 +08:00
|
|
|
for (const auto &imageSelSpec :
|
|
|
|
std::get<std::list<parser::ImageSelectorSpec>>(x.imageSelector.t)) {
|
2022-03-24 05:05:50 +08:00
|
|
|
common::visit(
|
2020-07-08 03:15:39 +08:00
|
|
|
common::visitors{
|
2020-07-09 01:05:04 +08:00
|
|
|
[&](const auto &x) { Analyze(x.v); },
|
2020-07-08 03:15:39 +08:00
|
|
|
},
|
|
|
|
imageSelSpec.u);
|
|
|
|
}
|
2019-09-14 03:32:43 +08:00
|
|
|
// Reverse the chain of symbols so that the base is first and coarray
|
|
|
|
// ultimate component is last.
|
2020-07-09 01:05:04 +08:00
|
|
|
if (cosubsOk) {
|
|
|
|
return Designate(
|
|
|
|
DataRef{CoarrayRef{SymbolVector{reversed.crbegin(), reversed.crend()},
|
|
|
|
std::move(subscripts), std::move(cosubscripts)}});
|
|
|
|
}
|
2019-09-14 03:32:43 +08:00
|
|
|
}
|
2018-08-08 03:34:09 +08:00
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
2019-03-08 06:46:31 +08:00
|
|
|
int ExpressionAnalyzer::IntegerTypeSpecKind(
|
|
|
|
const parser::IntegerTypeSpec &spec) {
|
|
|
|
Expr<SubscriptInteger> value{
|
|
|
|
AnalyzeKindSelector(TypeCategory::Integer, spec.v)};
|
2019-01-23 08:30:32 +08:00
|
|
|
if (auto kind{ToInt64(value)}) {
|
|
|
|
return static_cast<int>(*kind);
|
|
|
|
}
|
2019-03-08 06:46:31 +08:00
|
|
|
SayAt(spec, "Constant INTEGER kind value required here"_err_en_US);
|
|
|
|
return GetDefaultKind(TypeCategory::Integer);
|
2019-01-23 08:30:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Array constructors
|
|
|
|
|
2019-11-22 05:31:52 +08:00
|
|
|
// Inverts a collection of generic ArrayConstructorValues<SomeType> that
|
|
|
|
// all happen to have the same actual type T into one ArrayConstructor<T>.
|
2020-03-28 05:17:25 +08:00
|
|
|
template <typename T>
|
2019-11-22 05:31:52 +08:00
|
|
|
ArrayConstructorValues<T> MakeSpecific(
|
|
|
|
ArrayConstructorValues<SomeType> &&from) {
|
|
|
|
ArrayConstructorValues<T> to;
|
|
|
|
for (ArrayConstructorValue<SomeType> &x : from) {
|
2022-03-24 05:05:50 +08:00
|
|
|
common::visit(
|
2019-11-22 05:31:52 +08:00
|
|
|
common::visitors{
|
|
|
|
[&](common::CopyableIndirection<Expr<SomeType>> &&expr) {
|
|
|
|
auto *typed{UnwrapExpr<Expr<T>>(expr.value())};
|
|
|
|
to.Push(std::move(DEREF(typed)));
|
|
|
|
},
|
|
|
|
[&](ImpliedDo<SomeType> &&impliedDo) {
|
|
|
|
to.Push(ImpliedDo<T>{impliedDo.name(),
|
|
|
|
std::move(impliedDo.lower()), std::move(impliedDo.upper()),
|
|
|
|
std::move(impliedDo.stride()),
|
|
|
|
MakeSpecific<T>(std::move(impliedDo.values()))});
|
|
|
|
},
|
|
|
|
},
|
|
|
|
std::move(x.u));
|
|
|
|
}
|
|
|
|
return to;
|
|
|
|
}
|
|
|
|
|
|
|
|
class ArrayConstructorContext {
|
2019-02-16 04:20:30 +08:00
|
|
|
public:
|
|
|
|
ArrayConstructorContext(
|
2019-11-22 05:31:52 +08:00
|
|
|
ExpressionAnalyzer &c, std::optional<DynamicTypeWithLength> &&t)
|
2020-03-28 05:17:25 +08:00
|
|
|
: exprAnalyzer_{c}, type_{std::move(t)} {}
|
2019-11-22 05:31:52 +08:00
|
|
|
|
2019-01-23 08:30:32 +08:00
|
|
|
void Add(const parser::AcValue &);
|
2019-11-22 05:31:52 +08:00
|
|
|
MaybeExpr ToExpr();
|
|
|
|
|
|
|
|
// These interfaces allow *this to be used as a type visitor argument to
|
|
|
|
// common::SearchTypes() to convert the array constructor to a typed
|
|
|
|
// expression in ToExpr().
|
|
|
|
using Result = MaybeExpr;
|
|
|
|
using Types = AllTypes;
|
2020-03-28 05:17:25 +08:00
|
|
|
template <typename T> Result Test() {
|
2019-11-22 05:31:52 +08:00
|
|
|
if (type_ && type_->category() == T::category) {
|
|
|
|
if constexpr (T::category == TypeCategory::Derived) {
|
2021-06-03 08:13:55 +08:00
|
|
|
if (!type_->IsUnlimitedPolymorphic()) {
|
2020-07-15 01:08:34 +08:00
|
|
|
return AsMaybeExpr(ArrayConstructor<T>{type_->GetDerivedTypeSpec(),
|
|
|
|
MakeSpecific<T>(std::move(values_))});
|
|
|
|
}
|
2019-11-22 05:31:52 +08:00
|
|
|
} else if (type_->kind() == T::kind) {
|
|
|
|
if constexpr (T::category == TypeCategory::Character) {
|
|
|
|
if (auto len{type_->LEN()}) {
|
|
|
|
return AsMaybeExpr(ArrayConstructor<T>{
|
|
|
|
*std::move(len), MakeSpecific<T>(std::move(values_))});
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return AsMaybeExpr(
|
|
|
|
ArrayConstructor<T>{MakeSpecific<T>(std::move(values_))});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
2019-02-16 04:20:30 +08:00
|
|
|
|
|
|
|
private:
|
2020-10-31 03:57:28 +08:00
|
|
|
using ImpliedDoIntType = ResultType<ImpliedDoIndex>;
|
|
|
|
|
2019-11-22 05:31:52 +08:00
|
|
|
void Push(MaybeExpr &&);
|
2020-10-31 03:57:28 +08:00
|
|
|
void Add(const parser::AcValue::Triplet &);
|
|
|
|
void Add(const parser::Expr &);
|
|
|
|
void Add(const parser::AcImpliedDo &);
|
|
|
|
void UnrollConstantImpliedDo(const parser::AcImpliedDo &,
|
|
|
|
parser::CharBlock name, std::int64_t lower, std::int64_t upper,
|
|
|
|
std::int64_t stride);
|
2019-11-22 05:31:52 +08:00
|
|
|
|
2020-03-28 05:17:25 +08:00
|
|
|
template <int KIND, typename A>
|
2019-03-08 06:46:31 +08:00
|
|
|
std::optional<Expr<Type<TypeCategory::Integer, KIND>>> GetSpecificIntExpr(
|
|
|
|
const A &x) {
|
2019-11-22 05:31:52 +08:00
|
|
|
if (MaybeExpr y{exprAnalyzer_.Analyze(x)}) {
|
2019-03-08 06:46:31 +08:00
|
|
|
Expr<SomeInteger> *intExpr{UnwrapExpr<Expr<SomeInteger>>(*y)};
|
2020-10-31 03:57:28 +08:00
|
|
|
return Fold(exprAnalyzer_.GetFoldingContext(),
|
|
|
|
ConvertToType<Type<TypeCategory::Integer, KIND>>(
|
|
|
|
std::move(DEREF(intExpr))));
|
2019-03-08 06:46:31 +08:00
|
|
|
}
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
2019-11-22 05:31:52 +08:00
|
|
|
// Nested array constructors all reference the same ExpressionAnalyzer,
|
|
|
|
// which represents the nest of active implied DO loop indices.
|
|
|
|
ExpressionAnalyzer &exprAnalyzer_;
|
|
|
|
std::optional<DynamicTypeWithLength> type_;
|
2019-02-16 04:20:30 +08:00
|
|
|
bool explicitType_{type_.has_value()};
|
|
|
|
std::optional<std::int64_t> constantLength_;
|
|
|
|
ArrayConstructorValues<SomeType> values_;
|
2020-10-31 03:57:28 +08:00
|
|
|
std::uint64_t messageDisplayedSet_{0};
|
2019-01-23 08:30:32 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
void ArrayConstructorContext::Push(MaybeExpr &&x) {
|
2019-11-10 01:29:31 +08:00
|
|
|
if (!x) {
|
2019-03-12 06:39:11 +08:00
|
|
|
return;
|
|
|
|
}
|
2021-06-03 08:02:43 +08:00
|
|
|
if (!type_) {
|
|
|
|
if (auto *boz{std::get_if<BOZLiteralConstant>(&x->u)}) {
|
|
|
|
// Treat an array constructor of BOZ as if default integer.
|
|
|
|
if (exprAnalyzer_.context().ShouldWarn(
|
|
|
|
common::LanguageFeature::BOZAsDefaultInteger)) {
|
|
|
|
exprAnalyzer_.Say(
|
2022-03-08 05:57:37 +08:00
|
|
|
"BOZ literal in array constructor without explicit type is assumed to be default INTEGER"_port_en_US);
|
2021-06-03 08:02:43 +08:00
|
|
|
}
|
|
|
|
x = AsGenericExpr(ConvertToKind<TypeCategory::Integer>(
|
|
|
|
exprAnalyzer_.GetDefaultKind(TypeCategory::Integer),
|
|
|
|
std::move(*boz)));
|
|
|
|
}
|
|
|
|
}
|
2021-06-16 06:18:01 +08:00
|
|
|
std::optional<DynamicType> dyType{x->GetType()};
|
|
|
|
if (!dyType) {
|
|
|
|
if (auto *boz{std::get_if<BOZLiteralConstant>(&x->u)}) {
|
|
|
|
if (!type_) {
|
|
|
|
// Treat an array constructor of BOZ as if default integer.
|
|
|
|
if (exprAnalyzer_.context().ShouldWarn(
|
|
|
|
common::LanguageFeature::BOZAsDefaultInteger)) {
|
|
|
|
exprAnalyzer_.Say(
|
2022-03-08 05:57:37 +08:00
|
|
|
"BOZ literal in array constructor without explicit type is assumed to be default INTEGER"_port_en_US);
|
2021-06-16 06:18:01 +08:00
|
|
|
}
|
|
|
|
x = AsGenericExpr(ConvertToKind<TypeCategory::Integer>(
|
|
|
|
exprAnalyzer_.GetDefaultKind(TypeCategory::Integer),
|
|
|
|
std::move(*boz)));
|
|
|
|
dyType = x.value().GetType();
|
|
|
|
} else if (auto cast{ConvertToType(*type_, std::move(*x))}) {
|
|
|
|
x = std::move(cast);
|
|
|
|
dyType = *type_;
|
|
|
|
} else {
|
|
|
|
if (!(messageDisplayedSet_ & 0x80)) {
|
|
|
|
exprAnalyzer_.Say(
|
|
|
|
"BOZ literal is not suitable for use in this array constructor"_err_en_US);
|
|
|
|
messageDisplayedSet_ |= 0x80;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else { // procedure name, &c.
|
|
|
|
if (!(messageDisplayedSet_ & 0x40)) {
|
|
|
|
exprAnalyzer_.Say(
|
|
|
|
"Item is not suitable for use in an array constructor"_err_en_US);
|
|
|
|
messageDisplayedSet_ |= 0x40;
|
|
|
|
}
|
|
|
|
return;
|
2019-01-23 08:30:32 +08:00
|
|
|
}
|
2021-06-16 06:18:01 +08:00
|
|
|
} else if (dyType->IsUnlimitedPolymorphic()) {
|
|
|
|
if (!(messageDisplayedSet_ & 8)) {
|
|
|
|
exprAnalyzer_.Say("Cannot have an unlimited polymorphic value in an "
|
|
|
|
"array constructor"_err_en_US); // C7113
|
|
|
|
messageDisplayedSet_ |= 8;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
DynamicTypeWithLength xType{dyType.value()};
|
|
|
|
if (Expr<SomeCharacter> * charExpr{UnwrapExpr<Expr<SomeCharacter>>(*x)}) {
|
|
|
|
CHECK(xType.category() == TypeCategory::Character);
|
|
|
|
xType.length =
|
2022-03-24 05:05:50 +08:00
|
|
|
common::visit([](const auto &kc) { return kc.LEN(); }, charExpr->u);
|
2021-06-16 06:18:01 +08:00
|
|
|
}
|
|
|
|
if (!type_) {
|
|
|
|
// If there is no explicit type-spec in an array constructor, the type
|
|
|
|
// of the array is the declared type of all of the elements, which must
|
|
|
|
// be well-defined and all match.
|
|
|
|
// TODO: Possible language extension: use the most general type of
|
|
|
|
// the values as the type of a numeric constructed array, convert all
|
|
|
|
// of the other values to that type. Alternative: let the first value
|
|
|
|
// determine the type, and convert the others to that type.
|
|
|
|
CHECK(!explicitType_);
|
|
|
|
type_ = std::move(xType);
|
|
|
|
constantLength_ = ToInt64(type_->length);
|
|
|
|
values_.Push(std::move(*x));
|
|
|
|
} else if (!explicitType_) {
|
|
|
|
if (type_->IsTkCompatibleWith(xType) && xType.IsTkCompatibleWith(*type_)) {
|
2019-02-16 04:20:30 +08:00
|
|
|
values_.Push(std::move(*x));
|
2021-06-16 06:18:01 +08:00
|
|
|
if (auto thisLen{ToInt64(xType.LEN())}) {
|
|
|
|
if (constantLength_) {
|
|
|
|
if (exprAnalyzer_.context().warnOnNonstandardUsage() &&
|
|
|
|
*thisLen != *constantLength_) {
|
|
|
|
if (!(messageDisplayedSet_ & 1)) {
|
|
|
|
exprAnalyzer_.Say(
|
|
|
|
"Character literal in array constructor without explicit "
|
2022-03-08 05:57:37 +08:00
|
|
|
"type has different length than earlier elements"_port_en_US);
|
2021-06-16 06:18:01 +08:00
|
|
|
messageDisplayedSet_ |= 1;
|
2019-02-16 04:20:30 +08:00
|
|
|
}
|
2021-06-16 06:18:01 +08:00
|
|
|
}
|
|
|
|
if (*thisLen > *constantLength_) {
|
|
|
|
// Language extension: use the longest literal to determine the
|
|
|
|
// length of the array constructor's character elements, not the
|
|
|
|
// first, when there is no explicit type.
|
|
|
|
*constantLength_ = *thisLen;
|
2019-02-27 07:59:25 +08:00
|
|
|
type_->length = xType.LEN();
|
2019-02-16 04:20:30 +08:00
|
|
|
}
|
2021-06-16 06:18:01 +08:00
|
|
|
} else {
|
|
|
|
constantLength_ = *thisLen;
|
|
|
|
type_->length = xType.LEN();
|
2020-07-31 03:32:04 +08:00
|
|
|
}
|
2019-01-23 08:30:32 +08:00
|
|
|
}
|
|
|
|
} else {
|
2021-06-16 06:18:01 +08:00
|
|
|
if (!(messageDisplayedSet_ & 2)) {
|
2019-11-22 05:31:52 +08:00
|
|
|
exprAnalyzer_.Say(
|
2021-06-16 06:18:01 +08:00
|
|
|
"Values in array constructor must have the same declared type "
|
|
|
|
"when no explicit type appears"_err_en_US); // C7110
|
|
|
|
messageDisplayedSet_ |= 2;
|
2019-01-23 08:30:32 +08:00
|
|
|
}
|
|
|
|
}
|
2021-06-16 06:18:01 +08:00
|
|
|
} else {
|
|
|
|
if (auto cast{ConvertToType(*type_, std::move(*x))}) {
|
|
|
|
values_.Push(std::move(*cast));
|
|
|
|
} else if (!(messageDisplayedSet_ & 4)) {
|
|
|
|
exprAnalyzer_.Say("Value in array constructor of type '%s' could not "
|
|
|
|
"be converted to the type of the array '%s'"_err_en_US,
|
|
|
|
x->GetType()->AsFortran(), type_->AsFortran()); // C7111, C7112
|
|
|
|
messageDisplayedSet_ |= 4;
|
|
|
|
}
|
2019-01-23 08:30:32 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ArrayConstructorContext::Add(const parser::AcValue &x) {
|
2022-03-24 05:05:50 +08:00
|
|
|
common::visit(
|
2019-01-23 08:30:32 +08:00
|
|
|
common::visitors{
|
2020-10-31 03:57:28 +08:00
|
|
|
[&](const parser::AcValue::Triplet &triplet) { Add(triplet); },
|
2019-01-23 08:30:32 +08:00
|
|
|
[&](const common::Indirection<parser::Expr> &expr) {
|
2020-10-31 03:57:28 +08:00
|
|
|
Add(expr.value());
|
2019-01-23 08:30:32 +08:00
|
|
|
},
|
|
|
|
[&](const common::Indirection<parser::AcImpliedDo> &impliedDo) {
|
2020-10-31 03:57:28 +08:00
|
|
|
Add(impliedDo.value());
|
2019-01-23 08:30:32 +08:00
|
|
|
},
|
|
|
|
},
|
|
|
|
x.u);
|
|
|
|
}
|
|
|
|
|
2020-10-31 03:57:28 +08:00
|
|
|
// Transforms l:u(:s) into (_,_=l,u(,s)) with an anonymous index '_'
|
|
|
|
void ArrayConstructorContext::Add(const parser::AcValue::Triplet &triplet) {
|
|
|
|
std::optional<Expr<ImpliedDoIntType>> lower{
|
|
|
|
GetSpecificIntExpr<ImpliedDoIntType::kind>(std::get<0>(triplet.t))};
|
|
|
|
std::optional<Expr<ImpliedDoIntType>> upper{
|
|
|
|
GetSpecificIntExpr<ImpliedDoIntType::kind>(std::get<1>(triplet.t))};
|
|
|
|
std::optional<Expr<ImpliedDoIntType>> stride{
|
|
|
|
GetSpecificIntExpr<ImpliedDoIntType::kind>(std::get<2>(triplet.t))};
|
|
|
|
if (lower && upper) {
|
|
|
|
if (!stride) {
|
|
|
|
stride = Expr<ImpliedDoIntType>{1};
|
|
|
|
}
|
|
|
|
if (!type_) {
|
|
|
|
type_ = DynamicTypeWithLength{ImpliedDoIntType::GetType()};
|
|
|
|
}
|
|
|
|
auto v{std::move(values_)};
|
|
|
|
parser::CharBlock anonymous;
|
|
|
|
Push(Expr<SomeType>{
|
|
|
|
Expr<SomeInteger>{Expr<ImpliedDoIntType>{ImpliedDoIndex{anonymous}}}});
|
|
|
|
std::swap(v, values_);
|
|
|
|
values_.Push(ImpliedDo<SomeType>{anonymous, std::move(*lower),
|
|
|
|
std::move(*upper), std::move(*stride), std::move(v)});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ArrayConstructorContext::Add(const parser::Expr &expr) {
|
|
|
|
auto restorer{exprAnalyzer_.GetContextualMessages().SetLocation(expr.source)};
|
2021-06-16 06:18:01 +08:00
|
|
|
Push(exprAnalyzer_.Analyze(expr));
|
2020-10-31 03:57:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void ArrayConstructorContext::Add(const parser::AcImpliedDo &impliedDo) {
|
|
|
|
const auto &control{std::get<parser::AcImpliedDoControl>(impliedDo.t)};
|
|
|
|
const auto &bounds{std::get<parser::AcImpliedDoControl::Bounds>(control.t)};
|
|
|
|
exprAnalyzer_.Analyze(bounds.name);
|
|
|
|
parser::CharBlock name{bounds.name.thing.thing.source};
|
|
|
|
const Symbol *symbol{bounds.name.thing.thing.symbol};
|
|
|
|
int kind{ImpliedDoIntType::kind};
|
|
|
|
if (const auto dynamicType{DynamicType::From(symbol)}) {
|
|
|
|
kind = dynamicType->kind();
|
|
|
|
}
|
|
|
|
std::optional<Expr<ImpliedDoIntType>> lower{
|
|
|
|
GetSpecificIntExpr<ImpliedDoIntType::kind>(bounds.lower)};
|
|
|
|
std::optional<Expr<ImpliedDoIntType>> upper{
|
|
|
|
GetSpecificIntExpr<ImpliedDoIntType::kind>(bounds.upper)};
|
|
|
|
if (lower && upper) {
|
|
|
|
std::optional<Expr<ImpliedDoIntType>> stride{
|
|
|
|
GetSpecificIntExpr<ImpliedDoIntType::kind>(bounds.step)};
|
|
|
|
if (!stride) {
|
|
|
|
stride = Expr<ImpliedDoIntType>{1};
|
|
|
|
}
|
[flang] Extension: reduced scope for some implied DO loop indices
The index of an implied DO loop in a DATA statement or array
constructor is defined by Fortran 2018 to have scope over its
implied DO loop. This definition is unfortunate, because it
requires the implied DO loop's bounds expressions to be in the
scope of the index variable. Consequently, in code like
integer, parameter :: j = 5
real, save :: a(5) = [(j, j=1, j)]
the upper bound of the loop is a reference to the index variable,
not the parameter in the enclosing scope.
This patch limits the scope of the index variable to the "body"
of the implied DO loop as one would naturally expect, with a warning.
I would have preferred to make this a hard error, but most Fortran
compilers treat this case as f18 now does. If the standard
were to be fixed, the warning could be made optional.
Differential Revision: https://reviews.llvm.org/D108595
2021-08-21 06:18:21 +08:00
|
|
|
if (exprAnalyzer_.AddImpliedDo(name, kind)) {
|
|
|
|
// Check for constant bounds; the loop may require complete unrolling
|
|
|
|
// of the parse tree if all bounds are constant in order to allow the
|
|
|
|
// implied DO loop index to qualify as a constant expression.
|
|
|
|
auto cLower{ToInt64(lower)};
|
|
|
|
auto cUpper{ToInt64(upper)};
|
|
|
|
auto cStride{ToInt64(stride)};
|
|
|
|
if (!(messageDisplayedSet_ & 0x10) && cStride && *cStride == 0) {
|
|
|
|
exprAnalyzer_.SayAt(bounds.step.value().thing.thing.value().source,
|
|
|
|
"The stride of an implied DO loop must not be zero"_err_en_US);
|
|
|
|
messageDisplayedSet_ |= 0x10;
|
2020-10-31 03:57:28 +08:00
|
|
|
}
|
[flang] Extension: reduced scope for some implied DO loop indices
The index of an implied DO loop in a DATA statement or array
constructor is defined by Fortran 2018 to have scope over its
implied DO loop. This definition is unfortunate, because it
requires the implied DO loop's bounds expressions to be in the
scope of the index variable. Consequently, in code like
integer, parameter :: j = 5
real, save :: a(5) = [(j, j=1, j)]
the upper bound of the loop is a reference to the index variable,
not the parameter in the enclosing scope.
This patch limits the scope of the index variable to the "body"
of the implied DO loop as one would naturally expect, with a warning.
I would have preferred to make this a hard error, but most Fortran
compilers treat this case as f18 now does. If the standard
were to be fixed, the warning could be made optional.
Differential Revision: https://reviews.llvm.org/D108595
2021-08-21 06:18:21 +08:00
|
|
|
bool isConstant{cLower && cUpper && cStride && *cStride != 0};
|
|
|
|
bool isNonemptyConstant{isConstant &&
|
|
|
|
((*cStride > 0 && *cLower <= *cUpper) ||
|
|
|
|
(*cStride < 0 && *cLower >= *cUpper))};
|
|
|
|
bool unrollConstantLoop{false};
|
|
|
|
parser::Messages buffer;
|
|
|
|
auto saveMessagesDisplayed{messageDisplayedSet_};
|
|
|
|
{
|
|
|
|
auto messageRestorer{
|
|
|
|
exprAnalyzer_.GetContextualMessages().SetMessages(buffer)};
|
|
|
|
auto v{std::move(values_)};
|
|
|
|
for (const auto &value :
|
|
|
|
std::get<std::list<parser::AcValue>>(impliedDo.t)) {
|
|
|
|
Add(value);
|
|
|
|
}
|
|
|
|
std::swap(v, values_);
|
|
|
|
if (isNonemptyConstant && buffer.AnyFatalError()) {
|
|
|
|
unrollConstantLoop = true;
|
|
|
|
} else {
|
|
|
|
values_.Push(ImpliedDo<SomeType>{name, std::move(*lower),
|
|
|
|
std::move(*upper), std::move(*stride), std::move(v)});
|
|
|
|
}
|
2020-10-31 03:57:28 +08:00
|
|
|
}
|
[flang] Extension: reduced scope for some implied DO loop indices
The index of an implied DO loop in a DATA statement or array
constructor is defined by Fortran 2018 to have scope over its
implied DO loop. This definition is unfortunate, because it
requires the implied DO loop's bounds expressions to be in the
scope of the index variable. Consequently, in code like
integer, parameter :: j = 5
real, save :: a(5) = [(j, j=1, j)]
the upper bound of the loop is a reference to the index variable,
not the parameter in the enclosing scope.
This patch limits the scope of the index variable to the "body"
of the implied DO loop as one would naturally expect, with a warning.
I would have preferred to make this a hard error, but most Fortran
compilers treat this case as f18 now does. If the standard
were to be fixed, the warning could be made optional.
Differential Revision: https://reviews.llvm.org/D108595
2021-08-21 06:18:21 +08:00
|
|
|
if (unrollConstantLoop) {
|
|
|
|
messageDisplayedSet_ = saveMessagesDisplayed;
|
|
|
|
UnrollConstantImpliedDo(impliedDo, name, *cLower, *cUpper, *cStride);
|
|
|
|
} else if (auto *messages{
|
|
|
|
exprAnalyzer_.GetContextualMessages().messages()}) {
|
|
|
|
messages->Annex(std::move(buffer));
|
|
|
|
}
|
|
|
|
exprAnalyzer_.RemoveImpliedDo(name);
|
|
|
|
} else if (!(messageDisplayedSet_ & 0x20)) {
|
|
|
|
exprAnalyzer_.SayAt(name,
|
|
|
|
"Implied DO index '%s' is active in a surrounding implied DO loop "
|
|
|
|
"and may not have the same name"_err_en_US,
|
|
|
|
name); // C7115
|
|
|
|
messageDisplayedSet_ |= 0x20;
|
2020-10-31 03:57:28 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fortran considers an implied DO index of an array constructor to be
|
|
|
|
// a constant expression if the bounds of the implied DO loop are constant.
|
|
|
|
// Usually this doesn't matter, but if we emitted spurious messages as a
|
|
|
|
// result of not using constant values for the index while analyzing the
|
|
|
|
// items, we need to do it again the "hard" way with multiple iterations over
|
|
|
|
// the parse tree.
|
|
|
|
void ArrayConstructorContext::UnrollConstantImpliedDo(
|
|
|
|
const parser::AcImpliedDo &impliedDo, parser::CharBlock name,
|
|
|
|
std::int64_t lower, std::int64_t upper, std::int64_t stride) {
|
|
|
|
auto &foldingContext{exprAnalyzer_.GetFoldingContext()};
|
|
|
|
auto restorer{exprAnalyzer_.DoNotUseSavedTypedExprs()};
|
|
|
|
for (auto &at{foldingContext.StartImpliedDo(name, lower)};
|
|
|
|
(stride > 0 && at <= upper) || (stride < 0 && at >= upper);
|
|
|
|
at += stride) {
|
|
|
|
for (const auto &value :
|
|
|
|
std::get<std::list<parser::AcValue>>(impliedDo.t)) {
|
|
|
|
Add(value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
foldingContext.EndImpliedDo(name);
|
|
|
|
}
|
|
|
|
|
2019-11-22 05:31:52 +08:00
|
|
|
MaybeExpr ArrayConstructorContext::ToExpr() {
|
|
|
|
return common::SearchTypes(std::move(*this));
|
2019-01-23 08:30:32 +08:00
|
|
|
}
|
|
|
|
|
2019-03-08 06:46:31 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(const parser::ArrayConstructor &array) {
|
2019-01-23 08:30:32 +08:00
|
|
|
const parser::AcSpec &acSpec{array.v};
|
2019-11-22 05:31:52 +08:00
|
|
|
ArrayConstructorContext acContext{*this, AnalyzeTypeSpec(acSpec.type)};
|
2019-01-23 08:30:32 +08:00
|
|
|
for (const parser::AcValue &value : acSpec.values) {
|
2019-11-22 05:31:52 +08:00
|
|
|
acContext.Add(value);
|
2019-01-23 08:30:32 +08:00
|
|
|
}
|
2019-11-22 05:31:52 +08:00
|
|
|
return acContext.ToExpr();
|
2018-09-08 01:33:32 +08:00
|
|
|
}
|
|
|
|
|
2019-03-08 06:46:31 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(
|
2019-02-09 08:35:02 +08:00
|
|
|
const parser::StructureConstructor &structure) {
|
2019-02-15 04:51:20 +08:00
|
|
|
auto &parsedType{std::get<parser::DerivedTypeSpec>(structure.t)};
|
2021-04-27 06:20:22 +08:00
|
|
|
parser::Name structureType{std::get<parser::Name>(parsedType.t)};
|
|
|
|
parser::CharBlock &typeName{structureType.source};
|
|
|
|
if (semantics::Symbol * typeSymbol{structureType.symbol}) {
|
|
|
|
if (typeSymbol->has<semantics::DerivedTypeDetails>()) {
|
|
|
|
semantics::DerivedTypeSpec dtSpec{typeName, typeSymbol->GetUltimate()};
|
|
|
|
if (!CheckIsValidForwardReference(dtSpec)) {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-11-10 01:29:31 +08:00
|
|
|
if (!parsedType.derivedTypeSpec) {
|
2019-02-15 04:51:20 +08:00
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
const auto &spec{*parsedType.derivedTypeSpec};
|
|
|
|
const Symbol &typeSymbol{spec.typeSymbol()};
|
2019-11-10 01:29:31 +08:00
|
|
|
if (!spec.scope() || !typeSymbol.has<semantics::DerivedTypeDetails>()) {
|
2020-03-28 05:17:25 +08:00
|
|
|
return std::nullopt; // error recovery
|
2019-06-22 05:07:39 +08:00
|
|
|
}
|
|
|
|
const auto &typeDetails{typeSymbol.get<semantics::DerivedTypeDetails>()};
|
|
|
|
const Symbol *parentComponent{typeDetails.GetParentComponent(*spec.scope())};
|
2019-02-15 04:51:20 +08:00
|
|
|
|
2020-03-28 05:17:25 +08:00
|
|
|
if (typeSymbol.attrs().test(semantics::Attr::ABSTRACT)) { // C796
|
2019-11-02 04:08:16 +08:00
|
|
|
AttachDeclaration(Say(typeName,
|
|
|
|
"ABSTRACT derived type '%s' may not be used in a "
|
|
|
|
"structure constructor"_err_en_US,
|
|
|
|
typeName),
|
2020-07-31 03:32:04 +08:00
|
|
|
typeSymbol); // C7114
|
2019-02-15 06:37:55 +08:00
|
|
|
}
|
|
|
|
|
2019-08-13 03:10:43 +08:00
|
|
|
// This iterator traverses all of the components in the derived type and its
|
2019-02-15 04:51:20 +08:00
|
|
|
// parents. The symbols for whole parent components appear after their
|
|
|
|
// own components and before the components of the types that extend them.
|
|
|
|
// E.g., TYPE :: A; REAL X; END TYPE
|
|
|
|
// TYPE, EXTENDS(A) :: B; REAL Y; END TYPE
|
|
|
|
// produces the component list X, A, Y.
|
|
|
|
// The order is important below because a structure constructor can
|
|
|
|
// initialize X or A by name, but not both.
|
2019-07-31 22:19:22 +08:00
|
|
|
auto components{semantics::OrderedComponentIterator{spec}};
|
2019-02-15 06:37:55 +08:00
|
|
|
auto nextAnonymous{components.begin()};
|
2019-02-15 04:51:20 +08:00
|
|
|
|
|
|
|
std::set<parser::CharBlock> unavailable;
|
|
|
|
bool anyKeyword{false};
|
|
|
|
StructureConstructor result{spec};
|
2020-03-28 05:17:25 +08:00
|
|
|
bool checkConflicts{true}; // until we hit one
|
2020-06-19 08:17:04 +08:00
|
|
|
auto &messages{GetContextualMessages()};
|
2019-02-15 04:51:20 +08:00
|
|
|
|
|
|
|
for (const auto &component :
|
|
|
|
std::get<std::list<parser::ComponentSpec>>(structure.t)) {
|
|
|
|
const parser::Expr &expr{
|
2019-03-06 04:28:08 +08:00
|
|
|
std::get<parser::ComponentDataSource>(component.t).v.value()};
|
2019-02-15 04:51:20 +08:00
|
|
|
parser::CharBlock source{expr.source};
|
2019-03-12 06:39:11 +08:00
|
|
|
auto restorer{messages.SetLocation(source)};
|
2019-02-15 06:37:55 +08:00
|
|
|
const Symbol *symbol{nullptr};
|
2019-06-22 05:07:39 +08:00
|
|
|
MaybeExpr value{Analyze(expr)};
|
|
|
|
std::optional<DynamicType> valueType{DynamicType::From(value)};
|
2019-02-15 04:51:20 +08:00
|
|
|
if (const auto &kw{std::get<std::optional<parser::Keyword>>(component.t)}) {
|
2019-03-12 06:39:11 +08:00
|
|
|
anyKeyword = true;
|
2019-02-15 04:51:20 +08:00
|
|
|
source = kw->v.source;
|
2019-02-15 06:37:55 +08:00
|
|
|
symbol = kw->v.symbol;
|
2019-11-10 01:29:31 +08:00
|
|
|
if (!symbol) {
|
2019-03-12 06:39:11 +08:00
|
|
|
auto componentIter{std::find_if(components.begin(), components.end(),
|
2019-10-23 07:53:29 +08:00
|
|
|
[=](const Symbol &symbol) { return symbol.name() == source; })};
|
2019-03-12 06:39:11 +08:00
|
|
|
if (componentIter != components.end()) {
|
2019-10-23 07:53:29 +08:00
|
|
|
symbol = &*componentIter;
|
2019-03-12 06:39:11 +08:00
|
|
|
}
|
|
|
|
}
|
2020-03-28 05:17:25 +08:00
|
|
|
if (!symbol) { // C7101
|
2019-03-12 06:39:11 +08:00
|
|
|
Say(source,
|
|
|
|
"Keyword '%s=' does not name a component of derived type '%s'"_err_en_US,
|
2019-05-07 00:33:45 +08:00
|
|
|
source, typeName);
|
2019-03-12 06:39:11 +08:00
|
|
|
}
|
2019-02-15 06:37:55 +08:00
|
|
|
} else {
|
2020-03-28 05:17:25 +08:00
|
|
|
if (anyKeyword) { // C7100
|
2019-03-08 06:46:31 +08:00
|
|
|
Say(source,
|
2019-02-15 06:37:55 +08:00
|
|
|
"Value in structure constructor lacks a component name"_err_en_US);
|
2020-03-28 05:17:25 +08:00
|
|
|
checkConflicts = false; // stem cascade
|
2019-02-15 06:37:55 +08:00
|
|
|
}
|
2019-06-22 05:07:39 +08:00
|
|
|
// Here's a regrettably common extension of the standard: anonymous
|
|
|
|
// initialization of parent components, e.g., T(PT(1)) rather than
|
|
|
|
// T(1) or T(PT=PT(1)).
|
2019-11-10 01:29:31 +08:00
|
|
|
if (nextAnonymous == components.begin() && parentComponent &&
|
2019-06-22 05:07:39 +08:00
|
|
|
valueType == DynamicType::From(*parentComponent) &&
|
2019-11-07 07:54:26 +08:00
|
|
|
context().IsEnabled(LanguageFeature::AnonymousParents)) {
|
2019-06-22 05:07:39 +08:00
|
|
|
auto iter{
|
2019-10-23 07:53:29 +08:00
|
|
|
std::find(components.begin(), components.end(), *parentComponent)};
|
2019-06-22 05:07:39 +08:00
|
|
|
if (iter != components.end()) {
|
|
|
|
symbol = parentComponent;
|
|
|
|
nextAnonymous = ++iter;
|
2019-11-07 07:54:26 +08:00
|
|
|
if (context().ShouldWarn(LanguageFeature::AnonymousParents)) {
|
2019-06-22 05:07:39 +08:00
|
|
|
Say(source,
|
|
|
|
"Whole parent component '%s' in structure "
|
2022-03-08 05:57:37 +08:00
|
|
|
"constructor should not be anonymous"_port_en_US,
|
2019-06-22 05:07:39 +08:00
|
|
|
symbol->name());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-11-10 01:29:31 +08:00
|
|
|
while (!symbol && nextAnonymous != components.end()) {
|
2019-10-23 07:53:29 +08:00
|
|
|
const Symbol &next{*nextAnonymous};
|
|
|
|
++nextAnonymous;
|
|
|
|
if (!next.test(Symbol::Flag::ParentComp)) {
|
|
|
|
symbol = &next;
|
2019-02-09 08:35:02 +08:00
|
|
|
}
|
2019-02-15 06:37:55 +08:00
|
|
|
}
|
2019-11-10 01:29:31 +08:00
|
|
|
if (!symbol) {
|
2019-03-08 06:46:31 +08:00
|
|
|
Say(source, "Unexpected value in structure constructor"_err_en_US);
|
2019-02-15 06:37:55 +08:00
|
|
|
}
|
|
|
|
}
|
2019-11-10 01:29:31 +08:00
|
|
|
if (symbol) {
|
2020-03-03 08:43:01 +08:00
|
|
|
if (const auto *currScope{context_.globalScope().FindScope(source)}) {
|
|
|
|
if (auto msg{CheckAccessibleComponent(*currScope, *symbol)}) {
|
|
|
|
Say(source, *msg);
|
|
|
|
}
|
|
|
|
}
|
2019-02-20 07:38:55 +08:00
|
|
|
if (checkConflicts) {
|
2019-02-15 06:37:55 +08:00
|
|
|
auto componentIter{
|
2019-10-23 07:53:29 +08:00
|
|
|
std::find(components.begin(), components.end(), *symbol)};
|
2019-02-15 06:37:55 +08:00
|
|
|
if (unavailable.find(symbol->name()) != unavailable.cend()) {
|
|
|
|
// C797, C798
|
2019-03-08 06:46:31 +08:00
|
|
|
Say(source,
|
2019-02-20 07:38:55 +08:00
|
|
|
"Component '%s' conflicts with another component earlier in "
|
|
|
|
"this structure constructor"_err_en_US,
|
2019-05-07 00:33:45 +08:00
|
|
|
symbol->name());
|
2019-02-15 06:37:55 +08:00
|
|
|
} else if (symbol->test(Symbol::Flag::ParentComp)) {
|
|
|
|
// Make earlier components unavailable once a whole parent appears.
|
|
|
|
for (auto it{components.begin()}; it != componentIter; ++it) {
|
2019-10-23 07:53:29 +08:00
|
|
|
unavailable.insert(it->name());
|
2019-02-15 04:51:20 +08:00
|
|
|
}
|
2019-02-15 06:37:55 +08:00
|
|
|
} else {
|
|
|
|
// Make whole parent components unavailable after any of their
|
|
|
|
// constituents appear.
|
|
|
|
for (auto it{componentIter}; it != components.end(); ++it) {
|
2019-10-23 07:53:29 +08:00
|
|
|
if (it->test(Symbol::Flag::ParentComp)) {
|
|
|
|
unavailable.insert(it->name());
|
2019-02-15 06:37:55 +08:00
|
|
|
}
|
|
|
|
}
|
2019-02-15 04:51:20 +08:00
|
|
|
}
|
2019-02-09 08:35:02 +08:00
|
|
|
}
|
2019-02-15 06:37:55 +08:00
|
|
|
unavailable.insert(symbol->name());
|
2019-11-10 01:29:31 +08:00
|
|
|
if (value) {
|
2019-02-22 04:10:07 +08:00
|
|
|
if (symbol->has<semantics::ProcEntityDetails>()) {
|
2019-03-12 06:39:11 +08:00
|
|
|
CHECK(IsPointer(*symbol));
|
|
|
|
} else if (symbol->has<semantics::ObjectEntityDetails>()) {
|
2019-03-02 09:33:20 +08:00
|
|
|
// C1594(4)
|
2019-03-12 06:39:11 +08:00
|
|
|
const auto &innermost{context_.FindScope(expr.source)};
|
2020-03-03 08:43:01 +08:00
|
|
|
if (const auto *pureProc{FindPureProcedureContaining(innermost)}) {
|
|
|
|
if (const Symbol * pointer{FindPointerComponent(*symbol)}) {
|
2019-03-05 08:28:35 +08:00
|
|
|
if (const Symbol *
|
2020-03-03 08:43:01 +08:00
|
|
|
object{FindExternallyVisibleObject(*value, *pureProc)}) {
|
2019-03-12 06:39:11 +08:00
|
|
|
if (auto *msg{Say(expr.source,
|
2019-11-20 11:10:02 +08:00
|
|
|
"Externally visible object '%s' may not be "
|
2019-03-12 06:39:11 +08:00
|
|
|
"associated with pointer component '%s' in a "
|
2019-12-24 09:12:53 +08:00
|
|
|
"pure procedure"_err_en_US,
|
2019-05-07 00:33:45 +08:00
|
|
|
object->name(), pointer->name())}) {
|
2019-03-12 06:39:11 +08:00
|
|
|
msg->Attach(object->name(), "Object declaration"_en_US)
|
|
|
|
.Attach(pointer->name(), "Pointer declaration"_en_US);
|
2019-03-05 08:28:35 +08:00
|
|
|
}
|
2019-03-02 09:33:20 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-03-12 06:39:11 +08:00
|
|
|
} else if (symbol->has<semantics::TypeParamDetails>()) {
|
|
|
|
Say(expr.source,
|
|
|
|
"Type parameter '%s' may not appear as a component "
|
|
|
|
"of a structure constructor"_err_en_US,
|
2019-05-07 00:33:45 +08:00
|
|
|
symbol->name());
|
2019-03-12 06:39:11 +08:00
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
Say(expr.source,
|
|
|
|
"Component '%s' is neither a procedure pointer "
|
|
|
|
"nor a data object"_err_en_US,
|
2019-05-07 00:33:45 +08:00
|
|
|
symbol->name());
|
2019-03-12 06:39:11 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (IsPointer(*symbol)) {
|
2020-01-07 01:16:18 +08:00
|
|
|
semantics::CheckPointerAssignment(
|
2020-03-28 05:17:25 +08:00
|
|
|
GetFoldingContext(), *symbol, *value); // C7104, C7105
|
2020-01-04 02:38:51 +08:00
|
|
|
result.Add(*symbol, Fold(std::move(*value)));
|
2019-03-12 06:39:11 +08:00
|
|
|
} else if (MaybeExpr converted{
|
|
|
|
ConvertToType(*symbol, std::move(*value))}) {
|
2020-06-19 08:17:04 +08:00
|
|
|
if (auto componentShape{GetShape(GetFoldingContext(), *symbol)}) {
|
|
|
|
if (auto valueShape{GetShape(GetFoldingContext(), *converted)}) {
|
|
|
|
if (GetRank(*componentShape) == 0 && GetRank(*valueShape) > 0) {
|
|
|
|
AttachDeclaration(
|
|
|
|
Say(expr.source,
|
|
|
|
"Rank-%d array value is not compatible with scalar component '%s'"_err_en_US,
|
2020-09-01 02:54:48 +08:00
|
|
|
GetRank(*valueShape), symbol->name()),
|
2020-06-19 08:17:04 +08:00
|
|
|
*symbol);
|
2021-06-04 06:48:16 +08:00
|
|
|
} else {
|
|
|
|
auto checked{
|
|
|
|
CheckConformance(messages, *componentShape, *valueShape,
|
|
|
|
CheckConformanceFlags::RightIsExpandableDeferred,
|
|
|
|
"component", "value")};
|
|
|
|
if (checked && *checked && GetRank(*componentShape) > 0 &&
|
|
|
|
GetRank(*valueShape) == 0 &&
|
2020-06-19 08:17:04 +08:00
|
|
|
!IsExpandableScalar(*converted)) {
|
|
|
|
AttachDeclaration(
|
|
|
|
Say(expr.source,
|
|
|
|
"Scalar value cannot be expanded to shape of array component '%s'"_err_en_US,
|
|
|
|
symbol->name()),
|
|
|
|
*symbol);
|
2021-06-04 06:48:16 +08:00
|
|
|
}
|
|
|
|
if (checked.value_or(true)) {
|
2020-06-19 08:17:04 +08:00
|
|
|
result.Add(*symbol, std::move(*converted));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Say(expr.source, "Shape of value cannot be determined"_err_en_US);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
AttachDeclaration(
|
|
|
|
Say(expr.source,
|
|
|
|
"Shape of component '%s' cannot be determined"_err_en_US,
|
|
|
|
symbol->name()),
|
|
|
|
*symbol);
|
|
|
|
}
|
2021-10-15 01:32:59 +08:00
|
|
|
} else if (IsAllocatable(*symbol) && IsBareNullPointer(&*value)) {
|
2022-03-01 02:24:58 +08:00
|
|
|
// NULL() with no arguments allowed by 7.5.10 para 6 for ALLOCATABLE.
|
|
|
|
result.Add(*symbol, Expr<SomeType>{NullPointer{}});
|
2019-05-14 00:33:18 +08:00
|
|
|
} else if (auto symType{DynamicType::From(symbol)}) {
|
2022-01-05 08:17:15 +08:00
|
|
|
if (IsAllocatable(*symbol) && symType->IsUnlimitedPolymorphic() &&
|
|
|
|
valueType) {
|
|
|
|
// ok
|
|
|
|
} else if (valueType) {
|
2019-11-02 04:08:16 +08:00
|
|
|
AttachDeclaration(
|
|
|
|
Say(expr.source,
|
2022-02-09 03:56:22 +08:00
|
|
|
"Value in structure constructor of type '%s' is "
|
|
|
|
"incompatible with component '%s' of type '%s'"_err_en_US,
|
2019-06-22 05:07:39 +08:00
|
|
|
valueType->AsFortran(), symbol->name(),
|
2019-11-02 04:08:16 +08:00
|
|
|
symType->AsFortran()),
|
2019-11-23 05:20:58 +08:00
|
|
|
*symbol);
|
2019-05-14 00:33:18 +08:00
|
|
|
} else {
|
2019-11-02 04:08:16 +08:00
|
|
|
AttachDeclaration(
|
|
|
|
Say(expr.source,
|
2019-05-14 00:33:18 +08:00
|
|
|
"Value in structure constructor is incompatible with "
|
2022-02-09 03:56:22 +08:00
|
|
|
"component '%s' of type %s"_err_en_US,
|
2019-11-02 04:08:16 +08:00
|
|
|
symbol->name(), symType->AsFortran()),
|
2019-11-23 05:20:58 +08:00
|
|
|
*symbol);
|
2019-02-20 07:38:55 +08:00
|
|
|
}
|
|
|
|
}
|
2019-02-15 06:37:55 +08:00
|
|
|
}
|
2019-02-09 08:35:02 +08:00
|
|
|
}
|
|
|
|
}
|
2019-02-15 04:51:20 +08:00
|
|
|
|
|
|
|
// Ensure that unmentioned component objects have default initializers.
|
2019-10-23 07:53:29 +08:00
|
|
|
for (const Symbol &symbol : components) {
|
|
|
|
if (!symbol.test(Symbol::Flag::ParentComp) &&
|
2022-03-01 02:24:58 +08:00
|
|
|
unavailable.find(symbol.name()) == unavailable.cend()) {
|
|
|
|
if (IsAllocatable(symbol)) {
|
|
|
|
// Set all remaining allocatables to explicit NULL()
|
|
|
|
result.Add(symbol, Expr<SomeType>{NullPointer{}});
|
|
|
|
} else if (const auto *details{
|
|
|
|
symbol.detailsIf<semantics::ObjectEntityDetails>()}) {
|
2019-11-10 01:29:31 +08:00
|
|
|
if (details->init()) {
|
2019-10-23 07:53:29 +08:00
|
|
|
result.Add(symbol, common::Clone(*details->init()));
|
2020-03-28 05:17:25 +08:00
|
|
|
} else { // C799
|
2019-11-02 04:08:16 +08:00
|
|
|
AttachDeclaration(Say(typeName,
|
|
|
|
"Structure constructor lacks a value for "
|
|
|
|
"component '%s'"_err_en_US,
|
|
|
|
symbol.name()),
|
2019-11-23 05:20:58 +08:00
|
|
|
symbol);
|
2019-02-15 04:51:20 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return AsMaybeExpr(Expr<SomeDerived>{std::move(result)});
|
2018-09-13 02:20:30 +08:00
|
|
|
}
|
|
|
|
|
2019-12-06 02:24:18 +08:00
|
|
|
static std::optional<parser::CharBlock> GetPassName(
|
|
|
|
const semantics::Symbol &proc) {
|
2022-03-24 05:05:50 +08:00
|
|
|
return common::visit(
|
2019-12-06 02:24:18 +08:00
|
|
|
[](const auto &details) {
|
|
|
|
if constexpr (std::is_base_of_v<semantics::WithPassArg,
|
|
|
|
std::decay_t<decltype(details)>>) {
|
|
|
|
return details.passName();
|
|
|
|
} else {
|
|
|
|
return std::optional<parser::CharBlock>{};
|
|
|
|
}
|
|
|
|
},
|
|
|
|
proc.details());
|
|
|
|
}
|
|
|
|
|
2019-12-17 03:33:55 +08:00
|
|
|
static int GetPassIndex(const Symbol &proc) {
|
|
|
|
CHECK(!proc.attrs().test(semantics::Attr::NOPASS));
|
|
|
|
std::optional<parser::CharBlock> passName{GetPassName(proc)};
|
2022-03-24 06:20:56 +08:00
|
|
|
const auto *interface { semantics::FindInterface(proc) };
|
2019-12-17 03:33:55 +08:00
|
|
|
if (!passName || !interface) {
|
2020-03-28 05:17:25 +08:00
|
|
|
return 0; // first argument is passed-object
|
2019-12-17 03:33:55 +08:00
|
|
|
}
|
|
|
|
const auto &subp{interface->get<semantics::SubprogramDetails>()};
|
|
|
|
int index{0};
|
|
|
|
for (const auto *arg : subp.dummyArgs()) {
|
|
|
|
if (arg && arg->name() == passName) {
|
|
|
|
return index;
|
2019-12-06 02:24:18 +08:00
|
|
|
}
|
2019-12-17 03:33:55 +08:00
|
|
|
++index;
|
2019-09-17 07:58:13 +08:00
|
|
|
}
|
2019-12-17 03:33:55 +08:00
|
|
|
DIE("PASS argument name not in dummy argument list");
|
2019-09-17 07:58:13 +08:00
|
|
|
}
|
|
|
|
|
2019-12-10 05:52:12 +08:00
|
|
|
// Injects an expression into an actual argument list as the "passed object"
|
|
|
|
// for a type-bound procedure reference that is not NOPASS. Adds an
|
|
|
|
// argument keyword if possible, but not when the passed object goes
|
|
|
|
// before a positional argument.
|
|
|
|
// e.g., obj%tbp(x) -> tbp(obj,x).
|
2019-12-24 09:12:53 +08:00
|
|
|
static void AddPassArg(ActualArguments &actuals, const Expr<SomeDerived> &expr,
|
2019-12-10 05:52:12 +08:00
|
|
|
const Symbol &component, bool isPassedObject = true) {
|
2019-12-10 05:36:19 +08:00
|
|
|
if (component.attrs().test(semantics::Attr::NOPASS)) {
|
2019-12-10 05:52:12 +08:00
|
|
|
return;
|
2019-12-10 05:36:19 +08:00
|
|
|
}
|
2019-12-17 03:33:55 +08:00
|
|
|
int passIndex{GetPassIndex(component)};
|
2019-12-10 05:36:19 +08:00
|
|
|
auto iter{actuals.begin()};
|
|
|
|
int at{0};
|
|
|
|
while (iter < actuals.end() && at < passIndex) {
|
|
|
|
if (*iter && (*iter)->keyword()) {
|
|
|
|
iter = actuals.end();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
++iter;
|
|
|
|
++at;
|
|
|
|
}
|
2019-12-24 09:12:53 +08:00
|
|
|
ActualArgument passed{AsGenericExpr(common::Clone(expr))};
|
2019-12-10 05:52:12 +08:00
|
|
|
passed.set_isPassedObject(isPassedObject);
|
2019-12-17 03:33:55 +08:00
|
|
|
if (iter == actuals.end()) {
|
|
|
|
if (auto passName{GetPassName(component)}) {
|
|
|
|
passed.set_keyword(*passName);
|
|
|
|
}
|
2019-12-10 05:36:19 +08:00
|
|
|
}
|
|
|
|
actuals.emplace(iter, std::move(passed));
|
|
|
|
}
|
|
|
|
|
2019-12-17 03:33:55 +08:00
|
|
|
// Return the compile-time resolution of a procedure binding, if possible.
|
|
|
|
static const Symbol *GetBindingResolution(
|
|
|
|
const std::optional<DynamicType> &baseType, const Symbol &component) {
|
|
|
|
const auto *binding{component.detailsIf<semantics::ProcBindingDetails>()};
|
|
|
|
if (!binding) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
if (!component.attrs().test(semantics::Attr::NON_OVERRIDABLE) &&
|
|
|
|
(!baseType || baseType->IsPolymorphic())) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
return &binding->symbol();
|
|
|
|
}
|
|
|
|
|
2019-09-17 07:58:13 +08:00
|
|
|
auto ExpressionAnalyzer::AnalyzeProcedureComponentRef(
|
2022-05-27 07:56:27 +08:00
|
|
|
const parser::ProcComponentRef &pcr, ActualArguments &&arguments,
|
|
|
|
bool isSubroutine) -> std::optional<CalleeAndArguments> {
|
2019-04-13 07:50:58 +08:00
|
|
|
const parser::StructureComponent &sc{pcr.v.thing};
|
|
|
|
if (MaybeExpr base{Analyze(sc.base)}) {
|
2019-12-10 05:36:19 +08:00
|
|
|
if (const Symbol * sym{sc.component.symbol}) {
|
2020-10-24 04:10:41 +08:00
|
|
|
if (context_.HasError(sym)) {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
2021-06-16 06:15:34 +08:00
|
|
|
if (!IsProcedure(*sym)) {
|
|
|
|
AttachDeclaration(
|
|
|
|
Say(sc.component.source, "'%s' is not a procedure"_err_en_US,
|
|
|
|
sc.component.source),
|
|
|
|
*sym);
|
2021-06-16 06:17:16 +08:00
|
|
|
return std::nullopt;
|
2021-06-16 06:15:34 +08:00
|
|
|
}
|
2019-03-12 06:39:11 +08:00
|
|
|
if (auto *dtExpr{UnwrapExpr<Expr<SomeDerived>>(*base)}) {
|
2019-12-10 05:52:12 +08:00
|
|
|
if (sym->has<semantics::GenericDetails>()) {
|
2019-12-24 09:12:53 +08:00
|
|
|
AdjustActuals adjustment{
|
2019-12-17 03:33:55 +08:00
|
|
|
[&](const Symbol &proc, ActualArguments &actuals) {
|
|
|
|
if (!proc.attrs().test(semantics::Attr::NOPASS)) {
|
|
|
|
AddPassArg(actuals, std::move(*dtExpr), proc);
|
|
|
|
}
|
|
|
|
return true;
|
2019-12-24 09:12:53 +08:00
|
|
|
}};
|
2022-05-27 07:56:27 +08:00
|
|
|
auto pair{ResolveGeneric(*sym, arguments, adjustment, isSubroutine)};
|
2021-10-15 01:32:59 +08:00
|
|
|
sym = pair.first;
|
2022-05-20 06:32:06 +08:00
|
|
|
if (sym) {
|
|
|
|
// re-resolve the name to the specific binding
|
|
|
|
sc.component.symbol = const_cast<Symbol *>(sym);
|
|
|
|
} else {
|
2021-10-15 01:32:59 +08:00
|
|
|
EmitGenericResolutionError(*sc.component.symbol, pair.second);
|
2019-12-10 05:52:12 +08:00
|
|
|
return std::nullopt;
|
2019-12-06 02:24:18 +08:00
|
|
|
}
|
2019-03-12 06:39:11 +08:00
|
|
|
}
|
2022-06-14 10:17:44 +08:00
|
|
|
std::optional<DataRef> dataRef{ExtractDataRef(std::move(*dtExpr))};
|
|
|
|
if (dataRef.has_value() && !CheckRanks(std::move(*dataRef))) {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
2019-12-17 03:33:55 +08:00
|
|
|
if (const Symbol *
|
|
|
|
resolution{GetBindingResolution(dtExpr->GetType(), *sym)}) {
|
2019-12-10 05:52:12 +08:00
|
|
|
AddPassArg(arguments, std::move(*dtExpr), *sym, false);
|
|
|
|
return CalleeAndArguments{
|
|
|
|
ProcedureDesignator{*resolution}, std::move(arguments)};
|
2022-06-14 10:17:44 +08:00
|
|
|
} else if (dataRef.has_value()) {
|
2019-12-10 05:52:12 +08:00
|
|
|
if (sym->attrs().test(semantics::Attr::NOPASS)) {
|
|
|
|
return CalleeAndArguments{
|
|
|
|
ProcedureDesignator{Component{std::move(*dataRef), *sym}},
|
|
|
|
std::move(arguments)};
|
2019-03-12 06:39:11 +08:00
|
|
|
} else {
|
2019-12-10 05:52:12 +08:00
|
|
|
AddPassArg(arguments,
|
|
|
|
Expr<SomeDerived>{Designator<SomeDerived>{std::move(*dataRef)}},
|
|
|
|
*sym);
|
|
|
|
return CalleeAndArguments{
|
|
|
|
ProcedureDesignator{*sym}, std::move(arguments)};
|
2019-03-12 06:39:11 +08:00
|
|
|
}
|
2019-04-13 07:50:58 +08:00
|
|
|
}
|
|
|
|
}
|
2020-09-15 00:59:49 +08:00
|
|
|
Say(sc.component.source,
|
2019-12-10 05:52:12 +08:00
|
|
|
"Base of procedure component reference is not a derived-type object"_err_en_US);
|
2019-04-13 07:50:58 +08:00
|
|
|
}
|
|
|
|
}
|
2021-11-03 07:41:15 +08:00
|
|
|
CHECK(context_.AnyFatalError());
|
2019-04-13 07:50:58 +08:00
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
2019-10-10 07:08:13 +08:00
|
|
|
// Can actual be argument associated with dummy?
|
|
|
|
static bool CheckCompatibleArgument(bool isElemental,
|
|
|
|
const ActualArgument &actual, const characteristics::DummyArgument &dummy) {
|
2021-10-15 01:32:59 +08:00
|
|
|
const auto *expr{actual.UnwrapExpr()};
|
2022-03-24 05:05:50 +08:00
|
|
|
return common::visit(
|
2019-10-10 07:08:13 +08:00
|
|
|
common::visitors{
|
|
|
|
[&](const characteristics::DummyDataObject &x) {
|
2021-10-15 01:32:59 +08:00
|
|
|
if (x.attrs.test(characteristics::DummyDataObject::Attr::Pointer) &&
|
|
|
|
IsBareNullPointer(expr)) {
|
|
|
|
// NULL() without MOLD= is compatible with any dummy data pointer
|
|
|
|
// but cannot be allowed to lead to ambiguity.
|
|
|
|
return true;
|
|
|
|
} else if (!isElemental && actual.Rank() != x.type.Rank() &&
|
2021-01-31 02:14:07 +08:00
|
|
|
!x.type.attrs().test(
|
|
|
|
characteristics::TypeAndShape::Attr::AssumedRank)) {
|
2019-10-10 07:08:13 +08:00
|
|
|
return false;
|
|
|
|
} else if (auto actualType{actual.GetType()}) {
|
2021-01-31 02:14:07 +08:00
|
|
|
return x.type.type().IsTkCompatibleWith(*actualType);
|
2019-10-10 07:08:13 +08:00
|
|
|
}
|
2021-10-15 01:32:59 +08:00
|
|
|
return false;
|
2019-10-10 07:08:13 +08:00
|
|
|
},
|
|
|
|
[&](const characteristics::DummyProcedure &) {
|
2021-03-13 05:51:33 +08:00
|
|
|
return expr && IsProcedurePointerTarget(*expr);
|
2019-10-10 07:08:13 +08:00
|
|
|
},
|
|
|
|
[&](const characteristics::AlternateReturn &) {
|
2019-12-06 02:24:18 +08:00
|
|
|
return actual.isAlternateReturn();
|
2019-10-10 07:08:13 +08:00
|
|
|
},
|
|
|
|
},
|
|
|
|
dummy.u);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Are the actual arguments compatible with the dummy arguments of procedure?
|
|
|
|
static bool CheckCompatibleArguments(
|
|
|
|
const characteristics::Procedure &procedure,
|
|
|
|
const ActualArguments &actuals) {
|
|
|
|
bool isElemental{procedure.IsElemental()};
|
|
|
|
const auto &dummies{procedure.dummyArguments};
|
|
|
|
CHECK(dummies.size() == actuals.size());
|
|
|
|
for (std::size_t i{0}; i < dummies.size(); ++i) {
|
|
|
|
const characteristics::DummyArgument &dummy{dummies[i]};
|
|
|
|
const std::optional<ActualArgument> &actual{actuals[i]};
|
|
|
|
if (actual && !CheckCompatibleArgument(isElemental, *actual, dummy)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-03-14 03:19:44 +08:00
|
|
|
// Handles a forward reference to a module function from what must
|
|
|
|
// be a specification expression. Return false if the symbol is
|
|
|
|
// an invalid forward reference.
|
|
|
|
bool ExpressionAnalyzer::ResolveForward(const Symbol &symbol) {
|
|
|
|
if (context_.HasError(symbol)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (const auto *details{
|
|
|
|
symbol.detailsIf<semantics::SubprogramNameDetails>()}) {
|
|
|
|
if (details->kind() == semantics::SubprogramKind::Module) {
|
|
|
|
// If this symbol is still a SubprogramNameDetails, we must be
|
|
|
|
// checking a specification expression in a sibling module
|
|
|
|
// procedure. Resolve its names now so that its interface
|
|
|
|
// is known.
|
|
|
|
semantics::ResolveSpecificationParts(context_, symbol);
|
|
|
|
if (symbol.has<semantics::SubprogramNameDetails>()) {
|
|
|
|
// When the symbol hasn't had its details updated, we must have
|
|
|
|
// already been in the process of resolving the function's
|
|
|
|
// specification part; but recursive function calls are not
|
|
|
|
// allowed in specification parts (10.1.11 para 5).
|
|
|
|
Say("The module function '%s' may not be referenced recursively in a specification expression"_err_en_US,
|
|
|
|
symbol.name());
|
2020-08-29 01:30:23 +08:00
|
|
|
context_.SetError(symbol);
|
2020-03-14 03:19:44 +08:00
|
|
|
return false;
|
|
|
|
}
|
2020-03-28 05:17:25 +08:00
|
|
|
} else { // 10.1.11 para 4
|
2020-03-14 03:19:44 +08:00
|
|
|
Say("The internal function '%s' may not be referenced in a specification expression"_err_en_US,
|
|
|
|
symbol.name());
|
2020-08-29 01:30:23 +08:00
|
|
|
context_.SetError(symbol);
|
2020-03-14 03:19:44 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-12-10 05:36:19 +08:00
|
|
|
// Resolve a call to a generic procedure with given actual arguments.
|
2019-12-17 03:33:55 +08:00
|
|
|
// adjustActuals is called on procedure bindings to handle pass arg.
|
2021-10-15 01:32:59 +08:00
|
|
|
std::pair<const Symbol *, bool> ExpressionAnalyzer::ResolveGeneric(
|
|
|
|
const Symbol &symbol, const ActualArguments &actuals,
|
2022-05-27 07:56:27 +08:00
|
|
|
const AdjustActuals &adjustActuals, bool isSubroutine,
|
|
|
|
bool mightBeStructureConstructor) {
|
2020-03-28 05:17:25 +08:00
|
|
|
const Symbol *elemental{nullptr}; // matching elemental specific proc
|
2021-10-15 01:32:59 +08:00
|
|
|
const Symbol *nonElemental{nullptr}; // matching non-elemental specific
|
2022-05-27 07:56:27 +08:00
|
|
|
const Symbol &ultimate{symbol.GetUltimate()};
|
|
|
|
// Check for a match with an explicit INTRINSIC
|
|
|
|
if (ultimate.attrs().test(semantics::Attr::INTRINSIC)) {
|
|
|
|
parser::Messages buffer;
|
|
|
|
auto restorer{foldingContext_.messages().SetMessages(buffer)};
|
|
|
|
ActualArguments localActuals{actuals};
|
|
|
|
if (context_.intrinsics().Probe(
|
|
|
|
CallCharacteristics{ultimate.name().ToString(), isSubroutine},
|
|
|
|
localActuals, foldingContext_) &&
|
|
|
|
!buffer.AnyFatalError()) {
|
|
|
|
return {&ultimate, false};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (const auto *details{ultimate.detailsIf<semantics::GenericDetails>()}) {
|
|
|
|
bool anyBareNullActual{
|
|
|
|
std::find_if(actuals.begin(), actuals.end(), [](auto iter) {
|
|
|
|
return IsBareNullPointer(iter->UnwrapExpr());
|
|
|
|
}) != actuals.end()};
|
|
|
|
for (const Symbol &specific : details->specificProcs()) {
|
|
|
|
if (!ResolveForward(specific)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (std::optional<characteristics::Procedure> procedure{
|
|
|
|
characteristics::Procedure::Characterize(
|
|
|
|
ProcedureDesignator{specific}, context_.foldingContext())}) {
|
|
|
|
ActualArguments localActuals{actuals};
|
|
|
|
if (specific.has<semantics::ProcBindingDetails>()) {
|
|
|
|
if (!adjustActuals.value()(specific, localActuals)) {
|
|
|
|
continue;
|
|
|
|
}
|
2021-10-15 01:32:59 +08:00
|
|
|
}
|
2022-05-27 07:56:27 +08:00
|
|
|
if (semantics::CheckInterfaceForGeneric(*procedure, localActuals,
|
|
|
|
GetFoldingContext(), false /* no integer conversions */) &&
|
|
|
|
CheckCompatibleArguments(*procedure, localActuals)) {
|
|
|
|
if ((procedure->IsElemental() && elemental) ||
|
|
|
|
(!procedure->IsElemental() && nonElemental)) {
|
|
|
|
// 16.9.144(6): a bare NULL() is not allowed as an actual
|
|
|
|
// argument to a generic procedure if the specific procedure
|
|
|
|
// cannot be unambiguously distinguished
|
|
|
|
return {nullptr, true /* due to NULL actuals */};
|
|
|
|
}
|
|
|
|
if (!procedure->IsElemental()) {
|
|
|
|
// takes priority over elemental match
|
|
|
|
nonElemental = &specific;
|
|
|
|
if (!anyBareNullActual) {
|
|
|
|
break; // unambiguous case
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
elemental = &specific;
|
2019-10-23 00:31:33 +08:00
|
|
|
}
|
2019-10-10 07:08:13 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-05-27 07:56:27 +08:00
|
|
|
if (nonElemental) {
|
|
|
|
return {&AccessSpecific(symbol, *nonElemental), false};
|
|
|
|
} else if (elemental) {
|
|
|
|
return {&AccessSpecific(symbol, *elemental), false};
|
|
|
|
}
|
|
|
|
// Check parent derived type
|
|
|
|
if (const auto *parentScope{symbol.owner().GetDerivedTypeParent()}) {
|
|
|
|
if (const Symbol * extended{parentScope->FindComponent(symbol.name())}) {
|
|
|
|
auto pair{ResolveGeneric(
|
|
|
|
*extended, actuals, adjustActuals, isSubroutine, false)};
|
2021-10-15 01:32:59 +08:00
|
|
|
if (pair.first) {
|
|
|
|
return pair;
|
2019-12-21 05:03:30 +08:00
|
|
|
}
|
2019-12-10 05:52:12 +08:00
|
|
|
}
|
|
|
|
}
|
2022-05-27 07:56:27 +08:00
|
|
|
if (mightBeStructureConstructor && details->derivedType()) {
|
|
|
|
return {details->derivedType(), false};
|
|
|
|
}
|
2019-12-10 05:52:12 +08:00
|
|
|
}
|
2022-05-27 07:56:27 +08:00
|
|
|
// Check for generic or explicit INTRINSIC of the same name in outer scopes.
|
|
|
|
// See 15.5.5.2 for details.
|
|
|
|
if (!symbol.owner().IsGlobal() && !symbol.owner().IsDerivedType()) {
|
|
|
|
for (const std::string &n : GetAllNames(context_, symbol.name())) {
|
|
|
|
if (const Symbol * outer{symbol.owner().parent().FindSymbol(n)}) {
|
|
|
|
auto pair{ResolveGeneric(*outer, actuals, adjustActuals, isSubroutine,
|
|
|
|
mightBeStructureConstructor)};
|
|
|
|
if (pair.first) {
|
|
|
|
return pair;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-12-21 05:03:30 +08:00
|
|
|
}
|
2021-10-15 01:32:59 +08:00
|
|
|
return {nullptr, false};
|
2019-12-24 09:12:53 +08:00
|
|
|
}
|
|
|
|
|
2021-01-16 03:52:10 +08:00
|
|
|
const Symbol &ExpressionAnalyzer::AccessSpecific(
|
|
|
|
const Symbol &originalGeneric, const Symbol &specific) {
|
|
|
|
if (const auto *hosted{
|
|
|
|
originalGeneric.detailsIf<semantics::HostAssocDetails>()}) {
|
|
|
|
return AccessSpecific(hosted->symbol(), specific);
|
|
|
|
} else if (const auto *used{
|
|
|
|
originalGeneric.detailsIf<semantics::UseDetails>()}) {
|
|
|
|
const auto &scope{originalGeneric.owner()};
|
2021-02-11 08:28:34 +08:00
|
|
|
if (auto iter{scope.find(specific.name())}; iter != scope.end()) {
|
|
|
|
if (const auto *useDetails{
|
|
|
|
iter->second->detailsIf<semantics::UseDetails>()}) {
|
|
|
|
const Symbol &usedSymbol{useDetails->symbol()};
|
|
|
|
const auto *usedGeneric{
|
|
|
|
usedSymbol.detailsIf<semantics::GenericDetails>()};
|
|
|
|
if (&usedSymbol == &specific ||
|
|
|
|
(usedGeneric && usedGeneric->specific() == &specific)) {
|
|
|
|
return specific;
|
|
|
|
}
|
|
|
|
}
|
2021-01-16 03:52:10 +08:00
|
|
|
}
|
2021-02-11 08:28:34 +08:00
|
|
|
// Create a renaming USE of the specific procedure.
|
|
|
|
auto rename{context_.SaveTempName(
|
|
|
|
used->symbol().owner().GetName().value().ToString() + "$" +
|
2022-06-11 23:39:38 +08:00
|
|
|
specific.owner().GetName().value().ToString() + "$" +
|
2021-02-11 08:28:34 +08:00
|
|
|
specific.name().ToString())};
|
|
|
|
return *const_cast<semantics::Scope &>(scope)
|
|
|
|
.try_emplace(rename, specific.attrs(),
|
|
|
|
semantics::UseDetails{rename, specific})
|
|
|
|
.first->second;
|
2021-01-16 03:52:10 +08:00
|
|
|
} else {
|
|
|
|
return specific;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-15 01:32:59 +08:00
|
|
|
void ExpressionAnalyzer::EmitGenericResolutionError(
|
|
|
|
const Symbol &symbol, bool dueToNullActuals) {
|
|
|
|
Say(dueToNullActuals
|
|
|
|
? "One or more NULL() actual arguments to the generic procedure '%s' requires a MOLD= for disambiguation"_err_en_US
|
|
|
|
: semantics::IsGenericDefinedOp(symbol)
|
|
|
|
? "No specific procedure of generic operator '%s' matches the actual arguments"_err_en_US
|
|
|
|
: "No specific procedure of generic '%s' matches the actual arguments"_err_en_US,
|
|
|
|
symbol.name());
|
2019-10-10 07:08:13 +08:00
|
|
|
}
|
|
|
|
|
2019-09-17 07:58:13 +08:00
|
|
|
auto ExpressionAnalyzer::GetCalleeAndArguments(
|
|
|
|
const parser::ProcedureDesignator &pd, ActualArguments &&arguments,
|
2019-12-21 05:03:30 +08:00
|
|
|
bool isSubroutine, bool mightBeStructureConstructor)
|
|
|
|
-> std::optional<CalleeAndArguments> {
|
2022-03-24 05:05:50 +08:00
|
|
|
return common::visit(
|
2018-09-21 03:34:29 +08:00
|
|
|
common::visitors{
|
2019-10-23 00:31:33 +08:00
|
|
|
[&](const parser::Name &name) {
|
2019-12-21 05:03:30 +08:00
|
|
|
return GetCalleeAndArguments(name, std::move(arguments),
|
|
|
|
isSubroutine, mightBeStructureConstructor);
|
2018-09-21 03:34:29 +08:00
|
|
|
},
|
2019-10-10 07:08:13 +08:00
|
|
|
[&](const parser::ProcComponentRef &pcr) {
|
2022-05-27 07:56:27 +08:00
|
|
|
return AnalyzeProcedureComponentRef(
|
|
|
|
pcr, std::move(arguments), isSubroutine);
|
2018-09-21 03:34:29 +08:00
|
|
|
},
|
|
|
|
},
|
|
|
|
pd.u);
|
|
|
|
}
|
|
|
|
|
2019-12-21 05:03:30 +08:00
|
|
|
auto ExpressionAnalyzer::GetCalleeAndArguments(const parser::Name &name,
|
|
|
|
ActualArguments &&arguments, bool isSubroutine,
|
|
|
|
bool mightBeStructureConstructor) -> std::optional<CalleeAndArguments> {
|
2019-10-23 00:31:33 +08:00
|
|
|
const Symbol *symbol{name.symbol};
|
|
|
|
if (context_.HasError(symbol)) {
|
2020-03-28 05:17:25 +08:00
|
|
|
return std::nullopt; // also handles null symbol
|
2019-10-23 00:31:33 +08:00
|
|
|
}
|
2019-12-12 07:06:24 +08:00
|
|
|
const Symbol &ultimate{DEREF(symbol).GetUltimate()};
|
2022-04-04 21:44:05 +08:00
|
|
|
CheckForBadRecursion(name.source, ultimate);
|
|
|
|
bool dueToNullActual{false};
|
|
|
|
bool isGenericInterface{ultimate.has<semantics::GenericDetails>()};
|
2022-05-27 07:56:27 +08:00
|
|
|
bool isExplicitIntrinsic{ultimate.attrs().test(semantics::Attr::INTRINSIC)};
|
2022-04-04 21:44:05 +08:00
|
|
|
const Symbol *resolution{nullptr};
|
2022-05-27 07:56:27 +08:00
|
|
|
if (isGenericInterface || isExplicitIntrinsic) {
|
2022-04-04 21:44:05 +08:00
|
|
|
ExpressionAnalyzer::AdjustActuals noAdjustment;
|
2022-05-27 07:56:27 +08:00
|
|
|
auto pair{ResolveGeneric(*symbol, arguments, noAdjustment, isSubroutine,
|
|
|
|
mightBeStructureConstructor)};
|
2022-04-04 21:44:05 +08:00
|
|
|
resolution = pair.first;
|
|
|
|
dueToNullActual = pair.second;
|
2022-05-20 06:32:06 +08:00
|
|
|
if (resolution) {
|
|
|
|
// re-resolve name to the specific procedure
|
|
|
|
name.symbol = const_cast<Symbol *>(resolution);
|
|
|
|
}
|
2022-05-27 07:56:27 +08:00
|
|
|
} else {
|
|
|
|
resolution = symbol;
|
2022-04-04 21:44:05 +08:00
|
|
|
}
|
2022-05-27 07:56:27 +08:00
|
|
|
if (!resolution || resolution->attrs().test(semantics::Attr::INTRINSIC)) {
|
2022-04-04 21:44:05 +08:00
|
|
|
// Not generic, or no resolution; may be intrinsic
|
2022-05-27 07:56:27 +08:00
|
|
|
if (std::optional<SpecificCall> specificCall{context_.intrinsics().Probe(
|
|
|
|
CallCharacteristics{ultimate.name().ToString(), isSubroutine},
|
|
|
|
arguments, GetFoldingContext())}) {
|
2022-04-16 11:59:13 +08:00
|
|
|
CheckBadExplicitType(*specificCall, *symbol);
|
|
|
|
return CalleeAndArguments{
|
|
|
|
ProcedureDesignator{std::move(specificCall->specificIntrinsic)},
|
|
|
|
std::move(specificCall->arguments)};
|
|
|
|
} else {
|
|
|
|
if (isGenericInterface) {
|
|
|
|
EmitGenericResolutionError(*symbol, dueToNullActual);
|
2021-06-16 06:15:34 +08:00
|
|
|
}
|
2022-04-04 21:44:05 +08:00
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (resolution->GetUltimate().has<semantics::DerivedTypeDetails>()) {
|
|
|
|
if (mightBeStructureConstructor) {
|
|
|
|
return CalleeAndArguments{
|
|
|
|
semantics::SymbolRef{*resolution}, std::move(arguments)};
|
2019-10-23 00:31:33 +08:00
|
|
|
}
|
2022-04-04 21:44:05 +08:00
|
|
|
} else if (IsProcedure(*resolution)) {
|
|
|
|
return CalleeAndArguments{
|
|
|
|
ProcedureDesignator{*resolution}, std::move(arguments)};
|
|
|
|
}
|
|
|
|
if (!context_.HasError(*resolution)) {
|
|
|
|
AttachDeclaration(
|
|
|
|
Say(name.source, "'%s' is not a callable procedure"_err_en_US,
|
|
|
|
name.source),
|
|
|
|
*resolution);
|
2019-10-23 00:31:33 +08:00
|
|
|
}
|
2019-12-21 05:03:30 +08:00
|
|
|
return std::nullopt;
|
2019-10-23 00:31:33 +08:00
|
|
|
}
|
|
|
|
|
2021-02-18 02:24:14 +08:00
|
|
|
// Fortran 2018 expressly states (8.2 p3) that any declared type for a
|
|
|
|
// generic intrinsic function "has no effect" on the result type of a
|
|
|
|
// call to that intrinsic. So one can declare "character*8 cos" and
|
|
|
|
// still get a real result from "cos(1.)". This is a dangerous feature,
|
|
|
|
// especially since implementations are free to extend their sets of
|
|
|
|
// intrinsics, and in doing so might clash with a name in a program.
|
|
|
|
// So we emit a warning in this situation, and perhaps it should be an
|
|
|
|
// error -- any correctly working program can silence the message by
|
|
|
|
// simply deleting the pointless type declaration.
|
|
|
|
void ExpressionAnalyzer::CheckBadExplicitType(
|
|
|
|
const SpecificCall &call, const Symbol &intrinsic) {
|
|
|
|
if (intrinsic.GetUltimate().GetType()) {
|
|
|
|
const auto &procedure{call.specificIntrinsic.characteristics.value()};
|
|
|
|
if (const auto &result{procedure.functionResult}) {
|
|
|
|
if (const auto *typeAndShape{result->GetTypeAndShape()}) {
|
|
|
|
if (auto declared{
|
|
|
|
typeAndShape->Characterize(intrinsic, GetFoldingContext())}) {
|
|
|
|
if (!declared->type().IsTkCompatibleWith(typeAndShape->type())) {
|
|
|
|
if (auto *msg{Say(
|
2022-03-08 05:57:37 +08:00
|
|
|
"The result type '%s' of the intrinsic function '%s' is not the explicit declared type '%s'"_warn_en_US,
|
2021-02-18 02:24:14 +08:00
|
|
|
typeAndShape->AsFortran(), intrinsic.name(),
|
|
|
|
declared->AsFortran())}) {
|
|
|
|
msg->Attach(intrinsic.name(),
|
|
|
|
"Ignored declaration of intrinsic function '%s'"_en_US,
|
|
|
|
intrinsic.name());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-17 07:58:13 +08:00
|
|
|
void ExpressionAnalyzer::CheckForBadRecursion(
|
|
|
|
parser::CharBlock callSite, const semantics::Symbol &proc) {
|
|
|
|
if (const auto *scope{proc.scope()}) {
|
|
|
|
if (scope->sourceRange().Contains(callSite)) {
|
|
|
|
parser::Message *msg{nullptr};
|
2020-03-28 05:17:25 +08:00
|
|
|
if (proc.attrs().test(semantics::Attr::NON_RECURSIVE)) { // 15.6.2.1(3)
|
2019-09-17 07:58:13 +08:00
|
|
|
msg = Say("NON_RECURSIVE procedure '%s' cannot call itself"_err_en_US,
|
|
|
|
callSite);
|
2020-03-20 07:31:10 +08:00
|
|
|
} else if (IsAssumedLengthCharacter(proc) && IsExternal(proc)) {
|
2022-05-19 06:44:01 +08:00
|
|
|
// TODO: Also catch assumed PDT type parameters
|
2020-03-28 05:17:25 +08:00
|
|
|
msg = Say( // 15.6.2.1(3)
|
2019-09-17 07:58:13 +08:00
|
|
|
"Assumed-length CHARACTER(*) function '%s' cannot call itself"_err_en_US,
|
|
|
|
callSite);
|
|
|
|
}
|
2019-11-23 05:20:58 +08:00
|
|
|
AttachDeclaration(msg, proc);
|
2019-09-17 07:58:13 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-28 05:17:25 +08:00
|
|
|
template <typename A> static const Symbol *AssumedTypeDummy(const A &x) {
|
2019-03-12 06:39:11 +08:00
|
|
|
if (const auto *designator{
|
2019-06-04 03:18:52 +08:00
|
|
|
std::get_if<common::Indirection<parser::Designator>>(&x.u)}) {
|
2019-03-12 06:39:11 +08:00
|
|
|
if (const auto *dataRef{
|
|
|
|
std::get_if<parser::DataRef>(&designator->value().u)}) {
|
|
|
|
if (const auto *name{std::get_if<parser::Name>(&dataRef->u)}) {
|
2021-03-16 16:47:35 +08:00
|
|
|
return AssumedTypeDummy(*name);
|
2019-03-12 06:39:11 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
2021-03-16 16:47:35 +08:00
|
|
|
template <>
|
|
|
|
const Symbol *AssumedTypeDummy<parser::Name>(const parser::Name &name) {
|
|
|
|
if (const Symbol * symbol{name.symbol}) {
|
|
|
|
if (const auto *type{symbol->GetType()}) {
|
|
|
|
if (type->category() == semantics::DeclTypeSpec::TypeStar) {
|
|
|
|
return symbol;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
template <typename A>
|
|
|
|
static const Symbol *AssumedTypePointerOrAllocatableDummy(const A &object) {
|
|
|
|
// It is illegal for allocatable of pointer objects to be TYPE(*), but at that
|
|
|
|
// point it is is not guaranteed that it has been checked the object has
|
|
|
|
// POINTER or ALLOCATABLE attribute, so do not assume nullptr can be directly
|
|
|
|
// returned.
|
2022-03-24 05:05:50 +08:00
|
|
|
return common::visit(
|
2021-03-16 16:47:35 +08:00
|
|
|
common::visitors{
|
|
|
|
[&](const parser::StructureComponent &x) {
|
|
|
|
return AssumedTypeDummy(x.component);
|
|
|
|
},
|
|
|
|
[&](const parser::Name &x) { return AssumedTypeDummy(x); },
|
|
|
|
},
|
|
|
|
object.u);
|
|
|
|
}
|
|
|
|
template <>
|
|
|
|
const Symbol *AssumedTypeDummy<parser::AllocateObject>(
|
|
|
|
const parser::AllocateObject &x) {
|
|
|
|
return AssumedTypePointerOrAllocatableDummy(x);
|
|
|
|
}
|
|
|
|
template <>
|
|
|
|
const Symbol *AssumedTypeDummy<parser::PointerObject>(
|
|
|
|
const parser::PointerObject &x) {
|
|
|
|
return AssumedTypePointerOrAllocatableDummy(x);
|
|
|
|
}
|
2019-03-12 06:39:11 +08:00
|
|
|
|
2021-04-27 06:20:22 +08:00
|
|
|
bool ExpressionAnalyzer::CheckIsValidForwardReference(
|
|
|
|
const semantics::DerivedTypeSpec &dtSpec) {
|
|
|
|
if (dtSpec.IsForwardReferenced()) {
|
|
|
|
Say("Cannot construct value for derived type '%s' "
|
|
|
|
"before it is defined"_err_en_US,
|
|
|
|
dtSpec.name());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-12-21 05:03:30 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(const parser::FunctionReference &funcRef,
|
|
|
|
std::optional<parser::StructureConstructor> *structureConstructor) {
|
|
|
|
const parser::Call &call{funcRef.v};
|
|
|
|
auto restorer{GetContextualMessages().SetLocation(call.source)};
|
2020-08-19 01:47:52 +08:00
|
|
|
ArgumentAnalyzer analyzer{*this, call.source, true /* isProcedureCall */};
|
2019-12-21 05:03:30 +08:00
|
|
|
for (const auto &arg : std::get<std::list<parser::ActualArgSpec>>(call.t)) {
|
|
|
|
analyzer.Analyze(arg, false /* not subroutine call */);
|
2019-12-18 02:53:20 +08:00
|
|
|
}
|
2019-12-21 05:03:30 +08:00
|
|
|
if (analyzer.fatalErrors()) {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
if (std::optional<CalleeAndArguments> callee{
|
|
|
|
GetCalleeAndArguments(std::get<parser::ProcedureDesignator>(call.t),
|
|
|
|
analyzer.GetActuals(), false /* not subroutine */,
|
|
|
|
true /* might be structure constructor */)}) {
|
|
|
|
if (auto *proc{std::get_if<ProcedureDesignator>(&callee->u)}) {
|
|
|
|
return MakeFunctionRef(
|
|
|
|
call.source, std::move(*proc), std::move(callee->arguments));
|
2021-06-03 08:28:50 +08:00
|
|
|
}
|
|
|
|
CHECK(std::holds_alternative<semantics::SymbolRef>(callee->u));
|
|
|
|
const Symbol &symbol{*std::get<semantics::SymbolRef>(callee->u)};
|
|
|
|
if (structureConstructor) {
|
2019-12-21 05:03:30 +08:00
|
|
|
// Structure constructor misparsed as function reference?
|
|
|
|
const auto &designator{std::get<parser::ProcedureDesignator>(call.t)};
|
|
|
|
if (const auto *name{std::get_if<parser::Name>(&designator.u)}) {
|
|
|
|
semantics::Scope &scope{context_.FindScope(name->source)};
|
2021-06-03 08:28:50 +08:00
|
|
|
semantics::DerivedTypeSpec dtSpec{name->source, symbol.GetUltimate()};
|
2021-04-27 06:20:22 +08:00
|
|
|
if (!CheckIsValidForwardReference(dtSpec)) {
|
2020-09-12 02:02:04 +08:00
|
|
|
return std::nullopt;
|
|
|
|
}
|
2019-12-21 05:03:30 +08:00
|
|
|
const semantics::DeclTypeSpec &type{
|
2021-04-08 04:17:39 +08:00
|
|
|
semantics::FindOrInstantiateDerivedType(scope, std::move(dtSpec))};
|
2019-12-21 05:03:30 +08:00
|
|
|
auto &mutableRef{const_cast<parser::FunctionReference &>(funcRef)};
|
|
|
|
*structureConstructor =
|
|
|
|
mutableRef.ConvertToStructureConstructor(type.derivedTypeSpec());
|
|
|
|
return Analyze(structureConstructor->value());
|
|
|
|
}
|
|
|
|
}
|
2021-06-03 08:28:50 +08:00
|
|
|
if (!context_.HasError(symbol)) {
|
|
|
|
AttachDeclaration(
|
|
|
|
Say("'%s' is called like a function but is not a procedure"_err_en_US,
|
|
|
|
symbol.name()),
|
|
|
|
symbol);
|
|
|
|
context_.SetError(symbol);
|
|
|
|
}
|
2019-12-21 05:03:30 +08:00
|
|
|
}
|
|
|
|
return std::nullopt;
|
2019-09-05 03:45:08 +08:00
|
|
|
}
|
|
|
|
|
2021-01-05 01:35:15 +08:00
|
|
|
static bool HasAlternateReturns(const evaluate::ActualArguments &args) {
|
|
|
|
for (const auto &arg : args) {
|
|
|
|
if (arg && arg->isAlternateReturn()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-12-21 05:03:30 +08:00
|
|
|
void ExpressionAnalyzer::Analyze(const parser::CallStmt &callStmt) {
|
|
|
|
const parser::Call &call{callStmt.v};
|
2019-12-12 07:06:24 +08:00
|
|
|
auto restorer{GetContextualMessages().SetLocation(call.source)};
|
2020-08-19 01:47:52 +08:00
|
|
|
ArgumentAnalyzer analyzer{*this, call.source, true /* isProcedureCall */};
|
2020-07-02 08:28:00 +08:00
|
|
|
const auto &actualArgList{std::get<std::list<parser::ActualArgSpec>>(call.t)};
|
|
|
|
for (const auto &arg : actualArgList) {
|
2019-12-21 05:03:30 +08:00
|
|
|
analyzer.Analyze(arg, true /* is subroutine call */);
|
2019-10-23 00:31:33 +08:00
|
|
|
}
|
2019-11-03 00:56:46 +08:00
|
|
|
if (!analyzer.fatalErrors()) {
|
2019-09-17 07:58:13 +08:00
|
|
|
if (std::optional<CalleeAndArguments> callee{
|
|
|
|
GetCalleeAndArguments(std::get<parser::ProcedureDesignator>(call.t),
|
2019-12-21 05:03:30 +08:00
|
|
|
analyzer.GetActuals(), true /* subroutine */)}) {
|
|
|
|
ProcedureDesignator *proc{std::get_if<ProcedureDesignator>(&callee->u)};
|
|
|
|
CHECK(proc);
|
|
|
|
if (CheckCall(call.source, *proc, callee->arguments)) {
|
2020-07-10 02:08:41 +08:00
|
|
|
callStmt.typedCall.Reset(
|
|
|
|
new ProcedureRef{std::move(*proc), std::move(callee->arguments),
|
2022-02-10 02:33:20 +08:00
|
|
|
HasAlternateReturns(callee->arguments)},
|
2020-07-10 02:08:41 +08:00
|
|
|
ProcedureRef::Deleter);
|
2022-02-10 02:33:20 +08:00
|
|
|
return;
|
2019-09-05 03:45:08 +08:00
|
|
|
}
|
|
|
|
}
|
2022-02-10 02:33:20 +08:00
|
|
|
if (!context_.AnyFatalError()) {
|
|
|
|
std::string buf;
|
|
|
|
llvm::raw_string_ostream dump{buf};
|
|
|
|
parser::DumpTree(dump, callStmt);
|
|
|
|
Say("Internal error: Expression analysis failed on CALL statement: %s"_err_en_US,
|
|
|
|
dump.str());
|
|
|
|
}
|
2019-09-05 03:45:08 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-18 07:59:25 +08:00
|
|
|
const Assignment *ExpressionAnalyzer::Analyze(const parser::AssignmentStmt &x) {
|
|
|
|
if (!x.typedAssignment) {
|
|
|
|
ArgumentAnalyzer analyzer{*this};
|
|
|
|
analyzer.Analyze(std::get<parser::Variable>(x.t));
|
|
|
|
analyzer.Analyze(std::get<parser::Expr>(x.t));
|
2021-09-08 03:17:31 +08:00
|
|
|
std::optional<Assignment> assignment;
|
|
|
|
if (!analyzer.fatalErrors()) {
|
2019-12-18 07:59:25 +08:00
|
|
|
std::optional<ProcedureRef> procRef{analyzer.TryDefinedAssignment()};
|
2021-09-08 03:17:31 +08:00
|
|
|
if (!procRef) {
|
|
|
|
analyzer.CheckForNullPointer(
|
|
|
|
"in a non-pointer intrinsic assignment statement");
|
|
|
|
}
|
|
|
|
assignment.emplace(analyzer.MoveExpr(0), analyzer.MoveExpr(1));
|
2020-02-19 07:20:28 +08:00
|
|
|
if (procRef) {
|
2021-09-08 03:17:31 +08:00
|
|
|
assignment->u = std::move(*procRef);
|
2020-02-19 07:20:28 +08:00
|
|
|
}
|
2019-12-18 07:59:25 +08:00
|
|
|
}
|
2021-09-08 03:17:31 +08:00
|
|
|
x.typedAssignment.Reset(new GenericAssignmentWrapper{std::move(assignment)},
|
|
|
|
GenericAssignmentWrapper::Deleter);
|
2019-11-23 08:46:11 +08:00
|
|
|
}
|
2020-01-15 09:39:29 +08:00
|
|
|
return common::GetPtrFromOptional(x.typedAssignment->v);
|
2019-11-23 08:46:11 +08:00
|
|
|
}
|
|
|
|
|
2020-01-04 02:38:51 +08:00
|
|
|
const Assignment *ExpressionAnalyzer::Analyze(
|
|
|
|
const parser::PointerAssignmentStmt &x) {
|
2020-01-15 09:39:29 +08:00
|
|
|
if (!x.typedAssignment) {
|
|
|
|
MaybeExpr lhs{Analyze(std::get<parser::DataRef>(x.t))};
|
|
|
|
MaybeExpr rhs{Analyze(std::get<parser::Expr>(x.t))};
|
2020-02-19 07:20:28 +08:00
|
|
|
if (!lhs || !rhs) {
|
2020-07-10 02:08:41 +08:00
|
|
|
x.typedAssignment.Reset(
|
|
|
|
new GenericAssignmentWrapper{}, GenericAssignmentWrapper::Deleter);
|
2020-02-19 07:20:28 +08:00
|
|
|
} else {
|
|
|
|
Assignment assignment{std::move(*lhs), std::move(*rhs)};
|
2022-03-24 05:05:50 +08:00
|
|
|
common::visit(
|
|
|
|
common::visitors{
|
|
|
|
[&](const std::list<parser::BoundsRemapping> &list) {
|
|
|
|
Assignment::BoundsRemapping bounds;
|
|
|
|
for (const auto &elem : list) {
|
|
|
|
auto lower{AsSubscript(Analyze(std::get<0>(elem.t)))};
|
|
|
|
auto upper{AsSubscript(Analyze(std::get<1>(elem.t)))};
|
|
|
|
if (lower && upper) {
|
|
|
|
bounds.emplace_back(
|
|
|
|
Fold(std::move(*lower)), Fold(std::move(*upper)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assignment.u = std::move(bounds);
|
|
|
|
},
|
|
|
|
[&](const std::list<parser::BoundsSpec> &list) {
|
|
|
|
Assignment::BoundsSpec bounds;
|
|
|
|
for (const auto &bound : list) {
|
|
|
|
if (auto lower{AsSubscript(Analyze(bound.v))}) {
|
|
|
|
bounds.emplace_back(Fold(std::move(*lower)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assignment.u = std::move(bounds);
|
|
|
|
},
|
|
|
|
},
|
2020-02-19 07:20:28 +08:00
|
|
|
std::get<parser::PointerAssignmentStmt::Bounds>(x.t).u);
|
2020-07-10 02:08:41 +08:00
|
|
|
x.typedAssignment.Reset(
|
|
|
|
new GenericAssignmentWrapper{std::move(assignment)},
|
|
|
|
GenericAssignmentWrapper::Deleter);
|
2020-01-15 09:39:29 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return common::GetPtrFromOptional(x.typedAssignment->v);
|
2020-01-04 02:38:51 +08:00
|
|
|
}
|
|
|
|
|
2019-09-17 07:58:13 +08:00
|
|
|
static bool IsExternalCalledImplicitly(
|
|
|
|
parser::CharBlock callSite, const ProcedureDesignator &proc) {
|
|
|
|
if (const auto *symbol{proc.GetSymbol()}) {
|
2019-10-10 07:08:13 +08:00
|
|
|
return symbol->has<semantics::SubprogramDetails>() &&
|
|
|
|
symbol->owner().IsGlobal() &&
|
2020-03-20 07:31:10 +08:00
|
|
|
(!symbol->scope() /*ENTRY*/ ||
|
|
|
|
!symbol->scope()->sourceRange().Contains(callSite));
|
2019-09-17 07:58:13 +08:00
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::optional<characteristics::Procedure> ExpressionAnalyzer::CheckCall(
|
|
|
|
parser::CharBlock callSite, const ProcedureDesignator &proc,
|
|
|
|
ActualArguments &arguments) {
|
[flang] Improve initializer semantics, esp. for component default values
This patch plugs many holes in static initializer semantics, improves error
messages for default initial values and other component properties in
parameterized derived type instantiations, and cleans up several small
issues noticed during development. We now do proper scalar expansion,
folding, and type, rank, and shape conformance checking for component
default initializers in derived types and PDT instantiations.
The initial values of named constants are now guaranteed to have been folded
when installed in the symbol table, and are no longer folded or
scalar-expanded at each use in expression folding. Semantics documentation
was extended with information about the various kinds of initializations
in Fortran and when each of them are processed in the compiler.
Some necessary concomitant changes have bulked this patch out a bit:
* contextual messages attachments, which are now produced for parameterized
derived type instantiations so that the user can figure out which
instance caused a problem with a component, have been added as part
of ContextualMessages, and their implementation was debugged
* several APIs in evaluate::characteristics was changed so that a FoldingContext
is passed as an argument rather than just its intrinsic procedure table;
this affected client call sites in many files
* new tools in Evaluate/check-expression.cpp to determine when an Expr
actually is a single constant value and to validate a non-pointer
variable initializer or object component default value
* shape conformance checking has additional arguments that control
whether scalar expansion is allowed
* several now-unused functions and data members noticed and removed
* several crashes and bogus errors exposed by testing this new code
were fixed
* a -fdebug-stack-trace option to enable LLVM's stack tracing on
a crash, which might be useful in the future
TL;DR: Initialization processing does more and takes place at the right
times for all of the various kinds of things that can be initialized.
Differential Review: https://reviews.llvm.org/D92783
2020-12-08 04:08:58 +08:00
|
|
|
auto chars{characteristics::Procedure::Characterize(
|
|
|
|
proc, context_.foldingContext())};
|
2019-11-10 01:29:31 +08:00
|
|
|
if (chars) {
|
2019-09-17 07:58:13 +08:00
|
|
|
bool treatExternalAsImplicit{IsExternalCalledImplicitly(callSite, proc)};
|
|
|
|
if (treatExternalAsImplicit && !chars->CanBeCalledViaImplicitInterface()) {
|
|
|
|
Say(callSite,
|
2022-02-09 16:28:27 +08:00
|
|
|
"References to the procedure '%s' require an explicit interface"_err_en_US,
|
2019-09-17 07:58:13 +08:00
|
|
|
DEREF(proc.GetSymbol()).name());
|
|
|
|
}
|
2020-09-26 00:03:17 +08:00
|
|
|
// Checks for ASSOCIATED() are done in intrinsic table processing
|
2022-05-19 06:44:01 +08:00
|
|
|
const SpecificIntrinsic *specificIntrinsic{proc.GetSpecificIntrinsic()};
|
|
|
|
bool procIsAssociated{
|
|
|
|
specificIntrinsic && specificIntrinsic->name == "associated"};
|
2020-09-26 00:03:17 +08:00
|
|
|
if (!procIsAssociated) {
|
2022-05-26 00:19:19 +08:00
|
|
|
const Symbol *procSymbol{proc.GetSymbol()};
|
|
|
|
bool procIsDummy{procSymbol && IsDummy(*procSymbol)};
|
2022-05-19 06:44:01 +08:00
|
|
|
if (chars->functionResult &&
|
|
|
|
chars->functionResult->IsAssumedLengthCharacter() &&
|
2022-05-26 00:19:19 +08:00
|
|
|
!specificIntrinsic && !procIsDummy) {
|
2022-05-19 06:44:01 +08:00
|
|
|
Say(callSite,
|
|
|
|
"Assumed-length character function must be defined with a length to be called"_err_en_US);
|
|
|
|
}
|
2020-09-26 00:03:17 +08:00
|
|
|
semantics::CheckArguments(*chars, arguments, GetFoldingContext(),
|
2020-10-20 18:39:26 +08:00
|
|
|
context_.FindScope(callSite), treatExternalAsImplicit,
|
2022-05-19 06:44:01 +08:00
|
|
|
specificIntrinsic);
|
2020-09-26 00:03:17 +08:00
|
|
|
if (procSymbol && !IsPureProcedure(*procSymbol)) {
|
|
|
|
if (const semantics::Scope *
|
|
|
|
pure{semantics::FindPureProcedureContaining(
|
|
|
|
context_.FindScope(callSite))}) {
|
|
|
|
Say(callSite,
|
|
|
|
"Procedure '%s' referenced in pure subprogram '%s' must be pure too"_err_en_US,
|
|
|
|
procSymbol->name(), DEREF(pure->symbol()).name());
|
|
|
|
}
|
2019-11-13 07:43:09 +08:00
|
|
|
}
|
|
|
|
}
|
2019-09-17 07:58:13 +08:00
|
|
|
}
|
|
|
|
return chars;
|
|
|
|
}
|
|
|
|
|
2018-12-04 03:40:53 +08:00
|
|
|
// Unary operations
|
|
|
|
|
2019-03-08 06:46:31 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::Parentheses &x) {
|
|
|
|
if (MaybeExpr operand{Analyze(x.v.value())}) {
|
2019-05-04 02:29:15 +08:00
|
|
|
if (const semantics::Symbol * symbol{GetLastSymbol(*operand)}) {
|
|
|
|
if (const semantics::Symbol * result{FindFunctionResult(*symbol)}) {
|
|
|
|
if (semantics::IsProcedurePointer(*result)) {
|
|
|
|
Say("A function reference that returns a procedure "
|
2020-03-28 05:17:25 +08:00
|
|
|
"pointer may not be parenthesized"_err_en_US); // C1003
|
2019-05-04 02:29:15 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-11-23 06:58:26 +08:00
|
|
|
return Parenthesize(std::move(*operand));
|
2018-09-13 02:20:30 +08:00
|
|
|
}
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
2019-11-03 00:56:46 +08:00
|
|
|
static MaybeExpr NumericUnaryHelper(ExpressionAnalyzer &context,
|
|
|
|
NumericOperator opr, const parser::Expr::IntrinsicUnary &x) {
|
|
|
|
ArgumentAnalyzer analyzer{context};
|
|
|
|
analyzer.Analyze(x.v);
|
2021-09-08 03:17:31 +08:00
|
|
|
if (!analyzer.fatalErrors()) {
|
|
|
|
if (analyzer.IsIntrinsicNumeric(opr)) {
|
|
|
|
analyzer.CheckForNullPointer();
|
|
|
|
if (opr == NumericOperator::Add) {
|
|
|
|
return analyzer.MoveExpr(0);
|
|
|
|
} else {
|
|
|
|
return Negation(context.GetContextualMessages(), analyzer.MoveExpr(0));
|
|
|
|
}
|
2019-11-03 00:56:46 +08:00
|
|
|
} else {
|
2021-09-08 03:17:31 +08:00
|
|
|
return analyzer.TryDefinedOp(AsFortran(opr),
|
|
|
|
"Operand of unary %s must be numeric; have %s"_err_en_US);
|
2019-05-04 02:29:15 +08:00
|
|
|
}
|
2018-09-13 02:20:30 +08:00
|
|
|
}
|
2021-09-08 03:17:31 +08:00
|
|
|
return std::nullopt;
|
2019-11-03 00:56:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::UnaryPlus &x) {
|
|
|
|
return NumericUnaryHelper(*this, NumericOperator::Add, x);
|
2018-09-13 02:20:30 +08:00
|
|
|
}
|
|
|
|
|
2019-03-08 06:46:31 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::Negate &x) {
|
2022-05-25 06:06:12 +08:00
|
|
|
if (const auto *litConst{
|
|
|
|
std::get_if<parser::LiteralConstant>(&x.v.value().u)}) {
|
|
|
|
if (const auto *intConst{
|
|
|
|
std::get_if<parser::IntLiteralConstant>(&litConst->u)}) {
|
|
|
|
return Analyze(*intConst, true);
|
|
|
|
}
|
|
|
|
}
|
2019-11-03 00:56:46 +08:00
|
|
|
return NumericUnaryHelper(*this, NumericOperator::Subtract, x);
|
2018-09-13 02:20:30 +08:00
|
|
|
}
|
|
|
|
|
2019-03-08 06:46:31 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::NOT &x) {
|
2019-11-03 00:56:46 +08:00
|
|
|
ArgumentAnalyzer analyzer{*this};
|
|
|
|
analyzer.Analyze(x.v);
|
2021-09-08 03:17:31 +08:00
|
|
|
if (!analyzer.fatalErrors()) {
|
|
|
|
if (analyzer.IsIntrinsicLogical()) {
|
|
|
|
analyzer.CheckForNullPointer();
|
|
|
|
return AsGenericExpr(
|
|
|
|
LogicalNegation(std::get<Expr<SomeLogical>>(analyzer.MoveExpr(0).u)));
|
|
|
|
} else {
|
|
|
|
return analyzer.TryDefinedOp(LogicalOperator::Not,
|
|
|
|
"Operand of %s must be LOGICAL; have %s"_err_en_US);
|
|
|
|
}
|
2018-09-13 02:20:30 +08:00
|
|
|
}
|
2021-09-08 03:17:31 +08:00
|
|
|
return std::nullopt;
|
2018-09-08 01:33:32 +08:00
|
|
|
}
|
|
|
|
|
2019-07-02 04:22:22 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::PercentLoc &x) {
|
|
|
|
// Represent %LOC() exactly as if it had been a call to the LOC() extension
|
|
|
|
// intrinsic function.
|
|
|
|
// Use the actual source for the name of the call for error reporting.
|
2019-09-14 03:32:43 +08:00
|
|
|
std::optional<ActualArgument> arg;
|
|
|
|
if (const Symbol * assumedTypeDummy{AssumedTypeDummy(x.v.value())}) {
|
|
|
|
arg = ActualArgument{ActualArgument::AssumedType{*assumedTypeDummy}};
|
|
|
|
} else if (MaybeExpr argExpr{Analyze(x.v.value())}) {
|
|
|
|
arg = ActualArgument{std::move(*argExpr)};
|
2019-07-02 07:54:53 +08:00
|
|
|
} else {
|
|
|
|
return std::nullopt;
|
2019-07-02 04:22:22 +08:00
|
|
|
}
|
2019-09-14 03:32:43 +08:00
|
|
|
parser::CharBlock at{GetContextualMessages().at()};
|
|
|
|
CHECK(at.size() >= 4);
|
|
|
|
parser::CharBlock loc{at.begin() + 1, 3};
|
|
|
|
CHECK(loc == "loc");
|
|
|
|
return MakeFunctionRef(loc, ActualArguments{std::move(*arg)});
|
2018-09-08 01:33:32 +08:00
|
|
|
}
|
|
|
|
|
2019-10-23 00:31:33 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::DefinedUnary &x) {
|
|
|
|
const auto &name{std::get<parser::DefinedOpName>(x.t).v};
|
2020-01-03 01:55:03 +08:00
|
|
|
ArgumentAnalyzer analyzer{*this, name.source};
|
2019-10-23 00:31:33 +08:00
|
|
|
analyzer.Analyze(std::get<1>(x.t));
|
2020-01-03 01:55:03 +08:00
|
|
|
return analyzer.TryDefinedOp(name.source.ToString().c_str(),
|
2021-09-08 03:17:31 +08:00
|
|
|
"No operator %s defined for %s"_err_en_US, nullptr, true);
|
2018-09-08 01:33:32 +08:00
|
|
|
}
|
2020-01-03 01:55:03 +08:00
|
|
|
|
2018-12-04 03:40:53 +08:00
|
|
|
// Binary (dyadic) operations
|
|
|
|
|
2020-03-28 05:17:25 +08:00
|
|
|
template <template <typename> class OPR>
|
2019-11-03 00:56:46 +08:00
|
|
|
MaybeExpr NumericBinaryHelper(ExpressionAnalyzer &context, NumericOperator opr,
|
|
|
|
const parser::Expr::IntrinsicBinary &x) {
|
|
|
|
ArgumentAnalyzer analyzer{context};
|
|
|
|
analyzer.Analyze(std::get<0>(x.t));
|
|
|
|
analyzer.Analyze(std::get<1>(x.t));
|
2021-09-08 03:17:31 +08:00
|
|
|
if (!analyzer.fatalErrors()) {
|
|
|
|
if (analyzer.IsIntrinsicNumeric(opr)) {
|
|
|
|
analyzer.CheckForNullPointer();
|
|
|
|
analyzer.CheckConformance();
|
|
|
|
return NumericOperation<OPR>(context.GetContextualMessages(),
|
|
|
|
analyzer.MoveExpr(0), analyzer.MoveExpr(1),
|
|
|
|
context.GetDefaultKind(TypeCategory::Real));
|
|
|
|
} else {
|
|
|
|
return analyzer.TryDefinedOp(AsFortran(opr),
|
|
|
|
"Operands of %s must be numeric; have %s and %s"_err_en_US);
|
|
|
|
}
|
2018-09-01 07:14:14 +08:00
|
|
|
}
|
2021-09-08 03:17:31 +08:00
|
|
|
return std::nullopt;
|
2018-09-01 07:14:14 +08:00
|
|
|
}
|
|
|
|
|
2019-03-08 06:46:31 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::Power &x) {
|
2019-11-03 00:56:46 +08:00
|
|
|
return NumericBinaryHelper<Power>(*this, NumericOperator::Power, x);
|
2018-09-01 07:14:14 +08:00
|
|
|
}
|
|
|
|
|
2019-03-08 06:46:31 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::Multiply &x) {
|
2019-11-03 00:56:46 +08:00
|
|
|
return NumericBinaryHelper<Multiply>(*this, NumericOperator::Multiply, x);
|
2018-09-01 07:14:14 +08:00
|
|
|
}
|
|
|
|
|
2019-03-08 06:46:31 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::Divide &x) {
|
2019-11-03 00:56:46 +08:00
|
|
|
return NumericBinaryHelper<Divide>(*this, NumericOperator::Divide, x);
|
2018-09-01 07:14:14 +08:00
|
|
|
}
|
|
|
|
|
2019-03-08 06:46:31 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::Add &x) {
|
2019-11-03 00:56:46 +08:00
|
|
|
return NumericBinaryHelper<Add>(*this, NumericOperator::Add, x);
|
2018-09-08 06:25:10 +08:00
|
|
|
}
|
|
|
|
|
2019-03-08 06:46:31 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::Subtract &x) {
|
2019-11-03 00:56:46 +08:00
|
|
|
return NumericBinaryHelper<Subtract>(*this, NumericOperator::Subtract, x);
|
2018-09-08 06:25:10 +08:00
|
|
|
}
|
|
|
|
|
2019-03-08 06:46:31 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(
|
2018-12-04 03:40:53 +08:00
|
|
|
const parser::Expr::ComplexConstructor &x) {
|
2019-03-08 06:46:31 +08:00
|
|
|
auto re{Analyze(std::get<0>(x.t).value())};
|
|
|
|
auto im{Analyze(std::get<1>(x.t).value())};
|
2019-11-10 01:29:31 +08:00
|
|
|
if (re && im) {
|
2019-03-08 06:46:31 +08:00
|
|
|
ConformabilityCheck(GetContextualMessages(), *re, *im);
|
2019-01-10 07:06:07 +08:00
|
|
|
}
|
2019-03-08 06:46:31 +08:00
|
|
|
return AsMaybeExpr(ConstructComplex(GetContextualMessages(), std::move(re),
|
|
|
|
std::move(im), GetDefaultKind(TypeCategory::Real)));
|
2018-09-01 07:14:14 +08:00
|
|
|
}
|
2018-08-08 03:34:09 +08:00
|
|
|
|
2019-03-08 06:46:31 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::Concat &x) {
|
2019-11-03 00:56:46 +08:00
|
|
|
ArgumentAnalyzer analyzer{*this};
|
|
|
|
analyzer.Analyze(std::get<0>(x.t));
|
|
|
|
analyzer.Analyze(std::get<1>(x.t));
|
2021-09-08 03:17:31 +08:00
|
|
|
if (!analyzer.fatalErrors()) {
|
|
|
|
if (analyzer.IsIntrinsicConcat()) {
|
|
|
|
analyzer.CheckForNullPointer();
|
2022-03-24 05:05:50 +08:00
|
|
|
return common::visit(
|
2021-09-08 03:17:31 +08:00
|
|
|
[&](auto &&x, auto &&y) -> MaybeExpr {
|
|
|
|
using T = ResultType<decltype(x)>;
|
|
|
|
if constexpr (std::is_same_v<T, ResultType<decltype(y)>>) {
|
|
|
|
return AsGenericExpr(Concat<T::kind>{std::move(x), std::move(y)});
|
|
|
|
} else {
|
|
|
|
DIE("different types for intrinsic concat");
|
|
|
|
}
|
|
|
|
},
|
|
|
|
std::move(std::get<Expr<SomeCharacter>>(analyzer.MoveExpr(0).u).u),
|
|
|
|
std::move(std::get<Expr<SomeCharacter>>(analyzer.MoveExpr(1).u).u));
|
|
|
|
} else {
|
|
|
|
return analyzer.TryDefinedOp("//",
|
|
|
|
"Operands of %s must be CHARACTER with the same kind; have %s and %s"_err_en_US);
|
|
|
|
}
|
2018-09-08 07:54:33 +08:00
|
|
|
}
|
2021-09-08 03:17:31 +08:00
|
|
|
return std::nullopt;
|
2018-09-08 01:33:32 +08:00
|
|
|
}
|
|
|
|
|
2019-11-03 00:56:46 +08:00
|
|
|
// The Name represents a user-defined intrinsic operator.
|
|
|
|
// If the actuals match one of the specific procedures, return a function ref.
|
|
|
|
// Otherwise report the error in messages.
|
2019-12-17 03:33:55 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::AnalyzeDefinedOp(
|
2019-11-03 00:56:46 +08:00
|
|
|
const parser::Name &name, ActualArguments &&actuals) {
|
|
|
|
if (auto callee{GetCalleeAndArguments(name, std::move(actuals))}) {
|
2019-12-21 05:03:30 +08:00
|
|
|
CHECK(std::holds_alternative<ProcedureDesignator>(callee->u));
|
|
|
|
return MakeFunctionRef(name.source,
|
|
|
|
std::move(std::get<ProcedureDesignator>(callee->u)),
|
2019-11-03 00:56:46 +08:00
|
|
|
std::move(callee->arguments));
|
|
|
|
} else {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
MaybeExpr RelationHelper(ExpressionAnalyzer &context, RelationalOperator opr,
|
|
|
|
const parser::Expr::IntrinsicBinary &x) {
|
|
|
|
ArgumentAnalyzer analyzer{context};
|
|
|
|
analyzer.Analyze(std::get<0>(x.t));
|
|
|
|
analyzer.Analyze(std::get<1>(x.t));
|
2021-09-08 03:17:31 +08:00
|
|
|
if (!analyzer.fatalErrors()) {
|
2020-11-19 04:38:29 +08:00
|
|
|
std::optional<DynamicType> leftType{analyzer.GetType(0)};
|
|
|
|
std::optional<DynamicType> rightType{analyzer.GetType(1)};
|
2021-09-08 03:17:31 +08:00
|
|
|
analyzer.ConvertBOZ(leftType, 0, rightType);
|
|
|
|
analyzer.ConvertBOZ(rightType, 1, leftType);
|
|
|
|
if (leftType && rightType &&
|
|
|
|
analyzer.IsIntrinsicRelational(opr, *leftType, *rightType)) {
|
|
|
|
analyzer.CheckForNullPointer("as a relational operand");
|
2020-07-16 07:02:49 +08:00
|
|
|
return AsMaybeExpr(Relate(context.GetContextualMessages(), opr,
|
|
|
|
analyzer.MoveExpr(0), analyzer.MoveExpr(1)));
|
|
|
|
} else {
|
|
|
|
return analyzer.TryDefinedOp(opr,
|
2021-09-08 03:17:31 +08:00
|
|
|
leftType && leftType->category() == TypeCategory::Logical &&
|
|
|
|
rightType && rightType->category() == TypeCategory::Logical
|
|
|
|
? "LOGICAL operands must be compared using .EQV. or .NEQV."_err_en_US
|
|
|
|
: "Operands of %s must have comparable types; have %s and %s"_err_en_US);
|
2020-07-16 07:02:49 +08:00
|
|
|
}
|
2018-09-08 06:25:10 +08:00
|
|
|
}
|
2021-09-08 03:17:31 +08:00
|
|
|
return std::nullopt;
|
2018-09-08 01:33:32 +08:00
|
|
|
}
|
|
|
|
|
2019-03-08 06:46:31 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::LT &x) {
|
|
|
|
return RelationHelper(*this, RelationalOperator::LT, x);
|
2018-09-08 01:33:32 +08:00
|
|
|
}
|
|
|
|
|
2019-03-08 06:46:31 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::LE &x) {
|
|
|
|
return RelationHelper(*this, RelationalOperator::LE, x);
|
2018-09-08 01:33:32 +08:00
|
|
|
}
|
|
|
|
|
2019-03-08 06:46:31 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::EQ &x) {
|
|
|
|
return RelationHelper(*this, RelationalOperator::EQ, x);
|
2018-09-08 01:33:32 +08:00
|
|
|
}
|
|
|
|
|
2019-03-08 06:46:31 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::NE &x) {
|
|
|
|
return RelationHelper(*this, RelationalOperator::NE, x);
|
2018-09-08 01:33:32 +08:00
|
|
|
}
|
|
|
|
|
2019-03-08 06:46:31 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::GE &x) {
|
|
|
|
return RelationHelper(*this, RelationalOperator::GE, x);
|
2018-09-08 01:33:32 +08:00
|
|
|
}
|
|
|
|
|
2019-03-08 06:46:31 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::GT &x) {
|
|
|
|
return RelationHelper(*this, RelationalOperator::GT, x);
|
2018-09-08 01:33:32 +08:00
|
|
|
}
|
|
|
|
|
2019-11-03 00:56:46 +08:00
|
|
|
MaybeExpr LogicalBinaryHelper(ExpressionAnalyzer &context, LogicalOperator opr,
|
|
|
|
const parser::Expr::IntrinsicBinary &x) {
|
|
|
|
ArgumentAnalyzer analyzer{context};
|
|
|
|
analyzer.Analyze(std::get<0>(x.t));
|
|
|
|
analyzer.Analyze(std::get<1>(x.t));
|
2021-09-08 03:17:31 +08:00
|
|
|
if (!analyzer.fatalErrors()) {
|
|
|
|
if (analyzer.IsIntrinsicLogical()) {
|
|
|
|
analyzer.CheckForNullPointer("as a logical operand");
|
|
|
|
return AsGenericExpr(BinaryLogicalOperation(opr,
|
|
|
|
std::get<Expr<SomeLogical>>(analyzer.MoveExpr(0).u),
|
|
|
|
std::get<Expr<SomeLogical>>(analyzer.MoveExpr(1).u)));
|
|
|
|
} else {
|
|
|
|
return analyzer.TryDefinedOp(
|
|
|
|
opr, "Operands of %s must be LOGICAL; have %s and %s"_err_en_US);
|
|
|
|
}
|
2018-09-08 06:25:10 +08:00
|
|
|
}
|
2021-09-08 03:17:31 +08:00
|
|
|
return std::nullopt;
|
2018-09-08 01:33:32 +08:00
|
|
|
}
|
|
|
|
|
2019-03-08 06:46:31 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::AND &x) {
|
2019-11-03 00:56:46 +08:00
|
|
|
return LogicalBinaryHelper(*this, LogicalOperator::And, x);
|
2018-09-08 01:33:32 +08:00
|
|
|
}
|
|
|
|
|
2019-03-08 06:46:31 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::OR &x) {
|
2019-11-03 00:56:46 +08:00
|
|
|
return LogicalBinaryHelper(*this, LogicalOperator::Or, x);
|
2018-09-08 01:33:32 +08:00
|
|
|
}
|
|
|
|
|
2019-03-08 06:46:31 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::EQV &x) {
|
2019-11-03 00:56:46 +08:00
|
|
|
return LogicalBinaryHelper(*this, LogicalOperator::Eqv, x);
|
2018-09-08 06:25:10 +08:00
|
|
|
}
|
|
|
|
|
2019-03-08 06:46:31 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::NEQV &x) {
|
2019-11-03 00:56:46 +08:00
|
|
|
return LogicalBinaryHelper(*this, LogicalOperator::Neqv, x);
|
2018-09-08 06:25:10 +08:00
|
|
|
}
|
|
|
|
|
2019-10-23 00:31:33 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::DefinedBinary &x) {
|
|
|
|
const auto &name{std::get<parser::DefinedOpName>(x.t).v};
|
2020-01-03 01:55:03 +08:00
|
|
|
ArgumentAnalyzer analyzer{*this, name.source};
|
2019-10-23 00:31:33 +08:00
|
|
|
analyzer.Analyze(std::get<1>(x.t));
|
|
|
|
analyzer.Analyze(std::get<2>(x.t));
|
2020-01-03 01:55:03 +08:00
|
|
|
return analyzer.TryDefinedOp(name.source.ToString().c_str(),
|
2021-09-08 03:17:31 +08:00
|
|
|
"No operator %s defined for %s and %s"_err_en_US, nullptr, true);
|
2018-09-08 01:33:32 +08:00
|
|
|
}
|
2018-12-05 07:52:50 +08:00
|
|
|
|
2022-01-09 05:08:08 +08:00
|
|
|
// Returns true if a parsed function reference should be converted
|
|
|
|
// into an array element reference.
|
|
|
|
static bool CheckFuncRefToArrayElement(semantics::SemanticsContext &context,
|
2019-08-07 18:51:13 +08:00
|
|
|
const parser::FunctionReference &funcRef) {
|
2019-12-21 05:03:30 +08:00
|
|
|
// Emit message if the function reference fix will end up an array element
|
2022-01-09 05:08:08 +08:00
|
|
|
// reference with no subscripts, or subscripts on a scalar, because it will
|
|
|
|
// not be possible to later distinguish in expressions between an empty
|
|
|
|
// subscript list due to bad subscripts error recovery or because the
|
|
|
|
// user did not put any.
|
|
|
|
auto &proc{std::get<parser::ProcedureDesignator>(funcRef.v.t)};
|
|
|
|
const auto *name{std::get_if<parser::Name>(&proc.u)};
|
|
|
|
if (!name) {
|
|
|
|
name = &std::get<parser::ProcComponentRef>(proc.u).v.thing.component;
|
|
|
|
}
|
|
|
|
if (!name->symbol) {
|
|
|
|
return false;
|
|
|
|
} else if (name->symbol->Rank() == 0) {
|
|
|
|
if (const Symbol *
|
|
|
|
function{
|
|
|
|
semantics::IsFunctionResultWithSameNameAsFunction(*name->symbol)}) {
|
|
|
|
auto &msg{context.Say(funcRef.v.source,
|
|
|
|
"Recursive call to '%s' requires a distinct RESULT in its declaration"_err_en_US,
|
|
|
|
name->source)};
|
|
|
|
AttachDeclaration(&msg, *function);
|
|
|
|
name->symbol = const_cast<Symbol *>(function);
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
if (std::get<std::list<parser::ActualArgSpec>>(funcRef.v.t).empty()) {
|
|
|
|
auto &msg{context.Say(funcRef.v.source,
|
|
|
|
"Reference to array '%s' with empty subscript list"_err_en_US,
|
|
|
|
name->source)};
|
|
|
|
if (name->symbol) {
|
2019-11-23 05:20:58 +08:00
|
|
|
AttachDeclaration(&msg, *name->symbol);
|
2019-08-07 18:51:13 +08:00
|
|
|
}
|
|
|
|
}
|
2022-01-09 05:08:08 +08:00
|
|
|
return true;
|
2019-08-07 18:51:13 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-09 07:16:30 +08:00
|
|
|
// Converts, if appropriate, an original misparse of ambiguous syntax like
|
2019-12-21 05:03:30 +08:00
|
|
|
// A(1) as a function reference into an array reference.
|
2021-09-08 03:17:31 +08:00
|
|
|
// Misparsed structure constructors are detected elsewhere after generic
|
2019-12-21 05:03:30 +08:00
|
|
|
// function call resolution fails.
|
2020-03-28 05:17:25 +08:00
|
|
|
template <typename... A>
|
2019-05-14 00:33:18 +08:00
|
|
|
static void FixMisparsedFunctionReference(
|
2019-04-03 01:34:45 +08:00
|
|
|
semantics::SemanticsContext &context, const std::variant<A...> &constU) {
|
2019-03-09 04:55:57 +08:00
|
|
|
// The parse tree is updated in situ when resolving an ambiguous parse.
|
|
|
|
using uType = std::decay_t<decltype(constU)>;
|
|
|
|
auto &u{const_cast<uType &>(constU)};
|
|
|
|
if (auto *func{
|
|
|
|
std::get_if<common::Indirection<parser::FunctionReference>>(&u)}) {
|
|
|
|
parser::FunctionReference &funcRef{func->value()};
|
|
|
|
auto &proc{std::get<parser::ProcedureDesignator>(funcRef.v.t)};
|
2019-05-30 04:22:35 +08:00
|
|
|
if (Symbol *
|
2020-03-29 12:00:16 +08:00
|
|
|
origSymbol{
|
2022-03-24 05:05:50 +08:00
|
|
|
common::visit(common::visitors{
|
|
|
|
[&](parser::Name &name) { return name.symbol; },
|
|
|
|
[&](parser::ProcComponentRef &pcr) {
|
|
|
|
return pcr.v.thing.component.symbol;
|
|
|
|
},
|
|
|
|
},
|
2020-03-29 12:00:16 +08:00
|
|
|
proc.u)}) {
|
2019-05-30 04:22:35 +08:00
|
|
|
Symbol &symbol{origSymbol->GetUltimate()};
|
2019-08-20 21:15:52 +08:00
|
|
|
if (symbol.has<semantics::ObjectEntityDetails>() ||
|
|
|
|
symbol.has<semantics::AssocEntityDetails>()) {
|
2019-08-21 17:42:34 +08:00
|
|
|
// Note that expression in AssocEntityDetails cannot be a procedure
|
|
|
|
// pointer as per C1105 so this cannot be a function reference.
|
2019-03-09 07:16:30 +08:00
|
|
|
if constexpr (common::HasMember<common::Indirection<parser::Designator>,
|
|
|
|
uType>) {
|
2022-01-09 05:08:08 +08:00
|
|
|
if (CheckFuncRefToArrayElement(context, funcRef)) {
|
|
|
|
u = common::Indirection{funcRef.ConvertToArrayElementRef()};
|
|
|
|
}
|
2019-03-09 07:16:30 +08:00
|
|
|
} else {
|
2019-09-13 04:43:16 +08:00
|
|
|
DIE("can't fix misparsed function as array reference");
|
2019-03-09 04:55:57 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Rework DATA statement semantics to use typed expressions
Summary:
Updates recent work on DATA statement semantic checking in
flang/lib/Semantics/check-data.{h,cpp} to use the compiler's
internal representation for typed expressions rather than working
on the raw parse tree. Saves the analyzed expressions for DATA
statement values as parse tree decorations because they'll soon be
needed in lowering. Corrects wording of some error messages.
Fixes a bug in constant expression checking: structure constructors
are not constant expressions if they set an allocatable component
to anything other than NULL.
Includes infrastructure changes to make this work, some renaming
to reflect the fact that the implied DO loop indices tracked by
expression analysis are not (just) from array constructors, remove
some dead code, and improve some comments.
Reviewers: tskeith, sscalpone, jdoerfert, DavidTruby, anchu-rajendran, schweitz
Reviewed By: tskeith, anchu-rajendran, schweitz
Subscribers: llvm-commits, flang-commits
Tags: #flang, #llvm
Differential Revision: https://reviews.llvm.org/D78834
2020-04-25 04:54:11 +08:00
|
|
|
// Common handling of parse tree node types that retain the
|
|
|
|
// representation of the analyzed expression.
|
2020-03-28 05:17:25 +08:00
|
|
|
template <typename PARSED>
|
2020-10-31 04:28:10 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::ExprOrVariable(
|
|
|
|
const PARSED &x, parser::CharBlock source) {
|
2020-10-31 03:57:28 +08:00
|
|
|
if (useSavedTypedExprs_ && x.typedExpr) {
|
Rework DATA statement semantics to use typed expressions
Summary:
Updates recent work on DATA statement semantic checking in
flang/lib/Semantics/check-data.{h,cpp} to use the compiler's
internal representation for typed expressions rather than working
on the raw parse tree. Saves the analyzed expressions for DATA
statement values as parse tree decorations because they'll soon be
needed in lowering. Corrects wording of some error messages.
Fixes a bug in constant expression checking: structure constructors
are not constant expressions if they set an allocatable component
to anything other than NULL.
Includes infrastructure changes to make this work, some renaming
to reflect the fact that the implied DO loop indices tracked by
expression analysis are not (just) from array constructors, remove
some dead code, and improve some comments.
Reviewers: tskeith, sscalpone, jdoerfert, DavidTruby, anchu-rajendran, schweitz
Reviewed By: tskeith, anchu-rajendran, schweitz
Subscribers: llvm-commits, flang-commits
Tags: #flang, #llvm
Differential Revision: https://reviews.llvm.org/D78834
2020-04-25 04:54:11 +08:00
|
|
|
return x.typedExpr->v;
|
|
|
|
}
|
2020-10-31 04:28:10 +08:00
|
|
|
auto restorer{GetContextualMessages().SetLocation(source)};
|
Rework DATA statement semantics to use typed expressions
Summary:
Updates recent work on DATA statement semantic checking in
flang/lib/Semantics/check-data.{h,cpp} to use the compiler's
internal representation for typed expressions rather than working
on the raw parse tree. Saves the analyzed expressions for DATA
statement values as parse tree decorations because they'll soon be
needed in lowering. Corrects wording of some error messages.
Fixes a bug in constant expression checking: structure constructors
are not constant expressions if they set an allocatable component
to anything other than NULL.
Includes infrastructure changes to make this work, some renaming
to reflect the fact that the implied DO loop indices tracked by
expression analysis are not (just) from array constructors, remove
some dead code, and improve some comments.
Reviewers: tskeith, sscalpone, jdoerfert, DavidTruby, anchu-rajendran, schweitz
Reviewed By: tskeith, anchu-rajendran, schweitz
Subscribers: llvm-commits, flang-commits
Tags: #flang, #llvm
Differential Revision: https://reviews.llvm.org/D78834
2020-04-25 04:54:11 +08:00
|
|
|
if constexpr (std::is_same_v<PARSED, parser::Expr> ||
|
|
|
|
std::is_same_v<PARSED, parser::Variable>) {
|
2019-04-20 03:55:36 +08:00
|
|
|
FixMisparsedFunctionReference(context_, x.u);
|
2018-12-05 07:52:50 +08:00
|
|
|
}
|
Rework DATA statement semantics to use typed expressions
Summary:
Updates recent work on DATA statement semantic checking in
flang/lib/Semantics/check-data.{h,cpp} to use the compiler's
internal representation for typed expressions rather than working
on the raw parse tree. Saves the analyzed expressions for DATA
statement values as parse tree decorations because they'll soon be
needed in lowering. Corrects wording of some error messages.
Fixes a bug in constant expression checking: structure constructors
are not constant expressions if they set an allocatable component
to anything other than NULL.
Includes infrastructure changes to make this work, some renaming
to reflect the fact that the implied DO loop indices tracked by
expression analysis are not (just) from array constructors, remove
some dead code, and improve some comments.
Reviewers: tskeith, sscalpone, jdoerfert, DavidTruby, anchu-rajendran, schweitz
Reviewed By: tskeith, anchu-rajendran, schweitz
Subscribers: llvm-commits, flang-commits
Tags: #flang, #llvm
Differential Revision: https://reviews.llvm.org/D78834
2020-04-25 04:54:11 +08:00
|
|
|
if (AssumedTypeDummy(x)) { // C710
|
|
|
|
Say("TYPE(*) dummy argument may only be used as an actual argument"_err_en_US);
|
2021-06-03 08:21:41 +08:00
|
|
|
ResetExpr(x);
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
MaybeExpr result;
|
|
|
|
if constexpr (common::HasMember<parser::StructureConstructor,
|
|
|
|
std::decay_t<decltype(x.u)>> &&
|
|
|
|
common::HasMember<common::Indirection<parser::FunctionReference>,
|
|
|
|
std::decay_t<decltype(x.u)>>) {
|
|
|
|
if (const auto *funcRef{
|
|
|
|
std::get_if<common::Indirection<parser::FunctionReference>>(
|
|
|
|
&x.u)}) {
|
|
|
|
// Function references in Exprs might turn out to be misparsed structure
|
|
|
|
// constructors; we have to try generic procedure resolution
|
|
|
|
// first to be sure.
|
|
|
|
std::optional<parser::StructureConstructor> ctor;
|
|
|
|
result = Analyze(funcRef->value(), &ctor);
|
|
|
|
if (result && ctor) {
|
|
|
|
// A misparsed function reference is really a structure
|
|
|
|
// constructor. Repair the parse tree in situ.
|
|
|
|
const_cast<PARSED &>(x).u = std::move(*ctor);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
result = Analyze(x.u);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
result = Analyze(x.u);
|
|
|
|
}
|
|
|
|
if (result) {
|
2020-10-31 04:28:10 +08:00
|
|
|
SetExpr(x, Fold(std::move(*result)));
|
Rework DATA statement semantics to use typed expressions
Summary:
Updates recent work on DATA statement semantic checking in
flang/lib/Semantics/check-data.{h,cpp} to use the compiler's
internal representation for typed expressions rather than working
on the raw parse tree. Saves the analyzed expressions for DATA
statement values as parse tree decorations because they'll soon be
needed in lowering. Corrects wording of some error messages.
Fixes a bug in constant expression checking: structure constructors
are not constant expressions if they set an allocatable component
to anything other than NULL.
Includes infrastructure changes to make this work, some renaming
to reflect the fact that the implied DO loop indices tracked by
expression analysis are not (just) from array constructors, remove
some dead code, and improve some comments.
Reviewers: tskeith, sscalpone, jdoerfert, DavidTruby, anchu-rajendran, schweitz
Reviewed By: tskeith, anchu-rajendran, schweitz
Subscribers: llvm-commits, flang-commits
Tags: #flang, #llvm
Differential Revision: https://reviews.llvm.org/D78834
2020-04-25 04:54:11 +08:00
|
|
|
return x.typedExpr->v;
|
2021-06-03 08:21:41 +08:00
|
|
|
} else {
|
|
|
|
ResetExpr(x);
|
|
|
|
if (!context_.AnyFatalError()) {
|
|
|
|
std::string buf;
|
|
|
|
llvm::raw_string_ostream dump{buf};
|
|
|
|
parser::DumpTree(dump, x);
|
|
|
|
Say("Internal error: Expression analysis failed on: %s"_err_en_US,
|
|
|
|
dump.str());
|
|
|
|
}
|
|
|
|
return std::nullopt;
|
Rework DATA statement semantics to use typed expressions
Summary:
Updates recent work on DATA statement semantic checking in
flang/lib/Semantics/check-data.{h,cpp} to use the compiler's
internal representation for typed expressions rather than working
on the raw parse tree. Saves the analyzed expressions for DATA
statement values as parse tree decorations because they'll soon be
needed in lowering. Corrects wording of some error messages.
Fixes a bug in constant expression checking: structure constructors
are not constant expressions if they set an allocatable component
to anything other than NULL.
Includes infrastructure changes to make this work, some renaming
to reflect the fact that the implied DO loop indices tracked by
expression analysis are not (just) from array constructors, remove
some dead code, and improve some comments.
Reviewers: tskeith, sscalpone, jdoerfert, DavidTruby, anchu-rajendran, schweitz
Reviewed By: tskeith, anchu-rajendran, schweitz
Subscribers: llvm-commits, flang-commits
Tags: #flang, #llvm
Differential Revision: https://reviews.llvm.org/D78834
2020-04-25 04:54:11 +08:00
|
|
|
}
|
2018-12-05 07:52:50 +08:00
|
|
|
}
|
2019-02-15 04:51:20 +08:00
|
|
|
|
2019-04-20 03:55:36 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr &expr) {
|
2020-10-31 04:28:10 +08:00
|
|
|
return ExprOrVariable(expr, expr.source);
|
2019-04-20 03:55:36 +08:00
|
|
|
}
|
|
|
|
|
2019-03-08 06:46:31 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(const parser::Variable &variable) {
|
2020-10-31 04:28:10 +08:00
|
|
|
return ExprOrVariable(variable, variable.GetSource());
|
2019-01-17 09:18:10 +08:00
|
|
|
}
|
2019-01-10 07:06:07 +08:00
|
|
|
|
2021-06-03 08:21:41 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(const parser::Selector &selector) {
|
|
|
|
if (const auto *var{std::get_if<parser::Variable>(&selector.u)}) {
|
|
|
|
if (!useSavedTypedExprs_ || !var->typedExpr) {
|
|
|
|
parser::CharBlock source{var->GetSource()};
|
|
|
|
auto restorer{GetContextualMessages().SetLocation(source)};
|
|
|
|
FixMisparsedFunctionReference(context_, var->u);
|
|
|
|
if (const auto *funcRef{
|
|
|
|
std::get_if<common::Indirection<parser::FunctionReference>>(
|
|
|
|
&var->u)}) {
|
|
|
|
// A Selector that parsed as a Variable might turn out during analysis
|
|
|
|
// to actually be a structure constructor. In that case, repair the
|
|
|
|
// Variable parse tree node into an Expr
|
|
|
|
std::optional<parser::StructureConstructor> ctor;
|
|
|
|
if (MaybeExpr result{Analyze(funcRef->value(), &ctor)}) {
|
|
|
|
if (ctor) {
|
|
|
|
auto &writable{const_cast<parser::Selector &>(selector)};
|
|
|
|
writable.u = parser::Expr{std::move(*ctor)};
|
|
|
|
auto &expr{std::get<parser::Expr>(writable.u)};
|
|
|
|
expr.source = source;
|
|
|
|
SetExpr(expr, Fold(std::move(*result)));
|
|
|
|
return expr.typedExpr->v;
|
|
|
|
} else {
|
|
|
|
SetExpr(*var, Fold(std::move(*result)));
|
|
|
|
return var->typedExpr->v;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ResetExpr(*var);
|
|
|
|
if (context_.AnyFatalError()) {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Not a Variable -> FunctionReference; handle normally as Variable or Expr
|
|
|
|
return Analyze(selector.u);
|
|
|
|
}
|
|
|
|
|
Rework DATA statement semantics to use typed expressions
Summary:
Updates recent work on DATA statement semantic checking in
flang/lib/Semantics/check-data.{h,cpp} to use the compiler's
internal representation for typed expressions rather than working
on the raw parse tree. Saves the analyzed expressions for DATA
statement values as parse tree decorations because they'll soon be
needed in lowering. Corrects wording of some error messages.
Fixes a bug in constant expression checking: structure constructors
are not constant expressions if they set an allocatable component
to anything other than NULL.
Includes infrastructure changes to make this work, some renaming
to reflect the fact that the implied DO loop indices tracked by
expression analysis are not (just) from array constructors, remove
some dead code, and improve some comments.
Reviewers: tskeith, sscalpone, jdoerfert, DavidTruby, anchu-rajendran, schweitz
Reviewed By: tskeith, anchu-rajendran, schweitz
Subscribers: llvm-commits, flang-commits
Tags: #flang, #llvm
Differential Revision: https://reviews.llvm.org/D78834
2020-04-25 04:54:11 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(const parser::DataStmtConstant &x) {
|
2021-10-26 04:25:34 +08:00
|
|
|
auto restorer{common::ScopedSet(inDataStmtConstant_, true)};
|
2020-10-31 04:28:10 +08:00
|
|
|
return ExprOrVariable(x, x.source);
|
Rework DATA statement semantics to use typed expressions
Summary:
Updates recent work on DATA statement semantic checking in
flang/lib/Semantics/check-data.{h,cpp} to use the compiler's
internal representation for typed expressions rather than working
on the raw parse tree. Saves the analyzed expressions for DATA
statement values as parse tree decorations because they'll soon be
needed in lowering. Corrects wording of some error messages.
Fixes a bug in constant expression checking: structure constructors
are not constant expressions if they set an allocatable component
to anything other than NULL.
Includes infrastructure changes to make this work, some renaming
to reflect the fact that the implied DO loop indices tracked by
expression analysis are not (just) from array constructors, remove
some dead code, and improve some comments.
Reviewers: tskeith, sscalpone, jdoerfert, DavidTruby, anchu-rajendran, schweitz
Reviewed By: tskeith, anchu-rajendran, schweitz
Subscribers: llvm-commits, flang-commits
Tags: #flang, #llvm
Differential Revision: https://reviews.llvm.org/D78834
2020-04-25 04:54:11 +08:00
|
|
|
}
|
|
|
|
|
2021-03-16 16:47:35 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(const parser::AllocateObject &x) {
|
2021-06-03 08:21:41 +08:00
|
|
|
return ExprOrVariable(x, parser::FindSourceLocation(x));
|
2021-03-16 16:47:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
MaybeExpr ExpressionAnalyzer::Analyze(const parser::PointerObject &x) {
|
2021-06-03 08:21:41 +08:00
|
|
|
return ExprOrVariable(x, parser::FindSourceLocation(x));
|
2021-03-16 16:47:35 +08:00
|
|
|
}
|
|
|
|
|
2019-03-08 06:46:31 +08:00
|
|
|
Expr<SubscriptInteger> ExpressionAnalyzer::AnalyzeKindSelector(
|
|
|
|
TypeCategory category,
|
2019-01-10 07:06:07 +08:00
|
|
|
const std::optional<parser::KindSelector> &selector) {
|
|
|
|
int defaultKind{GetDefaultKind(category)};
|
2019-11-10 01:29:31 +08:00
|
|
|
if (!selector) {
|
2018-12-05 02:55:32 +08:00
|
|
|
return Expr<SubscriptInteger>{defaultKind};
|
2019-01-10 07:06:07 +08:00
|
|
|
}
|
2022-03-24 05:05:50 +08:00
|
|
|
return common::visit(
|
2019-01-10 07:06:07 +08:00
|
|
|
common::visitors{
|
2020-01-04 02:38:51 +08:00
|
|
|
[&](const parser::ScalarIntConstantExpr &x) {
|
2019-03-08 06:46:31 +08:00
|
|
|
if (MaybeExpr kind{Analyze(x)}) {
|
2020-10-31 04:28:10 +08:00
|
|
|
if (std::optional<std::int64_t> code{ToInt64(*kind)}) {
|
2019-02-09 08:03:23 +08:00
|
|
|
if (CheckIntrinsicKind(category, *code)) {
|
2018-12-05 02:55:32 +08:00
|
|
|
return Expr<SubscriptInteger>{*code};
|
2019-01-10 07:06:07 +08:00
|
|
|
}
|
2020-10-31 04:28:10 +08:00
|
|
|
} else if (auto *intExpr{UnwrapExpr<Expr<SomeInteger>>(*kind)}) {
|
2018-12-05 02:55:32 +08:00
|
|
|
return ConvertToType<SubscriptInteger>(std::move(*intExpr));
|
2019-01-10 07:06:07 +08:00
|
|
|
}
|
|
|
|
}
|
2018-12-05 02:55:32 +08:00
|
|
|
return Expr<SubscriptInteger>{defaultKind};
|
2019-01-10 07:06:07 +08:00
|
|
|
},
|
2020-01-04 02:38:51 +08:00
|
|
|
[&](const parser::KindSelector::StarSize &x) {
|
2019-01-10 07:06:07 +08:00
|
|
|
std::intmax_t size = x.v;
|
2019-02-09 08:03:23 +08:00
|
|
|
if (!CheckIntrinsicSize(category, size)) {
|
2018-12-05 02:55:32 +08:00
|
|
|
size = defaultKind;
|
2019-02-09 08:03:23 +08:00
|
|
|
} else if (category == TypeCategory::Complex) {
|
|
|
|
size /= 2;
|
2019-01-10 07:06:07 +08:00
|
|
|
}
|
2018-12-05 02:55:32 +08:00
|
|
|
return Expr<SubscriptInteger>{size};
|
2019-01-10 07:06:07 +08:00
|
|
|
},
|
|
|
|
},
|
|
|
|
selector->u);
|
|
|
|
}
|
|
|
|
|
2019-03-08 06:46:31 +08:00
|
|
|
int ExpressionAnalyzer::GetDefaultKind(common::TypeCategory category) {
|
2019-06-12 09:26:48 +08:00
|
|
|
return context_.GetDefaultKind(category);
|
2019-01-10 07:06:07 +08:00
|
|
|
}
|
|
|
|
|
2019-03-08 06:46:31 +08:00
|
|
|
DynamicType ExpressionAnalyzer::GetDefaultKindOfType(
|
2019-01-10 07:06:07 +08:00
|
|
|
common::TypeCategory category) {
|
|
|
|
return {category, GetDefaultKind(category)};
|
|
|
|
}
|
2019-01-23 08:30:32 +08:00
|
|
|
|
2019-03-08 06:46:31 +08:00
|
|
|
bool ExpressionAnalyzer::CheckIntrinsicKind(
|
2019-02-09 08:03:23 +08:00
|
|
|
TypeCategory category, std::int64_t kind) {
|
2022-07-02 02:40:44 +08:00
|
|
|
if (foldingContext_.targetCharacteristics().IsTypeEnabled(
|
|
|
|
category, kind)) { // C712, C714, C715, C727
|
|
|
|
return true;
|
|
|
|
} else if (foldingContext_.targetCharacteristics().CanSupportType(
|
|
|
|
category, kind)) {
|
|
|
|
Say("%s(KIND=%jd) is not an enabled type for this targe"_warn_en_US,
|
|
|
|
ToUpperCase(EnumToString(category)), kind);
|
2019-02-09 08:03:23 +08:00
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
Say("%s(KIND=%jd) is not a supported type"_err_en_US,
|
2019-11-03 00:56:46 +08:00
|
|
|
ToUpperCase(EnumToString(category)), kind);
|
2019-02-09 08:03:23 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-08 06:46:31 +08:00
|
|
|
bool ExpressionAnalyzer::CheckIntrinsicSize(
|
2019-02-09 08:03:23 +08:00
|
|
|
TypeCategory category, std::int64_t size) {
|
2022-07-02 02:40:44 +08:00
|
|
|
std::int64_t kind{size};
|
2019-02-09 08:03:23 +08:00
|
|
|
if (category == TypeCategory::Complex) {
|
|
|
|
// COMPLEX*16 == COMPLEX(KIND=8)
|
2022-07-02 02:40:44 +08:00
|
|
|
if (size % 2 == 0) {
|
|
|
|
kind = size / 2;
|
|
|
|
} else {
|
|
|
|
Say("COMPLEX*%jd is not a supported type"_err_en_US, size);
|
|
|
|
return false;
|
2019-02-09 08:03:23 +08:00
|
|
|
}
|
2022-07-02 02:40:44 +08:00
|
|
|
}
|
|
|
|
if (foldingContext_.targetCharacteristics().IsTypeEnabled(
|
|
|
|
category, kind)) { // C712, C714, C715, C727
|
|
|
|
return true;
|
|
|
|
} else if (foldingContext_.targetCharacteristics().CanSupportType(
|
|
|
|
category, kind)) {
|
|
|
|
Say("%s*%jd is not an enabled type for this target"_warn_en_US,
|
|
|
|
ToUpperCase(EnumToString(category)), size);
|
2019-02-09 08:03:23 +08:00
|
|
|
return true;
|
2022-07-02 02:40:44 +08:00
|
|
|
} else {
|
|
|
|
Say("%s*%jd is not a supported type"_err_en_US,
|
|
|
|
ToUpperCase(EnumToString(category)), size);
|
|
|
|
return false;
|
2019-02-09 08:03:23 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Rework DATA statement semantics to use typed expressions
Summary:
Updates recent work on DATA statement semantic checking in
flang/lib/Semantics/check-data.{h,cpp} to use the compiler's
internal representation for typed expressions rather than working
on the raw parse tree. Saves the analyzed expressions for DATA
statement values as parse tree decorations because they'll soon be
needed in lowering. Corrects wording of some error messages.
Fixes a bug in constant expression checking: structure constructors
are not constant expressions if they set an allocatable component
to anything other than NULL.
Includes infrastructure changes to make this work, some renaming
to reflect the fact that the implied DO loop indices tracked by
expression analysis are not (just) from array constructors, remove
some dead code, and improve some comments.
Reviewers: tskeith, sscalpone, jdoerfert, DavidTruby, anchu-rajendran, schweitz
Reviewed By: tskeith, anchu-rajendran, schweitz
Subscribers: llvm-commits, flang-commits
Tags: #flang, #llvm
Differential Revision: https://reviews.llvm.org/D78834
2020-04-25 04:54:11 +08:00
|
|
|
bool ExpressionAnalyzer::AddImpliedDo(parser::CharBlock name, int kind) {
|
|
|
|
return impliedDos_.insert(std::make_pair(name, kind)).second;
|
2019-01-23 08:30:32 +08:00
|
|
|
}
|
|
|
|
|
Rework DATA statement semantics to use typed expressions
Summary:
Updates recent work on DATA statement semantic checking in
flang/lib/Semantics/check-data.{h,cpp} to use the compiler's
internal representation for typed expressions rather than working
on the raw parse tree. Saves the analyzed expressions for DATA
statement values as parse tree decorations because they'll soon be
needed in lowering. Corrects wording of some error messages.
Fixes a bug in constant expression checking: structure constructors
are not constant expressions if they set an allocatable component
to anything other than NULL.
Includes infrastructure changes to make this work, some renaming
to reflect the fact that the implied DO loop indices tracked by
expression analysis are not (just) from array constructors, remove
some dead code, and improve some comments.
Reviewers: tskeith, sscalpone, jdoerfert, DavidTruby, anchu-rajendran, schweitz
Reviewed By: tskeith, anchu-rajendran, schweitz
Subscribers: llvm-commits, flang-commits
Tags: #flang, #llvm
Differential Revision: https://reviews.llvm.org/D78834
2020-04-25 04:54:11 +08:00
|
|
|
void ExpressionAnalyzer::RemoveImpliedDo(parser::CharBlock name) {
|
|
|
|
auto iter{impliedDos_.find(name)};
|
|
|
|
if (iter != impliedDos_.end()) {
|
|
|
|
impliedDos_.erase(iter);
|
2019-01-23 08:30:32 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Rework DATA statement semantics to use typed expressions
Summary:
Updates recent work on DATA statement semantic checking in
flang/lib/Semantics/check-data.{h,cpp} to use the compiler's
internal representation for typed expressions rather than working
on the raw parse tree. Saves the analyzed expressions for DATA
statement values as parse tree decorations because they'll soon be
needed in lowering. Corrects wording of some error messages.
Fixes a bug in constant expression checking: structure constructors
are not constant expressions if they set an allocatable component
to anything other than NULL.
Includes infrastructure changes to make this work, some renaming
to reflect the fact that the implied DO loop indices tracked by
expression analysis are not (just) from array constructors, remove
some dead code, and improve some comments.
Reviewers: tskeith, sscalpone, jdoerfert, DavidTruby, anchu-rajendran, schweitz
Reviewed By: tskeith, anchu-rajendran, schweitz
Subscribers: llvm-commits, flang-commits
Tags: #flang, #llvm
Differential Revision: https://reviews.llvm.org/D78834
2020-04-25 04:54:11 +08:00
|
|
|
std::optional<int> ExpressionAnalyzer::IsImpliedDo(
|
2019-01-23 08:30:32 +08:00
|
|
|
parser::CharBlock name) const {
|
Rework DATA statement semantics to use typed expressions
Summary:
Updates recent work on DATA statement semantic checking in
flang/lib/Semantics/check-data.{h,cpp} to use the compiler's
internal representation for typed expressions rather than working
on the raw parse tree. Saves the analyzed expressions for DATA
statement values as parse tree decorations because they'll soon be
needed in lowering. Corrects wording of some error messages.
Fixes a bug in constant expression checking: structure constructors
are not constant expressions if they set an allocatable component
to anything other than NULL.
Includes infrastructure changes to make this work, some renaming
to reflect the fact that the implied DO loop indices tracked by
expression analysis are not (just) from array constructors, remove
some dead code, and improve some comments.
Reviewers: tskeith, sscalpone, jdoerfert, DavidTruby, anchu-rajendran, schweitz
Reviewed By: tskeith, anchu-rajendran, schweitz
Subscribers: llvm-commits, flang-commits
Tags: #flang, #llvm
Differential Revision: https://reviews.llvm.org/D78834
2020-04-25 04:54:11 +08:00
|
|
|
auto iter{impliedDos_.find(name)};
|
|
|
|
if (iter != impliedDos_.cend()) {
|
2019-01-23 08:30:32 +08:00
|
|
|
return {iter->second};
|
|
|
|
} else {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
}
|
2019-04-12 08:22:16 +08:00
|
|
|
|
2019-04-26 04:18:33 +08:00
|
|
|
bool ExpressionAnalyzer::EnforceTypeConstraint(parser::CharBlock at,
|
2019-04-12 08:22:16 +08:00
|
|
|
const MaybeExpr &result, TypeCategory category, bool defaultKind) {
|
2019-11-10 01:29:31 +08:00
|
|
|
if (result) {
|
2019-04-12 08:22:16 +08:00
|
|
|
if (auto type{result->GetType()}) {
|
2020-03-28 05:17:25 +08:00
|
|
|
if (type->category() != category) { // C885
|
2019-04-12 08:22:16 +08:00
|
|
|
Say(at, "Must have %s type, but is %s"_err_en_US,
|
2019-11-03 00:56:46 +08:00
|
|
|
ToUpperCase(EnumToString(category)),
|
|
|
|
ToUpperCase(type->AsFortran()));
|
2019-04-26 04:18:33 +08:00
|
|
|
return false;
|
2019-04-12 08:22:16 +08:00
|
|
|
} else if (defaultKind) {
|
2019-06-12 09:26:48 +08:00
|
|
|
int kind{context_.GetDefaultKind(category)};
|
2019-05-14 00:33:18 +08:00
|
|
|
if (type->kind() != kind) {
|
2019-04-12 08:22:16 +08:00
|
|
|
Say(at, "Must have default kind(%d) of %s type, but is %s"_err_en_US,
|
2019-11-03 00:56:46 +08:00
|
|
|
kind, ToUpperCase(EnumToString(category)),
|
|
|
|
ToUpperCase(type->AsFortran()));
|
2019-04-26 04:18:33 +08:00
|
|
|
return false;
|
2019-04-12 08:22:16 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Say(at, "Must have %s type, but is typeless"_err_en_US,
|
2019-11-03 00:56:46 +08:00
|
|
|
ToUpperCase(EnumToString(category)));
|
2019-04-26 04:18:33 +08:00
|
|
|
return false;
|
2019-04-12 08:22:16 +08:00
|
|
|
}
|
|
|
|
}
|
2019-04-26 04:18:33 +08:00
|
|
|
return true;
|
2019-04-12 08:22:16 +08:00
|
|
|
}
|
2019-04-26 04:18:33 +08:00
|
|
|
|
2019-09-17 07:58:13 +08:00
|
|
|
MaybeExpr ExpressionAnalyzer::MakeFunctionRef(parser::CharBlock callSite,
|
2019-03-12 06:39:11 +08:00
|
|
|
ProcedureDesignator &&proc, ActualArguments &&arguments) {
|
|
|
|
if (const auto *intrinsic{std::get_if<SpecificIntrinsic>(&proc.u)}) {
|
2022-02-08 07:34:54 +08:00
|
|
|
if (intrinsic->characteristics.value().attrs.test(
|
|
|
|
characteristics::Procedure::Attr::NullPointer) &&
|
|
|
|
arguments.empty()) {
|
2019-03-12 06:39:11 +08:00
|
|
|
return Expr<SomeType>{NullPointer{}};
|
|
|
|
}
|
|
|
|
}
|
2020-03-14 03:19:44 +08:00
|
|
|
if (const Symbol * symbol{proc.GetSymbol()}) {
|
|
|
|
if (!ResolveForward(*symbol)) {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
}
|
2019-09-17 07:58:13 +08:00
|
|
|
if (auto chars{CheckCall(callSite, proc, arguments)}) {
|
2019-11-10 01:29:31 +08:00
|
|
|
if (chars->functionResult) {
|
2019-03-12 06:39:11 +08:00
|
|
|
const auto &result{*chars->functionResult};
|
|
|
|
if (result.IsProcedurePointer()) {
|
|
|
|
return Expr<SomeType>{
|
|
|
|
ProcedureRef{std::move(proc), std::move(arguments)}};
|
|
|
|
} else {
|
|
|
|
// Not a procedure pointer, so type and shape are known.
|
2019-07-30 00:12:52 +08:00
|
|
|
return TypedWrapper<FunctionRef, ProcedureRef>(
|
|
|
|
DEREF(result.GetTypeAndShape()).type(),
|
2019-03-12 06:39:11 +08:00
|
|
|
ProcedureRef{std::move(proc), std::move(arguments)});
|
|
|
|
}
|
2021-06-03 08:25:29 +08:00
|
|
|
} else {
|
|
|
|
Say("Function result characteristics are not known"_err_en_US);
|
2019-03-12 06:39:11 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
|
|
|
MaybeExpr ExpressionAnalyzer::MakeFunctionRef(
|
|
|
|
parser::CharBlock intrinsic, ActualArguments &&arguments) {
|
|
|
|
if (std::optional<SpecificCall> specificCall{
|
2019-12-12 07:06:24 +08:00
|
|
|
context_.intrinsics().Probe(CallCharacteristics{intrinsic.ToString()},
|
2021-09-22 07:06:30 +08:00
|
|
|
arguments, GetFoldingContext())}) {
|
2019-09-17 07:58:13 +08:00
|
|
|
return MakeFunctionRef(intrinsic,
|
2019-03-12 06:39:11 +08:00
|
|
|
ProcedureDesignator{std::move(specificCall->specificIntrinsic)},
|
|
|
|
std::move(specificCall->arguments));
|
|
|
|
} else {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
}
|
2019-10-23 00:31:33 +08:00
|
|
|
|
2019-11-23 08:46:11 +08:00
|
|
|
void ArgumentAnalyzer::Analyze(const parser::Variable &x) {
|
|
|
|
source_.ExtendToCover(x.GetSource());
|
|
|
|
if (MaybeExpr expr{context_.Analyze(x)}) {
|
2020-03-06 09:55:51 +08:00
|
|
|
if (!IsConstantExpr(*expr)) {
|
|
|
|
actuals_.emplace_back(std::move(*expr));
|
2022-02-12 08:58:01 +08:00
|
|
|
SetArgSourceLocation(actuals_.back(), x.GetSource());
|
2020-03-06 09:55:51 +08:00
|
|
|
return;
|
|
|
|
}
|
2020-08-07 11:33:59 +08:00
|
|
|
const Symbol *symbol{GetLastSymbol(*expr)};
|
|
|
|
if (!symbol) {
|
|
|
|
context_.SayAt(x, "Assignment to constant '%s' is not allowed"_err_en_US,
|
|
|
|
x.GetSource());
|
|
|
|
} else if (auto *subp{symbol->detailsIf<semantics::SubprogramDetails>()}) {
|
|
|
|
auto *msg{context_.SayAt(x,
|
|
|
|
"Assignment to subprogram '%s' is not allowed"_err_en_US,
|
|
|
|
symbol->name())};
|
|
|
|
if (subp->isFunction()) {
|
|
|
|
const auto &result{subp->result().name()};
|
2022-04-02 03:46:04 +08:00
|
|
|
msg->Attach(result, "Function result is '%s'"_en_US, result);
|
2020-08-07 11:33:59 +08:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
context_.SayAt(x, "Assignment to constant '%s' is not allowed"_err_en_US,
|
|
|
|
symbol->name());
|
|
|
|
}
|
2019-11-23 08:46:11 +08:00
|
|
|
}
|
2020-03-06 09:55:51 +08:00
|
|
|
fatalErrors_ = true;
|
2019-11-23 08:46:11 +08:00
|
|
|
}
|
|
|
|
|
2019-10-23 00:31:33 +08:00
|
|
|
void ArgumentAnalyzer::Analyze(
|
|
|
|
const parser::ActualArgSpec &arg, bool isSubroutine) {
|
|
|
|
// TODO: Actual arguments that are procedures and procedure pointers need to
|
|
|
|
// be detected and represented (they're not expressions).
|
|
|
|
// TODO: C1534: Don't allow a "restricted" specific intrinsic to be passed.
|
|
|
|
std::optional<ActualArgument> actual;
|
2022-03-24 05:05:50 +08:00
|
|
|
common::visit(
|
2022-03-24 06:20:56 +08:00
|
|
|
common::visitors{
|
|
|
|
[&](const common::Indirection<parser::Expr> &x) {
|
|
|
|
actual = AnalyzeExpr(x.value());
|
|
|
|
SetArgSourceLocation(actual, x.value().source);
|
|
|
|
},
|
|
|
|
[&](const parser::AltReturnSpec &label) {
|
|
|
|
if (!isSubroutine) {
|
|
|
|
context_.Say("alternate return specification may not appear on"
|
|
|
|
" function reference"_err_en_US);
|
|
|
|
}
|
|
|
|
actual = ActualArgument(label.v);
|
|
|
|
},
|
|
|
|
[&](const parser::ActualArg::PercentRef &) {
|
2022-05-05 09:30:22 +08:00
|
|
|
context_.Say("%REF() intrinsic for arguments"_todo_en_US);
|
2022-03-24 06:20:56 +08:00
|
|
|
},
|
|
|
|
[&](const parser::ActualArg::PercentVal &) {
|
2022-05-05 09:30:22 +08:00
|
|
|
context_.Say("%VAL() intrinsic for arguments"_todo_en_US);
|
2022-03-24 06:20:56 +08:00
|
|
|
},
|
|
|
|
},
|
2019-10-23 00:31:33 +08:00
|
|
|
std::get<parser::ActualArg>(arg.t).u);
|
2019-11-10 01:29:31 +08:00
|
|
|
if (actual) {
|
2019-10-23 00:31:33 +08:00
|
|
|
if (const auto &argKW{std::get<std::optional<parser::Keyword>>(arg.t)}) {
|
2019-12-06 02:24:18 +08:00
|
|
|
actual->set_keyword(argKW->v.source);
|
2019-10-23 00:31:33 +08:00
|
|
|
}
|
|
|
|
actuals_.emplace_back(std::move(*actual));
|
2021-01-05 01:35:15 +08:00
|
|
|
} else {
|
2019-11-03 00:56:46 +08:00
|
|
|
fatalErrors_ = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-08 03:17:31 +08:00
|
|
|
bool ArgumentAnalyzer::IsIntrinsicRelational(RelationalOperator opr,
|
|
|
|
const DynamicType &leftType, const DynamicType &rightType) const {
|
2019-12-03 00:55:44 +08:00
|
|
|
CHECK(actuals_.size() == 2);
|
|
|
|
return semantics::IsIntrinsicRelational(
|
2021-09-08 03:17:31 +08:00
|
|
|
opr, leftType, GetRank(0), rightType, GetRank(1));
|
2019-11-03 00:56:46 +08:00
|
|
|
}
|
|
|
|
|
2019-11-07 07:54:26 +08:00
|
|
|
bool ArgumentAnalyzer::IsIntrinsicNumeric(NumericOperator opr) const {
|
2021-09-08 03:17:31 +08:00
|
|
|
std::optional<DynamicType> leftType{GetType(0)};
|
2019-11-07 07:54:26 +08:00
|
|
|
if (actuals_.size() == 1) {
|
|
|
|
if (IsBOZLiteral(0)) {
|
2021-09-08 03:17:31 +08:00
|
|
|
return opr == NumericOperator::Add; // unary '+'
|
2019-11-07 07:54:26 +08:00
|
|
|
} else {
|
2021-09-08 03:17:31 +08:00
|
|
|
return leftType && semantics::IsIntrinsicNumeric(*leftType);
|
2019-11-07 07:54:26 +08:00
|
|
|
}
|
|
|
|
} else {
|
2021-09-08 03:17:31 +08:00
|
|
|
std::optional<DynamicType> rightType{GetType(1)};
|
|
|
|
if (IsBOZLiteral(0) && rightType) { // BOZ opr Integer/Real
|
|
|
|
auto cat1{rightType->category()};
|
2019-11-07 07:54:26 +08:00
|
|
|
return cat1 == TypeCategory::Integer || cat1 == TypeCategory::Real;
|
2021-09-08 03:17:31 +08:00
|
|
|
} else if (IsBOZLiteral(1) && leftType) { // Integer/Real opr BOZ
|
|
|
|
auto cat0{leftType->category()};
|
2019-11-07 07:54:26 +08:00
|
|
|
return cat0 == TypeCategory::Integer || cat0 == TypeCategory::Real;
|
|
|
|
} else {
|
2021-09-08 03:17:31 +08:00
|
|
|
return leftType && rightType &&
|
|
|
|
semantics::IsIntrinsicNumeric(
|
|
|
|
*leftType, GetRank(0), *rightType, GetRank(1));
|
2019-11-07 07:54:26 +08:00
|
|
|
}
|
|
|
|
}
|
2019-11-03 00:56:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
bool ArgumentAnalyzer::IsIntrinsicLogical() const {
|
2021-09-08 03:17:31 +08:00
|
|
|
if (std::optional<DynamicType> leftType{GetType(0)}) {
|
|
|
|
if (actuals_.size() == 1) {
|
|
|
|
return semantics::IsIntrinsicLogical(*leftType);
|
|
|
|
} else if (std::optional<DynamicType> rightType{GetType(1)}) {
|
|
|
|
return semantics::IsIntrinsicLogical(
|
|
|
|
*leftType, GetRank(0), *rightType, GetRank(1));
|
|
|
|
}
|
2019-12-03 00:55:44 +08:00
|
|
|
}
|
2021-09-08 03:17:31 +08:00
|
|
|
return false;
|
2019-11-03 00:56:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
bool ArgumentAnalyzer::IsIntrinsicConcat() const {
|
2021-09-08 03:17:31 +08:00
|
|
|
if (std::optional<DynamicType> leftType{GetType(0)}) {
|
|
|
|
if (std::optional<DynamicType> rightType{GetType(1)}) {
|
|
|
|
return semantics::IsIntrinsicConcat(
|
|
|
|
*leftType, GetRank(0), *rightType, GetRank(1));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
2019-11-03 00:56:46 +08:00
|
|
|
}
|
|
|
|
|
2021-09-08 03:17:31 +08:00
|
|
|
bool ArgumentAnalyzer::CheckConformance() {
|
2020-09-01 03:06:41 +08:00
|
|
|
if (actuals_.size() == 2) {
|
|
|
|
const auto *lhs{actuals_.at(0).value().UnwrapExpr()};
|
|
|
|
const auto *rhs{actuals_.at(1).value().UnwrapExpr()};
|
|
|
|
if (lhs && rhs) {
|
|
|
|
auto &foldingContext{context_.GetFoldingContext()};
|
|
|
|
auto lhShape{GetShape(foldingContext, *lhs)};
|
|
|
|
auto rhShape{GetShape(foldingContext, *rhs)};
|
|
|
|
if (lhShape && rhShape) {
|
2021-09-08 03:17:31 +08:00
|
|
|
if (!evaluate::CheckConformance(foldingContext.messages(), *lhShape,
|
|
|
|
*rhShape, CheckConformanceFlags::EitherScalarExpandable,
|
|
|
|
"left operand", "right operand")
|
|
|
|
.value_or(false /*fail when conformance is not known now*/)) {
|
|
|
|
fatalErrors_ = true;
|
|
|
|
return false;
|
|
|
|
}
|
2020-09-01 03:06:41 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true; // no proven problem
|
|
|
|
}
|
|
|
|
|
2021-09-08 03:17:31 +08:00
|
|
|
bool ArgumentAnalyzer::CheckForNullPointer(const char *where) {
|
|
|
|
for (const std::optional<ActualArgument> &arg : actuals_) {
|
|
|
|
if (arg) {
|
|
|
|
if (const Expr<SomeType> *expr{arg->UnwrapExpr()}) {
|
|
|
|
if (IsNullPointer(*expr)) {
|
|
|
|
context_.Say(
|
|
|
|
source_, "A NULL() pointer is not allowed %s"_err_en_US, where);
|
|
|
|
fatalErrors_ = true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
MaybeExpr ArgumentAnalyzer::TryDefinedOp(const char *opr,
|
|
|
|
parser::MessageFixedText error, const Symbol **definedOpSymbolPtr,
|
|
|
|
bool isUserOp) {
|
2021-01-16 03:52:10 +08:00
|
|
|
if (AnyUntypedOrMissingOperand()) {
|
2021-09-08 03:17:31 +08:00
|
|
|
context_.Say(error, ToUpperCase(opr), TypeAsFortran(0), TypeAsFortran(1));
|
2019-11-03 00:56:46 +08:00
|
|
|
return std::nullopt;
|
|
|
|
}
|
2021-09-08 03:17:31 +08:00
|
|
|
const Symbol *localDefinedOpSymbolPtr{nullptr};
|
|
|
|
if (!definedOpSymbolPtr) {
|
|
|
|
definedOpSymbolPtr = &localDefinedOpSymbolPtr;
|
|
|
|
}
|
2019-12-17 03:33:55 +08:00
|
|
|
{
|
|
|
|
auto restorer{context_.GetContextualMessages().DiscardMessages()};
|
2020-01-03 01:55:03 +08:00
|
|
|
std::string oprNameString{
|
|
|
|
isUserOp ? std::string{opr} : "operator("s + opr + ')'};
|
2019-12-17 03:33:55 +08:00
|
|
|
parser::CharBlock oprName{oprNameString};
|
|
|
|
const auto &scope{context_.context().FindScope(source_)};
|
|
|
|
if (Symbol * symbol{scope.FindSymbol(oprName)}) {
|
2021-09-08 03:17:31 +08:00
|
|
|
*definedOpSymbolPtr = symbol;
|
2020-01-03 01:55:03 +08:00
|
|
|
parser::Name name{symbol->name(), symbol};
|
2019-12-17 03:33:55 +08:00
|
|
|
if (auto result{context_.AnalyzeDefinedOp(name, GetActuals())}) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (std::size_t passIndex{0}; passIndex < actuals_.size(); ++passIndex) {
|
2021-09-08 03:17:31 +08:00
|
|
|
if (const Symbol *
|
|
|
|
symbol{FindBoundOp(oprName, passIndex, *definedOpSymbolPtr)}) {
|
2019-12-17 03:33:55 +08:00
|
|
|
if (MaybeExpr result{TryBoundOp(*symbol, passIndex)}) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-09-08 03:17:31 +08:00
|
|
|
if (*definedOpSymbolPtr) {
|
|
|
|
SayNoMatch(ToUpperCase((*definedOpSymbolPtr)->name().ToString()));
|
2019-12-17 03:33:55 +08:00
|
|
|
} else if (actuals_.size() == 1 || AreConformable()) {
|
2021-09-08 03:17:31 +08:00
|
|
|
if (CheckForNullPointer()) {
|
|
|
|
context_.Say(error, ToUpperCase(opr), TypeAsFortran(0), TypeAsFortran(1));
|
|
|
|
}
|
2019-11-03 00:56:46 +08:00
|
|
|
} else {
|
2019-12-17 03:33:55 +08:00
|
|
|
context_.Say(
|
|
|
|
"Operands of %s are not conformable; have rank %d and rank %d"_err_en_US,
|
|
|
|
ToUpperCase(opr), actuals_[0]->Rank(), actuals_[1]->Rank());
|
2019-11-03 00:56:46 +08:00
|
|
|
}
|
2019-12-17 03:33:55 +08:00
|
|
|
return std::nullopt;
|
2019-11-03 00:56:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
MaybeExpr ArgumentAnalyzer::TryDefinedOp(
|
2021-09-08 03:17:31 +08:00
|
|
|
std::vector<const char *> oprs, parser::MessageFixedText error) {
|
|
|
|
const Symbol *definedOpSymbolPtr{nullptr};
|
2019-11-07 07:54:26 +08:00
|
|
|
for (std::size_t i{1}; i < oprs.size(); ++i) {
|
2019-12-17 03:33:55 +08:00
|
|
|
auto restorer{context_.GetContextualMessages().DiscardMessages()};
|
2021-09-08 03:17:31 +08:00
|
|
|
if (auto result{TryDefinedOp(oprs[i], error, &definedOpSymbolPtr)}) {
|
2019-12-17 03:33:55 +08:00
|
|
|
return result;
|
2019-11-03 00:56:46 +08:00
|
|
|
}
|
2019-10-23 00:31:33 +08:00
|
|
|
}
|
2021-09-08 03:17:31 +08:00
|
|
|
return TryDefinedOp(oprs[0], error, &definedOpSymbolPtr);
|
2019-10-23 00:31:33 +08:00
|
|
|
}
|
|
|
|
|
2019-12-17 03:33:55 +08:00
|
|
|
MaybeExpr ArgumentAnalyzer::TryBoundOp(const Symbol &symbol, int passIndex) {
|
|
|
|
ActualArguments localActuals{actuals_};
|
2020-01-03 01:55:03 +08:00
|
|
|
const Symbol *proc{GetBindingResolution(GetType(passIndex), symbol)};
|
2019-12-17 03:33:55 +08:00
|
|
|
if (!proc) {
|
|
|
|
proc = &symbol;
|
2020-01-03 01:55:03 +08:00
|
|
|
localActuals.at(passIndex).value().set_isPassedObject();
|
2019-12-17 03:33:55 +08:00
|
|
|
}
|
2020-09-01 03:06:41 +08:00
|
|
|
CheckConformance();
|
2019-12-17 03:33:55 +08:00
|
|
|
return context_.MakeFunctionRef(
|
|
|
|
source_, ProcedureDesignator{*proc}, std::move(localActuals));
|
|
|
|
}
|
|
|
|
|
2019-11-23 08:46:11 +08:00
|
|
|
std::optional<ProcedureRef> ArgumentAnalyzer::TryDefinedAssignment() {
|
|
|
|
using semantics::Tristate;
|
|
|
|
const Expr<SomeType> &lhs{GetExpr(0)};
|
|
|
|
const Expr<SomeType> &rhs{GetExpr(1)};
|
2019-12-06 08:18:39 +08:00
|
|
|
std::optional<DynamicType> lhsType{lhs.GetType()};
|
|
|
|
std::optional<DynamicType> rhsType{rhs.GetType()};
|
|
|
|
int lhsRank{lhs.Rank()};
|
|
|
|
int rhsRank{rhs.Rank()};
|
|
|
|
Tristate isDefined{
|
|
|
|
semantics::IsDefinedAssignment(lhsType, lhsRank, rhsType, rhsRank)};
|
2019-11-23 08:46:11 +08:00
|
|
|
if (isDefined == Tristate::No) {
|
2020-05-10 00:10:08 +08:00
|
|
|
if (lhsType && rhsType) {
|
|
|
|
AddAssignmentConversion(*lhsType, *rhsType);
|
|
|
|
}
|
2020-03-28 05:17:25 +08:00
|
|
|
return std::nullopt; // user-defined assignment not allowed for these args
|
2019-11-23 08:46:11 +08:00
|
|
|
}
|
|
|
|
auto restorer{context_.GetContextualMessages().SetLocation(source_)};
|
2019-12-17 03:33:55 +08:00
|
|
|
if (std::optional<ProcedureRef> procRef{GetDefinedAssignmentProc()}) {
|
2021-09-15 23:28:48 +08:00
|
|
|
if (context_.inWhereBody() && !procRef->proc().IsElemental()) { // C1032
|
|
|
|
context_.Say(
|
|
|
|
"Defined assignment in WHERE must be elemental, but '%s' is not"_err_en_US,
|
|
|
|
DEREF(procRef->proc().GetSymbol()).name());
|
|
|
|
}
|
2019-12-17 03:33:55 +08:00
|
|
|
context_.CheckCall(source_, procRef->proc(), procRef->arguments());
|
|
|
|
return std::move(*procRef);
|
|
|
|
}
|
|
|
|
if (isDefined == Tristate::Yes) {
|
|
|
|
if (!lhsType || !rhsType || (lhsRank != rhsRank && rhsRank != 0) ||
|
|
|
|
!OkLogicalIntegerAssignment(lhsType->category(), rhsType->category())) {
|
|
|
|
SayNoMatch("ASSIGNMENT(=)", true);
|
2019-11-23 08:46:11 +08:00
|
|
|
}
|
|
|
|
}
|
2019-12-17 03:33:55 +08:00
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ArgumentAnalyzer::OkLogicalIntegerAssignment(
|
|
|
|
TypeCategory lhs, TypeCategory rhs) {
|
|
|
|
if (!context_.context().languageFeatures().IsEnabled(
|
|
|
|
common::LanguageFeature::LogicalIntegerAssignment)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
std::optional<parser::MessageFixedText> msg;
|
|
|
|
if (lhs == TypeCategory::Integer && rhs == TypeCategory::Logical) {
|
|
|
|
// allow assignment to LOGICAL from INTEGER as a legacy extension
|
2022-05-25 06:06:12 +08:00
|
|
|
msg = "assignment of LOGICAL to INTEGER"_port_en_US;
|
2019-12-17 03:33:55 +08:00
|
|
|
} else if (lhs == TypeCategory::Logical && rhs == TypeCategory::Integer) {
|
|
|
|
// ... and assignment to LOGICAL from INTEGER
|
2022-05-25 06:06:12 +08:00
|
|
|
msg = "assignment of INTEGER to LOGICAL"_port_en_US;
|
2019-12-17 03:33:55 +08:00
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (context_.context().languageFeatures().ShouldWarn(
|
|
|
|
common::LanguageFeature::LogicalIntegerAssignment)) {
|
|
|
|
context_.Say(std::move(*msg));
|
|
|
|
}
|
|
|
|
return true;
|
2019-11-23 08:46:11 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
std::optional<ProcedureRef> ArgumentAnalyzer::GetDefinedAssignmentProc() {
|
2019-12-17 03:33:55 +08:00
|
|
|
auto restorer{context_.GetContextualMessages().DiscardMessages()};
|
|
|
|
std::string oprNameString{"assignment(=)"};
|
|
|
|
parser::CharBlock oprName{oprNameString};
|
|
|
|
const Symbol *proc{nullptr};
|
2019-11-23 08:46:11 +08:00
|
|
|
const auto &scope{context_.context().FindScope(source_)};
|
2019-12-17 03:33:55 +08:00
|
|
|
if (const Symbol * symbol{scope.FindSymbol(oprName)}) {
|
2019-12-24 09:12:53 +08:00
|
|
|
ExpressionAnalyzer::AdjustActuals noAdjustment;
|
2022-05-27 07:56:27 +08:00
|
|
|
auto pair{context_.ResolveGeneric(*symbol, actuals_, noAdjustment, true)};
|
2021-10-15 01:32:59 +08:00
|
|
|
if (pair.first) {
|
|
|
|
proc = pair.first;
|
2019-12-24 09:12:53 +08:00
|
|
|
} else {
|
2021-10-15 01:32:59 +08:00
|
|
|
context_.EmitGenericResolutionError(*symbol, pair.second);
|
2019-11-23 08:46:11 +08:00
|
|
|
}
|
|
|
|
}
|
2020-09-15 00:59:49 +08:00
|
|
|
int passedObjectIndex{-1};
|
2021-09-08 03:17:31 +08:00
|
|
|
const Symbol *definedOpSymbol{nullptr};
|
2020-09-15 00:59:49 +08:00
|
|
|
for (std::size_t i{0}; i < actuals_.size(); ++i) {
|
2021-09-08 03:17:31 +08:00
|
|
|
if (const Symbol * specific{FindBoundOp(oprName, i, definedOpSymbol)}) {
|
2020-09-15 00:59:49 +08:00
|
|
|
if (const Symbol *
|
|
|
|
resolution{GetBindingResolution(GetType(i), *specific)}) {
|
|
|
|
proc = resolution;
|
|
|
|
} else {
|
|
|
|
proc = specific;
|
|
|
|
passedObjectIndex = i;
|
|
|
|
}
|
2019-12-17 03:33:55 +08:00
|
|
|
}
|
|
|
|
}
|
2020-09-15 00:59:49 +08:00
|
|
|
if (!proc) {
|
2019-12-17 03:33:55 +08:00
|
|
|
return std::nullopt;
|
|
|
|
}
|
2020-09-15 00:59:49 +08:00
|
|
|
ActualArguments actualsCopy{actuals_};
|
|
|
|
if (passedObjectIndex >= 0) {
|
|
|
|
actualsCopy[passedObjectIndex]->set_isPassedObject();
|
|
|
|
}
|
|
|
|
return ProcedureRef{ProcedureDesignator{*proc}, std::move(actualsCopy)};
|
2019-11-23 08:46:11 +08:00
|
|
|
}
|
|
|
|
|
2020-02-28 23:11:03 +08:00
|
|
|
void ArgumentAnalyzer::Dump(llvm::raw_ostream &os) {
|
2020-01-11 06:09:39 +08:00
|
|
|
os << "source_: " << source_.ToString() << " fatalErrors_ = " << fatalErrors_
|
|
|
|
<< '\n';
|
|
|
|
for (const auto &actual : actuals_) {
|
|
|
|
if (!actual.has_value()) {
|
|
|
|
os << "- error\n";
|
|
|
|
} else if (const Symbol * symbol{actual->GetAssumedTypeDummy()}) {
|
|
|
|
os << "- assumed type: " << symbol->name().ToString() << '\n';
|
|
|
|
} else if (const Expr<SomeType> *expr{actual->UnwrapExpr()}) {
|
|
|
|
expr->AsFortran(os << "- expr: ") << '\n';
|
|
|
|
} else {
|
|
|
|
DIE("bad ActualArgument");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-10-02 03:08:04 +08:00
|
|
|
|
2019-11-03 00:56:46 +08:00
|
|
|
std::optional<ActualArgument> ArgumentAnalyzer::AnalyzeExpr(
|
2019-10-23 00:31:33 +08:00
|
|
|
const parser::Expr &expr) {
|
2019-11-03 00:56:46 +08:00
|
|
|
source_.ExtendToCover(expr.source);
|
2019-10-23 00:31:33 +08:00
|
|
|
if (const Symbol * assumedTypeDummy{AssumedTypeDummy(expr)}) {
|
2020-07-10 02:08:41 +08:00
|
|
|
expr.typedExpr.Reset(new GenericExprWrapper{}, GenericExprWrapper::Deleter);
|
2020-08-19 01:47:52 +08:00
|
|
|
if (isProcedureCall_) {
|
2022-02-12 08:58:01 +08:00
|
|
|
ActualArgument arg{ActualArgument::AssumedType{*assumedTypeDummy}};
|
|
|
|
SetArgSourceLocation(arg, expr.source);
|
|
|
|
return std::move(arg);
|
2020-01-11 06:09:39 +08:00
|
|
|
}
|
2020-10-02 03:08:04 +08:00
|
|
|
context_.SayAt(expr.source,
|
|
|
|
"TYPE(*) dummy argument may only be used as an actual argument"_err_en_US);
|
|
|
|
} else if (MaybeExpr argExpr{AnalyzeExprOrWholeAssumedSizeArray(expr)}) {
|
|
|
|
if (isProcedureCall_ || !IsProcedure(*argExpr)) {
|
2022-02-12 08:58:01 +08:00
|
|
|
ActualArgument arg{std::move(*argExpr)};
|
|
|
|
SetArgSourceLocation(arg, expr.source);
|
|
|
|
return std::move(arg);
|
2020-10-02 03:08:04 +08:00
|
|
|
}
|
|
|
|
context_.SayAt(expr.source,
|
|
|
|
IsFunction(*argExpr) ? "Function call must have argument list"_err_en_US
|
|
|
|
: "Subroutine name is not allowed here"_err_en_US);
|
|
|
|
}
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
|
|
|
MaybeExpr ArgumentAnalyzer::AnalyzeExprOrWholeAssumedSizeArray(
|
|
|
|
const parser::Expr &expr) {
|
|
|
|
// If an expression's parse tree is a whole assumed-size array:
|
|
|
|
// Expr -> Designator -> DataRef -> Name
|
|
|
|
// treat it as a special case for argument passing and bypass
|
|
|
|
// the C1002/C1014 constraint checking in expression semantics.
|
|
|
|
if (const auto *name{parser::Unwrap<parser::Name>(expr)}) {
|
|
|
|
if (name->symbol && semantics::IsAssumedSizeArray(*name->symbol)) {
|
|
|
|
auto restorer{context_.AllowWholeAssumedSizeArray()};
|
|
|
|
return context_.Analyze(expr);
|
2020-08-19 01:47:52 +08:00
|
|
|
}
|
2019-10-23 00:31:33 +08:00
|
|
|
}
|
2020-10-02 03:08:04 +08:00
|
|
|
return context_.Analyze(expr);
|
2018-10-25 20:55:23 +08:00
|
|
|
}
|
2018-08-08 03:34:09 +08:00
|
|
|
|
2019-11-03 00:56:46 +08:00
|
|
|
bool ArgumentAnalyzer::AreConformable() const {
|
2021-09-08 03:17:31 +08:00
|
|
|
CHECK(actuals_.size() == 2);
|
|
|
|
return actuals_[0] && actuals_[1] &&
|
|
|
|
evaluate::AreConformable(*actuals_[0], *actuals_[1]);
|
2019-11-03 00:56:46 +08:00
|
|
|
}
|
|
|
|
|
2019-12-17 03:33:55 +08:00
|
|
|
// Look for a type-bound operator in the type of arg number passIndex.
|
|
|
|
const Symbol *ArgumentAnalyzer::FindBoundOp(
|
2021-09-08 03:17:31 +08:00
|
|
|
parser::CharBlock oprName, int passIndex, const Symbol *&definedOp) {
|
2019-12-17 03:33:55 +08:00
|
|
|
const auto *type{GetDerivedTypeSpec(GetType(passIndex))};
|
|
|
|
if (!type || !type->scope()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
2020-01-03 01:55:03 +08:00
|
|
|
const Symbol *symbol{type->scope()->FindComponent(oprName)};
|
2019-12-17 03:33:55 +08:00
|
|
|
if (!symbol) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
2021-09-08 03:17:31 +08:00
|
|
|
definedOp = symbol;
|
2019-12-24 09:12:53 +08:00
|
|
|
ExpressionAnalyzer::AdjustActuals adjustment{
|
|
|
|
[&](const Symbol &proc, ActualArguments &) {
|
2019-12-17 03:33:55 +08:00
|
|
|
return passIndex == GetPassIndex(proc);
|
2019-12-24 09:12:53 +08:00
|
|
|
}};
|
2022-05-27 07:56:27 +08:00
|
|
|
auto pair{context_.ResolveGeneric(*symbol, actuals_, adjustment, false)};
|
2021-10-15 01:32:59 +08:00
|
|
|
if (!pair.first) {
|
|
|
|
context_.EmitGenericResolutionError(*symbol, pair.second);
|
2019-12-24 09:12:53 +08:00
|
|
|
}
|
2021-10-15 01:32:59 +08:00
|
|
|
return pair.first;
|
2019-11-03 00:56:46 +08:00
|
|
|
}
|
|
|
|
|
2020-05-10 00:10:08 +08:00
|
|
|
// If there is an implicit conversion between intrinsic types, make it explicit
|
|
|
|
void ArgumentAnalyzer::AddAssignmentConversion(
|
|
|
|
const DynamicType &lhsType, const DynamicType &rhsType) {
|
|
|
|
if (lhsType.category() == rhsType.category() &&
|
2022-03-24 07:02:59 +08:00
|
|
|
(lhsType.category() == TypeCategory::Derived ||
|
|
|
|
lhsType.kind() == rhsType.kind())) {
|
2020-05-10 00:10:08 +08:00
|
|
|
// no conversion necessary
|
|
|
|
} else if (auto rhsExpr{evaluate::ConvertToType(lhsType, MoveExpr(1))}) {
|
2022-02-12 08:58:01 +08:00
|
|
|
std::optional<parser::CharBlock> source;
|
|
|
|
if (actuals_[1]) {
|
|
|
|
source = actuals_[1]->sourceLocation();
|
|
|
|
}
|
2020-05-10 00:10:08 +08:00
|
|
|
actuals_[1] = ActualArgument{*rhsExpr};
|
2022-02-12 08:58:01 +08:00
|
|
|
SetArgSourceLocation(actuals_[1], source);
|
2020-05-10 00:10:08 +08:00
|
|
|
} else {
|
|
|
|
actuals_[1] = std::nullopt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-03 00:56:46 +08:00
|
|
|
std::optional<DynamicType> ArgumentAnalyzer::GetType(std::size_t i) const {
|
|
|
|
return i < actuals_.size() ? actuals_[i].value().GetType() : std::nullopt;
|
|
|
|
}
|
2019-12-03 00:55:44 +08:00
|
|
|
int ArgumentAnalyzer::GetRank(std::size_t i) const {
|
|
|
|
return i < actuals_.size() ? actuals_[i].value().Rank() : 0;
|
|
|
|
}
|
2019-11-03 00:56:46 +08:00
|
|
|
|
2020-07-16 07:02:49 +08:00
|
|
|
// If the argument at index i is a BOZ literal, convert its type to match the
|
2021-06-03 08:02:43 +08:00
|
|
|
// otherType. If it's REAL convert to REAL, otherwise convert to INTEGER.
|
2020-07-16 07:02:49 +08:00
|
|
|
// Note that IBM supports comparing BOZ literals to CHARACTER operands. That
|
|
|
|
// is not currently supported.
|
2021-09-08 03:17:31 +08:00
|
|
|
void ArgumentAnalyzer::ConvertBOZ(std::optional<DynamicType> &thisType,
|
2020-07-16 07:02:49 +08:00
|
|
|
std::size_t i, std::optional<DynamicType> otherType) {
|
|
|
|
if (IsBOZLiteral(i)) {
|
|
|
|
Expr<SomeType> &&argExpr{MoveExpr(i)};
|
|
|
|
auto *boz{std::get_if<BOZLiteralConstant>(&argExpr.u)};
|
|
|
|
if (otherType && otherType->category() == TypeCategory::Real) {
|
2021-09-08 03:17:31 +08:00
|
|
|
int kind{context_.context().GetDefaultKind(TypeCategory::Real)};
|
|
|
|
MaybeExpr realExpr{
|
|
|
|
ConvertToKind<TypeCategory::Real>(kind, std::move(*boz))};
|
2020-07-16 07:02:49 +08:00
|
|
|
actuals_[i] = std::move(*realExpr);
|
2021-09-08 03:17:31 +08:00
|
|
|
thisType.emplace(TypeCategory::Real, kind);
|
2020-07-16 07:02:49 +08:00
|
|
|
} else {
|
2021-09-08 03:17:31 +08:00
|
|
|
int kind{context_.context().GetDefaultKind(TypeCategory::Integer)};
|
|
|
|
MaybeExpr intExpr{
|
|
|
|
ConvertToKind<TypeCategory::Integer>(kind, std::move(*boz))};
|
2020-07-16 07:02:49 +08:00
|
|
|
actuals_[i] = std::move(*intExpr);
|
2021-09-08 03:17:31 +08:00
|
|
|
thisType.emplace(TypeCategory::Integer, kind);
|
2020-07-16 07:02:49 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-03 00:56:46 +08:00
|
|
|
// Report error resolving opr when there is a user-defined one available
|
2019-11-23 08:46:11 +08:00
|
|
|
void ArgumentAnalyzer::SayNoMatch(const std::string &opr, bool isAssignment) {
|
|
|
|
std::string type0{TypeAsFortran(0)};
|
2019-11-03 00:56:46 +08:00
|
|
|
auto rank0{actuals_[0]->Rank()};
|
|
|
|
if (actuals_.size() == 1) {
|
|
|
|
if (rank0 > 0) {
|
2019-11-23 08:46:11 +08:00
|
|
|
context_.Say("No intrinsic or user-defined %s matches "
|
2019-11-03 00:56:46 +08:00
|
|
|
"rank %d array of %s"_err_en_US,
|
2019-11-23 08:46:11 +08:00
|
|
|
opr, rank0, type0);
|
2019-11-03 00:56:46 +08:00
|
|
|
} else {
|
2019-11-23 08:46:11 +08:00
|
|
|
context_.Say("No intrinsic or user-defined %s matches "
|
2019-11-03 00:56:46 +08:00
|
|
|
"operand type %s"_err_en_US,
|
2019-11-23 08:46:11 +08:00
|
|
|
opr, type0);
|
2019-11-03 00:56:46 +08:00
|
|
|
}
|
|
|
|
} else {
|
2019-11-23 08:46:11 +08:00
|
|
|
std::string type1{TypeAsFortran(1)};
|
2019-11-03 00:56:46 +08:00
|
|
|
auto rank1{actuals_[1]->Rank()};
|
|
|
|
if (rank0 > 0 && rank1 > 0 && rank0 != rank1) {
|
2019-11-23 08:46:11 +08:00
|
|
|
context_.Say("No intrinsic or user-defined %s matches "
|
2019-11-03 00:56:46 +08:00
|
|
|
"rank %d array of %s and rank %d array of %s"_err_en_US,
|
2019-11-23 08:46:11 +08:00
|
|
|
opr, rank0, type0, rank1, type1);
|
|
|
|
} else if (isAssignment && rank0 != rank1) {
|
|
|
|
if (rank0 == 0) {
|
|
|
|
context_.Say("No intrinsic or user-defined %s matches "
|
|
|
|
"scalar %s and rank %d array of %s"_err_en_US,
|
|
|
|
opr, type0, rank1, type1);
|
|
|
|
} else {
|
|
|
|
context_.Say("No intrinsic or user-defined %s matches "
|
|
|
|
"rank %d array of %s and scalar %s"_err_en_US,
|
|
|
|
opr, rank0, type0, type1);
|
|
|
|
}
|
2019-11-03 00:56:46 +08:00
|
|
|
} else {
|
2019-11-23 08:46:11 +08:00
|
|
|
context_.Say("No intrinsic or user-defined %s matches "
|
2019-11-03 00:56:46 +08:00
|
|
|
"operand types %s and %s"_err_en_US,
|
2019-11-23 08:46:11 +08:00
|
|
|
opr, type0, type1);
|
2019-11-03 00:56:46 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string ArgumentAnalyzer::TypeAsFortran(std::size_t i) {
|
2021-01-16 03:52:10 +08:00
|
|
|
if (i >= actuals_.size() || !actuals_[i]) {
|
|
|
|
return "missing argument";
|
|
|
|
} else if (std::optional<DynamicType> type{GetType(i)}) {
|
2022-03-24 07:02:59 +08:00
|
|
|
return type->IsAssumedType() ? "TYPE(*)"s
|
|
|
|
: type->IsUnlimitedPolymorphic() ? "CLASS(*)"s
|
|
|
|
: type->IsPolymorphic() ? "CLASS("s + type->AsFortran() + ')'
|
|
|
|
: type->category() == TypeCategory::Derived
|
2019-11-03 00:56:46 +08:00
|
|
|
? "TYPE("s + type->AsFortran() + ')'
|
2020-07-02 07:51:44 +08:00
|
|
|
: type->category() == TypeCategory::Character
|
2020-06-19 08:17:04 +08:00
|
|
|
? "CHARACTER(KIND="s + std::to_string(type->kind()) + ')'
|
|
|
|
: ToUpperCase(type->AsFortran());
|
2019-11-03 00:56:46 +08:00
|
|
|
} else {
|
|
|
|
return "untyped";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-16 03:52:10 +08:00
|
|
|
bool ArgumentAnalyzer::AnyUntypedOrMissingOperand() {
|
2019-11-03 00:56:46 +08:00
|
|
|
for (const auto &actual : actuals_) {
|
2021-10-15 01:32:59 +08:00
|
|
|
if (!actual ||
|
|
|
|
(!actual->GetType() && !IsBareNullPointer(actual->UnwrapExpr()))) {
|
2019-11-03 00:56:46 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2020-03-28 05:17:25 +08:00
|
|
|
} // namespace Fortran::evaluate
|
2019-10-23 00:31:33 +08:00
|
|
|
|
2018-09-01 04:28:21 +08:00
|
|
|
namespace Fortran::semantics {
|
2019-03-06 08:52:50 +08:00
|
|
|
evaluate::Expr<evaluate::SubscriptInteger> AnalyzeKindSelector(
|
|
|
|
SemanticsContext &context, common::TypeCategory category,
|
|
|
|
const std::optional<parser::KindSelector> &selector) {
|
2019-03-08 06:46:31 +08:00
|
|
|
evaluate::ExpressionAnalyzer analyzer{context};
|
2019-12-12 07:06:24 +08:00
|
|
|
auto restorer{
|
2019-08-21 20:33:03 +08:00
|
|
|
analyzer.GetContextualMessages().SetLocation(context.location().value())};
|
2019-03-08 06:46:31 +08:00
|
|
|
return analyzer.AnalyzeKindSelector(category, selector);
|
2019-03-06 08:52:50 +08:00
|
|
|
}
|
2019-04-13 02:43:03 +08:00
|
|
|
|
2019-09-05 03:45:08 +08:00
|
|
|
ExprChecker::ExprChecker(SemanticsContext &context) : context_{context} {}
|
|
|
|
|
2020-06-19 08:17:04 +08:00
|
|
|
bool ExprChecker::Pre(const parser::DataImpliedDo &ido) {
|
|
|
|
parser::Walk(std::get<parser::DataImpliedDo::Bounds>(ido.t), *this);
|
|
|
|
const auto &bounds{std::get<parser::DataImpliedDo::Bounds>(ido.t)};
|
|
|
|
auto name{bounds.name.thing.thing};
|
|
|
|
int kind{evaluate::ResultType<evaluate::ImpliedDoIndex>::kind};
|
|
|
|
if (const auto dynamicType{evaluate::DynamicType::From(*name.symbol)}) {
|
|
|
|
if (dynamicType->category() == TypeCategory::Integer) {
|
|
|
|
kind = dynamicType->kind();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
exprAnalyzer_.AddImpliedDo(name.source, kind);
|
|
|
|
parser::Walk(std::get<std::list<parser::DataIDoObject>>(ido.t), *this);
|
|
|
|
exprAnalyzer_.RemoveImpliedDo(name.source);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-04-12 08:22:16 +08:00
|
|
|
bool ExprChecker::Walk(const parser::Program &program) {
|
|
|
|
parser::Walk(program, *this);
|
|
|
|
return !context_.AnyFatalError();
|
2019-03-09 04:55:57 +08:00
|
|
|
}
|
2020-03-28 05:17:25 +08:00
|
|
|
} // namespace Fortran::semantics
|