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
24using namespace clang;
25using namespace sema;
26
27static Attr *handleFallThroughAttr(Sema &S, Stmt *St, const ParsedAttr &A,
28 SourceRange Range) {
29 FallThroughAttr Attr(S.Context, A);
30 if (isa<SwitchCase>(Val: St)) {
31 S.Diag(A.getRange().getBegin(), diag::err_fallthrough_attr_wrong_target)
32 << A << St->getBeginLoc();
33 SourceLocation L = S.getLocForEndOfToken(Loc: Range.getEnd());
34 S.Diag(L, diag::note_fallthrough_insert_semi_fixit)
35 << FixItHint::CreateInsertion(L, ";");
36 return nullptr;
37 }
38 auto *FnScope = S.getCurFunction();
39 if (FnScope->SwitchStack.empty()) {
40 S.Diag(A.getRange().getBegin(), diag::err_fallthrough_attr_outside_switch);
41 return nullptr;
42 }
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 if (!S.getLangOpts().CPlusPlus17 && A.isCXX11Attribute() &&
47 !A.getScopeName())
48 S.Diag(A.getLoc(), diag::ext_cxx17_attr) << A;
49
50 FnScope->setHasFallthroughStmt();
51 return ::new (S.Context) FallThroughAttr(S.Context, A);
52}
53
54static Attr *handleSuppressAttr(Sema &S, Stmt *St, const ParsedAttr &A,
55 SourceRange Range) {
56 if (A.getAttributeSpellingListIndex() == SuppressAttr::CXX11_gsl_suppress &&
57 A.getNumArgs() < 1) {
58 // Suppression attribute with GSL spelling requires at least 1 argument.
59 S.Diag(A.getLoc(), diag::err_attribute_too_few_arguments) << A << 1;
60 return nullptr;
61 }
62
63 std::vector<StringRef> DiagnosticIdentifiers;
64 for (unsigned I = 0, E = A.getNumArgs(); I != E; ++I) {
65 StringRef RuleName;
66
67 if (!S.checkStringLiteralArgumentAttr(Attr: A, ArgNum: I, Str&: RuleName, ArgLocation: nullptr))
68 return nullptr;
69
70 DiagnosticIdentifiers.push_back(x: RuleName);
71 }
72
73 return ::new (S.Context) SuppressAttr(
74 S.Context, A, DiagnosticIdentifiers.data(), DiagnosticIdentifiers.size());
75}
76
77static Attr *handleLoopHintAttr(Sema &S, Stmt *St, const ParsedAttr &A,
78 SourceRange) {
79 IdentifierLoc *PragmaNameLoc = A.getArgAsIdent(Arg: 0);
80 IdentifierLoc *OptionLoc = A.getArgAsIdent(Arg: 1);
81 IdentifierLoc *StateLoc = A.getArgAsIdent(Arg: 2);
82 Expr *ValueExpr = A.getArgAsExpr(Arg: 3);
83
84 StringRef PragmaName =
85 llvm::StringSwitch<StringRef>(PragmaNameLoc->Ident->getName())
86 .Cases(S0: "unroll", S1: "nounroll", S2: "unroll_and_jam", S3: "nounroll_and_jam",
87 Value: PragmaNameLoc->Ident->getName())
88 .Default(Value: "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 if (!isa<DoStmt, ForStmt, CXXForRangeStmt, WhileStmt>(Val: St)) {
94 std::string Pragma = "#pragma " + std::string(PragmaName);
95 S.Diag(St->getBeginLoc(), diag::err_pragma_loop_precedes_nonloop) << Pragma;
96 return nullptr;
97 }
98
99 LoopHintAttr::OptionType Option;
100 LoopHintAttr::LoopHintState State;
101
102 auto SetHints = [&Option, &State](LoopHintAttr::OptionType O,
103 LoopHintAttr::LoopHintState S) {
104 Option = O;
105 State = S;
106 };
107
108 if (PragmaName == "nounroll") {
109 SetHints(LoopHintAttr::Unroll, LoopHintAttr::Disable);
110 } else if (PragmaName == "unroll") {
111 // #pragma unroll N
112 if (ValueExpr)
113 SetHints(LoopHintAttr::UnrollCount, LoopHintAttr::Numeric);
114 else
115 SetHints(LoopHintAttr::Unroll, LoopHintAttr::Enable);
116 } else if (PragmaName == "nounroll_and_jam") {
117 SetHints(LoopHintAttr::UnrollAndJam, LoopHintAttr::Disable);
118 } else if (PragmaName == "unroll_and_jam") {
119 // #pragma unroll_and_jam N
120 if (ValueExpr)
121 SetHints(LoopHintAttr::UnrollAndJamCount, LoopHintAttr::Numeric);
122 else
123 SetHints(LoopHintAttr::UnrollAndJam, LoopHintAttr::Enable);
124 } else {
125 // #pragma clang loop ...
126 assert(OptionLoc && OptionLoc->Ident &&
127 "Attribute must have valid option info.");
128 Option = llvm::StringSwitch<LoopHintAttr::OptionType>(
129 OptionLoc->Ident->getName())
130 .Case("vectorize", LoopHintAttr::Vectorize)
131 .Case("vectorize_width", LoopHintAttr::VectorizeWidth)
132 .Case("interleave", LoopHintAttr::Interleave)
133 .Case("vectorize_predicate", LoopHintAttr::VectorizePredicate)
134 .Case("interleave_count", LoopHintAttr::InterleaveCount)
135 .Case("unroll", LoopHintAttr::Unroll)
136 .Case("unroll_count", LoopHintAttr::UnrollCount)
137 .Case("pipeline", LoopHintAttr::PipelineDisabled)
138 .Case("pipeline_initiation_interval",
139 LoopHintAttr::PipelineInitiationInterval)
140 .Case("distribute", LoopHintAttr::Distribute)
141 .Default(LoopHintAttr::Vectorize);
142 if (Option == LoopHintAttr::VectorizeWidth) {
143 assert((ValueExpr || (StateLoc && StateLoc->Ident)) &&
144 "Attribute must have a valid value expression or argument.");
145 if (ValueExpr && S.CheckLoopHintExpr(E: ValueExpr, Loc: St->getBeginLoc()))
146 return nullptr;
147 if (StateLoc && StateLoc->Ident && StateLoc->Ident->isStr("scalable"))
148 State = LoopHintAttr::ScalableWidth;
149 else
150 State = LoopHintAttr::FixedWidth;
151 } else if (Option == LoopHintAttr::InterleaveCount ||
152 Option == LoopHintAttr::UnrollCount ||
153 Option == LoopHintAttr::PipelineInitiationInterval) {
154 assert(ValueExpr && "Attribute must have a valid value expression.");
155 if (S.CheckLoopHintExpr(E: ValueExpr, Loc: St->getBeginLoc()))
156 return nullptr;
157 State = LoopHintAttr::Numeric;
158 } else if (Option == LoopHintAttr::Vectorize ||
159 Option == LoopHintAttr::Interleave ||
160 Option == LoopHintAttr::VectorizePredicate ||
161 Option == LoopHintAttr::Unroll ||
162 Option == LoopHintAttr::Distribute ||
163 Option == LoopHintAttr::PipelineDisabled) {
164 assert(StateLoc && StateLoc->Ident && "Loop hint must have an argument");
165 if (StateLoc->Ident->isStr(Str: "disable"))
166 State = LoopHintAttr::Disable;
167 else if (StateLoc->Ident->isStr(Str: "assume_safety"))
168 State = LoopHintAttr::AssumeSafety;
169 else if (StateLoc->Ident->isStr(Str: "full"))
170 State = LoopHintAttr::Full;
171 else if (StateLoc->Ident->isStr(Str: "enable"))
172 State = LoopHintAttr::Enable;
173 else
174 llvm_unreachable("bad loop hint argument");
175 } else
176 llvm_unreachable("bad loop hint");
177 }
178
179 return LoopHintAttr::CreateImplicit(S.Context, Option, State, ValueExpr, A);
180}
181
182namespace {
183class CallExprFinder : public ConstEvaluatedExprVisitor<CallExprFinder> {
184 bool FoundAsmStmt = false;
185 std::vector<const CallExpr *> CallExprs;
186
187public:
188 typedef ConstEvaluatedExprVisitor<CallExprFinder> Inherited;
189
190 CallExprFinder(Sema &S, const Stmt *St) : Inherited(S.Context) { Visit(St); }
191
192 bool foundCallExpr() { return !CallExprs.empty(); }
193 const std::vector<const CallExpr *> &getCallExprs() { return CallExprs; }
194
195 bool foundAsmStmt() { return FoundAsmStmt; }
196
197 void VisitCallExpr(const CallExpr *E) { CallExprs.push_back(x: E); }
198
199 void VisitAsmStmt(const AsmStmt *S) { FoundAsmStmt = true; }
200
201 void Visit(const Stmt *St) {
202 if (!St)
203 return;
204 ConstEvaluatedExprVisitor<CallExprFinder>::Visit(S: St);
205 }
206};
207} // namespace
208
209static Attr *handleNoMergeAttr(Sema &S, Stmt *St, const ParsedAttr &A,
210 SourceRange Range) {
211 NoMergeAttr NMA(S.Context, A);
212 CallExprFinder CEF(S, St);
213
214 if (!CEF.foundCallExpr() && !CEF.foundAsmStmt()) {
215 S.Diag(St->getBeginLoc(), diag::warn_attribute_ignored_no_calls_in_stmt)
216 << A;
217 return nullptr;
218 }
219
220 return ::new (S.Context) NoMergeAttr(S.Context, A);
221}
222
223template <typename OtherAttr, int DiagIdx>
224static bool CheckStmtInlineAttr(Sema &SemaRef, const Stmt *OrigSt,
225 const Stmt *CurSt,
226 const AttributeCommonInfo &A) {
227 CallExprFinder OrigCEF(SemaRef, OrigSt);
228 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 bool CanSuppressDiag =
238 OrigSt && CEF.getCallExprs().size() == OrigCEF.getCallExprs().size();
239
240 if (!CEF.foundCallExpr()) {
241 return SemaRef.Diag(CurSt->getBeginLoc(),
242 diag::warn_attribute_ignored_no_calls_in_stmt)
243 << A;
244 }
245
246 for (const auto &Tup :
247 llvm::zip_longest(t: OrigCEF.getCallExprs(), u: 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 if (!CanSuppressDiag || !(*std::get<0>(t: Tup))->getCalleeDecl()) {
252 const Decl *Callee = (*std::get<1>(t: Tup))->getCalleeDecl();
253 if (Callee &&
254 (Callee->hasAttr<OtherAttr>() || Callee->hasAttr<FlattenAttr>())) {
255 SemaRef.Diag(CurSt->getBeginLoc(),
256 diag::warn_function_stmt_attribute_precedence)
257 << A << (Callee->hasAttr<OtherAttr>() ? DiagIdx : 1);
258 SemaRef.Diag(Callee->getBeginLoc(), diag::note_conflicting_attribute);
259 }
260 }
261 }
262
263 return false;
264}
265
266bool Sema::CheckNoInlineAttr(const Stmt *OrigSt, const Stmt *CurSt,
267 const AttributeCommonInfo &A) {
268 return CheckStmtInlineAttr<AlwaysInlineAttr, 0>(*this, OrigSt, CurSt, A);
269}
270
271bool Sema::CheckAlwaysInlineAttr(const Stmt *OrigSt, const Stmt *CurSt,
272 const AttributeCommonInfo &A) {
273 return CheckStmtInlineAttr<NoInlineAttr, 2>(*this, OrigSt, CurSt, A);
274}
275
276static Attr *handleNoInlineAttr(Sema &S, Stmt *St, const ParsedAttr &A,
277 SourceRange Range) {
278 NoInlineAttr NIA(S.Context, A);
279 if (!NIA.isClangNoInline()) {
280 S.Diag(St->getBeginLoc(), diag::warn_function_attribute_ignored_in_stmt)
281 << "[[clang::noinline]]";
282 return nullptr;
283 }
284
285 if (S.CheckNoInlineAttr(/*OrigSt=*/nullptr, CurSt: St, A))
286 return nullptr;
287
288 return ::new (S.Context) NoInlineAttr(S.Context, A);
289}
290
291static Attr *handleAlwaysInlineAttr(Sema &S, Stmt *St, const ParsedAttr &A,
292 SourceRange Range) {
293 AlwaysInlineAttr AIA(S.Context, A);
294 if (!AIA.isClangAlwaysInline()) {
295 S.Diag(St->getBeginLoc(), diag::warn_function_attribute_ignored_in_stmt)
296 << "[[clang::always_inline]]";
297 return nullptr;
298 }
299
300 if (S.CheckAlwaysInlineAttr(/*OrigSt=*/nullptr, CurSt: St, A))
301 return nullptr;
302
303 return ::new (S.Context) AlwaysInlineAttr(S.Context, A);
304}
305
306static Attr *handleMustTailAttr(Sema &S, Stmt *St, const ParsedAttr &A,
307 SourceRange Range) {
308 // Validation is in Sema::ActOnAttributedStmt().
309 return ::new (S.Context) MustTailAttr(S.Context, A);
310}
311
312static Attr *handleLikely(Sema &S, Stmt *St, const ParsedAttr &A,
313 SourceRange Range) {
314
315 if (!S.getLangOpts().CPlusPlus20 && A.isCXX11Attribute() && !A.getScopeName())
316 S.Diag(A.getLoc(), diag::ext_cxx20_attr) << A << Range;
317
318 return ::new (S.Context) LikelyAttr(S.Context, A);
319}
320
321static Attr *handleUnlikely(Sema &S, Stmt *St, const ParsedAttr &A,
322 SourceRange Range) {
323
324 if (!S.getLangOpts().CPlusPlus20 && A.isCXX11Attribute() && !A.getScopeName())
325 S.Diag(A.getLoc(), diag::ext_cxx20_attr) << A << Range;
326
327 return ::new (S.Context) UnlikelyAttr(S.Context, A);
328}
329
330CodeAlignAttr *Sema::BuildCodeAlignAttr(const AttributeCommonInfo &CI,
331 Expr *E) {
332 if (!E->isValueDependent()) {
333 llvm::APSInt ArgVal;
334 ExprResult Res = VerifyIntegerConstantExpression(E, Result: &ArgVal);
335 if (Res.isInvalid())
336 return nullptr;
337 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 if (ArgVal < CodeAlignAttr::MinimumAlignment ||
342 ArgVal > CodeAlignAttr::MaximumAlignment || !ArgVal.isPowerOf2()) {
343 if (std::optional<int64_t> Value = ArgVal.trySExtValue())
344 Diag(CI.getLoc(), diag::err_attribute_power_of_two_in_range)
345 << CI << CodeAlignAttr::MinimumAlignment
346 << CodeAlignAttr::MaximumAlignment << Value.value();
347 else
348 Diag(CI.getLoc(), diag::err_attribute_power_of_two_in_range)
349 << CI << CodeAlignAttr::MinimumAlignment
350 << CodeAlignAttr::MaximumAlignment << E;
351 return nullptr;
352 }
353 }
354 return new (Context) CodeAlignAttr(Context, CI, E);
355}
356
357static Attr *handleCodeAlignAttr(Sema &S, Stmt *St, const ParsedAttr &A) {
358
359 Expr *E = A.getArgAsExpr(Arg: 0);
360 return S.BuildCodeAlignAttr(A, E);
361}
362
363// Diagnose non-identical duplicates as a 'conflicting' loop attributes
364// and suppress duplicate errors in cases where the two match.
365template <typename LoopAttrT>
366static void CheckForDuplicateLoopAttrs(Sema &S, ArrayRef<const Attr *> Attrs) {
367 auto FindFunc = [](const Attr *A) { return isa<const LoopAttrT>(A); };
368 const auto *FirstItr = std::find_if(Attrs.begin(), Attrs.end(), FindFunc);
369
370 if (FirstItr == Attrs.end()) // no attributes found
371 return;
372
373 const auto *LastFoundItr = FirstItr;
374 std::optional<llvm::APSInt> FirstValue;
375
376 const auto *CAFA =
377 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 if (!CAFA)
381 return;
382
383 while (Attrs.end() != (LastFoundItr = std::find_if(LastFoundItr + 1,
384 Attrs.end(), FindFunc))) {
385 const auto *CASA =
386 dyn_cast<ConstantExpr>(cast<LoopAttrT>(*LastFoundItr)->getAlignment());
387 // If the value is dependent, we can not test anything.
388 if (!CASA)
389 return;
390 // Test the attribute values.
391 llvm::APSInt SecondValue = CASA->getResultAsAPSInt();
392 if (!FirstValue)
393 FirstValue = CAFA->getResultAsAPSInt();
394
395 if (FirstValue != SecondValue) {
396 S.Diag((*LastFoundItr)->getLocation(), diag::err_loop_attr_conflict)
397 << *FirstItr;
398 S.Diag((*FirstItr)->getLocation(), diag::note_previous_attribute);
399 }
400 return;
401 }
402}
403
404static Attr *handleMSConstexprAttr(Sema &S, Stmt *St, const ParsedAttr &A,
405 SourceRange Range) {
406 if (!S.getLangOpts().isCompatibleWithMSVC(MajorVersion: LangOptions::MSVC2022_3)) {
407 S.Diag(A.getLoc(), diag::warn_unknown_attribute_ignored)
408 << A << A.getRange();
409 return nullptr;
410 }
411 return ::new (S.Context) MSConstexprAttr(S.Context, A);
412}
413
414#define WANT_STMT_MERGE_LOGIC
415#include "clang/Sema/AttrParsedAttrImpl.inc"
416#undef WANT_STMT_MERGE_LOGIC
417
418static void
419CheckForIncompatibleAttributes(Sema &S,
420 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 if (Attrs.size() < 2)
424 return;
425
426 // First, check for the easy cases that are table-generated for us.
427 if (!DiagnoseMutualExclusions(S, Attrs))
428 return;
429
430 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 Vectorize,
436 Interleave,
437 UnrollAndJam,
438 Pipeline,
439 // For unroll, default indicates full unrolling rather than enabling the
440 // transformation.
441 Unroll,
442 // The loop distribution transformation only has a state form that is
443 // exposed by #pragma clang loop distribute (enable | disable).
444 Distribute,
445 // The vector predication only has a state form that is exposed by
446 // #pragma clang loop vectorize_predicate (enable | disable).
447 VectorizePredicate,
448 // This serves as a indicator to how many category are listed in this enum.
449 NumberOfCategories
450 };
451 // The following array accumulates the hints encountered while iterating
452 // through the attributes to check for compatibility.
453 struct {
454 const LoopHintAttr *StateAttr;
455 const LoopHintAttr *NumericAttr;
456 } HintAttrs[CategoryType::NumberOfCategories] = {};
457
458 for (const auto *I : Attrs) {
459 const LoopHintAttr *LH = dyn_cast<LoopHintAttr>(I);
460
461 // Skip non loop hint attributes
462 if (!LH)
463 continue;
464
465 CategoryType Category = CategoryType::NumberOfCategories;
466 LoopHintAttr::OptionType Option = LH->getOption();
467 switch (Option) {
468 case LoopHintAttr::Vectorize:
469 case LoopHintAttr::VectorizeWidth:
470 Category = Vectorize;
471 break;
472 case LoopHintAttr::Interleave:
473 case LoopHintAttr::InterleaveCount:
474 Category = Interleave;
475 break;
476 case LoopHintAttr::Unroll:
477 case LoopHintAttr::UnrollCount:
478 Category = Unroll;
479 break;
480 case LoopHintAttr::UnrollAndJam:
481 case LoopHintAttr::UnrollAndJamCount:
482 Category = UnrollAndJam;
483 break;
484 case LoopHintAttr::Distribute:
485 // Perform the check for duplicated 'distribute' hints.
486 Category = Distribute;
487 break;
488 case LoopHintAttr::PipelineDisabled:
489 case LoopHintAttr::PipelineInitiationInterval:
490 Category = Pipeline;
491 break;
492 case LoopHintAttr::VectorizePredicate:
493 Category = VectorizePredicate;
494 break;
495 };
496
497 assert(Category != NumberOfCategories && "Unhandled loop hint option");
498 auto &CategoryState = HintAttrs[Category];
499 const LoopHintAttr *PrevAttr;
500 if (Option == LoopHintAttr::Vectorize ||
501 Option == LoopHintAttr::Interleave || Option == LoopHintAttr::Unroll ||
502 Option == LoopHintAttr::UnrollAndJam ||
503 Option == LoopHintAttr::VectorizePredicate ||
504 Option == LoopHintAttr::PipelineDisabled ||
505 Option == LoopHintAttr::Distribute) {
506 // Enable|Disable|AssumeSafety hint. For example, vectorize(enable).
507 PrevAttr = CategoryState.StateAttr;
508 CategoryState.StateAttr = LH;
509 } else {
510 // Numeric hint. For example, vectorize_width(8).
511 PrevAttr = CategoryState.NumericAttr;
512 CategoryState.NumericAttr = LH;
513 }
514
515 PrintingPolicy Policy(S.Context.getLangOpts());
516 SourceLocation OptionLoc = LH->getRange().getBegin();
517 if (PrevAttr)
518 // Cannot specify same type of attribute twice.
519 S.Diag(OptionLoc, diag::err_pragma_loop_compatibility)
520 << /*Duplicate=*/true << PrevAttr->getDiagnosticName(Policy)
521 << LH->getDiagnosticName(Policy);
522
523 if (CategoryState.StateAttr && CategoryState.NumericAttr &&
524 (Category == Unroll || Category == UnrollAndJam ||
525 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 S.Diag(OptionLoc, diag::err_pragma_loop_compatibility)
531 << /*Duplicate=*/false
532 << CategoryState.StateAttr->getDiagnosticName(Policy)
533 << CategoryState.NumericAttr->getDiagnosticName(Policy);
534 }
535 }
536}
537
538static Attr *handleOpenCLUnrollHint(Sema &S, Stmt *St, const ParsedAttr &A,
539 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 unsigned UnrollFactor = 0;
546 if (A.getNumArgs() == 1) {
547 Expr *E = A.getArgAsExpr(Arg: 0);
548 std::optional<llvm::APSInt> ArgVal;
549
550 if (!(ArgVal = E->getIntegerConstantExpr(Ctx: S.Context))) {
551 S.Diag(A.getLoc(), diag::err_attribute_argument_type)
552 << A << AANT_ArgumentIntegerConstant << E->getSourceRange();
553 return nullptr;
554 }
555
556 int Val = ArgVal->getSExtValue();
557 if (Val <= 0) {
558 S.Diag(A.getRange().getBegin(),
559 diag::err_attribute_requires_positive_integer)
560 << A << /* positive */ 0;
561 return nullptr;
562 }
563 UnrollFactor = static_cast<unsigned>(Val);
564 }
565
566 return ::new (S.Context) OpenCLUnrollHintAttr(S.Context, A, UnrollFactor);
567}
568
569static Attr *ProcessStmtAttribute(Sema &S, Stmt *St, const ParsedAttr &A,
570 SourceRange Range) {
571 if (A.isInvalid() || A.getKind() == ParsedAttr::IgnoredAttribute)
572 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 const TargetInfo *Aux = S.Context.getAuxTargetInfo();
578 if (A.getKind() == ParsedAttr::UnknownAttribute ||
579 !(A.existsInTarget(Target: S.Context.getTargetInfo()) ||
580 (S.Context.getLangOpts().SYCLIsDevice && Aux &&
581 A.existsInTarget(Target: *Aux)))) {
582 S.Diag(A.getLoc(), A.isRegularKeywordAttribute()
583 ? (unsigned)diag::err_keyword_not_supported_on_target
584 : A.isDeclspecAttribute()
585 ? (unsigned)diag::warn_unhandled_ms_attribute_ignored
586 : (unsigned)diag::warn_unknown_attribute_ignored)
587 << A << A.getRange();
588 return nullptr;
589 }
590
591 if (S.checkCommonAttributeFeatures(S: St, A))
592 return nullptr;
593
594 switch (A.getKind()) {
595 case ParsedAttr::AT_AlwaysInline:
596 return handleAlwaysInlineAttr(S, St, A, Range);
597 case ParsedAttr::AT_FallThrough:
598 return handleFallThroughAttr(S, St, A, Range);
599 case ParsedAttr::AT_LoopHint:
600 return handleLoopHintAttr(S, St, A, Range);
601 case ParsedAttr::AT_OpenCLUnrollHint:
602 return handleOpenCLUnrollHint(S, St, A, Range);
603 case ParsedAttr::AT_Suppress:
604 return handleSuppressAttr(S, St, A, Range);
605 case ParsedAttr::AT_NoMerge:
606 return handleNoMergeAttr(S, St, A, Range);
607 case ParsedAttr::AT_NoInline:
608 return handleNoInlineAttr(S, St, A, Range);
609 case ParsedAttr::AT_MustTail:
610 return handleMustTailAttr(S, St, A, Range);
611 case ParsedAttr::AT_Likely:
612 return handleLikely(S, St, A, Range);
613 case ParsedAttr::AT_Unlikely:
614 return handleUnlikely(S, St, A, Range);
615 case ParsedAttr::AT_CodeAlign:
616 return handleCodeAlignAttr(S, St, A);
617 case ParsedAttr::AT_MSConstexpr:
618 return handleMSConstexprAttr(S, St, A, Range);
619 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 S.Diag(A.getRange().getBegin(), diag::err_decl_attribute_invalid_on_stmt)
624 << A << A.isRegularKeywordAttribute() << St->getBeginLoc();
625 return nullptr;
626 }
627}
628
629void Sema::ProcessStmtAttributes(Stmt *S, const ParsedAttributes &InAttrs,
630 SmallVectorImpl<const Attr *> &OutAttrs) {
631 for (const ParsedAttr &AL : InAttrs) {
632 if (const Attr *A = ProcessStmtAttribute(S&: *this, St: S, A: AL, Range: InAttrs.Range))
633 OutAttrs.push_back(Elt: A);
634 }
635
636 CheckForIncompatibleAttributes(S&: *this, Attrs: OutAttrs);
637 CheckForDuplicateLoopAttrs<CodeAlignAttr>(*this, OutAttrs);
638}
639
640bool Sema::CheckRebuiltStmtAttributes(ArrayRef<const Attr *> Attrs) {
641 CheckForDuplicateLoopAttrs<CodeAlignAttr>(*this, Attrs);
642 return false;
643}
644

source code of clang/lib/Sema/SemaStmtAttr.cpp