diff --git a/flang/lib/semantics/check-declarations.cc b/flang/lib/semantics/check-declarations.cc index 41ad4e88ee59..29a5c0c3e14a 100644 --- a/flang/lib/semantics/check-declarations.cc +++ b/flang/lib/semantics/check-declarations.cc @@ -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(proc.dummyArguments[0].u).type}; + auto rhs{std::get(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(proc.dummyArguments[0].u), - std::get(proc.dummyArguments[1].u))) { + } else if (ConflictsWithIntrinsicAssignment(proc)) { msg = "Defined assignment subroutine '%s' conflicts with" " intrinsic assignment"_err_en_US; } else { diff --git a/flang/lib/semantics/expression.cc b/flang/lib/semantics/expression.cc index 0a08912094db..f8a43019d74d 100644 --- a/flang/lib/semantics/expression.cc +++ b/flang/lib/semantics/expression.cc @@ -147,9 +147,12 @@ public: CHECK(!fatalErrors_); return std::move(actuals_); } - Expr GetAsExpr(std::size_t i) const { + const Expr &GetExpr(std::size_t i) const { return DEREF(actuals_.at(i).value().UnwrapExpr()); } + Expr &&MoveExpr(std::size_t i) { + return std::move(DEREF(actuals_.at(i).value().UnwrapExpr())); + } void Analyze(const common::Indirection &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 TryDefinedAssignment(); + std::optional GetDefinedAssignmentProc(); private: MaybeExpr TryDefinedOp( std::vector, parser::MessageFixedText &&); std::optional AnalyzeExpr(const parser::Expr &); bool AreConformable() const; - const Symbol *FindDefinedOp(const char *) const; + Symbol *FindDefinedOp(const char *) const; std::optional GetType(std::size_t) const; bool IsBOZLiteral(std::size_t i) const { - return std::holds_alternative(GetAsExpr(i).u); + return std::holds_alternative(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(x.t)); + analyzer.Analyze(std::get(x.t)); + if (!analyzer.fatalErrors()) { + std::optional 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 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>(analyzer.GetAsExpr(0).u))); + LogicalNegation(std::get>(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(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>(analyzer.GetAsExpr(0).u).u), - std::move(std::get>(analyzer.GetAsExpr(1).u).u)); + std::move(std::get>(analyzer.MoveExpr(0).u).u), + std::move(std::get>(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>(analyzer.GetAsExpr(0).u), - std::get>(analyzer.GetAsExpr(1).u))); + std::get>(analyzer.MoveExpr(0).u), + std::get>(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)}; + 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 ArgumentAnalyzer::TryDefinedAssignment() { + using semantics::Tristate; + const Expr &lhs{GetExpr(0)}; + const Expr &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 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 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 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) { diff --git a/flang/lib/semantics/expression.h b/flang/lib/semantics/expression.h index b112594e684a..0c108c352fff 100644 --- a/flang/lib/semantics/expression.h +++ b/flang/lib/semantics/expression.h @@ -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 AnalyzeKindSelector( const std::optional &); 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 bool Pre(const parser::Scalar &x) { AnalyzeExpr(context_, x); diff --git a/flang/lib/semantics/tools.cc b/flang/lib/semantics/tools.cc index 0868d7447b9f..eb2495c7252d 100644 --- a/flang/lib/semantics/tools.cc +++ b/flang/lib/semantics/tools.cc @@ -82,6 +82,27 @@ const Scope *FindPureProcedureContaining(const Scope &start) { return nullptr; } +Tristate IsDefinedAssignment( + const std::optional &lhsType, int lhsRank, + const std::optional &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()}; return details && details->kind().IsDefinedOperator(); diff --git a/flang/lib/semantics/tools.h b/flang/lib/semantics/tools.h index 7f38608e9a91..e9fb90e9927e 100644 --- a/flang/lib/semantics/tools.h +++ b/flang/lib/semantics/tools.h @@ -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 &lhsType, int lhsRank, + const std::optional &rhsType, int rhsRank); bool IsGenericDefinedOp(const Symbol &); bool IsCommonBlockContaining(const Symbol &block, const Symbol &object); bool DoesScopeContain(const Scope *maybeAncestor, const Scope &maybeDescendent); diff --git a/flang/test/semantics/CMakeLists.txt b/flang/test/semantics/CMakeLists.txt index 2db2eb139c63..741a9a611210 100644 --- a/flang/test/semantics/CMakeLists.txt +++ b/flang/test/semantics/CMakeLists.txt @@ -102,6 +102,7 @@ set(ERROR_TESTS resolve63.f90 resolve64.f90 resolve65.f90 + resolve66.f90 stop01.f90 structconst01.f90 structconst02.f90 diff --git a/flang/test/semantics/call10.f90 b/flang/test/semantics/call10.f90 index 40738934cb60..f15cd607bc3b 100644 --- a/flang/test/semantics/call10.f90 +++ b/flang/test/semantics/call10.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 diff --git a/flang/test/semantics/resolve62.f90 b/flang/test/semantics/resolve62.f90 index 89e6584eb807..ac23c42d75bf 100644 --- a/flang/test/semantics/resolve62.f90 +++ b/flang/test/semantics/resolve62.f90 @@ -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 diff --git a/flang/test/semantics/resolve63.f90 b/flang/test/semantics/resolve63.f90 index f1c56148d62b..4a32f1de6ae5 100644 --- a/flang/test/semantics/resolve63.f90 +++ b/flang/test/semantics/resolve63.f90 @@ -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 diff --git a/flang/test/semantics/resolve64.f90 b/flang/test/semantics/resolve64.f90 index 6e5ba7cb877f..5df1122cb368 100644 --- a/flang/test/semantics/resolve64.f90 +++ b/flang/test/semantics/resolve64.f90 @@ -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 diff --git a/flang/test/semantics/resolve66.f90 b/flang/test/semantics/resolve66.f90 new file mode 100644 index 000000000000..4c0a257ef856 --- /dev/null +++ b/flang/test/semantics/resolve66.f90 @@ -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