mirror of
https://github.com/intel/llvm.git
synced 2026-01-22 23:49:22 +08:00
[flang] Analyze intrinsic and user-defined assignments
Change expression analysis to do assignment statements as it currently does call statements. Check there for defined assignment and set `typedAssignment` in the `AssignmentStmt` node to contain the analyzed assignment, either intrinsic or user-defined. When `var = expr` is implemented by subroutine `sub`, the analyzed assignment contains a procedure reference to `sub(var, (expr))`. Add `IsDefinedAssignment` to decide based on types and ranks of lhs and rhs whether is can be a defined assignment. The result is tri-state because when they are both the same derived type it can be either intrinsic or defined. Use this where a similar decision is made in `check-declarations.cc`. Change "Procedure referenced in PURE subprogram" error message to contain the name of the procedure. If the reference is from a defined assignment that name won't appear on the highlighted source line. Original-commit: flang-compiler/f18@5c87071210 Reviewed-on: https://github.com/flang-compiler/f18/pull/841
This commit is contained in:
@@ -450,18 +450,11 @@ void CheckHelper::SayNotDistinguishable(const SourceName &name,
|
||||
evaluate::AttachDeclaration(msg, proc2);
|
||||
}
|
||||
|
||||
static bool ConflictsWithIntrinsicAssignment(
|
||||
const DummyDataObject &arg0, const DummyDataObject &arg1) {
|
||||
auto cat0{arg0.type.type().category()};
|
||||
auto cat1{arg1.type.type().category()};
|
||||
int rank0{arg0.type.Rank()};
|
||||
int rank1{arg1.type.Rank()};
|
||||
if (cat0 == TypeCategory::Derived || (rank1 > 0 && rank0 != rank1)) {
|
||||
return false;
|
||||
} else {
|
||||
return cat0 == cat1 ||
|
||||
(IsNumericTypeCategory(cat0) && IsNumericTypeCategory(cat1));
|
||||
}
|
||||
static bool ConflictsWithIntrinsicAssignment(const Procedure &proc) {
|
||||
auto lhs{std::get<DummyDataObject>(proc.dummyArguments[0].u).type};
|
||||
auto rhs{std::get<DummyDataObject>(proc.dummyArguments[1].u).type};
|
||||
return Tristate::No ==
|
||||
IsDefinedAssignment(lhs.type(), lhs.Rank(), rhs.type(), rhs.Rank());
|
||||
}
|
||||
|
||||
// Check if this procedure can be used for defined assignment (see 15.4.3.4.3).
|
||||
@@ -476,9 +469,7 @@ bool CheckHelper::CheckDefinedAssignment(
|
||||
} else if (!CheckDefinedAssignmentArg(specific, proc.dummyArguments[0], 0) |
|
||||
!CheckDefinedAssignmentArg(specific, proc.dummyArguments[1], 1)) {
|
||||
return false; // error was reported
|
||||
} else if (ConflictsWithIntrinsicAssignment(
|
||||
std::get<DummyDataObject>(proc.dummyArguments[0].u),
|
||||
std::get<DummyDataObject>(proc.dummyArguments[1].u))) {
|
||||
} else if (ConflictsWithIntrinsicAssignment(proc)) {
|
||||
msg = "Defined assignment subroutine '%s' conflicts with"
|
||||
" intrinsic assignment"_err_en_US;
|
||||
} else {
|
||||
|
||||
@@ -147,9 +147,12 @@ public:
|
||||
CHECK(!fatalErrors_);
|
||||
return std::move(actuals_);
|
||||
}
|
||||
Expr<SomeType> GetAsExpr(std::size_t i) const {
|
||||
const Expr<SomeType> &GetExpr(std::size_t i) const {
|
||||
return DEREF(actuals_.at(i).value().UnwrapExpr());
|
||||
}
|
||||
Expr<SomeType> &&MoveExpr(std::size_t i) {
|
||||
return std::move(DEREF(actuals_.at(i).value().UnwrapExpr()));
|
||||
}
|
||||
void Analyze(const common::Indirection<parser::Expr> &x) {
|
||||
Analyze(x.value());
|
||||
}
|
||||
@@ -157,6 +160,7 @@ public:
|
||||
actuals_.emplace_back(AnalyzeExpr(x));
|
||||
fatalErrors_ |= !actuals_.back();
|
||||
}
|
||||
void Analyze(const parser::Variable &);
|
||||
void Analyze(const parser::ActualArgSpec &, bool isSubroutine);
|
||||
|
||||
bool IsIntrinsicRelational(RelationalOperator) const;
|
||||
@@ -172,18 +176,21 @@ public:
|
||||
return TryDefinedOp(
|
||||
context_.context().languageFeatures().GetNames(opr), std::move(msg));
|
||||
}
|
||||
// Find and return a user-defined assignment
|
||||
std::optional<ProcedureRef> TryDefinedAssignment();
|
||||
std::optional<ProcedureRef> GetDefinedAssignmentProc();
|
||||
|
||||
private:
|
||||
MaybeExpr TryDefinedOp(
|
||||
std::vector<const char *>, parser::MessageFixedText &&);
|
||||
std::optional<ActualArgument> AnalyzeExpr(const parser::Expr &);
|
||||
bool AreConformable() const;
|
||||
const Symbol *FindDefinedOp(const char *) const;
|
||||
Symbol *FindDefinedOp(const char *) const;
|
||||
std::optional<DynamicType> GetType(std::size_t) const;
|
||||
bool IsBOZLiteral(std::size_t i) const {
|
||||
return std::holds_alternative<BOZLiteralConstant>(GetAsExpr(i).u);
|
||||
return std::holds_alternative<BOZLiteralConstant>(GetExpr(i).u);
|
||||
}
|
||||
void SayNoMatch(const char *);
|
||||
void SayNoMatch(const std::string &, bool isAssignment = false);
|
||||
std::string TypeAsFortran(std::size_t);
|
||||
bool AnyUntypedOperand();
|
||||
|
||||
@@ -1785,6 +1792,18 @@ MaybeExpr ExpressionAnalyzer::AnalyzeCall(
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void ExpressionAnalyzer::Analyze(const parser::AssignmentStmt &x) {
|
||||
ArgumentAnalyzer analyzer{*this};
|
||||
analyzer.Analyze(std::get<parser::Variable>(x.t));
|
||||
analyzer.Analyze(std::get<parser::Expr>(x.t));
|
||||
if (!analyzer.fatalErrors()) {
|
||||
std::optional<ProcedureRef> procRef{analyzer.TryDefinedAssignment()};
|
||||
x.typedAssignment.reset(new GenericAssignmentWrapper{procRef
|
||||
? Assignment{std::move(*procRef)}
|
||||
: Assignment{analyzer.MoveExpr(0), analyzer.MoveExpr(1)}});
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsExternalCalledImplicitly(
|
||||
parser::CharBlock callSite, const ProcedureDesignator &proc) {
|
||||
if (const auto *symbol{proc.GetSymbol()}) {
|
||||
@@ -1815,8 +1834,8 @@ std::optional<characteristics::Procedure> ExpressionAnalyzer::CheckCall(
|
||||
pure{semantics::FindPureProcedureContaining(
|
||||
context_.FindScope(callSite))}) {
|
||||
Say(callSite,
|
||||
"Procedure referenced in PURE subprogram '%s' must be PURE too"_err_en_US,
|
||||
DEREF(pure->symbol()).name());
|
||||
"Procedure '%s' referenced in PURE subprogram '%s' must be PURE too"_err_en_US,
|
||||
DEREF(proc.GetSymbol()).name(), DEREF(pure->symbol()).name());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1848,9 +1867,9 @@ static MaybeExpr NumericUnaryHelper(ExpressionAnalyzer &context,
|
||||
return std::nullopt;
|
||||
} else if (analyzer.IsIntrinsicNumeric(opr)) {
|
||||
if (opr == NumericOperator::Add) {
|
||||
return analyzer.GetAsExpr(0);
|
||||
return analyzer.MoveExpr(0);
|
||||
} else {
|
||||
return Negation(context.GetContextualMessages(), analyzer.GetAsExpr(0));
|
||||
return Negation(context.GetContextualMessages(), analyzer.MoveExpr(0));
|
||||
}
|
||||
} else {
|
||||
return analyzer.TryDefinedOp(AsFortran(opr),
|
||||
@@ -1873,7 +1892,7 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::NOT &x) {
|
||||
return std::nullopt;
|
||||
} else if (analyzer.IsIntrinsicLogical()) {
|
||||
return AsGenericExpr(
|
||||
LogicalNegation(std::get<Expr<SomeLogical>>(analyzer.GetAsExpr(0).u)));
|
||||
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);
|
||||
@@ -1923,7 +1942,7 @@ MaybeExpr NumericBinaryHelper(ExpressionAnalyzer &context, NumericOperator opr,
|
||||
return std::nullopt;
|
||||
} else if (analyzer.IsIntrinsicNumeric(opr)) {
|
||||
return NumericOperation<OPR>(context.GetContextualMessages(),
|
||||
analyzer.GetAsExpr(0), analyzer.GetAsExpr(1),
|
||||
analyzer.MoveExpr(0), analyzer.MoveExpr(1),
|
||||
context.GetDefaultKind(TypeCategory::Real));
|
||||
} else {
|
||||
return analyzer.TryDefinedOp(AsFortran(opr),
|
||||
@@ -1978,8 +1997,8 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::Concat &x) {
|
||||
DIE("different types for intrinsic concat");
|
||||
}
|
||||
},
|
||||
std::move(std::get<Expr<SomeCharacter>>(analyzer.GetAsExpr(0).u).u),
|
||||
std::move(std::get<Expr<SomeCharacter>>(analyzer.GetAsExpr(1).u).u));
|
||||
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);
|
||||
@@ -2009,7 +2028,7 @@ MaybeExpr RelationHelper(ExpressionAnalyzer &context, RelationalOperator opr,
|
||||
return std::nullopt;
|
||||
} else if (analyzer.IsIntrinsicRelational(opr)) {
|
||||
return AsMaybeExpr(Relate(context.GetContextualMessages(), opr,
|
||||
analyzer.GetAsExpr(0), analyzer.GetAsExpr(1)));
|
||||
analyzer.MoveExpr(0), analyzer.MoveExpr(1)));
|
||||
} else {
|
||||
return analyzer.TryDefinedOp(opr,
|
||||
"Operands of %s must have comparable types; have %s and %s"_err_en_US);
|
||||
@@ -2049,8 +2068,8 @@ MaybeExpr LogicalBinaryHelper(ExpressionAnalyzer &context, LogicalOperator opr,
|
||||
return std::nullopt;
|
||||
} else if (analyzer.IsIntrinsicLogical()) {
|
||||
return AsGenericExpr(BinaryLogicalOperation(opr,
|
||||
std::get<Expr<SomeLogical>>(analyzer.GetAsExpr(0).u),
|
||||
std::get<Expr<SomeLogical>>(analyzer.GetAsExpr(1).u)));
|
||||
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);
|
||||
@@ -2396,6 +2415,15 @@ MaybeExpr ExpressionAnalyzer::MakeFunctionRef(
|
||||
}
|
||||
}
|
||||
|
||||
void ArgumentAnalyzer::Analyze(const parser::Variable &x) {
|
||||
source_.ExtendToCover(x.GetSource());
|
||||
if (MaybeExpr expr{context_.Analyze(x)}) {
|
||||
actuals_.emplace_back(std::move(*expr));
|
||||
} else {
|
||||
fatalErrors_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void ArgumentAnalyzer::Analyze(
|
||||
const parser::ActualArgSpec &arg, bool isSubroutine) {
|
||||
// TODO: C1002: Allow a whole assumed-size array to appear if the dummy
|
||||
@@ -2491,7 +2519,7 @@ bool ArgumentAnalyzer::IsIntrinsicConcat() const {
|
||||
|
||||
MaybeExpr ArgumentAnalyzer::TryDefinedOp(
|
||||
const char *opr, parser::MessageFixedText &&error) {
|
||||
const Symbol *symbol{AnyUntypedOperand() ? nullptr : FindDefinedOp(opr)};
|
||||
Symbol *symbol{AnyUntypedOperand() ? nullptr : FindDefinedOp(opr)};
|
||||
if (!symbol) {
|
||||
if (actuals_.size() == 1 || AreConformable()) {
|
||||
context_.Say(std::move(error), ToUpperCase(opr), TypeAsFortran(0),
|
||||
@@ -2504,11 +2532,11 @@ MaybeExpr ArgumentAnalyzer::TryDefinedOp(
|
||||
return std::nullopt;
|
||||
}
|
||||
parser::Messages messages;
|
||||
parser::Name name{source_, const_cast<Symbol *>(symbol)};
|
||||
parser::Name name{source_, symbol};
|
||||
if (auto result{context_.AnalyzeDefinedOp(messages, name, GetActuals())}) {
|
||||
return result;
|
||||
} else {
|
||||
SayNoMatch(opr);
|
||||
SayNoMatch("OPERATOR(" + ToUpperCase(opr) + ')');
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
@@ -2523,6 +2551,43 @@ MaybeExpr ArgumentAnalyzer::TryDefinedOp(
|
||||
return TryDefinedOp(oprs[0], std::move(error));
|
||||
}
|
||||
|
||||
std::optional<ProcedureRef> ArgumentAnalyzer::TryDefinedAssignment() {
|
||||
using semantics::Tristate;
|
||||
const Expr<SomeType> &lhs{GetExpr(0)};
|
||||
const Expr<SomeType> &rhs{GetExpr(1)};
|
||||
Tristate isDefined{semantics::IsDefinedAssignment(
|
||||
lhs.GetType(), lhs.Rank(), rhs.GetType(), rhs.Rank())};
|
||||
if (isDefined == Tristate::No) {
|
||||
return std::nullopt; // user-defined assignment not allowed for these args
|
||||
}
|
||||
auto restorer{context_.GetContextualMessages().SetLocation(source_)};
|
||||
auto procRef{GetDefinedAssignmentProc()};
|
||||
if (!procRef) {
|
||||
if (isDefined == Tristate::Yes) {
|
||||
SayNoMatch("ASSIGNMENT(=)", true);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
context_.CheckCall(source_, procRef->proc(), procRef->arguments());
|
||||
return std::move(*procRef);
|
||||
}
|
||||
|
||||
std::optional<ProcedureRef> ArgumentAnalyzer::GetDefinedAssignmentProc() {
|
||||
parser::Messages tmpMessages;
|
||||
auto restorer{context_.GetContextualMessages().SetMessages(tmpMessages)};
|
||||
const auto &scope{context_.context().FindScope(source_)};
|
||||
if (const Symbol *
|
||||
symbol{scope.FindSymbol(parser::CharBlock{"assignment(=)"s})}) {
|
||||
const Symbol *specific{context_.ResolveGeneric(*symbol, actuals_)};
|
||||
if (specific) {
|
||||
ProcedureDesignator designator{*specific};
|
||||
actuals_[1]->Parenthesize();
|
||||
return ProcedureRef{std::move(designator), std::move(actuals_)};
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<ActualArgument> ArgumentAnalyzer::AnalyzeExpr(
|
||||
const parser::Expr &expr) {
|
||||
source_.ExtendToCover(expr.source);
|
||||
@@ -2541,7 +2606,7 @@ bool ArgumentAnalyzer::AreConformable() const {
|
||||
return evaluate::AreConformable(*actuals_[0], *actuals_[1]);
|
||||
}
|
||||
|
||||
const Symbol *ArgumentAnalyzer::FindDefinedOp(const char *opr) const {
|
||||
Symbol *ArgumentAnalyzer::FindDefinedOp(const char *opr) const {
|
||||
const auto &scope{context_.context().FindScope(source_)};
|
||||
return scope.FindSymbol(parser::CharBlock{"operator("s + opr + ')'});
|
||||
}
|
||||
@@ -2551,28 +2616,40 @@ std::optional<DynamicType> ArgumentAnalyzer::GetType(std::size_t i) const {
|
||||
}
|
||||
|
||||
// Report error resolving opr when there is a user-defined one available
|
||||
void ArgumentAnalyzer::SayNoMatch(const char *opr) {
|
||||
void ArgumentAnalyzer::SayNoMatch(const std::string &opr, bool isAssignment) {
|
||||
std::string type0{TypeAsFortran(0)};
|
||||
auto rank0{actuals_[0]->Rank()};
|
||||
if (actuals_.size() == 1) {
|
||||
if (rank0 > 0) {
|
||||
context_.Say("No user-defined or intrinsic %s operator matches "
|
||||
context_.Say("No intrinsic or user-defined %s matches "
|
||||
"rank %d array of %s"_err_en_US,
|
||||
ToUpperCase(opr), rank0, TypeAsFortran(0));
|
||||
opr, rank0, type0);
|
||||
} else {
|
||||
context_.Say("No user-defined or intrinsic %s operator matches "
|
||||
context_.Say("No intrinsic or user-defined %s matches "
|
||||
"operand type %s"_err_en_US,
|
||||
ToUpperCase(opr), TypeAsFortran(0));
|
||||
opr, type0);
|
||||
}
|
||||
} else {
|
||||
std::string type1{TypeAsFortran(1)};
|
||||
auto rank1{actuals_[1]->Rank()};
|
||||
if (rank0 > 0 && rank1 > 0 && rank0 != rank1) {
|
||||
context_.Say("No user-defined or intrinsic %s operator matches "
|
||||
context_.Say("No intrinsic or user-defined %s matches "
|
||||
"rank %d array of %s and rank %d array of %s"_err_en_US,
|
||||
ToUpperCase(opr), rank0, TypeAsFortran(0), rank1, TypeAsFortran(1));
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
context_.Say("No user-defined or intrinsic %s operator matches "
|
||||
context_.Say("No intrinsic or user-defined %s matches "
|
||||
"operand types %s and %s"_err_en_US,
|
||||
ToUpperCase(opr), TypeAsFortran(0), TypeAsFortran(1));
|
||||
opr, type0, type1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2614,6 +2691,11 @@ void AnalyzeCallStmt(SemanticsContext &context, const parser::CallStmt &call) {
|
||||
evaluate::ExpressionAnalyzer{context}.Analyze(call);
|
||||
}
|
||||
|
||||
void AnalyzeAssignmentStmt(
|
||||
SemanticsContext &context, const parser::AssignmentStmt &stmt) {
|
||||
evaluate::ExpressionAnalyzer{context}.Analyze(stmt);
|
||||
}
|
||||
|
||||
ExprChecker::ExprChecker(SemanticsContext &context) : context_{context} {}
|
||||
|
||||
bool ExprChecker::Walk(const parser::Program &program) {
|
||||
|
||||
@@ -237,6 +237,7 @@ public:
|
||||
MaybeExpr Analyze(const parser::StructureComponent &);
|
||||
|
||||
void Analyze(const parser::CallStmt &);
|
||||
void Analyze(const parser::AssignmentStmt &);
|
||||
|
||||
protected:
|
||||
int IntegerTypeSpecKind(const parser::IntegerTypeSpec &);
|
||||
@@ -383,6 +384,7 @@ evaluate::Expr<evaluate::SubscriptInteger> AnalyzeKindSelector(
|
||||
const std::optional<parser::KindSelector> &);
|
||||
|
||||
void AnalyzeCallStmt(SemanticsContext &, const parser::CallStmt &);
|
||||
void AnalyzeAssignmentStmt(SemanticsContext &, const parser::AssignmentStmt &);
|
||||
|
||||
// Semantic analysis of all expressions in a parse tree, which becomes
|
||||
// decorated with typed representations for top-level expressions.
|
||||
@@ -406,6 +408,10 @@ public:
|
||||
AnalyzeCallStmt(context_, x);
|
||||
return false;
|
||||
}
|
||||
bool Pre(const parser::AssignmentStmt &x) {
|
||||
AnalyzeAssignmentStmt(context_, x);
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename A> bool Pre(const parser::Scalar<A> &x) {
|
||||
AnalyzeExpr(context_, x);
|
||||
|
||||
@@ -82,6 +82,27 @@ const Scope *FindPureProcedureContaining(const Scope &start) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Tristate IsDefinedAssignment(
|
||||
const std::optional<evaluate::DynamicType> &lhsType, int lhsRank,
|
||||
const std::optional<evaluate::DynamicType> &rhsType, int rhsRank) {
|
||||
if (!lhsType || !rhsType) {
|
||||
return Tristate::No; // error or rhs is untyped
|
||||
}
|
||||
TypeCategory lhsCat{lhsType->category()};
|
||||
TypeCategory rhsCat{rhsType->category()};
|
||||
if (rhsRank > 0 && lhsRank != rhsRank) {
|
||||
return Tristate::Yes;
|
||||
} else if (lhsCat != TypeCategory::Derived) {
|
||||
return ToTristate(lhsCat != rhsCat &&
|
||||
(!IsNumericTypeCategory(lhsCat) || !IsNumericTypeCategory(rhsCat)));
|
||||
} else if (rhsCat == TypeCategory::Derived &&
|
||||
lhsType->GetDerivedTypeSpec() == rhsType->GetDerivedTypeSpec()) {
|
||||
return Tristate::Maybe; // TYPE(t) = TYPE(t) can be defined or intrinsic
|
||||
} else {
|
||||
return Tristate::Yes;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsGenericDefinedOp(const Symbol &symbol) {
|
||||
const auto *details{symbol.GetUltimate().detailsIf<GenericDetails>()};
|
||||
return details && details->kind().IsDefinedOperator();
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "semantics.h"
|
||||
#include "../common/Fortran.h"
|
||||
#include "../evaluate/expression.h"
|
||||
#include "../evaluate/type.h"
|
||||
#include "../evaluate/variable.h"
|
||||
#include "../parser/message.h"
|
||||
#include "../parser/parse-tree.h"
|
||||
@@ -57,6 +58,14 @@ const DeclTypeSpec *FindParentTypeSpec(const Symbol &);
|
||||
// Return the Symbol of the variable of a construct association, if it exists
|
||||
const Symbol *GetAssociationRoot(const Symbol &);
|
||||
|
||||
enum class Tristate { No, Yes, Maybe };
|
||||
inline Tristate ToTristate(bool x) { return x ? Tristate::Yes : Tristate::No; }
|
||||
|
||||
// Is this a user-defined assignment? If both sides are the same derived type
|
||||
// (and the ranks are okay) the answer is Maybe.
|
||||
Tristate IsDefinedAssignment(
|
||||
const std::optional<evaluate::DynamicType> &lhsType, int lhsRank,
|
||||
const std::optional<evaluate::DynamicType> &rhsType, int rhsRank);
|
||||
bool IsGenericDefinedOp(const Symbol &);
|
||||
bool IsCommonBlockContaining(const Symbol &block, const Symbol &object);
|
||||
bool DoesScopeContain(const Scope *maybeAncestor, const Scope &maybeDescendent);
|
||||
|
||||
@@ -102,6 +102,7 @@ set(ERROR_TESTS
|
||||
resolve63.f90
|
||||
resolve64.f90
|
||||
resolve65.f90
|
||||
resolve66.f90
|
||||
stop01.f90
|
||||
structconst01.f90
|
||||
structconst02.f90
|
||||
|
||||
@@ -146,7 +146,7 @@ module m
|
||||
! C1594 is tested in call12.f90.
|
||||
pure subroutine s10 ! C1595
|
||||
integer :: n
|
||||
!ERROR: Procedure referenced in PURE subprogram 's10' must be PURE too
|
||||
!ERROR: Procedure 'notpure' referenced in PURE subprogram 's10' must be PURE too
|
||||
n = notpure(1)
|
||||
end subroutine
|
||||
pure subroutine s11(to) ! C1596
|
||||
|
||||
@@ -40,7 +40,8 @@ subroutine s2
|
||||
real :: x, y(10), z
|
||||
logical :: a
|
||||
a = f(1.0)
|
||||
a = f(y) !TODO: this should resolve to f2 -- should get error here
|
||||
!ERROR: No intrinsic or user-defined ASSIGNMENT(=) matches operand types LOGICAL(4) and REAL(4)
|
||||
a = f(y)
|
||||
end
|
||||
|
||||
! Resolve named operator
|
||||
|
||||
@@ -61,36 +61,36 @@ contains
|
||||
subroutine test_relational()
|
||||
l = x == y !OK
|
||||
l = x .eq. y !OK
|
||||
!ERROR: No user-defined or intrinsic == operator matches operand types TYPE(t) and REAL(4)
|
||||
!ERROR: No intrinsic or user-defined OPERATOR(==) matches operand types TYPE(t) and REAL(4)
|
||||
l = x == r
|
||||
end
|
||||
subroutine test_numeric()
|
||||
l = x + r !OK
|
||||
!ERROR: No user-defined or intrinsic + operator matches operand types REAL(4) and TYPE(t)
|
||||
!ERROR: No intrinsic or user-defined OPERATOR(+) matches operand types REAL(4) and TYPE(t)
|
||||
l = r + x
|
||||
end
|
||||
subroutine test_logical()
|
||||
l = x .and. r !OK
|
||||
!ERROR: No user-defined or intrinsic .AND. operator matches operand types REAL(4) and TYPE(t)
|
||||
!ERROR: No intrinsic or user-defined OPERATOR(.AND.) matches operand types REAL(4) and TYPE(t)
|
||||
l = r .and. x
|
||||
end
|
||||
subroutine test_unary()
|
||||
l = +x !OK
|
||||
!ERROR: No user-defined or intrinsic + operator matches operand type LOGICAL(4)
|
||||
!ERROR: No intrinsic or user-defined OPERATOR(+) matches operand type LOGICAL(4)
|
||||
l = +l
|
||||
l = .not. r !OK
|
||||
!ERROR: No user-defined or intrinsic .NOT. operator matches operand type TYPE(t)
|
||||
!ERROR: No intrinsic or user-defined OPERATOR(.NOT.) matches operand type TYPE(t)
|
||||
l = .not. x
|
||||
end
|
||||
subroutine test_concat()
|
||||
l = x // y !OK
|
||||
!ERROR: No user-defined or intrinsic // operator matches operand types TYPE(t) and REAL(4)
|
||||
!ERROR: No intrinsic or user-defined OPERATOR(//) matches operand types TYPE(t) and REAL(4)
|
||||
l = x // r
|
||||
end
|
||||
subroutine test_conformability(x, y)
|
||||
real :: x(10), y(10,10)
|
||||
l = x + y !OK
|
||||
!ERROR: No user-defined or intrinsic + operator matches rank 2 array of REAL(4) and rank 1 array of REAL(4)
|
||||
!ERROR: No intrinsic or user-defined OPERATOR(+) matches rank 2 array of REAL(4) and rank 1 array of REAL(4)
|
||||
l = y + x
|
||||
end
|
||||
end
|
||||
@@ -201,7 +201,7 @@ contains
|
||||
subroutine s1(x, y, z)
|
||||
logical :: x
|
||||
complex :: y, z
|
||||
!ERROR: No user-defined or intrinsic .AND. operator matches operand types COMPLEX(4) and COMPLEX(4)
|
||||
!ERROR: No intrinsic or user-defined OPERATOR(.AND.) matches operand types COMPLEX(4) and COMPLEX(4)
|
||||
x = y .and. z
|
||||
!ERROR: No specific procedure of generic operator '.a.' matches the actual arguments
|
||||
x = y .a. z
|
||||
|
||||
@@ -51,9 +51,9 @@ contains
|
||||
subroutine s1(x, y, z)
|
||||
logical :: x
|
||||
complex :: y, z
|
||||
!ERROR: No user-defined or intrinsic .A. operator matches operand types COMPLEX(4) and COMPLEX(4)
|
||||
!ERROR: No intrinsic or user-defined OPERATOR(.A.) matches operand types COMPLEX(4) and COMPLEX(4)
|
||||
x = y .and. z
|
||||
!ERROR: No user-defined or intrinsic .A. operator matches operand types COMPLEX(4) and COMPLEX(4)
|
||||
!ERROR: No intrinsic or user-defined OPERATOR(.A.) matches operand types COMPLEX(4) and COMPLEX(4)
|
||||
x = y .a. z
|
||||
end
|
||||
end
|
||||
|
||||
119
flang/test/semantics/resolve66.f90
Normal file
119
flang/test/semantics/resolve66.f90
Normal file
@@ -0,0 +1,119 @@
|
||||
! Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
|
||||
!
|
||||
! Licensed under the Apache License, Version 2.0 (the "License");
|
||||
! you may not use this file except in compliance with the License.
|
||||
! You may obtain a copy of the License at
|
||||
!
|
||||
! http://www.apache.org/licenses/LICENSE-2.0
|
||||
!
|
||||
! Unless required by applicable law or agreed to in writing, software
|
||||
! distributed under the License is distributed on an "AS IS" BASIS,
|
||||
! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
! See the License for the specific language governing permissions and
|
||||
! limitations under the License.
|
||||
|
||||
! Test that user-defined assignment is used in the right places
|
||||
|
||||
module m1
|
||||
type t1
|
||||
end type
|
||||
type t2
|
||||
end type
|
||||
interface assignment(=)
|
||||
subroutine assign_il(x, y)
|
||||
integer, intent(out) :: x
|
||||
logical, intent(in) :: y
|
||||
end
|
||||
subroutine assign_li(x, y)
|
||||
logical, intent(out) :: x
|
||||
integer, intent(in) :: y
|
||||
end
|
||||
subroutine assign_tt(x, y)
|
||||
import t1
|
||||
type(t1), intent(out) :: x
|
||||
type(t1), intent(in) :: y
|
||||
end
|
||||
subroutine assign_tz(x, y)
|
||||
import t1
|
||||
type(t1), intent(out) :: x
|
||||
complex, intent(in) :: y
|
||||
end
|
||||
subroutine assign_01(x, y)
|
||||
real, intent(out) :: x
|
||||
real, intent(in) :: y(:)
|
||||
end
|
||||
end interface
|
||||
contains
|
||||
! These are all intrinsic assignments
|
||||
pure subroutine test1()
|
||||
type(t2) :: a, b, b5(5)
|
||||
logical :: l
|
||||
integer :: i, i5(5)
|
||||
a = b
|
||||
b5 = a
|
||||
l = .true.
|
||||
i = z'1234'
|
||||
i5 = 1.0
|
||||
end
|
||||
|
||||
! These have invalid type combinations
|
||||
subroutine test2()
|
||||
type(t1) :: a
|
||||
type(t2) :: b
|
||||
logical :: l, l5(5)
|
||||
complex :: z, z5(5), z55(5,5)
|
||||
!ERROR: No intrinsic or user-defined ASSIGNMENT(=) matches operand types TYPE(t1) and TYPE(t2)
|
||||
a = b
|
||||
!ERROR: No intrinsic or user-defined ASSIGNMENT(=) matches operand types REAL(4) and LOGICAL(4)
|
||||
r = l
|
||||
!ERROR: No intrinsic or user-defined ASSIGNMENT(=) matches operand types LOGICAL(4) and REAL(4)
|
||||
l = r
|
||||
!ERROR: No intrinsic or user-defined ASSIGNMENT(=) matches operand types TYPE(t1) and REAL(4)
|
||||
a = r
|
||||
!ERROR: No intrinsic or user-defined ASSIGNMENT(=) matches operand types TYPE(t2) and COMPLEX(4)
|
||||
b = z
|
||||
!ERROR: No intrinsic or user-defined ASSIGNMENT(=) matches scalar COMPLEX(4) and rank 1 array of COMPLEX(4)
|
||||
z = z5
|
||||
!ERROR: No intrinsic or user-defined ASSIGNMENT(=) matches rank 1 array of LOGICAL(4) and scalar COMPLEX(4)
|
||||
l5 = z
|
||||
!ERROR: No intrinsic or user-defined ASSIGNMENT(=) matches rank 1 array of COMPLEX(4) and rank 2 array of COMPLEX(4)
|
||||
z5 = z55
|
||||
end
|
||||
|
||||
! These should all be defined assignments. Because the subroutines
|
||||
! implementing them are not pure, they should all produce errors
|
||||
pure subroutine test3()
|
||||
type(t1) :: a, b
|
||||
integer :: i
|
||||
logical :: l
|
||||
complex :: z
|
||||
real :: r, r5(5)
|
||||
!ERROR: Procedure 'assign_tt' referenced in PURE subprogram 'test3' must be PURE too
|
||||
a = b
|
||||
!ERROR: Procedure 'assign_il' referenced in PURE subprogram 'test3' must be PURE too
|
||||
i = l
|
||||
!ERROR: Procedure 'assign_li' referenced in PURE subprogram 'test3' must be PURE too
|
||||
l = i
|
||||
!ERROR: Procedure 'assign_il' referenced in PURE subprogram 'test3' must be PURE too
|
||||
i = .true.
|
||||
!ERROR: Procedure 'assign_tz' referenced in PURE subprogram 'test3' must be PURE too
|
||||
a = z
|
||||
!ERROR: Procedure 'assign_01' referenced in PURE subprogram 'test3' must be PURE too
|
||||
r = r5
|
||||
end
|
||||
|
||||
! Like test3 but not in a pure subroutine so no errors.
|
||||
subroutine test4()
|
||||
type(t1) :: a, b
|
||||
integer :: i
|
||||
logical :: l
|
||||
complex :: z
|
||||
real :: r, r5(5)
|
||||
a = b
|
||||
i = l
|
||||
l = i
|
||||
i = .true.
|
||||
a = z
|
||||
r = r5
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user