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"
23 using namespace clang
;
26 static Attr
*handleFallThroughAttr(Sema
&S
, Stmt
*St
, const ParsedAttr
&A
,
28 FallThroughAttr
Attr(S
.Context
, A
);
29 if (isa
<SwitchCase
>(St
)) {
30 S
.Diag(A
.getRange().getBegin(), diag::err_fallthrough_attr_wrong_target
)
31 << A
<< St
->getBeginLoc();
32 SourceLocation L
= S
.getLocForEndOfToken(Range
.getEnd());
33 S
.Diag(L
, diag::note_fallthrough_insert_semi_fixit
)
34 << FixItHint::CreateInsertion(L
, ";");
37 auto *FnScope
= S
.getCurFunction();
38 if (FnScope
->SwitchStack
.empty()) {
39 S
.Diag(A
.getRange().getBegin(), diag::err_fallthrough_attr_outside_switch
);
43 // If this is spelled as the standard C++17 attribute, but not in C++17, warn
44 // about using it as an extension.
45 if (!S
.getLangOpts().CPlusPlus17
&& A
.isCXX11Attribute() &&
47 S
.Diag(A
.getLoc(), diag::ext_cxx17_attr
) << A
;
49 FnScope
->setHasFallthroughStmt();
50 return ::new (S
.Context
) FallThroughAttr(S
.Context
, A
);
53 static Attr
*handleSuppressAttr(Sema
&S
, Stmt
*St
, const ParsedAttr
&A
,
55 std::vector
<StringRef
> DiagnosticIdentifiers
;
56 for (unsigned I
= 0, E
= A
.getNumArgs(); I
!= E
; ++I
) {
59 if (!S
.checkStringLiteralArgumentAttr(A
, I
, RuleName
, nullptr))
62 // FIXME: Warn if the rule name is unknown. This is tricky because only
63 // clang-tidy knows about available rules.
64 DiagnosticIdentifiers
.push_back(RuleName
);
67 return ::new (S
.Context
) SuppressAttr(
68 S
.Context
, A
, DiagnosticIdentifiers
.data(), DiagnosticIdentifiers
.size());
71 static Attr
*handleLoopHintAttr(Sema
&S
, Stmt
*St
, const ParsedAttr
&A
,
73 IdentifierLoc
*PragmaNameLoc
= A
.getArgAsIdent(0);
74 IdentifierLoc
*OptionLoc
= A
.getArgAsIdent(1);
75 IdentifierLoc
*StateLoc
= A
.getArgAsIdent(2);
76 Expr
*ValueExpr
= A
.getArgAsExpr(3);
78 StringRef PragmaName
=
79 llvm::StringSwitch
<StringRef
>(PragmaNameLoc
->Ident
->getName())
80 .Cases("unroll", "nounroll", "unroll_and_jam", "nounroll_and_jam",
81 PragmaNameLoc
->Ident
->getName())
82 .Default("clang loop");
84 // This could be handled automatically by adding a Subjects definition in
85 // Attr.td, but that would make the diagnostic behavior worse in this case
86 // because the user spells this attribute as a pragma.
87 if (!isa
<DoStmt
, ForStmt
, CXXForRangeStmt
, WhileStmt
>(St
)) {
88 std::string Pragma
= "#pragma " + std::string(PragmaName
);
89 S
.Diag(St
->getBeginLoc(), diag::err_pragma_loop_precedes_nonloop
) << Pragma
;
93 LoopHintAttr::OptionType Option
;
94 LoopHintAttr::LoopHintState State
;
96 auto SetHints
= [&Option
, &State
](LoopHintAttr::OptionType O
,
97 LoopHintAttr::LoopHintState S
) {
102 if (PragmaName
== "nounroll") {
103 SetHints(LoopHintAttr::Unroll
, LoopHintAttr::Disable
);
104 } else if (PragmaName
== "unroll") {
107 SetHints(LoopHintAttr::UnrollCount
, LoopHintAttr::Numeric
);
109 SetHints(LoopHintAttr::Unroll
, LoopHintAttr::Enable
);
110 } else if (PragmaName
== "nounroll_and_jam") {
111 SetHints(LoopHintAttr::UnrollAndJam
, LoopHintAttr::Disable
);
112 } else if (PragmaName
== "unroll_and_jam") {
113 // #pragma unroll_and_jam N
115 SetHints(LoopHintAttr::UnrollAndJamCount
, LoopHintAttr::Numeric
);
117 SetHints(LoopHintAttr::UnrollAndJam
, LoopHintAttr::Enable
);
119 // #pragma clang loop ...
120 assert(OptionLoc
&& OptionLoc
->Ident
&&
121 "Attribute must have valid option info.");
122 Option
= llvm::StringSwitch
<LoopHintAttr::OptionType
>(
123 OptionLoc
->Ident
->getName())
124 .Case("vectorize", LoopHintAttr::Vectorize
)
125 .Case("vectorize_width", LoopHintAttr::VectorizeWidth
)
126 .Case("interleave", LoopHintAttr::Interleave
)
127 .Case("vectorize_predicate", LoopHintAttr::VectorizePredicate
)
128 .Case("interleave_count", LoopHintAttr::InterleaveCount
)
129 .Case("unroll", LoopHintAttr::Unroll
)
130 .Case("unroll_count", LoopHintAttr::UnrollCount
)
131 .Case("pipeline", LoopHintAttr::PipelineDisabled
)
132 .Case("pipeline_initiation_interval",
133 LoopHintAttr::PipelineInitiationInterval
)
134 .Case("distribute", LoopHintAttr::Distribute
)
135 .Default(LoopHintAttr::Vectorize
);
136 if (Option
== LoopHintAttr::VectorizeWidth
) {
137 assert((ValueExpr
|| (StateLoc
&& StateLoc
->Ident
)) &&
138 "Attribute must have a valid value expression or argument.");
139 if (ValueExpr
&& S
.CheckLoopHintExpr(ValueExpr
, St
->getBeginLoc()))
141 if (StateLoc
&& StateLoc
->Ident
&& StateLoc
->Ident
->isStr("scalable"))
142 State
= LoopHintAttr::ScalableWidth
;
144 State
= LoopHintAttr::FixedWidth
;
145 } else if (Option
== LoopHintAttr::InterleaveCount
||
146 Option
== LoopHintAttr::UnrollCount
||
147 Option
== LoopHintAttr::PipelineInitiationInterval
) {
148 assert(ValueExpr
&& "Attribute must have a valid value expression.");
149 if (S
.CheckLoopHintExpr(ValueExpr
, St
->getBeginLoc()))
151 State
= LoopHintAttr::Numeric
;
152 } else if (Option
== LoopHintAttr::Vectorize
||
153 Option
== LoopHintAttr::Interleave
||
154 Option
== LoopHintAttr::VectorizePredicate
||
155 Option
== LoopHintAttr::Unroll
||
156 Option
== LoopHintAttr::Distribute
||
157 Option
== LoopHintAttr::PipelineDisabled
) {
158 assert(StateLoc
&& StateLoc
->Ident
&& "Loop hint must have an argument");
159 if (StateLoc
->Ident
->isStr("disable"))
160 State
= LoopHintAttr::Disable
;
161 else if (StateLoc
->Ident
->isStr("assume_safety"))
162 State
= LoopHintAttr::AssumeSafety
;
163 else if (StateLoc
->Ident
->isStr("full"))
164 State
= LoopHintAttr::Full
;
165 else if (StateLoc
->Ident
->isStr("enable"))
166 State
= LoopHintAttr::Enable
;
168 llvm_unreachable("bad loop hint argument");
170 llvm_unreachable("bad loop hint");
173 return LoopHintAttr::CreateImplicit(S
.Context
, Option
, State
, ValueExpr
, A
);
177 class CallExprFinder
: public ConstEvaluatedExprVisitor
<CallExprFinder
> {
178 bool FoundAsmStmt
= false;
179 std::vector
<const CallExpr
*> CallExprs
;
182 typedef ConstEvaluatedExprVisitor
<CallExprFinder
> Inherited
;
184 CallExprFinder(Sema
&S
, const Stmt
*St
) : Inherited(S
.Context
) { Visit(St
); }
186 bool foundCallExpr() { return !CallExprs
.empty(); }
187 const std::vector
<const CallExpr
*> &getCallExprs() { return CallExprs
; }
189 bool foundAsmStmt() { return FoundAsmStmt
; }
191 void VisitCallExpr(const CallExpr
*E
) { CallExprs
.push_back(E
); }
193 void VisitAsmStmt(const AsmStmt
*S
) { FoundAsmStmt
= true; }
195 void Visit(const Stmt
*St
) {
198 ConstEvaluatedExprVisitor
<CallExprFinder
>::Visit(St
);
203 static Attr
*handleNoMergeAttr(Sema
&S
, Stmt
*St
, const ParsedAttr
&A
,
205 NoMergeAttr
NMA(S
.Context
, A
);
206 CallExprFinder
CEF(S
, St
);
208 if (!CEF
.foundCallExpr() && !CEF
.foundAsmStmt()) {
209 S
.Diag(St
->getBeginLoc(), diag::warn_attribute_ignored_no_calls_in_stmt
)
214 return ::new (S
.Context
) NoMergeAttr(S
.Context
, A
);
217 static Attr
*handleNoInlineAttr(Sema
&S
, Stmt
*St
, const ParsedAttr
&A
,
219 NoInlineAttr
NIA(S
.Context
, A
);
220 if (!NIA
.isClangNoInline()) {
221 S
.Diag(St
->getBeginLoc(), diag::warn_function_attribute_ignored_in_stmt
)
222 << "[[clang::noinline]]";
226 CallExprFinder
CEF(S
, St
);
227 if (!CEF
.foundCallExpr()) {
228 S
.Diag(St
->getBeginLoc(), diag::warn_attribute_ignored_no_calls_in_stmt
)
233 for (const auto *CallExpr
: CEF
.getCallExprs()) {
234 const Decl
*Decl
= CallExpr
->getCalleeDecl();
235 if (Decl
->hasAttr
<AlwaysInlineAttr
>() || Decl
->hasAttr
<FlattenAttr
>())
236 S
.Diag(St
->getBeginLoc(), diag::warn_function_stmt_attribute_precedence
)
237 << A
<< (Decl
->hasAttr
<AlwaysInlineAttr
>() ? 0 : 1);
240 return ::new (S
.Context
) NoInlineAttr(S
.Context
, A
);
243 static Attr
*handleAlwaysInlineAttr(Sema
&S
, Stmt
*St
, const ParsedAttr
&A
,
245 AlwaysInlineAttr
AIA(S
.Context
, A
);
246 if (!AIA
.isClangAlwaysInline()) {
247 S
.Diag(St
->getBeginLoc(), diag::warn_function_attribute_ignored_in_stmt
)
248 << "[[clang::always_inline]]";
252 CallExprFinder
CEF(S
, St
);
253 if (!CEF
.foundCallExpr()) {
254 S
.Diag(St
->getBeginLoc(), diag::warn_attribute_ignored_no_calls_in_stmt
)
259 for (const auto *CallExpr
: CEF
.getCallExprs()) {
260 const Decl
*Decl
= CallExpr
->getCalleeDecl();
261 if (Decl
->hasAttr
<NoInlineAttr
>() || Decl
->hasAttr
<FlattenAttr
>())
262 S
.Diag(St
->getBeginLoc(), diag::warn_function_stmt_attribute_precedence
)
263 << A
<< (Decl
->hasAttr
<NoInlineAttr
>() ? 2 : 1);
266 return ::new (S
.Context
) AlwaysInlineAttr(S
.Context
, A
);
269 static Attr
*handleMustTailAttr(Sema
&S
, Stmt
*St
, const ParsedAttr
&A
,
271 // Validation is in Sema::ActOnAttributedStmt().
272 return ::new (S
.Context
) MustTailAttr(S
.Context
, A
);
275 static Attr
*handleLikely(Sema
&S
, Stmt
*St
, const ParsedAttr
&A
,
278 if (!S
.getLangOpts().CPlusPlus20
&& A
.isCXX11Attribute() && !A
.getScopeName())
279 S
.Diag(A
.getLoc(), diag::ext_cxx20_attr
) << A
<< Range
;
281 return ::new (S
.Context
) LikelyAttr(S
.Context
, A
);
284 static Attr
*handleUnlikely(Sema
&S
, Stmt
*St
, const ParsedAttr
&A
,
287 if (!S
.getLangOpts().CPlusPlus20
&& A
.isCXX11Attribute() && !A
.getScopeName())
288 S
.Diag(A
.getLoc(), diag::ext_cxx20_attr
) << A
<< Range
;
290 return ::new (S
.Context
) UnlikelyAttr(S
.Context
, A
);
293 #define WANT_STMT_MERGE_LOGIC
294 #include "clang/Sema/AttrParsedAttrImpl.inc"
295 #undef WANT_STMT_MERGE_LOGIC
298 CheckForIncompatibleAttributes(Sema
&S
,
299 const SmallVectorImpl
<const Attr
*> &Attrs
) {
300 // The vast majority of attributed statements will only have one attribute
301 // on them, so skip all of the checking in the common case.
302 if (Attrs
.size() < 2)
305 // First, check for the easy cases that are table-generated for us.
306 if (!DiagnoseMutualExclusions(S
, Attrs
))
309 // There are 6 categories of loop hints attributes: vectorize, interleave,
310 // unroll, unroll_and_jam, pipeline and distribute. Except for distribute they
311 // come in two variants: a state form and a numeric form. The state form
312 // selectively defaults/enables/disables the transformation for the loop
313 // (for unroll, default indicates full unrolling rather than enabling the
314 // transformation). The numeric form form provides an integer hint (for
315 // example, unroll count) to the transformer. The following array accumulates
316 // the hints encountered while iterating through the attributes to check for
319 const LoopHintAttr
*StateAttr
;
320 const LoopHintAttr
*NumericAttr
;
321 } HintAttrs
[] = {{nullptr, nullptr}, {nullptr, nullptr}, {nullptr, nullptr},
322 {nullptr, nullptr}, {nullptr, nullptr}, {nullptr, nullptr},
325 for (const auto *I
: Attrs
) {
326 const LoopHintAttr
*LH
= dyn_cast
<LoopHintAttr
>(I
);
328 // Skip non loop hint attributes
332 LoopHintAttr::OptionType Option
= LH
->getOption();
343 case LoopHintAttr::Vectorize
:
344 case LoopHintAttr::VectorizeWidth
:
345 Category
= Vectorize
;
347 case LoopHintAttr::Interleave
:
348 case LoopHintAttr::InterleaveCount
:
349 Category
= Interleave
;
351 case LoopHintAttr::Unroll
:
352 case LoopHintAttr::UnrollCount
:
355 case LoopHintAttr::UnrollAndJam
:
356 case LoopHintAttr::UnrollAndJamCount
:
357 Category
= UnrollAndJam
;
359 case LoopHintAttr::Distribute
:
360 // Perform the check for duplicated 'distribute' hints.
361 Category
= Distribute
;
363 case LoopHintAttr::PipelineDisabled
:
364 case LoopHintAttr::PipelineInitiationInterval
:
367 case LoopHintAttr::VectorizePredicate
:
368 Category
= VectorizePredicate
;
372 assert(Category
< sizeof(HintAttrs
) / sizeof(HintAttrs
[0]));
373 auto &CategoryState
= HintAttrs
[Category
];
374 const LoopHintAttr
*PrevAttr
;
375 if (Option
== LoopHintAttr::Vectorize
||
376 Option
== LoopHintAttr::Interleave
|| Option
== LoopHintAttr::Unroll
||
377 Option
== LoopHintAttr::UnrollAndJam
||
378 Option
== LoopHintAttr::VectorizePredicate
||
379 Option
== LoopHintAttr::PipelineDisabled
||
380 Option
== LoopHintAttr::Distribute
) {
381 // Enable|Disable|AssumeSafety hint. For example, vectorize(enable).
382 PrevAttr
= CategoryState
.StateAttr
;
383 CategoryState
.StateAttr
= LH
;
385 // Numeric hint. For example, vectorize_width(8).
386 PrevAttr
= CategoryState
.NumericAttr
;
387 CategoryState
.NumericAttr
= LH
;
390 PrintingPolicy
Policy(S
.Context
.getLangOpts());
391 SourceLocation OptionLoc
= LH
->getRange().getBegin();
393 // Cannot specify same type of attribute twice.
394 S
.Diag(OptionLoc
, diag::err_pragma_loop_compatibility
)
395 << /*Duplicate=*/true << PrevAttr
->getDiagnosticName(Policy
)
396 << LH
->getDiagnosticName(Policy
);
398 if (CategoryState
.StateAttr
&& CategoryState
.NumericAttr
&&
399 (Category
== Unroll
|| Category
== UnrollAndJam
||
400 CategoryState
.StateAttr
->getState() == LoopHintAttr::Disable
)) {
401 // Disable hints are not compatible with numeric hints of the same
402 // category. As a special case, numeric unroll hints are also not
403 // compatible with enable or full form of the unroll pragma because these
404 // directives indicate full unrolling.
405 S
.Diag(OptionLoc
, diag::err_pragma_loop_compatibility
)
406 << /*Duplicate=*/false
407 << CategoryState
.StateAttr
->getDiagnosticName(Policy
)
408 << CategoryState
.NumericAttr
->getDiagnosticName(Policy
);
413 static Attr
*handleOpenCLUnrollHint(Sema
&S
, Stmt
*St
, const ParsedAttr
&A
,
415 // Although the feature was introduced only in OpenCL C v2.0 s6.11.5, it's
416 // useful for OpenCL 1.x too and doesn't require HW support.
417 // opencl_unroll_hint can have 0 arguments (compiler
418 // determines unrolling factor) or 1 argument (the unroll factor provided
420 unsigned UnrollFactor
= 0;
421 if (A
.getNumArgs() == 1) {
422 Expr
*E
= A
.getArgAsExpr(0);
423 Optional
<llvm::APSInt
> ArgVal
;
425 if (!(ArgVal
= E
->getIntegerConstantExpr(S
.Context
))) {
426 S
.Diag(A
.getLoc(), diag::err_attribute_argument_type
)
427 << A
<< AANT_ArgumentIntegerConstant
<< E
->getSourceRange();
431 int Val
= ArgVal
->getSExtValue();
433 S
.Diag(A
.getRange().getBegin(),
434 diag::err_attribute_requires_positive_integer
)
435 << A
<< /* positive */ 0;
438 UnrollFactor
= static_cast<unsigned>(Val
);
441 return ::new (S
.Context
) OpenCLUnrollHintAttr(S
.Context
, A
, UnrollFactor
);
444 static Attr
*ProcessStmtAttribute(Sema
&S
, Stmt
*St
, const ParsedAttr
&A
,
446 if (A
.isInvalid() || A
.getKind() == ParsedAttr::IgnoredAttribute
)
449 // Unknown attributes are automatically warned on. Target-specific attributes
450 // which do not apply to the current target architecture are treated as
451 // though they were unknown attributes.
452 const TargetInfo
*Aux
= S
.Context
.getAuxTargetInfo();
453 if (A
.getKind() == ParsedAttr::UnknownAttribute
||
454 !(A
.existsInTarget(S
.Context
.getTargetInfo()) ||
455 (S
.Context
.getLangOpts().SYCLIsDevice
&& Aux
&&
456 A
.existsInTarget(*Aux
)))) {
457 S
.Diag(A
.getLoc(), A
.isDeclspecAttribute()
458 ? (unsigned)diag::warn_unhandled_ms_attribute_ignored
459 : (unsigned)diag::warn_unknown_attribute_ignored
)
460 << A
<< A
.getRange();
464 if (S
.checkCommonAttributeFeatures(St
, A
))
467 switch (A
.getKind()) {
468 case ParsedAttr::AT_AlwaysInline
:
469 return handleAlwaysInlineAttr(S
, St
, A
, Range
);
470 case ParsedAttr::AT_FallThrough
:
471 return handleFallThroughAttr(S
, St
, A
, Range
);
472 case ParsedAttr::AT_LoopHint
:
473 return handleLoopHintAttr(S
, St
, A
, Range
);
474 case ParsedAttr::AT_OpenCLUnrollHint
:
475 return handleOpenCLUnrollHint(S
, St
, A
, Range
);
476 case ParsedAttr::AT_Suppress
:
477 return handleSuppressAttr(S
, St
, A
, Range
);
478 case ParsedAttr::AT_NoMerge
:
479 return handleNoMergeAttr(S
, St
, A
, Range
);
480 case ParsedAttr::AT_NoInline
:
481 return handleNoInlineAttr(S
, St
, A
, Range
);
482 case ParsedAttr::AT_MustTail
:
483 return handleMustTailAttr(S
, St
, A
, Range
);
484 case ParsedAttr::AT_Likely
:
485 return handleLikely(S
, St
, A
, Range
);
486 case ParsedAttr::AT_Unlikely
:
487 return handleUnlikely(S
, St
, A
, Range
);
489 // N.B., ClangAttrEmitter.cpp emits a diagnostic helper that ensures a
490 // declaration attribute is not written on a statement, but this code is
491 // needed for attributes in Attr.td that do not list any subjects.
492 S
.Diag(A
.getRange().getBegin(), diag::err_decl_attribute_invalid_on_stmt
)
493 << A
<< St
->getBeginLoc();
498 void Sema::ProcessStmtAttributes(Stmt
*S
, const ParsedAttributes
&InAttrs
,
499 SmallVectorImpl
<const Attr
*> &OutAttrs
) {
500 for (const ParsedAttr
&AL
: InAttrs
) {
501 if (const Attr
*A
= ProcessStmtAttribute(*this, S
, AL
, InAttrs
.Range
))
502 OutAttrs
.push_back(A
);
505 CheckForIncompatibleAttributes(*this, OutAttrs
);