Coverage Report

Created: 2024-01-17 10:31

/src/llvm-project/clang/lib/Sema/SemaStmtAttr.cpp
Line
Count
Source (jump to first uncovered line)
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
24
using namespace clang;
25
using namespace sema;
26
27
static Attr *handleFallThroughAttr(Sema &S, Stmt *St, const ParsedAttr &A,
28
0
                                   SourceRange Range) {
29
0
  FallThroughAttr Attr(S.Context, A);
30
0
  if (isa<SwitchCase>(St)) {
31
0
    S.Diag(A.getRange().getBegin(), diag::err_fallthrough_attr_wrong_target)
32
0
        << A << St->getBeginLoc();
33
0
    SourceLocation L = S.getLocForEndOfToken(Range.getEnd());
34
0
    S.Diag(L, diag::note_fallthrough_insert_semi_fixit)
35
0
        << FixItHint::CreateInsertion(L, ";");
36
0
    return nullptr;
37
0
  }
38
0
  auto *FnScope = S.getCurFunction();
39
0
  if (FnScope->SwitchStack.empty()) {
40
0
    S.Diag(A.getRange().getBegin(), diag::err_fallthrough_attr_outside_switch);
41
0
    return nullptr;
42
0
  }
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
0
  if (!S.getLangOpts().CPlusPlus17 && A.isCXX11Attribute() &&
47
0
      !A.getScopeName())
48
0
    S.Diag(A.getLoc(), diag::ext_cxx17_attr) << A;
49
50
0
  FnScope->setHasFallthroughStmt();
51
0
  return ::new (S.Context) FallThroughAttr(S.Context, A);
52
0
}
53
54
static Attr *handleSuppressAttr(Sema &S, Stmt *St, const ParsedAttr &A,
55
0
                                SourceRange Range) {
56
0
  if (A.getAttributeSpellingListIndex() == SuppressAttr::CXX11_gsl_suppress &&
57
0
      A.getNumArgs() < 1) {
58
    // Suppression attribute with GSL spelling requires at least 1 argument.
59
0
    S.Diag(A.getLoc(), diag::err_attribute_too_few_arguments) << A << 1;
60
0
    return nullptr;
61
0
  }
62
63
0
  std::vector<StringRef> DiagnosticIdentifiers;
64
0
  for (unsigned I = 0, E = A.getNumArgs(); I != E; ++I) {
65
0
    StringRef RuleName;
66
67
0
    if (!S.checkStringLiteralArgumentAttr(A, I, RuleName, nullptr))
68
0
      return nullptr;
69
70
0
    DiagnosticIdentifiers.push_back(RuleName);
71
0
  }
72
73
0
  return ::new (S.Context) SuppressAttr(
74
0
      S.Context, A, DiagnosticIdentifiers.data(), DiagnosticIdentifiers.size());
75
0
}
76
77
static Attr *handleLoopHintAttr(Sema &S, Stmt *St, const ParsedAttr &A,
78
0
                                SourceRange) {
79
0
  IdentifierLoc *PragmaNameLoc = A.getArgAsIdent(0);
80
0
  IdentifierLoc *OptionLoc = A.getArgAsIdent(1);
81
0
  IdentifierLoc *StateLoc = A.getArgAsIdent(2);
82
0
  Expr *ValueExpr = A.getArgAsExpr(3);
83
84
0
  StringRef PragmaName =
85
0
      llvm::StringSwitch<StringRef>(PragmaNameLoc->Ident->getName())
86
0
          .Cases("unroll", "nounroll", "unroll_and_jam", "nounroll_and_jam",
87
0
                 PragmaNameLoc->Ident->getName())
88
0
          .Default("clang loop");
89
90
  // This could be handled automatically by adding a Subjects definition in
91
  // Attr.td, but that would make the diagnostic behavior worse in this case
92
  // because the user spells this attribute as a pragma.
93
0
  if (!isa<DoStmt, ForStmt, CXXForRangeStmt, WhileStmt>(St)) {
94
0
    std::string Pragma = "#pragma " + std::string(PragmaName);
95
0
    S.Diag(St->getBeginLoc(), diag::err_pragma_loop_precedes_nonloop) << Pragma;
96
0
    return nullptr;
97
0
  }
98
99
0
  LoopHintAttr::OptionType Option;
100
0
  LoopHintAttr::LoopHintState State;
101
102
0
  auto SetHints = [&Option, &State](LoopHintAttr::OptionType O,
103
0
                                    LoopHintAttr::LoopHintState S) {
104
0
    Option = O;
105
0
    State = S;
106
0
  };
107
108
0
  if (PragmaName == "nounroll") {
109
0
    SetHints(LoopHintAttr::Unroll, LoopHintAttr::Disable);
110
0
  } else if (PragmaName == "unroll") {
111
    // #pragma unroll N
112
0
    if (ValueExpr)
113
0
      SetHints(LoopHintAttr::UnrollCount, LoopHintAttr::Numeric);
114
0
    else
115
0
      SetHints(LoopHintAttr::Unroll, LoopHintAttr::Enable);
116
0
  } else if (PragmaName == "nounroll_and_jam") {
117
0
    SetHints(LoopHintAttr::UnrollAndJam, LoopHintAttr::Disable);
118
0
  } else if (PragmaName == "unroll_and_jam") {
119
    // #pragma unroll_and_jam N
120
0
    if (ValueExpr)
121
0
      SetHints(LoopHintAttr::UnrollAndJamCount, LoopHintAttr::Numeric);
122
0
    else
123
0
      SetHints(LoopHintAttr::UnrollAndJam, LoopHintAttr::Enable);
124
0
  } else {
125
    // #pragma clang loop ...
126
0
    assert(OptionLoc && OptionLoc->Ident &&
127
0
           "Attribute must have valid option info.");
128
0
    Option = llvm::StringSwitch<LoopHintAttr::OptionType>(
129
0
                 OptionLoc->Ident->getName())
130
0
                 .Case("vectorize", LoopHintAttr::Vectorize)
131
0
                 .Case("vectorize_width", LoopHintAttr::VectorizeWidth)
132
0
                 .Case("interleave", LoopHintAttr::Interleave)
133
0
                 .Case("vectorize_predicate", LoopHintAttr::VectorizePredicate)
134
0
                 .Case("interleave_count", LoopHintAttr::InterleaveCount)
135
0
                 .Case("unroll", LoopHintAttr::Unroll)
136
0
                 .Case("unroll_count", LoopHintAttr::UnrollCount)
137
0
                 .Case("pipeline", LoopHintAttr::PipelineDisabled)
138
0
                 .Case("pipeline_initiation_interval",
139
0
                       LoopHintAttr::PipelineInitiationInterval)
140
0
                 .Case("distribute", LoopHintAttr::Distribute)
141
0
                 .Default(LoopHintAttr::Vectorize);
142
0
    if (Option == LoopHintAttr::VectorizeWidth) {
143
0
      assert((ValueExpr || (StateLoc && StateLoc->Ident)) &&
144
0
             "Attribute must have a valid value expression or argument.");
145
0
      if (ValueExpr && S.CheckLoopHintExpr(ValueExpr, St->getBeginLoc()))
146
0
        return nullptr;
147
0
      if (StateLoc && StateLoc->Ident && StateLoc->Ident->isStr("scalable"))
148
0
        State = LoopHintAttr::ScalableWidth;
149
0
      else
150
0
        State = LoopHintAttr::FixedWidth;
151
0
    } else if (Option == LoopHintAttr::InterleaveCount ||
152
0
               Option == LoopHintAttr::UnrollCount ||
153
0
               Option == LoopHintAttr::PipelineInitiationInterval) {
154
0
      assert(ValueExpr && "Attribute must have a valid value expression.");
155
0
      if (S.CheckLoopHintExpr(ValueExpr, St->getBeginLoc()))
156
0
        return nullptr;
157
0
      State = LoopHintAttr::Numeric;
158
0
    } else if (Option == LoopHintAttr::Vectorize ||
159
0
               Option == LoopHintAttr::Interleave ||
160
0
               Option == LoopHintAttr::VectorizePredicate ||
161
0
               Option == LoopHintAttr::Unroll ||
162
0
               Option == LoopHintAttr::Distribute ||
163
0
               Option == LoopHintAttr::PipelineDisabled) {
164
0
      assert(StateLoc && StateLoc->Ident && "Loop hint must have an argument");
165
0
      if (StateLoc->Ident->isStr("disable"))
166
0
        State = LoopHintAttr::Disable;
167
0
      else if (StateLoc->Ident->isStr("assume_safety"))
168
0
        State = LoopHintAttr::AssumeSafety;
169
0
      else if (StateLoc->Ident->isStr("full"))
170
0
        State = LoopHintAttr::Full;
171
0
      else if (StateLoc->Ident->isStr("enable"))
172
0
        State = LoopHintAttr::Enable;
173
0
      else
174
0
        llvm_unreachable("bad loop hint argument");
175
0
    } else
176
0
      llvm_unreachable("bad loop hint");
177
0
  }
178
179
0
  return LoopHintAttr::CreateImplicit(S.Context, Option, State, ValueExpr, A);
180
0
}
181
182
namespace {
183
class CallExprFinder : public ConstEvaluatedExprVisitor<CallExprFinder> {
184
  bool FoundAsmStmt = false;
185
  std::vector<const CallExpr *> CallExprs;
186
187
public:
188
  typedef ConstEvaluatedExprVisitor<CallExprFinder> Inherited;
189
190
0
  CallExprFinder(Sema &S, const Stmt *St) : Inherited(S.Context) { Visit(St); }
191
192
0
  bool foundCallExpr() { return !CallExprs.empty(); }
193
0
  const std::vector<const CallExpr *> &getCallExprs() { return CallExprs; }
194
195
0
  bool foundAsmStmt() { return FoundAsmStmt; }
196
197
0
  void VisitCallExpr(const CallExpr *E) { CallExprs.push_back(E); }
198
199
0
  void VisitAsmStmt(const AsmStmt *S) { FoundAsmStmt = true; }
200
201
0
  void Visit(const Stmt *St) {
202
0
    if (!St)
203
0
      return;
204
0
    ConstEvaluatedExprVisitor<CallExprFinder>::Visit(St);
205
0
  }
206
};
207
} // namespace
208
209
static Attr *handleNoMergeAttr(Sema &S, Stmt *St, const ParsedAttr &A,
210
0
                               SourceRange Range) {
211
0
  NoMergeAttr NMA(S.Context, A);
212
0
  CallExprFinder CEF(S, St);
213
214
0
  if (!CEF.foundCallExpr() && !CEF.foundAsmStmt()) {
215
0
    S.Diag(St->getBeginLoc(), diag::warn_attribute_ignored_no_calls_in_stmt)
216
0
        << A;
217
0
    return nullptr;
218
0
  }
219
220
0
  return ::new (S.Context) NoMergeAttr(S.Context, A);
221
0
}
222
223
template <typename OtherAttr, int DiagIdx>
224
static bool CheckStmtInlineAttr(Sema &SemaRef, const Stmt *OrigSt,
225
                                const Stmt *CurSt,
226
0
                                const AttributeCommonInfo &A) {
227
0
  CallExprFinder OrigCEF(SemaRef, OrigSt);
228
0
  CallExprFinder CEF(SemaRef, CurSt);
229
230
  // If the call expressions lists are equal in size, we can skip
231
  // previously emitted diagnostics. However, if the statement has a pack
232
  // expansion, we have no way of telling which CallExpr is the instantiated
233
  // version of the other. In this case, we will end up re-diagnosing in the
234
  // instantiation.
235
  // ie: [[clang::always_inline]] non_dependent(), (other_call<Pack>()...)
236
  // will diagnose nondependent again.
237
0
  bool CanSuppressDiag =
238
0
      OrigSt && CEF.getCallExprs().size() == OrigCEF.getCallExprs().size();
239
240
0
  if (!CEF.foundCallExpr()) {
241
0
    return SemaRef.Diag(CurSt->getBeginLoc(),
242
0
                        diag::warn_attribute_ignored_no_calls_in_stmt)
243
0
           << A;
244
0
  }
245
246
0
  for (const auto &Tup :
247
0
       llvm::zip_longest(OrigCEF.getCallExprs(), CEF.getCallExprs())) {
248
    // If the original call expression already had a callee, we already
249
    // diagnosed this, so skip it here. We can't skip if there isn't a 1:1
250
    // relationship between the two lists of call expressions.
251
0
    if (!CanSuppressDiag || !(*std::get<0>(Tup))->getCalleeDecl()) {
252
0
      const Decl *Callee = (*std::get<1>(Tup))->getCalleeDecl();
253
0
      if (Callee &&
254
0
          (Callee->hasAttr<OtherAttr>() || Callee->hasAttr<FlattenAttr>())) {
255
0
        SemaRef.Diag(CurSt->getBeginLoc(),
256
0
                     diag::warn_function_stmt_attribute_precedence)
257
0
            << A << (Callee->hasAttr<OtherAttr>() ? DiagIdx : 1);
258
0
        SemaRef.Diag(Callee->getBeginLoc(), diag::note_conflicting_attribute);
259
0
      }
260
0
    }
261
0
  }
262
263
0
  return false;
264
0
}
Unexecuted instantiation: SemaStmtAttr.cpp:bool CheckStmtInlineAttr<clang::AlwaysInlineAttr, 0>(clang::Sema&, clang::Stmt const*, clang::Stmt const*, clang::AttributeCommonInfo const&)
Unexecuted instantiation: SemaStmtAttr.cpp:bool CheckStmtInlineAttr<clang::NoInlineAttr, 2>(clang::Sema&, clang::Stmt const*, clang::Stmt const*, clang::AttributeCommonInfo const&)
265
266
bool Sema::CheckNoInlineAttr(const Stmt *OrigSt, const Stmt *CurSt,
267
0
                             const AttributeCommonInfo &A) {
268
0
  return CheckStmtInlineAttr<AlwaysInlineAttr, 0>(*this, OrigSt, CurSt, A);
269
0
}
270
271
bool Sema::CheckAlwaysInlineAttr(const Stmt *OrigSt, const Stmt *CurSt,
272
0
                                 const AttributeCommonInfo &A) {
273
0
  return CheckStmtInlineAttr<NoInlineAttr, 2>(*this, OrigSt, CurSt, A);
274
0
}
275
276
static Attr *handleNoInlineAttr(Sema &S, Stmt *St, const ParsedAttr &A,
277
0
                                SourceRange Range) {
278
0
  NoInlineAttr NIA(S.Context, A);
279
0
  if (!NIA.isClangNoInline()) {
280
0
    S.Diag(St->getBeginLoc(), diag::warn_function_attribute_ignored_in_stmt)
281
0
        << "[[clang::noinline]]";
282
0
    return nullptr;
283
0
  }
284
285
0
  if (S.CheckNoInlineAttr(/*OrigSt=*/nullptr, St, A))
286
0
    return nullptr;
287
288
0
  return ::new (S.Context) NoInlineAttr(S.Context, A);
289
0
}
290
291
static Attr *handleAlwaysInlineAttr(Sema &S, Stmt *St, const ParsedAttr &A,
292
0
                                    SourceRange Range) {
293
0
  AlwaysInlineAttr AIA(S.Context, A);
294
0
  if (!AIA.isClangAlwaysInline()) {
295
0
    S.Diag(St->getBeginLoc(), diag::warn_function_attribute_ignored_in_stmt)
296
0
        << "[[clang::always_inline]]";
297
0
    return nullptr;
298
0
  }
299
300
0
  if (S.CheckAlwaysInlineAttr(/*OrigSt=*/nullptr, St, A))
301
0
    return nullptr;
302
303
0
  return ::new (S.Context) AlwaysInlineAttr(S.Context, A);
304
0
}
305
306
static Attr *handleMustTailAttr(Sema &S, Stmt *St, const ParsedAttr &A,
307
0
                                SourceRange Range) {
308
  // Validation is in Sema::ActOnAttributedStmt().
309
0
  return ::new (S.Context) MustTailAttr(S.Context, A);
310
0
}
311
312
static Attr *handleLikely(Sema &S, Stmt *St, const ParsedAttr &A,
313
0
                          SourceRange Range) {
314
315
0
  if (!S.getLangOpts().CPlusPlus20 && A.isCXX11Attribute() && !A.getScopeName())
316
0
    S.Diag(A.getLoc(), diag::ext_cxx20_attr) << A << Range;
317
318
0
  return ::new (S.Context) LikelyAttr(S.Context, A);
319
0
}
320
321
static Attr *handleUnlikely(Sema &S, Stmt *St, const ParsedAttr &A,
322
0
                            SourceRange Range) {
323
324
0
  if (!S.getLangOpts().CPlusPlus20 && A.isCXX11Attribute() && !A.getScopeName())
325
0
    S.Diag(A.getLoc(), diag::ext_cxx20_attr) << A << Range;
326
327
0
  return ::new (S.Context) UnlikelyAttr(S.Context, A);
328
0
}
329
330
CodeAlignAttr *Sema::BuildCodeAlignAttr(const AttributeCommonInfo &CI,
331
0
                                        Expr *E) {
332
0
  if (!E->isValueDependent()) {
333
0
    llvm::APSInt ArgVal;
334
0
    ExprResult Res = VerifyIntegerConstantExpression(E, &ArgVal);
335
0
    if (Res.isInvalid())
336
0
      return nullptr;
337
0
    E = Res.get();
338
339
    // This attribute requires an integer argument which is a constant power of
340
    // two between 1 and 4096 inclusive.
341
0
    if (ArgVal < CodeAlignAttr::MinimumAlignment ||
342
0
        ArgVal > CodeAlignAttr::MaximumAlignment || !ArgVal.isPowerOf2()) {
343
0
      if (std::optional<int64_t> Value = ArgVal.trySExtValue())
344
0
        Diag(CI.getLoc(), diag::err_attribute_power_of_two_in_range)
345
0
            << CI << CodeAlignAttr::MinimumAlignment
346
0
            << CodeAlignAttr::MaximumAlignment << Value.value();
347
0
      else
348
0
        Diag(CI.getLoc(), diag::err_attribute_power_of_two_in_range)
349
0
            << CI << CodeAlignAttr::MinimumAlignment
350
0
            << CodeAlignAttr::MaximumAlignment << E;
351
0
      return nullptr;
352
0
    }
353
0
  }
354
0
  return new (Context) CodeAlignAttr(Context, CI, E);
355
0
}
356
357
0
static Attr *handleCodeAlignAttr(Sema &S, Stmt *St, const ParsedAttr &A) {
358
359
0
  Expr *E = A.getArgAsExpr(0);
360
0
  return S.BuildCodeAlignAttr(A, E);
361
0
}
362
363
// Diagnose non-identical duplicates as a 'conflicting' loop attributes
364
// and suppress duplicate errors in cases where the two match.
365
template <typename LoopAttrT>
366
0
static void CheckForDuplicateLoopAttrs(Sema &S, ArrayRef<const Attr *> Attrs) {
367
0
  auto FindFunc = [](const Attr *A) { return isa<const LoopAttrT>(A); };
368
0
  const auto *FirstItr = std::find_if(Attrs.begin(), Attrs.end(), FindFunc);
369
370
0
  if (FirstItr == Attrs.end()) // no attributes found
371
0
    return;
372
373
0
  const auto *LastFoundItr = FirstItr;
374
0
  std::optional<llvm::APSInt> FirstValue;
375
376
0
  const auto *CAFA =
377
0
      dyn_cast<ConstantExpr>(cast<LoopAttrT>(*FirstItr)->getAlignment());
378
  // Return early if first alignment expression is dependent (since we don't
379
  // know what the effective size will be), and skip the loop entirely.
380
0
  if (!CAFA)
381
0
    return;
382
383
0
  while (Attrs.end() != (LastFoundItr = std::find_if(LastFoundItr + 1,
384
0
                                                     Attrs.end(), FindFunc))) {
385
0
    const auto *CASA =
386
0
        dyn_cast<ConstantExpr>(cast<LoopAttrT>(*LastFoundItr)->getAlignment());
387
    // If the value is dependent, we can not test anything.
388
0
    if (!CASA)
389
0
      return;
390
    // Test the attribute values.
391
0
    llvm::APSInt SecondValue = CASA->getResultAsAPSInt();
392
0
    if (!FirstValue)
393
0
      FirstValue = CAFA->getResultAsAPSInt();
394
395
0
    if (FirstValue != SecondValue) {
396
0
      S.Diag((*LastFoundItr)->getLocation(), diag::err_loop_attr_conflict)
397
0
          << *FirstItr;
398
0
      S.Diag((*FirstItr)->getLocation(), diag::note_previous_attribute);
399
0
    }
400
0
    return;
401
0
  }
402
0
}
403
404
static Attr *handleMSConstexprAttr(Sema &S, Stmt *St, const ParsedAttr &A,
405
0
                                   SourceRange Range) {
406
0
  if (!S.getLangOpts().isCompatibleWithMSVC(LangOptions::MSVC2022_3)) {
407
0
    S.Diag(A.getLoc(), diag::warn_unknown_attribute_ignored)
408
0
        << A << A.getRange();
409
0
    return nullptr;
410
0
  }
411
0
  return ::new (S.Context) MSConstexprAttr(S.Context, A);
412
0
}
413
414
#define WANT_STMT_MERGE_LOGIC
415
#include "clang/Sema/AttrParsedAttrImpl.inc"
416
#undef WANT_STMT_MERGE_LOGIC
417
418
static void
419
CheckForIncompatibleAttributes(Sema &S,
420
0
                               const SmallVectorImpl<const Attr *> &Attrs) {
421
  // The vast majority of attributed statements will only have one attribute
422
  // on them, so skip all of the checking in the common case.
423
0
  if (Attrs.size() < 2)
424
0
    return;
425
426
  // First, check for the easy cases that are table-generated for us.
427
0
  if (!DiagnoseMutualExclusions(S, Attrs))
428
0
    return;
429
430
0
  enum CategoryType {
431
    // For the following categories, they come in two variants: a state form and
432
    // a numeric form. The state form may be one of default, enable, and
433
    // disable. The numeric form provides an integer hint (for example, unroll
434
    // count) to the transformer.
435
0
    Vectorize,
436
0
    Interleave,
437
0
    UnrollAndJam,
438
0
    Pipeline,
439
    // For unroll, default indicates full unrolling rather than enabling the
440
    // transformation.
441
0
    Unroll,
442
    // The loop distribution transformation only has a state form that is
443
    // exposed by #pragma clang loop distribute (enable | disable).
444
0
    Distribute,
445
    // The vector predication only has a state form that is exposed by
446
    // #pragma clang loop vectorize_predicate (enable | disable).
447
0
    VectorizePredicate,
448
    // This serves as a indicator to how many category are listed in this enum.
449
0
    NumberOfCategories
450
0
  };
451
  // The following array accumulates the hints encountered while iterating
452
  // through the attributes to check for compatibility.
453
0
  struct {
454
0
    const LoopHintAttr *StateAttr;
455
0
    const LoopHintAttr *NumericAttr;
456
0
  } HintAttrs[CategoryType::NumberOfCategories] = {};
457
458
0
  for (const auto *I : Attrs) {
459
0
    const LoopHintAttr *LH = dyn_cast<LoopHintAttr>(I);
460
461
    // Skip non loop hint attributes
462
0
    if (!LH)
463
0
      continue;
464
465
0
    CategoryType Category = CategoryType::NumberOfCategories;
466
0
    LoopHintAttr::OptionType Option = LH->getOption();
467
0
    switch (Option) {
468
0
    case LoopHintAttr::Vectorize:
469
0
    case LoopHintAttr::VectorizeWidth:
470
0
      Category = Vectorize;
471
0
      break;
472
0
    case LoopHintAttr::Interleave:
473
0
    case LoopHintAttr::InterleaveCount:
474
0
      Category = Interleave;
475
0
      break;
476
0
    case LoopHintAttr::Unroll:
477
0
    case LoopHintAttr::UnrollCount:
478
0
      Category = Unroll;
479
0
      break;
480
0
    case LoopHintAttr::UnrollAndJam:
481
0
    case LoopHintAttr::UnrollAndJamCount:
482
0
      Category = UnrollAndJam;
483
0
      break;
484
0
    case LoopHintAttr::Distribute:
485
      // Perform the check for duplicated 'distribute' hints.
486
0
      Category = Distribute;
487
0
      break;
488
0
    case LoopHintAttr::PipelineDisabled:
489
0
    case LoopHintAttr::PipelineInitiationInterval:
490
0
      Category = Pipeline;
491
0
      break;
492
0
    case LoopHintAttr::VectorizePredicate:
493
0
      Category = VectorizePredicate;
494
0
      break;
495
0
    };
496
497
0
    assert(Category != NumberOfCategories && "Unhandled loop hint option");
498
0
    auto &CategoryState = HintAttrs[Category];
499
0
    const LoopHintAttr *PrevAttr;
500
0
    if (Option == LoopHintAttr::Vectorize ||
501
0
        Option == LoopHintAttr::Interleave || Option == LoopHintAttr::Unroll ||
502
0
        Option == LoopHintAttr::UnrollAndJam ||
503
0
        Option == LoopHintAttr::VectorizePredicate ||
504
0
        Option == LoopHintAttr::PipelineDisabled ||
505
0
        Option == LoopHintAttr::Distribute) {
506
      // Enable|Disable|AssumeSafety hint.  For example, vectorize(enable).
507
0
      PrevAttr = CategoryState.StateAttr;
508
0
      CategoryState.StateAttr = LH;
509
0
    } else {
510
      // Numeric hint.  For example, vectorize_width(8).
511
0
      PrevAttr = CategoryState.NumericAttr;
512
0
      CategoryState.NumericAttr = LH;
513
0
    }
514
515
0
    PrintingPolicy Policy(S.Context.getLangOpts());
516
0
    SourceLocation OptionLoc = LH->getRange().getBegin();
517
0
    if (PrevAttr)
518
      // Cannot specify same type of attribute twice.
519
0
      S.Diag(OptionLoc, diag::err_pragma_loop_compatibility)
520
0
          << /*Duplicate=*/true << PrevAttr->getDiagnosticName(Policy)
521
0
          << LH->getDiagnosticName(Policy);
522
523
0
    if (CategoryState.StateAttr && CategoryState.NumericAttr &&
524
0
        (Category == Unroll || Category == UnrollAndJam ||
525
0
         CategoryState.StateAttr->getState() == LoopHintAttr::Disable)) {
526
      // Disable hints are not compatible with numeric hints of the same
527
      // category.  As a special case, numeric unroll hints are also not
528
      // compatible with enable or full form of the unroll pragma because these
529
      // directives indicate full unrolling.
530
0
      S.Diag(OptionLoc, diag::err_pragma_loop_compatibility)
531
0
          << /*Duplicate=*/false
532
0
          << CategoryState.StateAttr->getDiagnosticName(Policy)
533
0
          << CategoryState.NumericAttr->getDiagnosticName(Policy);
534
0
    }
535
0
  }
536
0
}
537
538
static Attr *handleOpenCLUnrollHint(Sema &S, Stmt *St, const ParsedAttr &A,
539
0
                                    SourceRange Range) {
540
  // Although the feature was introduced only in OpenCL C v2.0 s6.11.5, it's
541
  // useful for OpenCL 1.x too and doesn't require HW support.
542
  // opencl_unroll_hint can have 0 arguments (compiler
543
  // determines unrolling factor) or 1 argument (the unroll factor provided
544
  // by the user).
545
0
  unsigned UnrollFactor = 0;
546
0
  if (A.getNumArgs() == 1) {
547
0
    Expr *E = A.getArgAsExpr(0);
548
0
    std::optional<llvm::APSInt> ArgVal;
549
550
0
    if (!(ArgVal = E->getIntegerConstantExpr(S.Context))) {
551
0
      S.Diag(A.getLoc(), diag::err_attribute_argument_type)
552
0
          << A << AANT_ArgumentIntegerConstant << E->getSourceRange();
553
0
      return nullptr;
554
0
    }
555
556
0
    int Val = ArgVal->getSExtValue();
557
0
    if (Val <= 0) {
558
0
      S.Diag(A.getRange().getBegin(),
559
0
             diag::err_attribute_requires_positive_integer)
560
0
          << A << /* positive */ 0;
561
0
      return nullptr;
562
0
    }
563
0
    UnrollFactor = static_cast<unsigned>(Val);
564
0
  }
565
566
0
  return ::new (S.Context) OpenCLUnrollHintAttr(S.Context, A, UnrollFactor);
567
0
}
568
569
static Attr *ProcessStmtAttribute(Sema &S, Stmt *St, const ParsedAttr &A,
570
0
                                  SourceRange Range) {
571
0
  if (A.isInvalid() || A.getKind() == ParsedAttr::IgnoredAttribute)
572
0
    return nullptr;
573
574
  // Unknown attributes are automatically warned on. Target-specific attributes
575
  // which do not apply to the current target architecture are treated as
576
  // though they were unknown attributes.
577
0
  const TargetInfo *Aux = S.Context.getAuxTargetInfo();
578
0
  if (A.getKind() == ParsedAttr::UnknownAttribute ||
579
0
      !(A.existsInTarget(S.Context.getTargetInfo()) ||
580
0
        (S.Context.getLangOpts().SYCLIsDevice && Aux &&
581
0
         A.existsInTarget(*Aux)))) {
582
0
    S.Diag(A.getLoc(), A.isRegularKeywordAttribute()
583
0
                           ? (unsigned)diag::err_keyword_not_supported_on_target
584
0
                       : A.isDeclspecAttribute()
585
0
                           ? (unsigned)diag::warn_unhandled_ms_attribute_ignored
586
0
                           : (unsigned)diag::warn_unknown_attribute_ignored)
587
0
        << A << A.getRange();
588
0
    return nullptr;
589
0
  }
590
591
0
  if (S.checkCommonAttributeFeatures(St, A))
592
0
    return nullptr;
593
594
0
  switch (A.getKind()) {
595
0
  case ParsedAttr::AT_AlwaysInline:
596
0
    return handleAlwaysInlineAttr(S, St, A, Range);
597
0
  case ParsedAttr::AT_FallThrough:
598
0
    return handleFallThroughAttr(S, St, A, Range);
599
0
  case ParsedAttr::AT_LoopHint:
600
0
    return handleLoopHintAttr(S, St, A, Range);
601
0
  case ParsedAttr::AT_OpenCLUnrollHint:
602
0
    return handleOpenCLUnrollHint(S, St, A, Range);
603
0
  case ParsedAttr::AT_Suppress:
604
0
    return handleSuppressAttr(S, St, A, Range);
605
0
  case ParsedAttr::AT_NoMerge:
606
0
    return handleNoMergeAttr(S, St, A, Range);
607
0
  case ParsedAttr::AT_NoInline:
608
0
    return handleNoInlineAttr(S, St, A, Range);
609
0
  case ParsedAttr::AT_MustTail:
610
0
    return handleMustTailAttr(S, St, A, Range);
611
0
  case ParsedAttr::AT_Likely:
612
0
    return handleLikely(S, St, A, Range);
613
0
  case ParsedAttr::AT_Unlikely:
614
0
    return handleUnlikely(S, St, A, Range);
615
0
  case ParsedAttr::AT_CodeAlign:
616
0
    return handleCodeAlignAttr(S, St, A);
617
0
  case ParsedAttr::AT_MSConstexpr:
618
0
    return handleMSConstexprAttr(S, St, A, Range);
619
0
  default:
620
    // N.B., ClangAttrEmitter.cpp emits a diagnostic helper that ensures a
621
    // declaration attribute is not written on a statement, but this code is
622
    // needed for attributes in Attr.td that do not list any subjects.
623
0
    S.Diag(A.getRange().getBegin(), diag::err_decl_attribute_invalid_on_stmt)
624
0
        << A << A.isRegularKeywordAttribute() << St->getBeginLoc();
625
0
    return nullptr;
626
0
  }
627
0
}
628
629
void Sema::ProcessStmtAttributes(Stmt *S, const ParsedAttributes &InAttrs,
630
0
                                 SmallVectorImpl<const Attr *> &OutAttrs) {
631
0
  for (const ParsedAttr &AL : InAttrs) {
632
0
    if (const Attr *A = ProcessStmtAttribute(*this, S, AL, InAttrs.Range))
633
0
      OutAttrs.push_back(A);
634
0
  }
635
636
0
  CheckForIncompatibleAttributes(*this, OutAttrs);
637
0
  CheckForDuplicateLoopAttrs<CodeAlignAttr>(*this, OutAttrs);
638
0
}
639
640
0
bool Sema::CheckRebuiltStmtAttributes(ArrayRef<const Attr *> Attrs) {
641
0
  CheckForDuplicateLoopAttrs<CodeAlignAttr>(*this, Attrs);
642
0
  return false;
643
0
}