LVal.getLValueCallIndex() == 0) &&
"have call index for global lvalue");
- // Check if this is a thread-local variable.
if (const ValueDecl *VD = Base.dyn_cast<const ValueDecl*>()) {
if (const VarDecl *Var = dyn_cast<const VarDecl>(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<DLLImportAttr>())
+ return false;
+ }
+ if (const auto *FD = dyn_cast<const FunctionDecl>(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<DLLImportAttr>())
+ return false;
}
}
}
bool LValueExprEvaluator::VisitDeclRefExpr(const DeclRefExpr *E) {
- if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(E->getDecl())) {
- if (FD->hasAttr<DLLImportAttr>())
- return ZeroInitialization(E);
+ if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(E->getDecl()))
return Success(FD);
- }
if (const VarDecl *VD = dyn_cast<VarDecl>(E->getDecl()))
return VisitVarDecl(E, VD);
return Error(E);
Result.set(VD, Frame->Index);
return true;
}
- // The address of __declspec(dllimport) variables aren't constant.
- if (VD->hasAttr<DLLImportAttr>())
- return ZeroInitialization(E);
return Success(VD);
}
// 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()
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}}
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}}