810 lines · 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-exception6//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/TargetInfo.h"16#include "clang/Sema/DelayedDiagnostic.h"17#include "clang/Sema/ParsedAttr.h"18#include "clang/Sema/ScopeInfo.h"19#include <optional>20 21using namespace clang;22using namespace sema;23 24static Attr *handleFallThroughAttr(Sema &S, Stmt *St, const ParsedAttr &A,25 SourceRange Range) {26 FallThroughAttr Attr(S.Context, A);27 if (isa<SwitchCase>(St)) {28 S.Diag(A.getRange().getBegin(), diag::err_fallthrough_attr_wrong_target)29 << A << St->getBeginLoc();30 SourceLocation L = S.getLocForEndOfToken(Range.getEnd());31 S.Diag(L, diag::note_fallthrough_insert_semi_fixit)32 << FixItHint::CreateInsertion(L, ";");33 return nullptr;34 }35 auto *FnScope = S.getCurFunction();36 if (FnScope->SwitchStack.empty()) {37 S.Diag(A.getRange().getBegin(), diag::err_fallthrough_attr_outside_switch);38 return nullptr;39 }40 41 // If this is spelled as the standard C++17 attribute, but not in C++17, warn42 // about using it as an extension.43 if (!S.getLangOpts().CPlusPlus17 && A.isCXX11Attribute() &&44 !A.getScopeName())45 S.Diag(A.getLoc(), diag::ext_cxx17_attr) << A;46 47 FnScope->setHasFallthroughStmt();48 return ::new (S.Context) FallThroughAttr(S.Context, A);49}50 51static Attr *handleSuppressAttr(Sema &S, Stmt *St, const ParsedAttr &A,52 SourceRange Range) {53 if (A.getAttributeSpellingListIndex() == SuppressAttr::CXX11_gsl_suppress &&54 A.getNumArgs() < 1) {55 // Suppression attribute with GSL spelling requires at least 1 argument.56 S.Diag(A.getLoc(), diag::err_attribute_too_few_arguments) << A << 1;57 return nullptr;58 }59 60 std::vector<StringRef> DiagnosticIdentifiers;61 for (unsigned I = 0, E = A.getNumArgs(); I != E; ++I) {62 StringRef RuleName;63 64 if (!S.checkStringLiteralArgumentAttr(A, I, RuleName, nullptr))65 return nullptr;66 67 DiagnosticIdentifiers.push_back(RuleName);68 }69 70 return ::new (S.Context) SuppressAttr(71 S.Context, A, DiagnosticIdentifiers.data(), DiagnosticIdentifiers.size());72}73 74static Attr *handleLoopHintAttr(Sema &S, Stmt *St, const ParsedAttr &A,75 SourceRange) {76 IdentifierLoc *PragmaNameLoc = A.getArgAsIdent(0);77 IdentifierLoc *OptionLoc = A.getArgAsIdent(1);78 IdentifierLoc *StateLoc = A.getArgAsIdent(2);79 Expr *ValueExpr = A.getArgAsExpr(3);80 81 StringRef PragmaName =82 llvm::StringSwitch<StringRef>(83 PragmaNameLoc->getIdentifierInfo()->getName())84 .Cases({"unroll", "nounroll", "unroll_and_jam", "nounroll_and_jam"},85 PragmaNameLoc->getIdentifierInfo()->getName())86 .Default("clang loop");87 88 // This could be handled automatically by adding a Subjects definition in89 // Attr.td, but that would make the diagnostic behavior worse in this case90 // because the user spells this attribute as a pragma.91 if (!isa<DoStmt, ForStmt, CXXForRangeStmt, WhileStmt>(St)) {92 std::string Pragma = "#pragma " + std::string(PragmaName);93 S.Diag(St->getBeginLoc(), diag::err_pragma_loop_precedes_nonloop) << Pragma;94 return nullptr;95 }96 97 LoopHintAttr::OptionType Option;98 LoopHintAttr::LoopHintState State;99 100 auto SetHints = [&Option, &State](LoopHintAttr::OptionType O,101 LoopHintAttr::LoopHintState S) {102 Option = O;103 State = S;104 };105 106 if (PragmaName == "nounroll") {107 SetHints(LoopHintAttr::Unroll, LoopHintAttr::Disable);108 } else if (PragmaName == "unroll") {109 // #pragma unroll N110 if (ValueExpr) {111 if (!ValueExpr->isValueDependent()) {112 auto Value = ValueExpr->EvaluateKnownConstInt(S.getASTContext());113 if (Value.isZero() || Value.isOne())114 SetHints(LoopHintAttr::Unroll, LoopHintAttr::Disable);115 else116 SetHints(LoopHintAttr::UnrollCount, LoopHintAttr::Numeric);117 } else118 SetHints(LoopHintAttr::UnrollCount, LoopHintAttr::Numeric);119 } else120 SetHints(LoopHintAttr::Unroll, LoopHintAttr::Enable);121 } else if (PragmaName == "nounroll_and_jam") {122 SetHints(LoopHintAttr::UnrollAndJam, LoopHintAttr::Disable);123 } else if (PragmaName == "unroll_and_jam") {124 // #pragma unroll_and_jam N125 if (ValueExpr)126 SetHints(LoopHintAttr::UnrollAndJamCount, LoopHintAttr::Numeric);127 else128 SetHints(LoopHintAttr::UnrollAndJam, LoopHintAttr::Enable);129 } else {130 // #pragma clang loop ...131 assert(OptionLoc && OptionLoc->getIdentifierInfo() &&132 "Attribute must have valid option info.");133 Option = llvm::StringSwitch<LoopHintAttr::OptionType>(134 OptionLoc->getIdentifierInfo()->getName())135 .Case("vectorize", LoopHintAttr::Vectorize)136 .Case("vectorize_width", LoopHintAttr::VectorizeWidth)137 .Case("interleave", LoopHintAttr::Interleave)138 .Case("vectorize_predicate", LoopHintAttr::VectorizePredicate)139 .Case("interleave_count", LoopHintAttr::InterleaveCount)140 .Case("unroll", LoopHintAttr::Unroll)141 .Case("unroll_count", LoopHintAttr::UnrollCount)142 .Case("pipeline", LoopHintAttr::PipelineDisabled)143 .Case("pipeline_initiation_interval",144 LoopHintAttr::PipelineInitiationInterval)145 .Case("distribute", LoopHintAttr::Distribute)146 .Default(LoopHintAttr::Vectorize);147 if (Option == LoopHintAttr::VectorizeWidth) {148 assert((ValueExpr || (StateLoc && StateLoc->getIdentifierInfo())) &&149 "Attribute must have a valid value expression or argument.");150 if (ValueExpr && S.CheckLoopHintExpr(ValueExpr, St->getBeginLoc(),151 /*AllowZero=*/false))152 return nullptr;153 if (StateLoc && StateLoc->getIdentifierInfo() &&154 StateLoc->getIdentifierInfo()->isStr("scalable"))155 State = LoopHintAttr::ScalableWidth;156 else157 State = LoopHintAttr::FixedWidth;158 } else if (Option == LoopHintAttr::InterleaveCount ||159 Option == LoopHintAttr::UnrollCount ||160 Option == LoopHintAttr::PipelineInitiationInterval) {161 assert(ValueExpr && "Attribute must have a valid value expression.");162 if (S.CheckLoopHintExpr(ValueExpr, St->getBeginLoc(),163 /*AllowZero=*/false))164 return nullptr;165 State = LoopHintAttr::Numeric;166 } else if (Option == LoopHintAttr::Vectorize ||167 Option == LoopHintAttr::Interleave ||168 Option == LoopHintAttr::VectorizePredicate ||169 Option == LoopHintAttr::Unroll ||170 Option == LoopHintAttr::Distribute ||171 Option == LoopHintAttr::PipelineDisabled) {172 assert(StateLoc && StateLoc->getIdentifierInfo() &&173 "Loop hint must have an argument");174 if (StateLoc->getIdentifierInfo()->isStr("disable"))175 State = LoopHintAttr::Disable;176 else if (StateLoc->getIdentifierInfo()->isStr("assume_safety"))177 State = LoopHintAttr::AssumeSafety;178 else if (StateLoc->getIdentifierInfo()->isStr("full"))179 State = LoopHintAttr::Full;180 else if (StateLoc->getIdentifierInfo()->isStr("enable"))181 State = LoopHintAttr::Enable;182 else183 llvm_unreachable("bad loop hint argument");184 } else185 llvm_unreachable("bad loop hint");186 }187 188 return LoopHintAttr::CreateImplicit(S.Context, Option, State, ValueExpr, A);189}190 191namespace {192class CallExprFinder : public ConstEvaluatedExprVisitor<CallExprFinder> {193 bool FoundAsmStmt = false;194 std::vector<const CallExpr *> CallExprs;195 196public:197 typedef ConstEvaluatedExprVisitor<CallExprFinder> Inherited;198 199 CallExprFinder(Sema &S, const Stmt *St) : Inherited(S.Context) { Visit(St); }200 201 bool foundCallExpr() { return !CallExprs.empty(); }202 const std::vector<const CallExpr *> &getCallExprs() { return CallExprs; }203 204 bool foundAsmStmt() { return FoundAsmStmt; }205 206 void VisitCallExpr(const CallExpr *E) { CallExprs.push_back(E); }207 208 void VisitAsmStmt(const AsmStmt *S) { FoundAsmStmt = true; }209 210 void Visit(const Stmt *St) {211 if (!St)212 return;213 ConstEvaluatedExprVisitor<CallExprFinder>::Visit(St);214 }215};216} // namespace217 218static Attr *handleNoMergeAttr(Sema &S, Stmt *St, const ParsedAttr &A,219 SourceRange Range) {220 CallExprFinder CEF(S, St);221 222 if (!CEF.foundCallExpr() && !CEF.foundAsmStmt()) {223 S.Diag(St->getBeginLoc(), diag::warn_attribute_ignored_no_calls_in_stmt)224 << A;225 return nullptr;226 }227 228 return ::new (S.Context) NoMergeAttr(S.Context, A);229}230 231static Attr *handleNoConvergentAttr(Sema &S, Stmt *St, const ParsedAttr &A,232 SourceRange Range) {233 CallExprFinder CEF(S, St);234 235 if (!CEF.foundCallExpr() && !CEF.foundAsmStmt()) {236 S.Diag(St->getBeginLoc(), diag::warn_attribute_ignored_no_calls_in_stmt)237 << A;238 return nullptr;239 }240 241 return ::new (S.Context) NoConvergentAttr(S.Context, A);242}243 244template <typename OtherAttr, int DiagIdx>245static bool CheckStmtInlineAttr(Sema &SemaRef, const Stmt *OrigSt,246 const Stmt *CurSt,247 const AttributeCommonInfo &A) {248 CallExprFinder OrigCEF(SemaRef, OrigSt);249 CallExprFinder CEF(SemaRef, CurSt);250 251 // If the call expressions lists are equal in size, we can skip252 // previously emitted diagnostics. However, if the statement has a pack253 // expansion, we have no way of telling which CallExpr is the instantiated254 // version of the other. In this case, we will end up re-diagnosing in the255 // instantiation.256 // ie: [[clang::always_inline]] non_dependent(), (other_call<Pack>()...)257 // will diagnose nondependent again.258 bool CanSuppressDiag =259 OrigSt && CEF.getCallExprs().size() == OrigCEF.getCallExprs().size();260 261 if (!CEF.foundCallExpr()) {262 return SemaRef.Diag(CurSt->getBeginLoc(),263 diag::warn_attribute_ignored_no_calls_in_stmt)264 << A;265 }266 267 for (const auto &Tup :268 llvm::zip_longest(OrigCEF.getCallExprs(), CEF.getCallExprs())) {269 // If the original call expression already had a callee, we already270 // diagnosed this, so skip it here. We can't skip if there isn't a 1:1271 // relationship between the two lists of call expressions.272 if (!CanSuppressDiag || !(*std::get<0>(Tup))->getCalleeDecl()) {273 const Decl *Callee = (*std::get<1>(Tup))->getCalleeDecl();274 if (Callee &&275 (Callee->hasAttr<OtherAttr>() || Callee->hasAttr<FlattenAttr>())) {276 SemaRef.Diag(CurSt->getBeginLoc(),277 diag::warn_function_stmt_attribute_precedence)278 << A << (Callee->hasAttr<OtherAttr>() ? DiagIdx : 1);279 SemaRef.Diag(Callee->getBeginLoc(), diag::note_conflicting_attribute);280 }281 }282 }283 284 return false;285}286 287bool Sema::CheckNoInlineAttr(const Stmt *OrigSt, const Stmt *CurSt,288 const AttributeCommonInfo &A) {289 return CheckStmtInlineAttr<AlwaysInlineAttr, 0>(*this, OrigSt, CurSt, A);290}291 292bool Sema::CheckAlwaysInlineAttr(const Stmt *OrigSt, const Stmt *CurSt,293 const AttributeCommonInfo &A) {294 return CheckStmtInlineAttr<NoInlineAttr, 2>(*this, OrigSt, CurSt, A);295}296 297static Attr *handleNoInlineAttr(Sema &S, Stmt *St, const ParsedAttr &A,298 SourceRange Range) {299 NoInlineAttr NIA(S.Context, A);300 if (!NIA.isStmtNoInline()) {301 S.Diag(St->getBeginLoc(), diag::warn_function_attribute_ignored_in_stmt)302 << "[[clang::noinline]]";303 return nullptr;304 }305 306 if (S.CheckNoInlineAttr(/*OrigSt=*/nullptr, St, A))307 return nullptr;308 309 return ::new (S.Context) NoInlineAttr(S.Context, A);310}311 312static Attr *handleAlwaysInlineAttr(Sema &S, Stmt *St, const ParsedAttr &A,313 SourceRange Range) {314 AlwaysInlineAttr AIA(S.Context, A);315 if (!AIA.isClangAlwaysInline()) {316 S.Diag(St->getBeginLoc(), diag::warn_function_attribute_ignored_in_stmt)317 << "[[clang::always_inline]]";318 return nullptr;319 }320 321 if (S.CheckAlwaysInlineAttr(/*OrigSt=*/nullptr, St, A))322 return nullptr;323 324 return ::new (S.Context) AlwaysInlineAttr(S.Context, A);325}326 327static Attr *handleCXXAssumeAttr(Sema &S, Stmt *St, const ParsedAttr &A,328 SourceRange Range) {329 ExprResult Res = S.ActOnCXXAssumeAttr(St, A, Range);330 if (!Res.isUsable())331 return nullptr;332 333 return ::new (S.Context) CXXAssumeAttr(S.Context, A, Res.get());334}335 336static Attr *handleMustTailAttr(Sema &S, Stmt *St, const ParsedAttr &A,337 SourceRange Range) {338 // Validation is in Sema::ActOnAttributedStmt().339 return ::new (S.Context) MustTailAttr(S.Context, A);340}341 342static Attr *handleLikely(Sema &S, Stmt *St, const ParsedAttr &A,343 SourceRange Range) {344 345 if (!S.getLangOpts().CPlusPlus20 && A.isCXX11Attribute() && !A.getScopeName())346 S.Diag(A.getLoc(), diag::ext_cxx20_attr) << A << Range;347 348 return ::new (S.Context) LikelyAttr(S.Context, A);349}350 351static Attr *handleUnlikely(Sema &S, Stmt *St, const ParsedAttr &A,352 SourceRange Range) {353 354 if (!S.getLangOpts().CPlusPlus20 && A.isCXX11Attribute() && !A.getScopeName())355 S.Diag(A.getLoc(), diag::ext_cxx20_attr) << A << Range;356 357 return ::new (S.Context) UnlikelyAttr(S.Context, A);358}359 360CodeAlignAttr *Sema::BuildCodeAlignAttr(const AttributeCommonInfo &CI,361 Expr *E) {362 if (!E->isValueDependent()) {363 llvm::APSInt ArgVal;364 ExprResult Res = VerifyIntegerConstantExpression(E, &ArgVal);365 if (Res.isInvalid())366 return nullptr;367 E = Res.get();368 369 // This attribute requires an integer argument which is a constant power of370 // two between 1 and 4096 inclusive.371 if (ArgVal < CodeAlignAttr::MinimumAlignment ||372 ArgVal > CodeAlignAttr::MaximumAlignment || !ArgVal.isPowerOf2()) {373 if (std::optional<int64_t> Value = ArgVal.trySExtValue())374 Diag(CI.getLoc(), diag::err_attribute_power_of_two_in_range)375 << CI << CodeAlignAttr::MinimumAlignment376 << CodeAlignAttr::MaximumAlignment << Value.value();377 else378 Diag(CI.getLoc(), diag::err_attribute_power_of_two_in_range)379 << CI << CodeAlignAttr::MinimumAlignment380 << CodeAlignAttr::MaximumAlignment << E;381 return nullptr;382 }383 }384 return new (Context) CodeAlignAttr(Context, CI, E);385}386 387static Attr *handleCodeAlignAttr(Sema &S, Stmt *St, const ParsedAttr &A) {388 389 Expr *E = A.getArgAsExpr(0);390 return S.BuildCodeAlignAttr(A, E);391}392 393// Diagnose non-identical duplicates as a 'conflicting' loop attributes394// and suppress duplicate errors in cases where the two match.395template <typename LoopAttrT>396static void CheckForDuplicateLoopAttrs(Sema &S, ArrayRef<const Attr *> Attrs) {397 auto FindFunc = [](const Attr *A) { return isa<const LoopAttrT>(A); };398 const auto *FirstItr = llvm::find_if(Attrs, FindFunc);399 400 if (FirstItr == Attrs.end()) // no attributes found401 return;402 403 const auto *LastFoundItr = FirstItr;404 std::optional<llvm::APSInt> FirstValue;405 406 const auto *CAFA =407 dyn_cast<ConstantExpr>(cast<LoopAttrT>(*FirstItr)->getAlignment());408 // Return early if first alignment expression is dependent (since we don't409 // know what the effective size will be), and skip the loop entirely.410 if (!CAFA)411 return;412 413 while (Attrs.end() != (LastFoundItr = std::find_if(LastFoundItr + 1,414 Attrs.end(), FindFunc))) {415 const auto *CASA =416 dyn_cast<ConstantExpr>(cast<LoopAttrT>(*LastFoundItr)->getAlignment());417 // If the value is dependent, we can not test anything.418 if (!CASA)419 return;420 // Test the attribute values.421 llvm::APSInt SecondValue = CASA->getResultAsAPSInt();422 if (!FirstValue)423 FirstValue = CAFA->getResultAsAPSInt();424 425 if (FirstValue != SecondValue) {426 S.Diag((*LastFoundItr)->getLocation(), diag::err_loop_attr_conflict)427 << *FirstItr;428 S.Diag((*FirstItr)->getLocation(), diag::note_previous_attribute);429 }430 }431}432 433static Attr *handleMSConstexprAttr(Sema &S, Stmt *St, const ParsedAttr &A,434 SourceRange Range) {435 if (!S.getLangOpts().isCompatibleWithMSVC(LangOptions::MSVC2022_3)) {436 S.Diag(A.getLoc(), diag::warn_unknown_attribute_ignored)437 << A << A.getRange();438 return nullptr;439 }440 return ::new (S.Context) MSConstexprAttr(S.Context, A);441}442 443#define WANT_STMT_MERGE_LOGIC444#include "clang/Sema/AttrParsedAttrImpl.inc"445#undef WANT_STMT_MERGE_LOGIC446 447static void448CheckForIncompatibleAttributes(Sema &S,449 const SmallVectorImpl<const Attr *> &Attrs) {450 // The vast majority of attributed statements will only have one attribute451 // on them, so skip all of the checking in the common case.452 if (Attrs.size() < 2)453 return;454 455 // First, check for the easy cases that are table-generated for us.456 if (!DiagnoseMutualExclusions(S, Attrs))457 return;458 459 enum CategoryType {460 // For the following categories, they come in two variants: a state form and461 // a numeric form. The state form may be one of default, enable, and462 // disable. The numeric form provides an integer hint (for example, unroll463 // count) to the transformer.464 Vectorize,465 Interleave,466 UnrollAndJam,467 Pipeline,468 // For unroll, default indicates full unrolling rather than enabling the469 // transformation.470 Unroll,471 // The loop distribution transformation only has a state form that is472 // exposed by #pragma clang loop distribute (enable | disable).473 Distribute,474 // The vector predication only has a state form that is exposed by475 // #pragma clang loop vectorize_predicate (enable | disable).476 VectorizePredicate,477 // This serves as a indicator to how many category are listed in this enum.478 NumberOfCategories479 };480 // The following array accumulates the hints encountered while iterating481 // through the attributes to check for compatibility.482 struct {483 const LoopHintAttr *StateAttr;484 const LoopHintAttr *NumericAttr;485 } HintAttrs[CategoryType::NumberOfCategories] = {};486 487 for (const auto *I : Attrs) {488 const LoopHintAttr *LH = dyn_cast<LoopHintAttr>(I);489 490 // Skip non loop hint attributes491 if (!LH)492 continue;493 494 CategoryType Category = CategoryType::NumberOfCategories;495 LoopHintAttr::OptionType Option = LH->getOption();496 switch (Option) {497 case LoopHintAttr::Vectorize:498 case LoopHintAttr::VectorizeWidth:499 Category = Vectorize;500 break;501 case LoopHintAttr::Interleave:502 case LoopHintAttr::InterleaveCount:503 Category = Interleave;504 break;505 case LoopHintAttr::Unroll:506 case LoopHintAttr::UnrollCount:507 Category = Unroll;508 break;509 case LoopHintAttr::UnrollAndJam:510 case LoopHintAttr::UnrollAndJamCount:511 Category = UnrollAndJam;512 break;513 case LoopHintAttr::Distribute:514 // Perform the check for duplicated 'distribute' hints.515 Category = Distribute;516 break;517 case LoopHintAttr::PipelineDisabled:518 case LoopHintAttr::PipelineInitiationInterval:519 Category = Pipeline;520 break;521 case LoopHintAttr::VectorizePredicate:522 Category = VectorizePredicate;523 break;524 };525 526 assert(Category != NumberOfCategories && "Unhandled loop hint option");527 auto &CategoryState = HintAttrs[Category];528 const LoopHintAttr *PrevAttr;529 if (Option == LoopHintAttr::Vectorize ||530 Option == LoopHintAttr::Interleave || Option == LoopHintAttr::Unroll ||531 Option == LoopHintAttr::UnrollAndJam ||532 Option == LoopHintAttr::VectorizePredicate ||533 Option == LoopHintAttr::PipelineDisabled ||534 Option == LoopHintAttr::Distribute) {535 // Enable|Disable|AssumeSafety hint. For example, vectorize(enable).536 PrevAttr = CategoryState.StateAttr;537 CategoryState.StateAttr = LH;538 } else {539 // Numeric hint. For example, vectorize_width(8).540 PrevAttr = CategoryState.NumericAttr;541 CategoryState.NumericAttr = LH;542 }543 544 PrintingPolicy Policy(S.Context.getLangOpts());545 SourceLocation OptionLoc = LH->getRange().getBegin();546 if (PrevAttr)547 // Cannot specify same type of attribute twice.548 S.Diag(OptionLoc, diag::err_pragma_loop_compatibility)549 << /*Duplicate=*/true << PrevAttr->getDiagnosticName(Policy)550 << LH->getDiagnosticName(Policy);551 552 if (CategoryState.StateAttr && CategoryState.NumericAttr &&553 (Category == Unroll || Category == UnrollAndJam ||554 CategoryState.StateAttr->getState() == LoopHintAttr::Disable)) {555 // Disable hints are not compatible with numeric hints of the same556 // category. As a special case, numeric unroll hints are also not557 // compatible with enable or full form of the unroll pragma because these558 // directives indicate full unrolling.559 S.Diag(OptionLoc, diag::err_pragma_loop_compatibility)560 << /*Duplicate=*/false561 << CategoryState.StateAttr->getDiagnosticName(Policy)562 << CategoryState.NumericAttr->getDiagnosticName(Policy);563 }564 }565}566 567static Attr *handleOpenCLUnrollHint(Sema &S, Stmt *St, const ParsedAttr &A,568 SourceRange Range) {569 // Although the feature was introduced only in OpenCL C v2.0 s6.11.5, it's570 // useful for OpenCL 1.x too and doesn't require HW support.571 // opencl_unroll_hint can have 0 arguments (compiler572 // determines unrolling factor) or 1 argument (the unroll factor provided573 // by the user).574 unsigned UnrollFactor = 0;575 if (A.getNumArgs() == 1) {576 Expr *E = A.getArgAsExpr(0);577 std::optional<llvm::APSInt> ArgVal;578 579 if (!(ArgVal = E->getIntegerConstantExpr(S.Context))) {580 S.Diag(A.getLoc(), diag::err_attribute_argument_type)581 << A << AANT_ArgumentIntegerConstant << E->getSourceRange();582 return nullptr;583 }584 585 int Val = ArgVal->getSExtValue();586 if (Val <= 0) {587 S.Diag(A.getRange().getBegin(),588 diag::err_attribute_requires_positive_integer)589 << A << /* positive */ 0;590 return nullptr;591 }592 UnrollFactor = static_cast<unsigned>(Val);593 }594 595 return ::new (S.Context) OpenCLUnrollHintAttr(S.Context, A, UnrollFactor);596}597 598static Attr *handleHLSLLoopHintAttr(Sema &S, Stmt *St, const ParsedAttr &A,599 SourceRange Range) {600 601 if (A.getSemanticSpelling() == HLSLLoopHintAttr::Spelling::Microsoft_loop &&602 !A.checkAtMostNumArgs(S, 0))603 return nullptr;604 605 unsigned UnrollFactor = 0;606 if (A.getNumArgs() == 1) {607 Expr *E = A.getArgAsExpr(0);608 609 if (S.CheckLoopHintExpr(E, St->getBeginLoc(),610 /*AllowZero=*/false))611 return nullptr;612 613 std::optional<llvm::APSInt> ArgVal = E->getIntegerConstantExpr(S.Context);614 // CheckLoopHintExpr handles non int const cases615 assert(ArgVal != std::nullopt && "ArgVal should be an integer constant.");616 int Val = ArgVal->getSExtValue();617 // CheckLoopHintExpr handles negative and zero cases618 assert(Val > 0 && "Val should be a positive integer greater than zero.");619 UnrollFactor = static_cast<unsigned>(Val);620 }621 return ::new (S.Context) HLSLLoopHintAttr(S.Context, A, UnrollFactor);622}623 624static Attr *handleHLSLControlFlowHint(Sema &S, Stmt *St, const ParsedAttr &A,625 SourceRange Range) {626 627 return ::new (S.Context) HLSLControlFlowHintAttr(S.Context, A);628}629 630static Attr *handleAtomicAttr(Sema &S, Stmt *St, const ParsedAttr &AL,631 SourceRange Range) {632 if (!AL.checkAtLeastNumArgs(S, 1))633 return nullptr;634 635 SmallVector<AtomicAttr::ConsumedOption, 6> Options;636 for (unsigned ArgIndex = 0; ArgIndex < AL.getNumArgs(); ++ArgIndex) {637 AtomicAttr::ConsumedOption Option;638 StringRef OptionString;639 SourceLocation Loc;640 641 if (!AL.isArgIdent(ArgIndex)) {642 S.Diag(AL.getArgAsExpr(ArgIndex)->getBeginLoc(),643 diag::err_attribute_argument_type)644 << AL << AANT_ArgumentIdentifier;645 return nullptr;646 }647 648 IdentifierLoc *Ident = AL.getArgAsIdent(ArgIndex);649 OptionString = Ident->getIdentifierInfo()->getName();650 Loc = Ident->getLoc();651 if (!AtomicAttr::ConvertStrToConsumedOption(OptionString, Option)) {652 S.Diag(Loc, diag::err_attribute_invalid_atomic_argument) << OptionString;653 return nullptr;654 }655 Options.push_back(Option);656 }657 658 return ::new (S.Context)659 AtomicAttr(S.Context, AL, Options.data(), Options.size());660}661 662static Attr *ProcessStmtAttribute(Sema &S, Stmt *St, const ParsedAttr &A,663 SourceRange Range) {664 if (A.isInvalid() || A.getKind() == ParsedAttr::IgnoredAttribute)665 return nullptr;666 667 // Unknown attributes are automatically warned on. Target-specific attributes668 // which do not apply to the current target architecture are treated as669 // though they were unknown attributes.670 const TargetInfo *Aux = S.Context.getAuxTargetInfo();671 if (A.getKind() == ParsedAttr::UnknownAttribute ||672 !(A.existsInTarget(S.Context.getTargetInfo()) ||673 (S.Context.getLangOpts().SYCLIsDevice && Aux &&674 A.existsInTarget(*Aux)))) {675 if (A.isRegularKeywordAttribute()) {676 S.Diag(A.getLoc(), diag::err_keyword_not_supported_on_target)677 << A << A.getRange();678 } else if (A.isDeclspecAttribute()) {679 S.Diag(A.getLoc(), diag::warn_unhandled_ms_attribute_ignored)680 << A << A.getRange();681 } else {682 S.DiagnoseUnknownAttribute(A);683 }684 return nullptr;685 }686 687 if (S.checkCommonAttributeFeatures(St, A))688 return nullptr;689 690 switch (A.getKind()) {691 case ParsedAttr::AT_AlwaysInline:692 return handleAlwaysInlineAttr(S, St, A, Range);693 case ParsedAttr::AT_CXXAssume:694 return handleCXXAssumeAttr(S, St, A, Range);695 case ParsedAttr::AT_FallThrough:696 return handleFallThroughAttr(S, St, A, Range);697 case ParsedAttr::AT_LoopHint:698 return handleLoopHintAttr(S, St, A, Range);699 case ParsedAttr::AT_HLSLLoopHint:700 return handleHLSLLoopHintAttr(S, St, A, Range);701 case ParsedAttr::AT_HLSLControlFlowHint:702 return handleHLSLControlFlowHint(S, St, A, Range);703 case ParsedAttr::AT_OpenCLUnrollHint:704 return handleOpenCLUnrollHint(S, St, A, Range);705 case ParsedAttr::AT_Suppress:706 return handleSuppressAttr(S, St, A, Range);707 case ParsedAttr::AT_NoMerge:708 return handleNoMergeAttr(S, St, A, Range);709 case ParsedAttr::AT_NoInline:710 return handleNoInlineAttr(S, St, A, Range);711 case ParsedAttr::AT_MustTail:712 return handleMustTailAttr(S, St, A, Range);713 case ParsedAttr::AT_Likely:714 return handleLikely(S, St, A, Range);715 case ParsedAttr::AT_Unlikely:716 return handleUnlikely(S, St, A, Range);717 case ParsedAttr::AT_CodeAlign:718 return handleCodeAlignAttr(S, St, A);719 case ParsedAttr::AT_MSConstexpr:720 return handleMSConstexprAttr(S, St, A, Range);721 case ParsedAttr::AT_NoConvergent:722 return handleNoConvergentAttr(S, St, A, Range);723 case ParsedAttr::AT_Annotate:724 return S.CreateAnnotationAttr(A);725 case ParsedAttr::AT_Atomic:726 return handleAtomicAttr(S, St, A, Range);727 default:728 if (Attr *AT = nullptr; A.getInfo().handleStmtAttribute(S, St, A, AT) !=729 ParsedAttrInfo::NotHandled) {730 return AT;731 }732 // N.B., ClangAttrEmitter.cpp emits a diagnostic helper that ensures a733 // declaration attribute is not written on a statement, but this code is734 // needed for attributes in Attr.td that do not list any subjects.735 S.Diag(A.getRange().getBegin(), diag::err_decl_attribute_invalid_on_stmt)736 << A << A.isRegularKeywordAttribute() << St->getBeginLoc();737 return nullptr;738 }739}740 741void Sema::ProcessStmtAttributes(Stmt *S, const ParsedAttributes &InAttrs,742 SmallVectorImpl<const Attr *> &OutAttrs) {743 for (const ParsedAttr &AL : InAttrs) {744 if (const Attr *A = ProcessStmtAttribute(*this, S, AL, InAttrs.Range))745 OutAttrs.push_back(A);746 }747 748 CheckForIncompatibleAttributes(*this, OutAttrs);749 CheckForDuplicateLoopAttrs<CodeAlignAttr>(*this, OutAttrs);750}751 752bool Sema::CheckRebuiltStmtAttributes(ArrayRef<const Attr *> Attrs) {753 CheckForDuplicateLoopAttrs<CodeAlignAttr>(*this, Attrs);754 return false;755}756 757ExprResult Sema::ActOnCXXAssumeAttr(Stmt *St, const ParsedAttr &A,758 SourceRange Range) {759 if (A.getNumArgs() != 1 || !A.getArgAsExpr(0)) {760 Diag(A.getLoc(), diag::err_attribute_wrong_number_arguments)761 << A.getAttrName() << 1 << Range;762 return ExprError();763 }764 765 auto *Assumption = A.getArgAsExpr(0);766 767 if (DiagnoseUnexpandedParameterPack(Assumption)) {768 return ExprError();769 }770 771 if (Assumption->getDependence() == ExprDependence::None) {772 ExprResult Res = BuildCXXAssumeExpr(Assumption, A.getAttrName(), Range);773 if (Res.isInvalid())774 return ExprError();775 Assumption = Res.get();776 }777 778 if (!getLangOpts().CPlusPlus23 &&779 A.getSyntax() == AttributeCommonInfo::AS_CXX11)780 Diag(A.getLoc(), diag::ext_cxx23_attr) << A << Range;781 782 return Assumption;783}784 785ExprResult Sema::BuildCXXAssumeExpr(Expr *Assumption,786 const IdentifierInfo *AttrName,787 SourceRange Range) {788 if (!Assumption)789 return ExprError();790 791 ExprResult Res = CheckPlaceholderExpr(Assumption);792 if (Res.isInvalid())793 return ExprError();794 795 Res = PerformContextuallyConvertToBool(Res.get());796 if (Res.isInvalid())797 return ExprError();798 799 Res = ActOnFinishFullExpr(Res.get(), /*DiscardedValue=*/false);800 if (Res.isInvalid())801 return ExprError();802 803 Assumption = Res.get();804 if (Assumption->HasSideEffects(Context))805 Diag(Assumption->getBeginLoc(), diag::warn_assume_side_effects)806 << AttrName << Range;807 808 return Assumption;809}810