Skip to content

Commit d2126ec

Browse files
authored
[flang] Fix bogus error about procedure incompatbility (#107645)
This was a subtle problem. When the shape of a function result is explicit but not constant, it is characterized with bounds expressions that use Extremum<SubscriptInteger> operations to force extents to 0 rather than be negative. These Extremum operations are formatted as "max()" intrinsic functions in the module file. Upon being read from the module file, they are not folded back into Extremum operations, but remain as function references; and this then leads to expressions not comparing equal when the procedure characteristics are compared to those of a local procedure declared identically. The real fix here would be for folding to just always change max and min function references into Extremum<> operations, constant operands or not, and I tried that, but it lead to test failures and crashes in lowering that I couldn't resolve. So, until those can be fixed, here's a change that will read max/min operations in module file declarations back into Extremum operations to solve the compatibility checking problem, but leave other non-constant max/min operations as function calls.
1 parent 5a229db commit d2126ec

File tree

6 files changed

+122
-20
lines changed

6 files changed

+122
-20
lines changed

flang/include/flang/Evaluate/expression.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,7 @@ template <typename A> struct Extremum : public Operation<Extremum<A>, A, A, A> {
342342
: Base{x, y}, ordering{ord} {}
343343
Extremum(Ordering ord, Expr<Operand> &&x, Expr<Operand> &&y)
344344
: Base{std::move(x), std::move(y)}, ordering{ord} {}
345+
bool operator==(const Extremum &) const;
345346
Ordering ordering{Ordering::Greater};
346347
};
347348

@@ -381,6 +382,7 @@ struct LogicalOperation
381382
: Base{x, y}, logicalOperator{opr} {}
382383
LogicalOperation(LogicalOperator opr, Expr<Operand> &&x, Expr<Operand> &&y)
383384
: Base{std::move(x), std::move(y)}, logicalOperator{opr} {}
385+
bool operator==(const LogicalOperation &) const;
384386
LogicalOperator logicalOperator;
385387
};
386388

@@ -634,6 +636,7 @@ class Relational : public Operation<Relational<T>, LogicalResult, T, T> {
634636
: Base{a, b}, opr{r} {}
635637
Relational(RelationalOperator r, Expr<Operand> &&a, Expr<Operand> &&b)
636638
: Base{std::move(a), std::move(b)}, opr{r} {}
639+
bool operator==(const Relational &) const;
637640
RelationalOperator opr;
638641
};
639642

flang/include/flang/Evaluate/tools.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,22 @@ template <typename A, typename B> A *UnwrapExpr(std::optional<B> &x) {
218218
}
219219
}
220220

221+
template <typename A, typename B> const A *UnwrapExpr(const B *x) {
222+
if (x) {
223+
return UnwrapExpr<A>(*x);
224+
} else {
225+
return nullptr;
226+
}
227+
}
228+
229+
template <typename A, typename B> A *UnwrapExpr(B *x) {
230+
if (x) {
231+
return UnwrapExpr<A>(*x);
232+
} else {
233+
return nullptr;
234+
}
235+
}
236+
221237
// A variant of UnwrapExpr above that also skips through (parentheses)
222238
// and conversions of kinds within a category. Useful for extracting LEN
223239
// type parameter inquiries, at least.

flang/lib/Evaluate/expression.cpp

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,24 @@ template <typename A> LLVM_DUMP_METHOD void ExpressionBase<A>::dump() const {
125125

126126
// Equality testing
127127

128+
template <typename A> bool Extremum<A>::operator==(const Extremum &that) const {
129+
return ordering == that.ordering && Base::operator==(that);
130+
}
131+
132+
template <int KIND>
133+
bool LogicalOperation<KIND>::operator==(const LogicalOperation &that) const {
134+
return logicalOperator == that.logicalOperator && Base::operator==(that);
135+
}
136+
137+
template <typename A>
138+
bool Relational<A>::operator==(const Relational &that) const {
139+
return opr == that.opr && Base::operator==(that);
140+
}
141+
142+
bool Relational<SomeType>::operator==(const Relational &that) const {
143+
return u == that.u;
144+
}
145+
128146
bool ImpliedDoIndex::operator==(const ImpliedDoIndex &that) const {
129147
return name == that.name;
130148
}
@@ -181,10 +199,6 @@ bool StructureConstructor::operator==(const StructureConstructor &that) const {
181199
return result_ == that.result_ && values_ == that.values_;
182200
}
183201

184-
bool Relational<SomeType>::operator==(const Relational<SomeType> &that) const {
185-
return u == that.u;
186-
}
187-
188202
template <int KIND>
189203
bool Expr<Type<TypeCategory::Integer, KIND>>::operator==(
190204
const Expr<Type<TypeCategory::Integer, KIND>> &that) const {

flang/lib/Evaluate/fold-implementation.h

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1088,24 +1088,42 @@ Expr<T> FoldMINorMAX(
10881088
static_assert(T::category == TypeCategory::Integer ||
10891089
T::category == TypeCategory::Real ||
10901090
T::category == TypeCategory::Character);
1091-
std::vector<Constant<T> *> constantArgs;
1092-
// Call Folding on all arguments, even if some are not constant,
1093-
// to make operand promotion explicit.
1094-
for (auto &arg : funcRef.arguments()) {
1095-
if (auto *cst{Folder<T>{context}.Folding(arg)}) {
1096-
constantArgs.push_back(cst);
1091+
auto &args{funcRef.arguments()};
1092+
bool ok{true};
1093+
std::optional<Expr<T>> result;
1094+
Folder<T> folder{context};
1095+
for (std::optional<ActualArgument> &arg : args) {
1096+
// Call Folding on all arguments to make operand promotion explicit.
1097+
if (!folder.Folding(arg)) {
1098+
// TODO: Lowering can't handle having every FunctionRef for max and min
1099+
// being converted into Extremum<T>. That needs fixing. Until that
1100+
// is corrected, however, it is important that max and min references
1101+
// in module files be converted into Extremum<T> even when not constant;
1102+
// the Extremum<SubscriptInteger> operations created to normalize the
1103+
// values of array bounds are formatted as max operations in the
1104+
// declarations in modules, and need to be read back in as such in
1105+
// order for expression comparison to not produce false inequalities
1106+
// when checking function results for procedure interface compatibility.
1107+
if (!context.moduleFileName()) {
1108+
ok = false;
1109+
}
1110+
}
1111+
Expr<SomeType> *argExpr{arg ? arg->UnwrapExpr() : nullptr};
1112+
if (argExpr) {
1113+
*argExpr = Fold(context, std::move(*argExpr));
1114+
}
1115+
if (Expr<T> * tExpr{UnwrapExpr<Expr<T>>(argExpr)}) {
1116+
if (result) {
1117+
result = FoldOperation(
1118+
context, Extremum<T>{order, std::move(*result), Expr<T>{*tExpr}});
1119+
} else {
1120+
result = Expr<T>{*tExpr};
1121+
}
1122+
} else {
1123+
ok = false;
10971124
}
10981125
}
1099-
if (constantArgs.size() != funcRef.arguments().size()) {
1100-
return Expr<T>(std::move(funcRef));
1101-
}
1102-
CHECK(!constantArgs.empty());
1103-
Expr<T> result{std::move(*constantArgs[0])};
1104-
for (std::size_t i{1}; i < constantArgs.size(); ++i) {
1105-
Extremum<T> extremum{order, result, Expr<T>{std::move(*constantArgs[i])}};
1106-
result = FoldOperation(context, std::move(extremum));
1107-
}
1108-
return result;
1126+
return ok && result ? std::move(*result) : Expr<T>{std::move(funcRef)};
11091127
}
11101128

11111129
// For AMAX0, AMIN0, AMAX1, AMIN1, DMAX1, DMIN1, MAX0, MIN0, MAX1, and MIN1
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
!mod$ v1 sum:37cfecee3234c8ab
2+
module modfile67
3+
type::t
4+
procedure(foo),nopass,pointer::p
5+
end type
6+
contains
7+
pure function foo(n,a) result(r)
8+
integer(4),intent(in)::n
9+
real(4),intent(in)::a(1_8:int(n,kind=8))
10+
logical(4)::r(1_8:int(int(max(0_8,int(n,kind=8)),kind=4),kind=8))
11+
end
12+
function fooptr(f)
13+
procedure(foo)::f
14+
type(t)::fooptr
15+
end
16+
end

flang/test/Semantics/modfile67.f90

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
!RUN: %flang_fc1 -fsyntax-only -J%S/Inputs %s
2+
3+
#if 0
4+
!modfile67.mod was produced from this source, and must be read into this
5+
!compilation from its module file in order to truly test this fix.
6+
module modfile67
7+
type t
8+
procedure(foo), nopass, pointer :: p
9+
end type
10+
contains
11+
pure function foo(n,a) result(r)
12+
integer, intent(in) :: n
13+
real, intent(in), dimension(n) :: a
14+
logical, dimension(size(a)) :: r
15+
r = .false.
16+
end
17+
type(t) function fooptr(f)
18+
procedure(foo) f
19+
fooptr%p => f
20+
end
21+
end
22+
#endif
23+
24+
program test
25+
use modfile67
26+
type(t) x
27+
x = fooptr(bar) ! ensure no bogus error about procedure incompatibility
28+
contains
29+
pure function bar(n,a) result(r)
30+
integer, intent(in) :: n
31+
real, intent(in), dimension(n) :: a
32+
logical, dimension(size(a)) :: r
33+
r = .false.
34+
end
35+
end

0 commit comments

Comments
 (0)