[Clang] Diagnose UB and emit error when identifier has both internal and external linkage (#192116)
C11 6.2.2p7 says if the same identifier appears with both internal and external linkage in a translation unit, the behavior is undefined. This patch adds a check in `Sema::MergeVarDecl` that uses `LookupResult::isShadowed()` to detect when a block-scope `extern` found a file-scope `static` through a shadow, and emits an error. Fixes #54215
This commit is contained in:
parent
facb9ab34f
commit
1e2175ec4d
7 changed files with 88 additions and 7 deletions
|
|
@ -155,6 +155,12 @@ C Language Changes
|
|||
C2y Feature Support
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
- 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)
|
||||
|
|
|
|||
|
|
@ -6534,6 +6534,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<
|
||||
|
|
|
|||
|
|
@ -4802,6 +4802,25 @@ void Sema::MergeVarDecl(VarDecl *New, LookupResult &Previous) {
|
|||
return New->setInvalidDecl();
|
||||
}
|
||||
}
|
||||
|
||||
// C2y 6.7.1p7:
|
||||
// Within a translation unit, the same identifier shall not appear with
|
||||
// both internal and external linkage.
|
||||
//
|
||||
// In C11 through C23, this was undefined behavior (C11 6.2.2p7).
|
||||
//
|
||||
// This can occur when an extern declaration in block scope finds a file-scope
|
||||
// static declaration, but a no-linkage local variable shadowed the static,
|
||||
// preventing linkage inheritance per C2y 6.2.2p4.
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
36
clang/test/Sema/linkage-internal-extern.c
Normal file
36
clang/test/Sema/linkage-internal-extern.c
Normal file
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
19
clang/test/Sema/linkage-internal-extern.cpp
Normal file
19
clang/test/Sema/linkage-internal-extern.cpp
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
// RUN: %clang_cc1 -verify -fsyntax-only -std=c++17 %s
|
||||
|
||||
// expected-no-diagnostics
|
||||
|
||||
// CWG 426 / [basic.link]p6: the same identifier with both internal and
|
||||
// external linkage should be ill-formed in C++, but Clang does not yet
|
||||
// diagnose this due to ABI break concerns. This test documents current
|
||||
// behavior.
|
||||
|
||||
static int x;
|
||||
void test_shadow(void) {
|
||||
int x;
|
||||
{
|
||||
// FIXME: Per CWG 426, this should be ill-formed because the
|
||||
// file-scope 'x' has internal linkage but this 'extern' gets
|
||||
// external linkage due to the local shadow.
|
||||
extern int x;
|
||||
}
|
||||
}
|
||||
|
|
@ -283,7 +283,7 @@ conformance.</p>
|
|||
<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>
|
||||
|
|
|
|||
Loading…
Reference in a new issue