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