/src/llvm-project/clang/lib/Sema/SemaStmtAttr.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===--- SemaStmtAttr.cpp - Statement Attribute Handling ------------------===// |
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 | | // This file implements stmt-related attribute processing. |
10 | | // |
11 | | //===----------------------------------------------------------------------===// |
12 | | |
13 | | #include "clang/AST/ASTContext.h" |
14 | | #include "clang/AST/EvaluatedExprVisitor.h" |
15 | | #include "clang/Basic/SourceManager.h" |
16 | | #include "clang/Basic/TargetInfo.h" |
17 | | #include "clang/Sema/DelayedDiagnostic.h" |
18 | | #include "clang/Sema/Lookup.h" |
19 | | #include "clang/Sema/ScopeInfo.h" |
20 | | #include "clang/Sema/SemaInternal.h" |
21 | | #include "llvm/ADT/StringExtras.h" |
22 | | #include <optional> |
23 | | |
24 | | using namespace clang; |
25 | | using namespace sema; |
26 | | |
27 | | static Attr *handleFallThroughAttr(Sema &S, Stmt *St, const ParsedAttr &A, |
28 | 0 | SourceRange Range) { |
29 | 0 | FallThroughAttr Attr(S.Context, A); |
30 | 0 | if (isa<SwitchCase>(St)) { |
31 | 0 | S.Diag(A.getRange().getBegin(), diag::err_fallthrough_attr_wrong_target) |
32 | 0 | << A << St->getBeginLoc(); |
33 | 0 | SourceLocation L = S.getLocForEndOfToken(Range.getEnd()); |
34 | 0 | S.Diag(L, diag::note_fallthrough_insert_semi_fixit) |
35 | 0 | << FixItHint::CreateInsertion(L, ";"); |
36 | 0 | return nullptr; |
37 | 0 | } |
38 | 0 | auto *FnScope = S.getCurFunction(); |
39 | 0 | if (FnScope->SwitchStack.empty()) { |
40 | 0 | S.Diag(A.getRange().getBegin(), diag::err_fallthrough_attr_outside_switch); |
41 | 0 | return nullptr; |
42 | 0 | } |
43 | | |
44 | | // If this is spelled as the standard C++17 attribute, but not in C++17, warn |
45 | | // about using it as an extension. |
46 | 0 | if (!S.getLangOpts().CPlusPlus17 && A.isCXX11Attribute() && |
47 | 0 | !A.getScopeName()) |
48 | 0 | S.Diag(A.getLoc(), diag::ext_cxx17_attr) << A; |
49 | |
|
50 | 0 | FnScope->setHasFallthroughStmt(); |
51 | 0 | return ::new (S.Context) FallThroughAttr(S.Context, A); |
52 | 0 | } |
53 | | |
54 | | static Attr *handleSuppressAttr(Sema &S, Stmt *St, const ParsedAttr &A, |
55 | 0 | SourceRange Range) { |
56 | 0 | if (A.getAttributeSpellingListIndex() == SuppressAttr::CXX11_gsl_suppress && |
57 | 0 | A.getNumArgs() < 1) { |
58 | | // Suppression attribute with GSL spelling requires at least 1 argument. |
59 | 0 | S.Diag(A.getLoc(), diag::err_attribute_too_few_arguments) << A << 1; |
60 | 0 | return nullptr; |
61 | 0 | } |
62 | | |
63 | 0 | std::vector<StringRef> DiagnosticIdentifiers; |
64 | 0 | for (unsigned I = 0, E = A.getNumArgs(); I != E; ++I) { |
65 | 0 | StringRef RuleName; |
66 | |
|
67 | 0 | if (!S.checkStringLiteralArgumentAttr(A, I, RuleName, nullptr)) |
68 | 0 | return nullptr; |
69 | | |
70 | 0 | DiagnosticIdentifiers.push_back(RuleName); |
71 | 0 | } |
72 | | |
73 | 0 | return ::new (S.Context) SuppressAttr( |
74 | 0 | S.Context, A, DiagnosticIdentifiers.data(), DiagnosticIdentifiers.size()); |
75 | 0 | } |
76 | | |
77 | | static Attr *handleLoopHintAttr(Sema &S, Stmt *St, const ParsedAttr &A, |
78 | 0 | SourceRange) { |
79 | 0 | IdentifierLoc *PragmaNameLoc = A.getArgAsIdent(0); |
80 | 0 | IdentifierLoc *OptionLoc = A.getArgAsIdent(1); |
81 | 0 | IdentifierLoc *StateLoc = A.getArgAsIdent(2); |
82 | 0 | Expr *ValueExpr = A.getArgAsExpr(3); |
83 | |
|
84 | 0 | StringRef PragmaName = |
85 | 0 | llvm::StringSwitch<StringRef>(PragmaNameLoc->Ident->getName()) |
86 | 0 | .Cases("unroll", "nounroll", "unroll_and_jam", "nounroll_and_jam", |
87 | 0 | PragmaNameLoc->Ident->getName()) |
88 | 0 | .Default("clang loop"); |
89 | | |
90 | | // This could be handled automatically by adding a Subjects definition in |
91 | | // Attr.td, but that would make the diagnostic behavior worse in this case |
92 | | // because the user spells this attribute as a pragma. |
93 | 0 | if (!isa<DoStmt, ForStmt, CXXForRangeStmt, WhileStmt>(St)) { |
94 | 0 | std::string Pragma = "#pragma " + std::string(PragmaName); |
95 | 0 | S.Diag(St->getBeginLoc(), diag::err_pragma_loop_precedes_nonloop) << Pragma; |
96 | 0 | return nullptr; |
97 | 0 | } |
98 | | |
99 | 0 | LoopHintAttr::OptionType Option; |
100 | 0 | LoopHintAttr::LoopHintState State; |
101 | |
|
102 | 0 | auto SetHints = [&Option, &State](LoopHintAttr::OptionType O, |
103 | 0 | LoopHintAttr::LoopHintState S) { |
104 | 0 | Option = O; |
105 | 0 | State = S; |
106 | 0 | }; |
107 | |
|
108 | 0 | if (PragmaName == "nounroll") { |
109 | 0 | SetHints(LoopHintAttr::Unroll, LoopHintAttr::Disable); |
110 | 0 | } else if (PragmaName == "unroll") { |
111 | | // #pragma unroll N |
112 | 0 | if (ValueExpr) |
113 | 0 | SetHints(LoopHintAttr::UnrollCount, LoopHintAttr::Numeric); |
114 | 0 | else |
115 | 0 | SetHints(LoopHintAttr::Unroll, LoopHintAttr::Enable); |
116 | 0 | } else if (PragmaName == "nounroll_and_jam") { |
117 | 0 | SetHints(LoopHintAttr::UnrollAndJam, LoopHintAttr::Disable); |
118 | 0 | } else if (PragmaName == "unroll_and_jam") { |
119 | | // #pragma unroll_and_jam N |
120 | 0 | if (ValueExpr) |
121 | 0 | SetHints(LoopHintAttr::UnrollAndJamCount, LoopHintAttr::Numeric); |
122 | 0 | else |
123 | 0 | SetHints(LoopHintAttr::UnrollAndJam, LoopHintAttr::Enable); |
124 | 0 | } else { |
125 | | // #pragma clang loop ... |
126 | 0 | assert(OptionLoc && OptionLoc->Ident && |
127 | 0 | "Attribute must have valid option info."); |
128 | 0 | Option = llvm::StringSwitch<LoopHintAttr::OptionType>( |
129 | 0 | OptionLoc->Ident->getName()) |
130 | 0 | .Case("vectorize", LoopHintAttr::Vectorize) |
131 | 0 | .Case("vectorize_width", LoopHintAttr::VectorizeWidth) |
132 | 0 | .Case("interleave", LoopHintAttr::Interleave) |
133 | 0 | .Case("vectorize_predicate", LoopHintAttr::VectorizePredicate) |
134 | 0 | .Case("interleave_count", LoopHintAttr::InterleaveCount) |
135 | 0 | .Case("unroll", LoopHintAttr::Unroll) |
136 | 0 | .Case("unroll_count", LoopHintAttr::UnrollCount) |
137 | 0 | .Case("pipeline", LoopHintAttr::PipelineDisabled) |
138 | 0 | .Case("pipeline_initiation_interval", |
139 | 0 | LoopHintAttr::PipelineInitiationInterval) |
140 | 0 | .Case("distribute", LoopHintAttr::Distribute) |
141 | 0 | .Default(LoopHintAttr::Vectorize); |
142 | 0 | if (Option == LoopHintAttr::VectorizeWidth) { |
143 | 0 | assert((ValueExpr || (StateLoc && StateLoc->Ident)) && |
144 | 0 | "Attribute must have a valid value expression or argument."); |
145 | 0 | if (ValueExpr && S.CheckLoopHintExpr(ValueExpr, St->getBeginLoc())) |
146 | 0 | return nullptr; |
147 | 0 | if (StateLoc && StateLoc->Ident && StateLoc->Ident->isStr("scalable")) |
148 | 0 | State = LoopHintAttr::ScalableWidth; |
149 | 0 | else |
150 | 0 | State = LoopHintAttr::FixedWidth; |
151 | 0 | } else if (Option == LoopHintAttr::InterleaveCount || |
152 | 0 | Option == LoopHintAttr::UnrollCount || |
153 | 0 | Option == LoopHintAttr::PipelineInitiationInterval) { |
154 | 0 | assert(ValueExpr && "Attribute must have a valid value expression."); |
155 | 0 | if (S.CheckLoopHintExpr(ValueExpr, St->getBeginLoc())) |
156 | 0 | return nullptr; |
157 | 0 | State = LoopHintAttr::Numeric; |
158 | 0 | } else if (Option == LoopHintAttr::Vectorize || |
159 | 0 | Option == LoopHintAttr::Interleave || |
160 | 0 | Option == LoopHintAttr::VectorizePredicate || |
161 | 0 | Option == LoopHintAttr::Unroll || |
162 | 0 | Option == LoopHintAttr::Distribute || |
163 | 0 | Option == LoopHintAttr::PipelineDisabled) { |
164 | 0 | assert(StateLoc && StateLoc->Ident && "Loop hint must have an argument"); |
165 | 0 | if (StateLoc->Ident->isStr("disable")) |
166 | 0 | State = LoopHintAttr::Disable; |
167 | 0 | else if (StateLoc->Ident->isStr("assume_safety")) |
168 | 0 | State = LoopHintAttr::AssumeSafety; |
169 | 0 | else if (StateLoc->Ident->isStr("full")) |
170 | 0 | State = LoopHintAttr::Full; |
171 | 0 | else if (StateLoc->Ident->isStr("enable")) |
172 | 0 | State = LoopHintAttr::Enable; |
173 | 0 | else |
174 | 0 | llvm_unreachable("bad loop hint argument"); |
175 | 0 | } else |
176 | 0 | llvm_unreachable("bad loop hint"); |
177 | 0 | } |
178 | | |
179 | 0 | return LoopHintAttr::CreateImplicit(S.Context, Option, State, ValueExpr, A); |
180 | 0 | } |
181 | | |
182 | | namespace { |
183 | | class CallExprFinder : public ConstEvaluatedExprVisitor<CallExprFinder> { |
184 | | bool FoundAsmStmt = false; |
185 | | std::vector<const CallExpr *> CallExprs; |
186 | | |
187 | | public: |
188 | | typedef ConstEvaluatedExprVisitor<CallExprFinder> Inherited; |
189 | | |
190 | 0 | CallExprFinder(Sema &S, const Stmt *St) : Inherited(S.Context) { Visit(St); } |
191 | | |
192 | 0 | bool foundCallExpr() { return !CallExprs.empty(); } |
193 | 0 | const std::vector<const CallExpr *> &getCallExprs() { return CallExprs; } |
194 | | |
195 | 0 | bool foundAsmStmt() { return FoundAsmStmt; } |
196 | | |
197 | 0 | void VisitCallExpr(const CallExpr *E) { CallExprs.push_back(E); } |
198 | | |
199 | 0 | void VisitAsmStmt(const AsmStmt *S) { FoundAsmStmt = true; } |
200 | | |
201 | 0 | void Visit(const Stmt *St) { |
202 | 0 | if (!St) |
203 | 0 | return; |
204 | 0 | ConstEvaluatedExprVisitor<CallExprFinder>::Visit(St); |
205 | 0 | } |
206 | | }; |
207 | | } // namespace |
208 | | |
209 | | static Attr *handleNoMergeAttr(Sema &S, Stmt *St, const ParsedAttr &A, |
210 | 0 | SourceRange Range) { |
211 | 0 | NoMergeAttr NMA(S.Context, A); |
212 | 0 | CallExprFinder CEF(S, St); |
213 | |
|
214 | 0 | if (!CEF.foundCallExpr() && !CEF.foundAsmStmt()) { |
215 | 0 | S.Diag(St->getBeginLoc(), diag::warn_attribute_ignored_no_calls_in_stmt) |
216 | 0 | << A; |
217 | 0 | return nullptr; |
218 | 0 | } |
219 | | |
220 | 0 | return ::new (S.Context) NoMergeAttr(S.Context, A); |
221 | 0 | } |
222 | | |
223 | | template <typename OtherAttr, int DiagIdx> |
224 | | static bool CheckStmtInlineAttr(Sema &SemaRef, const Stmt *OrigSt, |
225 | | const Stmt *CurSt, |
226 | 0 | const AttributeCommonInfo &A) { |
227 | 0 | CallExprFinder OrigCEF(SemaRef, OrigSt); |
228 | 0 | CallExprFinder CEF(SemaRef, CurSt); |
229 | | |
230 | | // If the call expressions lists are equal in size, we can skip |
231 | | // previously emitted diagnostics. However, if the statement has a pack |
232 | | // expansion, we have no way of telling which CallExpr is the instantiated |
233 | | // version of the other. In this case, we will end up re-diagnosing in the |
234 | | // instantiation. |
235 | | // ie: [[clang::always_inline]] non_dependent(), (other_call<Pack>()...) |
236 | | // will diagnose nondependent again. |
237 | 0 | bool CanSuppressDiag = |
238 | 0 | OrigSt && CEF.getCallExprs().size() == OrigCEF.getCallExprs().size(); |
239 | |
|
240 | 0 | if (!CEF.foundCallExpr()) { |
241 | 0 | return SemaRef.Diag(CurSt->getBeginLoc(), |
242 | 0 | diag::warn_attribute_ignored_no_calls_in_stmt) |
243 | 0 | << A; |
244 | 0 | } |
245 | | |
246 | 0 | for (const auto &Tup : |
247 | 0 | llvm::zip_longest(OrigCEF.getCallExprs(), CEF.getCallExprs())) { |
248 | | // If the original call expression already had a callee, we already |
249 | | // diagnosed this, so skip it here. We can't skip if there isn't a 1:1 |
250 | | // relationship between the two lists of call expressions. |
251 | 0 | if (!CanSuppressDiag || !(*std::get<0>(Tup))->getCalleeDecl()) { |
252 | 0 | const Decl *Callee = (*std::get<1>(Tup))->getCalleeDecl(); |
253 | 0 | if (Callee && |
254 | 0 | (Callee->hasAttr<OtherAttr>() || Callee->hasAttr<FlattenAttr>())) { |
255 | 0 | SemaRef.Diag(CurSt->getBeginLoc(), |
256 | 0 | diag::warn_function_stmt_attribute_precedence) |
257 | 0 | << A << (Callee->hasAttr<OtherAttr>() ? DiagIdx : 1); |
258 | 0 | SemaRef.Diag(Callee->getBeginLoc(), diag::note_conflicting_attribute); |
259 | 0 | } |
260 | 0 | } |
261 | 0 | } |
262 | |
|
263 | 0 | return false; |
264 | 0 | } Unexecuted instantiation: SemaStmtAttr.cpp:bool CheckStmtInlineAttr<clang::AlwaysInlineAttr, 0>(clang::Sema&, clang::Stmt const*, clang::Stmt const*, clang::AttributeCommonInfo const&) Unexecuted instantiation: SemaStmtAttr.cpp:bool CheckStmtInlineAttr<clang::NoInlineAttr, 2>(clang::Sema&, clang::Stmt const*, clang::Stmt const*, clang::AttributeCommonInfo const&) |
265 | | |
266 | | bool Sema::CheckNoInlineAttr(const Stmt *OrigSt, const Stmt *CurSt, |
267 | 0 | const AttributeCommonInfo &A) { |
268 | 0 | return CheckStmtInlineAttr<AlwaysInlineAttr, 0>(*this, OrigSt, CurSt, A); |
269 | 0 | } |
270 | | |
271 | | bool Sema::CheckAlwaysInlineAttr(const Stmt *OrigSt, const Stmt *CurSt, |
272 | 0 | const AttributeCommonInfo &A) { |
273 | 0 | return CheckStmtInlineAttr<NoInlineAttr, 2>(*this, OrigSt, CurSt, A); |
274 | 0 | } |
275 | | |
276 | | static Attr *handleNoInlineAttr(Sema &S, Stmt *St, const ParsedAttr &A, |
277 | 0 | SourceRange Range) { |
278 | 0 | NoInlineAttr NIA(S.Context, A); |
279 | 0 | if (!NIA.isClangNoInline()) { |
280 | 0 | S.Diag(St->getBeginLoc(), diag::warn_function_attribute_ignored_in_stmt) |
281 | 0 | << "[[clang::noinline]]"; |
282 | 0 | return nullptr; |
283 | 0 | } |
284 | | |
285 | 0 | if (S.CheckNoInlineAttr(/*OrigSt=*/nullptr, St, A)) |
286 | 0 | return nullptr; |
287 | | |
288 | 0 | return ::new (S.Context) NoInlineAttr(S.Context, A); |
289 | 0 | } |
290 | | |
291 | | static Attr *handleAlwaysInlineAttr(Sema &S, Stmt *St, const ParsedAttr &A, |
292 | 0 | SourceRange Range) { |
293 | 0 | AlwaysInlineAttr AIA(S.Context, A); |
294 | 0 | if (!AIA.isClangAlwaysInline()) { |
295 | 0 | S.Diag(St->getBeginLoc(), diag::warn_function_attribute_ignored_in_stmt) |
296 | 0 | << "[[clang::always_inline]]"; |
297 | 0 | return nullptr; |
298 | 0 | } |
299 | | |
300 | 0 | if (S.CheckAlwaysInlineAttr(/*OrigSt=*/nullptr, St, A)) |
301 | 0 | return nullptr; |
302 | | |
303 | 0 | return ::new (S.Context) AlwaysInlineAttr(S.Context, A); |
304 | 0 | } |
305 | | |
306 | | static Attr *handleMustTailAttr(Sema &S, Stmt *St, const ParsedAttr &A, |
307 | 0 | SourceRange Range) { |
308 | | // Validation is in Sema::ActOnAttributedStmt(). |
309 | 0 | return ::new (S.Context) MustTailAttr(S.Context, A); |
310 | 0 | } |
311 | | |
312 | | static Attr *handleLikely(Sema &S, Stmt *St, const ParsedAttr &A, |
313 | 0 | SourceRange Range) { |
314 | |
|
315 | 0 | if (!S.getLangOpts().CPlusPlus20 && A.isCXX11Attribute() && !A.getScopeName()) |
316 | 0 | S.Diag(A.getLoc(), diag::ext_cxx20_attr) << A << Range; |
317 | |
|
318 | 0 | return ::new (S.Context) LikelyAttr(S.Context, A); |
319 | 0 | } |
320 | | |
321 | | static Attr *handleUnlikely(Sema &S, Stmt *St, const ParsedAttr &A, |
322 | 0 | SourceRange Range) { |
323 | |
|
324 | 0 | if (!S.getLangOpts().CPlusPlus20 && A.isCXX11Attribute() && !A.getScopeName()) |
325 | 0 | S.Diag(A.getLoc(), diag::ext_cxx20_attr) << A << Range; |
326 | |
|
327 | 0 | return ::new (S.Context) UnlikelyAttr(S.Context, A); |
328 | 0 | } |
329 | | |
330 | | CodeAlignAttr *Sema::BuildCodeAlignAttr(const AttributeCommonInfo &CI, |
331 | 0 | Expr *E) { |
332 | 0 | if (!E->isValueDependent()) { |
333 | 0 | llvm::APSInt ArgVal; |
334 | 0 | ExprResult Res = VerifyIntegerConstantExpression(E, &ArgVal); |
335 | 0 | if (Res.isInvalid()) |
336 | 0 | return nullptr; |
337 | 0 | E = Res.get(); |
338 | | |
339 | | // This attribute requires an integer argument which is a constant power of |
340 | | // two between 1 and 4096 inclusive. |
341 | 0 | if (ArgVal < CodeAlignAttr::MinimumAlignment || |
342 | 0 | ArgVal > CodeAlignAttr::MaximumAlignment || !ArgVal.isPowerOf2()) { |
343 | 0 | if (std::optional<int64_t> Value = ArgVal.trySExtValue()) |
344 | 0 | Diag(CI.getLoc(), diag::err_attribute_power_of_two_in_range) |
345 | 0 | << CI << CodeAlignAttr::MinimumAlignment |
346 | 0 | << CodeAlignAttr::MaximumAlignment << Value.value(); |
347 | 0 | else |
348 | 0 | Diag(CI.getLoc(), diag::err_attribute_power_of_two_in_range) |
349 | 0 | << CI << CodeAlignAttr::MinimumAlignment |
350 | 0 | << CodeAlignAttr::MaximumAlignment << E; |
351 | 0 | return nullptr; |
352 | 0 | } |
353 | 0 | } |
354 | 0 | return new (Context) CodeAlignAttr(Context, CI, E); |
355 | 0 | } |
356 | | |
357 | 0 | static Attr *handleCodeAlignAttr(Sema &S, Stmt *St, const ParsedAttr &A) { |
358 | |
|
359 | 0 | Expr *E = A.getArgAsExpr(0); |
360 | 0 | return S.BuildCodeAlignAttr(A, E); |
361 | 0 | } |
362 | | |
363 | | // Diagnose non-identical duplicates as a 'conflicting' loop attributes |
364 | | // and suppress duplicate errors in cases where the two match. |
365 | | template <typename LoopAttrT> |
366 | 0 | static void CheckForDuplicateLoopAttrs(Sema &S, ArrayRef<const Attr *> Attrs) { |
367 | 0 | auto FindFunc = [](const Attr *A) { return isa<const LoopAttrT>(A); }; |
368 | 0 | const auto *FirstItr = std::find_if(Attrs.begin(), Attrs.end(), FindFunc); |
369 | |
|
370 | 0 | if (FirstItr == Attrs.end()) // no attributes found |
371 | 0 | return; |
372 | | |
373 | 0 | const auto *LastFoundItr = FirstItr; |
374 | 0 | std::optional<llvm::APSInt> FirstValue; |
375 | |
|
376 | 0 | const auto *CAFA = |
377 | 0 | dyn_cast<ConstantExpr>(cast<LoopAttrT>(*FirstItr)->getAlignment()); |
378 | | // Return early if first alignment expression is dependent (since we don't |
379 | | // know what the effective size will be), and skip the loop entirely. |
380 | 0 | if (!CAFA) |
381 | 0 | return; |
382 | | |
383 | 0 | while (Attrs.end() != (LastFoundItr = std::find_if(LastFoundItr + 1, |
384 | 0 | Attrs.end(), FindFunc))) { |
385 | 0 | const auto *CASA = |
386 | 0 | dyn_cast<ConstantExpr>(cast<LoopAttrT>(*LastFoundItr)->getAlignment()); |
387 | | // If the value is dependent, we can not test anything. |
388 | 0 | if (!CASA) |
389 | 0 | return; |
390 | | // Test the attribute values. |
391 | 0 | llvm::APSInt SecondValue = CASA->getResultAsAPSInt(); |
392 | 0 | if (!FirstValue) |
393 | 0 | FirstValue = CAFA->getResultAsAPSInt(); |
394 | |
|
395 | 0 | if (FirstValue != SecondValue) { |
396 | 0 | S.Diag((*LastFoundItr)->getLocation(), diag::err_loop_attr_conflict) |
397 | 0 | << *FirstItr; |
398 | 0 | S.Diag((*FirstItr)->getLocation(), diag::note_previous_attribute); |
399 | 0 | } |
400 | 0 | return; |
401 | 0 | } |
402 | 0 | } |
403 | | |
404 | | static Attr *handleMSConstexprAttr(Sema &S, Stmt *St, const ParsedAttr &A, |
405 | 0 | SourceRange Range) { |
406 | 0 | if (!S.getLangOpts().isCompatibleWithMSVC(LangOptions::MSVC2022_3)) { |
407 | 0 | S.Diag(A.getLoc(), diag::warn_unknown_attribute_ignored) |
408 | 0 | << A << A.getRange(); |
409 | 0 | return nullptr; |
410 | 0 | } |
411 | 0 | return ::new (S.Context) MSConstexprAttr(S.Context, A); |
412 | 0 | } |
413 | | |
414 | | #define WANT_STMT_MERGE_LOGIC |
415 | | #include "clang/Sema/AttrParsedAttrImpl.inc" |
416 | | #undef WANT_STMT_MERGE_LOGIC |
417 | | |
418 | | static void |
419 | | CheckForIncompatibleAttributes(Sema &S, |
420 | 0 | const SmallVectorImpl<const Attr *> &Attrs) { |
421 | | // The vast majority of attributed statements will only have one attribute |
422 | | // on them, so skip all of the checking in the common case. |
423 | 0 | if (Attrs.size() < 2) |
424 | 0 | return; |
425 | | |
426 | | // First, check for the easy cases that are table-generated for us. |
427 | 0 | if (!DiagnoseMutualExclusions(S, Attrs)) |
428 | 0 | return; |
429 | | |
430 | 0 | enum CategoryType { |
431 | | // For the following categories, they come in two variants: a state form and |
432 | | // a numeric form. The state form may be one of default, enable, and |
433 | | // disable. The numeric form provides an integer hint (for example, unroll |
434 | | // count) to the transformer. |
435 | 0 | Vectorize, |
436 | 0 | Interleave, |
437 | 0 | UnrollAndJam, |
438 | 0 | Pipeline, |
439 | | // For unroll, default indicates full unrolling rather than enabling the |
440 | | // transformation. |
441 | 0 | Unroll, |
442 | | // The loop distribution transformation only has a state form that is |
443 | | // exposed by #pragma clang loop distribute (enable | disable). |
444 | 0 | Distribute, |
445 | | // The vector predication only has a state form that is exposed by |
446 | | // #pragma clang loop vectorize_predicate (enable | disable). |
447 | 0 | VectorizePredicate, |
448 | | // This serves as a indicator to how many category are listed in this enum. |
449 | 0 | NumberOfCategories |
450 | 0 | }; |
451 | | // The following array accumulates the hints encountered while iterating |
452 | | // through the attributes to check for compatibility. |
453 | 0 | struct { |
454 | 0 | const LoopHintAttr *StateAttr; |
455 | 0 | const LoopHintAttr *NumericAttr; |
456 | 0 | } HintAttrs[CategoryType::NumberOfCategories] = {}; |
457 | |
|
458 | 0 | for (const auto *I : Attrs) { |
459 | 0 | const LoopHintAttr *LH = dyn_cast<LoopHintAttr>(I); |
460 | | |
461 | | // Skip non loop hint attributes |
462 | 0 | if (!LH) |
463 | 0 | continue; |
464 | | |
465 | 0 | CategoryType Category = CategoryType::NumberOfCategories; |
466 | 0 | LoopHintAttr::OptionType Option = LH->getOption(); |
467 | 0 | switch (Option) { |
468 | 0 | case LoopHintAttr::Vectorize: |
469 | 0 | case LoopHintAttr::VectorizeWidth: |
470 | 0 | Category = Vectorize; |
471 | 0 | break; |
472 | 0 | case LoopHintAttr::Interleave: |
473 | 0 | case LoopHintAttr::InterleaveCount: |
474 | 0 | Category = Interleave; |
475 | 0 | break; |
476 | 0 | case LoopHintAttr::Unroll: |
477 | 0 | case LoopHintAttr::UnrollCount: |
478 | 0 | Category = Unroll; |
479 | 0 | break; |
480 | 0 | case LoopHintAttr::UnrollAndJam: |
481 | 0 | case LoopHintAttr::UnrollAndJamCount: |
482 | 0 | Category = UnrollAndJam; |
483 | 0 | break; |
484 | 0 | case LoopHintAttr::Distribute: |
485 | | // Perform the check for duplicated 'distribute' hints. |
486 | 0 | Category = Distribute; |
487 | 0 | break; |
488 | 0 | case LoopHintAttr::PipelineDisabled: |
489 | 0 | case LoopHintAttr::PipelineInitiationInterval: |
490 | 0 | Category = Pipeline; |
491 | 0 | break; |
492 | 0 | case LoopHintAttr::VectorizePredicate: |
493 | 0 | Category = VectorizePredicate; |
494 | 0 | break; |
495 | 0 | }; |
496 | |
|
497 | 0 | assert(Category != NumberOfCategories && "Unhandled loop hint option"); |
498 | 0 | auto &CategoryState = HintAttrs[Category]; |
499 | 0 | const LoopHintAttr *PrevAttr; |
500 | 0 | if (Option == LoopHintAttr::Vectorize || |
501 | 0 | Option == LoopHintAttr::Interleave || Option == LoopHintAttr::Unroll || |
502 | 0 | Option == LoopHintAttr::UnrollAndJam || |
503 | 0 | Option == LoopHintAttr::VectorizePredicate || |
504 | 0 | Option == LoopHintAttr::PipelineDisabled || |
505 | 0 | Option == LoopHintAttr::Distribute) { |
506 | | // Enable|Disable|AssumeSafety hint. For example, vectorize(enable). |
507 | 0 | PrevAttr = CategoryState.StateAttr; |
508 | 0 | CategoryState.StateAttr = LH; |
509 | 0 | } else { |
510 | | // Numeric hint. For example, vectorize_width(8). |
511 | 0 | PrevAttr = CategoryState.NumericAttr; |
512 | 0 | CategoryState.NumericAttr = LH; |
513 | 0 | } |
514 | |
|
515 | 0 | PrintingPolicy Policy(S.Context.getLangOpts()); |
516 | 0 | SourceLocation OptionLoc = LH->getRange().getBegin(); |
517 | 0 | if (PrevAttr) |
518 | | // Cannot specify same type of attribute twice. |
519 | 0 | S.Diag(OptionLoc, diag::err_pragma_loop_compatibility) |
520 | 0 | << /*Duplicate=*/true << PrevAttr->getDiagnosticName(Policy) |
521 | 0 | << LH->getDiagnosticName(Policy); |
522 | |
|
523 | 0 | if (CategoryState.StateAttr && CategoryState.NumericAttr && |
524 | 0 | (Category == Unroll || Category == UnrollAndJam || |
525 | 0 | CategoryState.StateAttr->getState() == LoopHintAttr::Disable)) { |
526 | | // Disable hints are not compatible with numeric hints of the same |
527 | | // category. As a special case, numeric unroll hints are also not |
528 | | // compatible with enable or full form of the unroll pragma because these |
529 | | // directives indicate full unrolling. |
530 | 0 | S.Diag(OptionLoc, diag::err_pragma_loop_compatibility) |
531 | 0 | << /*Duplicate=*/false |
532 | 0 | << CategoryState.StateAttr->getDiagnosticName(Policy) |
533 | 0 | << CategoryState.NumericAttr->getDiagnosticName(Policy); |
534 | 0 | } |
535 | 0 | } |
536 | 0 | } |
537 | | |
538 | | static Attr *handleOpenCLUnrollHint(Sema &S, Stmt *St, const ParsedAttr &A, |
539 | 0 | SourceRange Range) { |
540 | | // Although the feature was introduced only in OpenCL C v2.0 s6.11.5, it's |
541 | | // useful for OpenCL 1.x too and doesn't require HW support. |
542 | | // opencl_unroll_hint can have 0 arguments (compiler |
543 | | // determines unrolling factor) or 1 argument (the unroll factor provided |
544 | | // by the user). |
545 | 0 | unsigned UnrollFactor = 0; |
546 | 0 | if (A.getNumArgs() == 1) { |
547 | 0 | Expr *E = A.getArgAsExpr(0); |
548 | 0 | std::optional<llvm::APSInt> ArgVal; |
549 | |
|
550 | 0 | if (!(ArgVal = E->getIntegerConstantExpr(S.Context))) { |
551 | 0 | S.Diag(A.getLoc(), diag::err_attribute_argument_type) |
552 | 0 | << A << AANT_ArgumentIntegerConstant << E->getSourceRange(); |
553 | 0 | return nullptr; |
554 | 0 | } |
555 | | |
556 | 0 | int Val = ArgVal->getSExtValue(); |
557 | 0 | if (Val <= 0) { |
558 | 0 | S.Diag(A.getRange().getBegin(), |
559 | 0 | diag::err_attribute_requires_positive_integer) |
560 | 0 | << A << /* positive */ 0; |
561 | 0 | return nullptr; |
562 | 0 | } |
563 | 0 | UnrollFactor = static_cast<unsigned>(Val); |
564 | 0 | } |
565 | | |
566 | 0 | return ::new (S.Context) OpenCLUnrollHintAttr(S.Context, A, UnrollFactor); |
567 | 0 | } |
568 | | |
569 | | static Attr *ProcessStmtAttribute(Sema &S, Stmt *St, const ParsedAttr &A, |
570 | 0 | SourceRange Range) { |
571 | 0 | if (A.isInvalid() || A.getKind() == ParsedAttr::IgnoredAttribute) |
572 | 0 | return nullptr; |
573 | | |
574 | | // Unknown attributes are automatically warned on. Target-specific attributes |
575 | | // which do not apply to the current target architecture are treated as |
576 | | // though they were unknown attributes. |
577 | 0 | const TargetInfo *Aux = S.Context.getAuxTargetInfo(); |
578 | 0 | if (A.getKind() == ParsedAttr::UnknownAttribute || |
579 | 0 | !(A.existsInTarget(S.Context.getTargetInfo()) || |
580 | 0 | (S.Context.getLangOpts().SYCLIsDevice && Aux && |
581 | 0 | A.existsInTarget(*Aux)))) { |
582 | 0 | S.Diag(A.getLoc(), A.isRegularKeywordAttribute() |
583 | 0 | ? (unsigned)diag::err_keyword_not_supported_on_target |
584 | 0 | : A.isDeclspecAttribute() |
585 | 0 | ? (unsigned)diag::warn_unhandled_ms_attribute_ignored |
586 | 0 | : (unsigned)diag::warn_unknown_attribute_ignored) |
587 | 0 | << A << A.getRange(); |
588 | 0 | return nullptr; |
589 | 0 | } |
590 | | |
591 | 0 | if (S.checkCommonAttributeFeatures(St, A)) |
592 | 0 | return nullptr; |
593 | | |
594 | 0 | switch (A.getKind()) { |
595 | 0 | case ParsedAttr::AT_AlwaysInline: |
596 | 0 | return handleAlwaysInlineAttr(S, St, A, Range); |
597 | 0 | case ParsedAttr::AT_FallThrough: |
598 | 0 | return handleFallThroughAttr(S, St, A, Range); |
599 | 0 | case ParsedAttr::AT_LoopHint: |
600 | 0 | return handleLoopHintAttr(S, St, A, Range); |
601 | 0 | case ParsedAttr::AT_OpenCLUnrollHint: |
602 | 0 | return handleOpenCLUnrollHint(S, St, A, Range); |
603 | 0 | case ParsedAttr::AT_Suppress: |
604 | 0 | return handleSuppressAttr(S, St, A, Range); |
605 | 0 | case ParsedAttr::AT_NoMerge: |
606 | 0 | return handleNoMergeAttr(S, St, A, Range); |
607 | 0 | case ParsedAttr::AT_NoInline: |
608 | 0 | return handleNoInlineAttr(S, St, A, Range); |
609 | 0 | case ParsedAttr::AT_MustTail: |
610 | 0 | return handleMustTailAttr(S, St, A, Range); |
611 | 0 | case ParsedAttr::AT_Likely: |
612 | 0 | return handleLikely(S, St, A, Range); |
613 | 0 | case ParsedAttr::AT_Unlikely: |
614 | 0 | return handleUnlikely(S, St, A, Range); |
615 | 0 | case ParsedAttr::AT_CodeAlign: |
616 | 0 | return handleCodeAlignAttr(S, St, A); |
617 | 0 | case ParsedAttr::AT_MSConstexpr: |
618 | 0 | return handleMSConstexprAttr(S, St, A, Range); |
619 | 0 | default: |
620 | | // N.B., ClangAttrEmitter.cpp emits a diagnostic helper that ensures a |
621 | | // declaration attribute is not written on a statement, but this code is |
622 | | // needed for attributes in Attr.td that do not list any subjects. |
623 | 0 | S.Diag(A.getRange().getBegin(), diag::err_decl_attribute_invalid_on_stmt) |
624 | 0 | << A << A.isRegularKeywordAttribute() << St->getBeginLoc(); |
625 | 0 | return nullptr; |
626 | 0 | } |
627 | 0 | } |
628 | | |
629 | | void Sema::ProcessStmtAttributes(Stmt *S, const ParsedAttributes &InAttrs, |
630 | 0 | SmallVectorImpl<const Attr *> &OutAttrs) { |
631 | 0 | for (const ParsedAttr &AL : InAttrs) { |
632 | 0 | if (const Attr *A = ProcessStmtAttribute(*this, S, AL, InAttrs.Range)) |
633 | 0 | OutAttrs.push_back(A); |
634 | 0 | } |
635 | |
|
636 | 0 | CheckForIncompatibleAttributes(*this, OutAttrs); |
637 | 0 | CheckForDuplicateLoopAttrs<CodeAlignAttr>(*this, OutAttrs); |
638 | 0 | } |
639 | | |
640 | 0 | bool Sema::CheckRebuiltStmtAttributes(ArrayRef<const Attr *> Attrs) { |
641 | 0 | CheckForDuplicateLoopAttrs<CodeAlignAttr>(*this, Attrs); |
642 | 0 | return false; |
643 | 0 | } |