diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -10387,6 +10387,9 @@ "expected %0 in OpenMP clause '%1'">; def err_omp_expected_var_name_member_expr : Error< "expected variable name%select{| or data member of current class}0">; +def err_omp_expected_var_name_member_expr_with_type : Error< + "expected variable%select{| or static data member|, static data member, " + "or non-static data member of current class}0 of type '%1'">; def err_omp_expected_var_name_member_expr_or_array_item : Error< "expected variable name%select{|, data member of current class}0, array element or array section">; def err_omp_expected_addressable_lvalue_or_array_item : Error< diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp --- a/clang/lib/Sema/SemaOpenMP.cpp +++ b/clang/lib/Sema/SemaOpenMP.cpp @@ -2718,7 +2718,8 @@ static std::pair getPrivateItem(Sema &S, Expr *&RefExpr, SourceLocation &ELoc, - SourceRange &ERange, bool AllowArraySection = false); + SourceRange &ERange, bool AllowArraySection = false, + StringRef DiagType = ""); /// Check consistency of the reduction clauses. static void checkReductionClauses(Sema &S, DSAStackTy *Stack, @@ -5279,7 +5280,8 @@ static std::pair getPrivateItem(Sema &S, Expr *&RefExpr, SourceLocation &ELoc, SourceRange &ERange, - bool AllowArraySection) { + bool AllowArraySection, + StringRef DiagType) { if (RefExpr->isTypeDependent() || RefExpr->isValueDependent() || RefExpr->containsUnexpandedParameterPack()) return std::make_pair(nullptr, true); @@ -5324,6 +5326,12 @@ if (IsArrayExpr != NoArrayExpr) { S.Diag(ELoc, diag::err_omp_expected_base_var_name) << IsArrayExpr << ERange; + } else if (!DiagType.empty()) { + unsigned DiagSelect = S.getLangOpts().CPlusPlus + ? (S.getCurrentThisType().isNull() ? 1 : 2) + : 0; + S.Diag(ELoc, diag::err_omp_expected_var_name_member_expr_with_type) + << DiagSelect << DiagType << ERange; } else { S.Diag(ELoc, AllowArraySection @@ -17249,32 +17257,28 @@ // OpenMP 5.1 [2.15.1, interop Construct, Restrictions] // Each interop-var may be specified for at most one action-clause of each // interop construct. - llvm::SmallPtrSet InteropVars; - for (const OMPClause *C : Clauses) { + llvm::SmallPtrSet InteropVars; + for (OMPClause *C : Clauses) { OpenMPClauseKind ClauseKind = C->getClauseKind(); - const DeclRefExpr *DRE = nullptr; - SourceLocation VarLoc; + std::pair DeclResult; + SourceLocation ELoc; + SourceRange ERange; if (ClauseKind == OMPC_init) { - const auto *IC = cast(C); - VarLoc = IC->getVarLoc(); - DRE = dyn_cast_or_null(IC->getInteropVar()); + auto *E = cast(C)->getInteropVar(); + DeclResult = getPrivateItem(*this, E, ELoc, ERange); } else if (ClauseKind == OMPC_use) { - const auto *UC = cast(C); - VarLoc = UC->getVarLoc(); - DRE = dyn_cast_or_null(UC->getInteropVar()); + auto *E = cast(C)->getInteropVar(); + DeclResult = getPrivateItem(*this, E, ELoc, ERange); } else if (ClauseKind == OMPC_destroy) { - const auto *DC = cast(C); - VarLoc = DC->getVarLoc(); - DRE = dyn_cast_or_null(DC->getInteropVar()); + auto *E = cast(C)->getInteropVar(); + DeclResult = getPrivateItem(*this, E, ELoc, ERange); } - if (!DRE) - continue; - - if (const auto *VD = dyn_cast(DRE->getDecl())) { - if (!InteropVars.insert(VD->getCanonicalDecl()).second) { - Diag(VarLoc, diag::err_omp_interop_var_multiple_actions) << VD; + if (DeclResult.first) { + if (!InteropVars.insert(DeclResult.first).second) { + Diag(ELoc, diag::err_omp_interop_var_multiple_actions) + << DeclResult.first; return StmtError(); } } @@ -17286,16 +17290,20 @@ static bool isValidInteropVariable(Sema &SemaRef, Expr *InteropVarExpr, SourceLocation VarLoc, OpenMPClauseKind Kind) { - if (InteropVarExpr->isValueDependent() || InteropVarExpr->isTypeDependent() || - InteropVarExpr->isInstantiationDependent() || - InteropVarExpr->containsUnexpandedParameterPack()) + SourceLocation ELoc; + SourceRange ERange; + Expr *RefExpr = InteropVarExpr; + auto Res = + getPrivateItem(SemaRef, RefExpr, ELoc, ERange, + /*AllowArraySection=*/false, /*DiagType=*/"omp_interop_t"); + + if (Res.second) { + // It will be analyzed later. return true; + } - const auto *DRE = dyn_cast(InteropVarExpr); - if (!DRE || !isa(DRE->getDecl())) { - SemaRef.Diag(VarLoc, diag::err_omp_interop_variable_expected) << 0; + if (!Res.first) return false; - } // Interop variable should be of type omp_interop_t. bool HasError = false; diff --git a/clang/test/OpenMP/interop_ast_print.cpp b/clang/test/OpenMP/interop_ast_print.cpp --- a/clang/test/OpenMP/interop_ast_print.cpp +++ b/clang/test/OpenMP/interop_ast_print.cpp @@ -23,6 +23,57 @@ typedef void *omp_interop_t; +struct S { + omp_interop_t o1; + omp_interop_t o2; + omp_interop_t o3; + static omp_interop_t so; + void foo(); + S(); + ~S(); +}; +omp_interop_t S::so; + +struct T { + static void static_member_func(); + static omp_interop_t to; +}; +omp_interop_t T::to; + +void T::static_member_func() { + omp_interop_t o1; + //PRINT: #pragma omp interop init(target : o1) + #pragma omp interop init(target:o1) + + //PRINT: #pragma omp interop init(target : to) + #pragma omp interop init(target: to) + + //PRINT: #pragma omp interop init(target : T::to) + #pragma omp interop init(target: T::to) + + //PRINT: #pragma omp interop init(target : S::so) + #pragma omp interop init(target: S::so) +} + + +S::S() { + //PRINT: #pragma omp interop init(target : this->o1) + #pragma omp interop init(target:o1) + //PRINT: #pragma omp interop use(this->o1) init(target : this->o2) + #pragma omp interop use(o1) init(target:o2) + //PRINT: #pragma omp interop use(this->o2) init(target : this->o3) + #pragma omp interop use(o2) init(target:o3) +} +S::~S() { + //PRINT: #pragma omp interop destroy(this->o1) destroy(this->o2) destroy(this->o3) + #pragma omp interop destroy(o1) destroy(o2) destroy(o3) +} + +void S::foo() { + //PRINT: #pragma omp interop init(target : so) + #pragma omp interop init(target:so) +} + //PRINT-LABEL: void foo1( //DUMP-LABEL: FunctionDecl {{.*}} foo1 void foo1(int *ap, int dev) { @@ -196,6 +247,9 @@ //DUMP: OMPUseClause //DUMP: DeclRefExpr{{.*}}'omp_interop_t'{{.*}}Var{{.*}}'J' #pragma omp interop destroy(I) use(J) + + //PRINT: #pragma omp interop init(target : S::so) + #pragma omp interop init(target: S::so) } //DUMP: FunctionTemplateDecl{{.*}}fooTemp @@ -274,6 +328,7 @@ fooTemp<3>(); omp_interop_t Ivar; barTemp(Ivar); + S s; } #endif // HEADER diff --git a/clang/test/OpenMP/interop_irbuilder.cpp b/clang/test/OpenMP/interop_irbuilder.cpp --- a/clang/test/OpenMP/interop_irbuilder.cpp +++ b/clang/test/OpenMP/interop_irbuilder.cpp @@ -29,6 +29,34 @@ : D0, D1) } +struct S { + omp_interop_t interop; + void member_test(); +}; + +void S::member_test() { + + int device_id = 4; + int D0, D1; + +#pragma omp interop init(target \ + : interop) + +#pragma omp interop init(targetsync \ + : interop) + +#pragma omp interop init(target \ + : interop) device(device_id) + +#pragma omp interop init(targetsync \ + : interop) device(device_id) + +#pragma omp interop use(interop) depend(in \ + : D0, D1) nowait + +#pragma omp interop destroy(interop) depend(in \ + : D0, D1) +} // CHECK-LABEL: @_Z5test1v( // CHECK-NEXT: entry: // CHECK-NEXT: [[DEVICE_ID:%.*]] = alloca i32, align 4 @@ -94,3 +122,77 @@ // CHECK-NEXT: call void @__tgt_interop_destroy(%struct.ident_t* @[[GLOB1]], i32 [[OMP_GLOBAL_THREAD_NUM7]], i8** [[INTEROP]], i32 -1, i32 2, i8* [[TMP25]], i32 0) // CHECK-NEXT: ret void // +// +// CHECK-LABEL: @_ZN1S11member_testEv( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[THIS_ADDR:%.*]] = alloca %struct.S*, align 8 +// CHECK-NEXT: [[DEVICE_ID:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[D0:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[D1:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTDEP_ARR_ADDR:%.*]] = alloca [2 x %struct.kmp_depend_info], align 8 +// CHECK-NEXT: [[DEP_COUNTER_ADDR:%.*]] = alloca i64, align 8 +// CHECK-NEXT: [[DOTDEP_ARR_ADDR10:%.*]] = alloca [2 x %struct.kmp_depend_info], align 8 +// CHECK-NEXT: [[DEP_COUNTER_ADDR11:%.*]] = alloca i64, align 8 +// CHECK-NEXT: store %struct.S* [[THIS:%.*]], %struct.S** [[THIS_ADDR]], align 8 +// CHECK-NEXT: [[THIS1:%.*]] = load %struct.S*, %struct.S** [[THIS_ADDR]], align 8 +// CHECK-NEXT: store i32 4, i32* [[DEVICE_ID]], align 4 +// CHECK-NEXT: [[INTEROP:%.*]] = getelementptr inbounds [[STRUCT_S:%.*]], %struct.S* [[THIS1]], i32 0, i32 0 +// CHECK-NEXT: [[OMP_GLOBAL_THREAD_NUM:%.*]] = call i32 @__kmpc_global_thread_num(%struct.ident_t* @[[GLOB1]]) +// CHECK-NEXT: call void @__tgt_interop_init(%struct.ident_t* @[[GLOB1]], i32 [[OMP_GLOBAL_THREAD_NUM]], i8** [[INTEROP]], i64 1, i32 -1, i32 0, i8* null, i32 0) +// CHECK-NEXT: [[INTEROP2:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[THIS1]], i32 0, i32 0 +// CHECK-NEXT: [[OMP_GLOBAL_THREAD_NUM3:%.*]] = call i32 @__kmpc_global_thread_num(%struct.ident_t* @[[GLOB1]]) +// CHECK-NEXT: call void @__tgt_interop_init(%struct.ident_t* @[[GLOB1]], i32 [[OMP_GLOBAL_THREAD_NUM3]], i8** [[INTEROP2]], i64 2, i32 -1, i32 0, i8* null, i32 0) +// CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* [[DEVICE_ID]], align 4 +// CHECK-NEXT: [[INTEROP4:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[THIS1]], i32 0, i32 0 +// CHECK-NEXT: [[OMP_GLOBAL_THREAD_NUM5:%.*]] = call i32 @__kmpc_global_thread_num(%struct.ident_t* @[[GLOB1]]) +// CHECK-NEXT: call void @__tgt_interop_init(%struct.ident_t* @[[GLOB1]], i32 [[OMP_GLOBAL_THREAD_NUM5]], i8** [[INTEROP4]], i64 1, i32 [[TMP0]], i32 0, i8* null, i32 0) +// CHECK-NEXT: [[TMP1:%.*]] = load i32, i32* [[DEVICE_ID]], align 4 +// CHECK-NEXT: [[INTEROP6:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[THIS1]], i32 0, i32 0 +// CHECK-NEXT: [[OMP_GLOBAL_THREAD_NUM7:%.*]] = call i32 @__kmpc_global_thread_num(%struct.ident_t* @[[GLOB1]]) +// CHECK-NEXT: call void @__tgt_interop_init(%struct.ident_t* @[[GLOB1]], i32 [[OMP_GLOBAL_THREAD_NUM7]], i8** [[INTEROP6]], i64 2, i32 [[TMP1]], i32 0, i8* null, i32 0) +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds [2 x %struct.kmp_depend_info], [2 x %struct.kmp_depend_info]* [[DOTDEP_ARR_ADDR]], i64 0, i64 0 +// CHECK-NEXT: [[TMP3:%.*]] = ptrtoint i32* [[D0]] to i64 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr [[STRUCT_KMP_DEPEND_INFO:%.*]], %struct.kmp_depend_info* [[TMP2]], i64 0 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds [[STRUCT_KMP_DEPEND_INFO]], %struct.kmp_depend_info* [[TMP4]], i32 0, i32 0 +// CHECK-NEXT: store i64 [[TMP3]], i64* [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds [[STRUCT_KMP_DEPEND_INFO]], %struct.kmp_depend_info* [[TMP4]], i32 0, i32 1 +// CHECK-NEXT: store i64 4, i64* [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds [[STRUCT_KMP_DEPEND_INFO]], %struct.kmp_depend_info* [[TMP4]], i32 0, i32 2 +// CHECK-NEXT: store i8 1, i8* [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = ptrtoint i32* [[D1]] to i64 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr [[STRUCT_KMP_DEPEND_INFO]], %struct.kmp_depend_info* [[TMP2]], i64 1 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds [[STRUCT_KMP_DEPEND_INFO]], %struct.kmp_depend_info* [[TMP9]], i32 0, i32 0 +// CHECK-NEXT: store i64 [[TMP8]], i64* [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds [[STRUCT_KMP_DEPEND_INFO]], %struct.kmp_depend_info* [[TMP9]], i32 0, i32 1 +// CHECK-NEXT: store i64 4, i64* [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds [[STRUCT_KMP_DEPEND_INFO]], %struct.kmp_depend_info* [[TMP9]], i32 0, i32 2 +// CHECK-NEXT: store i8 1, i8* [[TMP12]], align 8 +// CHECK-NEXT: store i64 2, i64* [[DEP_COUNTER_ADDR]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = bitcast %struct.kmp_depend_info* [[TMP2]] to i8* +// CHECK-NEXT: [[INTEROP8:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[THIS1]], i32 0, i32 0 +// CHECK-NEXT: [[OMP_GLOBAL_THREAD_NUM9:%.*]] = call i32 @__kmpc_global_thread_num(%struct.ident_t* @[[GLOB1]]) +// CHECK-NEXT: call void @__tgt_interop_use(%struct.ident_t* @[[GLOB1]], i32 [[OMP_GLOBAL_THREAD_NUM9]], i8** [[INTEROP8]], i32 -1, i32 2, i8* [[TMP13]], i32 1) +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds [2 x %struct.kmp_depend_info], [2 x %struct.kmp_depend_info]* [[DOTDEP_ARR_ADDR10]], i64 0, i64 0 +// CHECK-NEXT: [[TMP15:%.*]] = ptrtoint i32* [[D0]] to i64 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr [[STRUCT_KMP_DEPEND_INFO]], %struct.kmp_depend_info* [[TMP14]], i64 0 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds [[STRUCT_KMP_DEPEND_INFO]], %struct.kmp_depend_info* [[TMP16]], i32 0, i32 0 +// CHECK-NEXT: store i64 [[TMP15]], i64* [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds [[STRUCT_KMP_DEPEND_INFO]], %struct.kmp_depend_info* [[TMP16]], i32 0, i32 1 +// CHECK-NEXT: store i64 4, i64* [[TMP18]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds [[STRUCT_KMP_DEPEND_INFO]], %struct.kmp_depend_info* [[TMP16]], i32 0, i32 2 +// CHECK-NEXT: store i8 1, i8* [[TMP19]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = ptrtoint i32* [[D1]] to i64 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr [[STRUCT_KMP_DEPEND_INFO]], %struct.kmp_depend_info* [[TMP14]], i64 1 +// CHECK-NEXT: [[TMP22:%.*]] = getelementptr inbounds [[STRUCT_KMP_DEPEND_INFO]], %struct.kmp_depend_info* [[TMP21]], i32 0, i32 0 +// CHECK-NEXT: store i64 [[TMP20]], i64* [[TMP22]], align 8 +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds [[STRUCT_KMP_DEPEND_INFO]], %struct.kmp_depend_info* [[TMP21]], i32 0, i32 1 +// CHECK-NEXT: store i64 4, i64* [[TMP23]], align 8 +// CHECK-NEXT: [[TMP24:%.*]] = getelementptr inbounds [[STRUCT_KMP_DEPEND_INFO]], %struct.kmp_depend_info* [[TMP21]], i32 0, i32 2 +// CHECK-NEXT: store i8 1, i8* [[TMP24]], align 8 +// CHECK-NEXT: store i64 2, i64* [[DEP_COUNTER_ADDR11]], align 8 +// CHECK-NEXT: [[TMP25:%.*]] = bitcast %struct.kmp_depend_info* [[TMP14]] to i8* +// CHECK-NEXT: [[INTEROP12:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[THIS1]], i32 0, i32 0 +// CHECK-NEXT: [[OMP_GLOBAL_THREAD_NUM13:%.*]] = call i32 @__kmpc_global_thread_num(%struct.ident_t* @[[GLOB1]]) +// CHECK-NEXT: call void @__tgt_interop_destroy(%struct.ident_t* @[[GLOB1]], i32 [[OMP_GLOBAL_THREAD_NUM13]], i8** [[INTEROP12]], i32 -1, i32 2, i8* [[TMP25]], i32 0) +// CHECK-NEXT: ret void +// diff --git a/clang/test/OpenMP/interop_messages.cpp b/clang/test/OpenMP/interop_messages.cpp --- a/clang/test/OpenMP/interop_messages.cpp +++ b/clang/test/OpenMP/interop_messages.cpp @@ -1,5 +1,6 @@ // RUN: %clang_cc1 -verify -fopenmp -std=c++11 -o - -DWITHDEF %s // RUN: %clang_cc1 -verify -fopenmp -std=c++11 -o - -DWITHOUTDEF %s +// RUN: %clang_cc1 -verify -fopenmp -std=c99 -x c -o - -DCTEST %s #ifdef WITHDEF typedef void *omp_interop_t; @@ -55,13 +56,13 @@ #pragma omp interop destroy(SVar) destroy(Another) int a, b; - //expected-error@+1 {{expected variable of type 'omp_interop_t'}} + //expected-error@+1 {{expected variable or static data member of type 'omp_interop_t'}} #pragma omp interop init(target:a+b) init(target:Another) - //expected-error@+1 {{expected variable of type 'omp_interop_t'}} + //expected-error@+1 {{expected variable or static data member of type 'omp_interop_t'}} #pragma omp interop use(a+b) use(Another) - //expected-error@+1 {{expected variable of type 'omp_interop_t'}} + //expected-error@+1 {{expected variable or static data member of type 'omp_interop_t'}} #pragma omp interop destroy(a+b) destroy(Another) const omp_interop_t C = (omp_interop_t)5; @@ -115,6 +116,61 @@ //expected-error@+1 {{directive '#pragma omp interop' cannot contain more than one 'nowait' clause}} #pragma omp interop nowait init(target:InteropVar) nowait } + +struct S { + void foo(); + omp_interop_t InteropVar; + omp_interop_t func(); + static omp_interop_t sfunc(); +}; + +struct T { + static void static_member_func(); +}; + +void T::static_member_func() { + S s; + omp_interop_t o; + + //expected-error@+1 {{expected variable or static data member of type 'omp_interop_t'}} + #pragma omp interop init(target:s.InteropVar) init(target:o) +} + +void S::foo() { + //expected-error@+1 {{interop variable 'InteropVar' used in multiple action clauses}} + #pragma omp interop init(target:InteropVar) init(target:InteropVar) + + //expected-error@+1 {{interop variable 'InteropVar' used in multiple action clauses}} + #pragma omp interop use(InteropVar) use(InteropVar) + + //expected-error@+1 {{interop variable 'InteropVar' used in multiple action clauses}} + #pragma omp interop destroy(InteropVar) destroy(InteropVar) + + //expected-error@+1 {{interop variable 'InteropVar' used in multiple action clauses}} + #pragma omp interop init(target:InteropVar) use(InteropVar) + + //expected-error@+1 {{interop variable 'InteropVar' used in multiple action clauses}} + #pragma omp interop init(target:InteropVar) destroy(InteropVar) + + //expected-error@+1 {{interop variable 'InteropVar' used in multiple action clauses}} + #pragma omp interop use(InteropVar) destroy(InteropVar) + + //expected-error@+1 {{expected variable, static data member, or non-static data member of current class of type 'omp_interop_t'}} + #pragma omp interop init(target:InteropVar) init(target:func()) + + //expected-error@+1 {{expected variable, static data member, or non-static data member of current class of type 'omp_interop_t'}} + #pragma omp interop init(target:InteropVar) init(target:sfunc()) +} + +void foo2() { + S s; + omp_interop_t another; + //expected-error@+1 {{expected variable or static data member of type 'omp_interop_t'}} + #pragma omp interop init(target:s.InteropVar) init(target:another) + + //expected-error@+1 {{expected variable or static data member of type 'omp_interop_t'}} + #pragma omp interop init(target: S::sfunc()) init(target:another) +} #endif #ifdef WITHOUTDEF void foo() { @@ -127,3 +183,20 @@ #pragma omp interop destroy(InteropVar) nowait } #endif +#ifdef CTEST +typedef void *omp_interop_t; +omp_interop_t bar(); +struct S { + omp_interop_t o; +}; +void foo() { + omp_interop_t o; + struct S s; + + //expected-error@+1 {{expected variable of type 'omp_interop_t'}} + #pragma omp interop init(target:o) init(target:bar()) + + //expected-error@+1 {{expected variable of type 'omp_interop_t'}} + #pragma omp interop init(target:o) init(target:s.o) +} +#endif