1 //===--- SemaStmtAttr.cpp - Statement Attribute Handling ------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 //  This file implements stmt-related attribute processing.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "clang/Sema/SemaInternal.h"
15 #include "clang/AST/ASTContext.h"
16 #include "clang/Basic/SourceManager.h"
17 #include "clang/Sema/DelayedDiagnostic.h"
18 #include "clang/Sema/Lookup.h"
19 #include "clang/Sema/ScopeInfo.h"
20 #include "llvm/ADT/StringExtras.h"
21 
22 using namespace clang;
23 using namespace sema;
24 
25 static Attr *handleFallThroughAttr(Sema &S, Stmt *St, const ParsedAttr &A,
26                                    SourceRange Range) {
27   FallThroughAttr Attr(A.getRange(), S.Context,
28                        A.getAttributeSpellingListIndex());
29   if (!isa<NullStmt>(St)) {
30     S.Diag(A.getRange().getBegin(), diag::err_fallthrough_attr_wrong_target)
31         << Attr.getSpelling() << St->getBeginLoc();
32     if (isa<SwitchCase>(St)) {
33       SourceLocation L = S.getLocForEndOfToken(Range.getEnd());
34       S.Diag(L, diag::note_fallthrough_insert_semi_fixit)
35           << FixItHint::CreateInsertion(L, ";");
36     }
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.getName();
50 
51   FnScope->setHasFallthroughStmt();
52   return ::new (S.Context) auto(Attr);
53 }
54 
55 static Attr *handleSuppressAttr(Sema &S, Stmt *St, const ParsedAttr &A,
56                                 SourceRange Range) {
57   if (A.getNumArgs() < 1) {
58     S.Diag(A.getLoc(), diag::err_attribute_too_few_arguments) << A << 1;
59     return nullptr;
60   }
61 
62   std::vector<StringRef> DiagnosticIdentifiers;
63   for (unsigned I = 0, E = A.getNumArgs(); I != E; ++I) {
64     StringRef RuleName;
65 
66     if (!S.checkStringLiteralArgumentAttr(A, I, RuleName, nullptr))
67       return nullptr;
68 
69     // FIXME: Warn if the rule name is unknown. This is tricky because only
70     // clang-tidy knows about available rules.
71     DiagnosticIdentifiers.push_back(RuleName);
72   }
73 
74   return ::new (S.Context) SuppressAttr(
75       A.getRange(), S.Context, DiagnosticIdentifiers.data(),
76       DiagnosticIdentifiers.size(), A.getAttributeSpellingListIndex());
77 }
78 
79 static Attr *handleLoopHintAttr(Sema &S, Stmt *St, const ParsedAttr &A,
80                                 SourceRange) {
81   IdentifierLoc *PragmaNameLoc = A.getArgAsIdent(0);
82   IdentifierLoc *OptionLoc = A.getArgAsIdent(1);
83   IdentifierLoc *StateLoc = A.getArgAsIdent(2);
84   Expr *ValueExpr = A.getArgAsExpr(3);
85 
86   bool PragmaUnroll = PragmaNameLoc->Ident->getName() == "unroll";
87   bool PragmaNoUnroll = PragmaNameLoc->Ident->getName() == "nounroll";
88   bool PragmaUnrollAndJam = PragmaNameLoc->Ident->getName() == "unroll_and_jam";
89   bool PragmaNoUnrollAndJam =
90       PragmaNameLoc->Ident->getName() == "nounroll_and_jam";
91   if (St->getStmtClass() != Stmt::DoStmtClass &&
92       St->getStmtClass() != Stmt::ForStmtClass &&
93       St->getStmtClass() != Stmt::CXXForRangeStmtClass &&
94       St->getStmtClass() != Stmt::WhileStmtClass) {
95     const char *Pragma =
96         llvm::StringSwitch<const char *>(PragmaNameLoc->Ident->getName())
97             .Case("unroll", "#pragma unroll")
98             .Case("nounroll", "#pragma nounroll")
99             .Case("unroll_and_jam", "#pragma unroll_and_jam")
100             .Case("nounroll_and_jam", "#pragma nounroll_and_jam")
101             .Default("#pragma clang loop");
102     S.Diag(St->getBeginLoc(), diag::err_pragma_loop_precedes_nonloop) << Pragma;
103     return nullptr;
104   }
105 
106   LoopHintAttr::Spelling Spelling =
107       LoopHintAttr::Spelling(A.getAttributeSpellingListIndex());
108   LoopHintAttr::OptionType Option;
109   LoopHintAttr::LoopHintState State;
110   if (PragmaNoUnroll) {
111     // #pragma nounroll
112     Option = LoopHintAttr::Unroll;
113     State = LoopHintAttr::Disable;
114   } else if (PragmaUnroll) {
115     if (ValueExpr) {
116       // #pragma unroll N
117       Option = LoopHintAttr::UnrollCount;
118       State = LoopHintAttr::Numeric;
119     } else {
120       // #pragma unroll
121       Option = LoopHintAttr::Unroll;
122       State = LoopHintAttr::Enable;
123     }
124   } else if (PragmaNoUnrollAndJam) {
125     // #pragma nounroll_and_jam
126     Option = LoopHintAttr::UnrollAndJam;
127     State = LoopHintAttr::Disable;
128   } else if (PragmaUnrollAndJam) {
129     if (ValueExpr) {
130       // #pragma unroll_and_jam N
131       Option = LoopHintAttr::UnrollAndJamCount;
132       State = LoopHintAttr::Numeric;
133     } else {
134       // #pragma unroll_and_jam
135       Option = LoopHintAttr::UnrollAndJam;
136       State = LoopHintAttr::Enable;
137     }
138   } else {
139     // #pragma clang loop ...
140     assert(OptionLoc && OptionLoc->Ident &&
141            "Attribute must have valid option info.");
142     Option = llvm::StringSwitch<LoopHintAttr::OptionType>(
143                  OptionLoc->Ident->getName())
144                  .Case("vectorize", LoopHintAttr::Vectorize)
145                  .Case("vectorize_width", LoopHintAttr::VectorizeWidth)
146                  .Case("interleave", LoopHintAttr::Interleave)
147                  .Case("interleave_count", LoopHintAttr::InterleaveCount)
148                  .Case("unroll", LoopHintAttr::Unroll)
149                  .Case("unroll_count", LoopHintAttr::UnrollCount)
150                  .Case("distribute", LoopHintAttr::Distribute)
151                  .Default(LoopHintAttr::Vectorize);
152     if (Option == LoopHintAttr::VectorizeWidth ||
153         Option == LoopHintAttr::InterleaveCount ||
154         Option == LoopHintAttr::UnrollCount) {
155       assert(ValueExpr && "Attribute must have a valid value expression.");
156       if (S.CheckLoopHintExpr(ValueExpr, St->getBeginLoc()))
157         return nullptr;
158       State = LoopHintAttr::Numeric;
159     } else if (Option == LoopHintAttr::Vectorize ||
160                Option == LoopHintAttr::Interleave ||
161                Option == LoopHintAttr::Unroll ||
162                Option == LoopHintAttr::Distribute) {
163       assert(StateLoc && StateLoc->Ident && "Loop hint must have an argument");
164       if (StateLoc->Ident->isStr("disable"))
165         State = LoopHintAttr::Disable;
166       else if (StateLoc->Ident->isStr("assume_safety"))
167         State = LoopHintAttr::AssumeSafety;
168       else if (StateLoc->Ident->isStr("full"))
169         State = LoopHintAttr::Full;
170       else if (StateLoc->Ident->isStr("enable"))
171         State = LoopHintAttr::Enable;
172       else
173         llvm_unreachable("bad loop hint argument");
174     } else
175       llvm_unreachable("bad loop hint");
176   }
177 
178   return LoopHintAttr::CreateImplicit(S.Context, Spelling, Option, State,
179                                       ValueExpr, A.getRange());
180 }
181 
182 static void
183 CheckForIncompatibleAttributes(Sema &S,
184                                const SmallVectorImpl<const Attr *> &Attrs) {
185   // There are 5 categories of loop hints attributes: vectorize, interleave,
186   // unroll, unroll_and_jam and distribute. Except for distribute they come
187   // in two variants: a state form and a numeric form.  The state form
188   // selectively defaults/enables/disables the transformation for the loop
189   // (for unroll, default indicates full unrolling rather than enabling the
190   // transformation). The numeric form form provides an integer hint (for
191   // example, unroll count) to the transformer. The following array accumulates
192   // the hints encountered while iterating through the attributes to check for
193   // compatibility.
194   struct {
195     const LoopHintAttr *StateAttr;
196     const LoopHintAttr *NumericAttr;
197   } HintAttrs[] = {{nullptr, nullptr},
198                    {nullptr, nullptr},
199                    {nullptr, nullptr},
200                    {nullptr, nullptr},
201                    {nullptr, nullptr}};
202 
203   for (const auto *I : Attrs) {
204     const LoopHintAttr *LH = dyn_cast<LoopHintAttr>(I);
205 
206     // Skip non loop hint attributes
207     if (!LH)
208       continue;
209 
210     LoopHintAttr::OptionType Option = LH->getOption();
211     enum { Vectorize, Interleave, Unroll, UnrollAndJam, Distribute } Category;
212     switch (Option) {
213     case LoopHintAttr::Vectorize:
214     case LoopHintAttr::VectorizeWidth:
215       Category = Vectorize;
216       break;
217     case LoopHintAttr::Interleave:
218     case LoopHintAttr::InterleaveCount:
219       Category = Interleave;
220       break;
221     case LoopHintAttr::Unroll:
222     case LoopHintAttr::UnrollCount:
223       Category = Unroll;
224       break;
225     case LoopHintAttr::UnrollAndJam:
226     case LoopHintAttr::UnrollAndJamCount:
227       Category = UnrollAndJam;
228       break;
229     case LoopHintAttr::Distribute:
230       // Perform the check for duplicated 'distribute' hints.
231       Category = Distribute;
232       break;
233     };
234 
235     assert(Category < sizeof(HintAttrs) / sizeof(HintAttrs[0]));
236     auto &CategoryState = HintAttrs[Category];
237     const LoopHintAttr *PrevAttr;
238     if (Option == LoopHintAttr::Vectorize ||
239         Option == LoopHintAttr::Interleave || Option == LoopHintAttr::Unroll ||
240         Option == LoopHintAttr::UnrollAndJam ||
241         Option == LoopHintAttr::Distribute) {
242       // Enable|Disable|AssumeSafety hint.  For example, vectorize(enable).
243       PrevAttr = CategoryState.StateAttr;
244       CategoryState.StateAttr = LH;
245     } else {
246       // Numeric hint.  For example, vectorize_width(8).
247       PrevAttr = CategoryState.NumericAttr;
248       CategoryState.NumericAttr = LH;
249     }
250 
251     PrintingPolicy Policy(S.Context.getLangOpts());
252     SourceLocation OptionLoc = LH->getRange().getBegin();
253     if (PrevAttr)
254       // Cannot specify same type of attribute twice.
255       S.Diag(OptionLoc, diag::err_pragma_loop_compatibility)
256           << /*Duplicate=*/true << PrevAttr->getDiagnosticName(Policy)
257           << LH->getDiagnosticName(Policy);
258 
259     if (CategoryState.StateAttr && CategoryState.NumericAttr &&
260         (Category == Unroll || Category == UnrollAndJam ||
261          CategoryState.StateAttr->getState() == LoopHintAttr::Disable)) {
262       // Disable hints are not compatible with numeric hints of the same
263       // category.  As a special case, numeric unroll hints are also not
264       // compatible with enable or full form of the unroll pragma because these
265       // directives indicate full unrolling.
266       S.Diag(OptionLoc, diag::err_pragma_loop_compatibility)
267           << /*Duplicate=*/false
268           << CategoryState.StateAttr->getDiagnosticName(Policy)
269           << CategoryState.NumericAttr->getDiagnosticName(Policy);
270     }
271   }
272 }
273 
274 static Attr *handleOpenCLUnrollHint(Sema &S, Stmt *St, const ParsedAttr &A,
275                                     SourceRange Range) {
276   // Although the feature was introduced only in OpenCL C v2.0 s6.11.5, it's
277   // useful for OpenCL 1.x too and doesn't require HW support.
278   // opencl_unroll_hint can have 0 arguments (compiler
279   // determines unrolling factor) or 1 argument (the unroll factor provided
280   // by the user).
281 
282   unsigned NumArgs = A.getNumArgs();
283 
284   if (NumArgs > 1) {
285     S.Diag(A.getLoc(), diag::err_attribute_too_many_arguments) << A << 1;
286     return nullptr;
287   }
288 
289   unsigned UnrollFactor = 0;
290 
291   if (NumArgs == 1) {
292     Expr *E = A.getArgAsExpr(0);
293     llvm::APSInt ArgVal(32);
294 
295     if (!E->isIntegerConstantExpr(ArgVal, S.Context)) {
296       S.Diag(A.getLoc(), diag::err_attribute_argument_type)
297           << A << AANT_ArgumentIntegerConstant << E->getSourceRange();
298       return nullptr;
299     }
300 
301     int Val = ArgVal.getSExtValue();
302 
303     if (Val <= 0) {
304       S.Diag(A.getRange().getBegin(),
305              diag::err_attribute_requires_positive_integer)
306           << A << /* positive */ 0;
307       return nullptr;
308     }
309     UnrollFactor = Val;
310   }
311 
312   return OpenCLUnrollHintAttr::CreateImplicit(S.Context, UnrollFactor);
313 }
314 
315 static Attr *ProcessStmtAttribute(Sema &S, Stmt *St, const ParsedAttr &A,
316                                   SourceRange Range) {
317   switch (A.getKind()) {
318   case ParsedAttr::UnknownAttribute:
319     S.Diag(A.getLoc(), A.isDeclspecAttribute() ?
320            diag::warn_unhandled_ms_attribute_ignored :
321            diag::warn_unknown_attribute_ignored) << A.getName();
322     return nullptr;
323   case ParsedAttr::AT_FallThrough:
324     return handleFallThroughAttr(S, St, A, Range);
325   case ParsedAttr::AT_LoopHint:
326     return handleLoopHintAttr(S, St, A, Range);
327   case ParsedAttr::AT_OpenCLUnrollHint:
328     return handleOpenCLUnrollHint(S, St, A, Range);
329   case ParsedAttr::AT_Suppress:
330     return handleSuppressAttr(S, St, A, Range);
331   default:
332     // if we're here, then we parsed a known attribute, but didn't recognize
333     // it as a statement attribute => it is declaration attribute
334     S.Diag(A.getRange().getBegin(), diag::err_decl_attribute_invalid_on_stmt)
335         << A.getName() << St->getBeginLoc();
336     return nullptr;
337   }
338 }
339 
340 StmtResult Sema::ProcessStmtAttributes(Stmt *S,
341                                        const ParsedAttributesView &AttrList,
342                                        SourceRange Range) {
343   SmallVector<const Attr*, 8> Attrs;
344   for (const ParsedAttr &AL : AttrList) {
345     if (Attr *a = ProcessStmtAttribute(*this, S, AL, Range))
346       Attrs.push_back(a);
347   }
348 
349   CheckForIncompatibleAttributes(*this, Attrs);
350 
351   if (Attrs.empty())
352     return S;
353 
354   return ActOnAttributedStmt(Range.getBegin(), Attrs, S);
355 }
356