Skip to content

Commit 2852010

Browse files
klauslerjeanPerier
authored andcommitted
[flang] Include default component initialization in static initializers
The combined initializers constructed from DATA statements and explicit static initialization in declarations needs to include derived type component default initializations, overriding those default values without complaint with values from explicit DATA statement or declaration initializations when they overlap. This also has to work for objects with storage association due to EQUIVALENCE. When storage association causes default component initializations to overlap, emit errors if and only if the values differ (See Fortran 2018 subclause 19.5.3, esp. paragraph 10). The f18 front-end has a module that analyzes and converts DATA statements into equivalent static initializers for objects. For storage-associated objects, compiler-generated objects are created that overlay the entire association and fill it with a combined initializer. This "data-to-inits" module already exists, and this patch is essentially extension and clean-up of its machinery to complete the job. Also: emit EQUIVALENCE to module files; mark compiler-created symbols and *don't* emit those to module files; check non-static EQUIVALENCE sets for conflicting default component initializations, so lowering doesn't have to check them or emit diagnostics. Differential Revision: https://reviews.llvm.org/D109022
1 parent 53d598b commit 2852010

File tree

18 files changed

+713
-271
lines changed

18 files changed

+713
-271
lines changed

flang/include/flang/Evaluate/initial-image.h

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class InitialImage {
3030
};
3131

3232
explicit InitialImage(std::size_t bytes) : data_(bytes) {}
33+
InitialImage(InitialImage &&that) = default;
3334

3435
std::size_t size() const { return data_.size(); }
3536

@@ -93,19 +94,17 @@ class InitialImage {
9394

9495
void AddPointer(ConstantSubscript, const Expr<SomeType> &);
9596

96-
void Incorporate(ConstantSubscript, const InitialImage &);
97+
void Incorporate(ConstantSubscript toOffset, const InitialImage &from,
98+
ConstantSubscript fromOffset, ConstantSubscript bytes);
9799

98100
// Conversions to constant initializers
99101
std::optional<Expr<SomeType>> AsConstant(FoldingContext &,
100102
const DynamicType &, const ConstantSubscripts &,
101103
ConstantSubscript offset = 0) const;
102-
std::optional<Expr<SomeType>> AsConstantDataPointer(
103-
const DynamicType &, ConstantSubscript offset = 0) const;
104-
const ProcedureDesignator &AsConstantProcPointer(
104+
std::optional<Expr<SomeType>> AsConstantPointer(
105105
ConstantSubscript offset = 0) const;
106106

107107
friend class AsConstantHelper;
108-
friend class AsConstantDataPointerHelper;
109108

110109
private:
111110
std::vector<char> data_;

flang/include/flang/Semantics/scope.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ struct EquivalenceObject {
4141
std::optional<ConstantSubscript> substringStart, parser::CharBlock source)
4242
: symbol{symbol}, subscripts{subscripts},
4343
substringStart{substringStart}, source{source} {}
44+
explicit EquivalenceObject(Symbol &symbol)
45+
: symbol{symbol}, source{symbol.name()} {}
4446

4547
bool operator==(const EquivalenceObject &) const;
4648
bool operator<(const EquivalenceObject &) const;

flang/include/flang/Semantics/symbol.h

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,7 @@ class Symbol {
497497
LocalityShared, // named in SHARED locality-spec
498498
InDataStmt, // initialized in a DATA statement
499499
InNamelist, // flag is set if the symbol is in Namelist statement
500+
CompilerCreated,
500501
// OpenACC data-sharing attribute
501502
AccPrivate, AccFirstPrivate, AccShared,
502503
// OpenACC data-mapping attribute
@@ -782,7 +783,7 @@ struct SymbolAddressCompare {
782783
}
783784
};
784785

785-
// Symbol comparison is based on the order of cooked source
786+
// Symbol comparison is usually based on the order of cooked source
786787
// stream creation and, when both are from the same cooked source,
787788
// their positions in that cooked source stream.
788789
// Don't use this comparator or OrderedSymbolSet to hold
@@ -794,12 +795,17 @@ struct SymbolSourcePositionCompare {
794795
bool operator()(const MutableSymbolRef &, const MutableSymbolRef &) const;
795796
};
796797

798+
struct SymbolOffsetCompare {
799+
bool operator()(const SymbolRef &, const SymbolRef &) const;
800+
bool operator()(const MutableSymbolRef &, const MutableSymbolRef &) const;
801+
};
802+
797803
using UnorderedSymbolSet = std::set<SymbolRef, SymbolAddressCompare>;
798-
using OrderedSymbolSet = std::set<SymbolRef, SymbolSourcePositionCompare>;
804+
using SourceOrderedSymbolSet = std::set<SymbolRef, SymbolSourcePositionCompare>;
799805

800806
template <typename A>
801-
OrderedSymbolSet OrderBySourcePosition(const A &container) {
802-
OrderedSymbolSet result;
807+
SourceOrderedSymbolSet OrderBySourcePosition(const A &container) {
808+
SourceOrderedSymbolSet result;
803809
for (SymbolRef x : container) {
804810
result.emplace(x);
805811
}

flang/include/flang/Semantics/tools.h

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ const DeclTypeSpec *FindParentTypeSpec(const DeclTypeSpec &);
5656
const DeclTypeSpec *FindParentTypeSpec(const Scope &);
5757
const DeclTypeSpec *FindParentTypeSpec(const Symbol &);
5858

59+
const EquivalenceSet *FindEquivalenceSet(const Symbol &);
60+
5961
enum class Tristate { No, Yes, Maybe };
6062
inline Tristate ToTristate(bool x) { return x ? Tristate::Yes : Tristate::No; }
6163

@@ -105,14 +107,13 @@ bool IsEventTypeOrLockType(const DerivedTypeSpec *);
105107
bool IsOrContainsEventOrLockComponent(const Symbol &);
106108
bool CanBeTypeBoundProc(const Symbol *);
107109
// Does a non-PARAMETER symbol have explicit initialization with =value or
108-
// =>target in its declaration, or optionally in a DATA statement? (Being
110+
// =>target in its declaration (but not in a DATA statement)? (Being
109111
// ALLOCATABLE or having a derived type with default component initialization
110112
// doesn't count; it must be a variable initialization that implies the SAVE
111113
// attribute, or a derived type component default value.)
112-
bool IsStaticallyInitialized(const Symbol &, bool ignoreDATAstatements = false);
114+
bool HasDeclarationInitializer(const Symbol &);
113115
// Is the symbol explicitly or implicitly initialized in any way?
114-
bool IsInitialized(const Symbol &, bool ignoreDATAstatements = false,
115-
const Symbol *derivedType = nullptr);
116+
bool IsInitialized(const Symbol &, bool ignoreDATAstatements = false);
116117
// Is the symbol a component subject to deallocation or finalization?
117118
bool IsDestructible(const Symbol &, const Symbol *derivedType = nullptr);
118119
bool HasIntrinsicTypeName(const Symbol &);
@@ -330,6 +331,13 @@ enum class ProcedureDefinitionClass {
330331

331332
ProcedureDefinitionClass ClassifyProcedure(const Symbol &);
332333

334+
// Returns a list of storage associations due to EQUIVALENCE in a
335+
// scope; each storage association is a list of symbol references
336+
// in ascending order of scope offset. Note that the scope may have
337+
// more EquivalenceSets than this function's result has storage
338+
// associations; these are closures over equivalences.
339+
std::list<std::list<SymbolRef>> GetStorageAssociations(const Scope &);
340+
333341
// Derived type component iterator that provides a C++ LegacyForwardIterator
334342
// iterator over the Ordered, Direct, Ultimate or Potential components of a
335343
// DerivedTypeSpec. These iterators can be used with STL algorithms

flang/lib/Evaluate/initial-image.cpp

Lines changed: 31 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,14 @@ void InitialImage::AddPointer(
5454
pointers_.emplace(offset, pointer);
5555
}
5656

57-
void InitialImage::Incorporate(
58-
ConstantSubscript offset, const InitialImage &that) {
59-
CHECK(that.pointers_.empty()); // pointers are not allowed in EQUIVALENCE
60-
CHECK(offset + that.size() <= size());
61-
std::memcpy(&data_[offset], &that.data_[0], that.size());
57+
void InitialImage::Incorporate(ConstantSubscript toOffset,
58+
const InitialImage &from, ConstantSubscript fromOffset,
59+
ConstantSubscript bytes) {
60+
CHECK(from.pointers_.empty()); // pointers are not allowed in EQUIVALENCE
61+
CHECK(fromOffset >= 0 && bytes >= 0 &&
62+
static_cast<std::size_t>(fromOffset + bytes) <= from.size());
63+
CHECK(static_cast<std::size_t>(toOffset + bytes) <= size());
64+
std::memcpy(&data_[toOffset], &from.data_[fromOffset], bytes);
6265
}
6366

6467
// Classes used with common::SearchTypes() to (re)construct Constant<> values
@@ -97,26 +100,31 @@ class AsConstantHelper {
97100
const semantics::DerivedTypeSpec &derived{type_.GetDerivedTypeSpec()};
98101
for (auto iter : DEREF(derived.scope())) {
99102
const Symbol &component{*iter.second};
100-
bool isPointer{IsPointer(component)};
101-
if (component.has<semantics::ObjectEntityDetails>() ||
102-
component.has<semantics::ProcEntityDetails>()) {
103-
auto componentType{DynamicType::From(component)};
104-
CHECK(componentType);
103+
bool isProcPtr{IsProcedurePointer(component)};
104+
if (isProcPtr || component.has<semantics::ObjectEntityDetails>()) {
105105
auto at{offset_ + component.offset()};
106-
if (isPointer) {
106+
if (isProcPtr) {
107107
for (std::size_t j{0}; j < elements; ++j, at += stride) {
108-
Result value{image_.AsConstantDataPointer(*componentType, at)};
109-
CHECK(value);
110-
typedValue[j].emplace(component, std::move(*value));
108+
if (Result value{image_.AsConstantPointer(at)}) {
109+
typedValue[j].emplace(component, std::move(*value));
110+
}
111+
}
112+
} else if (IsPointer(component)) {
113+
for (std::size_t j{0}; j < elements; ++j, at += stride) {
114+
if (Result value{image_.AsConstantPointer(at)}) {
115+
typedValue[j].emplace(component, std::move(*value));
116+
}
111117
}
112118
} else {
119+
auto componentType{DynamicType::From(component)};
120+
CHECK(componentType.has_value());
113121
auto componentExtents{GetConstantExtents(context_, component)};
114-
CHECK(componentExtents);
122+
CHECK(componentExtents.has_value());
115123
for (std::size_t j{0}; j < elements; ++j, at += stride) {
116-
Result value{image_.AsConstant(
117-
context_, *componentType, *componentExtents, at)};
118-
CHECK(value);
119-
typedValue[j].emplace(component, std::move(*value));
124+
if (Result value{image_.AsConstant(
125+
context_, *componentType, *componentExtents, at)}) {
126+
typedValue[j].emplace(component, std::move(*value));
127+
}
120128
}
121129
}
122130
}
@@ -159,45 +167,11 @@ std::optional<Expr<SomeType>> InitialImage::AsConstant(FoldingContext &context,
159167
AsConstantHelper{context, type, extents, *this, offset});
160168
}
161169

162-
class AsConstantDataPointerHelper {
163-
public:
164-
using Result = std::optional<Expr<SomeType>>;
165-
using Types = AllTypes;
166-
AsConstantDataPointerHelper(const DynamicType &type,
167-
const InitialImage &image, ConstantSubscript offset = 0)
168-
: type_{type}, image_{image}, offset_{offset} {}
169-
template <typename T> Result Test() {
170-
if (T::category != type_.category()) {
171-
return std::nullopt;
172-
}
173-
if constexpr (T::category != TypeCategory::Derived) {
174-
if (T::kind != type_.kind()) {
175-
return std::nullopt;
176-
}
177-
}
178-
auto iter{image_.pointers_.find(offset_)};
179-
if (iter == image_.pointers_.end()) {
180-
return AsGenericExpr(NullPointer{});
181-
}
182-
return iter->second;
183-
}
184-
185-
private:
186-
const DynamicType &type_;
187-
const InitialImage &image_;
188-
ConstantSubscript offset_;
189-
};
190-
191-
std::optional<Expr<SomeType>> InitialImage::AsConstantDataPointer(
192-
const DynamicType &type, ConstantSubscript offset) const {
193-
return common::SearchTypes(AsConstantDataPointerHelper{type, *this, offset});
194-
}
195-
196-
const ProcedureDesignator &InitialImage::AsConstantProcPointer(
170+
std::optional<Expr<SomeType>> InitialImage::AsConstantPointer(
197171
ConstantSubscript offset) const {
198-
auto iter{pointers_.find(0)};
199-
CHECK(iter != pointers_.end());
200-
return DEREF(std::get_if<ProcedureDesignator>(&iter->second.u));
172+
auto iter{pointers_.find(offset)};
173+
return iter == pointers_.end() ? std::optional<Expr<SomeType>>{}
174+
: iter->second;
201175
}
202176

203177
} // namespace Fortran::evaluate

flang/lib/Semantics/check-declarations.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -526,7 +526,7 @@ void CheckHelper::CheckObjectEntity(
526526
messages_.Say("OPTIONAL attribute may apply only to a dummy "
527527
"argument"_err_en_US); // C849
528528
}
529-
if (IsStaticallyInitialized(symbol, true /* ignore DATA inits */)) { // C808
529+
if (HasDeclarationInitializer(symbol)) { // C808; ignore DATA initialization
530530
CheckPointerInitialization(symbol);
531531
if (IsAutomatic(symbol)) {
532532
messages_.Say(

flang/lib/Semantics/compute-offsets.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,9 @@ class ComputeOffsetsHelper {
3838
};
3939
struct SymbolAndOffset {
4040
SymbolAndOffset(Symbol &s, std::size_t off, const EquivalenceObject &obj)
41-
: symbol{&s}, offset{off}, object{&obj} {}
41+
: symbol{s}, offset{off}, object{&obj} {}
4242
SymbolAndOffset(const SymbolAndOffset &) = default;
43-
Symbol *symbol;
43+
MutableSymbolRef symbol;
4444
std::size_t offset;
4545
const EquivalenceObject *object;
4646
};

0 commit comments

Comments
 (0)