Coverage Report

Created: 2020-09-15 12:33

/Users/buildslave/jenkins/workspace/coverage/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/EvaluatedExprVisitor.h"
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
963
                                   SourceRange Range) {
27
963
  FallThroughAttr Attr(S.Context, A);
28
963
  if (!isa<NullStmt>(St)) {
29
5
    S.Diag(A.getRange().getBegin(), diag::err_fallthrough_attr_wrong_target)
30
5
        << Attr.getSpelling() << St->getBeginLoc();
31
5
    if (isa<SwitchCase>(St)) {
32
1
      SourceLocation L = S.getLocForEndOfToken(Range.getEnd());
33
1
      S.Diag(L, diag::note_fallthrough_insert_semi_fixit)
34
1
          << FixItHint::CreateInsertion(L, ";");
35
1
    }
36
5
    return nullptr;
37
5
  }
38
958
  auto *FnScope = S.getCurFunction();
39
958
  if (FnScope->SwitchStack.empty()) {
40
1
    S.Diag(A.getRange().getBegin(), diag::err_fallthrough_attr_outside_switch);
41
1
    return nullptr;
42
1
  }
43
957
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
957
  if (!S.getLangOpts().CPlusPlus17 && 
A.isCXX11Attribute()927
&&
47
908
      !A.getScopeName())
48
8
    S.Diag(A.getLoc(), diag::ext_cxx17_attr) << A;
49
957
50
957
  FnScope->setHasFallthroughStmt();
51
957
  return ::new (S.Context) FallThroughAttr(S.Context, A);
52
957
}
53
54
static Attr *handleSuppressAttr(Sema &S, Stmt *St, const ParsedAttr &A,
55
5
                                SourceRange Range) {
56
5
  if (A.getNumArgs() < 1) {
57
0
    S.Diag(A.getLoc(), diag::err_attribute_too_few_arguments) << A << 1;
58
0
    return nullptr;
59
0
  }
60
5
61
5
  std::vector<StringRef> DiagnosticIdentifiers;
62
11
  for (unsigned I = 0, E = A.getNumArgs(); I != E; 
++I6
) {
63
6
    StringRef RuleName;
64
6
65
6
    if (!S.checkStringLiteralArgumentAttr(A, I, RuleName, nullptr))
66
0
      return nullptr;
67
6
68
    // FIXME: Warn if the rule name is unknown. This is tricky because only
69
    // clang-tidy knows about available rules.
70
6
    DiagnosticIdentifiers.push_back(RuleName);
71
6
  }
72
5
73
5
  return ::new (S.Context) SuppressAttr(
74
5
      S.Context, A, DiagnosticIdentifiers.data(), DiagnosticIdentifiers.size());
75
5
}
76
77
static Attr *handleLoopHintAttr(Sema &S, Stmt *St, const ParsedAttr &A,
78
297
                                SourceRange) {
79
297
  IdentifierLoc *PragmaNameLoc = A.getArgAsIdent(0);
80
297
  IdentifierLoc *OptionLoc = A.getArgAsIdent(1);
81
297
  IdentifierLoc *StateLoc = A.getArgAsIdent(2);
82
297
  Expr *ValueExpr = A.getArgAsExpr(3);
83
297
84
297
  StringRef PragmaName =
85
297
      llvm::StringSwitch<StringRef>(PragmaNameLoc->Ident->getName())
86
297
          .Cases("unroll", "nounroll", "unroll_and_jam", "nounroll_and_jam",
87
297
                 PragmaNameLoc->Ident->getName())
88
297
          .Default("clang loop");
89
297
90
297
  if (St->getStmtClass() != Stmt::DoStmtClass &&
91
290
      St->getStmtClass() != Stmt::ForStmtClass &&
92
137
      St->getStmtClass() != Stmt::CXXForRangeStmtClass &&
93
127
      St->getStmtClass() != Stmt::WhileStmtClass) {
94
10
    std::string Pragma = "#pragma " + std::string(PragmaName);
95
10
    S.Diag(St->getBeginLoc(), diag::err_pragma_loop_precedes_nonloop) << Pragma;
96
10
    return nullptr;
97
10
  }
98
287
99
287
  LoopHintAttr::OptionType Option;
100
287
  LoopHintAttr::LoopHintState State;
101
287
102
287
  auto SetHints = [&Option, &State](LoopHintAttr::OptionType O,
103
64
                                    LoopHintAttr::LoopHintState S) {
104
64
    Option = O;
105
64
    State = S;
106
64
  };
107
287
108
287
  if (PragmaName == "nounroll") {
109
8
    SetHints(LoopHintAttr::Unroll, LoopHintAttr::Disable);
110
279
  } else if (PragmaName == "unroll") {
111
    // #pragma unroll N
112
43
    if (ValueExpr)
113
32
      SetHints(LoopHintAttr::UnrollCount, LoopHintAttr::Numeric);
114
11
    else
115
11
      SetHints(LoopHintAttr::Unroll, LoopHintAttr::Enable);
116
236
  } else if (PragmaName == "nounroll_and_jam") {
117
5
    SetHints(LoopHintAttr::UnrollAndJam, LoopHintAttr::Disable);
118
231
  } else if (PragmaName == "unroll_and_jam") {
119
    // #pragma unroll_and_jam N
120
8
    if (ValueExpr)
121
5
      SetHints(LoopHintAttr::UnrollAndJamCount, LoopHintAttr::Numeric);
122
3
    else
123
3
      SetHints(LoopHintAttr::UnrollAndJam, LoopHintAttr::Enable);
124
223
  } else {
125
    // #pragma clang loop ...
126
223
    assert(OptionLoc && OptionLoc->Ident &&
127
223
           "Attribute must have valid option info.");
128
223
    Option = llvm::StringSwitch<LoopHintAttr::OptionType>(
129
223
                 OptionLoc->Ident->getName())
130
223
                 .Case("vectorize", LoopHintAttr::Vectorize)
131
223
                 .Case("vectorize_width", LoopHintAttr::VectorizeWidth)
132
223
                 .Case("interleave", LoopHintAttr::Interleave)
133
223
                 .Case("vectorize_predicate", LoopHintAttr::VectorizePredicate)
134
223
                 .Case("interleave_count", LoopHintAttr::InterleaveCount)
135
223
                 .Case("unroll", LoopHintAttr::Unroll)
136
223
                 .Case("unroll_count", LoopHintAttr::UnrollCount)
137
223
                 .Case("pipeline", LoopHintAttr::PipelineDisabled)
138
223
                 .Case("pipeline_initiation_interval",
139
223
                       LoopHintAttr::PipelineInitiationInterval)
140
223
                 .Case("distribute", LoopHintAttr::Distribute)
141
223
                 .Default(LoopHintAttr::Vectorize);
142
223
    if (Option == LoopHintAttr::VectorizeWidth ||
143
187
        Option == LoopHintAttr::InterleaveCount ||
144
159
        Option == LoopHintAttr::UnrollCount ||
145
141
        Option == LoopHintAttr::PipelineInitiationInterval) {
146
87
      assert(ValueExpr && "Attribute must have a valid value expression.");
147
87
      if (S.CheckLoopHintExpr(ValueExpr, St->getBeginLoc()))
148
0
        return nullptr;
149
87
      State = LoopHintAttr::Numeric;
150
136
    } else if (Option == LoopHintAttr::Vectorize ||
151
88
               Option == LoopHintAttr::Interleave ||
152
62
               Option == LoopHintAttr::VectorizePredicate ||
153
49
               Option == LoopHintAttr::Unroll ||
154
20
               Option == LoopHintAttr::Distribute ||
155
136
               
Option == LoopHintAttr::PipelineDisabled4
) {
156
136
      assert(StateLoc && StateLoc->Ident && "Loop hint must have an argument");
157
136
      if (StateLoc->Ident->isStr("disable"))
158
63
        State = LoopHintAttr::Disable;
159
73
      else if (StateLoc->Ident->isStr("assume_safety"))
160
19
        State = LoopHintAttr::AssumeSafety;
161
54
      else if (StateLoc->Ident->isStr("full"))
162
10
        State = LoopHintAttr::Full;
163
44
      else if (StateLoc->Ident->isStr("enable"))
164
44
        State = LoopHintAttr::Enable;
165
44
      else
166
0
        llvm_unreachable("bad loop hint argument");
167
136
    } else
168
0
      llvm_unreachable("bad loop hint");
169
223
  }
170
287
171
287
  return LoopHintAttr::CreateImplicit(S.Context, Option, State, ValueExpr, A);
172
287
}
173
174
namespace {
175
class CallExprFinder : public ConstEvaluatedExprVisitor<CallExprFinder> {
176
  bool FoundCallExpr = false;
177
178
public:
179
  typedef ConstEvaluatedExprVisitor<CallExprFinder> Inherited;
180
181
9
  CallExprFinder(Sema &S, const Stmt *St) : Inherited(S.Context) { Visit(St); }
182
183
9
  bool foundCallExpr() { return FoundCallExpr; }
184
185
9
  void VisitCallExpr(const CallExpr *E) { FoundCallExpr = true; }
186
1
  void VisitAsmStmt(const AsmStmt *S) { FoundCallExpr = true; }
187
188
9
  void Visit(const Stmt *St) {
189
9
    if (!St)
190
0
      return;
191
9
    ConstEvaluatedExprVisitor<CallExprFinder>::Visit(St);
192
9
  }
193
};
194
} // namespace
195
196
static Attr *handleNoMergeAttr(Sema &S, Stmt *St, const ParsedAttr &A,
197
10
                               SourceRange Range) {
198
10
  NoMergeAttr NMA(S.Context, A);
199
10
  if (S.CheckAttrNoArgs(A))
200
1
    return nullptr;
201
9
202
9
  CallExprFinder CEF(S, St);
203
9
204
9
  if (!CEF.foundCallExpr()) {
205
1
    S.Diag(St->getBeginLoc(), diag::warn_nomerge_attribute_ignored_in_stmt)
206
1
        << NMA.getSpelling();
207
1
    return nullptr;
208
1
  }
209
8
210
8
  return ::new (S.Context) NoMergeAttr(S.Context, A);
211
8
}
212
213
static Attr *handleLikely(Sema &S, Stmt *St, const ParsedAttr &A,
214
22
                          SourceRange Range) {
215
22
216
22
  if (!S.getLangOpts().CPlusPlus20 && A.isCXX11Attribute() && 
!A.getScopeName()17
)
217
17
    S.Diag(A.getLoc(), diag::ext_cxx20_attr) << A << Range;
218
22
219
22
  return ::new (S.Context) LikelyAttr(S.Context, A);
220
22
}
221
222
static Attr *handleUnlikely(Sema &S, Stmt *St, const ParsedAttr &A,
223
54
                            SourceRange Range) {
224
54
225
54
  if (!S.getLangOpts().CPlusPlus20 && A.isCXX11Attribute() && 
!A.getScopeName()46
)
226
46
    S.Diag(A.getLoc(), diag::ext_cxx20_attr) << A << Range;
227
54
228
54
  return ::new (S.Context) UnlikelyAttr(S.Context, A);
229
54
}
230
231
static void
232
CheckForIncompatibleAttributes(Sema &S,
233
4.86k
                               const SmallVectorImpl<const Attr *> &Attrs) {
234
  // There are 6 categories of loop hints attributes: vectorize, interleave,
235
  // unroll, unroll_and_jam, pipeline and distribute. Except for distribute they
236
  // come in two variants: a state form and a numeric form.  The state form
237
  // selectively defaults/enables/disables the transformation for the loop
238
  // (for unroll, default indicates full unrolling rather than enabling the
239
  // transformation). The numeric form form provides an integer hint (for
240
  // example, unroll count) to the transformer. The following array accumulates
241
  // the hints encountered while iterating through the attributes to check for
242
  // compatibility.
243
4.86k
  struct {
244
4.86k
    const LoopHintAttr *StateAttr;
245
4.86k
    const LoopHintAttr *NumericAttr;
246
4.86k
  } HintAttrs[] = {{nullptr, nullptr}, {nullptr, nullptr}, {nullptr, nullptr},
247
4.86k
                   {nullptr, nullptr}, {nullptr, nullptr}, {nullptr, nullptr},
248
4.86k
                   {nullptr, nullptr}};
249
4.86k
250
1.35k
  for (const auto *I : Attrs) {
251
1.35k
    const LoopHintAttr *LH = dyn_cast<LoopHintAttr>(I);
252
1.35k
253
    // Skip non loop hint attributes
254
1.35k
    if (!LH)
255
1.06k
      continue;
256
287
257
287
    LoopHintAttr::OptionType Option = LH->getOption();
258
287
    enum {
259
287
      Vectorize,
260
287
      Interleave,
261
287
      Unroll,
262
287
      UnrollAndJam,
263
287
      Distribute,
264
287
      Pipeline,
265
287
      VectorizePredicate
266
287
    } Category;
267
287
    switch (Option) {
268
84
    case LoopHintAttr::Vectorize:
269
84
    case LoopHintAttr::VectorizeWidth:
270
84
      Category = Vectorize;
271
84
      break;
272
54
    case LoopHintAttr::Interleave:
273
54
    case LoopHintAttr::InterleaveCount:
274
54
      Category = Interleave;
275
54
      break;
276
98
    case LoopHintAttr::Unroll:
277
98
    case LoopHintAttr::UnrollCount:
278
98
      Category = Unroll;
279
98
      break;
280
13
    case LoopHintAttr::UnrollAndJam:
281
13
    case LoopHintAttr::UnrollAndJamCount:
282
13
      Category = UnrollAndJam;
283
13
      break;
284
16
    case LoopHintAttr::Distribute:
285
      // Perform the check for duplicated 'distribute' hints.
286
16
      Category = Distribute;
287
16
      break;
288
9
    case LoopHintAttr::PipelineDisabled:
289
9
    case LoopHintAttr::PipelineInitiationInterval:
290
9
      Category = Pipeline;
291
9
      break;
292
13
    case LoopHintAttr::VectorizePredicate:
293
13
      Category = VectorizePredicate;
294
13
      break;
295
287
    };
296
287
297
287
    assert(Category < sizeof(HintAttrs) / sizeof(HintAttrs[0]));
298
287
    auto &CategoryState = HintAttrs[Category];
299
287
    const LoopHintAttr *PrevAttr;
300
287
    if (Option == LoopHintAttr::Vectorize ||
301
239
        Option == LoopHintAttr::Interleave || 
Option == LoopHintAttr::Unroll213
||
302
165
        Option == LoopHintAttr::UnrollAndJam ||
303
157
        Option == LoopHintAttr::VectorizePredicate ||
304
144
        Option == LoopHintAttr::PipelineDisabled ||
305
163
        
Option == LoopHintAttr::Distribute140
) {
306
      // Enable|Disable|AssumeSafety hint.  For example, vectorize(enable).
307
163
      PrevAttr = CategoryState.StateAttr;
308
163
      CategoryState.StateAttr = LH;
309
124
    } else {
310
      // Numeric hint.  For example, vectorize_width(8).
311
124
      PrevAttr = CategoryState.NumericAttr;
312
124
      CategoryState.NumericAttr = LH;
313
124
    }
314
287
315
287
    PrintingPolicy Policy(S.Context.getLangOpts());
316
287
    SourceLocation OptionLoc = LH->getRange().getBegin();
317
287
    if (PrevAttr)
318
      // Cannot specify same type of attribute twice.
319
14
      S.Diag(OptionLoc, diag::err_pragma_loop_compatibility)
320
14
          << /*Duplicate=*/true << PrevAttr->getDiagnosticName(Policy)
321
14
          << LH->getDiagnosticName(Policy);
322
287
323
287
    if (CategoryState.StateAttr && 
CategoryState.NumericAttr172
&&
324
18
        (Category == Unroll || 
Category == UnrollAndJam10
||
325
14
         
CategoryState.StateAttr->getState() == LoopHintAttr::Disable9
)) {
326
      // Disable hints are not compatible with numeric hints of the same
327
      // category.  As a special case, numeric unroll hints are also not
328
      // compatible with enable or full form of the unroll pragma because these
329
      // directives indicate full unrolling.
330
14
      S.Diag(OptionLoc, diag::err_pragma_loop_compatibility)
331
14
          << /*Duplicate=*/false
332
14
          << CategoryState.StateAttr->getDiagnosticName(Policy)
333
14
          << CategoryState.NumericAttr->getDiagnosticName(Policy);
334
14
    }
335
287
  }
336
4.86k
337
  // C++20 [dcl.attr.likelihood]p1 The attribute-token likely shall not appear
338
  // in an attribute-specifier-seq that contains the attribute-token unlikely.
339
4.86k
  const LikelyAttr *Likely = nullptr;
340
4.86k
  const UnlikelyAttr *Unlikely = nullptr;
341
1.35k
  for (const auto *I : Attrs) {
342
1.35k
    if (const auto *Attr = dyn_cast<LikelyAttr>(I)) {
343
22
      if (Unlikely) {
344
0
        S.Diag(Attr->getLocation(), diag::err_attributes_are_not_compatible)
345
0
            << Attr << Unlikely << Attr->getRange();
346
0
        S.Diag(Unlikely->getLocation(), diag::note_conflicting_attribute)
347
0
            << Unlikely->getRange();
348
0
        return;
349
0
      }
350
22
      Likely = Attr;
351
1.32k
    } else if (const auto *Attr = dyn_cast<UnlikelyAttr>(I)) {
352
54
      if (Likely) {
353
0
        S.Diag(Attr->getLocation(), diag::err_attributes_are_not_compatible)
354
0
            << Attr << Likely << Attr->getRange();
355
0
        S.Diag(Likely->getLocation(), diag::note_conflicting_attribute)
356
0
            << Likely->getRange();
357
0
        return;
358
0
      }
359
54
      Unlikely = Attr;
360
54
    }
361
1.35k
  }
362
4.86k
}
363
364
static Attr *handleOpenCLUnrollHint(Sema &S, Stmt *St, const ParsedAttr &A,
365
21
                                    SourceRange Range) {
366
  // Although the feature was introduced only in OpenCL C v2.0 s6.11.5, it's
367
  // useful for OpenCL 1.x too and doesn't require HW support.
368
  // opencl_unroll_hint can have 0 arguments (compiler
369
  // determines unrolling factor) or 1 argument (the unroll factor provided
370
  // by the user).
371
21
372
21
  unsigned NumArgs = A.getNumArgs();
373
21
374
21
  if (NumArgs > 1) {
375
1
    S.Diag(A.getLoc(), diag::err_attribute_too_many_arguments) << A << 1;
376
1
    return nullptr;
377
1
  }
378
20
379
20
  unsigned UnrollFactor = 0;
380
20
381
20
  if (NumArgs == 1) {
382
14
    Expr *E = A.getArgAsExpr(0);
383
14
    Optional<llvm::APSInt> ArgVal;
384
14
385
14
    if (!(ArgVal = E->getIntegerConstantExpr(S.Context))) {
386
1
      S.Diag(A.getLoc(), diag::err_attribute_argument_type)
387
1
          << A << AANT_ArgumentIntegerConstant << E->getSourceRange();
388
1
      return nullptr;
389
1
    }
390
13
391
13
    int Val = ArgVal->getSExtValue();
392
13
393
13
    if (Val <= 0) {
394
1
      S.Diag(A.getRange().getBegin(),
395
1
             diag::err_attribute_requires_positive_integer)
396
1
          << A << /* positive */ 0;
397
1
      return nullptr;
398
1
    }
399
12
    UnrollFactor = Val;
400
12
  }
401
20
402
18
  return OpenCLUnrollHintAttr::CreateImplicit(S.Context, UnrollFactor);
403
20
}
404
405
static Attr *ProcessStmtAttribute(Sema &S, Stmt *St, const ParsedAttr &A,
406
1.42k
                                  SourceRange Range) {
407
1.42k
  switch (A.getKind()) {
408
29
  case ParsedAttr::UnknownAttribute:
409
29
    S.Diag(A.getLoc(), A.isDeclspecAttribute()
410
0
                           ? (unsigned)diag::warn_unhandled_ms_attribute_ignored
411
29
                           : (unsigned)diag::warn_unknown_attribute_ignored)
412
29
        << A;
413
29
    return nullptr;
414
963
  case ParsedAttr::AT_FallThrough:
415
963
    return handleFallThroughAttr(S, St, A, Range);
416
297
  case ParsedAttr::AT_LoopHint:
417
297
    return handleLoopHintAttr(S, St, A, Range);
418
21
  case ParsedAttr::AT_OpenCLUnrollHint:
419
21
    return handleOpenCLUnrollHint(S, St, A, Range);
420
5
  case ParsedAttr::AT_Suppress:
421
5
    return handleSuppressAttr(S, St, A, Range);
422
10
  case ParsedAttr::AT_NoMerge:
423
10
    return handleNoMergeAttr(S, St, A, Range);
424
22
  case ParsedAttr::AT_Likely:
425
22
    return handleLikely(S, St, A, Range);
426
54
  case ParsedAttr::AT_Unlikely:
427
54
    return handleUnlikely(S, St, A, Range);
428
23
  default:
429
    // if we're here, then we parsed a known attribute, but didn't recognize
430
    // it as a statement attribute => it is declaration attribute
431
23
    S.Diag(A.getRange().getBegin(), diag::err_decl_attribute_invalid_on_stmt)
432
23
        << A << St->getBeginLoc();
433
23
    return nullptr;
434
1.42k
  }
435
1.42k
}
436
437
StmtResult Sema::ProcessStmtAttributes(Stmt *S,
438
                                       const ParsedAttributesView &AttrList,
439
4.86k
                                       SourceRange Range) {
440
4.86k
  SmallVector<const Attr*, 8> Attrs;
441
1.42k
  for (const ParsedAttr &AL : AttrList) {
442
1.42k
    if (Attr *a = ProcessStmtAttribute(*this, S, AL, Range))
443
1.35k
      Attrs.push_back(a);
444
1.42k
  }
445
4.86k
446
4.86k
  CheckForIncompatibleAttributes(*this, Attrs);
447
4.86k
448
4.86k
  if (Attrs.empty())
449
3.65k
    return S;
450
1.21k
451
1.21k
  return ActOnAttributedStmt(Range.getBegin(), Attrs, S);
452
1.21k
}