Clang Project

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