/// \brief Whether this variable is (C++0x) constexpr.
unsigned IsConstexpr : 1;
+
+ /// \brief Whether this local extern variable's previous declaration was
+ /// declared in the same block scope. This controls whether we should merge
+ /// the type of this declaration with its previous declaration.
+ unsigned PreviousDeclInSameBlockScope : 1;
};
- enum { NumVarDeclBits = 12 };
+ enum { NumVarDeclBits = 13 };
friend class ASTDeclReader;
friend class StmtIteratorBase;
bool isConstexpr() const { return VarDeclBits.IsConstexpr; }
void setConstexpr(bool IC) { VarDeclBits.IsConstexpr = IC; }
+ /// Whether this local extern variable declaration's previous declaration
+ /// was declared in the same block scope. Only correct in C++.
+ bool isPreviousDeclInSameBlockScope() const {
+ return VarDeclBits.PreviousDeclInSameBlockScope;
+ }
+ void setPreviousDeclInSameBlockScope(bool Same) {
+ VarDeclBits.PreviousDeclInSameBlockScope = Same;
+ }
+
/// \brief If this variable is an instantiated static data member of a
/// class template specialization, returns the templated static data member
/// from which it was instantiated.
Redecl(Redecl != Sema::NotForRedeclaration),
HideTags(true),
Diagnose(Redecl == Sema::NotForRedeclaration),
- AllowHidden(Redecl == Sema::ForRedeclaration)
+ AllowHidden(Redecl == Sema::ForRedeclaration),
+ Shadowed(false)
{
configure();
}
Redecl(Redecl != Sema::NotForRedeclaration),
HideTags(true),
Diagnose(Redecl == Sema::NotForRedeclaration),
- AllowHidden(Redecl == Sema::ForRedeclaration)
+ AllowHidden(Redecl == Sema::ForRedeclaration),
+ Shadowed(false)
{
configure();
}
Redecl(Other.Redecl),
HideTags(Other.HideTags),
Diagnose(false),
- AllowHidden(Other.AllowHidden)
+ AllowHidden(Other.AllowHidden),
+ Shadowed(false)
{}
~LookupResult() {
assert(ResultKind == NotFound && Decls.empty());
ResultKind = NotFoundInCurrentInstantiation;
}
-
+
+ /// \brief Determine whether the lookup result was shadowed by some other
+ /// declaration that lookup ignored.
+ bool isShadowed() const { return Shadowed; }
+
+ /// \brief Note that we found and ignored a declaration while performing
+ /// lookup.
+ void setShadowed() { Shadowed = true; }
+
/// \brief Resolves the result kind of the lookup, possibly hiding
/// decls.
///
if (Paths) deletePaths(Paths);
Paths = NULL;
NamingClass = 0;
+ Shadowed = false;
}
/// \brief Clears out any current state and re-initializes for a
/// \brief True if we should allow hidden declarations to be 'visible'.
bool AllowHidden;
+
+ /// \brief True if the found declarations were shadowed by some other
+ /// declaration that we skipped. This only happens when \c LookupKind
+ /// is \c LookupRedeclarationWithLinkage.
+ bool Shadowed;
};
/// \brief Consumes visible declarations found when searching for
/// \param ExplicitInstantiationOrSpecialization When true, we are checking
/// whether the declaration is in scope for the purposes of explicit template
/// instantiation or specialization. The default is false.
- bool isDeclInScope(NamedDecl *&D, DeclContext *Ctx, Scope *S = 0,
+ bool isDeclInScope(NamedDecl *D, DeclContext *Ctx, Scope *S = 0,
bool ExplicitInstantiationOrSpecialization = false);
/// Finds the scope corresponding to the given decl context, if it
void mergeDeclAttributes(NamedDecl *New, Decl *Old,
AvailabilityMergeKind AMK = AMK_Redeclaration);
void MergeTypedefNameDecl(TypedefNameDecl *New, LookupResult &OldDecls);
- bool MergeFunctionDecl(FunctionDecl *New, Decl *Old, Scope *S);
+ bool MergeFunctionDecl(FunctionDecl *New, Decl *Old, Scope *S,
+ bool MergeTypeWithOld);
bool MergeCompatibleFunctionDecls(FunctionDecl *New, FunctionDecl *Old,
- Scope *S);
+ Scope *S, bool MergeTypeWithOld);
void mergeObjCMethodDecls(ObjCMethodDecl *New, ObjCMethodDecl *Old);
- void MergeVarDecl(VarDecl *New, LookupResult &OldDecls,
- bool OldDeclsWereHidden);
- void MergeVarDeclTypes(VarDecl *New, VarDecl *Old, bool OldIsHidden);
+ void MergeVarDecl(VarDecl *New, LookupResult &Previous,
+ bool MergeTypeWithPrevious);
+ void MergeVarDeclTypes(VarDecl *New, VarDecl *Old,
+ bool MergeTypeWithOld);
void MergeVarDeclExceptionSpecs(VarDecl *New, VarDecl *Old);
bool MergeCXXFunctionDecl(FunctionDecl *New, FunctionDecl *Old, Scope *S);
TUScope->AddDecl(D);
}
-bool Sema::isDeclInScope(NamedDecl *&D, DeclContext *Ctx, Scope *S,
+bool Sema::isDeclInScope(NamedDecl *D, DeclContext *Ctx, Scope *S,
bool ExplicitInstantiationOrSpecialization) {
return IdResolver.isDeclInScope(D, Ctx, S,
ExplicitInstantiationOrSpecialization);
/// merged with.
///
/// Returns true if there was an error, false otherwise.
-bool Sema::MergeFunctionDecl(FunctionDecl *New, Decl *OldD, Scope *S) {
+bool Sema::MergeFunctionDecl(FunctionDecl *New, Decl *OldD, Scope *S,
+ bool MergeTypeWithOld) {
// Verify the old decl was also a function.
FunctionDecl *Old = 0;
if (FunctionTemplateDecl *OldFunctionTemplate
}
if (OldQTypeForComparison == NewQType)
- return MergeCompatibleFunctionDecls(New, Old, S);
+ return MergeCompatibleFunctionDecls(New, Old, S, MergeTypeWithOld);
// Fall through for conflicting redeclarations and redefinitions.
}
const FunctionType *OldFuncType = OldQType->getAs<FunctionType>();
const FunctionType *NewFuncType = NewQType->getAs<FunctionType>();
const FunctionProtoType *OldProto = 0;
- if (isa<FunctionNoProtoType>(NewFuncType) &&
+ if (MergeTypeWithOld && isa<FunctionNoProtoType>(NewFuncType) &&
(OldProto = dyn_cast<FunctionProtoType>(OldFuncType))) {
// The old declaration provided a function prototype, but the
// new declaration does not. Merge in the prototype.
New->setParams(Params);
}
- return MergeCompatibleFunctionDecls(New, Old, S);
+ return MergeCompatibleFunctionDecls(New, Old, S, MergeTypeWithOld);
}
// GNU C permits a K&R definition to follow a prototype declaration
diag::note_previous_declaration);
}
- New->setType(Context.getFunctionType(MergedReturn, ArgTypes,
- OldProto->getExtProtoInfo()));
- return MergeCompatibleFunctionDecls(New, Old, S);
+ if (MergeTypeWithOld)
+ New->setType(Context.getFunctionType(MergedReturn, ArgTypes,
+ OldProto->getExtProtoInfo()));
+ return MergeCompatibleFunctionDecls(New, Old, S, MergeTypeWithOld);
}
// Fall through to diagnose conflicting types.
///
/// \returns false
bool Sema::MergeCompatibleFunctionDecls(FunctionDecl *New, FunctionDecl *Old,
- Scope *S) {
+ Scope *S, bool MergeTypeWithOld) {
// Merge the attributes
mergeDeclAttributes(New, Old);
return MergeCXXFunctionDecl(New, Old, S);
// Merge the function types so the we get the composite types for the return
- // and argument types.
+ // and argument types. Per C11 6.2.7/4, only update the type if the old decl
+ // was visible.
QualType Merged = Context.mergeTypes(Old->getType(), New->getType());
- if (!Merged.isNull())
+ if (!Merged.isNull() && MergeTypeWithOld)
New->setType(Merged);
return false;
/// Declarations using the auto type specifier (C++ [decl.spec.auto]) call back
/// to here in AddInitializerToDecl. We can't check them before the initializer
/// is attached.
-void Sema::MergeVarDeclTypes(VarDecl *New, VarDecl *Old, bool OldWasHidden) {
+void Sema::MergeVarDeclTypes(VarDecl *New, VarDecl *Old,
+ bool MergeTypeWithOld) {
if (New->isInvalidDecl() || Old->isInvalidDecl())
return;
MergedT = Context.mergeTypes(New->getType(), Old->getType());
}
if (MergedT.isNull()) {
+ // It's OK if we couldn't merge types if either type is dependent, for a
+ // block-scope variable. In other cases (static data members of class
+ // templates, variable templates, ...), we require the types to be
+ // equivalent.
+ // FIXME: The C++ standard doesn't say anything about this.
+ if ((New->getType()->isDependentType() ||
+ Old->getType()->isDependentType()) && New->isLocalVarDecl()) {
+ // If the old type was dependent, we can't merge with it, so the new type
+ // becomes dependent for now. We'll reproduce the original type when we
+ // instantiate the TypeSourceInfo for the variable.
+ if (!New->getType()->isDependentType() && MergeTypeWithOld)
+ New->setType(Context.DependentTy);
+ return;
+ }
+
+ // FIXME: Even if this merging succeeds, some other non-visible declaration
+ // of this variable might have an incompatible type. For instance:
+ //
+ // extern int arr[];
+ // void f() { extern int arr[2]; }
+ // void g() { extern int arr[3]; }
+ //
+ // Neither C nor C++ requires a diagnostic for this, but we should still try
+ // to diagnose it.
Diag(New->getLocation(), diag::err_redefinition_different_type)
<< New->getDeclName() << New->getType() << Old->getType();
Diag(Old->getLocation(), diag::note_previous_definition);
// Don't actually update the type on the new declaration if the old
// declaration was a extern declaration in a different scope.
- if (!OldWasHidden)
+ if (MergeTypeWithOld)
New->setType(MergedT);
}
/// definitions here, since the initializer hasn't been attached.
///
void Sema::MergeVarDecl(VarDecl *New, LookupResult &Previous,
- bool PreviousWasHidden) {
+ bool MergeTypeWithPrevious) {
// If the new decl is already invalid, don't do any other checking.
if (New->isInvalidDecl())
return;
}
// Merge the types.
- MergeVarDeclTypes(New, Old, PreviousWasHidden);
+ MergeVarDeclTypes(New, Old, MergeTypeWithPrevious);
if (New->isInvalidDecl())
return;
// See if this is a redefinition of a variable in the same scope.
if (!D.getCXXScopeSpec().isSet()) {
bool IsLinkageLookup = false;
+ bool CreateBuiltins = false;
// If the declaration we're planning to build will be a function
// or object with linkage, then look for another declaration with
// linkage (C99 6.2.2p4-5 and C++ [basic.link]p6).
+ //
+ // If the declaration we're planning to build will be declared with
+ // external linkage in the translation unit, create any builtin with
+ // the same name.
if (D.getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_typedef)
/* Do nothing*/;
- else if (R->isFunctionType()) {
- if (CurContext->isFunctionOrMethod() ||
- D.getDeclSpec().getStorageClassSpec() != DeclSpec::SCS_static)
- IsLinkageLookup = true;
- } else if (D.getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_extern)
- IsLinkageLookup = true;
- else if (CurContext->getRedeclContext()->isTranslationUnit() &&
- D.getDeclSpec().getStorageClassSpec() != DeclSpec::SCS_static)
+ else if (CurContext->isFunctionOrMethod() &&
+ (D.getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_extern ||
+ R->isFunctionType())) {
IsLinkageLookup = true;
+ CreateBuiltins =
+ CurContext->getEnclosingNamespaceContext()->isTranslationUnit();
+ } else if (CurContext->getRedeclContext()->isTranslationUnit() &&
+ D.getDeclSpec().getStorageClassSpec() != DeclSpec::SCS_static)
+ CreateBuiltins = true;
if (IsLinkageLookup)
Previous.clear(LookupRedeclarationWithLinkage);
- LookupName(Previous, S, /* CreateBuiltins = */ IsLinkageLookup);
+ LookupName(Previous, S, CreateBuiltins);
} else { // Something like "int foo::x;"
LookupQualifiedName(Previous, DC);
Previous, DC, S, shouldConsiderLinkage(NewVD),
IsExplicitSpecialization || IsVariableTemplateSpecialization);
+ // Check whether the previous declaration is in the same block scope. This
+ // affects whether we merge types with it, per C++11 [dcl.array]p3.
+ if (getLangOpts().CPlusPlus &&
+ NewVD->isLocalVarDecl() && NewVD->hasExternalStorage())
+ NewVD->setPreviousDeclInSameBlockScope(
+ Previous.isSingleResult() && !Previous.isShadowed() &&
+ isDeclInScope(Previous.getFoundDecl(), DC, S, false));
+
if (!getLangOpts().CPlusPlus) {
D.setRedeclaration(CheckVariableDeclaration(NewVD, Previous));
} else {
// 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:
// block scope declaration declares that same entity and
// receives the linkage of the previous declaration.
//
+ // C++11 [dcl.array]p3:
+ // If there is a preceding declaration of the entity in the same
+ // scope in which the bound was specified, an omitted array bound
+ // is taken to be the same as in that earlier 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
//
// 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 =
- Previous.empty() &&
- checkForConflictWithNonVisibleExternC(*this, NewVD, Previous);
+ // not in scope (in C++) or not visible (in C).
+ bool MergeTypeWithPrevious;
+ if (Previous.empty() &&
+ checkForConflictWithNonVisibleExternC(*this, NewVD, Previous))
+ MergeTypeWithPrevious = false;
+ else
+ MergeTypeWithPrevious =
+ !Previous.isShadowed() &&
+ (!getLangOpts().CPlusPlus || NewVD->isPreviousDeclInSameBlockScope() ||
+ !NewVD->getLexicalDeclContext()->isFunctionOrMethod());
// Filter out any non-conflicting previous declarations.
filterNonConflictingPreviousDecls(Context, NewVD, Previous);
if (!Previous.empty()) {
- MergeVarDecl(NewVD, Previous, PreviousWasHidden);
+ MergeVarDecl(NewVD, Previous, MergeTypeWithPrevious);
return true;
}
return false;
FilterLookupForScope(Previous, DC, S, shouldConsiderLinkage(NewFD),
isExplicitSpecialization ||
isFunctionTemplateSpecialization);
-
+
// Handle GNU asm-label extension (encoded as an attribute).
if (Expr *E = (Expr*) D.getAsmLabel()) {
// The parser guarantees this is a string.
if (!NewFD->isInvalidDecl())
D.setRedeclaration(CheckFunctionDeclaration(S, NewFD, Previous,
isExplicitSpecialization));
- // Make graceful recovery from an invalid redeclaration.
else if (!Previous.empty())
- D.setRedeclaration(true);
+ // Make graceful recovery from an invalid redeclaration.
+ D.setRedeclaration(true);
assert((NewFD->isInvalidDecl() || !D.isRedeclaration() ||
Previous.getResultKind() != LookupResult::FoundOverloaded) &&
"previous declaration set still overloaded");
assert(!NewFD->getResultType()->isVariablyModifiedType()
&& "Variably modified return types are not handled here");
+ // Determine whether the type of this function should be merged with
+ // a previous visible declaration. This never happens for functions in C++,
+ // and always happens in C if the previous declaration was visible.
+ bool MergeTypeWithPrevious = !getLangOpts().CPlusPlus &&
+ !Previous.isShadowed();
+
// Filter out any non-conflicting previous declarations.
filterNonConflictingPreviousDecls(Context, NewFD, Previous);
// declaration, and thus redeclares that entity...
Redeclaration = true;
OldDecl = Previous.getFoundDecl();
+ MergeTypeWithPrevious = false;
// ... except in the presence of __attribute__((overloadable)).
if (OldDecl->hasAttr<OverloadableAttr>()) {
if (Redeclaration) {
// NewFD and OldDecl represent declarations that need to be
// merged.
- if (MergeFunctionDecl(NewFD, OldDecl, S)) {
+ if (MergeFunctionDecl(NewFD, OldDecl, S, MergeTypeWithPrevious)) {
NewFD->setInvalidDecl();
return Redeclaration;
}
// If this is a redeclaration, check that the type we just deduced matches
// the previously declared type.
- if (VarDecl *Old = VDecl->getPreviousDecl())
- MergeVarDeclTypes(VDecl, Old, /*OldWasHidden*/ false);
+ if (VarDecl *Old = VDecl->getPreviousDecl()) {
+ // We never need to merge the type, because we cannot form an incomplete
+ // array of auto, nor deduce such a type.
+ MergeVarDeclTypes(VDecl, Old, /*MergeTypeWithPrevious*/false);
+ }
// Check the deduced type is valid for a variable declaration.
CheckVariableDeclarationType(VDecl);
assert(getLangOpts().CPlusPlus && "Can perform only C++ lookup");
DeclarationName Name = R.getLookupName();
+ Sema::LookupNameKind NameKind = R.getLookupKind();
// If this is the name of an implicitly-declared special member function,
// go through the scope stack to implicitly declare
//
UnqualUsingDirectiveSet UDirs;
bool VisitedUsingDirectives = false;
+ bool LeftStartingScope = false;
DeclContext *OutsideOfTemplateParamDC = 0;
for (; S && !isNamespaceOrTranslationUnitScope(S); S = S->getParent()) {
DeclContext *Ctx = static_cast<DeclContext*>(S->getEntity());
bool Found = false;
for (; I != IEnd && S->isDeclScope(*I); ++I) {
if (NamedDecl *ND = R.getAcceptableDecl(*I)) {
+ if (NameKind == LookupRedeclarationWithLinkage) {
+ // Determine whether this (or a previous) declaration is
+ // out-of-scope.
+ if (!LeftStartingScope && !Initial->isDeclScope(*I))
+ LeftStartingScope = true;
+
+ // If we found something outside of our starting scope that
+ // does not have linkage, skip it.
+ if (LeftStartingScope && !((*I)->hasLinkage())) {
+ R.setShadowed();
+ continue;
+ }
+ }
+
Found = true;
R.addDecl(ND);
}
return true;
}
- if (R.getLookupKind() == LookupLocalFriendName && !S->isClassScope()) {
+ if (NameKind == LookupLocalFriendName && !S->isClassScope()) {
// C++11 [class.friend]p11:
// If a friend declaration appears in a local class and the name
// specified is an unqualified name, a prior declaration is
if (!S) return false;
// If we are looking for members, no need to look into global/namespace scope.
- if (R.getLookupKind() == LookupMemberName)
+ if (NameKind == LookupMemberName)
return false;
// Collect UsingDirectiveDecls in all scopes, and recursively all
// If we found something outside of our starting scope that
// does not have linkage, skip it.
- if (LeftStartingScope && !((*I)->hasLinkage()))
+ if (LeftStartingScope && !((*I)->hasLinkage())) {
+ R.setShadowed();
continue;
+ }
}
else if (NameKind == LookupObjCImplicitSelfParam &&
!isa<ImplicitParamDecl>(*I))
// FIXME: This, and ForVarTemplate, is a hack that is probably unnecessary.
// We should use a simplified version of VisitVarDecl.
- VarDecl *VarInst = cast_or_null<VarDecl>(VisitVarDecl(Pattern, /*ForVarTemplate=*/true));
+ VarDecl *VarInst =
+ cast_or_null<VarDecl>(VisitVarDecl(Pattern, /*ForVarTemplate=*/ true));
DeclContext *DC = Owner;
NewVar->setInitStyle(OldVar->getInitStyle());
NewVar->setCXXForRangeDecl(OldVar->isCXXForRangeDecl());
NewVar->setConstexpr(OldVar->isConstexpr());
+ NewVar->setPreviousDeclInSameBlockScope(
+ OldVar->isPreviousDeclInSameBlockScope());
NewVar->setAccess(OldVar->getAccess());
if (!OldVar->isStaticDataMember()) {
if (NewVar->hasAttrs())
CheckAlignasUnderalignment(NewVar);
- // FIXME: In theory, we could have a previous declaration for variables that
- // are not static data members.
- // FIXME: having to fake up a LookupResult is dumb.
LookupResult Previous(*this, NewVar->getDeclName(), NewVar->getLocation(),
Sema::LookupOrdinaryName, Sema::ForRedeclaration);
- if (!isa<VarTemplateSpecializationDecl>(NewVar))
+ if (NewVar->getLexicalDeclContext()->isFunctionOrMethod() &&
+ OldVar->getPreviousDecl()) {
+ // We have a previous declaration. Use that one, so we merge with the
+ // right type.
+ if (NamedDecl *NewPrev = FindInstantiatedDecl(
+ NewVar->getLocation(), OldVar->getPreviousDecl(), TemplateArgs))
+ Previous.addDecl(NewPrev);
+ } else if (!isa<VarTemplateSpecializationDecl>(NewVar) &&
+ OldVar->hasLinkage())
LookupQualifiedName(Previous, NewVar->getDeclContext(), false);
CheckVariableDeclaration(NewVar, Previous);
VD->VarDeclBits.CXXForRangeDecl = Record[Idx++];
VD->VarDeclBits.ARCPseudoStrong = Record[Idx++];
VD->VarDeclBits.IsConstexpr = Record[Idx++];
+ VD->VarDeclBits.PreviousDeclInSameBlockScope = Record[Idx++];
VD->setCachedLinkage(Linkage(Record[Idx++]));
// Only true variables (not parameters or implicit parameters) can be merged.
Record.push_back(D->isCXXForRangeDecl());
Record.push_back(D->isARCPseudoStrong());
Record.push_back(D->isConstexpr());
+ Record.push_back(D->isPreviousDeclInSameBlockScope());
Record.push_back(D->getLinkageInternal());
if (D->getInit()) {
!isa<ParmVarDecl>(D) &&
!isa<VarTemplateSpecializationDecl>(D) &&
!D->isConstexpr() &&
+ !D->isPreviousDeclInSameBlockScope() &&
!SpecInfo)
AbbrevToUse = Writer.getDeclVarAbbrev();
Abv->Add(BitCodeAbbrevOp(0)); // isCXXForRangeDecl
Abv->Add(BitCodeAbbrevOp(0)); // isARCPseudoStrong
Abv->Add(BitCodeAbbrevOp(0)); // isConstexpr
+ Abv->Add(BitCodeAbbrevOp(0)); // isPrevDeclInSameScope
Abv->Add(BitCodeAbbrevOp(0)); // Linkage
Abv->Add(BitCodeAbbrevOp(0)); // HasInit
Abv->Add(BitCodeAbbrevOp(0)); // HasMemberSpecializationInfo
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isCXXForRangeDecl
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isARCPseudoStrong
Abv->Add(BitCodeAbbrevOp(0)); // isConstexpr
+ Abv->Add(BitCodeAbbrevOp(0)); // isPrevDeclInSameScope
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // Linkage
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // HasInit
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // HasMemberSpecInfo
-// RUN: %clang_cc1 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++1y %s
+
+// expected-no-diagnostics
// C++11 [basic.link]p6:
// The name of a function declared in block scope and the name
// 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 []'}}
+extern int same_entity;
+constexpr int *get1() {
+ int same_entity = 0; // not the same entity
+ {
+ extern int same_entity;
+ return &same_entity;
}
}
+static_assert(get1() == &same_entity, "failed to find previous decl");
-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 []'}}
+static int same_entity_2[3];
+constexpr int *get2() {
+ // This is a redeclaration of the same entity, even though it doesn't
+ // inherit the type of the prior declaration.
+ extern int same_entity_2[];
+ return same_entity_2;
}
+static_assert(get2() == same_entity_2, "failed to find previous decl");
-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 []'}}
+static int different_entities;
+constexpr int *get3() {
+ int different_entities = 0;
+ {
+ // FIXME: This is not a redeclaration of the prior entity, because
+ // it is not visible here. Under DR426, this is ill-formed, and without
+ // it, the static_assert below should fail.
+ extern int different_entities;
+ return &different_entities;
}
}
-
-
+static_assert(get3() == &different_entities, "failed to find previous decl");
--- /dev/null
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++1y -triple x86_64-linux-gnu %s
+
+// If there is a preceding declaration of the entity *in the same scope* in
+// which the bound was specified, an omitted array bound is taken to be the
+// same as in that earlier declaration
+
+// rdar://13535367
+namespace test0 {
+ extern "C" int array[];
+ void declare() { extern int array[100]; }
+ int value1 = sizeof(array); // expected-error {{invalid application of 'sizeof' to an incomplete type 'int []'}}
+ extern "C" int array[];
+ int value2 = sizeof(array); // expected-error {{invalid application of 'sizeof' to an incomplete type 'int []'}}
+}
+
+namespace test1 {
+ extern "C" int array[];
+ void test() {
+ { extern int array[100]; }
+ extern int array[];
+ int x = sizeof(array); // expected-error {{invalid application of 'sizeof' to an incomplete type 'int []'}}
+ }
+}
+
+namespace test2 {
+ void declare() { extern int array[100]; }
+ extern int array[];
+ int value = sizeof(array); // expected-error {{invalid application of 'sizeof' to an incomplete type 'int []'}}
+}
+
+namespace test3 {
+ void test() {
+ { extern int array[100]; }
+ extern int array[];
+ int x = sizeof(array); // expected-error {{invalid application of 'sizeof' to an incomplete type 'int []'}}
+ }
+}
+
+namespace test4 {
+ extern int array[];
+ void test() {
+ extern int array[100];
+ int x = sizeof(array);
+ }
+ int y = sizeof(array); // expected-error {{invalid application of 'sizeof' to an incomplete type 'int []'}}
+}
+
+namespace test5 {
+ void test() {
+ extern int array[100];
+ extern int array[];
+ int x = sizeof(array);
+ }
+}
+
+namespace test6 {
+ void test() {
+ extern int array[100];
+ {
+ extern int array[];
+ int x = sizeof(array); // expected-error {{invalid application of 'sizeof' to an incomplete type 'int []'}}
+ }
+ int y = sizeof(array);
+ }
+}
+
+namespace test7 {
+ extern int array[100];
+ void test() {
+ extern int array[];
+ int x = sizeof(array); // expected-error {{invalid application of 'sizeof' to an incomplete type 'int []'}}
+ }
+ int y = sizeof(array);
+}
+
+namespace dependent {
+ template<typename T> void f() {
+ extern int arr1[];
+ extern T arr1;
+ extern T arr2;
+ extern int arr2[];
+ static_assert(sizeof(arr1) == 12, "");
+ static_assert(sizeof(arr2) == 12, "");
+
+ // Use a failing test to ensure the type isn't considered dependent.
+ static_assert(sizeof(arr2) == 13, ""); // expected-error {{failed}}
+ }
+
+ void g() { f<int[3]>(); } // expected-note {{in instantiation of}}
+
+ template<typename T> void h1() {
+ extern T arr3;
+ {
+ int arr3;
+ {
+ extern int arr3[];
+ // Detected in template definition.
+ (void)sizeof(arr3); // expected-error {{incomplete}}
+ }
+ }
+ }
+
+ template<typename T> void h2() {
+ extern int arr4[3];
+ {
+ int arr4;
+ {
+ extern T arr4;
+ // Detected in template instantiation.
+ (void)sizeof(arr4); // expected-error {{incomplete}}
+ }
+ }
+ }
+
+ void i() {
+ h1<int[3]>();
+ h2<int[]>(); // expected-note {{in instantiation of}}
+ }
+
+ int arr5[3];
+ template<typename T> void j() {
+ extern T arr5;
+ extern T arr6;
+ (void)sizeof(arr5); // expected-error {{incomplete}}
+ (void)sizeof(arr6); // expected-error {{incomplete}}
+ }
+ int arr6[3];
+
+ void k() { j<int[]>(); } // expected-note {{in instantiation of}}
+
+ template<typename T, typename U> void l() {
+ extern T arrX; // expected-note {{previous}}
+ extern U arrX; // expected-error {{different type: 'int [4]' vs 'int [3]'}}
+ (void)sizeof(arrX); // expected-error {{incomplete}}
+ }
+
+ void m() {
+ l<int[], int[3]>(); // ok
+ l<int[3], int[]>(); // ok
+ l<int[3], int[3]>(); // ok
+ l<int[3], int[4]>(); // expected-note {{in instantiation of}}
+ l<int[], int[]>(); // expected-note {{in instantiation of}}
+ }
+
+ template<typename T> void n() {
+ extern T n_var;
+ }
+ template void n<int>();
+ // FIXME: Diagnose this!
+ float n_var;
+ template void n<double>();
+}
// Test this without pch.
// RUN: %clang_cc1 -std=c++11 -fcxx-exceptions -fexceptions -include %S/cxx-templates.h -verify %s -ast-dump -o -
-// RUN: %clang_cc1 -std=c++11 -fcxx-exceptions -fexceptions -include %S/cxx-templates.h %s -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 -std=c++11 -fcxx-exceptions -fexceptions -include %S/cxx-templates.h %s -emit-llvm -o - -DNO_ERRORS | FileCheck %s
// Test with pch.
// RUN: %clang_cc1 -std=c++11 -fcxx-exceptions -fexceptions -x c++-header -emit-pch -o %t %S/cxx-templates.h
// RUN: %clang_cc1 -std=c++11 -fcxx-exceptions -fexceptions -include-pch %t -verify %s -ast-dump -o -
-// RUN: %clang_cc1 -std=c++11 -fcxx-exceptions -fexceptions -include-pch %t %s -emit-llvm -o - -error-on-deserialized-decl doNotDeserialize | FileCheck %s
+// RUN: %clang_cc1 -std=c++11 -fcxx-exceptions -fexceptions -include-pch %t %s -emit-llvm -o - -error-on-deserialized-decl doNotDeserialize -DNO_ERRORS | FileCheck %s
// Test with modules.
// RUN: %clang_cc1 -std=c++11 -fcxx-exceptions -fexceptions -fmodules -x c++-header -emit-pch -o %t %S/cxx-templates.h
// RUN: %clang_cc1 -std=c++11 -fcxx-exceptions -fexceptions -fmodules -include-pch %t -verify %s -ast-dump -o -
-// RUN: %clang_cc1 -std=c++11 -fcxx-exceptions -fexceptions -fmodules -include-pch %t %s -emit-llvm -o - -error-on-deserialized-decl doNotDeserialize | FileCheck %s
+// RUN: %clang_cc1 -std=c++11 -fcxx-exceptions -fexceptions -fmodules -include-pch %t %s -emit-llvm -o - -error-on-deserialized-decl doNotDeserialize -DNO_ERRORS | FileCheck %s
// Test with pch and delayed template parsing.
// RUN: %clang_cc1 -std=c++11 -fcxx-exceptions -fdelayed-template-parsing -fexceptions -x c++-header -emit-pch -o %t %S/cxx-templates.h
// RUN: %clang_cc1 -std=c++11 -fcxx-exceptions -fdelayed-template-parsing -fexceptions -include-pch %t -verify %s -ast-dump -o -
-// RUN: %clang_cc1 -std=c++11 -fcxx-exceptions -fdelayed-template-parsing -fexceptions -include-pch %t %s -emit-llvm -o - | FileCheck %s
-
-// expected-no-diagnostics
+// RUN: %clang_cc1 -std=c++11 -fcxx-exceptions -fdelayed-template-parsing -fexceptions -include-pch %t %s -emit-llvm -o - -DNO_ERRORS | FileCheck %s
// CHECK: define weak_odr void @_ZN2S4IiE1mEv
// CHECK: define linkonce_odr void @_ZN2S3IiE1mEv
extern std::valarray<int> x;
std::valarray<int> y(x);
}
+
+#ifndef NO_ERRORS
+// expected-error@cxx-templates.h:305 {{incomplete}}
+template int local_extern::f<int[]>(); // expected-note {{in instantiation of}}
+#endif
+template int local_extern::g<int[]>();
};
}
}
+
+namespace local_extern {
+ template<typename T> int f() {
+ extern int arr[3];
+ {
+ extern T arr;
+ return sizeof(arr);
+ }
+ }
+ template<typename T> int g() {
+ extern int arr[3];
+ extern T arr;
+ return sizeof(arr);
+ }
+}
void *(*_malloc)() = &malloc;
float *(*_calloc)() = &calloc;
}
+
+void test6() {
+ extern int test6_array1[100];
+ extern int test6_array2[100];
+ void test6_fn1(int*);
+ void test6_fn2(int*);
+ {
+ // Types are only merged from visible declarations.
+ char test6_array2;
+ char test6_fn2;
+ {
+ extern int test6_array1[];
+ extern int test6_array2[];
+ (void)sizeof(test6_array1); // ok
+ (void)sizeof(test6_array2); // expected-error {{incomplete type}}
+
+ void test6_fn1();
+ void test6_fn2();
+ test6_fn1(1.2); // expected-error {{passing 'double' to parameter of incompatible type 'int *'}}
+ // FIXME: This is valid, but we should warn on it.
+ test6_fn2(1.2);
+ }
+ }
+}
namespace N {
void f1() {
void foo(int); // okay
+ void bar(int);
}
- // FIXME: we shouldn't even need this declaration to detect errors
- // below.
- void foo(int); // expected-note{{previous declaration is here}}
+ void foo(int); // expected-note 2{{previous declaration is here}}
void f2() {
- int foo(int); // expected-error{{functions that differ only in their return type cannot be overloaded}}
+ int foo(int); // expected-error {{functions that differ only in their return type cannot be overloaded}}
+ // FIXME: We should be able to diagnose the conflict between this
+ // declaration of 'bar' and the previous one, even though they come
+ // from different lexical scopes.
+ int bar(int); // expected-note {{previous declaration is here}}
+ int baz(int); // expected-note {{previous declaration is here}}
{
int foo;
+ int bar;
+ int baz;
{
- // FIXME: should diagnose this because it's incompatible with
- // N::foo. However, name lookup isn't properly "skipping" the
- // "int foo" above.
- float foo(int);
+ float foo(int); // expected-error {{functions that differ only in their return type cannot be overloaded}}
+ float bar(int); // expected-error {{functions that differ only in their return type cannot be overloaded}}
+ float baz(int); // expected-error {{functions that differ only in their return type cannot be overloaded}}
}
}
}