diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index c57a802512be8..5897f2c48569b 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -1269,11 +1269,36 @@ static bool CheckLValueConstantExpression(EvalInfo &Info, SourceLocation Loc, LVal.getLValueCallIndex() == 0) && "have call index for global lvalue"); - // Check if this is a thread-local variable. if (const ValueDecl *VD = Base.dyn_cast()) { if (const VarDecl *Var = dyn_cast(VD)) { + // Check if this is a thread-local variable. if (Var->getTLSKind()) return false; + + // Check if this is a dllimport variable. Fail evaluation if we care + // about side effects; a dllimport variable rarely acts like a constant + // except in places like template arguments. It never acts like a + // constant in C. + if ((!Info.getLangOpts().CPlusPlus || + !Info.keepEvaluatingAfterSideEffect()) && + Var->hasAttr()) + return false; + } + if (const auto *FD = dyn_cast(VD)) { + // __declspec(dllimport) must be handled very carefully: + // We must never initialize an expression with the thunk in C++. + // Doing otherwise would allow the same id-expression to yield + // different addresses for the same function in different translation + // units. However, this means that we must dynamically initialize the + // expression with the contents of the import address table at runtime. + // + // The C language has no notion of ODR; furthermore, it has no notion of + // dynamic initialization. This means that we are permitted to + // perform initialization with the address of the thunk. + if (Info.getLangOpts().CPlusPlus && + !Info.keepEvaluatingAfterSideEffect() && + FD->hasAttr()) + return false; } } @@ -4373,11 +4398,8 @@ static bool EvaluateLValue(const Expr *E, LValue &Result, EvalInfo &Info) { } bool LValueExprEvaluator::VisitDeclRefExpr(const DeclRefExpr *E) { - if (const FunctionDecl *FD = dyn_cast(E->getDecl())) { - if (FD->hasAttr()) - return ZeroInitialization(E); + if (const FunctionDecl *FD = dyn_cast(E->getDecl())) return Success(FD); - } if (const VarDecl *VD = dyn_cast(E->getDecl())) return VisitVarDecl(E, VD); return Error(E); @@ -4393,9 +4415,6 @@ bool LValueExprEvaluator::VisitVarDecl(const Expr *E, const VarDecl *VD) { Result.set(VD, Frame->Index); return true; } - // The address of __declspec(dllimport) variables aren't constant. - if (VD->hasAttr()) - return ZeroInitialization(E); return Success(VD); } diff --git a/clang/test/CodeGen/dllimport.c b/clang/test/CodeGen/dllimport.c index 485f6e21b2a69..32ee81f85924f 100644 --- a/clang/test/CodeGen/dllimport.c +++ b/clang/test/CodeGen/dllimport.c @@ -53,7 +53,10 @@ USEVAR(GlobalRedecl3) // Import function declaration. // CHECK-DAG: declare dllimport void @decl() __declspec(dllimport) void decl(void); -USE(decl) + +// Initialize use_decl with the address of the thunk. +// CHECK-DAG: @use_decl = global void ()* @decl +void (*use_decl)(void) = &decl; // Import inline function. // CHECK-DAG: declare dllimport void @inlineFunc() diff --git a/clang/test/Sema/dllimport.c b/clang/test/Sema/dllimport.c index f4d26fb8c8786..2702453b61c08 100644 --- a/clang/test/Sema/dllimport.c +++ b/clang/test/Sema/dllimport.c @@ -25,6 +25,9 @@ __declspec(dllimport) int GlobalDecl; int **__attribute__((dllimport))* GlobalDeclChunkAttr; int GlobalDeclAttr __attribute__((dllimport)); +// Address of variables can't be used for initialization in C language modes. +int *VarForInit = &GlobalDecl; // expected-error{{initializer element is not a compile-time constant}} + // Not allowed on definitions. __declspec(dllimport) extern int ExternGlobalInit = 1; // expected-error{{definition of dllimport data}} __declspec(dllimport) int GlobalInit1 = 1; // expected-error{{definition of dllimport data}} @@ -96,6 +99,11 @@ __declspec(dllimport) void decl1B(); void __attribute__((dllimport)) decl2A(); void __declspec(dllimport) decl2B(); +// Address of functions can be used for initialization in C language modes. +// However, the address of the thunk wrapping the function is used instead of +// the address in the import address table. +void (*FunForInit)() = &decl2A; + // Not allowed on function definitions. __declspec(dllimport) void def() {} // expected-error{{dllimport cannot be applied to non-inline function definition}} diff --git a/clang/test/SemaCXX/PR19955.cpp b/clang/test/SemaCXX/PR19955.cpp index e0d4618f2c4e7..4596e5a459e4b 100644 --- a/clang/test/SemaCXX/PR19955.cpp +++ b/clang/test/SemaCXX/PR19955.cpp @@ -5,3 +5,11 @@ constexpr int *varp = &var; // expected-error {{must be initialized by a constan extern __attribute__((dllimport)) void fun(); constexpr void (*funp)(void) = &fun; // expected-error {{must be initialized by a constant expression}} + +template +struct S {}; +S<&fun> x; + +template +struct U {}; +U<&var> y;