1 | //===-- lib/Semantics/data-to-inits.cpp -----------------------------------===// |
---|---|
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | // DATA statement object/value checking and conversion to static |
10 | // initializers |
11 | // - Applies specific checks to each scalar element initialization with a |
12 | // constant value or pointer target with class DataInitializationCompiler; |
13 | // - Collects the elemental initializations for each symbol and converts them |
14 | // into a single init() expression with member function |
15 | // DataChecker::ConstructInitializer(). |
16 | |
17 | #include "data-to-inits.h" |
18 | #include "pointer-assignment.h" |
19 | #include "flang/Evaluate/fold-designator.h" |
20 | #include "flang/Evaluate/tools.h" |
21 | #include "flang/Semantics/tools.h" |
22 | |
23 | // The job of generating explicit static initializers for objects that don't |
24 | // have them in order to implement default component initialization is now being |
25 | // done in lowering, so don't do it here in semantics; but the code remains here |
26 | // in case we change our minds. |
27 | static constexpr bool makeDefaultInitializationExplicit{false}; |
28 | |
29 | // Whether to delete the original "init()" initializers from storage-associated |
30 | // objects and pointers. |
31 | static constexpr bool removeOriginalInits{false}; |
32 | |
33 | // Impose a hard limit that's more than large enough for real applications but |
34 | // small enough to cause artificial stress tests to fail reasonably instead of |
35 | // crashing the compiler with a memory allocation failure. |
36 | static constexpr auto maxDataInitBytes{std::size_t{1000000000}}; // 1GiB |
37 | |
38 | namespace Fortran::semantics { |
39 | |
40 | // Steps through a list of values in a DATA statement set; implements |
41 | // repetition. |
42 | template <typename DSV = parser::DataStmtValue> class ValueListIterator { |
43 | public: |
44 | ValueListIterator(SemanticsContext &context, const std::list<DSV> &list) |
45 | : context_{context}, end_{list.end()}, at_{list.begin()} { |
46 | SetRepetitionCount(); |
47 | } |
48 | bool hasFatalError() const { return hasFatalError_; } |
49 | bool IsAtEnd() const { return at_ == end_; } |
50 | const SomeExpr *operator*() const { return GetExpr(context_, GetConstant()); } |
51 | std::optional<parser::CharBlock> LocateSource() const { |
52 | if (!hasFatalError_) { |
53 | return GetConstant().source; |
54 | } |
55 | return {}; |
56 | } |
57 | ValueListIterator &operator++() { |
58 | if (repetitionsRemaining_ > 0) { |
59 | --repetitionsRemaining_; |
60 | } else if (at_ != end_) { |
61 | ++at_; |
62 | SetRepetitionCount(); |
63 | } |
64 | return *this; |
65 | } |
66 | |
67 | private: |
68 | using listIterator = typename std::list<DSV>::const_iterator; |
69 | void SetRepetitionCount(); |
70 | const parser::DataStmtValue &GetValue() const { |
71 | return DEREF(common::Unwrap<const parser::DataStmtValue>(*at_)); |
72 | } |
73 | const parser::DataStmtConstant &GetConstant() const { |
74 | return std::get<parser::DataStmtConstant>(GetValue().t); |
75 | } |
76 | |
77 | SemanticsContext &context_; |
78 | listIterator end_, at_; |
79 | ConstantSubscript repetitionsRemaining_{0}; |
80 | bool hasFatalError_{false}; |
81 | }; |
82 | |
83 | template <typename DSV> void ValueListIterator<DSV>::SetRepetitionCount() { |
84 | for (; at_ != end_; ++at_) { |
85 | auto repetitions{GetValue().repetitions}; |
86 | if (repetitions < 0) { |
87 | hasFatalError_ = true; |
88 | } else if (repetitions > 0) { |
89 | repetitionsRemaining_ = repetitions - 1; |
90 | return; |
91 | } |
92 | } |
93 | repetitionsRemaining_ = 0; |
94 | } |
95 | |
96 | // Collects all of the elemental initializations from DATA statements |
97 | // into a single image for each symbol that appears in any DATA. |
98 | // Expands the implied DO loops and array references. |
99 | // Applies checks that validate each distinct elemental initialization |
100 | // of the variables in a data-stmt-set, as well as those that apply |
101 | // to the corresponding values being used to initialize each element. |
102 | template <typename DSV = parser::DataStmtValue> |
103 | class DataInitializationCompiler { |
104 | public: |
105 | DataInitializationCompiler(DataInitializations &inits, |
106 | evaluate::ExpressionAnalyzer &a, const std::list<DSV> &list) |
107 | : inits_{inits}, exprAnalyzer_{a}, values_{a.context(), list} {} |
108 | const DataInitializations &inits() const { return inits_; } |
109 | bool HasSurplusValues() const { return !values_.IsAtEnd(); } |
110 | bool Scan(const parser::DataStmtObject &); |
111 | // Initializes all elements of whole variable or component |
112 | bool Scan(const Symbol &); |
113 | |
114 | private: |
115 | bool Scan(const parser::Variable &); |
116 | bool Scan(const parser::Designator &); |
117 | bool Scan(const parser::DataImpliedDo &); |
118 | bool Scan(const parser::DataIDoObject &); |
119 | |
120 | // Initializes all elements of a designator, which can be an array or section. |
121 | bool InitDesignator(const SomeExpr &, const Scope &); |
122 | // Initializes a single scalar object. |
123 | bool InitElement(const evaluate::OffsetSymbol &, const SomeExpr &designator, |
124 | const Scope &); |
125 | // If the returned flag is true, emit a warning about CHARACTER misusage. |
126 | std::optional<std::pair<SomeExpr, bool>> ConvertElement( |
127 | const SomeExpr &, const evaluate::DynamicType &); |
128 | |
129 | DataInitializations &inits_; |
130 | evaluate::ExpressionAnalyzer &exprAnalyzer_; |
131 | ValueListIterator<DSV> values_; |
132 | }; |
133 | |
134 | template <typename DSV> |
135 | bool DataInitializationCompiler<DSV>::Scan( |
136 | const parser::DataStmtObject &object) { |
137 | return common::visit( |
138 | common::visitors{ |
139 | [&](const common::Indirection<parser::Variable> &var) { |
140 | return Scan(var.value()); |
141 | }, |
142 | [&](const parser::DataImpliedDo &ido) { return Scan(ido); }, |
143 | }, |
144 | object.u); |
145 | } |
146 | |
147 | template <typename DSV> |
148 | bool DataInitializationCompiler<DSV>::Scan(const parser::Variable &var) { |
149 | if (const auto *expr{GetExpr(exprAnalyzer_.context(), var)}) { |
150 | parser::CharBlock at{var.GetSource()}; |
151 | exprAnalyzer_.GetFoldingContext().messages().SetLocation(at); |
152 | if (InitDesignator(*expr, exprAnalyzer_.context().FindScope(at))) { |
153 | return true; |
154 | } |
155 | } |
156 | return false; |
157 | } |
158 | |
159 | template <typename DSV> |
160 | bool DataInitializationCompiler<DSV>::Scan( |
161 | const parser::Designator &designator) { |
162 | MaybeExpr expr; |
163 | { // The out-of-range subscript errors from the designator folder are a |
164 | // more specific than the default ones from expression semantics, so |
165 | // disable those to avoid piling on. |
166 | auto restorer{exprAnalyzer_.GetContextualMessages().DiscardMessages()}; |
167 | expr = exprAnalyzer_.Analyze(designator); |
168 | } |
169 | if (expr) { |
170 | parser::CharBlock at{parser::FindSourceLocation(designator)}; |
171 | exprAnalyzer_.GetFoldingContext().messages().SetLocation(at); |
172 | if (InitDesignator(*expr, exprAnalyzer_.context().FindScope(at))) { |
173 | return true; |
174 | } |
175 | } |
176 | return false; |
177 | } |
178 | |
179 | template <typename DSV> |
180 | bool DataInitializationCompiler<DSV>::Scan(const parser::DataImpliedDo &ido) { |
181 | const auto &bounds{std::get<parser::DataImpliedDo::Bounds>(ido.t)}; |
182 | auto name{bounds.name.thing.thing}; |
183 | const auto *lowerExpr{ |
184 | GetExpr(exprAnalyzer_.context(), bounds.lower.thing.thing)}; |
185 | const auto *upperExpr{ |
186 | GetExpr(exprAnalyzer_.context(), bounds.upper.thing.thing)}; |
187 | const auto *stepExpr{bounds.step |
188 | ? GetExpr(exprAnalyzer_.context(), bounds.step->thing.thing) |
189 | : nullptr}; |
190 | if (lowerExpr && upperExpr) { |
191 | // Fold the bounds expressions (again) in case any of them depend |
192 | // on outer implied DO loops. |
193 | evaluate::FoldingContext &context{exprAnalyzer_.GetFoldingContext()}; |
194 | std::int64_t stepVal{1}; |
195 | if (stepExpr) { |
196 | auto foldedStep{evaluate::Fold(context, SomeExpr{*stepExpr})}; |
197 | stepVal = ToInt64(foldedStep).value_or(1); |
198 | if (stepVal == 0) { |
199 | exprAnalyzer_.Say(name.source, |
200 | "DATA statement implied DO loop has a step value of zero"_err_en_US); |
201 | return false; |
202 | } |
203 | } |
204 | auto foldedLower{evaluate::Fold(context, SomeExpr{*lowerExpr})}; |
205 | auto lower{ToInt64(foldedLower)}; |
206 | auto foldedUpper{evaluate::Fold(context, SomeExpr{*upperExpr})}; |
207 | auto upper{ToInt64(foldedUpper)}; |
208 | if (lower && upper) { |
209 | int kind{evaluate::ResultType<evaluate::ImpliedDoIndex>::kind}; |
210 | if (const auto dynamicType{evaluate::DynamicType::From(*name.symbol)}) { |
211 | if (dynamicType->category() == TypeCategory::Integer) { |
212 | kind = dynamicType->kind(); |
213 | } |
214 | } |
215 | if (exprAnalyzer_.AddImpliedDo(name.source, kind)) { |
216 | auto &value{context.StartImpliedDo(name.source, *lower)}; |
217 | bool result{true}; |
218 | for (auto n{(*upper - value + stepVal) / stepVal}; n > 0; |
219 | --n, value += stepVal) { |
220 | for (const auto &object : |
221 | std::get<std::list<parser::DataIDoObject>>(ido.t)) { |
222 | if (!Scan(object)) { |
223 | result = false; |
224 | break; |
225 | } |
226 | } |
227 | } |
228 | context.EndImpliedDo(name.source); |
229 | exprAnalyzer_.RemoveImpliedDo(name.source); |
230 | return result; |
231 | } |
232 | } |
233 | } |
234 | return false; |
235 | } |
236 | |
237 | template <typename DSV> |
238 | bool DataInitializationCompiler<DSV>::Scan( |
239 | const parser::DataIDoObject &object) { |
240 | return common::visit( |
241 | common::visitors{ |
242 | [&](const parser::Scalar<common::Indirection<parser::Designator>> |
243 | &var) { return Scan(var.thing.value()); }, |
244 | [&](const common::Indirection<parser::DataImpliedDo> &ido) { |
245 | return Scan(ido.value()); |
246 | }, |
247 | }, |
248 | object.u); |
249 | } |
250 | |
251 | template <typename DSV> |
252 | bool DataInitializationCompiler<DSV>::Scan(const Symbol &symbol) { |
253 | auto designator{exprAnalyzer_.Designate(evaluate::DataRef{symbol})}; |
254 | CHECK(designator.has_value()); |
255 | return InitDesignator(*designator, symbol.owner()); |
256 | } |
257 | |
258 | template <typename DSV> |
259 | bool DataInitializationCompiler<DSV>::InitDesignator( |
260 | const SomeExpr &designator, const Scope &scope) { |
261 | evaluate::FoldingContext &context{exprAnalyzer_.GetFoldingContext()}; |
262 | evaluate::DesignatorFolder folder{context}; |
263 | while (auto offsetSymbol{folder.FoldDesignator(designator)}) { |
264 | if (folder.isOutOfRange()) { |
265 | if (auto bad{evaluate::OffsetToDesignator(context, *offsetSymbol)}) { |
266 | exprAnalyzer_.context().Say( |
267 | "DATA statement designator '%s' is out of range"_err_en_US, |
268 | bad->AsFortran()); |
269 | } else { |
270 | exprAnalyzer_.context().Say( |
271 | "DATA statement designator '%s' is out of range"_err_en_US, |
272 | designator.AsFortran()); |
273 | } |
274 | return false; |
275 | } else if (!InitElement(*offsetSymbol, designator, scope)) { |
276 | return false; |
277 | } else { |
278 | ++values_; |
279 | } |
280 | } |
281 | return folder.isEmpty(); |
282 | } |
283 | |
284 | template <typename DSV> |
285 | std::optional<std::pair<SomeExpr, bool>> |
286 | DataInitializationCompiler<DSV>::ConvertElement( |
287 | const SomeExpr &expr, const evaluate::DynamicType &type) { |
288 | if (auto converted{evaluate::ConvertToType(type, SomeExpr{expr})}) { |
289 | return {std::make_pair(std::move(*converted), false)}; |
290 | } |
291 | // Allow DATA initialization with Hollerith and kind=1 CHARACTER like |
292 | // (most) other Fortran compilers do. |
293 | if (auto converted{evaluate::HollerithToBOZ( |
294 | exprAnalyzer_.GetFoldingContext(), expr, type)}) { |
295 | return {std::make_pair(std::move(*converted), true)}; |
296 | } |
297 | SemanticsContext &context{exprAnalyzer_.context()}; |
298 | if (context.IsEnabled(common::LanguageFeature::LogicalIntegerAssignment)) { |
299 | if (MaybeExpr converted{evaluate::DataConstantConversionExtension( |
300 | exprAnalyzer_.GetFoldingContext(), type, expr)}) { |
301 | context.Warn(common::LanguageFeature::LogicalIntegerAssignment, |
302 | exprAnalyzer_.GetFoldingContext().messages().at(), |
303 | "nonstandard usage: initialization of %s with %s"_port_en_US, |
304 | type.AsFortran(), expr.GetType().value().AsFortran()); |
305 | return {std::make_pair(std::move(*converted), false)}; |
306 | } |
307 | } |
308 | return std::nullopt; |
309 | } |
310 | |
311 | template <typename DSV> |
312 | bool DataInitializationCompiler<DSV>::InitElement( |
313 | const evaluate::OffsetSymbol &offsetSymbol, const SomeExpr &designator, |
314 | const Scope &scope) { |
315 | const Symbol &symbol{offsetSymbol.symbol()}; |
316 | const Symbol *lastSymbol{GetLastSymbol(designator)}; |
317 | bool isPointer{lastSymbol && IsPointer(*lastSymbol)}; |
318 | bool isProcPointer{lastSymbol && IsProcedurePointer(*lastSymbol)}; |
319 | evaluate::FoldingContext &context{exprAnalyzer_.GetFoldingContext()}; |
320 | |
321 | const auto DescribeElement{[&]() { |
322 | if (auto badDesignator{ |
323 | evaluate::OffsetToDesignator(context, offsetSymbol)}) { |
324 | return badDesignator->AsFortran(); |
325 | } else { |
326 | // Error recovery |
327 | std::string buf; |
328 | llvm::raw_string_ostream ss{buf}; |
329 | ss << offsetSymbol.symbol().name() << " offset "<< offsetSymbol.offset() |
330 | << " bytes for "<< offsetSymbol.size() << " bytes"; |
331 | return ss.str(); |
332 | } |
333 | }}; |
334 | const auto GetImage{[&]() -> evaluate::InitialImage & { |
335 | // This could be (and was) written to always call std::map<>::emplace(), |
336 | // which should handle duplicate entries gracefully, but it was still |
337 | // causing memory allocation & deallocation with gcc. |
338 | auto iter{inits_.find(&symbol)}; |
339 | if (iter == inits_.end()) { |
340 | iter = inits_.emplace(&symbol, symbol.size()).first; |
341 | } |
342 | auto &symbolInit{iter->second}; |
343 | symbolInit.NoteInitializedRange(offsetSymbol); |
344 | return symbolInit.image; |
345 | }}; |
346 | const auto OutOfRangeError{[&]() { |
347 | evaluate::AttachDeclaration( |
348 | exprAnalyzer_.context().Say( |
349 | "DATA statement designator '%s' is out of range for its variable '%s'"_err_en_US, |
350 | DescribeElement(), symbol.name()), |
351 | symbol); |
352 | }}; |
353 | |
354 | if (values_.hasFatalError()) { |
355 | return false; |
356 | } else if (values_.IsAtEnd()) { |
357 | exprAnalyzer_.context().Say( |
358 | "DATA statement set has no value for '%s'"_err_en_US, |
359 | DescribeElement()); |
360 | return false; |
361 | } else if (static_cast<std::size_t>( |
362 | offsetSymbol.offset() + offsetSymbol.size()) > symbol.size()) { |
363 | OutOfRangeError(); |
364 | return false; |
365 | } |
366 | |
367 | auto &messages{context.messages()}; |
368 | auto restorer{ |
369 | messages.SetLocation(values_.LocateSource().value_or(messages.at()))}; |
370 | const SomeExpr *expr{*values_}; |
371 | if (!expr) { |
372 | CHECK(exprAnalyzer_.context().AnyFatalError()); |
373 | } else if (symbol.size() > maxDataInitBytes) { |
374 | evaluate::AttachDeclaration( |
375 | exprAnalyzer_.context().Say( |
376 | "'%s' is too large to initialize with a DATA statement"_todo_en_US, |
377 | symbol.name()), |
378 | symbol); |
379 | return false; |
380 | } else if (isPointer) { |
381 | if (static_cast<std::size_t>(offsetSymbol.offset() + offsetSymbol.size()) > |
382 | symbol.size()) { |
383 | OutOfRangeError(); |
384 | } else if (evaluate::IsNullPointer(expr)) { |
385 | // nothing to do; rely on zero initialization |
386 | return true; |
387 | } else if (isProcPointer) { |
388 | if (evaluate::IsProcedureDesignator(*expr)) { |
389 | if (CheckPointerAssignment(exprAnalyzer_.context(), designator, *expr, |
390 | scope, |
391 | /*isBoundsRemapping=*/false, /*isAssumedRank=*/false)) { |
392 | if (lastSymbol->has<ProcEntityDetails>()) { |
393 | GetImage().AddPointer(offsetSymbol.offset(), *expr); |
394 | return true; |
395 | } else { |
396 | evaluate::AttachDeclaration( |
397 | exprAnalyzer_.context().Say( |
398 | "DATA statement initialization of procedure pointer '%s' declared using a POINTER statement and an INTERFACE instead of a PROCEDURE statement"_todo_en_US, |
399 | DescribeElement()), |
400 | *lastSymbol); |
401 | } |
402 | } |
403 | } else { |
404 | exprAnalyzer_.Say( |
405 | "Data object '%s' may not be used to initialize '%s', which is a procedure pointer"_err_en_US, |
406 | expr->AsFortran(), DescribeElement()); |
407 | } |
408 | } else if (evaluate::IsProcedure(*expr)) { |
409 | exprAnalyzer_.Say( |
410 | "Procedure '%s' may not be used to initialize '%s', which is not a procedure pointer"_err_en_US, |
411 | expr->AsFortran(), DescribeElement()); |
412 | } else if (CheckInitialDataPointerTarget( |
413 | exprAnalyzer_.context(), designator, *expr, scope)) { |
414 | GetImage().AddPointer(offsetSymbol.offset(), *expr); |
415 | return true; |
416 | } |
417 | } else if (evaluate::IsNullPointer(expr)) { |
418 | exprAnalyzer_.Say("Initializer for '%s' must not be a pointer"_err_en_US, |
419 | DescribeElement()); |
420 | } else if (evaluate::IsProcedureDesignator(*expr)) { |
421 | exprAnalyzer_.Say("Initializer for '%s' must not be a procedure"_err_en_US, |
422 | DescribeElement()); |
423 | } else if (auto designatorType{designator.GetType()}) { |
424 | if (expr->Rank() > 0) { |
425 | // Because initial-data-target is ambiguous with scalar-constant and |
426 | // scalar-constant-subobject at parse time, enforcement of scalar-* |
427 | // must be deferred to here. |
428 | exprAnalyzer_.Say( |
429 | "DATA statement value initializes '%s' with an array"_err_en_US, |
430 | DescribeElement()); |
431 | } else if (auto converted{ConvertElement(*expr, *designatorType)}) { |
432 | // value non-pointer initialization |
433 | if (IsBOZLiteral(*expr) && |
434 | designatorType->category() != TypeCategory::Integer) { // 8.6.7(11) |
435 | exprAnalyzer_.Warn(common::LanguageFeature::DataStmtExtensions, |
436 | "BOZ literal should appear in a DATA statement only as a value for an integer object, but '%s' is '%s'"_port_en_US, |
437 | DescribeElement(), designatorType->AsFortran()); |
438 | } else if (converted->second) { |
439 | exprAnalyzer_.Warn(common::LanguageFeature::DataStmtExtensions, |
440 | "DATA statement value initializes '%s' of type '%s' with CHARACTER"_port_en_US, |
441 | DescribeElement(), designatorType->AsFortran()); |
442 | } |
443 | auto folded{evaluate::Fold(context, std::move(converted->first))}; |
444 | // Rewritten from a switch() in order to avoid getting complaints |
445 | // about a missing "default:" from some compilers and complaints |
446 | // about a redundant "default:" from others. |
447 | auto status{GetImage().Add( |
448 | offsetSymbol.offset(), offsetSymbol.size(), folded, context)}; |
449 | if (status == evaluate::InitialImage::Ok) { |
450 | return true; |
451 | } else if (status == evaluate::InitialImage::NotAConstant) { |
452 | exprAnalyzer_.Say( |
453 | "DATA statement value '%s' for '%s' is not a constant"_err_en_US, |
454 | folded.AsFortran(), DescribeElement()); |
455 | } else if (status == evaluate::InitialImage::OutOfRange) { |
456 | OutOfRangeError(); |
457 | } else if (status == evaluate::InitialImage::LengthMismatch) { |
458 | exprAnalyzer_.Warn(common::UsageWarning::DataLength, |
459 | "DATA statement value '%s' for '%s' has the wrong length"_warn_en_US, |
460 | folded.AsFortran(), DescribeElement()); |
461 | return true; |
462 | } else if (status == evaluate::InitialImage::TooManyElems) { |
463 | exprAnalyzer_.Say("DATA statement has too many elements"_err_en_US); |
464 | } else { |
465 | CHECK(exprAnalyzer_.context().AnyFatalError()); |
466 | } |
467 | } else { |
468 | exprAnalyzer_.context().Say( |
469 | "DATA statement value could not be converted to the type '%s' of the object '%s'"_err_en_US, |
470 | designatorType->AsFortran(), DescribeElement()); |
471 | } |
472 | } else { |
473 | CHECK(exprAnalyzer_.context().AnyFatalError()); |
474 | } |
475 | return false; |
476 | } |
477 | |
478 | void AccumulateDataInitializations(DataInitializations &inits, |
479 | evaluate::ExpressionAnalyzer &exprAnalyzer, |
480 | const parser::DataStmtSet &set) { |
481 | DataInitializationCompiler scanner{ |
482 | inits, exprAnalyzer, std::get<std::list<parser::DataStmtValue>>(set.t)}; |
483 | for (const auto &object : |
484 | std::get<std::list<parser::DataStmtObject>>(set.t)) { |
485 | if (!scanner.Scan(object)) { |
486 | return; |
487 | } |
488 | } |
489 | if (scanner.HasSurplusValues()) { |
490 | exprAnalyzer.context().Say( |
491 | "DATA statement set has more values than objects"_err_en_US); |
492 | } |
493 | } |
494 | |
495 | void AccumulateDataInitializations(DataInitializations &inits, |
496 | evaluate::ExpressionAnalyzer &exprAnalyzer, const Symbol &symbol, |
497 | const std::list<common::Indirection<parser::DataStmtValue>> &list) { |
498 | DataInitializationCompiler<common::Indirection<parser::DataStmtValue>> |
499 | scanner{inits, exprAnalyzer, list}; |
500 | if (scanner.Scan(symbol) && scanner.HasSurplusValues()) { |
501 | exprAnalyzer.context().Say( |
502 | "DATA statement set has more values than objects"_err_en_US); |
503 | } |
504 | } |
505 | |
506 | // Looks for default derived type component initialization -- but |
507 | // *not* allocatables. |
508 | static const DerivedTypeSpec *HasDefaultInitialization(const Symbol &symbol) { |
509 | if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) { |
510 | if (object->init().has_value()) { |
511 | return nullptr; // init is explicit, not default |
512 | } else if (!object->isDummy() && object->type()) { |
513 | if (const DerivedTypeSpec * derived{object->type()->AsDerived()}) { |
514 | DirectComponentIterator directs{*derived}; |
515 | if (llvm::any_of(directs, [](const Symbol &component) { |
516 | return !IsAllocatable(component) && |
517 | HasDeclarationInitializer(component); |
518 | })) { |
519 | return derived; |
520 | } |
521 | } |
522 | } |
523 | } |
524 | return nullptr; |
525 | } |
526 | |
527 | // PopulateWithComponentDefaults() adds initializations to an instance |
528 | // of SymbolDataInitialization containing all of the default component |
529 | // initializers |
530 | |
531 | static void PopulateWithComponentDefaults(SymbolDataInitialization &init, |
532 | std::size_t offset, const DerivedTypeSpec &derived, |
533 | evaluate::FoldingContext &foldingContext); |
534 | |
535 | static void PopulateWithComponentDefaults(SymbolDataInitialization &init, |
536 | std::size_t offset, const DerivedTypeSpec &derived, |
537 | evaluate::FoldingContext &foldingContext, const Symbol &symbol) { |
538 | if (auto extents{evaluate::GetConstantExtents(foldingContext, symbol)}) { |
539 | const Scope &scope{derived.scope() ? *derived.scope() |
540 | : DEREF(derived.typeSymbol().scope())}; |
541 | std::size_t stride{scope.size()}; |
542 | if (std::size_t alignment{scope.alignment().value_or(0)}) { |
543 | stride = ((stride + alignment - 1) / alignment) * alignment; |
544 | } |
545 | for (auto elements{evaluate::GetSize(*extents)}; elements-- > 0; |
546 | offset += stride) { |
547 | PopulateWithComponentDefaults(init, offset, derived, foldingContext); |
548 | } |
549 | } |
550 | } |
551 | |
552 | // F'2018 19.5.3(10) allows storage-associated default component initialization |
553 | // when the values are identical. |
554 | static void PopulateWithComponentDefaults(SymbolDataInitialization &init, |
555 | std::size_t offset, const DerivedTypeSpec &derived, |
556 | evaluate::FoldingContext &foldingContext) { |
557 | const Scope &scope{ |
558 | derived.scope() ? *derived.scope() : DEREF(derived.typeSymbol().scope())}; |
559 | for (const auto &pair : scope) { |
560 | const Symbol &component{*pair.second}; |
561 | std::size_t componentOffset{offset + component.offset()}; |
562 | if (const auto *object{component.detailsIf<ObjectEntityDetails>()}) { |
563 | if (!IsAllocatable(component) && !IsAutomatic(component)) { |
564 | bool initialized{false}; |
565 | if (object->init()) { |
566 | initialized = true; |
567 | if (IsPointer(component)) { |
568 | if (auto extant{init.image.AsConstantPointer(componentOffset)}) { |
569 | initialized = !(*extant == *object->init()); |
570 | } |
571 | if (initialized) { |
572 | init.image.AddPointer(componentOffset, *object->init()); |
573 | } |
574 | } else { // data, not pointer |
575 | if (auto dyType{evaluate::DynamicType::From(component)}) { |
576 | if (auto extents{evaluate::GetConstantExtents( |
577 | foldingContext, component)}) { |
578 | if (auto extant{init.image.AsConstant(foldingContext, *dyType, |
579 | std::nullopt, *extents, false /*don't pad*/, |
580 | componentOffset)}) { |
581 | initialized = !(*extant == *object->init()); |
582 | } |
583 | } |
584 | } |
585 | if (initialized) { |
586 | init.image.Add(componentOffset, component.size(), *object->init(), |
587 | foldingContext); |
588 | } |
589 | } |
590 | } else if (const DeclTypeSpec * type{component.GetType()}) { |
591 | if (const DerivedTypeSpec * componentDerived{type->AsDerived()}) { |
592 | PopulateWithComponentDefaults(init, componentOffset, |
593 | *componentDerived, foldingContext, component); |
594 | } |
595 | } |
596 | if (initialized) { |
597 | init.NoteInitializedRange(componentOffset, component.size()); |
598 | } |
599 | } |
600 | } else if (const auto *proc{component.detailsIf<ProcEntityDetails>()}) { |
601 | if (proc->init() && *proc->init()) { |
602 | SomeExpr procPtrInit{evaluate::ProcedureDesignator{**proc->init()}}; |
603 | auto extant{init.image.AsConstantPointer(componentOffset)}; |
604 | if (!extant || !(*extant == procPtrInit)) { |
605 | init.NoteInitializedRange(componentOffset, component.size()); |
606 | init.image.AddPointer(componentOffset, std::move(procPtrInit)); |
607 | } |
608 | } |
609 | } |
610 | } |
611 | } |
612 | |
613 | static bool CheckForOverlappingInitialization( |
614 | const std::list<SymbolRef> &symbols, |
615 | SymbolDataInitialization &initialization, |
616 | evaluate::ExpressionAnalyzer &exprAnalyzer, const std::string &what) { |
617 | bool result{true}; |
618 | auto &context{exprAnalyzer.GetFoldingContext()}; |
619 | initialization.initializedRanges.sort(); |
620 | ConstantSubscript next{0}; |
621 | for (const auto &range : initialization.initializedRanges) { |
622 | if (range.start() < next) { |
623 | result = false; // error: overlap |
624 | bool hit{false}; |
625 | for (const Symbol &symbol : symbols) { |
626 | auto offset{range.start() - |
627 | static_cast<ConstantSubscript>( |
628 | symbol.offset() - symbols.front()->offset())}; |
629 | if (offset >= 0) { |
630 | if (auto badDesignator{evaluate::OffsetToDesignator( |
631 | context, symbol, offset, range.size())}) { |
632 | hit = true; |
633 | exprAnalyzer.Say(symbol.name(), |
634 | "%s affect '%s' more than once"_err_en_US, what, |
635 | badDesignator->AsFortran()); |
636 | } |
637 | } |
638 | } |
639 | CHECK(hit); |
640 | } |
641 | next = range.start() + range.size(); |
642 | CHECK(next <= static_cast<ConstantSubscript>(initialization.image.size())); |
643 | } |
644 | return result; |
645 | } |
646 | |
647 | static void IncorporateExplicitInitialization( |
648 | SymbolDataInitialization &combined, DataInitializations &inits, |
649 | const Symbol &symbol, ConstantSubscript firstOffset, |
650 | evaluate::FoldingContext &foldingContext) { |
651 | auto iter{inits.find(x: &symbol)}; |
652 | const auto offset{symbol.offset() - firstOffset}; |
653 | if (iter != inits.end()) { // DATA statement initialization |
654 | for (const auto &range : iter->second.initializedRanges) { |
655 | auto at{offset + range.start()}; |
656 | combined.NoteInitializedRange(at, range.size()); |
657 | combined.image.Incorporate( |
658 | at, iter->second.image, range.start(), range.size()); |
659 | } |
660 | if (removeOriginalInits) { |
661 | inits.erase(position: iter); |
662 | } |
663 | } else { // Declaration initialization |
664 | Symbol &mutableSymbol{const_cast<Symbol &>(symbol)}; |
665 | if (IsPointer(mutableSymbol)) { |
666 | if (auto *object{mutableSymbol.detailsIf<ObjectEntityDetails>()}) { |
667 | if (object->init()) { |
668 | combined.NoteInitializedRange(offset, mutableSymbol.size()); |
669 | combined.image.AddPointer(offset, *object->init()); |
670 | if (removeOriginalInits) { |
671 | object->init().reset(); |
672 | } |
673 | } |
674 | } else if (auto *proc{mutableSymbol.detailsIf<ProcEntityDetails>()}) { |
675 | if (proc->init() && *proc->init()) { |
676 | combined.NoteInitializedRange(offset, mutableSymbol.size()); |
677 | combined.image.AddPointer( |
678 | offset, SomeExpr{evaluate::ProcedureDesignator{**proc->init()}}); |
679 | if (removeOriginalInits) { |
680 | proc->init().reset(); |
681 | } |
682 | } |
683 | } |
684 | } else if (auto *object{mutableSymbol.detailsIf<ObjectEntityDetails>()}) { |
685 | if (!IsNamedConstant(mutableSymbol) && object->init()) { |
686 | combined.NoteInitializedRange(offset, mutableSymbol.size()); |
687 | combined.image.Add( |
688 | offset, mutableSymbol.size(), *object->init(), foldingContext); |
689 | if (removeOriginalInits) { |
690 | object->init().reset(); |
691 | } |
692 | } |
693 | } |
694 | } |
695 | } |
696 | |
697 | // Finds the size of the smallest element type in a list of |
698 | // storage-associated objects. |
699 | static std::size_t ComputeMinElementBytes( |
700 | const std::list<SymbolRef> &associated, |
701 | evaluate::FoldingContext &foldingContext) { |
702 | std::size_t minElementBytes{1}; |
703 | const Symbol &first{*associated.front()}; |
704 | for (const Symbol &s : associated) { |
705 | if (auto dyType{evaluate::DynamicType::From(s)}) { |
706 | auto size{static_cast<std::size_t>( |
707 | evaluate::ToInt64(dyType->MeasureSizeInBytes(foldingContext, true)) |
708 | .value_or(1))}; |
709 | if (std::size_t alignment{ |
710 | dyType->GetAlignment(foldingContext.targetCharacteristics())}) { |
711 | size = ((size + alignment - 1) / alignment) * alignment; |
712 | } |
713 | if (&s == &first) { |
714 | minElementBytes = size; |
715 | } else { |
716 | minElementBytes = std::min(minElementBytes, size); |
717 | } |
718 | } else { |
719 | minElementBytes = 1; |
720 | } |
721 | } |
722 | return minElementBytes; |
723 | } |
724 | |
725 | // Checks for overlapping initialization errors in a list of |
726 | // storage-associated objects. Default component initializations |
727 | // are allowed to be overridden by explicit initializations. |
728 | // If the objects are static, save the combined initializer as |
729 | // a compiler-created object that covers all of them. |
730 | static bool CombineEquivalencedInitialization( |
731 | const std::list<SymbolRef> &associated, |
732 | evaluate::ExpressionAnalyzer &exprAnalyzer, DataInitializations &inits) { |
733 | // Compute the minimum common granularity and total size |
734 | const Symbol &first{*associated.front()}; |
735 | std::size_t maxLimit{0}; |
736 | for (const Symbol &s : associated) { |
737 | CHECK(s.offset() >= first.offset()); |
738 | auto limit{s.offset() + s.size()}; |
739 | if (limit > maxLimit) { |
740 | maxLimit = limit; |
741 | } |
742 | } |
743 | auto bytes{static_cast<common::ConstantSubscript>(maxLimit - first.offset())}; |
744 | Scope &scope{const_cast<Scope &>(first.owner())}; |
745 | // Combine the initializations of the associated objects. |
746 | // Apply all default initializations first. |
747 | SymbolDataInitialization combined{static_cast<std::size_t>(bytes)}; |
748 | auto &foldingContext{exprAnalyzer.GetFoldingContext()}; |
749 | for (const Symbol &s : associated) { |
750 | if (!IsNamedConstant(s)) { |
751 | if (const auto *derived{HasDefaultInitialization(s)}) { |
752 | PopulateWithComponentDefaults( |
753 | combined, s.offset() - first.offset(), *derived, foldingContext, s); |
754 | } |
755 | } |
756 | } |
757 | if (!CheckForOverlappingInitialization(associated, combined, exprAnalyzer, |
758 | "Distinct default component initializations of equivalenced objects"s)) { |
759 | return false; |
760 | } |
761 | // Don't complain about overlap between explicit initializations and |
762 | // default initializations. |
763 | combined.initializedRanges.clear(); |
764 | // Now overlay all explicit initializations from DATA statements and |
765 | // from initializers in declarations. |
766 | for (const Symbol &symbol : associated) { |
767 | IncorporateExplicitInitialization( |
768 | combined, inits, symbol, first.offset(), foldingContext); |
769 | } |
770 | if (!CheckForOverlappingInitialization(associated, combined, exprAnalyzer, |
771 | "Explicit initializations of equivalenced objects"s)) { |
772 | return false; |
773 | } |
774 | // If the items are in static storage, save the final initialization. |
775 | if (llvm::any_of(associated, [](SymbolRef ref) { return IsSaved(*ref); })) { |
776 | // Create a compiler array temp that overlaps all the items. |
777 | SourceName name{exprAnalyzer.context().GetTempName(scope)}; |
778 | auto emplaced{ |
779 | scope.try_emplace(name, Attrs{Attr::SAVE}, ObjectEntityDetails{})}; |
780 | CHECK(emplaced.second); |
781 | Symbol &combinedSymbol{*emplaced.first->second}; |
782 | combinedSymbol.set(Symbol::Flag::CompilerCreated); |
783 | inits.emplace(args: &combinedSymbol, args: std::move(combined)); |
784 | auto &details{combinedSymbol.get<ObjectEntityDetails>()}; |
785 | combinedSymbol.set_offset(first.offset()); |
786 | combinedSymbol.set_size(bytes); |
787 | std::size_t minElementBytes{ |
788 | ComputeMinElementBytes(associated, foldingContext)}; |
789 | if (!exprAnalyzer.GetFoldingContext().targetCharacteristics().IsTypeEnabled( |
790 | TypeCategory::Integer, minElementBytes) || |
791 | (bytes % minElementBytes) != 0) { |
792 | minElementBytes = 1; |
793 | } |
794 | const DeclTypeSpec &typeSpec{scope.MakeNumericType( |
795 | TypeCategory::Integer, KindExpr{minElementBytes})}; |
796 | details.set_type(typeSpec); |
797 | ArraySpec arraySpec; |
798 | arraySpec.emplace_back(ShapeSpec::MakeExplicit(Bound{ |
799 | bytes / static_cast<common::ConstantSubscript>(minElementBytes)})); |
800 | details.set_shape(arraySpec); |
801 | if (const auto *commonBlock{FindCommonBlockContaining(first)}) { |
802 | details.set_commonBlock(*commonBlock); |
803 | } |
804 | // Add an EQUIVALENCE set to the scope so that the new object appears in |
805 | // the results of GetStorageAssociations(). |
806 | auto &newSet{scope.equivalenceSets().emplace_back()}; |
807 | newSet.emplace_back(combinedSymbol); |
808 | newSet.emplace_back(const_cast<Symbol &>(first)); |
809 | } |
810 | return true; |
811 | } |
812 | |
813 | // When a statically-allocated derived type variable has no explicit |
814 | // initialization, but its type has at least one nonallocatable ultimate |
815 | // component with default initialization, make its initialization explicit. |
816 | [[maybe_unused]] static void MakeDefaultInitializationExplicit( |
817 | const Scope &scope, const std::list<std::list<SymbolRef>> &associations, |
818 | evaluate::FoldingContext &foldingContext, DataInitializations &inits) { |
819 | UnorderedSymbolSet equivalenced; |
820 | for (const std::list<SymbolRef> &association : associations) { |
821 | for (const Symbol &symbol : association) { |
822 | equivalenced.emplace(symbol); |
823 | } |
824 | } |
825 | for (const auto &pair : scope) { |
826 | const Symbol &symbol{*pair.second}; |
827 | if (!symbol.test(Symbol::Flag::InDataStmt) && |
828 | !HasDeclarationInitializer(symbol) && IsSaved(symbol) && |
829 | equivalenced.find(symbol) == equivalenced.end()) { |
830 | // Static object, no local storage association, no explicit initialization |
831 | if (const DerivedTypeSpec * derived{HasDefaultInitialization(symbol)}) { |
832 | auto newInitIter{inits.emplace(&symbol, symbol.size())}; |
833 | CHECK(newInitIter.second); |
834 | auto &newInit{newInitIter.first->second}; |
835 | PopulateWithComponentDefaults( |
836 | newInit, 0, *derived, foldingContext, symbol); |
837 | } |
838 | } |
839 | } |
840 | } |
841 | |
842 | // Traverses the Scopes to: |
843 | // 1) combine initialization of equivalenced objects, & |
844 | // 2) optionally make initialization explicit for otherwise uninitialized static |
845 | // objects of derived types with default component initialization |
846 | // Returns false on error. |
847 | static bool ProcessScopes(const Scope &scope, |
848 | evaluate::ExpressionAnalyzer &exprAnalyzer, DataInitializations &inits) { |
849 | bool result{true}; // no error |
850 | switch (scope.kind()) { |
851 | case Scope::Kind::Global: |
852 | case Scope::Kind::Module: |
853 | case Scope::Kind::MainProgram: |
854 | case Scope::Kind::Subprogram: |
855 | case Scope::Kind::BlockData: |
856 | case Scope::Kind::BlockConstruct: { |
857 | std::list<std::list<SymbolRef>> associations{GetStorageAssociations(scope)}; |
858 | for (const std::list<SymbolRef> &associated : associations) { |
859 | if (std::find_if(associated.begin(), associated.end(), [](SymbolRef ref) { |
860 | return IsInitialized(*ref); |
861 | }) != associated.end()) { |
862 | result &= |
863 | CombineEquivalencedInitialization(associated, exprAnalyzer, inits); |
864 | } |
865 | } |
866 | if constexpr (makeDefaultInitializationExplicit) { |
867 | MakeDefaultInitializationExplicit( |
868 | scope, associations, exprAnalyzer.GetFoldingContext(), inits); |
869 | } |
870 | for (const Scope &child : scope.children()) { |
871 | result &= ProcessScopes(child, exprAnalyzer, inits); |
872 | } |
873 | } break; |
874 | default:; |
875 | } |
876 | return result; |
877 | } |
878 | |
879 | // Converts the static initialization image for a single symbol with |
880 | // one or more DATA statement appearances. |
881 | void ConstructInitializer(const Symbol &symbol, |
882 | SymbolDataInitialization &initialization, |
883 | evaluate::ExpressionAnalyzer &exprAnalyzer) { |
884 | std::list<SymbolRef> symbols{symbol}; |
885 | CheckForOverlappingInitialization( |
886 | symbols, initialization, exprAnalyzer, "DATA statement initializations"s); |
887 | auto &context{exprAnalyzer.GetFoldingContext()}; |
888 | if (const auto *proc{symbol.detailsIf<ProcEntityDetails>()}) { |
889 | CHECK(IsProcedurePointer(symbol)); |
890 | auto &mutableProc{const_cast<ProcEntityDetails &>(*proc)}; |
891 | if (MaybeExpr expr{initialization.image.AsConstantPointer()}) { |
892 | if (const auto *procDesignator{ |
893 | std::get_if<evaluate::ProcedureDesignator>(&expr->u)}) { |
894 | CHECK(!procDesignator->GetComponent()); |
895 | if (const auto *intrin{procDesignator->GetSpecificIntrinsic()}) { |
896 | const Symbol *intrinSymbol{ |
897 | symbol.owner().FindSymbol(SourceName{intrin->name})}; |
898 | mutableProc.set_init(DEREF(intrinSymbol)); |
899 | } else { |
900 | mutableProc.set_init(DEREF(procDesignator->GetSymbol())); |
901 | } |
902 | } else { |
903 | CHECK(evaluate::IsNullProcedurePointer(&*expr)); |
904 | mutableProc.set_init(nullptr); |
905 | } |
906 | } else { |
907 | mutableProc.set_init(nullptr); |
908 | } |
909 | } else if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) { |
910 | auto &mutableObject{const_cast<ObjectEntityDetails &>(*object)}; |
911 | if (IsPointer(symbol)) { |
912 | if (auto ptr{initialization.image.AsConstantPointer()}) { |
913 | mutableObject.set_init(*ptr); |
914 | } else { |
915 | mutableObject.set_init(SomeExpr{evaluate::NullPointer{}}); |
916 | } |
917 | } else if (auto symbolType{evaluate::DynamicType::From(symbol)}) { |
918 | if (auto extents{evaluate::GetConstantExtents(context, symbol)}) { |
919 | mutableObject.set_init(initialization.image.AsConstant( |
920 | context, *symbolType, std::nullopt, *extents)); |
921 | } else { |
922 | exprAnalyzer.Say(symbol.name(), |
923 | "internal: unknown shape for '%s' while constructing initializer from DATA"_err_en_US, |
924 | symbol.name()); |
925 | return; |
926 | } |
927 | } else { |
928 | exprAnalyzer.Say(symbol.name(), |
929 | "internal: no type for '%s' while constructing initializer from DATA"_err_en_US, |
930 | symbol.name()); |
931 | return; |
932 | } |
933 | if (!object->init()) { |
934 | exprAnalyzer.Say(symbol.name(), |
935 | "internal: could not construct an initializer from DATA statements for '%s'"_err_en_US, |
936 | symbol.name()); |
937 | } |
938 | } else { |
939 | CHECK(exprAnalyzer.context().AnyFatalError()); |
940 | } |
941 | } |
942 | |
943 | void ConvertToInitializers( |
944 | DataInitializations &inits, evaluate::ExpressionAnalyzer &exprAnalyzer) { |
945 | if (ProcessScopes( |
946 | exprAnalyzer.context().globalScope(), exprAnalyzer, inits)) { |
947 | for (auto &[symbolPtr, initialization] : inits) { |
948 | ConstructInitializer(symbol: *symbolPtr, initialization, exprAnalyzer); |
949 | } |
950 | } |
951 | } |
952 | } // namespace Fortran::semantics |
953 |
Definitions
- makeDefaultInitializationExplicit
- removeOriginalInits
- maxDataInitBytes
- ValueListIterator
- ValueListIterator
- hasFatalError
- IsAtEnd
- operator*
- LocateSource
- operator++
- GetValue
- GetConstant
- SetRepetitionCount
- DataInitializationCompiler
- DataInitializationCompiler
- inits
- HasSurplusValues
- Scan
- Scan
- Scan
- Scan
- Scan
- Scan
- InitDesignator
- ConvertElement
- InitElement
- AccumulateDataInitializations
- AccumulateDataInitializations
- HasDefaultInitialization
- PopulateWithComponentDefaults
- PopulateWithComponentDefaults
- CheckForOverlappingInitialization
- IncorporateExplicitInitialization
- ComputeMinElementBytes
- CombineEquivalencedInitialization
- MakeDefaultInitializationExplicit
- ProcessScopes
- ConstructInitializer
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more