[clang] [Clang] Reland "Diagnose UB and emit error when identifier has both internal and external linkage" (PR #193567)
Aditya Medhane via cfe-commits
cfe-commits at lists.llvm.org
Wed Jun 3 00:17:50 PDT 2026
https://github.com/flash1729 updated https://github.com/llvm/llvm-project/pull/193567
>From 210026569ec49c47d38ee0e3e540b1e131c71a52 Mon Sep 17 00:00:00 2001
From: flash1729 <sherlockedaditya at gmail.com>
Date: Wed, 15 Apr 2026 00:42:00 +0530
Subject: [PATCH 1/5] [Clang] Diagnose mixed internal/external linkage in C
(reland)
C2y 6.7.1p7: an identifier shall not appear with both internal and
external linkage within a translation unit. Before C2y this was UB
(C11 6.2.2p7).
In C, a local shadow prevents a block-scope extern from inheriting
the file-scope static's internal linkage (C2y 6.2.2p4), so it
defaults to external linkage, creating the conflict.
This relands the original patch with fixes:
- Corrected C++ test: CWG 426 was superseded by P1787R6; Clang
applies P1787R6 semantics so no linkage conflict exists in C++
- Added [dcl.meaning.general]/3.5 citation for C++ exclusion
- Restored C2y 6.2.2p4 citation
Fixes #54215
---
clang/docs/ReleaseNotes.rst | 6 ++++
.../clang/Basic/DiagnosticSemaKinds.td | 3 ++
clang/lib/Sema/SemaDecl.cpp | 21 +++++++++++
clang/test/C/C2y/n3410.c | 10 +++---
clang/test/Sema/linkage-internal-extern.c | 36 +++++++++++++++++++
clang/test/Sema/linkage-internal-extern.cpp | 22 ++++++++++++
clang/www/c_status.html | 2 +-
7 files changed, 93 insertions(+), 7 deletions(-)
create mode 100644 clang/test/Sema/linkage-internal-extern.c
create mode 100644 clang/test/Sema/linkage-internal-extern.cpp
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 843a01f57c39f..060d68ef7f4d0 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -187,6 +187,12 @@ C2y Feature Support
``stdc_rotate_left_{uc,us,ui,ul,ull}`` and
``stdc_rotate_right_{uc,us,ui,ul,ull}``.
+- Clang now diagnoses the use of the same identifier with both internal and
+ external linkage within a translation unit, as made ill-formed by
+ `N3410 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3410.pdf>`_.
+ This is also diagnosed in older C language modes as the behavior was
+ undefined prior to C2y. (#GH54215)
+
C23 Feature Support
^^^^^^^^^^^^^^^^^^^
- Clang now allows C23 ``constexpr`` struct member access through the dot operator in constant expressions. (#GH178349)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 9cd3a5b2fc049..2a942ae204dd8 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -6535,6 +6535,9 @@ def err_inline_declaration_block_scope : Error<
"inline declaration of %0 not allowed in block scope">;
def err_static_non_static : Error<
"static declaration of %0 follows non-static declaration">;
+def err_internal_extern_mismatch : Error<
+ "variable %0 declared with both internal and external linkage "
+ "in the same translation unit%select{; behavior is undefined|}1">;
def err_different_language_linkage : Error<
"declaration of %0 has a different language linkage">;
def ext_retained_language_linkage : Extension<
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 62cb9360d1322..1c29ec1a2bbd7 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -4822,6 +4822,27 @@ void Sema::MergeVarDecl(VarDecl *New, LookupResult &Previous) {
return New->setInvalidDecl();
}
}
+
+ // C2y 6.7.1p7: an identifier shall not appear with both internal and
+ // external linkage within a translation unit. Before C2y this was UB
+ // (C11 6.2.2p7).
+ //
+ // In C, a local shadow prevents a block-scope extern from inheriting the
+ // file-scope static's internal linkage (C2y 6.2.2p4), so it defaults to
+ // external linkage, creating the conflict.
+ //
+ // In C++, block-scope extern declarations target the enclosing namespace
+ // scope ([dcl.meaning.general]/3.5), bypassing local shadows entirely, so
+ // the extern always inherits internal linkage. No conflict arises.
+ if (!getLangOpts().CPlusPlus && New->isLocalVarDecl() &&
+ New->hasExternalStorage() && Old->isFileVarDecl() && Old->hasLinkage() &&
+ Previous.isShadowed() && Old->getFormalLinkage() == Linkage::Internal) {
+ Diag(New->getLocation(), diag::err_internal_extern_mismatch)
+ << New->getDeclName() << getLangOpts().C2y;
+ Diag(OldLocation, PrevDiag);
+ return New->setInvalidDecl();
+ }
+
// C99 6.2.2p4:
// For an identifier declared with the storage-class specifier
// extern in a scope in which a prior declaration of that
diff --git a/clang/test/C/C2y/n3410.c b/clang/test/C/C2y/n3410.c
index e1cb41f375b82..7ce3c2916d300 100644
--- a/clang/test/C/C2y/n3410.c
+++ b/clang/test/C/C2y/n3410.c
@@ -1,6 +1,6 @@
// RUN: %clang_cc1 -verify -std=c2y -Wall -pedantic -Wno-unused %s
-/* WG14 N3410: No
+/* WG14 N3410: Clang 23
* Slay Some Earthly Demons XI
*
* It is now ill-formed for the same identifier within a TU to have both
@@ -24,19 +24,17 @@ void func2() {
extern int b; // Ok
}
-static int c, d;
+static int c, d; // expected-note 2 {{previous definition is here}}
void func3() {
int c; // no linkage, different object from the one declared above.
for (int d;;) {
// This 'c' is the same as the one declared at file scope, but because of
// the local scope 'c', the file scope 'c' is not visible.
- // FIXME: This should be diagnosed under N3410.
- extern int c;
+ extern int c; // expected-error {{declared with both internal and external linkage}}
// This 'd' is the same as the one declared at file scope as well, but
// because of the 'd' declared within the for loop, the file scope 'd' is
// also not visible, same as with 'c'.
- // FIXME: This should be diagnosed under N3410.
- extern int d;
+ extern int d; // expected-error {{declared with both internal and external linkage}}
}
for (static int e;;) {
extern int e; // Ok for the same reason as 'b' above.
diff --git a/clang/test/Sema/linkage-internal-extern.c b/clang/test/Sema/linkage-internal-extern.c
new file mode 100644
index 0000000000000..42591c5b21f0b
--- /dev/null
+++ b/clang/test/Sema/linkage-internal-extern.c
@@ -0,0 +1,36 @@
+// RUN: %clang_cc1 -verify -fsyntax-only %s
+
+// C11 6.2.2p7: same identifier with both internal and external linkage is UB.
+
+// Conflicting linkage (UB)
+
+static int x; // expected-note {{previous}}
+void test_basic_shadow(void) {
+ int x;
+ { extern int x; } // expected-error {{declared with both internal and external linkage}}
+}
+
+static int y; // expected-note {{previous}}
+void test_deep_nesting(void) {
+ int y;
+ { int y; { { extern int y; } } } // expected-error {{declared with both internal and external linkage}}
+}
+
+static int p; // expected-note {{previous}}
+void test_param_shadow(int p) {
+ { extern int p; } // expected-error {{declared with both internal and external linkage}}
+}
+
+// Valid cases
+
+static int a;
+void test_no_shadow(void) {
+ extern int a;
+}
+
+void test_no_file_scope(void) {
+ for (static int b = 0;;) {
+ extern int b;
+ break;
+ }
+}
diff --git a/clang/test/Sema/linkage-internal-extern.cpp b/clang/test/Sema/linkage-internal-extern.cpp
new file mode 100644
index 0000000000000..242f38978b4c5
--- /dev/null
+++ b/clang/test/Sema/linkage-internal-extern.cpp
@@ -0,0 +1,22 @@
+// RUN: %clang_cc1 -verify -fsyntax-only -std=c++17 %s
+
+// expected-no-diagnostics
+
+// In C++, block-scope extern declarations target the enclosing namespace
+// scope ([dcl.meaning.general]/3.5), so they find the file-scope static
+// despite local shadows and inherit internal linkage. No conflict arises.
+//
+// This differs from C, where a local shadow breaks linkage inheritance,
+// causing the conflict diagnosed by err_internal_extern_mismatch.
+
+// Example adapted from [basic.link]/6.
+static void f();
+static int i = 0;
+void g() {
+ extern void f(); // internal linkage
+ int i; // no linkage
+ {
+ extern void f(); // internal linkage
+ extern int i; // internal linkage
+ }
+}
diff --git a/clang/www/c_status.html b/clang/www/c_status.html
index 5270033471167..4b3695831de58 100644
--- a/clang/www/c_status.html
+++ b/clang/www/c_status.html
@@ -283,7 +283,7 @@ <h2 id="c2y">C2y implementation status</h2>
<tr>
<td>Slay Some Earthly Demons XI</td>
<td><a href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3410.pdf">N3410</a></td>
- <td class="none" align="center">No</td>
+ <td class="unreleased" align="center">Clang 23</td>
</tr>
<tr>
<td>Slay Some Earthly Demons XII</td>
>From 5b852b777b3030ae30a02ad68a14fd8d058b0fae Mon Sep 17 00:00:00 2001
From: flash1729 <sherlockedaditya at gmail.com>
Date: Sat, 25 Apr 2026 23:16:13 +0530
Subject: [PATCH 2/5] fixup! [Clang] Diagnose mixed internal/external linkage
in C (reland)
---
clang/lib/Sema/SemaDecl.cpp | 4 ++--
clang/test/Sema/linkage-internal-extern.c | 12 +++++++++++-
clang/test/Sema/linkage-internal-extern.cpp | 4 ++--
3 files changed, 15 insertions(+), 5 deletions(-)
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 1c29ec1a2bbd7..c80faf7c5b902 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -4835,8 +4835,8 @@ void Sema::MergeVarDecl(VarDecl *New, LookupResult &Previous) {
// scope ([dcl.meaning.general]/3.5), bypassing local shadows entirely, so
// the extern always inherits internal linkage. No conflict arises.
if (!getLangOpts().CPlusPlus && New->isLocalVarDecl() &&
- New->hasExternalStorage() && Old->isFileVarDecl() && Old->hasLinkage() &&
- Previous.isShadowed() && Old->getFormalLinkage() == Linkage::Internal) {
+ New->hasExternalStorage() && Old->hasLinkage() && Previous.isShadowed() &&
+ Old->getFormalLinkage() == Linkage::Internal) {
Diag(New->getLocation(), diag::err_internal_extern_mismatch)
<< New->getDeclName() << getLangOpts().C2y;
Diag(OldLocation, PrevDiag);
diff --git a/clang/test/Sema/linkage-internal-extern.c b/clang/test/Sema/linkage-internal-extern.c
index 42591c5b21f0b..06f30e60261e2 100644
--- a/clang/test/Sema/linkage-internal-extern.c
+++ b/clang/test/Sema/linkage-internal-extern.c
@@ -21,6 +21,16 @@ void test_param_shadow(int p) {
{ extern int p; } // expected-error {{declared with both internal and external linkage}}
}
+static int z;
+void test_chained_shadow(void) {
+ extern int z; // expected-note {{previous}} #2: internal linkage
+ {
+ int z; // shadow: no linkage
+ { extern int z; } // expected-error {{declared with both internal and external linkage}}
+ }
+}
+
+
// Valid cases
static int a;
@@ -29,7 +39,7 @@ void test_no_shadow(void) {
}
void test_no_file_scope(void) {
- for (static int b = 0;;) {
+ for (int b = 0;;) {
extern int b;
break;
}
diff --git a/clang/test/Sema/linkage-internal-extern.cpp b/clang/test/Sema/linkage-internal-extern.cpp
index 242f38978b4c5..f20822a5635ae 100644
--- a/clang/test/Sema/linkage-internal-extern.cpp
+++ b/clang/test/Sema/linkage-internal-extern.cpp
@@ -3,8 +3,8 @@
// expected-no-diagnostics
// In C++, block-scope extern declarations target the enclosing namespace
-// scope ([dcl.meaning.general]/3.5), so they find the file-scope static
-// despite local shadows and inherit internal linkage. No conflict arises.
+// scope ([dcl.meaning.general]/3.5), so they match against the namespace-scope
+// static despite local shadows and inherit internal linkage. No conflict arises.
//
// This differs from C, where a local shadow breaks linkage inheritance,
// causing the conflict diagnosed by err_internal_extern_mismatch.
>From e4ae3215da4ad5b23a9f83528ee6710b577268c6 Mon Sep 17 00:00:00 2001
From: flash1729 <sherlockedaditya at gmail.com>
Date: Tue, 26 May 2026 16:58:29 +0530
Subject: [PATCH 3/5] fixup! [Clang] Diagnose mixed internal/external linkage
in C (reland)
---
clang/lib/Sema/SemaDecl.cpp | 2 +-
clang/test/Sema/linkage-internal-extern.cpp | 33 ++++++++++++++++-----
2 files changed, 27 insertions(+), 8 deletions(-)
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index c80faf7c5b902..87f410927829e 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -4835,7 +4835,7 @@ void Sema::MergeVarDecl(VarDecl *New, LookupResult &Previous) {
// scope ([dcl.meaning.general]/3.5), bypassing local shadows entirely, so
// the extern always inherits internal linkage. No conflict arises.
if (!getLangOpts().CPlusPlus && New->isLocalVarDecl() &&
- New->hasExternalStorage() && Old->hasLinkage() && Previous.isShadowed() &&
+ New->hasExternalStorage() && Previous.isShadowed() &&
Old->getFormalLinkage() == Linkage::Internal) {
Diag(New->getLocation(), diag::err_internal_extern_mismatch)
<< New->getDeclName() << getLangOpts().C2y;
diff --git a/clang/test/Sema/linkage-internal-extern.cpp b/clang/test/Sema/linkage-internal-extern.cpp
index f20822a5635ae..27b9566620289 100644
--- a/clang/test/Sema/linkage-internal-extern.cpp
+++ b/clang/test/Sema/linkage-internal-extern.cpp
@@ -1,6 +1,4 @@
-// RUN: %clang_cc1 -verify -fsyntax-only -std=c++17 %s
-
-// expected-no-diagnostics
+// RUN: %clang_cc1 -ast-dump -std=c++17 %s | FileCheck %s
// In C++, block-scope extern declarations target the enclosing namespace
// scope ([dcl.meaning.general]/3.5), so they match against the namespace-scope
@@ -11,12 +9,33 @@
// Example adapted from [basic.link]/6.
static void f();
+// CHECK: FunctionDecl {{.*}} f 'void ()' static internal-linkage
static int i = 0;
+// CHECK: VarDecl {{.*}} i 'int' static cinit internal-linkage
void g() {
- extern void f(); // internal linkage
- int i; // no linkage
+// CHECK: FunctionDecl {{.*}} g 'void ()' external-linkage
+ extern void f();
+ // CHECK: FunctionDecl {{.*}} prev {{.*}} f 'void ()' extern internal-linkage
+ int i;
+ // CHECK: VarDecl {{.*}} i 'int'{{$}}
+ {
+ extern void f();
+ // CHECK: FunctionDecl {{.*}} prev {{.*}} f 'void ()' extern internal-linkage
+ extern int i;
+ // CHECK: VarDecl {{.*}} prev {{.*}} i 'int' extern internal-linkage
+ }
+}
+
+// Block-scope function declarations behave identically without extern
+// (C11 6.2.2p5, C++ [dcl.stc]p5).
+static void h();
+// CHECK: FunctionDecl {{.*}} h 'void ()' static internal-linkage
+void g2() {
+// CHECK: FunctionDecl {{.*}} g2 'void ()' external-linkage
+ int h;
+ // CHECK: VarDecl {{.*}} h 'int'{{$}}
{
- extern void f(); // internal linkage
- extern int i; // internal linkage
+ void h();
+ // CHECK: FunctionDecl {{.*}} prev {{.*}} h 'void ()' internal-linkage
}
}
>From c6d1a78317a2e6f3e0ff2c99be7c421f8c8e3b76 Mon Sep 17 00:00:00 2001
From: flash1729 <sherlockedaditya at gmail.com>
Date: Thu, 28 May 2026 18:59:49 +0530
Subject: [PATCH 4/5] fixup! [Clang] Diagnose mixed internal/external linkage
in C (reland)
---
clang/test/C/C2y/n3410.c | 68 +++++++++++++--------
clang/test/Sema/linkage-internal-extern.c | 2 +-
clang/test/Sema/linkage-internal-extern.cpp | 2 +-
3 files changed, 46 insertions(+), 26 deletions(-)
diff --git a/clang/test/C/C2y/n3410.c b/clang/test/C/C2y/n3410.c
index 7ce3c2916d300..501ec074562fa 100644
--- a/clang/test/C/C2y/n3410.c
+++ b/clang/test/C/C2y/n3410.c
@@ -1,4 +1,9 @@
-// RUN: %clang_cc1 -verify -std=c2y -Wall -pedantic -Wno-unused %s
+// RUN: %clang_cc1 -verify=c2y -std=c2y -Wall -pedantic -Wno-unused %s
+// RUN: %clang_cc1 -verify=pre -std=c23 -Wall -pedantic -Wno-unused %s
+// RUN: %clang_cc1 -verify=pre -std=c17 -Wall -pedantic -Wno-unused %s
+// RUN: %clang_cc1 -verify=pre -std=c11 -Wall -pedantic -Wno-unused %s
+// RUN: %clang_cc1 -verify=pre -std=c99 -Wall -pedantic -Wno-unused %s
+// RUN: %clang_cc1 -verify=pre -std=c89 -Wall -pedantic -Wno-unused -Wno-comment %s
/* WG14 N3410: Clang 23
* Slay Some Earthly Demons XI
@@ -7,37 +12,52 @@
* internal and external linkage.
*/
-void func1() {
- extern int a; // #a
+void func1(void) {
+ extern int a; /* #a */
}
-// This 'a' is the same as the one declared extern above.
-static int a; /* expected-error {{static declaration of 'a' follows non-static declaration}}
- expected-note@#a {{previous declaration is here}}
+/* This 'a' is the same as the one declared extern above. */
+static int a; /* c2y-error {{static declaration of 'a' follows non-static declaration}}
+ c2y-note@#a {{previous declaration is here}}
+ pre-error {{static declaration of 'a' follows non-static declaration}}
+ pre-note@#a {{previous declaration is here}}
*/
static int b;
-void func2() {
- // This 'b' is the same as the one declaraed static above, but this is not
- // ill-formed because of C2y 6.2.2p4, which gives this variable internal
- // linkage because the previous declaration had internal linkage.
- extern int b; // Ok
+void func2(void) {
+ /* This 'b' is the same as the one declared static above, but this is not
+ ill-formed because of C2y 6.2.2p4, which gives this variable internal
+ linkage because the previous declaration had internal linkage.
+ */
+ extern int b; /* Ok */
}
-static int c, d; // expected-note 2 {{previous definition is here}}
-void func3() {
- int c; // no linkage, different object from the one declared above.
- for (int d;;) {
- // This 'c' is the same as the one declared at file scope, but because of
- // the local scope 'c', the file scope 'c' is not visible.
- extern int c; // expected-error {{declared with both internal and external linkage}}
- // This 'd' is the same as the one declared at file scope as well, but
- // because of the 'd' declared within the for loop, the file scope 'd' is
- // also not visible, same as with 'c'.
- extern int d; // expected-error {{declared with both internal and external linkage}}
+static int c, d; /* c2y-note 2 {{previous definition is here}}
+ pre-note 2 {{previous definition is here}}
+ */
+void func3(void) {
+ int c; /* no linkage, different object from the one declared above. */
+ {
+ int d; /* no linkage, different object from the file-scope 'd'. */
+ {
+ /* This 'c' is the same as the one declared at file scope, but because
+ of the local scope 'c', the file scope 'c' is not visible. */
+ extern int c; /* c2y-error {{'c' declared with both internal and external linkage in the same translation unit}}
+ pre-error {{'c' declared with both internal and external linkage in the same translation unit; behavior is undefined}}
+ */
+ /* This 'd' is the same as the one declared at file scope as well, but
+ because of the enclosing block-scope 'd', the file scope 'd' is also
+ not visible, same as with 'c'. */
+ extern int d; /* c2y-error {{'d' declared with both internal and external linkage in the same translation unit}}
+ pre-error {{'d' declared with both internal and external linkage in the same translation unit; behavior is undefined}}
+ */
+ }
}
- for (static int e;;) {
- extern int e; // Ok for the same reason as 'b' above.
+ {
+ static int e;
+ {
+ extern int e; /* Ok for the same reason as 'b' above. */
+ }
}
}
diff --git a/clang/test/Sema/linkage-internal-extern.c b/clang/test/Sema/linkage-internal-extern.c
index 06f30e60261e2..3b2e7706868c6 100644
--- a/clang/test/Sema/linkage-internal-extern.c
+++ b/clang/test/Sema/linkage-internal-extern.c
@@ -23,7 +23,7 @@ void test_param_shadow(int p) {
static int z;
void test_chained_shadow(void) {
- extern int z; // expected-note {{previous}} #2: internal linkage
+ extern int z; // expected-note {{previous}}
{
int z; // shadow: no linkage
{ extern int z; } // expected-error {{declared with both internal and external linkage}}
diff --git a/clang/test/Sema/linkage-internal-extern.cpp b/clang/test/Sema/linkage-internal-extern.cpp
index 27b9566620289..3b28300512e32 100644
--- a/clang/test/Sema/linkage-internal-extern.cpp
+++ b/clang/test/Sema/linkage-internal-extern.cpp
@@ -27,7 +27,7 @@ void g() {
}
// Block-scope function declarations behave identically without extern
-// (C11 6.2.2p5, C++ [dcl.stc]p5).
+// (C11 6.2.2p5, C++ [dcl.meaning.general]/3.5).
static void h();
// CHECK: FunctionDecl {{.*}} h 'void ()' static internal-linkage
void g2() {
>From 9b96f6e093be37ebb179cdd88cb0442d1c72e785 Mon Sep 17 00:00:00 2001
From: flash1729 <sherlockedaditya at gmail.com>
Date: Wed, 3 Jun 2026 12:47:16 +0530
Subject: [PATCH 5/5] fixup! [Clang] Diagnose mixed internal/external linkage
in C (reland)
---
clang/test/C/C2y/n3410.c | 63 +++++++++++++++++++----
clang/test/Sema/linkage-internal-extern.c | 46 -----------------
2 files changed, 53 insertions(+), 56 deletions(-)
delete mode 100644 clang/test/Sema/linkage-internal-extern.c
diff --git a/clang/test/C/C2y/n3410.c b/clang/test/C/C2y/n3410.c
index 501ec074562fa..9780db956e51c 100644
--- a/clang/test/C/C2y/n3410.c
+++ b/clang/test/C/C2y/n3410.c
@@ -1,9 +1,9 @@
// RUN: %clang_cc1 -verify=c2y -std=c2y -Wall -pedantic -Wno-unused %s
-// RUN: %clang_cc1 -verify=pre -std=c23 -Wall -pedantic -Wno-unused %s
-// RUN: %clang_cc1 -verify=pre -std=c17 -Wall -pedantic -Wno-unused %s
-// RUN: %clang_cc1 -verify=pre -std=c11 -Wall -pedantic -Wno-unused %s
-// RUN: %clang_cc1 -verify=pre -std=c99 -Wall -pedantic -Wno-unused %s
-// RUN: %clang_cc1 -verify=pre -std=c89 -Wall -pedantic -Wno-unused -Wno-comment %s
+// RUN: %clang_cc1 -verify=c89-23 -std=c23 -Wall -pedantic -Wno-unused %s
+// RUN: %clang_cc1 -verify=c89-23 -std=c17 -Wall -pedantic -Wno-unused %s
+// RUN: %clang_cc1 -verify=c89-23 -std=c11 -Wall -pedantic -Wno-unused %s
+// RUN: %clang_cc1 -verify=c89-23 -std=c99 -Wall -pedantic -Wno-unused %s
+// RUN: %clang_cc1 -verify=c89-23 -std=c89 -Wall -pedantic -Wno-unused -Wno-comment %s
/* WG14 N3410: Clang 23
* Slay Some Earthly Demons XI
@@ -19,8 +19,8 @@ void func1(void) {
/* This 'a' is the same as the one declared extern above. */
static int a; /* c2y-error {{static declaration of 'a' follows non-static declaration}}
c2y-note@#a {{previous declaration is here}}
- pre-error {{static declaration of 'a' follows non-static declaration}}
- pre-note@#a {{previous declaration is here}}
+ c89-23-error {{static declaration of 'a' follows non-static declaration}}
+ c89-23-note@#a {{previous declaration is here}}
*/
static int b;
@@ -33,7 +33,7 @@ void func2(void) {
}
static int c, d; /* c2y-note 2 {{previous definition is here}}
- pre-note 2 {{previous definition is here}}
+ c89-23-note 2 {{previous definition is here}}
*/
void func3(void) {
int c; /* no linkage, different object from the one declared above. */
@@ -43,13 +43,13 @@ void func3(void) {
/* This 'c' is the same as the one declared at file scope, but because
of the local scope 'c', the file scope 'c' is not visible. */
extern int c; /* c2y-error {{'c' declared with both internal and external linkage in the same translation unit}}
- pre-error {{'c' declared with both internal and external linkage in the same translation unit; behavior is undefined}}
+ c89-23-error {{'c' declared with both internal and external linkage in the same translation unit; behavior is undefined}}
*/
/* This 'd' is the same as the one declared at file scope as well, but
because of the enclosing block-scope 'd', the file scope 'd' is also
not visible, same as with 'c'. */
extern int d; /* c2y-error {{'d' declared with both internal and external linkage in the same translation unit}}
- pre-error {{'d' declared with both internal and external linkage in the same translation unit; behavior is undefined}}
+ c89-23-error {{'d' declared with both internal and external linkage in the same translation unit; behavior is undefined}}
*/
}
}
@@ -61,3 +61,46 @@ void func3(void) {
}
}
+/* A function parameter shadows the file-scope 'p' the same way a local
+ variable does, so the block-scope 'extern' does not inherit internal
+ linkage and conflicts. */
+static int p; /* c2y-note {{previous definition is here}}
+ c89-23-note {{previous definition is here}}
+ */
+void func4(int p) {
+ {
+ extern int p; /* c2y-error {{'p' declared with both internal and external linkage in the same translation unit}}
+ c89-23-error {{'p' declared with both internal and external linkage in the same translation unit; behavior is undefined}}
+ */
+ }
+}
+
+static int q;
+void func5(void) {
+ /* No shadow intervenes here, so this 'q' inherits the internal linkage of
+ the file-scope 'q', which is fine. */
+ extern int q; /* #q */
+ {
+ int q; /* no linkage; shadows the declarations above. */
+ {
+ /* The file-scope 'q' is now hidden, so this 'extern' has external
+ linkage and conflicts with the internal-linkage declaration above. */
+ extern int q; /* c2y-error {{'q' declared with both internal and external linkage in the same translation unit}}
+ c2y-note@#q {{previous declaration is here}}
+ c89-23-error {{'q' declared with both internal and external linkage in the same translation unit; behavior is undefined}}
+ c89-23-note@#q {{previous declaration is here}}
+ */
+ }
+ }
+}
+
+void func6(void) {
+ /* No file-scope declaration of 'r' exists, so the block-scope 'extern' just
+ has external linkage and there is no conflict. */
+ {
+ int r; /* no linkage. */
+ {
+ extern int r; /* Ok */
+ }
+ }
+}
diff --git a/clang/test/Sema/linkage-internal-extern.c b/clang/test/Sema/linkage-internal-extern.c
deleted file mode 100644
index 3b2e7706868c6..0000000000000
--- a/clang/test/Sema/linkage-internal-extern.c
+++ /dev/null
@@ -1,46 +0,0 @@
-// RUN: %clang_cc1 -verify -fsyntax-only %s
-
-// C11 6.2.2p7: same identifier with both internal and external linkage is UB.
-
-// Conflicting linkage (UB)
-
-static int x; // expected-note {{previous}}
-void test_basic_shadow(void) {
- int x;
- { extern int x; } // expected-error {{declared with both internal and external linkage}}
-}
-
-static int y; // expected-note {{previous}}
-void test_deep_nesting(void) {
- int y;
- { int y; { { extern int y; } } } // expected-error {{declared with both internal and external linkage}}
-}
-
-static int p; // expected-note {{previous}}
-void test_param_shadow(int p) {
- { extern int p; } // expected-error {{declared with both internal and external linkage}}
-}
-
-static int z;
-void test_chained_shadow(void) {
- extern int z; // expected-note {{previous}}
- {
- int z; // shadow: no linkage
- { extern int z; } // expected-error {{declared with both internal and external linkage}}
- }
-}
-
-
-// Valid cases
-
-static int a;
-void test_no_shadow(void) {
- extern int a;
-}
-
-void test_no_file_scope(void) {
- for (int b = 0;;) {
- extern int b;
- break;
- }
-}
More information about the cfe-commits
mailing list