Skip to content

Commit 9bbec0a

Browse files
authored
[flang] Fix SIZEOF() expression rewriting (#66241)
The rewriting of the extension intrinsic function SIZEOF was producing results that would reference symbols that were not available in the current scope, leading to crashes in lowering. The symbols could be function result variables, for SIZEOF(func()), or bare derived type component names, for SIZEOF(array(n)%component). Fixing this without regressing on a current test case involved careful threading of some state through the TypeAndShape characterization code and the shape/bounds analyzer, and some clean-up was done along the way.
1 parent bb7b872 commit 9bbec0a

File tree

4 files changed

+72
-56
lines changed

4 files changed

+72
-56
lines changed

flang/include/flang/Evaluate/characteristics.h

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -88,11 +88,11 @@ class TypeAndShape {
8888
static std::optional<TypeAndShape> Characterize(
8989
const ActualArgument &, FoldingContext &, bool invariantOnly = false);
9090

91-
// General case for Expr<T>, ActualArgument, &c.
91+
// General case for Expr<T>, &c.
9292
template <typename A>
9393
static std::optional<TypeAndShape> Characterize(
9494
const A &x, FoldingContext &context, bool invariantOnly = false) {
95-
if (const auto *symbol{UnwrapWholeSymbolOrComponentDataRef(x)}) {
95+
if (const auto *symbol{UnwrapWholeSymbolDataRef(x)}) {
9696
if (auto result{Characterize(*symbol, context, invariantOnly)}) {
9797
return result;
9898
}
@@ -116,7 +116,7 @@ class TypeAndShape {
116116
static std::optional<TypeAndShape> Characterize(
117117
const Designator<Type<TypeCategory::Character, KIND>> &x,
118118
FoldingContext &context, bool invariantOnly = true) {
119-
if (const auto *symbol{UnwrapWholeSymbolOrComponentDataRef(x)}) {
119+
if (const auto *symbol{UnwrapWholeSymbolDataRef(x)}) {
120120
if (auto result{Characterize(*symbol, context, invariantOnly)}) {
121121
return result;
122122
}
@@ -184,8 +184,6 @@ class TypeAndShape {
184184
static std::optional<TypeAndShape> Characterize(
185185
const semantics::AssocEntityDetails &, FoldingContext &,
186186
bool invariantOnly = true);
187-
static std::optional<TypeAndShape> Characterize(
188-
const semantics::ProcEntityDetails &, FoldingContext &);
189187
void AcquireAttrs(const semantics::Symbol &);
190188
void AcquireLEN();
191189
void AcquireLEN(const semantics::Symbol &);

flang/include/flang/Evaluate/shape.h

Lines changed: 9 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -131,14 +131,8 @@ class GetShapeHelper
131131
using Result = std::optional<Shape>;
132132
using Base = AnyTraverse<GetShapeHelper, Result>;
133133
using Base::operator();
134-
explicit GetShapeHelper(bool invariantOnly)
135-
: Base{*this}, invariantOnly_{invariantOnly} {}
136-
explicit GetShapeHelper(FoldingContext &c, bool invariantOnly)
137-
: Base{*this}, context_{&c}, invariantOnly_{invariantOnly} {}
138-
explicit GetShapeHelper(
139-
FoldingContext &c, bool useResultSymbolShape, bool invariantOnly)
140-
: Base{*this}, context_{&c}, useResultSymbolShape_{useResultSymbolShape},
141-
invariantOnly_{invariantOnly} {}
134+
GetShapeHelper(FoldingContext *context, bool invariantOnly)
135+
: Base{*this}, context_{context}, invariantOnly_{invariantOnly} {}
142136

143137
Result operator()(const ImpliedDoIndex &) const { return ScalarShape(); }
144138
Result operator()(const DescriptorInquiry &) const { return ScalarShape(); }
@@ -187,9 +181,7 @@ class GetShapeHelper
187181
return common::visit(
188182
common::visitors{
189183
[&](const Expr<T> &x) -> MaybeExtentExpr {
190-
if (auto xShape{!useResultSymbolShape_ ? (*this)(x)
191-
: context_ ? GetShape(*context_, x)
192-
: GetShape(x)}) {
184+
if (auto xShape{(*this)(x)}) {
193185
// Array values in array constructors get linearized.
194186
return GetSize(std::move(*xShape));
195187
} else {
@@ -233,7 +225,7 @@ class GetShapeHelper
233225
void AccumulateExtent(ExtentExpr &, ExtentExpr &&) const;
234226

235227
FoldingContext *context_{nullptr};
236-
bool useResultSymbolShape_{true};
228+
mutable bool useResultSymbolShape_{true};
237229
// When invariantOnly=false, the returned shape need not be invariant
238230
// in its scope; in particular, it may contain references to dummy arguments.
239231
bool invariantOnly_{true};
@@ -242,7 +234,7 @@ class GetShapeHelper
242234
template <typename A>
243235
std::optional<Shape> GetShape(
244236
FoldingContext &context, const A &x, bool invariantOnly) {
245-
if (auto shape{GetShapeHelper{context, invariantOnly}(x)}) {
237+
if (auto shape{GetShapeHelper{&context, invariantOnly}(x)}) {
246238
return Fold(context, std::move(shape));
247239
} else {
248240
return std::nullopt;
@@ -251,17 +243,13 @@ std::optional<Shape> GetShape(
251243

252244
template <typename A>
253245
std::optional<Shape> GetShape(const A &x, bool invariantOnly) {
254-
return GetShapeHelper{invariantOnly}(x);
246+
return GetShapeHelper{/*context=*/nullptr, invariantOnly}(x);
255247
}
256248

257249
template <typename A>
258250
std::optional<Shape> GetShape(
259251
FoldingContext *context, const A &x, bool invariantOnly = true) {
260-
if (context) {
261-
return GetShape(*context, x, invariantOnly);
262-
} else {
263-
return GetShapeHelper{invariantOnly}(x);
264-
}
252+
return GetShapeHelper{context, invariantOnly}(x);
265253
}
266254

267255
template <typename A>
@@ -286,12 +274,11 @@ std::optional<ConstantSubscripts> GetConstantExtents(
286274

287275
// Get shape that does not depends on callee scope symbols if the expression
288276
// contains calls. Return std::nullopt if it is not possible to build such shape
289-
// (e.g. for calls to array functions whose result shape depends on the
277+
// (e.g. for calls to array-valued functions whose result shape depends on the
290278
// arguments).
291279
template <typename A>
292280
std::optional<Shape> GetContextFreeShape(FoldingContext &context, const A &x) {
293-
return GetShapeHelper{
294-
context, /*useResultSymbolShape=*/false, /*invariantOnly=*/true}(x);
281+
return GetShapeHelper{&context, /*invariantOnly=*/true}(x);
295282
}
296283

297284
// Compilation-time shape conformance checking, when corresponding extents

flang/lib/Evaluate/shape.cpp

Lines changed: 26 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -323,10 +323,10 @@ class GetLowerBoundHelper
323323
if (IsActuallyConstant(exprLowerBound)) {
324324
return std::move(exprLowerBound);
325325
} else {
326-
// If the lower bound of the associated entity is not resolved to
326+
// If the lower bound of the associated entity is not resolved to a
327327
// constant expression at the time of the association, it is unsafe
328328
// to re-evaluate it later in the associate construct. Statements
329-
// in-between may have modified its operands value.
329+
// in between may have modified its operands value.
330330
return ExtentExpr{DescriptorInquiry{std::move(base),
331331
DescriptorInquiry::Field::LowerBound, dimension_}};
332332
}
@@ -476,24 +476,23 @@ static MaybeExtentExpr GetNonNegativeExtent(
476476
}
477477
}
478478

479-
MaybeExtentExpr GetAssociatedExtent(const NamedEntity &base,
480-
const semantics::AssocEntityDetails &assoc, int dimension) {
481-
if (auto shape{GetShape(assoc.expr())}) {
482-
if (dimension < static_cast<int>(shape->size())) {
483-
auto &extent{shape->at(dimension)};
484-
if (extent && IsActuallyConstant(*extent)) {
479+
static MaybeExtentExpr GetAssociatedExtent(
480+
const Symbol &symbol, int dimension) {
481+
if (const auto *assoc{symbol.detailsIf<semantics::AssocEntityDetails>()};
482+
assoc && !assoc->rank()) { // not SELECT RANK case
483+
if (auto shape{GetShape(assoc->expr())};
484+
shape && dimension < static_cast<int>(shape->size())) {
485+
if (auto &extent{shape->at(dimension)};
486+
// Don't return a non-constant extent, as the variables that
487+
// determine the shape of the selector's expression may change
488+
// during execution of the construct.
489+
extent && IsActuallyConstant(*extent)) {
485490
return std::move(extent);
486-
} else {
487-
// Otherwise, evaluating the associated expression extent expression
488-
// after the associate statement is unsafe given statements inside the
489-
// associate may have modified the associated expression operands
490-
// values.
491-
return ExtentExpr{DescriptorInquiry{
492-
NamedEntity{base}, DescriptorInquiry::Field::Extent, dimension}};
493491
}
494492
}
495493
}
496-
return std::nullopt;
494+
return ExtentExpr{DescriptorInquiry{
495+
NamedEntity{symbol}, DescriptorInquiry::Field::Extent, dimension}};
497496
}
498497

499498
MaybeExtentExpr GetExtent(
@@ -508,14 +507,16 @@ MaybeExtentExpr GetExtent(
508507
if (semantics::IsDescriptor(symbol) && dimension < *assoc->rank()) {
509508
return ExtentExpr{DescriptorInquiry{
510509
NamedEntity{base}, DescriptorInquiry::Field::Extent, dimension}};
510+
} else {
511+
return std::nullopt;
511512
}
512513
} else {
513-
return GetAssociatedExtent(base, *assoc, dimension);
514+
return GetAssociatedExtent(last, dimension);
514515
}
515516
}
516517
if (const auto *details{symbol.detailsIf<semantics::ObjectEntityDetails>()}) {
517518
if (IsImpliedShape(symbol) && details->init()) {
518-
if (auto shape{GetShape(symbol)}) {
519+
if (auto shape{GetShape(symbol, invariantOnly)}) {
519520
if (dimension < static_cast<int>(shape->size())) {
520521
return std::move(shape->at(dimension));
521522
}
@@ -527,7 +528,7 @@ MaybeExtentExpr GetExtent(
527528
if (auto extent{GetNonNegativeExtent(shapeSpec, invariantOnly)}) {
528529
return extent;
529530
} else if (details->IsAssumedSize() && j == symbol.Rank()) {
530-
return std::nullopt;
531+
break;
531532
} else if (semantics::IsDescriptor(symbol)) {
532533
return ExtentExpr{DescriptorInquiry{NamedEntity{base},
533534
DescriptorInquiry::Field::Extent, dimension}};
@@ -620,7 +621,7 @@ MaybeExtentExpr GetRawUpperBound(
620621
return std::nullopt;
621622
} else if (assoc->rank() && dimension >= *assoc->rank()) {
622623
return std::nullopt;
623-
} else if (auto extent{GetAssociatedExtent(base, *assoc, dimension)}) {
624+
} else if (auto extent{GetAssociatedExtent(symbol, dimension)}) {
624625
return ComputeUpperBound(
625626
GetRawLowerBound(base, dimension), std::move(extent));
626627
}
@@ -680,11 +681,9 @@ static MaybeExtentExpr GetUBOUND(FoldingContext *context,
680681
std::move(base), DescriptorInquiry::Field::Extent, dimension}};
681682
return ComputeUpperBound(std::move(lb), std::move(extent));
682683
}
683-
} else if (assoc->expr()) {
684-
if (auto extent{GetAssociatedExtent(base, *assoc, dimension)}) {
685-
if (auto lb{GetLBOUND(base, dimension, invariantOnly)}) {
686-
return ComputeUpperBound(std::move(*lb), std::move(extent));
687-
}
684+
} else if (auto extent{GetAssociatedExtent(symbol, dimension)}) {
685+
if (auto lb{GetLBOUND(base, dimension, invariantOnly)}) {
686+
return ComputeUpperBound(std::move(*lb), std::move(extent));
688687
}
689688
}
690689
}
@@ -768,7 +767,7 @@ auto GetShapeHelper::operator()(const Symbol &symbol) const -> Result {
768767
auto resultShape{(*this)(subp.result())};
769768
if (resultShape && !useResultSymbolShape_) {
770769
// Ensure the shape is constant. Otherwise, it may be referring
771-
// to symbols that belong to the subroutine scope and are
770+
// to symbols that belong to the function's scope and are
772771
// meaningless on the caller side without the related call
773772
// expression.
774773
for (auto &extent : *resultShape) {
@@ -799,9 +798,6 @@ auto GetShapeHelper::operator()(const Component &component) const -> Result {
799798
} else if (symbol.has<semantics::ObjectEntityDetails>()) {
800799
NamedEntity base{Component{component}};
801800
return CreateShape(rank, base);
802-
} else if (symbol.has<semantics::AssocEntityDetails>()) {
803-
NamedEntity base{Component{component}};
804-
return Result{CreateShape(rank, base)};
805801
} else {
806802
return (*this)(symbol);
807803
}
@@ -878,6 +874,7 @@ auto GetShapeHelper::operator()(const ProcedureRef &call) const -> Result {
878874
}
879875
return ScalarShape();
880876
} else if (const Symbol * symbol{call.proc().GetSymbol()}) {
877+
auto restorer{common::ScopedSet(useResultSymbolShape_, false)};
881878
return (*this)(*symbol);
882879
} else if (const auto *intrinsic{call.proc().GetSpecificIntrinsic()}) {
883880
if (intrinsic->name == "shape" || intrinsic->name == "lbound" ||

flang/test/Evaluate/rewrite05.f90

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
! RUN: %flang_fc1 -fdebug-unparse %s 2>&1 | FileCheck %s
2+
program main
3+
type t
4+
integer, allocatable :: component(:)
5+
end type
6+
type(t) :: x
7+
call init(10)
8+
!CHECK: PRINT *, [INTEGER(4)::int(lbound(x%component,dim=1,kind=8),kind=4)]
9+
print *, lbound(x%component)
10+
!CHECK: PRINT *, [INTEGER(4)::int(size(x%component,dim=1,kind=8)+lbound(x%component,dim=1,kind=8)-1_8,kind=4)]
11+
print *, ubound(x%component)
12+
!CHECK: PRINT *, int(size(x%component,dim=1,kind=8),kind=4)
13+
print *, size(x%component)
14+
!CHECK: PRINT *, 4_8*size(x%component,dim=1,kind=8)
15+
print *, sizeof(x%component)
16+
!CHECK: PRINT *, 1_4
17+
print *, lbound(iota(10), 1)
18+
!CHECK: PRINT *, ubound(iota(10_4),1_4)
19+
print *, ubound(iota(10), 1)
20+
!CHECK: PRINT *, size(iota(10_4))
21+
print *, size(iota(10))
22+
!CHECK: PRINT *, sizeof(iota(10_4))
23+
print *, sizeof(iota(10))
24+
contains
25+
function iota(n) result(result)
26+
integer, intent(in) :: n
27+
integer, allocatable :: result(:)
28+
result = [(j,j=1,n)]
29+
end
30+
subroutine init(n)
31+
integer, intent(in) :: n
32+
allocate(x%component(0:n-1))
33+
end
34+
end

0 commit comments

Comments
 (0)