1 | //===-- lib/Semantics/semantics.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 | #include "flang/Semantics/semantics.h" |
10 | #include "assignment.h" |
11 | #include "canonicalize-acc.h" |
12 | #include "canonicalize-directives.h" |
13 | #include "canonicalize-do.h" |
14 | #include "canonicalize-omp.h" |
15 | #include "check-acc-structure.h" |
16 | #include "check-allocate.h" |
17 | #include "check-arithmeticif.h" |
18 | #include "check-case.h" |
19 | #include "check-coarray.h" |
20 | #include "check-cuda.h" |
21 | #include "check-data.h" |
22 | #include "check-deallocate.h" |
23 | #include "check-declarations.h" |
24 | #include "check-do-forall.h" |
25 | #include "check-if-stmt.h" |
26 | #include "check-io.h" |
27 | #include "check-namelist.h" |
28 | #include "check-nullify.h" |
29 | #include "check-omp-structure.h" |
30 | #include "check-purity.h" |
31 | #include "check-return.h" |
32 | #include "check-select-rank.h" |
33 | #include "check-select-type.h" |
34 | #include "check-stop.h" |
35 | #include "compute-offsets.h" |
36 | #include "mod-file.h" |
37 | #include "resolve-labels.h" |
38 | #include "resolve-names.h" |
39 | #include "rewrite-parse-tree.h" |
40 | #include "flang/Parser/parse-tree-visitor.h" |
41 | #include "flang/Parser/tools.h" |
42 | #include "flang/Semantics/expression.h" |
43 | #include "flang/Semantics/scope.h" |
44 | #include "flang/Semantics/symbol.h" |
45 | #include "flang/Support/default-kinds.h" |
46 | #include "llvm/Support/raw_ostream.h" |
47 | #include "llvm/TargetParser/Host.h" |
48 | #include "llvm/TargetParser/Triple.h" |
49 | |
50 | namespace Fortran::semantics { |
51 | |
52 | using NameToSymbolMap = std::multimap<parser::CharBlock, SymbolRef>; |
53 | static void DoDumpSymbols(llvm::raw_ostream &, const Scope &, int indent = 0); |
54 | static void PutIndent(llvm::raw_ostream &, int indent); |
55 | |
56 | static void GetSymbolNames(const Scope &scope, NameToSymbolMap &symbols) { |
57 | // Finds all symbol names in the scope without collecting duplicates. |
58 | for (const auto &pair : scope) { |
59 | symbols.emplace(pair.second->name(), *pair.second); |
60 | } |
61 | for (const auto &pair : scope.commonBlocks()) { |
62 | symbols.emplace(pair.second->name(), *pair.second); |
63 | } |
64 | for (const auto &child : scope.children()) { |
65 | GetSymbolNames(child, symbols); |
66 | } |
67 | } |
68 | |
69 | // A parse tree visitor that calls Enter/Leave functions from each checker |
70 | // class C supplied as template parameters. Enter is called before the node's |
71 | // children are visited, Leave is called after. No two checkers may have the |
72 | // same Enter or Leave function. Each checker must be constructible from |
73 | // SemanticsContext and have BaseChecker as a virtual base class. |
74 | template <typename... C> |
75 | class SemanticsVisitor : public virtual BaseChecker, public virtual C... { |
76 | public: |
77 | using BaseChecker::Enter; |
78 | using BaseChecker::Leave; |
79 | using C::Enter...; |
80 | using C::Leave...; |
81 | SemanticsVisitor(SemanticsContext &context) |
82 | : C{context}..., context_{context} {} |
83 | |
84 | template <typename N> bool Pre(const N &node) { |
85 | if constexpr (common::HasMember<const N *, ConstructNode>) { |
86 | context_.PushConstruct(node); |
87 | } |
88 | Enter(node); |
89 | return true; |
90 | } |
91 | template <typename N> void Post(const N &node) { |
92 | Leave(node); |
93 | if constexpr (common::HasMember<const N *, ConstructNode>) { |
94 | context_.PopConstruct(); |
95 | } |
96 | } |
97 | |
98 | template <typename T> bool Pre(const parser::Statement<T> &node) { |
99 | context_.set_location(node.source); |
100 | Enter(node); |
101 | return true; |
102 | } |
103 | template <typename T> bool Pre(const parser::UnlabeledStatement<T> &node) { |
104 | context_.set_location(node.source); |
105 | Enter(node); |
106 | return true; |
107 | } |
108 | template <typename T> void Post(const parser::Statement<T> &node) { |
109 | Leave(node); |
110 | context_.set_location(std::nullopt); |
111 | } |
112 | template <typename T> void Post(const parser::UnlabeledStatement<T> &node) { |
113 | Leave(node); |
114 | context_.set_location(std::nullopt); |
115 | } |
116 | |
117 | bool Walk(const parser::Program &program) { |
118 | parser::Walk(program, *this); |
119 | return !context_.AnyFatalError(); |
120 | } |
121 | |
122 | private: |
123 | SemanticsContext &context_; |
124 | }; |
125 | |
126 | class MiscChecker : public virtual BaseChecker { |
127 | public: |
128 | explicit MiscChecker(SemanticsContext &context) : context_{context} {} |
129 | void Leave(const parser::EntryStmt &) { |
130 | if (!context_.constructStack().empty()) { // C1571 |
131 | context_.Say("ENTRY may not appear in an executable construct"_err_en_US ); |
132 | } |
133 | } |
134 | void Leave(const parser::AssignStmt &stmt) { |
135 | CheckAssignGotoName(name: std::get<parser::Name>(stmt.t)); |
136 | } |
137 | void Leave(const parser::AssignedGotoStmt &stmt) { |
138 | CheckAssignGotoName(name: std::get<parser::Name>(stmt.t)); |
139 | } |
140 | |
141 | private: |
142 | void CheckAssignGotoName(const parser::Name &name) { |
143 | if (context_.HasError(name.symbol)) { |
144 | return; |
145 | } |
146 | const Symbol &symbol{DEREF(name.symbol)}; |
147 | auto type{evaluate::DynamicType::From(symbol)}; |
148 | if (!IsVariableName(symbol) || symbol.Rank() != 0 || !type || |
149 | type->category() != TypeCategory::Integer || |
150 | type->kind() != |
151 | context_.defaultKinds().GetDefaultKind(TypeCategory::Integer)) { |
152 | context_ |
153 | .Say(name.source, |
154 | "'%s' must be a default integer scalar variable"_err_en_US , |
155 | name.source) |
156 | .Attach(symbol.name(), "Declaration of '%s'"_en_US , symbol.name()); |
157 | } |
158 | } |
159 | |
160 | SemanticsContext &context_; |
161 | }; |
162 | |
163 | static void WarnUndefinedFunctionResult( |
164 | SemanticsContext &context, const Scope &scope) { |
165 | auto WasDefined{[&context](const Symbol &symbol) { |
166 | return context.IsSymbolDefined(symbol) || |
167 | IsInitialized(symbol, /*ignoreDataStatements=*/true, |
168 | /*ignoreAllocatable=*/true, /*ignorePointer=*/true); |
169 | }}; |
170 | if (const Symbol * symbol{scope.symbol()}) { |
171 | if (const auto *subp{symbol->detailsIf<SubprogramDetails>()}) { |
172 | if (subp->isFunction() && !subp->isInterface() && !subp->stmtFunction()) { |
173 | bool wasDefined{WasDefined(subp->result())}; |
174 | if (!wasDefined) { |
175 | // Definitions of ENTRY result variables also count. |
176 | for (const auto &pair : scope) { |
177 | const Symbol &local{*pair.second}; |
178 | if (IsFunctionResult(local) && WasDefined(local)) { |
179 | wasDefined = true; |
180 | break; |
181 | } |
182 | } |
183 | if (!wasDefined) { |
184 | context.Warn(common::UsageWarning::UndefinedFunctionResult, |
185 | symbol->name(), "Function result is never defined"_warn_en_US ); |
186 | } |
187 | } |
188 | } |
189 | } |
190 | } |
191 | if (!scope.IsModuleFile()) { |
192 | for (const Scope &child : scope.children()) { |
193 | WarnUndefinedFunctionResult(context, child); |
194 | } |
195 | } |
196 | } |
197 | |
198 | using StatementSemanticsPass1 = ExprChecker; |
199 | using StatementSemanticsPass2 = SemanticsVisitor<AllocateChecker, |
200 | ArithmeticIfStmtChecker, AssignmentChecker, CaseChecker, CoarrayChecker, |
201 | DataChecker, DeallocateChecker, DoForallChecker, IfStmtChecker, IoChecker, |
202 | MiscChecker, NamelistChecker, NullifyChecker, PurityChecker, |
203 | ReturnStmtChecker, SelectRankConstructChecker, SelectTypeChecker, |
204 | StopChecker>; |
205 | |
206 | static bool PerformStatementSemantics( |
207 | SemanticsContext &context, parser::Program &program) { |
208 | ResolveNames(context, program, context.globalScope()); |
209 | RewriteParseTree(context, program); |
210 | ComputeOffsets(context, context.globalScope()); |
211 | CheckDeclarations(context); |
212 | StatementSemanticsPass1{context}.Walk(program); |
213 | StatementSemanticsPass2 pass2{context}; |
214 | pass2.Walk(program); |
215 | if (context.languageFeatures().IsEnabled(common::LanguageFeature::OpenACC)) { |
216 | SemanticsVisitor<AccStructureChecker>{context}.Walk(program); |
217 | } |
218 | if (context.languageFeatures().IsEnabled(common::LanguageFeature::OpenMP)) { |
219 | SemanticsVisitor<OmpStructureChecker>{context}.Walk(program); |
220 | } |
221 | if (context.languageFeatures().IsEnabled(common::LanguageFeature::CUDA)) { |
222 | SemanticsVisitor<CUDAChecker>{context}.Walk(program); |
223 | } |
224 | if (!context.messages().AnyFatalError()) { |
225 | WarnUndefinedFunctionResult(context, context.globalScope()); |
226 | } |
227 | if (!context.AnyFatalError()) { |
228 | pass2.CompileDataInitializationsIntoInitializers(); |
229 | } |
230 | return !context.AnyFatalError(); |
231 | } |
232 | |
233 | /// This class keeps track of the common block appearances with the biggest size |
234 | /// and with an initial value (if any) in a program. This allows reporting |
235 | /// conflicting initialization and warning about appearances of a same |
236 | /// named common block with different sizes. The biggest common block size and |
237 | /// initialization (if any) can later be provided so that lowering can generate |
238 | /// the correct symbol size and initial values, even when named common blocks |
239 | /// appears with different sizes and are initialized outside of block data. |
240 | class CommonBlockMap { |
241 | private: |
242 | struct CommonBlockInfo { |
243 | // Common block symbol for the appearance with the biggest size. |
244 | SymbolRef biggestSize; |
245 | // Common block symbol for the appearance with the initialized members (if |
246 | // any). |
247 | std::optional<SymbolRef> initialization; |
248 | }; |
249 | |
250 | public: |
251 | void MapCommonBlockAndCheckConflicts( |
252 | SemanticsContext &context, const Symbol &common) { |
253 | const Symbol *isInitialized{CommonBlockIsInitialized(common)}; |
254 | // Merge common according to the name they will have in the object files. |
255 | // This allows merging BIND(C) and non BIND(C) common block instead of |
256 | // later crashing. This "merge" matches what ifort/gfortran/nvfortran are |
257 | // doing and what a linker would do if the definition were in distinct |
258 | // files. |
259 | std::string commonName{ |
260 | GetCommonBlockObjectName(common, context.underscoring())}; |
261 | auto [it, firstAppearance] = commonBlocks_.insert(x: {commonName, |
262 | isInitialized ? CommonBlockInfo{common, common} |
263 | : CommonBlockInfo{common, std::nullopt}}); |
264 | if (!firstAppearance) { |
265 | CommonBlockInfo &info{it->second}; |
266 | if (isInitialized) { |
267 | if (info.initialization.has_value() && |
268 | &**info.initialization != &common) { |
269 | // Use the location of the initialization in the error message because |
270 | // common block symbols may have no location if they are blank |
271 | // commons. |
272 | const Symbol &previousInit{ |
273 | DEREF(CommonBlockIsInitialized(common: **info.initialization))}; |
274 | context |
275 | .Say(isInitialized->name(), |
276 | "Multiple initialization of COMMON block /%s/"_err_en_US , |
277 | common.name()) |
278 | .Attach(previousInit.name(), |
279 | "Previous initialization of COMMON block /%s/"_en_US , |
280 | common.name()); |
281 | } else { |
282 | info.initialization = common; |
283 | } |
284 | } |
285 | if (common.size() != info.biggestSize->size() && !common.name().empty()) { |
286 | if (auto *msg{context.Warn(common::LanguageFeature::DistinctCommonSizes, |
287 | common.name(), |
288 | "A named COMMON block should have the same size everywhere it appears (%zd bytes here)"_port_en_US , |
289 | common.size())}) { |
290 | msg->Attach(info.biggestSize->name(), |
291 | "Previously defined with a size of %zd bytes"_en_US , |
292 | info.biggestSize->size()); |
293 | } |
294 | } |
295 | if (common.size() > info.biggestSize->size()) { |
296 | info.biggestSize = common; |
297 | } |
298 | } |
299 | } |
300 | |
301 | CommonBlockList GetCommonBlocks() const { |
302 | CommonBlockList result; |
303 | for (const auto &[_, blockInfo] : commonBlocks_) { |
304 | result.emplace_back( |
305 | std::make_pair(blockInfo.initialization ? *blockInfo.initialization |
306 | : blockInfo.biggestSize, |
307 | blockInfo.biggestSize->size())); |
308 | } |
309 | return result; |
310 | } |
311 | |
312 | private: |
313 | /// Return the symbol of an initialized member if a COMMON block |
314 | /// is initalized. Otherwise, return nullptr. |
315 | static Symbol *CommonBlockIsInitialized(const Symbol &common) { |
316 | const auto &commonDetails = |
317 | common.get<Fortran::semantics::CommonBlockDetails>(); |
318 | |
319 | for (const auto &member : commonDetails.objects()) { |
320 | if (IsInitialized(*member)) { |
321 | return &*member; |
322 | } |
323 | } |
324 | |
325 | // Common block may be initialized via initialized variables that are in an |
326 | // equivalence with the common block members. |
327 | for (const Fortran::semantics::EquivalenceSet &set : |
328 | common.owner().equivalenceSets()) { |
329 | for (const Fortran::semantics::EquivalenceObject &obj : set) { |
330 | if (!obj.symbol.test( |
331 | Fortran::semantics::Symbol::Flag::CompilerCreated)) { |
332 | if (FindCommonBlockContaining(obj.symbol) == &common && |
333 | IsInitialized(obj.symbol)) { |
334 | return &obj.symbol; |
335 | } |
336 | } |
337 | } |
338 | } |
339 | return nullptr; |
340 | } |
341 | |
342 | std::map<std::string, CommonBlockInfo> commonBlocks_; |
343 | }; |
344 | |
345 | SemanticsContext::SemanticsContext( |
346 | const common::IntrinsicTypeDefaultKinds &defaultKinds, |
347 | const common::LanguageFeatureControl &languageFeatures, |
348 | const common::LangOptions &langOpts, |
349 | parser::AllCookedSources &allCookedSources) |
350 | : defaultKinds_{defaultKinds}, languageFeatures_{languageFeatures}, |
351 | langOpts_{langOpts}, allCookedSources_{allCookedSources}, |
352 | intrinsics_{evaluate::IntrinsicProcTable::Configure(defaultKinds_)}, |
353 | globalScope_{*this}, intrinsicModulesScope_{globalScope_.MakeScope( |
354 | Scope::Kind::IntrinsicModules, nullptr)}, |
355 | foldingContext_{parser::ContextualMessages{&messages_}, defaultKinds_, |
356 | intrinsics_, targetCharacteristics_, languageFeatures_, tempNames_} {} |
357 | |
358 | SemanticsContext::~SemanticsContext() {} |
359 | |
360 | int SemanticsContext::GetDefaultKind(TypeCategory category) const { |
361 | return defaultKinds_.GetDefaultKind(category); |
362 | } |
363 | |
364 | const DeclTypeSpec &SemanticsContext::MakeNumericType( |
365 | TypeCategory category, int kind) { |
366 | if (kind == 0) { |
367 | kind = GetDefaultKind(category); |
368 | } |
369 | return globalScope_.MakeNumericType(category, KindExpr{kind}); |
370 | } |
371 | const DeclTypeSpec &SemanticsContext::MakeLogicalType(int kind) { |
372 | if (kind == 0) { |
373 | kind = GetDefaultKind(TypeCategory::Logical); |
374 | } |
375 | return globalScope_.MakeLogicalType(KindExpr{kind}); |
376 | } |
377 | |
378 | bool SemanticsContext::AnyFatalError() const { |
379 | return !messages_.empty() && |
380 | (warningsAreErrors_ || messages_.AnyFatalError()); |
381 | } |
382 | bool SemanticsContext::HasError(const Symbol &symbol) { |
383 | return errorSymbols_.count(symbol) > 0; |
384 | } |
385 | bool SemanticsContext::HasError(const Symbol *symbol) { |
386 | return !symbol || HasError(*symbol); |
387 | } |
388 | bool SemanticsContext::HasError(const parser::Name &name) { |
389 | return HasError(name.symbol); |
390 | } |
391 | void SemanticsContext::SetError(const Symbol &symbol, bool value) { |
392 | if (value) { |
393 | CheckError(symbol); |
394 | errorSymbols_.emplace(symbol); |
395 | } |
396 | } |
397 | void SemanticsContext::CheckError(const Symbol &symbol) { |
398 | if (!AnyFatalError()) { |
399 | std::string buf; |
400 | llvm::raw_string_ostream ss{buf}; |
401 | ss << symbol; |
402 | common::die( |
403 | "No error was reported but setting error on: %s" , ss.str().c_str()); |
404 | } |
405 | } |
406 | |
407 | bool SemanticsContext::ScopeIndexComparator::operator()( |
408 | parser::CharBlock x, parser::CharBlock y) const { |
409 | return x.begin() < y.begin() || |
410 | (x.begin() == y.begin() && x.size() > y.size()); |
411 | } |
412 | |
413 | auto SemanticsContext::SearchScopeIndex(parser::CharBlock source) |
414 | -> ScopeIndex::iterator { |
415 | if (!scopeIndex_.empty()) { |
416 | auto iter{scopeIndex_.upper_bound(source)}; |
417 | auto begin{scopeIndex_.begin()}; |
418 | do { |
419 | --iter; |
420 | if (iter->first.Contains(source)) { |
421 | return iter; |
422 | } |
423 | } while (iter != begin); |
424 | } |
425 | return scopeIndex_.end(); |
426 | } |
427 | |
428 | const Scope &SemanticsContext::FindScope(parser::CharBlock source) const { |
429 | return const_cast<SemanticsContext *>(this)->FindScope(source); |
430 | } |
431 | |
432 | Scope &SemanticsContext::FindScope(parser::CharBlock source) { |
433 | if (auto iter{SearchScopeIndex(source)}; iter != scopeIndex_.end()) { |
434 | return iter->second; |
435 | } else { |
436 | common::die( |
437 | "SemanticsContext::FindScope(): invalid source location for '%s'" , |
438 | source.ToString().c_str()); |
439 | } |
440 | } |
441 | |
442 | void SemanticsContext::UpdateScopeIndex( |
443 | Scope &scope, parser::CharBlock newSource) { |
444 | if (scope.sourceRange().empty()) { |
445 | scopeIndex_.emplace(newSource, scope); |
446 | } else if (!scope.sourceRange().Contains(newSource)) { |
447 | auto iter{SearchScopeIndex(scope.sourceRange())}; |
448 | CHECK(iter != scopeIndex_.end()); |
449 | while (&iter->second != &scope) { |
450 | CHECK(iter != scopeIndex_.begin()); |
451 | --iter; |
452 | } |
453 | scopeIndex_.erase(iter); |
454 | scopeIndex_.emplace(newSource, scope); |
455 | } |
456 | } |
457 | |
458 | bool SemanticsContext::IsInModuleFile(parser::CharBlock source) const { |
459 | for (const Scope *scope{&FindScope(source)}; !scope->IsGlobal(); |
460 | scope = &scope->parent()) { |
461 | if (scope->IsModuleFile()) { |
462 | return true; |
463 | } |
464 | } |
465 | return false; |
466 | } |
467 | |
468 | void SemanticsContext::PopConstruct() { |
469 | CHECK(!constructStack_.empty()); |
470 | constructStack_.pop_back(); |
471 | } |
472 | |
473 | parser::Message *SemanticsContext::CheckIndexVarRedefine( |
474 | const parser::CharBlock &location, const Symbol &variable, |
475 | parser::MessageFixedText &&message) { |
476 | const Symbol &symbol{ResolveAssociations(variable)}; |
477 | auto it{activeIndexVars_.find(symbol)}; |
478 | if (it != activeIndexVars_.end()) { |
479 | std::string kind{EnumToString(it->second.kind)}; |
480 | return &Say(location, std::move(message), kind, symbol.name()) |
481 | .Attach( |
482 | it->second.location, "Enclosing %s construct"_en_US , kind); |
483 | } else { |
484 | return nullptr; |
485 | } |
486 | } |
487 | |
488 | void SemanticsContext::WarnIndexVarRedefine( |
489 | const parser::CharBlock &location, const Symbol &variable) { |
490 | if (ShouldWarn(common::UsageWarning::IndexVarRedefinition)) { |
491 | if (auto *msg{CheckIndexVarRedefine(location, variable, |
492 | "Possible redefinition of %s variable '%s'"_warn_en_US )}) { |
493 | msg->set_usageWarning(common::UsageWarning::IndexVarRedefinition); |
494 | } |
495 | } |
496 | } |
497 | |
498 | void SemanticsContext::CheckIndexVarRedefine( |
499 | const parser::CharBlock &location, const Symbol &variable) { |
500 | CheckIndexVarRedefine( |
501 | location, variable, "Cannot redefine %s variable '%s'"_err_en_US ); |
502 | } |
503 | |
504 | void SemanticsContext::CheckIndexVarRedefine(const parser::Variable &variable) { |
505 | if (const Symbol * entity{GetLastName(variable).symbol}) { |
506 | CheckIndexVarRedefine(variable.GetSource(), *entity); |
507 | } |
508 | } |
509 | |
510 | void SemanticsContext::CheckIndexVarRedefine(const parser::Name &name) { |
511 | if (const Symbol * entity{name.symbol}) { |
512 | CheckIndexVarRedefine(name.source, *entity); |
513 | } |
514 | } |
515 | |
516 | void SemanticsContext::ActivateIndexVar( |
517 | const parser::Name &name, IndexVarKind kind) { |
518 | CheckIndexVarRedefine(name); |
519 | if (const Symbol * indexVar{name.symbol}) { |
520 | activeIndexVars_.emplace( |
521 | ResolveAssociations(*indexVar), IndexVarInfo{name.source, kind}); |
522 | } |
523 | } |
524 | |
525 | void SemanticsContext::DeactivateIndexVar(const parser::Name &name) { |
526 | if (Symbol * indexVar{name.symbol}) { |
527 | auto it{activeIndexVars_.find(ResolveAssociations(*indexVar))}; |
528 | if (it != activeIndexVars_.end() && it->second.location == name.source) { |
529 | activeIndexVars_.erase(it); |
530 | } |
531 | } |
532 | } |
533 | |
534 | SymbolVector SemanticsContext::GetIndexVars(IndexVarKind kind) { |
535 | SymbolVector result; |
536 | for (const auto &[symbol, info] : activeIndexVars_) { |
537 | if (info.kind == kind) { |
538 | result.push_back(symbol); |
539 | } |
540 | } |
541 | return result; |
542 | } |
543 | |
544 | SourceName SemanticsContext::SaveTempName(std::string &&name) { |
545 | return {*tempNames_.emplace(std::move(name)).first}; |
546 | } |
547 | |
548 | SourceName SemanticsContext::GetTempName(const Scope &scope) { |
549 | for (const auto &str : tempNames_) { |
550 | if (IsTempName(str)) { |
551 | SourceName name{str}; |
552 | if (scope.find(name) == scope.end()) { |
553 | return name; |
554 | } |
555 | } |
556 | } |
557 | return SaveTempName(".F18."s + std::to_string(tempNames_.size())); |
558 | } |
559 | |
560 | bool SemanticsContext::IsTempName(const std::string &name) { |
561 | return name.size() > 5 && name.substr(0, 5) == ".F18." ; |
562 | } |
563 | |
564 | Scope *SemanticsContext::GetBuiltinModule(const char *name) { |
565 | return ModFileReader{*this}.Read(SourceName{name, std::strlen(name)}, |
566 | true /*intrinsic*/, nullptr, /*silent=*/true); |
567 | } |
568 | |
569 | void SemanticsContext::UseFortranBuiltinsModule() { |
570 | if (builtinsScope_ == nullptr) { |
571 | builtinsScope_ = GetBuiltinModule("__fortran_builtins" ); |
572 | if (builtinsScope_) { |
573 | intrinsics_.SupplyBuiltins(*builtinsScope_); |
574 | } |
575 | } |
576 | } |
577 | |
578 | void SemanticsContext::UsePPCBuiltinTypesModule() { |
579 | if (ppcBuiltinTypesScope_ == nullptr) { |
580 | ppcBuiltinTypesScope_ = GetBuiltinModule("__ppc_types" ); |
581 | } |
582 | } |
583 | |
584 | const Scope &SemanticsContext::GetCUDABuiltinsScope() { |
585 | if (!cudaBuiltinsScope_) { |
586 | cudaBuiltinsScope_ = GetBuiltinModule("__cuda_builtins" ); |
587 | CHECK(cudaBuiltinsScope_.value() != nullptr); |
588 | } |
589 | return **cudaBuiltinsScope_; |
590 | } |
591 | |
592 | const Scope &SemanticsContext::GetCUDADeviceScope() { |
593 | if (!cudaDeviceScope_) { |
594 | cudaDeviceScope_ = GetBuiltinModule("cudadevice" ); |
595 | CHECK(cudaDeviceScope_.value() != nullptr); |
596 | } |
597 | return **cudaDeviceScope_; |
598 | } |
599 | |
600 | void SemanticsContext::UsePPCBuiltinsModule() { |
601 | if (ppcBuiltinsScope_ == nullptr) { |
602 | ppcBuiltinsScope_ = GetBuiltinModule("__ppc_intrinsics" ); |
603 | } |
604 | } |
605 | |
606 | parser::Program &SemanticsContext::SaveParseTree(parser::Program &&tree) { |
607 | return modFileParseTrees_.emplace_back(std::move(tree)); |
608 | } |
609 | |
610 | bool Semantics::Perform() { |
611 | // Implicitly USE the __Fortran_builtins module so that special types |
612 | // (e.g., __builtin_team_type) are available to semantics, esp. for |
613 | // intrinsic checking. |
614 | if (!program_.v.empty()) { |
615 | const auto *frontModule{std::get_if<common::Indirection<parser::Module>>( |
616 | &program_.v.front().u)}; |
617 | if (frontModule && |
618 | (std::get<parser::Statement<parser::ModuleStmt>>(frontModule->value().t) |
619 | .statement.v.source == "__fortran_builtins" || |
620 | std::get<parser::Statement<parser::ModuleStmt>>( |
621 | frontModule->value().t) |
622 | .statement.v.source == "__ppc_types" )) { |
623 | // Don't try to read the builtins module when we're actually building it. |
624 | } else if (frontModule && |
625 | (std::get<parser::Statement<parser::ModuleStmt>>(frontModule->value().t) |
626 | .statement.v.source == "__ppc_intrinsics" || |
627 | std::get<parser::Statement<parser::ModuleStmt>>( |
628 | frontModule->value().t) |
629 | .statement.v.source == "mma" )) { |
630 | // The derived type definition for the vectors is needed. |
631 | context_.UsePPCBuiltinTypesModule(); |
632 | } else { |
633 | context_.UseFortranBuiltinsModule(); |
634 | llvm::Triple targetTriple{llvm::Triple( |
635 | llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple()))}; |
636 | // Only use __ppc_intrinsics module when targetting PowerPC arch |
637 | if (context_.targetCharacteristics().isPPC()) { |
638 | context_.UsePPCBuiltinTypesModule(); |
639 | context_.UsePPCBuiltinsModule(); |
640 | } |
641 | } |
642 | } |
643 | return ValidateLabels(context_, program_) && |
644 | parser::CanonicalizeDo(program_) && // force line break |
645 | CanonicalizeAcc(context_.messages(), program_) && |
646 | CanonicalizeOmp(context_.messages(), program_) && |
647 | CanonicalizeCUDA(program_) && |
648 | PerformStatementSemantics(context_, program_) && |
649 | CanonicalizeDirectives(context_.messages(), program_) && |
650 | ModFileWriter{context_} |
651 | .set_hermeticModuleFileOutput(hermeticModuleFileOutput_) |
652 | .WriteAll(); |
653 | } |
654 | |
655 | void Semantics::EmitMessages(llvm::raw_ostream &os) { |
656 | // Resolve the CharBlock locations of the Messages to ProvenanceRanges |
657 | // so messages from parsing and semantics are intermixed in source order. |
658 | context_.messages().ResolveProvenances(context_.allCookedSources()); |
659 | context_.messages().Emit(os, context_.allCookedSources()); |
660 | } |
661 | |
662 | void SemanticsContext::DumpSymbols(llvm::raw_ostream &os) { |
663 | DoDumpSymbols(os, globalScope()); |
664 | } |
665 | |
666 | ProgramTree &SemanticsContext::SaveProgramTree(ProgramTree &&tree) { |
667 | return programTrees_.emplace_back(std::move(tree)); |
668 | } |
669 | |
670 | void Semantics::DumpSymbols(llvm::raw_ostream &os) { context_.DumpSymbols(os); } |
671 | |
672 | void Semantics::DumpSymbolsSources(llvm::raw_ostream &os) const { |
673 | NameToSymbolMap symbols; |
674 | GetSymbolNames(context_.globalScope(), symbols); |
675 | const parser::AllCookedSources &allCooked{context_.allCookedSources()}; |
676 | for (const auto &pair : symbols) { |
677 | const Symbol &symbol{pair.second}; |
678 | if (auto sourceInfo{allCooked.GetSourcePositionRange(symbol.name())}) { |
679 | os << symbol.name().ToString() << ": " << sourceInfo->first.path << ", " |
680 | << sourceInfo->first.line << ", " << sourceInfo->first.column << "-" |
681 | << sourceInfo->second.column << "\n" ; |
682 | } else if (symbol.has<semantics::UseDetails>()) { |
683 | os << symbol.name().ToString() << ": " |
684 | << symbol.GetUltimate().owner().symbol()->name().ToString() << "\n" ; |
685 | } |
686 | } |
687 | } |
688 | |
689 | void DoDumpSymbols(llvm::raw_ostream &os, const Scope &scope, int indent) { |
690 | PutIndent(os, indent); |
691 | os << Scope::EnumToString(scope.kind()) << " scope:" ; |
692 | if (const auto *symbol{scope.symbol()}) { |
693 | os << ' ' << symbol->name(); |
694 | } |
695 | if (scope.alignment().has_value()) { |
696 | os << " size=" << scope.size() << " alignment=" << *scope.alignment(); |
697 | } |
698 | if (scope.derivedTypeSpec()) { |
699 | os << " instantiation of " << *scope.derivedTypeSpec(); |
700 | } |
701 | os << " sourceRange=" << scope.sourceRange().size() << " bytes\n" ; |
702 | ++indent; |
703 | for (const auto &pair : scope) { |
704 | const auto &symbol{*pair.second}; |
705 | PutIndent(os, indent); |
706 | os << symbol << '\n'; |
707 | if (const auto *details{symbol.detailsIf<GenericDetails>()}) { |
708 | if (const auto &type{details->derivedType()}) { |
709 | PutIndent(os, indent); |
710 | os << *type << '\n'; |
711 | } |
712 | } |
713 | } |
714 | if (!scope.equivalenceSets().empty()) { |
715 | PutIndent(os, indent); |
716 | os << "Equivalence Sets:" ; |
717 | for (const auto &set : scope.equivalenceSets()) { |
718 | os << ' '; |
719 | char sep = '('; |
720 | for (const auto &object : set) { |
721 | os << sep << object.AsFortran(); |
722 | sep = ','; |
723 | } |
724 | os << ')'; |
725 | } |
726 | os << '\n'; |
727 | } |
728 | if (!scope.crayPointers().empty()) { |
729 | PutIndent(os, indent); |
730 | os << "Cray Pointers:" ; |
731 | for (const auto &[pointee, pointer] : scope.crayPointers()) { |
732 | os << " (" << pointer->name() << ',' << pointee << ')'; |
733 | } |
734 | os << '\n'; |
735 | } |
736 | for (const auto &pair : scope.commonBlocks()) { |
737 | const auto &symbol{*pair.second}; |
738 | PutIndent(os, indent); |
739 | os << symbol << '\n'; |
740 | } |
741 | for (const auto &child : scope.children()) { |
742 | DoDumpSymbols(os, child, indent); |
743 | } |
744 | --indent; |
745 | } |
746 | |
747 | static void PutIndent(llvm::raw_ostream &os, int indent) { |
748 | for (int i = 0; i < indent; ++i) { |
749 | os << " " ; |
750 | } |
751 | } |
752 | |
753 | void SemanticsContext::MapCommonBlockAndCheckConflicts(const Symbol &common) { |
754 | if (!commonBlockMap_) { |
755 | commonBlockMap_ = std::make_unique<CommonBlockMap>(); |
756 | } |
757 | commonBlockMap_->MapCommonBlockAndCheckConflicts(*this, common); |
758 | } |
759 | |
760 | CommonBlockList SemanticsContext::GetCommonBlocks() const { |
761 | if (commonBlockMap_) { |
762 | return commonBlockMap_->GetCommonBlocks(); |
763 | } |
764 | return {}; |
765 | } |
766 | |
767 | void SemanticsContext::NoteDefinedSymbol(const Symbol &symbol) { |
768 | isDefined_.insert(symbol); |
769 | } |
770 | |
771 | bool SemanticsContext::IsSymbolDefined(const Symbol &symbol) const { |
772 | return isDefined_.find(symbol) != isDefined_.end(); |
773 | } |
774 | |
775 | } // namespace Fortran::semantics |
776 | |