1 //===--- SemaStmtAttr.cpp - Statement Attribute Handling ------------------===//
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
7 //===----------------------------------------------------------------------===//
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/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"
24 using namespace clang
;
27 static Attr
*handleFallThroughAttr(Sema
&S
, Stmt
*St
, const ParsedAttr
&A
,
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
, ";");
38 auto *FnScope
= S
.getCurFunction();
39 if (FnScope
->SwitchStack
.empty()) {
40 S
.Diag(A
.getRange().getBegin(), diag::err_fallthrough_attr_outside_switch
);
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() &&
48 S
.Diag(A
.getLoc(), diag::ext_cxx17_attr
) << A
;
50 FnScope
->setHasFallthroughStmt();
51 return ::new (S
.Context
) FallThroughAttr(S
.Context
, A
);
54 static Attr
*handleSuppressAttr(Sema
&S
, Stmt
*St
, const ParsedAttr
&A
,
56 std::vector
<StringRef
> DiagnosticIdentifiers
;
57 for (unsigned I
= 0, E
= A
.getNumArgs(); I
!= E
; ++I
) {
60 if (!S
.checkStringLiteralArgumentAttr(A
, I
, RuleName
, nullptr))
63 // FIXME: Warn if the rule name is unknown. This is tricky because only
64 // clang-tidy knows about available rules.
65 DiagnosticIdentifiers
.push_back(RuleName
);
68 return ::new (S
.Context
) SuppressAttr(
69 S
.Context
, A
, DiagnosticIdentifiers
.data(), DiagnosticIdentifiers
.size());
72 static Attr
*handleLoopHintAttr(Sema
&S
, Stmt
*St
, const ParsedAttr
&A
,
74 IdentifierLoc
*PragmaNameLoc
= A
.getArgAsIdent(0);
75 IdentifierLoc
*OptionLoc
= A
.getArgAsIdent(1);
76 IdentifierLoc
*StateLoc
= A
.getArgAsIdent(2);
77 Expr
*ValueExpr
= A
.getArgAsExpr(3);
79 StringRef PragmaName
=
80 llvm::StringSwitch
<StringRef
>(PragmaNameLoc
->Ident
->getName())
81 .Cases("unroll", "nounroll", "unroll_and_jam", "nounroll_and_jam",
82 PragmaNameLoc
->Ident
->getName())
83 .Default("clang loop");
85 // This could be handled automatically by adding a Subjects definition in
86 // Attr.td, but that would make the diagnostic behavior worse in this case
87 // because the user spells this attribute as a pragma.
88 if (!isa
<DoStmt
, ForStmt
, CXXForRangeStmt
, WhileStmt
>(St
)) {
89 std::string Pragma
= "#pragma " + std::string(PragmaName
);
90 S
.Diag(St
->getBeginLoc(), diag::err_pragma_loop_precedes_nonloop
) << Pragma
;
94 LoopHintAttr::OptionType Option
;
95 LoopHintAttr::LoopHintState State
;
97 auto SetHints
= [&Option
, &State
](LoopHintAttr::OptionType O
,
98 LoopHintAttr::LoopHintState S
) {
103 if (PragmaName
== "nounroll") {
104 SetHints(LoopHintAttr::Unroll
, LoopHintAttr::Disable
);
105 } else if (PragmaName
== "unroll") {
108 SetHints(LoopHintAttr::UnrollCount
, LoopHintAttr::Numeric
);
110 SetHints(LoopHintAttr::Unroll
, LoopHintAttr::Enable
);
111 } else if (PragmaName
== "nounroll_and_jam") {
112 SetHints(LoopHintAttr::UnrollAndJam
, LoopHintAttr::Disable
);
113 } else if (PragmaName
== "unroll_and_jam") {
114 // #pragma unroll_and_jam N
116 SetHints(LoopHintAttr::UnrollAndJamCount
, LoopHintAttr::Numeric
);
118 SetHints(LoopHintAttr::UnrollAndJam
, LoopHintAttr::Enable
);
120 // #pragma clang loop ...
121 assert(OptionLoc
&& OptionLoc
->Ident
&&
122 "Attribute must have valid option info.");
123 Option
= llvm::StringSwitch
<LoopHintAttr::OptionType
>(
124 OptionLoc
->Ident
->getName())
125 .Case("vectorize", LoopHintAttr::Vectorize
)
126 .Case("vectorize_width", LoopHintAttr::VectorizeWidth
)
127 .Case("interleave", LoopHintAttr::Interleave
)
128 .Case("vectorize_predicate", LoopHintAttr::VectorizePredicate
)
129 .Case("interleave_count", LoopHintAttr::InterleaveCount
)
130 .Case("unroll", LoopHintAttr::Unroll
)
131 .Case("unroll_count", LoopHintAttr::UnrollCount
)
132 .Case("pipeline", LoopHintAttr::PipelineDisabled
)
133 .Case("pipeline_initiation_interval",
134 LoopHintAttr::PipelineInitiationInterval
)
135 .Case("distribute", LoopHintAttr::Distribute
)
136 .Default(LoopHintAttr::Vectorize
);
137 if (Option
== LoopHintAttr::VectorizeWidth
) {
138 assert((ValueExpr
|| (StateLoc
&& StateLoc
->Ident
)) &&
139 "Attribute must have a valid value expression or argument.");
140 if (ValueExpr
&& S
.CheckLoopHintExpr(ValueExpr
, St
->getBeginLoc()))
142 if (StateLoc
&& StateLoc
->Ident
&& StateLoc
->Ident
->isStr("scalable"))
143 State
= LoopHintAttr::ScalableWidth
;
145 State
= LoopHintAttr::FixedWidth
;
146 } else if (Option
== LoopHintAttr::InterleaveCount
||
147 Option
== LoopHintAttr::UnrollCount
||
148 Option
== LoopHintAttr::PipelineInitiationInterval
) {
149 assert(ValueExpr
&& "Attribute must have a valid value expression.");
150 if (S
.CheckLoopHintExpr(ValueExpr
, St
->getBeginLoc()))
152 State
= LoopHintAttr::Numeric
;
153 } else if (Option
== LoopHintAttr::Vectorize
||
154 Option
== LoopHintAttr::Interleave
||
155 Option
== LoopHintAttr::VectorizePredicate
||
156 Option
== LoopHintAttr::Unroll
||
157 Option
== LoopHintAttr::Distribute
||
158 Option
== LoopHintAttr::PipelineDisabled
) {
159 assert(StateLoc
&& StateLoc
->Ident
&& "Loop hint must have an argument");
160 if (StateLoc
->Ident
->isStr("disable"))
161 State
= LoopHintAttr::Disable
;
162 else if (StateLoc
->Ident
->isStr("assume_safety"))
163 State
= LoopHintAttr::AssumeSafety
;
164 else if (StateLoc
->Ident
->isStr("full"))
165 State
= LoopHintAttr::Full
;
166 else if (StateLoc
->Ident
->isStr("enable"))
167 State
= LoopHintAttr::Enable
;
169 llvm_unreachable("bad loop hint argument");
171 llvm_unreachable("bad loop hint");
174 return LoopHintAttr::CreateImplicit(S
.Context
, Option
, State
, ValueExpr
, A
);
178 class CallExprFinder
: public ConstEvaluatedExprVisitor
<CallExprFinder
> {
179 bool FoundAsmStmt
= false;
180 std::vector
<const CallExpr
*> CallExprs
;
183 typedef ConstEvaluatedExprVisitor
<CallExprFinder
> Inherited
;
185 CallExprFinder(Sema
&S
, const Stmt
*St
) : Inherited(S
.Context
) { Visit(St
); }
187 bool foundCallExpr() { return !CallExprs
.empty(); }
188 const std::vector
<const CallExpr
*> &getCallExprs() { return CallExprs
; }
190 bool foundAsmStmt() { return FoundAsmStmt
; }
192 void VisitCallExpr(const CallExpr
*E
) { CallExprs
.push_back(E
); }
194 void VisitAsmStmt(const AsmStmt
*S
) { FoundAsmStmt
= true; }
196 void Visit(const Stmt
*St
) {
199 ConstEvaluatedExprVisitor
<CallExprFinder
>::Visit(St
);
204 static Attr
*handleNoMergeAttr(Sema
&S
, Stmt
*St
, const ParsedAttr
&A
,
206 NoMergeAttr
NMA(S
.Context
, A
);
207 CallExprFinder
CEF(S
, St
);
209 if (!CEF
.foundCallExpr() && !CEF
.foundAsmStmt()) {
210 S
.Diag(St
->getBeginLoc(), diag::warn_attribute_ignored_no_calls_in_stmt
)
215 return ::new (S
.Context
) NoMergeAttr(S
.Context
, A
);
218 template <typename OtherAttr
, int DiagIdx
>
219 static bool CheckStmtInlineAttr(Sema
&SemaRef
, const Stmt
*OrigSt
,
221 const AttributeCommonInfo
&A
) {
222 CallExprFinder
OrigCEF(SemaRef
, OrigSt
);
223 CallExprFinder
CEF(SemaRef
, CurSt
);
225 // If the call expressions lists are equal in size, we can skip
226 // previously emitted diagnostics. However, if the statement has a pack
227 // expansion, we have no way of telling which CallExpr is the instantiated
228 // version of the other. In this case, we will end up re-diagnosing in the
230 // ie: [[clang::always_inline]] non_dependent(), (other_call<Pack>()...)
231 // will diagnose nondependent again.
232 bool CanSuppressDiag
=
233 OrigSt
&& CEF
.getCallExprs().size() == OrigCEF
.getCallExprs().size();
235 if (!CEF
.foundCallExpr()) {
236 return SemaRef
.Diag(CurSt
->getBeginLoc(),
237 diag::warn_attribute_ignored_no_calls_in_stmt
)
241 for (const auto &Tup
:
242 llvm::zip_longest(OrigCEF
.getCallExprs(), CEF
.getCallExprs())) {
243 // If the original call expression already had a callee, we already
244 // diagnosed this, so skip it here. We can't skip if there isn't a 1:1
245 // relationship between the two lists of call expressions.
246 if (!CanSuppressDiag
|| !(*std::get
<0>(Tup
))->getCalleeDecl()) {
247 const Decl
*Callee
= (*std::get
<1>(Tup
))->getCalleeDecl();
249 (Callee
->hasAttr
<OtherAttr
>() || Callee
->hasAttr
<FlattenAttr
>())) {
250 SemaRef
.Diag(CurSt
->getBeginLoc(),
251 diag::warn_function_stmt_attribute_precedence
)
252 << A
<< (Callee
->hasAttr
<OtherAttr
>() ? DiagIdx
: 1);
253 SemaRef
.Diag(Callee
->getBeginLoc(), diag::note_conflicting_attribute
);
261 bool Sema::CheckNoInlineAttr(const Stmt
*OrigSt
, const Stmt
*CurSt
,
262 const AttributeCommonInfo
&A
) {
263 return CheckStmtInlineAttr
<AlwaysInlineAttr
, 0>(*this, OrigSt
, CurSt
, A
);
266 bool Sema::CheckAlwaysInlineAttr(const Stmt
*OrigSt
, const Stmt
*CurSt
,
267 const AttributeCommonInfo
&A
) {
268 return CheckStmtInlineAttr
<NoInlineAttr
, 2>(*this, OrigSt
, CurSt
, A
);
271 static Attr
*handleNoInlineAttr(Sema
&S
, Stmt
*St
, const ParsedAttr
&A
,
273 NoInlineAttr
NIA(S
.Context
, A
);
274 if (!NIA
.isClangNoInline()) {
275 S
.Diag(St
->getBeginLoc(), diag::warn_function_attribute_ignored_in_stmt
)
276 << "[[clang::noinline]]";
280 if (S
.CheckNoInlineAttr(/*OrigSt=*/nullptr, St
, A
))
283 return ::new (S
.Context
) NoInlineAttr(S
.Context
, A
);
286 static Attr
*handleAlwaysInlineAttr(Sema
&S
, Stmt
*St
, const ParsedAttr
&A
,
288 AlwaysInlineAttr
AIA(S
.Context
, A
);
289 if (!AIA
.isClangAlwaysInline()) {
290 S
.Diag(St
->getBeginLoc(), diag::warn_function_attribute_ignored_in_stmt
)
291 << "[[clang::always_inline]]";
295 if (S
.CheckAlwaysInlineAttr(/*OrigSt=*/nullptr, St
, A
))
298 return ::new (S
.Context
) AlwaysInlineAttr(S
.Context
, A
);
301 static Attr
*handleMustTailAttr(Sema
&S
, Stmt
*St
, const ParsedAttr
&A
,
303 // Validation is in Sema::ActOnAttributedStmt().
304 return ::new (S
.Context
) MustTailAttr(S
.Context
, A
);
307 static Attr
*handleLikely(Sema
&S
, Stmt
*St
, const ParsedAttr
&A
,
310 if (!S
.getLangOpts().CPlusPlus20
&& A
.isCXX11Attribute() && !A
.getScopeName())
311 S
.Diag(A
.getLoc(), diag::ext_cxx20_attr
) << A
<< Range
;
313 return ::new (S
.Context
) LikelyAttr(S
.Context
, A
);
316 static Attr
*handleUnlikely(Sema
&S
, Stmt
*St
, const ParsedAttr
&A
,
319 if (!S
.getLangOpts().CPlusPlus20
&& A
.isCXX11Attribute() && !A
.getScopeName())
320 S
.Diag(A
.getLoc(), diag::ext_cxx20_attr
) << A
<< Range
;
322 return ::new (S
.Context
) UnlikelyAttr(S
.Context
, A
);
325 #define WANT_STMT_MERGE_LOGIC
326 #include "clang/Sema/AttrParsedAttrImpl.inc"
327 #undef WANT_STMT_MERGE_LOGIC
330 CheckForIncompatibleAttributes(Sema
&S
,
331 const SmallVectorImpl
<const Attr
*> &Attrs
) {
332 // The vast majority of attributed statements will only have one attribute
333 // on them, so skip all of the checking in the common case.
334 if (Attrs
.size() < 2)
337 // First, check for the easy cases that are table-generated for us.
338 if (!DiagnoseMutualExclusions(S
, Attrs
))
342 // For the following categories, they come in two variants: a state form and
343 // a numeric form. The state form may be one of default, enable, and
344 // disable. The numeric form provides an integer hint (for example, unroll
345 // count) to the transformer.
350 // For unroll, default indicates full unrolling rather than enabling the
353 // The loop distribution transformation only has a state form that is
354 // exposed by #pragma clang loop distribute (enable | disable).
356 // The vector predication only has a state form that is exposed by
357 // #pragma clang loop vectorize_predicate (enable | disable).
359 // This serves as a indicator to how many category are listed in this enum.
362 // The following array accumulates the hints encountered while iterating
363 // through the attributes to check for compatibility.
365 const LoopHintAttr
*StateAttr
;
366 const LoopHintAttr
*NumericAttr
;
367 } HintAttrs
[CategoryType::NumberOfCategories
] = {};
369 for (const auto *I
: Attrs
) {
370 const LoopHintAttr
*LH
= dyn_cast
<LoopHintAttr
>(I
);
372 // Skip non loop hint attributes
376 CategoryType Category
= CategoryType::NumberOfCategories
;
377 LoopHintAttr::OptionType Option
= LH
->getOption();
379 case LoopHintAttr::Vectorize
:
380 case LoopHintAttr::VectorizeWidth
:
381 Category
= Vectorize
;
383 case LoopHintAttr::Interleave
:
384 case LoopHintAttr::InterleaveCount
:
385 Category
= Interleave
;
387 case LoopHintAttr::Unroll
:
388 case LoopHintAttr::UnrollCount
:
391 case LoopHintAttr::UnrollAndJam
:
392 case LoopHintAttr::UnrollAndJamCount
:
393 Category
= UnrollAndJam
;
395 case LoopHintAttr::Distribute
:
396 // Perform the check for duplicated 'distribute' hints.
397 Category
= Distribute
;
399 case LoopHintAttr::PipelineDisabled
:
400 case LoopHintAttr::PipelineInitiationInterval
:
403 case LoopHintAttr::VectorizePredicate
:
404 Category
= VectorizePredicate
;
408 assert(Category
!= NumberOfCategories
&& "Unhandled loop hint option");
409 auto &CategoryState
= HintAttrs
[Category
];
410 const LoopHintAttr
*PrevAttr
;
411 if (Option
== LoopHintAttr::Vectorize
||
412 Option
== LoopHintAttr::Interleave
|| Option
== LoopHintAttr::Unroll
||
413 Option
== LoopHintAttr::UnrollAndJam
||
414 Option
== LoopHintAttr::VectorizePredicate
||
415 Option
== LoopHintAttr::PipelineDisabled
||
416 Option
== LoopHintAttr::Distribute
) {
417 // Enable|Disable|AssumeSafety hint. For example, vectorize(enable).
418 PrevAttr
= CategoryState
.StateAttr
;
419 CategoryState
.StateAttr
= LH
;
421 // Numeric hint. For example, vectorize_width(8).
422 PrevAttr
= CategoryState
.NumericAttr
;
423 CategoryState
.NumericAttr
= LH
;
426 PrintingPolicy
Policy(S
.Context
.getLangOpts());
427 SourceLocation OptionLoc
= LH
->getRange().getBegin();
429 // Cannot specify same type of attribute twice.
430 S
.Diag(OptionLoc
, diag::err_pragma_loop_compatibility
)
431 << /*Duplicate=*/true << PrevAttr
->getDiagnosticName(Policy
)
432 << LH
->getDiagnosticName(Policy
);
434 if (CategoryState
.StateAttr
&& CategoryState
.NumericAttr
&&
435 (Category
== Unroll
|| Category
== UnrollAndJam
||
436 CategoryState
.StateAttr
->getState() == LoopHintAttr::Disable
)) {
437 // Disable hints are not compatible with numeric hints of the same
438 // category. As a special case, numeric unroll hints are also not
439 // compatible with enable or full form of the unroll pragma because these
440 // directives indicate full unrolling.
441 S
.Diag(OptionLoc
, diag::err_pragma_loop_compatibility
)
442 << /*Duplicate=*/false
443 << CategoryState
.StateAttr
->getDiagnosticName(Policy
)
444 << CategoryState
.NumericAttr
->getDiagnosticName(Policy
);
449 static Attr
*handleOpenCLUnrollHint(Sema
&S
, Stmt
*St
, const ParsedAttr
&A
,
451 // Although the feature was introduced only in OpenCL C v2.0 s6.11.5, it's
452 // useful for OpenCL 1.x too and doesn't require HW support.
453 // opencl_unroll_hint can have 0 arguments (compiler
454 // determines unrolling factor) or 1 argument (the unroll factor provided
456 unsigned UnrollFactor
= 0;
457 if (A
.getNumArgs() == 1) {
458 Expr
*E
= A
.getArgAsExpr(0);
459 std::optional
<llvm::APSInt
> ArgVal
;
461 if (!(ArgVal
= E
->getIntegerConstantExpr(S
.Context
))) {
462 S
.Diag(A
.getLoc(), diag::err_attribute_argument_type
)
463 << A
<< AANT_ArgumentIntegerConstant
<< E
->getSourceRange();
467 int Val
= ArgVal
->getSExtValue();
469 S
.Diag(A
.getRange().getBegin(),
470 diag::err_attribute_requires_positive_integer
)
471 << A
<< /* positive */ 0;
474 UnrollFactor
= static_cast<unsigned>(Val
);
477 return ::new (S
.Context
) OpenCLUnrollHintAttr(S
.Context
, A
, UnrollFactor
);
480 static Attr
*ProcessStmtAttribute(Sema
&S
, Stmt
*St
, const ParsedAttr
&A
,
482 if (A
.isInvalid() || A
.getKind() == ParsedAttr::IgnoredAttribute
)
485 // Unknown attributes are automatically warned on. Target-specific attributes
486 // which do not apply to the current target architecture are treated as
487 // though they were unknown attributes.
488 const TargetInfo
*Aux
= S
.Context
.getAuxTargetInfo();
489 if (A
.getKind() == ParsedAttr::UnknownAttribute
||
490 !(A
.existsInTarget(S
.Context
.getTargetInfo()) ||
491 (S
.Context
.getLangOpts().SYCLIsDevice
&& Aux
&&
492 A
.existsInTarget(*Aux
)))) {
493 S
.Diag(A
.getLoc(), A
.isRegularKeywordAttribute()
494 ? (unsigned)diag::err_keyword_not_supported_on_target
495 : A
.isDeclspecAttribute()
496 ? (unsigned)diag::warn_unhandled_ms_attribute_ignored
497 : (unsigned)diag::warn_unknown_attribute_ignored
)
498 << A
<< A
.getRange();
502 if (S
.checkCommonAttributeFeatures(St
, A
))
505 switch (A
.getKind()) {
506 case ParsedAttr::AT_AlwaysInline
:
507 return handleAlwaysInlineAttr(S
, St
, A
, Range
);
508 case ParsedAttr::AT_FallThrough
:
509 return handleFallThroughAttr(S
, St
, A
, Range
);
510 case ParsedAttr::AT_LoopHint
:
511 return handleLoopHintAttr(S
, St
, A
, Range
);
512 case ParsedAttr::AT_OpenCLUnrollHint
:
513 return handleOpenCLUnrollHint(S
, St
, A
, Range
);
514 case ParsedAttr::AT_Suppress
:
515 return handleSuppressAttr(S
, St
, A
, Range
);
516 case ParsedAttr::AT_NoMerge
:
517 return handleNoMergeAttr(S
, St
, A
, Range
);
518 case ParsedAttr::AT_NoInline
:
519 return handleNoInlineAttr(S
, St
, A
, Range
);
520 case ParsedAttr::AT_MustTail
:
521 return handleMustTailAttr(S
, St
, A
, Range
);
522 case ParsedAttr::AT_Likely
:
523 return handleLikely(S
, St
, A
, Range
);
524 case ParsedAttr::AT_Unlikely
:
525 return handleUnlikely(S
, St
, A
, Range
);
527 // N.B., ClangAttrEmitter.cpp emits a diagnostic helper that ensures a
528 // declaration attribute is not written on a statement, but this code is
529 // needed for attributes in Attr.td that do not list any subjects.
530 S
.Diag(A
.getRange().getBegin(), diag::err_decl_attribute_invalid_on_stmt
)
531 << A
<< A
.isRegularKeywordAttribute() << St
->getBeginLoc();
536 void Sema::ProcessStmtAttributes(Stmt
*S
, const ParsedAttributes
&InAttrs
,
537 SmallVectorImpl
<const Attr
*> &OutAttrs
) {
538 for (const ParsedAttr
&AL
: InAttrs
) {
539 if (const Attr
*A
= ProcessStmtAttribute(*this, S
, AL
, InAttrs
.Range
))
540 OutAttrs
.push_back(A
);
543 CheckForIncompatibleAttributes(*this, OutAttrs
);