1234973Sdim//===--- SemaStmtAttr.cpp - Statement Attribute Handling ------------------===//
2234973Sdim//
3234973Sdim//                     The LLVM Compiler Infrastructure
4234973Sdim//
5234973Sdim// This file is distributed under the University of Illinois Open Source
6234973Sdim// License. See LICENSE.TXT for details.
7234973Sdim//
8234973Sdim//===----------------------------------------------------------------------===//
9234973Sdim//
10234973Sdim//  This file implements stmt-related attribute processing.
11234973Sdim//
12234973Sdim//===----------------------------------------------------------------------===//
13234973Sdim
14234973Sdim#include "clang/Sema/SemaInternal.h"
15234973Sdim#include "clang/AST/ASTContext.h"
16234973Sdim#include "clang/Basic/SourceManager.h"
17234973Sdim#include "clang/Sema/DelayedDiagnostic.h"
18234973Sdim#include "clang/Sema/Lookup.h"
19276479Sdim#include "clang/Sema/LoopHint.h"
20239462Sdim#include "clang/Sema/ScopeInfo.h"
21234973Sdim#include "llvm/ADT/StringExtras.h"
22239462Sdim
23234973Sdimusing namespace clang;
24234973Sdimusing namespace sema;
25234973Sdim
26239462Sdimstatic Attr *handleFallThroughAttr(Sema &S, Stmt *St, const AttributeList &A,
27239462Sdim                                   SourceRange Range) {
28239462Sdim  if (!isa<NullStmt>(St)) {
29239462Sdim    S.Diag(A.getRange().getBegin(), diag::err_fallthrough_attr_wrong_target)
30239462Sdim        << St->getLocStart();
31239462Sdim    if (isa<SwitchCase>(St)) {
32276479Sdim      SourceLocation L = S.getLocForEndOfToken(Range.getEnd());
33239462Sdim      S.Diag(L, diag::note_fallthrough_insert_semi_fixit)
34239462Sdim          << FixItHint::CreateInsertion(L, ";");
35239462Sdim    }
36276479Sdim    return nullptr;
37239462Sdim  }
38239462Sdim  if (S.getCurFunction()->SwitchStack.empty()) {
39239462Sdim    S.Diag(A.getRange().getBegin(), diag::err_fallthrough_attr_outside_switch);
40276479Sdim    return nullptr;
41239462Sdim  }
42276479Sdim  return ::new (S.Context) FallThroughAttr(A.getRange(), S.Context,
43276479Sdim                                           A.getAttributeSpellingListIndex());
44239462Sdim}
45234973Sdim
46276479Sdimstatic Attr *handleLoopHintAttr(Sema &S, Stmt *St, const AttributeList &A,
47276479Sdim                                SourceRange) {
48276479Sdim  IdentifierLoc *PragmaNameLoc = A.getArgAsIdent(0);
49276479Sdim  IdentifierLoc *OptionLoc = A.getArgAsIdent(1);
50280031Sdim  IdentifierLoc *StateLoc = A.getArgAsIdent(2);
51276479Sdim  Expr *ValueExpr = A.getArgAsExpr(3);
52239462Sdim
53280031Sdim  bool PragmaUnroll = PragmaNameLoc->Ident->getName() == "unroll";
54280031Sdim  bool PragmaNoUnroll = PragmaNameLoc->Ident->getName() == "nounroll";
55276479Sdim  if (St->getStmtClass() != Stmt::DoStmtClass &&
56276479Sdim      St->getStmtClass() != Stmt::ForStmtClass &&
57276479Sdim      St->getStmtClass() != Stmt::CXXForRangeStmtClass &&
58276479Sdim      St->getStmtClass() != Stmt::WhileStmtClass) {
59280031Sdim    const char *Pragma =
60280031Sdim        llvm::StringSwitch<const char *>(PragmaNameLoc->Ident->getName())
61280031Sdim            .Case("unroll", "#pragma unroll")
62280031Sdim            .Case("nounroll", "#pragma nounroll")
63280031Sdim            .Default("#pragma clang loop");
64276479Sdim    S.Diag(St->getLocStart(), diag::err_pragma_loop_precedes_nonloop) << Pragma;
65276479Sdim    return nullptr;
66276479Sdim  }
67276479Sdim
68296417Sdim  LoopHintAttr::Spelling Spelling;
69276479Sdim  LoopHintAttr::OptionType Option;
70296417Sdim  LoopHintAttr::LoopHintState State;
71296417Sdim  if (PragmaNoUnroll) {
72296417Sdim    // #pragma nounroll
73296417Sdim    Spelling = LoopHintAttr::Pragma_nounroll;
74296417Sdim    Option = LoopHintAttr::Unroll;
75296417Sdim    State = LoopHintAttr::Disable;
76296417Sdim  } else if (PragmaUnroll) {
77276479Sdim    Spelling = LoopHintAttr::Pragma_unroll;
78296417Sdim    if (ValueExpr) {
79296417Sdim      // #pragma unroll N
80296417Sdim      Option = LoopHintAttr::UnrollCount;
81296417Sdim      State = LoopHintAttr::Numeric;
82296417Sdim    } else {
83296417Sdim      // #pragma unroll
84296417Sdim      Option = LoopHintAttr::Unroll;
85296417Sdim      State = LoopHintAttr::Enable;
86296417Sdim    }
87276479Sdim  } else {
88296417Sdim    // #pragma clang loop ...
89296417Sdim    Spelling = LoopHintAttr::Pragma_clang_loop;
90280031Sdim    assert(OptionLoc && OptionLoc->Ident &&
91280031Sdim           "Attribute must have valid option info.");
92296417Sdim    Option = llvm::StringSwitch<LoopHintAttr::OptionType>(
93296417Sdim                 OptionLoc->Ident->getName())
94276479Sdim                 .Case("vectorize", LoopHintAttr::Vectorize)
95276479Sdim                 .Case("vectorize_width", LoopHintAttr::VectorizeWidth)
96276479Sdim                 .Case("interleave", LoopHintAttr::Interleave)
97276479Sdim                 .Case("interleave_count", LoopHintAttr::InterleaveCount)
98276479Sdim                 .Case("unroll", LoopHintAttr::Unroll)
99276479Sdim                 .Case("unroll_count", LoopHintAttr::UnrollCount)
100276479Sdim                 .Default(LoopHintAttr::Vectorize);
101296417Sdim    if (Option == LoopHintAttr::VectorizeWidth ||
102296417Sdim        Option == LoopHintAttr::InterleaveCount ||
103296417Sdim        Option == LoopHintAttr::UnrollCount) {
104296417Sdim      assert(ValueExpr && "Attribute must have a valid value expression.");
105296417Sdim      if (S.CheckLoopHintExpr(ValueExpr, St->getLocStart()))
106296417Sdim        return nullptr;
107296417Sdim      State = LoopHintAttr::Numeric;
108296417Sdim    } else if (Option == LoopHintAttr::Vectorize ||
109296417Sdim               Option == LoopHintAttr::Interleave ||
110296417Sdim               Option == LoopHintAttr::Unroll) {
111296417Sdim      assert(StateLoc && StateLoc->Ident && "Loop hint must have an argument");
112280031Sdim      if (StateLoc->Ident->isStr("disable"))
113280031Sdim        State = LoopHintAttr::Disable;
114288943Sdim      else if (StateLoc->Ident->isStr("assume_safety"))
115288943Sdim        State = LoopHintAttr::AssumeSafety;
116296417Sdim      else if (StateLoc->Ident->isStr("full"))
117296417Sdim        State = LoopHintAttr::Full;
118296417Sdim      else if (StateLoc->Ident->isStr("enable"))
119296417Sdim        State = LoopHintAttr::Enable;
120280031Sdim      else
121296417Sdim        llvm_unreachable("bad loop hint argument");
122296417Sdim    } else
123296417Sdim      llvm_unreachable("bad loop hint");
124280031Sdim  }
125276479Sdim
126280031Sdim  return LoopHintAttr::CreateImplicit(S.Context, Spelling, Option, State,
127280031Sdim                                      ValueExpr, A.getRange());
128276479Sdim}
129276479Sdim
130280031Sdimstatic void
131280031SdimCheckForIncompatibleAttributes(Sema &S,
132280031Sdim                               const SmallVectorImpl<const Attr *> &Attrs) {
133280031Sdim  // There are 3 categories of loop hints attributes: vectorize, interleave,
134280031Sdim  // and unroll. Each comes in two variants: a state form and a numeric form.
135280031Sdim  // The state form selectively defaults/enables/disables the transformation
136280031Sdim  // for the loop (for unroll, default indicates full unrolling rather than
137280031Sdim  // enabling the transformation).  The numeric form form provides an integer
138280031Sdim  // hint (for example, unroll count) to the transformer. The following array
139280031Sdim  // accumulates the hints encountered while iterating through the attributes
140280031Sdim  // to check for compatibility.
141276479Sdim  struct {
142280031Sdim    const LoopHintAttr *StateAttr;
143276479Sdim    const LoopHintAttr *NumericAttr;
144276479Sdim  } HintAttrs[] = {{nullptr, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}};
145276479Sdim
146276479Sdim  for (const auto *I : Attrs) {
147276479Sdim    const LoopHintAttr *LH = dyn_cast<LoopHintAttr>(I);
148276479Sdim
149276479Sdim    // Skip non loop hint attributes
150276479Sdim    if (!LH)
151276479Sdim      continue;
152276479Sdim
153296417Sdim    LoopHintAttr::OptionType Option = LH->getOption();
154296417Sdim    enum { Vectorize, Interleave, Unroll } Category;
155276479Sdim    switch (Option) {
156276479Sdim    case LoopHintAttr::Vectorize:
157276479Sdim    case LoopHintAttr::VectorizeWidth:
158280031Sdim      Category = Vectorize;
159276479Sdim      break;
160276479Sdim    case LoopHintAttr::Interleave:
161276479Sdim    case LoopHintAttr::InterleaveCount:
162280031Sdim      Category = Interleave;
163276479Sdim      break;
164276479Sdim    case LoopHintAttr::Unroll:
165276479Sdim    case LoopHintAttr::UnrollCount:
166280031Sdim      Category = Unroll;
167276479Sdim      break;
168276479Sdim    };
169276479Sdim
170276479Sdim    auto &CategoryState = HintAttrs[Category];
171276479Sdim    const LoopHintAttr *PrevAttr;
172276479Sdim    if (Option == LoopHintAttr::Vectorize ||
173276479Sdim        Option == LoopHintAttr::Interleave || Option == LoopHintAttr::Unroll) {
174288943Sdim      // Enable|Disable|AssumeSafety hint.  For example, vectorize(enable).
175280031Sdim      PrevAttr = CategoryState.StateAttr;
176280031Sdim      CategoryState.StateAttr = LH;
177276479Sdim    } else {
178276479Sdim      // Numeric hint.  For example, vectorize_width(8).
179276479Sdim      PrevAttr = CategoryState.NumericAttr;
180276479Sdim      CategoryState.NumericAttr = LH;
181276479Sdim    }
182276479Sdim
183280031Sdim    PrintingPolicy Policy(S.Context.getLangOpts());
184280031Sdim    SourceLocation OptionLoc = LH->getRange().getBegin();
185276479Sdim    if (PrevAttr)
186276479Sdim      // Cannot specify same type of attribute twice.
187276479Sdim      S.Diag(OptionLoc, diag::err_pragma_loop_compatibility)
188280031Sdim          << /*Duplicate=*/true << PrevAttr->getDiagnosticName(Policy)
189280031Sdim          << LH->getDiagnosticName(Policy);
190276479Sdim
191280031Sdim    if (CategoryState.StateAttr && CategoryState.NumericAttr &&
192280031Sdim        (Category == Unroll ||
193280031Sdim         CategoryState.StateAttr->getState() == LoopHintAttr::Disable)) {
194280031Sdim      // Disable hints are not compatible with numeric hints of the same
195280031Sdim      // category.  As a special case, numeric unroll hints are also not
196296417Sdim      // compatible with enable or full form of the unroll pragma because these
197296417Sdim      // directives indicate full unrolling.
198276479Sdim      S.Diag(OptionLoc, diag::err_pragma_loop_compatibility)
199276479Sdim          << /*Duplicate=*/false
200280031Sdim          << CategoryState.StateAttr->getDiagnosticName(Policy)
201280031Sdim          << CategoryState.NumericAttr->getDiagnosticName(Policy);
202276479Sdim    }
203276479Sdim  }
204276479Sdim}
205276479Sdim
206239462Sdimstatic Attr *ProcessStmtAttribute(Sema &S, Stmt *St, const AttributeList &A,
207239462Sdim                                  SourceRange Range) {
208234973Sdim  switch (A.getKind()) {
209243830Sdim  case AttributeList::UnknownAttribute:
210243830Sdim    S.Diag(A.getLoc(), A.isDeclspecAttribute() ?
211243830Sdim           diag::warn_unhandled_ms_attribute_ignored :
212243830Sdim           diag::warn_unknown_attribute_ignored) << A.getName();
213276479Sdim    return nullptr;
214239462Sdim  case AttributeList::AT_FallThrough:
215239462Sdim    return handleFallThroughAttr(S, St, A, Range);
216276479Sdim  case AttributeList::AT_LoopHint:
217276479Sdim    return handleLoopHintAttr(S, St, A, Range);
218234973Sdim  default:
219243830Sdim    // if we're here, then we parsed a known attribute, but didn't recognize
220243830Sdim    // it as a statement attribute => it is declaration attribute
221249423Sdim    S.Diag(A.getRange().getBegin(), diag::err_attribute_invalid_on_stmt)
222249423Sdim        << A.getName() << St->getLocStart();
223276479Sdim    return nullptr;
224234973Sdim  }
225234973Sdim}
226234973Sdim
227234973SdimStmtResult Sema::ProcessStmtAttributes(Stmt *S, AttributeList *AttrList,
228234973Sdim                                       SourceRange Range) {
229239462Sdim  SmallVector<const Attr*, 8> Attrs;
230234973Sdim  for (const AttributeList* l = AttrList; l; l = l->getNext()) {
231239462Sdim    if (Attr *a = ProcessStmtAttribute(*this, S, *l, Range))
232234973Sdim      Attrs.push_back(a);
233234973Sdim  }
234234973Sdim
235276479Sdim  CheckForIncompatibleAttributes(*this, Attrs);
236276479Sdim
237234973Sdim  if (Attrs.empty())
238234973Sdim    return S;
239234973Sdim
240234973Sdim  return ActOnAttributedStmt(Range.getBegin(), Attrs, S);
241234973Sdim}
242