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 
handleFallThroughAttr(Sema & S,Stmt * St,const ParsedAttr & A,SourceRange Range)27 static Attr *handleFallThroughAttr(Sema &S, Stmt *St, const ParsedAttr &A,
28                                    SourceRange Range) {
29   FallThroughAttr Attr(S.Context, A);
30   if (isa<SwitchCase>(St)) {
31     S.Diag(A.getRange().getBegin(), diag::err_fallthrough_attr_wrong_target)
32         << A << St->getBeginLoc();
33     SourceLocation L = S.getLocForEndOfToken(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 
handleSuppressAttr(Sema & S,Stmt * St,const ParsedAttr & A,SourceRange Range)54 static 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(A, I, RuleName, nullptr))
68       return nullptr;
69 
70     DiagnosticIdentifiers.push_back(RuleName);
71   }
72 
73   return ::new (S.Context) SuppressAttr(
74       S.Context, A, DiagnosticIdentifiers.data(), DiagnosticIdentifiers.size());
75 }
76 
handleLoopHintAttr(Sema & S,Stmt * St,const ParsedAttr & A,SourceRange)77 static Attr *handleLoopHintAttr(Sema &S, Stmt *St, const ParsedAttr &A,
78                                 SourceRange) {
79   IdentifierLoc *PragmaNameLoc = A.getArgAsIdent(0);
80   IdentifierLoc *OptionLoc = A.getArgAsIdent(1);
81   IdentifierLoc *StateLoc = A.getArgAsIdent(2);
82   Expr *ValueExpr = A.getArgAsExpr(3);
83 
84   StringRef PragmaName =
85       llvm::StringSwitch<StringRef>(PragmaNameLoc->Ident->getName())
86           .Cases("unroll", "nounroll", "unroll_and_jam", "nounroll_and_jam",
87                  PragmaNameLoc->Ident->getName())
88           .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   if (!isa<DoStmt, ForStmt, CXXForRangeStmt, WhileStmt>(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(ValueExpr, 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(ValueExpr, 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("disable"))
166         State = LoopHintAttr::Disable;
167       else if (StateLoc->Ident->isStr("assume_safety"))
168         State = LoopHintAttr::AssumeSafety;
169       else if (StateLoc->Ident->isStr("full"))
170         State = LoopHintAttr::Full;
171       else if (StateLoc->Ident->isStr("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 
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 
CallExprFinder(Sema & S,const Stmt * St)190   CallExprFinder(Sema &S, const Stmt *St) : Inherited(S.Context) { Visit(St); }
191 
foundCallExpr()192   bool foundCallExpr() { return !CallExprs.empty(); }
getCallExprs()193   const std::vector<const CallExpr *> &getCallExprs() { return CallExprs; }
194 
foundAsmStmt()195   bool foundAsmStmt() { return FoundAsmStmt; }
196 
VisitCallExpr(const CallExpr * E)197   void VisitCallExpr(const CallExpr *E) { CallExprs.push_back(E); }
198 
VisitAsmStmt(const AsmStmt * S)199   void VisitAsmStmt(const AsmStmt *S) { FoundAsmStmt = true; }
200 
Visit(const Stmt * St)201   void Visit(const Stmt *St) {
202     if (!St)
203       return;
204     ConstEvaluatedExprVisitor<CallExprFinder>::Visit(St);
205   }
206 };
207 } // namespace
208 
handleNoMergeAttr(Sema & S,Stmt * St,const ParsedAttr & A,SourceRange Range)209 static 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 
223 template <typename OtherAttr, int DiagIdx>
CheckStmtInlineAttr(Sema & SemaRef,const Stmt * OrigSt,const Stmt * CurSt,const AttributeCommonInfo & A)224 static 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(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     if (!CanSuppressDiag || !(*std::get<0>(Tup))->getCalleeDecl()) {
252       const Decl *Callee = (*std::get<1>(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 
CheckNoInlineAttr(const Stmt * OrigSt,const Stmt * CurSt,const AttributeCommonInfo & A)266 bool Sema::CheckNoInlineAttr(const Stmt *OrigSt, const Stmt *CurSt,
267                              const AttributeCommonInfo &A) {
268   return CheckStmtInlineAttr<AlwaysInlineAttr, 0>(*this, OrigSt, CurSt, A);
269 }
270 
CheckAlwaysInlineAttr(const Stmt * OrigSt,const Stmt * CurSt,const AttributeCommonInfo & A)271 bool Sema::CheckAlwaysInlineAttr(const Stmt *OrigSt, const Stmt *CurSt,
272                                  const AttributeCommonInfo &A) {
273   return CheckStmtInlineAttr<NoInlineAttr, 2>(*this, OrigSt, CurSt, A);
274 }
275 
handleNoInlineAttr(Sema & S,Stmt * St,const ParsedAttr & A,SourceRange Range)276 static 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, St, A))
286     return nullptr;
287 
288   return ::new (S.Context) NoInlineAttr(S.Context, A);
289 }
290 
handleAlwaysInlineAttr(Sema & S,Stmt * St,const ParsedAttr & A,SourceRange Range)291 static 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, St, A))
301     return nullptr;
302 
303   return ::new (S.Context) AlwaysInlineAttr(S.Context, A);
304 }
305 
handleMustTailAttr(Sema & S,Stmt * St,const ParsedAttr & A,SourceRange Range)306 static 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 
handleLikely(Sema & S,Stmt * St,const ParsedAttr & A,SourceRange Range)312 static 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 
handleUnlikely(Sema & S,Stmt * St,const ParsedAttr & A,SourceRange Range)321 static 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 
BuildCodeAlignAttr(const AttributeCommonInfo & CI,Expr * E)330 CodeAlignAttr *Sema::BuildCodeAlignAttr(const AttributeCommonInfo &CI,
331                                         Expr *E) {
332   if (!E->isValueDependent()) {
333     llvm::APSInt ArgVal;
334     ExprResult Res = VerifyIntegerConstantExpression(E, &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 
handleCodeAlignAttr(Sema & S,Stmt * St,const ParsedAttr & A)357 static Attr *handleCodeAlignAttr(Sema &S, Stmt *St, const ParsedAttr &A) {
358 
359   Expr *E = A.getArgAsExpr(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.
365 template <typename LoopAttrT>
CheckForDuplicateLoopAttrs(Sema & S,ArrayRef<const Attr * > Attrs)366 static 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 
handleMSConstexprAttr(Sema & S,Stmt * St,const ParsedAttr & A,SourceRange Range)404 static Attr *handleMSConstexprAttr(Sema &S, Stmt *St, const ParsedAttr &A,
405                                    SourceRange Range) {
406   if (!S.getLangOpts().isCompatibleWithMSVC(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 
418 static void
CheckForIncompatibleAttributes(Sema & S,const SmallVectorImpl<const Attr * > & Attrs)419 CheckForIncompatibleAttributes(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 
handleOpenCLUnrollHint(Sema & S,Stmt * St,const ParsedAttr & A,SourceRange Range)538 static 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(0);
548     std::optional<llvm::APSInt> ArgVal;
549 
550     if (!(ArgVal = E->getIntegerConstantExpr(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 
ProcessStmtAttribute(Sema & S,Stmt * St,const ParsedAttr & A,SourceRange Range)569 static 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(S.Context.getTargetInfo()) ||
580         (S.Context.getLangOpts().SYCLIsDevice && Aux &&
581          A.existsInTarget(*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(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 
ProcessStmtAttributes(Stmt * S,const ParsedAttributes & InAttrs,SmallVectorImpl<const Attr * > & OutAttrs)629 void Sema::ProcessStmtAttributes(Stmt *S, const ParsedAttributes &InAttrs,
630                                  SmallVectorImpl<const Attr *> &OutAttrs) {
631   for (const ParsedAttr &AL : InAttrs) {
632     if (const Attr *A = ProcessStmtAttribute(*this, S, AL, InAttrs.Range))
633       OutAttrs.push_back(A);
634   }
635 
636   CheckForIncompatibleAttributes(*this, OutAttrs);
637   CheckForDuplicateLoopAttrs<CodeAlignAttr>(*this, OutAttrs);
638 }
639 
CheckRebuiltStmtAttributes(ArrayRef<const Attr * > Attrs)640 bool Sema::CheckRebuiltStmtAttributes(ArrayRef<const Attr *> Attrs) {
641   CheckForDuplicateLoopAttrs<CodeAlignAttr>(*this, Attrs);
642   return false;
643 }
644