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