Only merge down a variable type if the previous declaration was

visible.  There's a lot of potential badness in how we're modelling
these things, but getting this much correct is reasonably easy.

rdar://13535367

llvm-svn: 178488
This commit is contained in:
John McCall 2013-04-01 18:34:28 +00:00
parent ec0748068e
commit b65e8fe143
4 changed files with 97 additions and 11 deletions

View File

@ -1759,8 +1759,9 @@ public:
bool MergeCompatibleFunctionDecls(FunctionDecl *New, FunctionDecl *Old, bool MergeCompatibleFunctionDecls(FunctionDecl *New, FunctionDecl *Old,
Scope *S); Scope *S);
void mergeObjCMethodDecls(ObjCMethodDecl *New, ObjCMethodDecl *Old); void mergeObjCMethodDecls(ObjCMethodDecl *New, ObjCMethodDecl *Old);
void MergeVarDecl(VarDecl *New, LookupResult &OldDecls); void MergeVarDecl(VarDecl *New, LookupResult &OldDecls,
void MergeVarDeclTypes(VarDecl *New, VarDecl *Old); bool OldDeclsWereHidden);
void MergeVarDeclTypes(VarDecl *New, VarDecl *Old, bool OldIsHidden);
void MergeVarDeclExceptionSpecs(VarDecl *New, VarDecl *Old); void MergeVarDeclExceptionSpecs(VarDecl *New, VarDecl *Old);
bool MergeCXXFunctionDecl(FunctionDecl *New, FunctionDecl *Old, Scope *S); bool MergeCXXFunctionDecl(FunctionDecl *New, FunctionDecl *Old, Scope *S);

View File

@ -2773,7 +2773,7 @@ void Sema::mergeObjCMethodDecls(ObjCMethodDecl *newMethod,
/// Declarations using the auto type specifier (C++ [decl.spec.auto]) call back /// Declarations using the auto type specifier (C++ [decl.spec.auto]) call back
/// to here in AddInitializerToDecl. We can't check them before the initializer /// to here in AddInitializerToDecl. We can't check them before the initializer
/// is attached. /// is attached.
void Sema::MergeVarDeclTypes(VarDecl *New, VarDecl *Old) { void Sema::MergeVarDeclTypes(VarDecl *New, VarDecl *Old, bool OldWasHidden) {
if (New->isInvalidDecl() || Old->isInvalidDecl()) if (New->isInvalidDecl() || Old->isInvalidDecl())
return; return;
@ -2820,7 +2820,11 @@ void Sema::MergeVarDeclTypes(VarDecl *New, VarDecl *Old) {
Diag(Old->getLocation(), diag::note_previous_definition); Diag(Old->getLocation(), diag::note_previous_definition);
return New->setInvalidDecl(); return New->setInvalidDecl();
} }
New->setType(MergedT);
// Don't actually update the type on the new declaration if the old
// declaration was a extern declaration in a different scope.
if (!OldWasHidden)
New->setType(MergedT);
} }
/// MergeVarDecl - We just parsed a variable 'New' which has the same name /// MergeVarDecl - We just parsed a variable 'New' which has the same name
@ -2831,7 +2835,8 @@ void Sema::MergeVarDeclTypes(VarDecl *New, VarDecl *Old) {
/// FinalizeDeclaratorGroup. Unfortunately, we can't analyze tentative /// FinalizeDeclaratorGroup. Unfortunately, we can't analyze tentative
/// definitions here, since the initializer hasn't been attached. /// definitions here, since the initializer hasn't been attached.
/// ///
void Sema::MergeVarDecl(VarDecl *New, LookupResult &Previous) { void Sema::MergeVarDecl(VarDecl *New, LookupResult &Previous,
bool PreviousWasHidden) {
// If the new decl is already invalid, don't do any other checking. // If the new decl is already invalid, don't do any other checking.
if (New->isInvalidDecl()) if (New->isInvalidDecl())
return; return;
@ -2871,7 +2876,7 @@ void Sema::MergeVarDecl(VarDecl *New, LookupResult &Previous) {
} }
// Merge the types. // Merge the types.
MergeVarDeclTypes(New, Old); MergeVarDeclTypes(New, Old, PreviousWasHidden);
if (New->isInvalidDecl()) if (New->isInvalidDecl())
return; return;
@ -5206,13 +5211,39 @@ bool Sema::CheckVariableDeclaration(VarDecl *NewVD,
NewVD->setTypeSourceInfo(FixedTInfo); NewVD->setTypeSourceInfo(FixedTInfo);
} }
// If we did not find anything by this name, look for a non-visible
// extern "C" declaration with the same name.
//
// Clang has a lot of problems with extern local declarations.
// The actual standards text here is:
//
// C++11 [basic.link]p6:
// The name of a function declared in block scope and the name
// of a variable declared by a block scope extern declaration
// have linkage. If there is a visible declaration of an entity
// with linkage having the same name and type, ignoring entities
// declared outside the innermost enclosing namespace scope, the
// block scope declaration declares that same entity and
// receives the linkage of the previous declaration.
//
// C11 6.2.7p4:
// For an identifier with internal or external linkage declared
// in a scope in which a prior declaration of that identifier is
// visible, if the prior declaration specifies internal or
// external linkage, the type of the identifier at the later
// declaration becomes the composite type.
//
// The most important point here is that we're not allowed to
// update our understanding of the type according to declarations
// not in scope.
bool PreviousWasHidden = false;
if (Previous.empty() && mayConflictWithNonVisibleExternC(NewVD)) { if (Previous.empty() && mayConflictWithNonVisibleExternC(NewVD)) {
// Since we did not find anything by this name, look for a non-visible
// extern "C" declaration with the same name.
llvm::DenseMap<DeclarationName, NamedDecl *>::iterator Pos llvm::DenseMap<DeclarationName, NamedDecl *>::iterator Pos
= findLocallyScopedExternCDecl(NewVD->getDeclName()); = findLocallyScopedExternCDecl(NewVD->getDeclName());
if (Pos != LocallyScopedExternCDecls.end()) if (Pos != LocallyScopedExternCDecls.end()) {
Previous.addDecl(Pos->second); Previous.addDecl(Pos->second);
PreviousWasHidden = true;
}
} }
// Filter out any non-conflicting previous declarations. // Filter out any non-conflicting previous declarations.
@ -5245,7 +5276,7 @@ bool Sema::CheckVariableDeclaration(VarDecl *NewVD,
} }
if (!Previous.empty()) { if (!Previous.empty()) {
MergeVarDecl(NewVD, Previous); MergeVarDecl(NewVD, Previous, PreviousWasHidden);
return true; return true;
} }
return false; return false;
@ -7277,7 +7308,7 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init,
// If this is a redeclaration, check that the type we just deduced matches // If this is a redeclaration, check that the type we just deduced matches
// the previously declared type. // the previously declared type.
if (VarDecl *Old = VDecl->getPreviousDecl()) if (VarDecl *Old = VDecl->getPreviousDecl())
MergeVarDeclTypes(VDecl, Old); MergeVarDeclTypes(VDecl, Old, /*OldWasHidden*/ false);
} }
if (VDecl->isLocalVarDecl() && VDecl->hasExternalStorage()) { if (VDecl->isLocalVarDecl() && VDecl->hasExternalStorage()) {

View File

@ -0,0 +1,43 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s
// C++11 [basic.link]p6:
// The name of a function declared in block scope and the name
// of a variable declared by a block scope extern declaration
// have linkage. If there is a visible declaration of an entity
// with linkage having the same name and type, ignoring entities
// declared outside the innermost enclosing namespace scope, the
// block scope declaration declares that same entity and
// receives the linkage of the previous declaration.
// rdar://13535367
namespace test0 {
extern "C" int test0_array[];
void declare() { extern int test0_array[100]; }
extern "C" int test0_array[];
int value = sizeof(test0_array); // expected-error {{invalid application of 'sizeof' to an incomplete type 'int []'}}
}
namespace test1 {
extern "C" int test1_array[];
void test() {
{ extern int test1_array[100]; }
extern int test1_array[];
int x = sizeof(test1_array); // expected-error {{invalid application of 'sizeof' to an incomplete type 'int []'}}
}
}
namespace test2 {
void declare() { extern int test2_array[100]; }
extern int test2_array[];
int value = sizeof(test2_array); // expected-error {{invalid application of 'sizeof' to an incomplete type 'int []'}}
}
namespace test3 {
void test() {
{ extern int test3_array[100]; }
extern int test3_array[];
int x = sizeof(test3_array); // expected-error {{invalid application of 'sizeof' to an incomplete type 'int []'}}
}
}

View File

@ -22,3 +22,14 @@ int PR10013(void) {
static int test1_a[]; // expected-warning {{tentative array definition assumed to have one element}} static int test1_a[]; // expected-warning {{tentative array definition assumed to have one element}}
extern int test1_a[]; extern int test1_a[];
// rdar://13535367
void test2declarer() { extern int test2_array[100]; }
extern int test2_array[];
int test2v = sizeof(test2_array); // expected-error {{invalid application of 'sizeof' to an incomplete type 'int []'}}
void test3declarer() {
{ extern int test3_array[100]; }
extern int test3_array[];
int x = sizeof(test3_array); // expected-error {{invalid application of 'sizeof' to an incomplete type 'int []'}}
}