[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:
Tim Keith
2019-11-22 16:46:11 -08:00
parent ddb4f259f6
commit 18f3a2beef
11 changed files with 285 additions and 55 deletions

View File

@@ -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 {

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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();

View File

@@ -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);

View File

@@ -102,6 +102,7 @@ set(ERROR_TESTS
resolve63.f90
resolve64.f90
resolve65.f90
resolve66.f90
stop01.f90
structconst01.f90
structconst02.f90

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View 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