Skip to content

Commit a6181dc

Browse files
authored
[flang] Refine checks for NULL() in expressions (#163655)
Fix a false positive "NULL can't be an operand here" error message arising in a defined generic interface for an intrinsic operator (==) with multiple spellings. Fixes llvm/llvm-project#163255.
1 parent e0bffe1 commit a6181dc

File tree

3 files changed

+70
-28
lines changed

3 files changed

+70
-28
lines changed

flang/lib/Semantics/expression.cpp

Lines changed: 47 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -176,8 +176,8 @@ class ArgumentAnalyzer {
176176

177177
// Find and return a user-defined operator or report an error.
178178
// The provided message is used if there is no such operator.
179-
MaybeExpr TryDefinedOp(
180-
const char *, parser::MessageFixedText, bool isUserOp = false);
179+
MaybeExpr TryDefinedOp(const char *, parser::MessageFixedText,
180+
bool isUserOp = false, bool checkForNullPointer = true);
181181
template <typename E>
182182
MaybeExpr TryDefinedOp(E opr, parser::MessageFixedText msg) {
183183
return TryDefinedOp(
@@ -211,7 +211,8 @@ class ArgumentAnalyzer {
211211
void SayNoMatch(
212212
const std::string &, bool isAssignment = false, bool isAmbiguous = false);
213213
std::string TypeAsFortran(std::size_t);
214-
bool AnyUntypedOrMissingOperand() const;
214+
bool AnyUntypedOperand() const;
215+
bool AnyMissingOperand() const;
215216

216217
ExpressionAnalyzer &context_;
217218
ActualArguments actuals_;
@@ -3696,11 +3697,12 @@ std::optional<characteristics::Procedure> ExpressionAnalyzer::CheckCall(
36963697

36973698
MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::Parentheses &x) {
36983699
if (MaybeExpr operand{Analyze(x.v.value())}) {
3699-
if (const semantics::Symbol *symbol{GetLastSymbol(*operand)}) {
3700+
if (IsNullPointerOrAllocatable(&*operand)) {
3701+
Say("NULL() may not be parenthesized"_err_en_US);
3702+
} else if (const semantics::Symbol *symbol{GetLastSymbol(*operand)}) {
37003703
if (const semantics::Symbol *result{FindFunctionResult(*symbol)}) {
37013704
if (semantics::IsProcedurePointer(*result)) {
3702-
Say("A function reference that returns a procedure "
3703-
"pointer may not be parenthesized"_err_en_US); // C1003
3705+
Say("A function reference that returns a procedure pointer may not be parenthesized"_err_en_US); // C1003
37043706
}
37053707
}
37063708
}
@@ -3789,7 +3791,7 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::DefinedUnary &x) {
37893791
ArgumentAnalyzer analyzer{*this, name.source};
37903792
analyzer.Analyze(std::get<1>(x.t));
37913793
return analyzer.TryDefinedOp(name.source.ToString().c_str(),
3792-
"No operator %s defined for %s"_err_en_US, true);
3794+
"No operator %s defined for %s"_err_en_US, /*isUserOp=*/true);
37933795
}
37943796

37953797
// Binary (dyadic) operations
@@ -4181,15 +4183,23 @@ MaybeExpr ExpressionAnalyzer::IterativelyAnalyzeSubexpressions(
41814183
} while (!queue.empty());
41824184
// Analyze the collected subexpressions in bottom-up order.
41834185
// On an error, bail out and leave partial results in place.
4184-
MaybeExpr result;
4185-
for (auto riter{finish.rbegin()}; riter != finish.rend(); ++riter) {
4186-
const parser::Expr &expr{**riter};
4187-
result = ExprOrVariable(expr, expr.source);
4188-
if (!result) {
4189-
return result;
4186+
if (finish.size() == 1) {
4187+
const parser::Expr &expr{DEREF(finish.front())};
4188+
return ExprOrVariable(expr, expr.source);
4189+
} else {
4190+
// NULL() operand catching is deferred to operation analysis so
4191+
// that they can be accepted by defined operators.
4192+
auto restorer{AllowNullPointer()};
4193+
MaybeExpr result;
4194+
for (auto riter{finish.rbegin()}; riter != finish.rend(); ++riter) {
4195+
const parser::Expr &expr{**riter};
4196+
result = ExprOrVariable(expr, expr.source);
4197+
if (!result) {
4198+
return result;
4199+
}
41904200
}
4201+
return result; // last value was from analysis of "top"
41914202
}
4192-
return result; // last value was from analysis of "top"
41934203
}
41944204

41954205
MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr &expr) {
@@ -4686,7 +4696,7 @@ bool ArgumentAnalyzer::AnyCUDADeviceData() const {
46864696
// attribute.
46874697
bool ArgumentAnalyzer::HasDeviceDefinedIntrinsicOpOverride(
46884698
const char *opr) const {
4689-
if (AnyCUDADeviceData() && !AnyUntypedOrMissingOperand()) {
4699+
if (AnyCUDADeviceData() && !AnyUntypedOperand() && !AnyMissingOperand()) {
46904700
std::string oprNameString{"operator("s + opr + ')'};
46914701
parser::CharBlock oprName{oprNameString};
46924702
parser::Messages buffer;
@@ -4714,9 +4724,9 @@ bool ArgumentAnalyzer::HasDeviceDefinedIntrinsicOpOverride(
47144724
return false;
47154725
}
47164726

4717-
MaybeExpr ArgumentAnalyzer::TryDefinedOp(
4718-
const char *opr, parser::MessageFixedText error, bool isUserOp) {
4719-
if (AnyUntypedOrMissingOperand()) {
4727+
MaybeExpr ArgumentAnalyzer::TryDefinedOp(const char *opr,
4728+
parser::MessageFixedText error, bool isUserOp, bool checkForNullPointer) {
4729+
if (AnyMissingOperand()) {
47204730
context_.Say(error, ToUpperCase(opr), TypeAsFortran(0), TypeAsFortran(1));
47214731
return std::nullopt;
47224732
}
@@ -4795,7 +4805,9 @@ MaybeExpr ArgumentAnalyzer::TryDefinedOp(
47954805
context_.Say(
47964806
"Operands of %s are not conformable; have rank %d and rank %d"_err_en_US,
47974807
ToUpperCase(opr), actuals_[0]->Rank(), actuals_[1]->Rank());
4798-
} else if (CheckForNullPointer() && CheckForAssumedRank()) {
4808+
} else if (!CheckForAssumedRank()) {
4809+
} else if (checkForNullPointer && !CheckForNullPointer()) {
4810+
} else { // use the supplied error
47994811
context_.Say(error, ToUpperCase(opr), TypeAsFortran(0), TypeAsFortran(1));
48004812
}
48014813
return result;
@@ -4813,15 +4825,16 @@ MaybeExpr ArgumentAnalyzer::TryDefinedOp(
48134825
for (std::size_t i{0}; i < oprs.size(); ++i) {
48144826
parser::Messages buffer;
48154827
auto restorer{context_.GetContextualMessages().SetMessages(buffer)};
4816-
if (MaybeExpr thisResult{TryDefinedOp(oprs[i], error)}) {
4828+
if (MaybeExpr thisResult{TryDefinedOp(oprs[i], error, /*isUserOp=*/false,
4829+
/*checkForNullPointer=*/false)}) {
48174830
result = std::move(thisResult);
48184831
hit.push_back(oprs[i]);
48194832
hitBuffer = std::move(buffer);
48204833
}
48214834
}
48224835
}
4823-
if (hit.empty()) { // for the error
4824-
result = TryDefinedOp(oprs[0], error);
4836+
if (hit.empty()) { // run TryDefinedOp() again just to emit errors
4837+
CHECK(!TryDefinedOp(oprs[0], error).has_value());
48254838
} else if (hit.size() > 1) {
48264839
context_.Say(
48274840
"Matching accessible definitions were found with %zd variant spellings of the generic operator ('%s', '%s')"_err_en_US,
@@ -5237,10 +5250,19 @@ std::string ArgumentAnalyzer::TypeAsFortran(std::size_t i) {
52375250
}
52385251
}
52395252

5240-
bool ArgumentAnalyzer::AnyUntypedOrMissingOperand() const {
5253+
bool ArgumentAnalyzer::AnyUntypedOperand() const {
5254+
for (const auto &actual : actuals_) {
5255+
if (actual && !actual->GetType() &&
5256+
!IsBareNullPointer(actual->UnwrapExpr())) {
5257+
return true;
5258+
}
5259+
}
5260+
return false;
5261+
}
5262+
5263+
bool ArgumentAnalyzer::AnyMissingOperand() const {
52415264
for (const auto &actual : actuals_) {
5242-
if (!actual ||
5243-
(!actual->GetType() && !IsBareNullPointer(actual->UnwrapExpr()))) {
5265+
if (!actual) {
52445266
return true;
52455267
}
52465268
}

flang/test/Semantics/bug163255.f90

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
!RUN: %flang_fc1 -fdebug-unparse %s | FileCheck %s
2+
module m
3+
type t
4+
end type
5+
interface operator (==)
6+
module procedure equal
7+
end interface
8+
contains
9+
logical function equal(b1, b2)
10+
class(t), pointer, intent(in) :: b1, b2
11+
equal = associated(b1, b2)
12+
end
13+
end module
14+
15+
program test
16+
use m
17+
type(t), target :: target
18+
class(t), pointer :: p => target
19+
!CHECK: IF (equal(p,null(p))) STOP
20+
if (p == null(p)) stop
21+
end

flang/test/Semantics/resolve63.f90

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,13 +165,12 @@ subroutine s1(x, y)
165165
logical :: l
166166
complex :: z
167167
y = y + z'1' !OK
168-
!ERROR: Operands of + must be numeric; have untyped and COMPLEX(4)
168+
!ERROR: No intrinsic or user-defined OPERATOR(+) matches operand types untyped and COMPLEX(4)
169169
z = z'1' + z
170170
y = +z'1' !OK
171171
!ERROR: Operand of unary - must be numeric; have untyped
172172
y = -z'1'
173-
!ERROR: Operands of + must be numeric; have LOGICAL(4) and untyped
174-
y = x + z'1'
173+
y = x + z'1' ! matches "add" with conversion of untyped to integer
175174
!ERROR: A NULL() pointer is not allowed as an operand here
176175
l = x /= null()
177176
!ERROR: A NULL() pointer is not allowed as a relational operand

0 commit comments

Comments
 (0)