summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--clang/lib/AST/Decl.cpp41
-rw-r--r--clang/test/CodeGen/ms-compat-extern-static.c11
2 files changed, 46 insertions, 6 deletions
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index 4e42dd7d3bd..1ca66cfb5f1 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -613,12 +613,41 @@ static LinkageInfo getExternalLinkageFor(const NamedDecl *D) {
static StorageClass getStorageClass(const Decl *D) {
if (auto *TD = dyn_cast<TemplateDecl>(D))
D = TD->getTemplatedDecl();
- if (D) {
- if (auto *VD = dyn_cast<VarDecl>(D))
- return VD->getStorageClass();
- if (auto *FD = dyn_cast<FunctionDecl>(D))
- return FD->getStorageClass();
+ if (!D)
+ return SC_None;
+
+ if (auto *VD = dyn_cast<VarDecl>(D)) {
+ // Generally, the storage class is determined by the first declaration.
+ auto SC = VD->getCanonicalDecl()->getStorageClass();
+
+ // ... except that MSVC permits an 'extern' declaration to be redeclared
+ // 'static' as an extension.
+ if (SC == SC_Extern) {
+ for (auto *Redecl : VD->redecls()) {
+ if (Redecl->getStorageClass() == SC_Static)
+ return SC_Static;
+ if (Redecl->getStorageClass() != SC_Extern &&
+ !Redecl->isLocalExternDecl() && !Redecl->getFriendObjectKind())
+ break;
+ }
+ }
+ return SC;
}
+
+ if (auto *FD = dyn_cast<FunctionDecl>(D)) {
+ auto SC = FD->getCanonicalDecl()->getStorageClass();
+ if (SC == SC_Extern) {
+ for (auto *Redecl : FD->redecls()) {
+ if (Redecl->getStorageClass() == SC_Static)
+ return SC_Static;
+ if (Redecl->getStorageClass() != SC_Extern &&
+ !Redecl->isLocalExternDecl() && !Redecl->getFriendObjectKind())
+ break;
+ }
+ }
+ return SC;
+ }
+
return SC_None;
}
@@ -634,7 +663,7 @@ LinkageComputer::getLVForNamespaceScopeDecl(const NamedDecl *D,
// A name having namespace scope (3.3.6) has internal linkage if it
// is the name of
- if (getStorageClass(D->getCanonicalDecl()) == SC_Static) {
+ if (getStorageClass(D) == SC_Static) {
// - a variable, variable template, function, or function template
// that is explicitly declared static; or
// (This bullet corresponds to C99 6.2.2p3.)
diff --git a/clang/test/CodeGen/ms-compat-extern-static.c b/clang/test/CodeGen/ms-compat-extern-static.c
new file mode 100644
index 00000000000..2ad60ef7bc9
--- /dev/null
+++ b/clang/test/CodeGen/ms-compat-extern-static.c
@@ -0,0 +1,11 @@
+// RUN: %clang_cc1 -emit-llvm %s -o - -fms-extensions -triple x86_64-windows | FileCheck %s
+
+// CHECK: @n = internal global i32 1
+extern int n;
+static int n = 1;
+int *use = &n;
+
+// CHECK: define internal void @f(
+extern void f();
+static void f() {}
+void g() { return f(); }
OpenPOWER on IntegriCloud