diff --git a/flang/include/flang/Evaluate/tools.h b/flang/include/flang/Evaluate/tools.h --- a/flang/include/flang/Evaluate/tools.h +++ b/flang/include/flang/Evaluate/tools.h @@ -589,8 +589,9 @@ // Given two expressions of arbitrary type, try to combine them with a // relational operator (e.g., .LT.), possibly with data type conversion. -std::optional> Relate(parser::ContextualMessages &, - RelationalOperator, Expr &&, Expr &&); +std::optional> Relate(semantics::SemanticsContext &context, + parser::ContextualMessages &, RelationalOperator, Expr &&, + Expr &&); template Expr> LogicalNegation( @@ -864,7 +865,7 @@ parser::Message *AttachDeclaration(parser::Message *, const Symbol &); template parser::Message *SayWithDeclaration( - MESSAGES &messages, const Symbol &symbol, A &&... x) { + MESSAGES &messages, const Symbol &symbol, A &&...x) { return AttachDeclaration(messages.Say(std::forward(x)...), symbol); } diff --git a/flang/lib/Evaluate/tools.cpp b/flang/lib/Evaluate/tools.cpp --- a/flang/lib/Evaluate/tools.cpp +++ b/flang/lib/Evaluate/tools.cpp @@ -476,92 +476,109 @@ AsSameKindExprs(std::move(x), std::move(y))); } -std::optional> Relate(parser::ContextualMessages &messages, - RelationalOperator opr, Expr &&x, Expr &&y) { - return std::visit( - common::visitors{ - [=](Expr &&ix, - Expr &&iy) -> std::optional> { - return PromoteAndRelate(opr, std::move(ix), std::move(iy)); - }, - [=](Expr &&rx, - Expr &&ry) -> std::optional> { - return PromoteAndRelate(opr, std::move(rx), std::move(ry)); - }, - [&](Expr &&rx, Expr &&iy) { - return Relate(messages, opr, std::move(x), - AsGenericExpr(ConvertTo(rx, std::move(iy)))); - }, - [&](Expr &&ix, Expr &&ry) { - return Relate(messages, opr, - AsGenericExpr(ConvertTo(ry, std::move(ix))), std::move(y)); - }, - [&](Expr &&zx, - Expr &&zy) -> std::optional> { - if (opr != RelationalOperator::EQ && - opr != RelationalOperator::NE) { - messages.Say( - "COMPLEX data may be compared only for equality"_err_en_US); - } else { - auto rr{Relate(messages, opr, - AsGenericExpr(GetComplexPart(zx, false)), - AsGenericExpr(GetComplexPart(zy, false)))}; - auto ri{ - Relate(messages, opr, AsGenericExpr(GetComplexPart(zx, true)), - AsGenericExpr(GetComplexPart(zy, true)))}; - if (auto parts{ - common::AllPresent(std::move(rr), std::move(ri))}) { - // (a,b)==(c,d) -> (a==c) .AND. (b==d) - // (a,b)/=(c,d) -> (a/=c) .OR. (b/=d) - LogicalOperator combine{opr == RelationalOperator::EQ - ? LogicalOperator::And - : LogicalOperator::Or}; - return Expr{ - LogicalOperation{combine, - std::get<0>(std::move(*parts)), - std::get<1>(std::move(*parts))}}; +std::optional> Relate(semantics::SemanticsContext &context, + parser::ContextualMessages &messages, RelationalOperator opr, + Expr &&x, Expr &&y) { + // Convert any BOZ literal operands to integers + if (auto *boz{std::get_if(&x.u)}) { + return Relate(context, messages, opr, + std::move(Expr{ConvertToKind( + context.GetDefaultKind(TypeCategory::Integer), std::move(*boz))}), + std::move(y)); + } else if (auto *boz{std::get_if(&y.u)}) { + Expr yExpr{ConvertToKind( + context.GetDefaultKind(TypeCategory::Integer), std::move(*boz))}; + return Relate(context, messages, opr, std::move(x), + std::move(Expr{ConvertToKind( + context.GetDefaultKind(TypeCategory::Integer), std::move(*boz))})); + } else { + return std::visit( + common::visitors{ + [=](Expr &&ix, + Expr &&iy) -> std::optional> { + return PromoteAndRelate(opr, std::move(ix), std::move(iy)); + }, + [=](Expr &&rx, + Expr &&ry) -> std::optional> { + return PromoteAndRelate(opr, std::move(rx), std::move(ry)); + }, + [&](Expr &&rx, Expr &&iy) { + return Relate(context, messages, opr, std::move(x), + AsGenericExpr(ConvertTo(rx, std::move(iy)))); + }, + [&](Expr &&ix, Expr &&ry) { + return Relate(context, messages, opr, + AsGenericExpr(ConvertTo(ry, std::move(ix))), std::move(y)); + }, + [&](Expr &&zx, + Expr &&zy) -> std::optional> { + if (opr != RelationalOperator::EQ && + opr != RelationalOperator::NE) { + messages.Say( + "COMPLEX data may be compared only for equality"_err_en_US); + } else { + auto rr{Relate(context, messages, opr, + AsGenericExpr(GetComplexPart(zx, false)), + AsGenericExpr(GetComplexPart(zy, false)))}; + auto ri{Relate(context, messages, opr, + AsGenericExpr(GetComplexPart(zx, true)), + AsGenericExpr(GetComplexPart(zy, true)))}; + if (auto parts{ + common::AllPresent(std::move(rr), std::move(ri))}) { + // (a,b)==(c,d) -> (a==c) .AND. (b==d) + // (a,b)/=(c,d) -> (a/=c) .OR. (b/=d) + LogicalOperator combine{opr == RelationalOperator::EQ + ? LogicalOperator::And + : LogicalOperator::Or}; + return Expr{ + LogicalOperation{combine, + std::get<0>(std::move(*parts)), + std::get<1>(std::move(*parts))}}; + } } - } - return std::nullopt; - }, - [&](Expr &&zx, Expr &&iy) { - return Relate(messages, opr, std::move(x), - AsGenericExpr(ConvertTo(zx, std::move(iy)))); - }, - [&](Expr &&zx, Expr &&ry) { - return Relate(messages, opr, std::move(x), - AsGenericExpr(ConvertTo(zx, std::move(ry)))); - }, - [&](Expr &&ix, Expr &&zy) { - return Relate(messages, opr, - AsGenericExpr(ConvertTo(zy, std::move(ix))), std::move(y)); - }, - [&](Expr &&rx, Expr &&zy) { - return Relate(messages, opr, - AsGenericExpr(ConvertTo(zy, std::move(rx))), std::move(y)); - }, - [&](Expr &&cx, Expr &&cy) { - return std::visit( - [&](auto &&cxk, - auto &&cyk) -> std::optional> { - using Ty = ResultType; - if constexpr (std::is_same_v>) { - return PackageRelation(opr, std::move(cxk), std::move(cyk)); - } else { - messages.Say( - "CHARACTER operands do not have same KIND"_err_en_US); - return std::nullopt; - } - }, - std::move(cx.u), std::move(cy.u)); - }, - // Default case - [&](auto &&, auto &&) { - DIE("invalid types for relational operator"); - return std::optional>{}; - }, - }, - std::move(x.u), std::move(y.u)); + return std::nullopt; + }, + [&](Expr &&zx, Expr &&iy) { + return Relate(context, messages, opr, std::move(x), + AsGenericExpr(ConvertTo(zx, std::move(iy)))); + }, + [&](Expr &&zx, Expr &&ry) { + return Relate(context, messages, opr, std::move(x), + AsGenericExpr(ConvertTo(zx, std::move(ry)))); + }, + [&](Expr &&ix, Expr &&zy) { + return Relate(context, messages, opr, + AsGenericExpr(ConvertTo(zy, std::move(ix))), std::move(y)); + }, + [&](Expr &&rx, Expr &&zy) { + return Relate(context, messages, opr, + AsGenericExpr(ConvertTo(zy, std::move(rx))), std::move(y)); + }, + [&](Expr &&cx, Expr &&cy) { + return std::visit( + [&](auto &&cxk, + auto &&cyk) -> std::optional> { + using Ty = ResultType; + if constexpr (std::is_same_v>) { + return PackageRelation( + opr, std::move(cxk), std::move(cyk)); + } else { + messages.Say( + "CHARACTER operands do not have same KIND"_err_en_US); + return std::nullopt; + } + }, + std::move(cx.u), std::move(cy.u)); + }, + // Default case + [&](auto &&, auto &&) { + DIE("invalid types for relational operator"); + return std::optional>{}; + }, + }, + std::move(x.u), std::move(y.u)); + } } Expr BinaryLogicalOperation( @@ -597,8 +614,8 @@ switch (type.category()) { case TypeCategory::Integer: if (auto *boz{std::get_if(&x.u)}) { - // Extension to C7109: allow BOZ literals to appear in integer contexts - // when the type is unambiguous. + // Extension to C7109: allow BOZ literals to appear in integer + // contexts when the type is unambiguous. return Expr{ ConvertToKind(type.kind(), std::move(*boz))}; } 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 @@ -2338,8 +2338,9 @@ if (analyzer.fatalErrors()) { return std::nullopt; } else if (analyzer.IsIntrinsicRelational(opr)) { - return AsMaybeExpr(Relate(context.GetContextualMessages(), opr, - analyzer.MoveExpr(0), analyzer.MoveExpr(1))); + return AsMaybeExpr( + Relate(context.context(), 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); 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,10 +44,14 @@ type(t) :: x, y real :: r logical :: l + integer :: iVar 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 !ERROR: No intrinsic or user-defined OPERATOR(==) matches operand types TYPE(t) and REAL(4) l = x == r end