diff options
Diffstat (limited to 'clang/lib/Sema')
-rw-r--r-- | clang/lib/Sema/SemaCUDA.cpp | 35 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDecl.cpp | 32 |
2 files changed, 67 insertions, 0 deletions
diff --git a/clang/lib/Sema/SemaCUDA.cpp b/clang/lib/Sema/SemaCUDA.cpp index 568c765984f..84fccd5ef59 100644 --- a/clang/lib/Sema/SemaCUDA.cpp +++ b/clang/lib/Sema/SemaCUDA.cpp @@ -14,6 +14,7 @@ #include "clang/Sema/Sema.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" +#include "clang/AST/ExprCXX.h" #include "clang/Lex/Preprocessor.h" #include "clang/Sema/SemaDiagnostic.h" #include "llvm/ADT/Optional.h" @@ -419,3 +420,37 @@ bool Sema::inferCUDATargetForImplicitSpecialMember(CXXRecordDecl *ClassDecl, return false; } + +bool Sema::isEmptyCudaConstructor(SourceLocation Loc, CXXConstructorDecl *CD) { + if (!CD->isDefined() && CD->isTemplateInstantiation()) + InstantiateFunctionDefinition(Loc, CD->getFirstDecl()); + + // (E.2.3.1, CUDA 7.5) A constructor for a class type is considered + // empty at a point in the translation unit, if it is either a + // trivial constructor + if (CD->isTrivial()) + return true; + + // ... or it satisfies all of the following conditions: + // The constructor function has been defined. + // The constructor function has no parameters, + // and the function body is an empty compound statement. + if (!(CD->hasTrivialBody() && CD->getNumParams() == 0)) + return false; + + // Its class has no virtual functions and no virtual base classes. + if (CD->getParent()->isDynamicClass()) + return false; + + // The only form of initializer allowed is an empty constructor. + // This will recursively checks all base classes and member initializers + if (!llvm::all_of(CD->inits(), [&](const CXXCtorInitializer *CI) { + if (const CXXConstructExpr *CE = + dyn_cast<CXXConstructExpr>(CI->getInit())) + return isEmptyCudaConstructor(Loc, CE->getConstructor()); + return false; + })) + return false; + + return true; +} diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 5db2c374aed..ffd71e38c6c 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -10223,6 +10223,38 @@ Sema::FinalizeDeclaration(Decl *ThisDecl) { } } + // Perform check for initializers of device-side global variables. + // CUDA allows empty constructors as initializers (see E.2.3.1, CUDA + // 7.5). CUDA also allows constant initializers for __constant__ and + // __device__ variables. + if (getLangOpts().CUDA && getLangOpts().CUDAIsDevice) { + const Expr *Init = VD->getInit(); + const bool IsGlobal = VD->hasGlobalStorage() && !VD->isStaticLocal(); + if (Init && IsGlobal && + (VD->hasAttr<CUDADeviceAttr>() || VD->hasAttr<CUDAConstantAttr>() || + VD->hasAttr<CUDASharedAttr>())) { + bool AllowedInit = false; + if (const CXXConstructExpr *CE = dyn_cast<CXXConstructExpr>(Init)) + AllowedInit = + isEmptyCudaConstructor(VD->getLocation(), CE->getConstructor()); + // We'll allow constant initializers even if it's a non-empty + // constructor according to CUDA rules. This deviates from NVCC, + // but allows us to handle things like constexpr constructors. + if (!AllowedInit && + (VD->hasAttr<CUDADeviceAttr>() || VD->hasAttr<CUDAConstantAttr>())) + AllowedInit = VD->getInit()->isConstantInitializer( + Context, VD->getType()->isReferenceType()); + + if (!AllowedInit) { + Diag(VD->getLocation(), VD->hasAttr<CUDASharedAttr>() + ? diag::err_shared_var_init + : diag::err_dynamic_var_init) + << Init->getSourceRange(); + VD->setInvalidDecl(); + } + } + } + // Grab the dllimport or dllexport attribute off of the VarDecl. const InheritableAttr *DLLAttr = getDLLAttr(VD); |