Index: include/clang/AST/StmtTransform.h =================================================================== --- /dev/null +++ include/clang/AST/StmtTransform.h @@ -0,0 +1,51 @@ +//===--- StmtTransform.h - Code transformation AST nodes --------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Transformation directive statement and clauses for the AST. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_STMTTRANSFROM_H +#define LLVM_CLANG_AST_STMTTRANSFROM_H + +#include "clang/AST/Stmt.h" +#include "clang/AST/Transform.h" +#include "llvm/Support/raw_ostream.h" + +namespace clang { + +/// Represents a clause of a \p TransformExecutableDirective. +class TransformClause { +public: + enum Kind { + UnknownKind, +#define TRANSFORM_CLAUSE(Keyword, Name) Name##Kind, +#define TRANSFORM_CLAUSE_LAST(Keyword, Name) Name##Kind, LastKind = Name##Kind +#include "clang/AST/TransformKinds.def" + }; + + static bool isValidForTransform(Transform::Kind TransformKind, + TransformClause::Kind ClauseKind); + static Kind getClauseKind(Transform::Kind TransformKind, llvm::StringRef Str); + + // TODO: implement +}; + +/// Represents +/// +/// #pragma clang transform +/// +/// in the AST. +class TransformExecutableDirective final { + // TODO: implement +}; + +const Stmt *getAssociatedLoop(const Stmt *S); +} // namespace clang + +#endif /* LLVM_CLANG_AST_STMTTRANSFROM_H */ Index: include/clang/AST/Transform.h =================================================================== --- /dev/null +++ include/clang/AST/Transform.h @@ -0,0 +1,40 @@ +//===--- Transform.h - Code transformation classes --------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines classes used for code transformations such as +// #pragma clang transform ... +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_TRANSFORM_H +#define LLVM_CLANG_AST_TRANSFORM_H + +#include "llvm/ADT/StringRef.h" + +namespace clang { + +class Transform { +public: + enum Kind { + UnknownKind, +#define TRANSFORM_DIRECTIVE(Keyword, Name) Name##Kind, +#define TRANSFORM_DIRECTIVE_LAST(Keyword, Name) \ + TRANSFORM_DIRECTIVE(Keyword, Name) \ + LastKind = Name##Kind +#include "clang/AST/TransformKinds.def" + }; + + static Kind getTransformDirectiveKind(llvm::StringRef Str); + static llvm::StringRef getTransformDirectiveKeyword(Kind K); + static llvm::StringRef getTransformDirectiveName(Kind K); + + // TODO: implement +}; + +} // namespace clang +#endif /* LLVM_CLANG_AST_TRANSFORM_H */ Index: include/clang/AST/TransformKinds.def =================================================================== --- /dev/null +++ include/clang/AST/TransformKinds.def @@ -0,0 +1,39 @@ + +#ifndef TRANSFORM_DIRECTIVE +# define TRANSFORM_DIRECTIVE(Keyword, Name) +#endif +#ifndef TRANSFORM_DIRECTIVE_LAST +# define TRANSFORM_DIRECTIVE_LAST(Keyword, Name) TRANSFORM_DIRECTIVE(Keyword, Name) +#endif + +// Loop transformations +TRANSFORM_DIRECTIVE(distribute,LoopDistribution) +TRANSFORM_DIRECTIVE(vectorize,LoopVectorization) +TRANSFORM_DIRECTIVE(interleave,LoopInterleaving) +TRANSFORM_DIRECTIVE(vectorize_interleave,LoopVectorizationInterleaving) +TRANSFORM_DIRECTIVE(unrollandjam,LoopUnrollAndJam) +TRANSFORM_DIRECTIVE(unroll,LoopUnrolling) +TRANSFORM_DIRECTIVE(pipeline,LoopPipelining) + +// Assumptions +TRANSFORM_DIRECTIVE_LAST(assume_parallel,LoopAssumeParallel) + +#undef TRANSFORM_DIRECTIVE +#undef TRANSFORM_DIRECTIVE_LAST + + +#ifndef TRANSFORM_CLAUSE +# define TRANSFORM_CLAUSE(Keyword, Name) +#endif +#ifndef TRANSFORM_CLAUSE_LAST +# define TRANSFORM_CLAUSE_LAST(Keyword, Name) TRANSFORM_CLAUSE(Keyword, Name) +#endif + +TRANSFORM_CLAUSE(full,Full) +TRANSFORM_CLAUSE(partial,Partial) + +TRANSFORM_CLAUSE(width,Width) +TRANSFORM_CLAUSE_LAST(factor,Factor) + +#undef TRANSFORM_CLAUSE +#undef TRANSFORM_CLAUSE_LAST Index: include/clang/Basic/DiagnosticGroups.td =================================================================== --- include/clang/Basic/DiagnosticGroups.td +++ include/clang/Basic/DiagnosticGroups.td @@ -1105,3 +1105,5 @@ def CTADMaybeUnsupported : DiagGroup<"ctad-maybe-unsupported">; def FortifySource : DiagGroup<"fortify-source">; + +def ClangTransform : DiagGroup<"pragma-transform">; Index: include/clang/Basic/DiagnosticParseKinds.td =================================================================== --- include/clang/Basic/DiagnosticParseKinds.td +++ include/clang/Basic/DiagnosticParseKinds.td @@ -1233,6 +1233,18 @@ "vectorize_width, interleave, interleave_count, unroll, unroll_count, " "pipeline, pipeline_initiation_interval, vectorize_predicate, or distribute">; +// Pragma transform support. +def err_pragma_transform_expected_directive : Error< + "expected a transformation name">; +def err_pragma_transform_unknown_directive : Error< + "unknown transformation">; +def err_pragma_transform_expected_loop : Error< + "expected loop after transformation pragma">; +def err_pragma_transform_expected_clause : Error< + "expected a clause name">; +def err_pragma_transform_unknown_clause : Error< + "unknown clause name">; + def err_pragma_fp_invalid_option : Error< "%select{invalid|missing}0 option%select{ %1|}0; expected contract">; def err_pragma_fp_invalid_argument : Error< Index: include/clang/Parse/Parser.h =================================================================== --- include/clang/Parse/Parser.h +++ include/clang/Parse/Parser.h @@ -1642,6 +1642,17 @@ IsTypeCast }; + using TransformClauseResult = ActionResult; + static TransformClauseResult ClauseError() { + return TransformClauseResult(true); + } + static TransformClauseResult ClauseError(const DiagnosticBuilder &) { + return ClauseError(); + } + static TransformClauseResult ClauseEmpty() { + return TransformClauseResult(false); + } + ExprResult ParseExpression(TypeCastState isTypeCast = NotTypeCast); ExprResult ParseConstantExpressionInExprEvalContext( TypeCastState isTypeCast = NotTypeCast); @@ -1978,6 +1989,12 @@ SourceLocation *TrailingElseLoc, ParsedAttributesWithRange &Attrs); + Transform::Kind + tryParsePragmaTransform(SourceLocation BeginLoc, ParsedStmtContext StmtCtx, + SmallVectorImpl &Clauses); + StmtResult ParsePragmaTransform(ParsedStmtContext StmtCtx); + TransformClauseResult ParseTransformClause(Transform::Kind TransformKind); + /// Describes the behavior that should be taken for an __if_exists /// block. enum IfExistsBehavior { Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -28,6 +28,7 @@ #include "clang/AST/NSAPI.h" #include "clang/AST/PrettyPrinter.h" #include "clang/AST/StmtCXX.h" +#include "clang/AST/StmtTransform.h" #include "clang/AST/TypeLoc.h" #include "clang/AST/TypeOrdering.h" #include "clang/Basic/ExpressionTraits.h" @@ -11528,6 +11529,26 @@ ConstructorDestructor, BuiltinFunction }; + + using TransformResult = ActionResult; + static TransformResult TransformError() { return TransformResult(true); } + static TransformResult TransformError(const DiagnosticBuilder &) { + return TransformResult(); + } + static TransformResult TransformEmpty() { return TransformResult(false); } + + TransformResult ActOnTransform(Transform::Kind Kind, + llvm::ArrayRef Clauses, + SourceRange Loc); + StmtResult + ActOnLoopTransformDirective(Transform::Kind Kind, Transform *Trans, + llvm::ArrayRef Clauses, + Stmt *AStmt, SourceRange Loc); + + TransformClause *ActOnFullClause(SourceRange Loc); + TransformClause *ActOnPartialClause(SourceRange Loc, Expr *Factor); + TransformClause *ActOnWidthClause(SourceRange Loc, Expr *Width); + TransformClause *ActOnFactorClause(SourceRange Loc, Expr *Factor); }; /// RAII object that enters a new expression evaluation context. Index: lib/AST/CMakeLists.txt =================================================================== --- lib/AST/CMakeLists.txt +++ lib/AST/CMakeLists.txt @@ -98,10 +98,12 @@ StmtOpenMP.cpp StmtPrinter.cpp StmtProfile.cpp + StmtTransform.cpp StmtViz.cpp TemplateBase.cpp TemplateName.cpp TextNodeDumper.cpp + Transform.cpp Type.cpp TypeLoc.cpp TypePrinter.cpp Index: lib/AST/StmtTransform.cpp =================================================================== --- /dev/null +++ lib/AST/StmtTransform.cpp @@ -0,0 +1,64 @@ +//===--- StmtTransform.h - Code transformation AST nodes --------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Transformation directive statement and clauses for the AST. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/StmtTransform.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Stmt.h" +#include "clang/AST/StmtOpenMP.h" + +using namespace clang; + +bool TransformClause::isValidForTransform(Transform::Kind TransformKind, + TransformClause::Kind ClauseKind) { + switch (TransformKind) { + case clang::Transform::LoopUnrollingKind: + return ClauseKind == PartialKind || ClauseKind == FullKind; + case clang::Transform::LoopUnrollAndJamKind: + return ClauseKind == PartialKind; + case clang::Transform::LoopVectorizationKind: + return ClauseKind == WidthKind; + case clang::Transform::LoopInterleavingKind: + return ClauseKind == FactorKind; + default: + return false; + } +} + +TransformClause::Kind +TransformClause ::getClauseKind(Transform::Kind TransformKind, + llvm::StringRef Str) { +#define TRANSFORM_CLAUSE(Keyword, Name) \ + if (isValidForTransform(TransformKind, TransformClause::Kind::Name##Kind) && \ + Str == #Keyword) \ + return TransformClause::Kind::Name##Kind; +#include "clang/AST/TransformKinds.def" + return TransformClause::UnknownKind; +} + +const Stmt *clang::getAssociatedLoop(const Stmt *S) { + switch (S->getStmtClass()) { + case Stmt::ForStmtClass: + case Stmt::WhileStmtClass: + case Stmt::DoStmtClass: + case Stmt::CXXForRangeStmtClass: + return S; + case Stmt::CapturedStmtClass: + return getAssociatedLoop(cast(S)->getCapturedStmt()); + case Stmt::AttributedStmtClass: + return getAssociatedLoop(cast(S)->getSubStmt()); + default: + if (auto LD = dyn_cast(S)) + return getAssociatedLoop(LD->getAssociatedStmt()); + } + + return nullptr; +} Index: lib/AST/Transform.cpp =================================================================== --- /dev/null +++ lib/AST/Transform.cpp @@ -0,0 +1,48 @@ +//===--- Transform.h - Code transformation classes --------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines classes used for code transformations such as +// #pragma clang transform ... +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/Transform.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/Casting.h" + +using namespace clang; + +Transform::Kind Transform ::getTransformDirectiveKind(llvm::StringRef Str) { + return llvm::StringSwitch(Str) +#define TRANSFORM_DIRECTIVE(Keyword, Name) \ + .Case(#Keyword, Transform::Kind::Name##Kind) +#include "clang/AST/TransformKinds.def" + .Default(Transform::UnknownKind); +} + +llvm::StringRef Transform ::getTransformDirectiveKeyword(Kind K) { + assert(K >= UnknownKind); + assert(K <= LastKind); + const char *Keywords[LastKind + 1] = { + "<>", +#define TRANSFORM_DIRECTIVE(Keyword, Name) #Keyword, +#include "clang/AST/TransformKinds.def" + }; + return Keywords[K]; +} + +llvm::StringRef Transform ::getTransformDirectiveName(Kind K) { + assert(K >= UnknownKind); + assert(K <= LastKind); + const char *Keywords[LastKind + 1] = { + "<>", +#define TRANSFORM_DIRECTIVE(Keyword, Name) #Name, +#include "clang/AST/TransformKinds.def" + }; + return Keywords[K]; +} Index: lib/Parse/CMakeLists.txt =================================================================== --- lib/Parse/CMakeLists.txt +++ lib/Parse/CMakeLists.txt @@ -19,6 +19,7 @@ ParseStmtAsm.cpp ParseTemplate.cpp ParseTentative.cpp + ParseTransform.cpp Parser.cpp LINK_LIBS Index: lib/Parse/ParseStmt.cpp =================================================================== --- lib/Parse/ParseStmt.cpp +++ lib/Parse/ParseStmt.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "clang/AST/PrettyDeclStackTrace.h" +#include "clang/AST/Transform.h" #include "clang/Basic/Attributes.h" #include "clang/Basic/PrettyStackTrace.h" #include "clang/Parse/LoopHint.h" @@ -398,6 +399,10 @@ ProhibitAttributes(Attrs); return ParsePragmaLoopHint(Stmts, StmtCtx, TrailingElseLoc, Attrs); + case tok::annot_pragma_transform: + ProhibitAttributes(Attrs); + return ParsePragmaTransform(StmtCtx); + case tok::annot_pragma_dump: HandlePragmaDump(); return StmtEmpty(); Index: lib/Parse/ParseTransform.cpp =================================================================== --- /dev/null +++ lib/Parse/ParseTransform.cpp @@ -0,0 +1,145 @@ +//===---- ParseTransform.h -------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Parse #pragma clang transform ... +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/StmtTransform.h" +#include "clang/Parse/Parser.h" +#include "clang/Parse/RAIIObjectsForParser.h" + +using namespace clang; + +Transform::Kind +Parser::tryParsePragmaTransform(SourceLocation BeginLoc, + ParsedStmtContext StmtCtx, + SmallVectorImpl &Clauses) { + // ... Tok= | <...> tok::annot_pragma_transform_end ... + if (Tok.isNot(tok::identifier)) { + Diag(Tok, diag::err_pragma_transform_expected_directive); + return Transform::UnknownKind; + } + std::string DirectiveStr = PP.getSpelling(Tok); + Transform::Kind DirectiveKind = + Transform::getTransformDirectiveKind(DirectiveStr); + ConsumeToken(); + + switch (DirectiveKind) { + case Transform::LoopUnrollingKind: + case Transform::LoopUnrollAndJamKind: + case Transform::LoopDistributionKind: + case Transform::LoopVectorizationKind: + case Transform::LoopInterleavingKind: + break; + default: + Diag(Tok, diag::err_pragma_transform_unknown_directive); + return Transform::UnknownKind; + } + + while (true) { + TransformClauseResult Clause = ParseTransformClause(DirectiveKind); + if (Clause.isInvalid()) + return Transform::UnknownKind; + if (!Clause.isUsable()) + break; + + Clauses.push_back(Clause.get()); + } + + assert(Tok.is(tok::annot_pragma_transform_end)); + return DirectiveKind; +} + +StmtResult Parser::ParsePragmaTransform(ParsedStmtContext StmtCtx) { + assert(Tok.is(tok::annot_pragma_transform) && "Not a transform directive!"); + + // ... Tok=annot_pragma_transform | <...> annot_pragma_transform_end + // ... + SourceLocation BeginLoc = ConsumeAnnotationToken(); + + ParenBraceBracketBalancer BalancerRAIIObj(*this); + + SmallVector DirectiveClauses; + Transform::Kind DirectiveKind = + tryParsePragmaTransform(BeginLoc, StmtCtx, DirectiveClauses); + if (DirectiveKind == Transform::UnknownKind) { + SkipUntil(tok::annot_pragma_transform_end); + return StmtError(); + } + + assert(Tok.is(tok::annot_pragma_transform_end)); + SourceLocation EndLoc = ConsumeAnnotationToken(); + + SourceLocation PreStmtLoc = Tok.getLocation(); + StmtResult AssociatedStmt = ParseStatement(); + if (AssociatedStmt.isInvalid()) + return AssociatedStmt; + if (!getAssociatedLoop(AssociatedStmt.get())) + return StmtError( + Diag(PreStmtLoc, diag::err_pragma_transform_expected_loop)); + + return Actions.ActOnLoopTransformDirective( + DirectiveKind, nullptr, DirectiveClauses, AssociatedStmt.get(), + {BeginLoc, EndLoc}); +} + +Parser::TransformClauseResult +Parser::ParseTransformClause(Transform::Kind TransformKind) { + // No more clauses + if (Tok.is(tok::annot_pragma_transform_end)) + return ClauseEmpty(); + + SourceLocation StartLoc = Tok.getLocation(); + if (Tok.isNot(tok::identifier)) + return ClauseError(Diag(Tok, diag::err_pragma_transform_expected_clause)); + std::string ClauseKeyword = PP.getSpelling(Tok); + ConsumeToken(); + TransformClause::Kind Kind = + TransformClause::getClauseKind(TransformKind, ClauseKeyword); + + switch (Kind) { + case TransformClause::UnknownKind: + return ClauseError(Diag(Tok, diag::err_pragma_transform_unknown_clause)); + + // Clauses without arguments. + case TransformClause::FullKind: + return Actions.ActOnFullClause(SourceRange{StartLoc, StartLoc}); + + // Clauses with integer argument. + case TransformClause::PartialKind: + case TransformClause::WidthKind: + case TransformClause::FactorKind: { + BalancedDelimiterTracker T(*this, tok::l_paren, + tok::annot_pragma_transform_end); + if (T.expectAndConsume(diag::err_expected_lparen_after, + ClauseKeyword.data())) + return ClauseError(); + + ExprResult Expr = ParseConstantExpression(); + if (Expr.isInvalid()) + return ClauseError(); + + if (T.consumeClose()) + return ClauseError(); + SourceLocation EndLoc = T.getCloseLocation(); + SourceRange Range{StartLoc, EndLoc}; + switch (Kind) { + case TransformClause::PartialKind: + return Actions.ActOnPartialClause(Range, Expr.get()); + case TransformClause::WidthKind: + return Actions.ActOnWidthClause(Range, Expr.get()); + case TransformClause::FactorKind: + return Actions.ActOnFactorClause(Range, Expr.get()); + default: + llvm_unreachable("Unhandled clause"); + } + } + } + llvm_unreachable("Unhandled clause"); +} Index: lib/Sema/CMakeLists.txt =================================================================== --- lib/Sema/CMakeLists.txt +++ lib/Sema/CMakeLists.txt @@ -62,6 +62,7 @@ SemaTemplateInstantiate.cpp SemaTemplateInstantiateDecl.cpp SemaTemplateVariadic.cpp + SemaTransform.cpp SemaType.cpp TypeLocBuilder.cpp Index: lib/Sema/SemaTransform.cpp =================================================================== --- /dev/null +++ lib/Sema/SemaTransform.cpp @@ -0,0 +1,62 @@ +//===---- SemaTransform.h ------------------------------------- -*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Semantic analysis for code transformations. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/AST/StmtTransform.h" +#include "clang/AST/Transform.h" +#include "clang/Sema/Sema.h" +#include "clang/Sema/SemaDiagnostic.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringMap.h" + +using namespace clang; + +static bool isTemplateDependent(Expr *E) { + return E->isValueDependent() || E->isTypeDependent() || + E->isInstantiationDependent() || E->containsUnexpandedParameterPack(); +} + +Sema::TransformResult +Sema::ActOnTransform(Transform::Kind Kind, + llvm::ArrayRef Clauses, + SourceRange Loc) { + // TOOD: implement + return TransformError(); +} + +StmtResult +Sema::ActOnLoopTransformDirective(Transform::Kind Kind, Transform *Trans, + llvm::ArrayRef Clauses, + Stmt *AStmt, SourceRange Loc) { + // TOOD: implement + return StmtError(); +} + +TransformClause *Sema::ActOnFullClause(SourceRange Loc) { + // TOOD: implement + return nullptr; +} + +TransformClause *Sema::ActOnPartialClause(SourceRange Loc, Expr *Factor) { + // TOOD: implement + return nullptr; +} + +TransformClause *Sema::ActOnWidthClause(SourceRange Loc, Expr *Width) { + // TOOD: implement + return nullptr; +} + +TransformClause *Sema::ActOnFactorClause(SourceRange Loc, Expr *Factor) { + // TOOD: implement + return nullptr; +} Index: test/Parser/pragma-transform.cpp =================================================================== --- /dev/null +++ test/Parser/pragma-transform.cpp @@ -0,0 +1,90 @@ +// RUN: %clang_cc1 -std=c++11 -verify %s + +void pragma_transform(int *List, int Length) { +// FIXME: This does not emit an error +#pragma clang + +/* expected-error@+1 {{expected a transformation name}} */ +#pragma clang transform + for (int i = 0; i < Length; i+=1) + List[i] = i; + +/* expected-error@+1 {{unknown transformation}} */ +#pragma clang transform unknown_transformation + for (int i = 0; i < Length; i+=1) + List[i] = i; + +/* expected-error@+2 {{expected loop after transformation pragma}} */ +#pragma clang transform unroll + pragma_transform(List, Length); + +/* expected-error@+1 {{unknown clause name}} */ +#pragma clang transform unroll unknown_clause + for (int i = 0; i < Length; i+=1) + List[i] = i; + +/* expected-error@+1 {{expected '(' after 'partial'}} */ +#pragma clang transform unroll partial + for (int i = 0; i < Length; i+=1) + List[i] = i; + +/* expected-error@+1 {{expected expression}} */ +#pragma clang transform unroll partial( + for (int i = 0; i < Length; i+=1) + List[i] = i; + +/* expected-error@+1 {{expected '(' after 'partial'}} */ +#pragma clang transform unroll partial) + for (int i = 0; i < Length; i+=1) + List[i] = i; + +/* expected-error@+2 {{expected ')'}} */ +/* expected-note@+1 {{to match this '('}} */ +#pragma clang transform unroll partial(4 + for (int i = 0; i < Length; i+=1) + List[i] = i; + +/* expected-error@+1 {{expected expression}} */ +#pragma clang transform unroll partial() + for (int i = 0; i < Length; i+=1) + List[i] = i; + +/* expected-error@+1 {{use of undeclared identifier 'badvalue'}} */ +#pragma clang transform unroll partial(badvalue) + for (int i = 0; i < Length; i+=1) + List[i] = i; + + { +/* expected-error@+2 {{expected statement}} */ +#pragma clang transform unroll + } +} + +/* expected-error@+1 {{expected unqualified-id}} */ +#pragma clang transform unroll +int I; + +/* expected-error@+1 {{expected unqualified-id}} */ +#pragma clang transform unroll +void func(); + +class C1 { +/* expected-error@+1 {{expected member name or ';' after declaration specifiers}} */ +#pragma clang transform unroll +}; + +template +void pragma_transform_template_func(int *List, int Length) { +#pragma clang transform unroll partial(F) + for (int i = 0; i < Length; i+=1) + List[i] = i; +} + +template +class C2 { + void pragma_transform_template_class(int *List, int Length) { +#pragma clang transform unroll partial(F) + for (int i = 0; i < Length; i+=1) + List[i] = i; + } +};