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/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"
22#include <optional>
23
24using namespace clang;
25using namespace sema;
26
27static Attr *handleFallThroughAttr(Sema &S, Stmt *St, const ParsedAttr &A,
28                                   SourceRange Range) {
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, ";");
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;
49
50  FnScope->setHasFallthroughStmt();
51  return ::new (S.Context) FallThroughAttr(S.Context, A);
52}
53
54static Attr *handleSuppressAttr(Sema &S, Stmt *St, const ParsedAttr &A,
55                                SourceRange Range) {
56  std::vector<StringRef> DiagnosticIdentifiers;
57  for (unsigned I = 0, E = A.getNumArgs(); I != E; ++I) {
58    StringRef RuleName;
59
60    if (!S.checkStringLiteralArgumentAttr(A, I, RuleName, nullptr))
61      return nullptr;
62
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);
66  }
67
68  return ::new (S.Context) SuppressAttr(
69      S.Context, A, DiagnosticIdentifiers.data(), DiagnosticIdentifiers.size());
70}
71
72static Attr *handleLoopHintAttr(Sema &S, Stmt *St, const ParsedAttr &A,
73                                SourceRange) {
74  IdentifierLoc *PragmaNameLoc = A.getArgAsIdent(0);
75  IdentifierLoc *OptionLoc = A.getArgAsIdent(1);
76  IdentifierLoc *StateLoc = A.getArgAsIdent(2);
77  Expr *ValueExpr = A.getArgAsExpr(3);
78
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");
84
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;
91    return nullptr;
92  }
93
94  LoopHintAttr::OptionType Option;
95  LoopHintAttr::LoopHintState State;
96
97  auto SetHints = [&Option, &State](LoopHintAttr::OptionType O,
98                                    LoopHintAttr::LoopHintState S) {
99    Option = O;
100    State = S;
101  };
102
103  if (PragmaName == "nounroll") {
104    SetHints(LoopHintAttr::Unroll, LoopHintAttr::Disable);
105  } else if (PragmaName == "unroll") {
106    // #pragma unroll N
107    if (ValueExpr)
108      SetHints(LoopHintAttr::UnrollCount, LoopHintAttr::Numeric);
109    else
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
115    if (ValueExpr)
116      SetHints(LoopHintAttr::UnrollAndJamCount, LoopHintAttr::Numeric);
117    else
118      SetHints(LoopHintAttr::UnrollAndJam, LoopHintAttr::Enable);
119  } else {
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()))
141        return nullptr;
142      if (StateLoc && StateLoc->Ident && StateLoc->Ident->isStr("scalable"))
143        State = LoopHintAttr::ScalableWidth;
144      else
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()))
151        return nullptr;
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;
168      else
169        llvm_unreachable("bad loop hint argument");
170    } else
171      llvm_unreachable("bad loop hint");
172  }
173
174  return LoopHintAttr::CreateImplicit(S.Context, Option, State, ValueExpr, A);
175}
176
177namespace {
178class CallExprFinder : public ConstEvaluatedExprVisitor<CallExprFinder> {
179  bool FoundAsmStmt = false;
180  std::vector<const CallExpr *> CallExprs;
181
182public:
183  typedef ConstEvaluatedExprVisitor<CallExprFinder> Inherited;
184
185  CallExprFinder(Sema &S, const Stmt *St) : Inherited(S.Context) { Visit(St); }
186
187  bool foundCallExpr() { return !CallExprs.empty(); }
188  const std::vector<const CallExpr *> &getCallExprs() { return CallExprs; }
189
190  bool foundAsmStmt() { return FoundAsmStmt; }
191
192  void VisitCallExpr(const CallExpr *E) { CallExprs.push_back(E); }
193
194  void VisitAsmStmt(const AsmStmt *S) { FoundAsmStmt = true; }
195
196  void Visit(const Stmt *St) {
197    if (!St)
198      return;
199    ConstEvaluatedExprVisitor<CallExprFinder>::Visit(St);
200  }
201};
202} // namespace
203
204static Attr *handleNoMergeAttr(Sema &S, Stmt *St, const ParsedAttr &A,
205                               SourceRange Range) {
206  NoMergeAttr NMA(S.Context, A);
207  CallExprFinder CEF(S, St);
208
209  if (!CEF.foundCallExpr() && !CEF.foundAsmStmt()) {
210    S.Diag(St->getBeginLoc(), diag::warn_attribute_ignored_no_calls_in_stmt)
211        << A;
212    return nullptr;
213  }
214
215  return ::new (S.Context) NoMergeAttr(S.Context, A);
216}
217
218static Attr *handleNoInlineAttr(Sema &S, Stmt *St, const ParsedAttr &A,
219                                SourceRange Range) {
220  NoInlineAttr NIA(S.Context, A);
221  if (!NIA.isClangNoInline()) {
222    S.Diag(St->getBeginLoc(), diag::warn_function_attribute_ignored_in_stmt)
223        << "[[clang::noinline]]";
224    return nullptr;
225  }
226
227  CallExprFinder CEF(S, St);
228  if (!CEF.foundCallExpr()) {
229    S.Diag(St->getBeginLoc(), diag::warn_attribute_ignored_no_calls_in_stmt)
230        << A;
231    return nullptr;
232  }
233
234  for (const auto *CallExpr : CEF.getCallExprs()) {
235    const Decl *Decl = CallExpr->getCalleeDecl();
236    if (Decl->hasAttr<AlwaysInlineAttr>() || Decl->hasAttr<FlattenAttr>())
237      S.Diag(St->getBeginLoc(), diag::warn_function_stmt_attribute_precedence)
238          << A << (Decl->hasAttr<AlwaysInlineAttr>() ? 0 : 1);
239  }
240
241  return ::new (S.Context) NoInlineAttr(S.Context, A);
242}
243
244static Attr *handleAlwaysInlineAttr(Sema &S, Stmt *St, const ParsedAttr &A,
245                                    SourceRange Range) {
246  AlwaysInlineAttr AIA(S.Context, A);
247  if (!AIA.isClangAlwaysInline()) {
248    S.Diag(St->getBeginLoc(), diag::warn_function_attribute_ignored_in_stmt)
249        << "[[clang::always_inline]]";
250    return nullptr;
251  }
252
253  CallExprFinder CEF(S, St);
254  if (!CEF.foundCallExpr()) {
255    S.Diag(St->getBeginLoc(), diag::warn_attribute_ignored_no_calls_in_stmt)
256        << A;
257    return nullptr;
258  }
259
260  for (const auto *CallExpr : CEF.getCallExprs()) {
261    const Decl *Decl = CallExpr->getCalleeDecl();
262    if (Decl->hasAttr<NoInlineAttr>() || Decl->hasAttr<FlattenAttr>())
263      S.Diag(St->getBeginLoc(), diag::warn_function_stmt_attribute_precedence)
264          << A << (Decl->hasAttr<NoInlineAttr>() ? 2 : 1);
265  }
266
267  return ::new (S.Context) AlwaysInlineAttr(S.Context, A);
268}
269
270static Attr *handleMustTailAttr(Sema &S, Stmt *St, const ParsedAttr &A,
271                                SourceRange Range) {
272  // Validation is in Sema::ActOnAttributedStmt().
273  return ::new (S.Context) MustTailAttr(S.Context, A);
274}
275
276static Attr *handleLikely(Sema &S, Stmt *St, const ParsedAttr &A,
277                          SourceRange Range) {
278
279  if (!S.getLangOpts().CPlusPlus20 && A.isCXX11Attribute() && !A.getScopeName())
280    S.Diag(A.getLoc(), diag::ext_cxx20_attr) << A << Range;
281
282  return ::new (S.Context) LikelyAttr(S.Context, A);
283}
284
285static Attr *handleUnlikely(Sema &S, Stmt *St, const ParsedAttr &A,
286                            SourceRange Range) {
287
288  if (!S.getLangOpts().CPlusPlus20 && A.isCXX11Attribute() && !A.getScopeName())
289    S.Diag(A.getLoc(), diag::ext_cxx20_attr) << A << Range;
290
291  return ::new (S.Context) UnlikelyAttr(S.Context, A);
292}
293
294#define WANT_STMT_MERGE_LOGIC
295#include "clang/Sema/AttrParsedAttrImpl.inc"
296#undef WANT_STMT_MERGE_LOGIC
297
298static void
299CheckForIncompatibleAttributes(Sema &S,
300                               const SmallVectorImpl<const Attr *> &Attrs) {
301  // The vast majority of attributed statements will only have one attribute
302  // on them, so skip all of the checking in the common case.
303  if (Attrs.size() < 2)
304    return;
305
306  // First, check for the easy cases that are table-generated for us.
307  if (!DiagnoseMutualExclusions(S, Attrs))
308    return;
309
310  enum CategoryType {
311    // For the following categories, they come in two variants: a state form and
312    // a numeric form. The state form may be one of default, enable, and
313    // disable. The numeric form provides an integer hint (for example, unroll
314    // count) to the transformer.
315    Vectorize,
316    Interleave,
317    UnrollAndJam,
318    Pipeline,
319    // For unroll, default indicates full unrolling rather than enabling the
320    // transformation.
321    Unroll,
322    // The loop distribution transformation only has a state form that is
323    // exposed by #pragma clang loop distribute (enable | disable).
324    Distribute,
325    // The vector predication only has a state form that is exposed by
326    // #pragma clang loop vectorize_predicate (enable | disable).
327    VectorizePredicate,
328    // This serves as a indicator to how many category are listed in this enum.
329    NumberOfCategories
330  };
331  // The following array accumulates the hints encountered while iterating
332  // through the attributes to check for compatibility.
333  struct {
334    const LoopHintAttr *StateAttr;
335    const LoopHintAttr *NumericAttr;
336  } HintAttrs[CategoryType::NumberOfCategories] = {};
337
338  for (const auto *I : Attrs) {
339    const LoopHintAttr *LH = dyn_cast<LoopHintAttr>(I);
340
341    // Skip non loop hint attributes
342    if (!LH)
343      continue;
344
345    CategoryType Category = CategoryType::NumberOfCategories;
346    LoopHintAttr::OptionType Option = LH->getOption();
347    switch (Option) {
348    case LoopHintAttr::Vectorize:
349    case LoopHintAttr::VectorizeWidth:
350      Category = Vectorize;
351      break;
352    case LoopHintAttr::Interleave:
353    case LoopHintAttr::InterleaveCount:
354      Category = Interleave;
355      break;
356    case LoopHintAttr::Unroll:
357    case LoopHintAttr::UnrollCount:
358      Category = Unroll;
359      break;
360    case LoopHintAttr::UnrollAndJam:
361    case LoopHintAttr::UnrollAndJamCount:
362      Category = UnrollAndJam;
363      break;
364    case LoopHintAttr::Distribute:
365      // Perform the check for duplicated 'distribute' hints.
366      Category = Distribute;
367      break;
368    case LoopHintAttr::PipelineDisabled:
369    case LoopHintAttr::PipelineInitiationInterval:
370      Category = Pipeline;
371      break;
372    case LoopHintAttr::VectorizePredicate:
373      Category = VectorizePredicate;
374      break;
375    };
376
377    assert(Category != NumberOfCategories && "Unhandled loop hint option");
378    auto &CategoryState = HintAttrs[Category];
379    const LoopHintAttr *PrevAttr;
380    if (Option == LoopHintAttr::Vectorize ||
381        Option == LoopHintAttr::Interleave || Option == LoopHintAttr::Unroll ||
382        Option == LoopHintAttr::UnrollAndJam ||
383        Option == LoopHintAttr::VectorizePredicate ||
384        Option == LoopHintAttr::PipelineDisabled ||
385        Option == LoopHintAttr::Distribute) {
386      // Enable|Disable|AssumeSafety hint.  For example, vectorize(enable).
387      PrevAttr = CategoryState.StateAttr;
388      CategoryState.StateAttr = LH;
389    } else {
390      // Numeric hint.  For example, vectorize_width(8).
391      PrevAttr = CategoryState.NumericAttr;
392      CategoryState.NumericAttr = LH;
393    }
394
395    PrintingPolicy Policy(S.Context.getLangOpts());
396    SourceLocation OptionLoc = LH->getRange().getBegin();
397    if (PrevAttr)
398      // Cannot specify same type of attribute twice.
399      S.Diag(OptionLoc, diag::err_pragma_loop_compatibility)
400          << /*Duplicate=*/true << PrevAttr->getDiagnosticName(Policy)
401          << LH->getDiagnosticName(Policy);
402
403    if (CategoryState.StateAttr && CategoryState.NumericAttr &&
404        (Category == Unroll || Category == UnrollAndJam ||
405         CategoryState.StateAttr->getState() == LoopHintAttr::Disable)) {
406      // Disable hints are not compatible with numeric hints of the same
407      // category.  As a special case, numeric unroll hints are also not
408      // compatible with enable or full form of the unroll pragma because these
409      // directives indicate full unrolling.
410      S.Diag(OptionLoc, diag::err_pragma_loop_compatibility)
411          << /*Duplicate=*/false
412          << CategoryState.StateAttr->getDiagnosticName(Policy)
413          << CategoryState.NumericAttr->getDiagnosticName(Policy);
414    }
415  }
416}
417
418static Attr *handleOpenCLUnrollHint(Sema &S, Stmt *St, const ParsedAttr &A,
419                                    SourceRange Range) {
420  // Although the feature was introduced only in OpenCL C v2.0 s6.11.5, it's
421  // useful for OpenCL 1.x too and doesn't require HW support.
422  // opencl_unroll_hint can have 0 arguments (compiler
423  // determines unrolling factor) or 1 argument (the unroll factor provided
424  // by the user).
425  unsigned UnrollFactor = 0;
426  if (A.getNumArgs() == 1) {
427    Expr *E = A.getArgAsExpr(0);
428    std::optional<llvm::APSInt> ArgVal;
429
430    if (!(ArgVal = E->getIntegerConstantExpr(S.Context))) {
431      S.Diag(A.getLoc(), diag::err_attribute_argument_type)
432          << A << AANT_ArgumentIntegerConstant << E->getSourceRange();
433      return nullptr;
434    }
435
436    int Val = ArgVal->getSExtValue();
437    if (Val <= 0) {
438      S.Diag(A.getRange().getBegin(),
439             diag::err_attribute_requires_positive_integer)
440          << A << /* positive */ 0;
441      return nullptr;
442    }
443    UnrollFactor = static_cast<unsigned>(Val);
444  }
445
446  return ::new (S.Context) OpenCLUnrollHintAttr(S.Context, A, UnrollFactor);
447}
448
449static Attr *ProcessStmtAttribute(Sema &S, Stmt *St, const ParsedAttr &A,
450                                  SourceRange Range) {
451  if (A.isInvalid() || A.getKind() == ParsedAttr::IgnoredAttribute)
452    return nullptr;
453
454  // Unknown attributes are automatically warned on. Target-specific attributes
455  // which do not apply to the current target architecture are treated as
456  // though they were unknown attributes.
457  const TargetInfo *Aux = S.Context.getAuxTargetInfo();
458  if (A.getKind() == ParsedAttr::UnknownAttribute ||
459      !(A.existsInTarget(S.Context.getTargetInfo()) ||
460        (S.Context.getLangOpts().SYCLIsDevice && Aux &&
461         A.existsInTarget(*Aux)))) {
462    S.Diag(A.getLoc(), A.isDeclspecAttribute()
463                           ? (unsigned)diag::warn_unhandled_ms_attribute_ignored
464                           : (unsigned)diag::warn_unknown_attribute_ignored)
465        << A << A.getRange();
466    return nullptr;
467  }
468
469  if (S.checkCommonAttributeFeatures(St, A))
470    return nullptr;
471
472  switch (A.getKind()) {
473  case ParsedAttr::AT_AlwaysInline:
474    return handleAlwaysInlineAttr(S, St, A, Range);
475  case ParsedAttr::AT_FallThrough:
476    return handleFallThroughAttr(S, St, A, Range);
477  case ParsedAttr::AT_LoopHint:
478    return handleLoopHintAttr(S, St, A, Range);
479  case ParsedAttr::AT_OpenCLUnrollHint:
480    return handleOpenCLUnrollHint(S, St, A, Range);
481  case ParsedAttr::AT_Suppress:
482    return handleSuppressAttr(S, St, A, Range);
483  case ParsedAttr::AT_NoMerge:
484    return handleNoMergeAttr(S, St, A, Range);
485  case ParsedAttr::AT_NoInline:
486    return handleNoInlineAttr(S, St, A, Range);
487  case ParsedAttr::AT_MustTail:
488    return handleMustTailAttr(S, St, A, Range);
489  case ParsedAttr::AT_Likely:
490    return handleLikely(S, St, A, Range);
491  case ParsedAttr::AT_Unlikely:
492    return handleUnlikely(S, St, A, Range);
493  default:
494    // N.B., ClangAttrEmitter.cpp emits a diagnostic helper that ensures a
495    // declaration attribute is not written on a statement, but this code is
496    // needed for attributes in Attr.td that do not list any subjects.
497    S.Diag(A.getRange().getBegin(), diag::err_decl_attribute_invalid_on_stmt)
498        << A << St->getBeginLoc();
499    return nullptr;
500  }
501}
502
503void Sema::ProcessStmtAttributes(Stmt *S, const ParsedAttributes &InAttrs,
504                                 SmallVectorImpl<const Attr *> &OutAttrs) {
505  for (const ParsedAttr &AL : InAttrs) {
506    if (const Attr *A = ProcessStmtAttribute(*this, S, AL, InAttrs.Range))
507      OutAttrs.push_back(A);
508  }
509
510  CheckForIncompatibleAttributes(*this, OutAttrs);
511}
512