diff --git a/flang/lib/Semantics/expression.cpp b/flang/lib/Semantics/expression.cpp --- a/flang/lib/Semantics/expression.cpp +++ b/flang/lib/Semantics/expression.cpp @@ -123,6 +123,7 @@ } void Analyze(const parser::Variable &); void Analyze(const parser::ActualArgSpec &, bool isSubroutine); + void ConvertBOZ(std::size_t i, std::optional otherType); bool IsIntrinsicRelational(RelationalOperator) const; bool IsIntrinsicLogical() const; @@ -141,6 +142,7 @@ // Find and return a user-defined assignment std::optional TryDefinedAssignment(); std::optional GetDefinedAssignmentProc(); + std::optional GetType(std::size_t) const; void Dump(llvm::raw_ostream &); private: @@ -153,7 +155,6 @@ void AddAssignmentConversion( const DynamicType &lhsType, const DynamicType &rhsType); bool OkLogicalIntegerAssignment(TypeCategory lhs, TypeCategory rhs); - std::optional GetType(std::size_t) const; int GetRank(std::size_t) const; bool IsBOZLiteral(std::size_t i) const { return std::holds_alternative(GetExpr(i).u); @@ -2337,12 +2338,16 @@ analyzer.Analyze(std::get<1>(x.t)); if (analyzer.fatalErrors()) { return std::nullopt; - } else if (analyzer.IsIntrinsicRelational(opr)) { - return AsMaybeExpr(Relate(context.GetContextualMessages(), opr, - 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); + analyzer.ConvertBOZ(0, analyzer.GetType(1)); + analyzer.ConvertBOZ(1, analyzer.GetType(0)); + if (analyzer.IsIntrinsicRelational(opr)) { + return AsMaybeExpr(Relate(context.GetContextualMessages(), opr, + 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); + } } } @@ -3014,6 +3019,29 @@ return i < actuals_.size() ? actuals_[i].value().Rank() : 0; } +// If the argument at index i is a BOZ literal, convert its type to match the +// otherType. It it's REAL convert to REAL, otherwise convert to INTEGER. +// Note that IBM supports comparing BOZ literals to CHARACTER operands. That +// is not currently supported. +void ArgumentAnalyzer::ConvertBOZ( + std::size_t i, std::optional otherType) { + if (IsBOZLiteral(i)) { + Expr &&argExpr{MoveExpr(i)}; + auto *boz{std::get_if(&argExpr.u)}; + if (otherType && otherType->category() == TypeCategory::Real) { + MaybeExpr realExpr{ConvertToKind( + context_.context().GetDefaultKind(TypeCategory::Real), + std::move(*boz))}; + actuals_[i] = std::move(*realExpr); + } else { + MaybeExpr intExpr{ConvertToKind( + context_.context().GetDefaultKind(TypeCategory::Integer), + std::move(*boz))}; + actuals_[i] = std::move(*intExpr); + } + } +} + // Report error resolving opr when there is a user-defined one available void ArgumentAnalyzer::SayNoMatch(const std::string &opr, bool isAssignment) { std::string type0{TypeAsFortran(0)}; diff --git a/flang/test/Semantics/resolve63.f90 b/flang/test/Semantics/resolve63.f90 --- a/flang/test/Semantics/resolve63.f90 +++ b/flang/test/Semantics/resolve63.f90 @@ -44,12 +44,32 @@ type(t) :: x, y real :: r logical :: l + integer :: iVar + complex :: cvar + character :: charVar contains subroutine test_relational() l = x == y !OK l = x .eq. y !OK + l = x .eq. y !OK + l = iVar == z'fe' !OK + l = z'fe' == iVar !OK + l = r == z'fe' !OK + l = z'fe' == r !OK + l = cVar == z'fe' !OK + l = z'fe' == cVar !OK + !ERROR: No intrinsic or user-defined OPERATOR(==) matches operand types CHARACTER(KIND=1) and INTEGER(4) + l = charVar == z'fe' + !ERROR: No intrinsic or user-defined OPERATOR(==) matches operand types INTEGER(4) and CHARACTER(KIND=1) + l = z'fe' == charVar + !ERROR: No intrinsic or user-defined OPERATOR(==) matches operand types LOGICAL(4) and INTEGER(4) + l = l == z'fe' !OK + !ERROR: No intrinsic or user-defined OPERATOR(==) matches operand types INTEGER(4) and LOGICAL(4) + l = z'fe' == l !OK !ERROR: No intrinsic or user-defined OPERATOR(==) matches operand types TYPE(t) and REAL(4) l = x == r + + lVar = z'a' == b'1010' !OK end subroutine test_numeric() l = x + r !OK