1 | //===-- lib/Support/Fortran-features.cpp ------------------------*- C++ -*-===// |
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/Support/Fortran-features.h" |
10 | #include "flang/Common/idioms.h" |
11 | #include "flang/Parser/characters.h" |
12 | #include "flang/Support/Fortran.h" |
13 | #include <string> |
14 | #include <string_view> |
15 | |
16 | namespace Fortran::common { |
17 | |
18 | static std::vector<std::string_view> SplitCamelCase(std::string_view x) { |
19 | std::vector<std::string_view> result; |
20 | // NB, we start at 1 because the first character is never a word boundary. |
21 | size_t xSize{x.size()}, wordStart{0}, wordEnd{1}; |
22 | for (; wordEnd < xSize; ++wordEnd) { |
23 | // Identify when wordEnd is at the start of a new word. |
24 | if ((!parser::IsUpperCaseLetter(x[wordEnd - 1]) && |
25 | parser::IsUpperCaseLetter(x[wordEnd])) || |
26 | // ACCUsage => ACC-Usage, CComment => C-Comment, etc. |
27 | (parser::IsUpperCaseLetter(x[wordEnd]) && wordEnd + 1 < xSize && |
28 | parser::IsLowerCaseLetter(x[wordEnd + 1]))) { |
29 | result.push_back(x.substr(wordStart, wordEnd - wordStart)); |
30 | wordStart = wordEnd; |
31 | } |
32 | } |
33 | // We went one past the end of the last word. |
34 | result.push_back(x.substr(wordStart, wordEnd - wordStart)); |
35 | return result; |
36 | } |
37 | |
38 | // Namespace for helper functions for parsing Cli options used instead of static |
39 | // so that there can be unit tests for this function. |
40 | namespace details { |
41 | std::string CamelCaseToLowerCaseHyphenated(std::string_view x) { |
42 | std::vector<std::string_view> words{SplitCamelCase(x)}; |
43 | std::string result{}; |
44 | result.reserve(x.size() + words.size() + 1); |
45 | for (size_t i{0}; i < words.size(); ++i) { |
46 | std::string word{parser::ToLowerCaseLetters(words[i])}; |
47 | result += i == 0 ? "" : "-" ; |
48 | result += word; |
49 | } |
50 | return result; |
51 | } |
52 | } // namespace details |
53 | |
54 | LanguageFeatureControl::LanguageFeatureControl() { |
55 | // Initialize the bidirectional maps with the default spellings. |
56 | cliOptions_.reserve(LanguageFeature_enumSize + UsageWarning_enumSize); |
57 | ForEachLanguageFeature([&](auto feature) { |
58 | std::string_view name{Fortran::common::EnumToString(feature)}; |
59 | std::string cliOption{details::CamelCaseToLowerCaseHyphenated(name)}; |
60 | cliOptions_.insert({cliOption, {feature}}); |
61 | languageFeatureCliCanonicalSpelling_[EnumToInt(feature)] = |
62 | std::string_view{cliOption}; |
63 | }); |
64 | |
65 | ForEachUsageWarning([&](auto warning) { |
66 | std::string_view name{Fortran::common::EnumToString(warning)}; |
67 | std::string cliOption{details::CamelCaseToLowerCaseHyphenated(name)}; |
68 | cliOptions_.insert({cliOption, {warning}}); |
69 | usageWarningCliCanonicalSpelling_[EnumToInt(warning)] = |
70 | std::string_view{cliOption}; |
71 | }); |
72 | |
73 | // These features must be explicitly enabled by command line options. |
74 | disable_.set(LanguageFeature::OldDebugLines); |
75 | disable_.set(LanguageFeature::OpenACC); |
76 | disable_.set(LanguageFeature::OpenMP); |
77 | disable_.set(LanguageFeature::CUDA); // !@cuf |
78 | disable_.set(LanguageFeature::CudaManaged); |
79 | disable_.set(LanguageFeature::CudaUnified); |
80 | disable_.set(LanguageFeature::ImplicitNoneTypeNever); |
81 | disable_.set(LanguageFeature::ImplicitNoneTypeAlways); |
82 | disable_.set(LanguageFeature::ImplicitNoneExternal); |
83 | disable_.set(LanguageFeature::DefaultSave); |
84 | disable_.set(LanguageFeature::SaveMainProgram); |
85 | // These features, if enabled, conflict with valid standard usage, |
86 | // so there are disabled here by default. |
87 | disable_.set(LanguageFeature::BackslashEscapes); |
88 | disable_.set(LanguageFeature::LogicalAbbreviations); |
89 | disable_.set(LanguageFeature::XOROperator); |
90 | disable_.set(LanguageFeature::OldStyleParameter); |
91 | // Possibly an accidental "feature" of nvfortran. |
92 | disable_.set(LanguageFeature::AssumedRankPassedToNonAssumedRank); |
93 | // These warnings are enabled by default, but only because they used |
94 | // to be unconditional. TODO: prune this list |
95 | warnLanguage_.set(LanguageFeature::ExponentMatchingKindParam); |
96 | warnLanguage_.set(LanguageFeature::RedundantAttribute); |
97 | warnLanguage_.set(LanguageFeature::SubroutineAndFunctionSpecifics); |
98 | warnLanguage_.set(LanguageFeature::EmptySequenceType); |
99 | warnLanguage_.set(LanguageFeature::NonSequenceCrayPointee); |
100 | warnLanguage_.set(LanguageFeature::BranchIntoConstruct); |
101 | warnLanguage_.set(LanguageFeature::BadBranchTarget); |
102 | warnLanguage_.set(LanguageFeature::HollerithPolymorphic); |
103 | warnLanguage_.set(LanguageFeature::ListDirectedSize); |
104 | warnLanguage_.set(LanguageFeature::IgnoreIrrelevantAttributes); |
105 | warnLanguage_.set(LanguageFeature::AmbiguousStructureConstructor); |
106 | warnUsage_.set(UsageWarning::ShortArrayActual); |
107 | warnUsage_.set(UsageWarning::FoldingException); |
108 | warnUsage_.set(UsageWarning::FoldingAvoidsRuntimeCrash); |
109 | warnUsage_.set(UsageWarning::FoldingValueChecks); |
110 | warnUsage_.set(UsageWarning::FoldingFailure); |
111 | warnUsage_.set(UsageWarning::FoldingLimit); |
112 | warnUsage_.set(UsageWarning::Interoperability); |
113 | // CharacterInteroperability warnings about length are off by default |
114 | warnUsage_.set(UsageWarning::Bounds); |
115 | warnUsage_.set(UsageWarning::Preprocessing); |
116 | warnUsage_.set(UsageWarning::Scanning); |
117 | warnUsage_.set(UsageWarning::OpenAccUsage); |
118 | warnUsage_.set(UsageWarning::ProcPointerCompatibility); |
119 | warnUsage_.set(UsageWarning::VoidMold); |
120 | warnUsage_.set(UsageWarning::KnownBadImplicitInterface); |
121 | warnUsage_.set(UsageWarning::EmptyCase); |
122 | warnUsage_.set(UsageWarning::CaseOverflow); |
123 | warnUsage_.set(UsageWarning::CUDAUsage); |
124 | warnUsage_.set(UsageWarning::IgnoreTKRUsage); |
125 | warnUsage_.set(UsageWarning::ExternalInterfaceMismatch); |
126 | warnUsage_.set(UsageWarning::DefinedOperatorArgs); |
127 | warnUsage_.set(UsageWarning::Final); |
128 | warnUsage_.set(UsageWarning::ZeroDoStep); |
129 | warnUsage_.set(UsageWarning::UnusedForallIndex); |
130 | warnUsage_.set(UsageWarning::OpenMPUsage); |
131 | warnUsage_.set(UsageWarning::DataLength); |
132 | warnUsage_.set(UsageWarning::IgnoredDirective); |
133 | warnUsage_.set(UsageWarning::HomonymousSpecific); |
134 | warnUsage_.set(UsageWarning::HomonymousResult); |
135 | warnUsage_.set(UsageWarning::IgnoredIntrinsicFunctionType); |
136 | warnUsage_.set(UsageWarning::PreviousScalarUse); |
137 | warnUsage_.set(UsageWarning::RedeclaredInaccessibleComponent); |
138 | warnUsage_.set(UsageWarning::ImplicitShared); |
139 | warnUsage_.set(UsageWarning::IndexVarRedefinition); |
140 | warnUsage_.set(UsageWarning::IncompatibleImplicitInterfaces); |
141 | warnUsage_.set(UsageWarning::VectorSubscriptFinalization); |
142 | warnUsage_.set(UsageWarning::UndefinedFunctionResult); |
143 | warnUsage_.set(UsageWarning::UselessIomsg); |
144 | warnUsage_.set(UsageWarning::UnsignedLiteralTruncation); |
145 | warnUsage_.set(UsageWarning::NullActualForDefaultIntentAllocatable); |
146 | warnUsage_.set(UsageWarning::UseAssociationIntoSameNameSubprogram); |
147 | warnUsage_.set(UsageWarning::HostAssociatedIntentOutInSpecExpr); |
148 | warnUsage_.set(UsageWarning::NonVolatilePointerToVolatile); |
149 | // New warnings, on by default |
150 | warnLanguage_.set(LanguageFeature::SavedLocalInSpecExpr); |
151 | warnLanguage_.set(LanguageFeature::NullActualForAllocatable); |
152 | } |
153 | |
154 | // Take a string from the Cli and apply it to the LanguageFeatureControl. |
155 | bool LanguageFeatureControl::ApplyCliOption(std::string input) { |
156 | bool negated{false}; |
157 | if (input.size() > 3 && input.substr(0, 3) == "no-" ) { |
158 | negated = true; |
159 | input = input.substr(3); |
160 | } |
161 | if (auto it{cliOptions_.find(input)}; it != cliOptions_.end()) { |
162 | if (std::holds_alternative<LanguageFeature>(it->second)) { |
163 | EnableWarning(std::get<LanguageFeature>(it->second), !negated); |
164 | return true; |
165 | } |
166 | if (std::holds_alternative<UsageWarning>(it->second)) { |
167 | EnableWarning(std::get<UsageWarning>(it->second), !negated); |
168 | return true; |
169 | } |
170 | } |
171 | return false; |
172 | } |
173 | |
174 | void LanguageFeatureControl::ReplaceCliCanonicalSpelling( |
175 | LanguageFeature f, std::string input) { |
176 | std::string_view &old{languageFeatureCliCanonicalSpelling_[EnumToInt(f)]}; |
177 | cliOptions_.erase(std::string{old}); |
178 | languageFeatureCliCanonicalSpelling_[EnumToInt(f)] = input; |
179 | cliOptions_.insert({input, {f}}); |
180 | } |
181 | |
182 | void LanguageFeatureControl::ReplaceCliCanonicalSpelling( |
183 | UsageWarning w, std::string input) { |
184 | std::string_view &old{usageWarningCliCanonicalSpelling_[EnumToInt(w)]}; |
185 | cliOptions_.erase(std::string{old}); |
186 | usageWarningCliCanonicalSpelling_[EnumToInt(w)] = input; |
187 | cliOptions_.insert({input, {w}}); |
188 | } |
189 | |
190 | std::vector<const char *> LanguageFeatureControl::GetNames( |
191 | LogicalOperator opr) const { |
192 | std::vector<const char *> result; |
193 | result.push_back(AsFortran(opr)); |
194 | if (opr == LogicalOperator::Neqv && IsEnabled(LanguageFeature::XOROperator)) { |
195 | result.push_back(".xor." ); |
196 | } |
197 | if (IsEnabled(LanguageFeature::LogicalAbbreviations)) { |
198 | switch (opr) { |
199 | SWITCH_COVERS_ALL_CASES |
200 | case LogicalOperator::And: |
201 | result.push_back(".a." ); |
202 | break; |
203 | case LogicalOperator::Or: |
204 | result.push_back(".o." ); |
205 | break; |
206 | case LogicalOperator::Not: |
207 | result.push_back(".n." ); |
208 | break; |
209 | case LogicalOperator::Neqv: |
210 | if (IsEnabled(LanguageFeature::XOROperator)) { |
211 | result.push_back(".x." ); |
212 | } |
213 | break; |
214 | case LogicalOperator::Eqv: |
215 | break; |
216 | } |
217 | } |
218 | return result; |
219 | } |
220 | |
221 | std::vector<const char *> LanguageFeatureControl::GetNames( |
222 | RelationalOperator opr) const { |
223 | switch (opr) { |
224 | SWITCH_COVERS_ALL_CASES |
225 | case RelationalOperator::LT: |
226 | return {".lt." , "<" }; |
227 | case RelationalOperator::LE: |
228 | return {".le." , "<=" }; |
229 | case RelationalOperator::EQ: |
230 | return {".eq." , "==" }; |
231 | case RelationalOperator::GE: |
232 | return {".ge." , ">=" }; |
233 | case RelationalOperator::GT: |
234 | return {".gt." , ">" }; |
235 | case RelationalOperator::NE: |
236 | if (IsEnabled(LanguageFeature::AlternativeNE)) { |
237 | return {".ne." , "/=" , "<>" }; |
238 | } else { |
239 | return {".ne." , "/=" }; |
240 | } |
241 | } |
242 | } |
243 | |
244 | void LanguageFeatureControl::WarnOnAllNonstandard(bool yes) { |
245 | warnAllLanguage_ = yes; |
246 | warnLanguage_.reset(); |
247 | if (yes) { |
248 | disableAllWarnings_ = false; |
249 | warnLanguage_.flip(); |
250 | // These three features do not need to be warned about, |
251 | // but we do want their feature flags. |
252 | warnLanguage_.set(LanguageFeature::OpenMP, false); |
253 | warnLanguage_.set(LanguageFeature::OpenACC, false); |
254 | warnLanguage_.set(LanguageFeature::CUDA, false); |
255 | } |
256 | } |
257 | |
258 | void LanguageFeatureControl::WarnOnAllUsage(bool yes) { |
259 | warnAllUsage_ = yes; |
260 | warnUsage_.reset(); |
261 | if (yes) { |
262 | disableAllWarnings_ = false; |
263 | warnUsage_.flip(); |
264 | } |
265 | } |
266 | } // namespace Fortran::common |
267 | |