diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 68016ec4d58a..8a3b9de19ad3 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -12622,6 +12622,10 @@ def err_omp_transparent_invalid_value : Error<"invalid value for transparent cla " expected one of: omp_not_impex, omp_import, omp_export, omp_impex">; def err_omp_transparent_invalid_type : Error< "transparent clause cannot be applied to type: %0">; +def err_omp_num_teams_multi_expr_not_allowed + : Error<"only two expressions allowed in 'num_teams' clause">; +def err_omp_num_teams_lower_bound_larger + : Error<"lower bound is greater than upper bound in 'num_teams' clause">; } // end of OpenMP category let CategoryName = "Related Result Type Issue" in { diff --git a/clang/lib/AST/OpenMPClause.cpp b/clang/lib/AST/OpenMPClause.cpp index 9e7a8a48372c..48ec1551d060 100644 --- a/clang/lib/AST/OpenMPClause.cpp +++ b/clang/lib/AST/OpenMPClause.cpp @@ -2290,8 +2290,20 @@ void OMPClausePrinter::VisitOMPDeviceClause(OMPDeviceClause *Node) { void OMPClausePrinter::VisitOMPNumTeamsClause(OMPNumTeamsClause *Node) { if (!Node->varlist_empty()) { - OS << "num_teams"; - VisitOMPClauseList(Node, '('); + OS << "num_teams("; + // Handle lower-bound:upper-bound syntax when there are exactly 2 + // expressions + if (Node->varlist_size() == 2) { + llvm::interleave( + Node->varlist(), OS, + [&](const auto *Expr) { Expr->printPretty(OS, nullptr, Policy, 0); }, + ":"); + } else { + // For single expression or other cases, use comma-separated list + llvm::interleaveComma(Node->varlist(), OS, [&](const auto *Expr) { + Expr->printPretty(OS, nullptr, Policy, 0); + }); + } OS << ")"; } } diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp index b41803d23cb2..0bb503484299 100644 --- a/clang/lib/Parse/ParseOpenMP.cpp +++ b/clang/lib/Parse/ParseOpenMP.cpp @@ -5080,6 +5080,62 @@ bool Parser::ParseOpenMPVarList(OpenMPDirectiveKind DKind, Diag(Tok, diag::err_modifier_expected_colon) << "fallback"; } } + } // Handle num_teams clause with optional lower-bound:upper-bound syntax + if (Kind == OMPC_num_teams && !Tok.is(tok::r_paren) && + !Tok.is(tok::annot_pragma_openmp_end)) { + ExprResult FirstExpr = ParseAssignmentExpression(); + if (FirstExpr.isInvalid()) { + SkipUntil(tok::r_paren, tok::annot_pragma_openmp_end, StopBeforeMatch); + Data.RLoc = Tok.getLocation(); + if (!T.consumeClose()) + Data.RLoc = T.getCloseLocation(); + return true; + } + + if (Tok.is(tok::colon)) { + // Lower-bound:upper-bound syntax + ConsumeToken(); + ExprResult UpperBound = ParseAssignmentExpression(); + if (UpperBound.isInvalid()) { + SkipUntil(tok::r_paren, tok::annot_pragma_openmp_end, StopBeforeMatch); + Data.RLoc = Tok.getLocation(); + if (!T.consumeClose()) + Data.RLoc = T.getCloseLocation(); + return true; + } + Vars.push_back(FirstExpr.get()); // lower-bound + Vars.push_back(UpperBound.get()); // upper-bound + Data.RLoc = Tok.getLocation(); + if (!T.consumeClose()) + Data.RLoc = T.getCloseLocation(); + return false; // Success + } + if (Tok.is(tok::comma)) { + Vars.push_back(FirstExpr.get()); + while (Tok.is(tok::comma)) { + ConsumeToken(); + ExprResult NextExpr = ParseAssignmentExpression(); + if (NextExpr.isUsable()) { + Vars.push_back(NextExpr.get()); + } else { + SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openmp_end, + StopBeforeMatch); + break; + } + } + Data.RLoc = Tok.getLocation(); + bool HadError = T.consumeClose(); + if (!HadError) + Data.RLoc = T.getCloseLocation(); + return HadError; + } + + // Single value - parse closing paren + Vars.push_back(FirstExpr.get()); + Data.RLoc = Tok.getLocation(); + if (!T.consumeClose()) + Data.RLoc = T.getCloseLocation(); + return false; // Success } bool IsComma = diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp index e90884a89bf6..3a0e87efdc0e 100644 --- a/clang/lib/Sema/SemaOpenMP.cpp +++ b/clang/lib/Sema/SemaOpenMP.cpp @@ -13508,7 +13508,8 @@ StmtResult SemaOpenMP::ActOnOpenMPTeamsDirective(ArrayRef Clauses, return StmtError(); if (!checkNumExprsInClause( - *this, Clauses, /*MaxNum=*/1, diag::err_omp_multi_expr_not_allowed) || + *this, Clauses, /*MaxNum=*/2, + diag::err_omp_num_teams_multi_expr_not_allowed) || !checkNumExprsInClause( *this, Clauses, /*MaxNum=*/1, diag::err_omp_multi_expr_not_allowed)) return StmtError(); @@ -14287,16 +14288,20 @@ StmtResult SemaOpenMP::ActOnOpenMPTargetTeamsDirective( return StmtError(); } - unsigned ClauseMaxNumExprs = HasBareClause ? 3 : 1; - unsigned DiagNo = HasBareClause - ? diag::err_ompx_more_than_three_expr_not_allowed - : diag::err_omp_multi_expr_not_allowed; - if (!checkNumExprsInClause(*this, Clauses, - ClauseMaxNumExprs, DiagNo) || - !checkNumExprsInClause(*this, Clauses, - ClauseMaxNumExprs, DiagNo)) - return StmtError(); + unsigned ClauseMaxNumExprs = HasBareClause ? 3 : 2; + unsigned NumTeamsDiag = HasBareClause + ? diag::err_ompx_more_than_three_expr_not_allowed + : diag::err_omp_num_teams_multi_expr_not_allowed; + unsigned ThreadLimitDiag = + HasBareClause ? diag::err_ompx_more_than_three_expr_not_allowed + : diag::err_omp_multi_expr_not_allowed; + if (!checkNumExprsInClause( + *this, Clauses, ClauseMaxNumExprs, NumTeamsDiag) || + !checkNumExprsInClause( + *this, Clauses, ClauseMaxNumExprs, ThreadLimitDiag)) { + return StmtError(); + } return OMPTargetTeamsDirective::Create(getASTContext(), StartLoc, EndLoc, Clauses, AStmt); } @@ -14308,7 +14313,8 @@ StmtResult SemaOpenMP::ActOnOpenMPTargetTeamsDistributeDirective( return StmtError(); if (!checkNumExprsInClause( - *this, Clauses, /*MaxNum=*/1, diag::err_omp_multi_expr_not_allowed) || + *this, Clauses, /*MaxNum=*/2, + diag::err_omp_num_teams_multi_expr_not_allowed) || !checkNumExprsInClause( *this, Clauses, /*MaxNum=*/1, diag::err_omp_multi_expr_not_allowed)) return StmtError(); @@ -14340,7 +14346,8 @@ StmtResult SemaOpenMP::ActOnOpenMPTargetTeamsDistributeParallelForDirective( return StmtError(); if (!checkNumExprsInClause( - *this, Clauses, /*MaxNum=*/1, diag::err_omp_multi_expr_not_allowed) || + *this, Clauses, /*MaxNum=*/2, + diag::err_omp_num_teams_multi_expr_not_allowed) || !checkNumExprsInClause( *this, Clauses, /*MaxNum=*/1, diag::err_omp_multi_expr_not_allowed)) return StmtError(); @@ -14373,7 +14380,8 @@ StmtResult SemaOpenMP::ActOnOpenMPTargetTeamsDistributeParallelForSimdDirective( return StmtError(); if (!checkNumExprsInClause( - *this, Clauses, /*MaxNum=*/1, diag::err_omp_multi_expr_not_allowed) || + *this, Clauses, /*MaxNum=*/2, + diag::err_omp_num_teams_multi_expr_not_allowed) || !checkNumExprsInClause( *this, Clauses, /*MaxNum=*/1, diag::err_omp_multi_expr_not_allowed)) return StmtError(); @@ -14409,7 +14417,8 @@ StmtResult SemaOpenMP::ActOnOpenMPTargetTeamsDistributeSimdDirective( return StmtError(); if (!checkNumExprsInClause( - *this, Clauses, /*MaxNum=*/1, diag::err_omp_multi_expr_not_allowed) || + *this, Clauses, /*MaxNum=*/2, + diag::err_omp_num_teams_multi_expr_not_allowed) || !checkNumExprsInClause( *this, Clauses, /*MaxNum=*/1, diag::err_omp_multi_expr_not_allowed)) return StmtError(); @@ -23880,6 +23889,31 @@ OMPClause *SemaOpenMP::ActOnOpenMPNumTeamsClause(ArrayRef VarList, return nullptr; } + // OpenMP 5.2: Validate lower-bound ≤ upper-bound constraint + if (VarList.size() == 2) { + Expr *LowerBound = VarList[0]; + Expr *UpperBound = VarList[1]; + + // Check if both are compile-time constants for validation + if (!LowerBound->isValueDependent() && !UpperBound->isValueDependent() && + LowerBound->isIntegerConstantExpr(getASTContext()) && + UpperBound->isIntegerConstantExpr(getASTContext())) { + + // Get the actual constant values + llvm::APSInt LowerVal = + LowerBound->EvaluateKnownConstInt(getASTContext()); + llvm::APSInt UpperVal = + UpperBound->EvaluateKnownConstInt(getASTContext()); + + if (LowerVal > UpperVal) { + Diag(LowerBound->getExprLoc(), + diag::err_omp_num_teams_lower_bound_larger) + << LowerBound->getSourceRange() << UpperBound->getSourceRange(); + return nullptr; + } + } + } + OpenMPDirectiveKind DKind = DSAStack->getCurrentDirective(); OpenMPDirectiveKind CaptureRegion = getOpenMPCaptureRegionForClause( DKind, OMPC_num_teams, getLangOpts().OpenMP); diff --git a/clang/test/OpenMP/num_teams_clause_ast.cpp b/clang/test/OpenMP/num_teams_clause_ast.cpp new file mode 100644 index 000000000000..c7addd6387f5 --- /dev/null +++ b/clang/test/OpenMP/num_teams_clause_ast.cpp @@ -0,0 +1,103 @@ +// RUN: %clang_cc1 -verify -fopenmp -ast-print %s | FileCheck %s +// RUN: %clang_cc1 -fopenmp -x c++ -std=c++11 -emit-pch -o %t %s +// RUN: %clang_cc1 -fopenmp -std=c++11 -include-pch %t -verify %s -ast-print | FileCheck %s +// expected-no-diagnostics + +#ifndef HEADER +#define HEADER + +void foo(); + +// CHECK-LABEL: void test_lower_upper_bound() +void test_lower_upper_bound() { + int lower = 2, upper = 8; + // CHECK: #pragma omp teams num_teams(2:8) + #pragma omp teams num_teams(2:8) + { foo(); } + + // CHECK: #pragma omp teams num_teams(lower:upper) + #pragma omp teams num_teams(lower:upper) + { foo(); } + + // CHECK: #pragma omp target teams num_teams(1:10) + #pragma omp target teams num_teams(1:10) + { foo(); } + + // CHECK: #pragma omp target teams distribute num_teams(3:6) + #pragma omp target teams distribute num_teams(3:6) + for (int i = 0; i < 100; ++i) { } +} + +// CHECK-LABEL: void test_various_directives() +void test_various_directives() { + int lb = 4, ub = 12; + + // CHECK: #pragma omp target teams distribute parallel for num_teams(lb:ub) + #pragma omp target teams distribute parallel for num_teams(lb:ub) + for (int i = 0; i < 100; ++i) { } + + // CHECK: #pragma omp target teams distribute parallel for simd num_teams(2:16) + #pragma omp target teams distribute parallel for simd num_teams(2:16) + for (int i = 0; i < 100; ++i) { } + + // CHECK: #pragma omp target teams distribute simd num_teams(1:8) + #pragma omp target teams distribute simd num_teams(1:8) + for (int i = 0; i < 100; ++i) { } +} + +// CHECK-LABEL: void test_nested_expressions() +void test_nested_expressions() { + int arr[10][10]; + int x = 5, y = 10; + + // CHECK: #pragma omp teams num_teams(arr[0][0]:arr[1][1]) + #pragma omp teams num_teams(arr[0][0]:arr[1][1]) + { foo(); } + + // CHECK: #pragma omp teams num_teams(arr[x][0]:arr[y][1]) + #pragma omp teams num_teams(arr[x][0]:arr[y][1]) + { foo(); } + + // CHECK: #pragma omp teams num_teams((x + 1):(y - 1)) + #pragma omp teams num_teams((x + 1):(y - 1)) + { foo(); } + +} + +// CHECK-LABEL: void test_multi_level_matching_delimiters() +void test_multi_level_matching_delimiters() { + int arr[10][10]; + int x = 5, y = 10; + + // CHECK: #pragma omp teams num_teams(((x + 1) * 2):((y - 1) * 3)) + #pragma omp teams num_teams(((x + 1) * 2):((y - 1) * 3)) + { foo(); } + + + // CHECK: #pragma omp teams num_teams(arr[arr[0][0]][0]:arr[arr[1][1]][1]) + #pragma omp teams num_teams(arr[arr[0][0]][0]:arr[arr[1][1]][1]) + { foo(); } + + // CHECK: #pragma omp teams num_teams((arr[x][y] + 1):(arr[y][x] - 1)) + #pragma omp teams num_teams((arr[x][y] + 1):(arr[y][x] - 1)) + { foo(); } + + // CHECK: #pragma omp teams num_teams((x + (y * 2)):((x * 2) + y)) + #pragma omp teams num_teams((x + (y * 2)):((x * 2) + y)) + { foo(); } + + // CHECK: #pragma omp teams num_teams((arr[0][1] + arr[2][3]):(arr[4][5] + arr[6][7])) + #pragma omp teams num_teams((arr[0][1] + arr[2][3]):(arr[4][5] + arr[6][7])) + { foo(); } +} + +// Template tests +// CHECK-LABEL: template void test_template_type(T lower, T upper) +template +void test_template_type(T lower, T upper) { + // CHECK: #pragma omp teams num_teams(lower:upper) + #pragma omp teams num_teams(lower:upper) + {} +} + +#endif diff --git a/clang/test/OpenMP/target_teams_ast_print.cpp b/clang/test/OpenMP/target_teams_ast_print.cpp index cc47ae92efac..5a577bd2d1b1 100644 --- a/clang/test/OpenMP/target_teams_ast_print.cpp +++ b/clang/test/OpenMP/target_teams_ast_print.cpp @@ -95,7 +95,7 @@ T tmain(T argc, T *argv) { // CHECK-NEXT: foo() // CHECK-NEXT: #pragma omp target teams allocate(my_allocator: f) reduction(^: e,f) reduction(&&: g) uses_allocators(my_allocator(traits)) // CHECK-NEXT: foo() -// CHECK-NEXT: #pragma omp target teams ompx_bare num_teams(1,1,1) thread_limit(d * 1,d * 1,d * 1) +// CHECK-NEXT: #pragma omp target teams ompx_bare num_teams(1, 1, 1) thread_limit(d * 1,d * 1,d * 1) // CHECK-NEXT: foo(); enum Enum { }; @@ -116,7 +116,7 @@ int main (int argc, char **argv) { a=3; // CHECK-NEXT: a = 3; #pragma omp target teams ompx_bare num_teams(1, 2, 3) thread_limit(2, 4, 6) -// CHECK-NEXT: #pragma omp target teams ompx_bare num_teams(1,2,3) thread_limit(2,4,6) +// CHECK-NEXT: #pragma omp target teams ompx_bare num_teams(1, 2, 3) thread_limit(2,4,6) a=4; // CHECK-NEXT: a = 4; #pragma omp target teams default(none), private(argc,b) num_teams(f) firstprivate(argv) reduction(| : c, d) reduction(* : e) thread_limit(f+g) diff --git a/clang/test/OpenMP/target_teams_distribute_num_teams_messages.cpp b/clang/test/OpenMP/target_teams_distribute_num_teams_messages.cpp index 8bf388f0b5da..51948adba354 100644 --- a/clang/test/OpenMP/target_teams_distribute_num_teams_messages.cpp +++ b/clang/test/OpenMP/target_teams_distribute_num_teams_messages.cpp @@ -44,7 +44,7 @@ T tmain(T argc) { #pragma omp target teams distribute num_teams(3.14) // expected-error 2 {{expression must have integral or unscoped enumeration type, not 'double'}} for (int i=0; i<100; i++) foo(); -#pragma omp target teams distribute num_teams(1, 2, 3) // expected-error {{only one expression allowed in 'num_teams' clause}} +#pragma omp target teams distribute num_teams(1, 2, 3) // expected-error {{only two expressions allowed in 'num_teams' clause}} for (int i=0; i<100; i++) foo(); #pragma omp target teams distribute thread_limit(1, 2, 3) // expected-error {{only one expression allowed in 'thread_limit' clause}} @@ -97,7 +97,7 @@ int main(int argc, char **argv) { #pragma omp target teams distribute num_teams (3.14) // expected-error {{expression must have integral or unscoped enumeration type, not 'double'}} for (int i=0; i<100; i++) foo(); -#pragma omp target teams distribute num_teams(1, 2, 3) // expected-error {{only one expression allowed in 'num_teams' clause}} +#pragma omp target teams distribute num_teams(1, 2, 3) // expected-error {{only two expressions allowed in 'num_teams' clause}} for (int i=0; i<100; i++) foo(); #pragma omp target teams distribute thread_limit(1, 2, 3) // expected-error {{only one expression allowed in 'thread_limit' clause}} diff --git a/clang/test/OpenMP/target_teams_distribute_parallel_for_num_teams_messages.cpp b/clang/test/OpenMP/target_teams_distribute_parallel_for_num_teams_messages.cpp index 092e0137d250..02393f5094e0 100644 --- a/clang/test/OpenMP/target_teams_distribute_parallel_for_num_teams_messages.cpp +++ b/clang/test/OpenMP/target_teams_distribute_parallel_for_num_teams_messages.cpp @@ -43,7 +43,7 @@ T tmain(T argc) { for (int i=0; i<100; i++) foo(); #pragma omp target teams distribute parallel for num_teams(3.14) // expected-error 2 {{expression must have integral or unscoped enumeration type, not 'double'}} for (int i=0; i<100; i++) foo(); -#pragma omp target teams distribute parallel for num_teams(1, 2, 3) // expected-error {{only one expression allowed in 'num_teams' clause}} +#pragma omp target teams distribute parallel for num_teams(1, 2, 3) // expected-error {{only two expressions allowed in 'num_teams' clause}} for (int i=0; i<100; i++) foo(); #pragma omp target teams distribute parallel for thread_limit(1, 2, 3) // expected-error {{only one expression allowed in 'thread_limit' clause}} for (int i=0; i<100; i++) foo(); @@ -89,7 +89,7 @@ int main(int argc, char **argv) { #pragma omp target teams distribute parallel for num_teams (3.14) // expected-error {{expression must have integral or unscoped enumeration type, not 'double'}} for (int i=0; i<100; i++) foo(); -#pragma omp target teams distribute parallel for num_teams(1, 2, 3) // expected-error {{only one expression allowed in 'num_teams' clause}} +#pragma omp target teams distribute parallel for num_teams(1, 2, 3) // expected-error {{only two expressions allowed in 'num_teams' clause}} for (int i=0; i<100; i++) foo(); #pragma omp target teams distribute parallel for thread_limit(1, 2, 3) // expected-error {{only one expression allowed in 'thread_limit' clause}} diff --git a/clang/test/OpenMP/teams_num_teams_messages.cpp b/clang/test/OpenMP/teams_num_teams_messages.cpp index 615bf0be0d81..166e77142e27 100644 --- a/clang/test/OpenMP/teams_num_teams_messages.cpp +++ b/clang/test/OpenMP/teams_num_teams_messages.cpp @@ -58,7 +58,7 @@ T tmain(T argc) { #pragma omp teams num_teams(3.14) // expected-error 2 {{expression must have integral or unscoped enumeration type, not 'double'}} foo(); #pragma omp target -#pragma omp teams num_teams (1, 2, 3) // expected-error {{only one expression allowed in 'num_teams' clause}} +#pragma omp teams num_teams (1, 2, 3) // expected-error {{only two expressions allowed in 'num_teams' clause}} foo(); #pragma omp target #pragma omp teams thread_limit(1, 2, 3) // expected-error {{only one expression allowed in 'thread_limit' clause}} @@ -118,7 +118,7 @@ int main(int argc, char **argv) { foo(); #pragma omp target -#pragma omp teams num_teams (1, 2, 3) // expected-error {{only one expression allowed in 'num_teams' clause}} +#pragma omp teams num_teams (1, 2, 3) // expected-error {{only two expressions allowed in 'num_teams' clause}} foo(); #pragma omp target @@ -127,3 +127,123 @@ int main(int argc, char **argv) { return tmain(argc); // expected-note {{in instantiation of function template specialization 'tmain' requested here}} } + +// Test invalid syntax cases for num_teams lower-bound:upper-bound +void test_invalid_syntax() { + int a = 1, b = 2, c = 3; + + // expected-error@+1 {{only two expressions allowed in 'num_teams' clause}} + #pragma omp teams num_teams(a, b, c) + { } + // expected-error@+1 {{lower bound is greater than upper bound in 'num_teams' clause}} + #pragma omp teams num_teams(10:5) + { } + + // expected-error@+1 {{only two expressions allowed in 'num_teams' clause}} + #pragma omp target teams num_teams(a, b, c) + { } + // expected-error@+1 {{lower bound is greater than upper bound in 'num_teams' clause}} + #pragma omp target teams num_teams(8:3) + { } + + // expected-error@+1 {{only two expressions allowed in 'num_teams' clause}} + #pragma omp target teams distribute num_teams(a, b, c) + for (int i = 0; i < 100; ++i) { } + // expected-error@+1 {{lower bound is greater than upper bound in 'num_teams' clause}} + #pragma omp target teams distribute num_teams(15:7) + for (int i = 0; i < 100; ++i) { } + + // expected-error@+1 {{only two expressions allowed in 'num_teams' clause}} + #pragma omp target teams distribute parallel for num_teams(a, b, c) + for (int i = 0; i < 100; ++i) { } + // expected-error@+1 {{lower bound is greater than upper bound in 'num_teams' clause}} + #pragma omp target teams distribute parallel for num_teams(12:4) + for (int i = 0; i < 100; ++i) { } + + // Test target teams distribute parallel for simd directive + // expected-error@+1 {{only two expressions allowed in 'num_teams' clause}} + #pragma omp target teams distribute parallel for simd num_teams(a, b, c) + for (int i = 0; i < 100; ++i) { } + // expected-error@+1 {{lower bound is greater than upper bound in 'num_teams' clause}} + #pragma omp target teams distribute parallel for simd num_teams(20:6) + for (int i = 0; i < 100; ++i) { } + + // expected-error@+1 {{only two expressions allowed in 'num_teams' clause}} + #pragma omp target teams distribute simd num_teams(a, b, c) + for (int i = 0; i < 100; ++i) { } + // expected-error@+1 {{lower bound is greater than upper bound in 'num_teams' clause}} + #pragma omp target teams distribute simd num_teams(9:2) + for (int i = 0; i < 100; ++i) { } +} + +// Test non-matching parentheses and brackets +void test_non_matching_delimiters() { + int arr[10]; + int x = 5; + + // expected-error@+4 {{expected ')'}} + // expected-error@+3 {{expected ')'}} + // expected-note@+2 {{to match this '('}} + // expected-note@+1 {{to match this '('}} + #pragma omp teams num_teams((x + 1:10) + { } + + // expected-error@+2 {{expected ']'}} + // expected-note@+1 {{to match this '['}} + #pragma omp teams num_teams(arr[0:10) + { } + + // expected-error@+2 {{expected ')'}} + // expected-note@+1 {{to match this '('}} + #pragma omp teams num_teams(x:((10 + 1)) + { } +} + +// Test multi-level non-matching parentheses and brackets +void test_multi_level_non_matching_delimiters() { + int arr[10][10]; + int x = 5, y = 10; + + // expected-error@+4 {{expected ')'}} + // expected-note@+3 {{to match this '('}} + // expected-error@+2 {{expected ')'}} + // expected-note@+1 {{to match this '('}} + #pragma omp teams num_teams(((x + 1) * 2:10) + { } + + // expected-error@+4 {{expected ')'}} + // expected-note@+3 {{to match this '('}} + // expected-error@+2 {{expected ')'}} + // expected-note@+1 {{to match this '('}} + #pragma omp teams num_teams((x + (y - 1):10) + { } + + // expected-error@+2 {{expected ']'}} + // expected-note@+1 {{to match this '['}} + #pragma omp teams num_teams((arr[0 + 1):10) + { } + + // expected-error@+2 {{expected ')'}} + // expected-note@+1 {{to match this '('}} + #pragma omp teams num_teams(x:((y + 1) * 2) + { } + + // expected-error@+4 {{expected ']'}} + // expected-note@+3 {{to match this '['}} + // expected-error@+2 {{expected ']'}} + // expected-note@+1 {{to match this '['}} + #pragma omp teams num_teams(arr[0][1:arr[2][3) + { } +} + +template +void test_template_type_constants() { + // expected-error@+1 {{lower bound is greater than upper bound in 'num_teams' clause}} + #pragma omp teams num_teams(Lower:Upper) + {} +} + +void instantiate_template_invalid() { + test_template_type_constants<10, 5>(); // expected-note {{in instantiation of function template specialization 'test_template_type_constants<10, 5>' requested here}} +} +