Implement redeclaration checking and hiding semantics for using declarations. There

are a couple of O(n^2) operations in this, some analogous to the usual O(n^2)
redeclaration problem and some not.  In particular, retroactively removing
shadow declarations when they're hidden by later decls is pretty unfortunate.
I'm not yet convinced it's worse than the alternative, though.

llvm-svn: 91045
This commit is contained in:
John McCall 2009-12-10 09:41:52 +00:00
parent e919e382a4
commit 84d8767c15
14 changed files with 638 additions and 74 deletions

View File

@ -916,6 +916,9 @@ public:
/// only happens with friends.
void addHiddenDecl(Decl *D);
/// @brief Removes a declaration from this context.
void removeDecl(Decl *D);
/// lookup_iterator - An iterator that provides access to the results
/// of looking up a name within this context.
typedef NamedDecl **lookup_iterator;

View File

@ -131,6 +131,24 @@ public:
return DK == DK_DeclID || DK == DK_ID_Vector;
}
void remove(NamedDecl *D) {
assert(!isNull() && "removing from empty list");
if (NamedDecl *Singleton = getAsDecl()) {
assert(Singleton == D && "list is different singleton");
Data = 0;
return;
}
VectorTy &Vec = *getAsVector();
VectorTy::iterator I = std::find(Vec.begin(), Vec.end(),
reinterpret_cast<uintptr_t>(D));
assert(I != Vec.end() && "list does not contain decl");
Vec.erase(I);
assert(std::find(Vec.begin(), Vec.end(), reinterpret_cast<uintptr_t>(D))
== Vec.end() && "list still contains decl");
}
/// getLookupResult - Return an array of all the decls that this list
/// represents.
DeclContext::lookup_result getLookupResult(ASTContext &Context) {

View File

@ -121,8 +121,13 @@ def err_using_decl_destructor : Error<
"using declaration can not refer to a destructor">;
def err_using_decl_template_id : Error<
"using declaration can not refer to a template specialization">;
def note_using_decl_target : Note<
"target of using declaration">;
def note_using_decl_target : Note<"target of using declaration">;
def note_using_decl_conflict : Note<"conflicting declaration">;
def err_using_decl_redeclaration : Error<"redeclaration of using decl">;
def note_previous_using_decl : Note<"previous using decl">;
def err_using_decl_conflict : Error<
"%select{function|non-function}0 target of using declaration conflicts "
"with %select{function|non-function}1 declaration already in scope">;
def err_invalid_thread : Error<
"'__thread' is only allowed on variable declarations">;

View File

@ -636,6 +636,46 @@ bool DeclContext::decls_empty() const {
return !FirstDecl;
}
void DeclContext::removeDecl(Decl *D) {
assert(D->getLexicalDeclContext() == this &&
"decl being removed from non-lexical context");
assert((D->NextDeclInContext || D == LastDecl) &&
"decl is not in decls list");
// Remove D from the decl chain. This is O(n) but hopefully rare.
if (D == FirstDecl) {
if (D == LastDecl)
FirstDecl = LastDecl = 0;
else
FirstDecl = D->NextDeclInContext;
} else {
for (Decl *I = FirstDecl; true; I = I->NextDeclInContext) {
assert(I && "decl not found in linked list");
if (I->NextDeclInContext == D) {
I->NextDeclInContext = D->NextDeclInContext;
if (D == LastDecl) LastDecl = I;
break;
}
}
}
// Mark that D is no longer in the decl chain.
D->NextDeclInContext = 0;
// Remove D from the lookup table if necessary.
if (isa<NamedDecl>(D)) {
NamedDecl *ND = cast<NamedDecl>(D);
void *OpaqueMap = getPrimaryContext()->LookupPtr;
if (!OpaqueMap) return;
StoredDeclsMap *Map = static_cast<StoredDeclsMap*>(OpaqueMap);
StoredDeclsMap::iterator Pos = Map->find(ND->getDeclName());
assert(Pos != Map->end() && "no lookup entry for decl");
Pos->second.remove(ND);
}
}
void DeclContext::addHiddenDecl(Decl *D) {
assert(D->getLexicalDeclContext() == this &&
"Decl inserted into wrong lexical context");

View File

@ -616,7 +616,8 @@ public:
bool IsFunctionDefinition,
bool &Redeclaration);
void AddOverriddenMethods(CXXRecordDecl *DC, CXXMethodDecl *MD);
void CheckFunctionDeclaration(FunctionDecl *NewFD, LookupResult &Previous,
void CheckFunctionDeclaration(Scope *S,
FunctionDecl *NewFD, LookupResult &Previous,
bool IsExplicitSpecialization,
bool &Redeclaration,
bool &OverloadableAttrRequired);
@ -839,7 +840,7 @@ public:
Ovl_NonFunction
};
OverloadKind CheckOverload(FunctionDecl *New,
LookupResult &OldDecls,
const LookupResult &OldDecls,
NamedDecl *&OldDecl);
bool IsOverload(FunctionDecl *New, FunctionDecl *Old);
@ -1123,6 +1124,10 @@ public:
/// namespace alias definition, ignoring non-namespace names (C++
/// [basic.lookup.udir]p1).
LookupNamespaceName,
/// Look up all declarations in a scope with the given name,
/// including resolved using declarations. This is appropriate
/// for checking redeclarations for a using declaration.
LookupUsingDeclName,
/// Look up an ordinary name that is going to be redeclared as a
/// name with linkage. This lookup ignores any declarations that
/// are outside of the current scope unless they have linkage. See
@ -1154,6 +1159,7 @@ public:
case Sema::LookupTagName:
case Sema::LookupMemberName:
case Sema::LookupRedeclarationWithLinkage: // FIXME: check linkage, scoping
case Sema::LookupUsingDeclName:
case Sema::LookupObjCProtocolName:
case Sema::LookupObjCImplementationName:
case Sema::LookupObjCCategoryImplName:
@ -1739,9 +1745,17 @@ public:
SourceLocation IdentLoc,
IdentifierInfo *Ident);
UsingShadowDecl *BuildUsingShadowDecl(Scope *S, AccessSpecifier AS,
UsingDecl *UD, NamedDecl *Target);
void HideUsingShadowDecl(Scope *S, UsingShadowDecl *Shadow);
bool CheckUsingShadowDecl(UsingDecl *UD, NamedDecl *Target,
const LookupResult &PreviousDecls);
UsingShadowDecl *BuildUsingShadowDecl(Scope *S, UsingDecl *UD,
NamedDecl *Target);
bool CheckUsingDeclRedeclaration(SourceLocation UsingLoc,
bool isTypeName,
const CXXScopeSpec &SS,
SourceLocation NameLoc,
const LookupResult &Previous);
bool CheckUsingDeclQualifier(SourceLocation UsingLoc,
const CXXScopeSpec &SS,
SourceLocation NameLoc);

View File

@ -3015,7 +3015,7 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,
// Perform semantic checking on the function declaration.
bool OverloadableAttrRequired = false; // FIXME: HACK!
CheckFunctionDeclaration(NewFD, Previous, isExplicitSpecialization,
CheckFunctionDeclaration(S, NewFD, Previous, isExplicitSpecialization,
Redeclaration, /*FIXME:*/OverloadableAttrRequired);
assert((NewFD->isInvalidDecl() || !Redeclaration ||
@ -3137,7 +3137,7 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,
/// an explicit specialization of the previous declaration.
///
/// This sets NewFD->isInvalidDecl() to true if there was an error.
void Sema::CheckFunctionDeclaration(FunctionDecl *NewFD,
void Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
LookupResult &Previous,
bool IsExplicitSpecialization,
bool &Redeclaration,
@ -3202,8 +3202,11 @@ void Sema::CheckFunctionDeclaration(FunctionDecl *NewFD,
switch (CheckOverload(NewFD, Previous, OldDecl)) {
case Ovl_Match:
// FIXME: hide or conflict with using shadow decls as appropriate
Redeclaration = !isa<UsingShadowDecl>(OldDecl);
Redeclaration = true;
if (isa<UsingShadowDecl>(OldDecl) && CurContext->isRecord()) {
HideUsingShadowDecl(S, cast<UsingShadowDecl>(OldDecl));
Redeclaration = false;
}
break;
case Ovl_NonFunction:

View File

@ -2925,12 +2925,146 @@ Sema::DeclPtrTy Sema::ActOnUsingDeclaration(Scope *S,
return DeclPtrTy::make(UD);
}
/// Determines whether to create a using shadow decl for a particular
/// decl, given the set of decls existing prior to this using lookup.
bool Sema::CheckUsingShadowDecl(UsingDecl *Using, NamedDecl *Orig,
const LookupResult &Previous) {
// Diagnose finding a decl which is not from a base class of the
// current class. We do this now because there are cases where this
// function will silently decide not to build a shadow decl, which
// will pre-empt further diagnostics.
//
// We don't need to do this in C++0x because we do the check once on
// the qualifier.
//
// FIXME: diagnose the following if we care enough:
// struct A { int foo; };
// struct B : A { using A::foo; };
// template <class T> struct C : A {};
// template <class T> struct D : C<T> { using B::foo; } // <---
// This is invalid (during instantiation) in C++03 because B::foo
// resolves to the using decl in B, which is not a base class of D<T>.
// We can't diagnose it immediately because C<T> is an unknown
// specialization. The UsingShadowDecl in D<T> then points directly
// to A::foo, which will look well-formed when we instantiate.
// The right solution is to not collapse the shadow-decl chain.
if (!getLangOptions().CPlusPlus0x && CurContext->isRecord()) {
DeclContext *OrigDC = Orig->getDeclContext();
// Handle enums and anonymous structs.
if (isa<EnumDecl>(OrigDC)) OrigDC = OrigDC->getParent();
CXXRecordDecl *OrigRec = cast<CXXRecordDecl>(OrigDC);
while (OrigRec->isAnonymousStructOrUnion())
OrigRec = cast<CXXRecordDecl>(OrigRec->getDeclContext());
if (cast<CXXRecordDecl>(CurContext)->isProvablyNotDerivedFrom(OrigRec)) {
if (OrigDC == CurContext) {
Diag(Using->getLocation(),
diag::err_using_decl_nested_name_specifier_is_current_class)
<< Using->getNestedNameRange();
Diag(Orig->getLocation(), diag::note_using_decl_target);
return true;
}
Diag(Using->getNestedNameRange().getBegin(),
diag::err_using_decl_nested_name_specifier_is_not_base_class)
<< Using->getTargetNestedNameDecl()
<< cast<CXXRecordDecl>(CurContext)
<< Using->getNestedNameRange();
Diag(Orig->getLocation(), diag::note_using_decl_target);
return true;
}
}
if (Previous.empty()) return false;
NamedDecl *Target = Orig;
if (isa<UsingShadowDecl>(Target))
Target = cast<UsingShadowDecl>(Target)->getTargetDecl();
if (Target->isFunctionOrFunctionTemplate()) {
FunctionDecl *FD;
if (isa<FunctionTemplateDecl>(Target))
FD = cast<FunctionTemplateDecl>(Target)->getTemplatedDecl();
else
FD = cast<FunctionDecl>(Target);
NamedDecl *OldDecl = 0;
switch (CheckOverload(FD, Previous, OldDecl)) {
case Ovl_Overload:
return false;
case Ovl_NonFunction:
Diag(Using->getLocation(), diag::err_using_decl_conflict)
<< 0 // target decl is a function
<< 1; // other decl is not a function
break;
// We found a decl with the exact signature.
case Ovl_Match:
if (isa<UsingShadowDecl>(OldDecl)) {
// Silently ignore the possible conflict.
return false;
}
// If we're in a record, we want to hide the target, so we
// return true (without a diagnostic) to tell the caller not to
// build a shadow decl.
if (CurContext->isRecord())
return true;
// If we're not in a record, this is an error.
Diag(Using->getLocation(), diag::err_using_decl_conflict)
<< 0 // target decl is a function
<< 0; // other decl is a function
break;
}
Diag(Target->getLocation(), diag::note_using_decl_target);
Diag(OldDecl->getLocation(), diag::note_using_decl_conflict);
return true;
}
// Target is not a function.
// If the target happens to be one of the previous declarations, we
// don't have a conflict.
NamedDecl *NonTag = 0, *Tag = 0;
for (LookupResult::iterator I = Previous.begin(), E = Previous.end();
I != E; ++I) {
NamedDecl *D = (*I)->getUnderlyingDecl();
if (D->getCanonicalDecl() == Target->getCanonicalDecl())
return false;
(isa<TagDecl>(D) ? Tag : NonTag) = D;
}
if (isa<TagDecl>(Target)) {
// No conflict between a tag and a non-tag.
if (!Tag) return false;
Diag(Using->getLocation(), diag::err_using_decl_conflict)
<< 1 << 1; // both non-functions
Diag(Target->getLocation(), diag::note_using_decl_target);
Diag(Tag->getLocation(), diag::note_using_decl_conflict);
return true;
}
// No conflict between a tag and a non-tag.
if (!NonTag) return false;
Diag(Using->getLocation(), diag::err_using_decl_conflict)
<< 1 // target not a function
<< int(NonTag->isFunctionOrFunctionTemplate());
Diag(Target->getLocation(), diag::note_using_decl_target);
Diag(NonTag->getLocation(), diag::note_using_decl_conflict);
return true;
}
/// Builds a shadow declaration corresponding to a 'using' declaration.
UsingShadowDecl *Sema::BuildUsingShadowDecl(Scope *S,
AccessSpecifier AS,
UsingDecl *UD,
NamedDecl *Orig) {
// FIXME: diagnose hiding, collisions
// If we resolved to another shadow declaration, just coalesce them.
NamedDecl *Target = Orig;
@ -2948,45 +3082,56 @@ UsingShadowDecl *Sema::BuildUsingShadowDecl(Scope *S,
PushOnScopeChains(Shadow, S);
else
CurContext->addDecl(Shadow);
Shadow->setAccess(AS);
Shadow->setAccess(UD->getAccess());
if (Orig->isInvalidDecl() || UD->isInvalidDecl())
Shadow->setInvalidDecl();
// If we haven't already declared the shadow decl invalid, check
// whether the decl comes from a base class of the current class.
// We don't have to do this in C++0x because we do the check once on
// the qualifier.
else if (!getLangOptions().CPlusPlus0x && CurContext->isRecord()) {
DeclContext *OrigDC = Orig->getDeclContext();
return Shadow;
}
// Handle enums and anonymous structs.
if (isa<EnumDecl>(OrigDC)) OrigDC = OrigDC->getParent();
CXXRecordDecl *OrigRec = cast<CXXRecordDecl>(OrigDC);
while (OrigRec->isAnonymousStructOrUnion())
OrigRec = cast<CXXRecordDecl>(OrigRec->getDeclContext());
/// Hides a using shadow declaration. This is required by the current
/// using-decl implementation when a resolvable using declaration in a
/// class is followed by a declaration which would hide or override
/// one or more of the using decl's targets; for example:
///
/// struct Base { void foo(int); };
/// struct Derived : Base {
/// using Base::foo;
/// void foo(int);
/// };
///
/// The governing language is C++03 [namespace.udecl]p12:
///
/// When a using-declaration brings names from a base class into a
/// derived class scope, member functions in the derived class
/// override and/or hide member functions with the same name and
/// parameter types in a base class (rather than conflicting).
///
/// There are two ways to implement this:
/// (1) optimistically create shadow decls when they're not hidden
/// by existing declarations, or
/// (2) don't create any shadow decls (or at least don't make them
/// visible) until we've fully parsed/instantiated the class.
/// The problem with (1) is that we might have to retroactively remove
/// a shadow decl, which requires several O(n) operations because the
/// decl structures are (very reasonably) not designed for removal.
/// (2) avoids this but is very fiddly and phase-dependent.
void Sema::HideUsingShadowDecl(Scope *S, UsingShadowDecl *Shadow) {
// Remove it from the DeclContext...
Shadow->getDeclContext()->removeDecl(Shadow);
if (cast<CXXRecordDecl>(CurContext)->isProvablyNotDerivedFrom(OrigRec)) {
if (OrigDC == CurContext) {
Diag(UD->getLocation(),
diag::err_using_decl_nested_name_specifier_is_current_class)
<< UD->getNestedNameRange();
Diag(Orig->getLocation(), diag::note_using_decl_target);
Shadow->setInvalidDecl();
return Shadow;
}
Diag(UD->getNestedNameRange().getBegin(),
diag::err_using_decl_nested_name_specifier_is_not_base_class)
<< UD->getTargetNestedNameDecl()
<< cast<CXXRecordDecl>(CurContext)
<< UD->getNestedNameRange();
Diag(Orig->getLocation(), diag::note_using_decl_target);
return Shadow;
}
// ...and the scope, if applicable...
if (S) {
S->RemoveDecl(DeclPtrTy::make(static_cast<Decl*>(Shadow)));
IdResolver.RemoveDecl(Shadow);
}
return Shadow;
// ...and the using decl.
Shadow->getUsingDecl()->removeShadowDecl(Shadow);
// TODO: complain somehow if Shadow was used. It shouldn't
// be possible for this to happen, because
}
/// Builds a using declaration.
@ -3014,9 +3159,35 @@ NamedDecl *Sema::BuildUsingDeclaration(Scope *S, AccessSpecifier AS,
return 0;
}
// Do the redeclaration lookup in the current scope.
LookupResult Previous(*this, Name, IdentLoc, LookupUsingDeclName,
ForRedeclaration);
Previous.setHideTags(false);
if (S) {
LookupName(Previous, S);
// It is really dumb that we have to do this.
LookupResult::Filter F = Previous.makeFilter();
while (F.hasNext()) {
NamedDecl *D = F.next();
if (!isDeclInScope(D, CurContext, S))
F.erase();
}
F.done();
} else {
assert(IsInstantiation && "no scope in non-instantiation");
assert(CurContext->isRecord() && "scope not record in instantiation");
LookupQualifiedName(Previous, CurContext);
}
NestedNameSpecifier *NNS =
static_cast<NestedNameSpecifier *>(SS.getScopeRep());
// Check for invalid redeclarations.
if (CheckUsingDeclRedeclaration(UsingLoc, IsTypeName, SS, IdentLoc, Previous))
return 0;
// Check for bad qualifiers.
if (CheckUsingDeclQualifier(UsingLoc, SS, IdentLoc))
return 0;
@ -3106,12 +3277,71 @@ NamedDecl *Sema::BuildUsingDeclaration(Scope *S, AccessSpecifier AS,
return UD;
}
for (LookupResult::iterator I = R.begin(), E = R.end(); I != E; ++I)
BuildUsingShadowDecl(S, AS, UD, *I);
for (LookupResult::iterator I = R.begin(), E = R.end(); I != E; ++I) {
if (!CheckUsingShadowDecl(UD, *I, Previous))
BuildUsingShadowDecl(S, UD, *I);
}
return UD;
}
/// Checks that the given using declaration is not an invalid
/// redeclaration. Note that this is checking only for the using decl
/// itself, not for any ill-formedness among the UsingShadowDecls.
bool Sema::CheckUsingDeclRedeclaration(SourceLocation UsingLoc,
bool isTypeName,
const CXXScopeSpec &SS,
SourceLocation NameLoc,
const LookupResult &Prev) {
// C++03 [namespace.udecl]p8:
// C++0x [namespace.udecl]p10:
// A using-declaration is a declaration and can therefore be used
// repeatedly where (and only where) multiple declarations are
// allowed.
// That's only in file contexts.
if (CurContext->getLookupContext()->isFileContext())
return false;
NestedNameSpecifier *Qual
= static_cast<NestedNameSpecifier*>(SS.getScopeRep());
for (LookupResult::iterator I = Prev.begin(), E = Prev.end(); I != E; ++I) {
NamedDecl *D = *I;
bool DTypename;
NestedNameSpecifier *DQual;
if (UsingDecl *UD = dyn_cast<UsingDecl>(D)) {
DTypename = UD->isTypeName();
DQual = UD->getTargetNestedNameDecl();
} else if (UnresolvedUsingValueDecl *UD
= dyn_cast<UnresolvedUsingValueDecl>(D)) {
DTypename = false;
DQual = UD->getTargetNestedNameSpecifier();
} else if (UnresolvedUsingTypenameDecl *UD
= dyn_cast<UnresolvedUsingTypenameDecl>(D)) {
DTypename = true;
DQual = UD->getTargetNestedNameSpecifier();
} else continue;
// using decls differ if one says 'typename' and the other doesn't.
// FIXME: non-dependent using decls?
if (isTypeName != DTypename) continue;
// using decls differ if they name different scopes (but note that
// template instantiation can cause this check to trigger when it
// didn't before instantiation).
if (Context.getCanonicalNestedNameSpecifier(Qual) !=
Context.getCanonicalNestedNameSpecifier(DQual))
continue;
Diag(NameLoc, diag::err_using_decl_redeclaration) << SS.getRange();
Diag(D->getLocation(), diag::note_previous_using_decl);
return true;
}
return false;
}
/// Checks that the given nested-name qualifier used in a using decl
/// in the current context is appropriately related to the current

View File

@ -222,6 +222,11 @@ getIdentifierNamespacesFromLookupNameKind(Sema::LookupNameKind NameKind,
IDNS = Decl::IDNS_Ordinary | Decl::IDNS_Tag | Decl::IDNS_Member;
break;
case Sema::LookupUsingDeclName:
IDNS = Decl::IDNS_Ordinary | Decl::IDNS_Tag
| Decl::IDNS_Member | Decl::IDNS_Using;
break;
case Sema::LookupObjCProtocolName:
IDNS = Decl::IDNS_ObjCProtocol;
break;
@ -245,7 +250,7 @@ void LookupResult::deletePaths(CXXBasePaths *Paths) {
/// Resolves the result kind of this lookup.
void LookupResult::resolveKind() {
unsigned N = Decls.size();
// Fast case: no possible ambiguity.
if (N == 0) {
assert(ResultKind == NotFound);
@ -613,6 +618,7 @@ bool Sema::LookupName(LookupResult &R, Scope *S, bool AllowBuiltinCreation) {
case Sema::LookupOperatorName:
case Sema::LookupNestedNameSpecifierName:
case Sema::LookupNamespaceName:
case Sema::LookupUsingDeclName:
assert(false && "C does not perform these kinds of name lookup");
break;
@ -928,6 +934,9 @@ bool Sema::LookupQualifiedName(LookupResult &R, DeclContext *LookupCtx) {
case LookupTagName:
BaseCallback = &CXXRecordDecl::FindTagMember;
break;
case LookupUsingDeclName:
// This lookup is for redeclarations only.
case LookupOperatorName:
case LookupNamespaceName:

View File

@ -287,7 +287,8 @@ void ImplicitConversionSequence::DebugPrint() const {
// signature), IsOverload returns false and MatchedDecl will be set to
// point to the FunctionDecl for #2.
Sema::OverloadKind
Sema::CheckOverload(FunctionDecl *New, LookupResult &Old, NamedDecl *&Match) {
Sema::CheckOverload(FunctionDecl *New, const LookupResult &Old,
NamedDecl *&Match) {
for (LookupResult::iterator I = Old.begin(), E = Old.end();
I != E; ++I) {
NamedDecl *OldD = (*I)->getUnderlyingDecl();
@ -301,12 +302,18 @@ Sema::CheckOverload(FunctionDecl *New, LookupResult &Old, NamedDecl *&Match) {
Match = *I;
return Ovl_Match;
}
} else if (!isa<UnresolvedUsingValueDecl>(OldD)) {
} else if (isa<UsingDecl>(OldD) || isa<TagDecl>(OldD)) {
// We can overload with these, which can show up when doing
// redeclaration checks for UsingDecls.
assert(Old.getLookupKind() == LookupUsingDeclName);
} else if (isa<UnresolvedUsingValueDecl>(OldD)) {
// Optimistically assume that an unresolved using decl will
// overload; if it doesn't, we'll have to diagnose during
// template instantiation.
} else {
// (C++ 13p1):
// Only function declarations can be overloaded; object and type
// declarations cannot be overloaded.
// But we permit unresolved using value decls and diagnose the error
// during template instantiation.
Match = *I;
return Ovl_NonFunction;
}

View File

@ -708,7 +708,8 @@ Decl *TemplateDeclInstantiator::VisitCXXRecordDecl(CXXRecordDecl *D) {
Previous.clear();
}
SemaRef.CheckFunctionDeclaration(Function, Previous, false, Redeclaration,
SemaRef.CheckFunctionDeclaration(/*Scope*/ 0, Function, Previous,
false, Redeclaration,
/*FIXME:*/OverloadableAttrRequired);
// If the original function was part of a friend declaration,
@ -868,7 +869,7 @@ TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D,
bool Redeclaration = false;
bool OverloadableAttrRequired = false;
SemaRef.CheckFunctionDeclaration(Method, Previous, false, Redeclaration,
SemaRef.CheckFunctionDeclaration(0, Method, Previous, false, Redeclaration,
/*FIXME:*/OverloadableAttrRequired);
if (D->isPure())
@ -1040,6 +1041,14 @@ Decl *TemplateDeclInstantiator::VisitUsingDecl(UsingDecl *D) {
// The nested name specifier is non-dependent, so no transformation
// is required.
// We only need to do redeclaration lookups if we're in a class
// scope (in fact, it's not really even possible in non-class
// scopes).
bool CheckRedeclaration = Owner->isRecord();
LookupResult Prev(SemaRef, D->getDeclName(), D->getLocation(),
Sema::LookupUsingDeclName, Sema::ForRedeclaration);
UsingDecl *NewUD = UsingDecl::Create(SemaRef.Context, Owner,
D->getLocation(),
D->getNestedNameRange(),
@ -1051,34 +1060,55 @@ Decl *TemplateDeclInstantiator::VisitUsingDecl(UsingDecl *D) {
CXXScopeSpec SS;
SS.setScopeRep(D->getTargetNestedNameDecl());
SS.setRange(D->getNestedNameRange());
if (SemaRef.CheckUsingDeclQualifier(D->getUsingLocation(), SS,
if (CheckRedeclaration) {
Prev.setHideTags(false);
SemaRef.LookupQualifiedName(Prev, Owner);
// Check for invalid redeclarations.
if (SemaRef.CheckUsingDeclRedeclaration(D->getUsingLocation(),
D->isTypeName(), SS,
D->getLocation(), Prev))
NewUD->setInvalidDecl();
}
if (!NewUD->isInvalidDecl() &&
SemaRef.CheckUsingDeclQualifier(D->getUsingLocation(), SS,
D->getLocation()))
NewUD->setInvalidDecl();
SemaRef.Context.setInstantiatedFromUsingDecl(NewUD, D);
NewUD->setAccess(D->getAccess());
Owner->addDecl(NewUD);
// We'll transform the UsingShadowDecls as we reach them.
// Don't process the shadow decls for an invalid decl.
if (NewUD->isInvalidDecl())
return NewUD;
// Process the shadow decls.
for (UsingDecl::shadow_iterator I = D->shadow_begin(), E = D->shadow_end();
I != E; ++I) {
UsingShadowDecl *Shadow = *I;
NamedDecl *InstTarget =
cast<NamedDecl>(SemaRef.FindInstantiatedDecl(Shadow->getTargetDecl(),
TemplateArgs));
if (CheckRedeclaration &&
SemaRef.CheckUsingShadowDecl(NewUD, InstTarget, Prev))
continue;
UsingShadowDecl *InstShadow
= SemaRef.BuildUsingShadowDecl(/*Scope*/ 0, NewUD, InstTarget);
SemaRef.Context.setInstantiatedFromUsingShadowDecl(InstShadow, Shadow);
}
return NewUD;
}
Decl *TemplateDeclInstantiator::VisitUsingShadowDecl(UsingShadowDecl *D) {
UsingDecl *InstUsing =
cast<UsingDecl>(SemaRef.FindInstantiatedDecl(D->getUsingDecl(),
TemplateArgs));
NamedDecl *InstTarget =
cast<NamedDecl>(SemaRef.FindInstantiatedDecl(D->getTargetDecl(),
TemplateArgs));
UsingShadowDecl *InstD = SemaRef.BuildUsingShadowDecl(/*Scope*/ 0,
D->getAccess(),
InstUsing,
InstTarget);
SemaRef.Context.setInstantiatedFromUsingShadowDecl(InstD, D);
return InstD;
// Ignore these; we handle them in bulk when processing the UsingDecl.
return 0;
}
Decl * TemplateDeclInstantiator
@ -2087,7 +2117,10 @@ NamedDecl *Sema::FindInstantiatedDecl(NamedDecl *D,
ParentDC->decls_end());
}
assert(Result && "Unable to find instantiation of declaration!");
// UsingShadowDecls can instantiate to nothing because of using hiding.
assert((Result || isa<UsingShadowDecl>(D))
&& "Unable to find instantiation of declaration!");
D = Result;
}

View File

@ -4506,8 +4506,14 @@ TreeTransform<Derived>::TransformUnresolvedLookupExpr(
for (UnresolvedLookupExpr::decls_iterator I = Old->decls_begin(),
E = Old->decls_end(); I != E; ++I) {
NamedDecl *InstD = static_cast<NamedDecl*>(getDerived().TransformDecl(*I));
if (!InstD)
return SemaRef.ExprError();
if (!InstD) {
// Silently ignore these if a UsingShadowDecl instantiated to nothing.
// This can happen because of dependent hiding.
if (isa<UsingShadowDecl>(*I))
continue;
else
return SemaRef.ExprError();
}
// Expand using declarations.
if (isa<UsingDecl>(InstD)) {
@ -4913,8 +4919,14 @@ TreeTransform<Derived>::TransformUnresolvedMemberExpr(UnresolvedMemberExpr *Old)
for (UnresolvedMemberExpr::decls_iterator I = Old->decls_begin(),
E = Old->decls_end(); I != E; ++I) {
NamedDecl *InstD = static_cast<NamedDecl*>(getDerived().TransformDecl(*I));
if (!InstD)
return SemaRef.ExprError();
if (!InstD) {
// Silently ignore these if a UsingShadowDecl instantiated to nothing.
// This can happen because of dependent hiding.
if (isa<UsingShadowDecl>(*I))
continue;
else
return SemaRef.ExprError();
}
// Expand using declarations.
if (isa<UsingDecl>(InstD)) {

View File

@ -0,0 +1,19 @@
// RUN: clang -fsyntax-only -verify %s
namespace test0 {
namespace ns0 {
class tag;
int tag();
}
namespace ns1 {
using ns0::tag;
}
namespace ns2 {
using ns0::tag;
}
using ns1::tag;
using ns2::tag;
}

View File

@ -1,6 +1,13 @@
// RUN: clang -fsyntax-only -verify %s
// C++03 [namespace.udecl]p12:
// When a using-declaration brings names from a base class into a
// derived class scope, member functions in the derived class
// override and/or hide member functions with the same name and
// parameter types in a base class (rather than conflicting).
// PR5727
// This just shouldn't crash.
namespace test0 {
template<typename> struct RefPtr { };
template<typename> struct PtrHash {
@ -11,3 +18,84 @@ namespace test0 {
static void f() { f(); }
};
}
// Simple hiding.
namespace test1 {
template <unsigned n> struct Opaque {};
struct Base {
Opaque<0> foo(Opaque<0>);
Opaque<0> foo(Opaque<1>);
Opaque<0> foo(Opaque<2>);
};
// using before decls
struct Test0 : Base {
using Base::foo;
Opaque<1> foo(Opaque<1>);
Opaque<1> foo(Opaque<3>);
void test0() { Opaque<0> _ = foo(Opaque<0>()); }
void test1() { Opaque<1> _ = foo(Opaque<1>()); }
void test2() { Opaque<0> _ = foo(Opaque<2>()); }
void test3() { Opaque<1> _ = foo(Opaque<3>()); }
};
// using after decls
struct Test1 : Base {
Opaque<1> foo(Opaque<1>);
Opaque<1> foo(Opaque<3>);
using Base::foo;
void test0() { Opaque<0> _ = foo(Opaque<0>()); }
void test1() { Opaque<1> _ = foo(Opaque<1>()); }
void test2() { Opaque<0> _ = foo(Opaque<2>()); }
void test3() { Opaque<1> _ = foo(Opaque<3>()); }
};
// using between decls
struct Test2 : Base {
Opaque<1> foo(Opaque<0>);
using Base::foo;
Opaque<1> foo(Opaque<2>);
Opaque<1> foo(Opaque<3>);
void test0() { Opaque<1> _ = foo(Opaque<0>()); }
void test1() { Opaque<0> _ = foo(Opaque<1>()); }
void test2() { Opaque<1> _ = foo(Opaque<2>()); }
void test3() { Opaque<1> _ = foo(Opaque<3>()); }
};
}
// Crazy dependent hiding.
namespace test2 {
struct Base {
void foo(int);
};
template <typename T> struct Derived1 : Base {
using Base::foo;
void foo(T);
void testUnresolved(int i) { foo(i); }
};
void test0(int i) {
Derived1<int> d1;
d1.foo(i);
d1.testUnresolved(i);
}
// Same thing, except with the order of members reversed.
template <typename T> struct Derived2 : Base {
void foo(T);
using Base::foo;
void testUnresolved(int i) { foo(i); }
};
void test1(int i) {
Derived2<int> d2;
d2.foo(i);
d2.testUnresolved(i);
}
}

View File

@ -0,0 +1,83 @@
// RUN: clang-cc -fsyntax-only -verify %s
struct Opaque0 {};
struct Opaque1 {};
// Redeclarations are okay in a namespace.
namespace test0 {
namespace ns {
void foo(Opaque0); // expected-note 2 {{candidate function}}
}
using ns::foo;
using ns::foo;
void test0() {
foo(Opaque1()); // expected-error {{no matching function for call}}
}
namespace ns {
void foo(Opaque1);
}
void test1() {
foo(Opaque1()); // expected-error {{no matching function for call}}
}
using ns::foo;
void test2() {
foo(Opaque1());
}
using ns::foo;
}
// Make sure we handle transparent contexts the same way.
namespace test1 {
namespace ns {
void foo(Opaque0); // expected-note 2 {{candidate function}}
}
extern "C++" {
using ns::foo;
}
void test0() {
foo(Opaque1()); // expected-error {{no matching function for call}}
}
namespace ns {
void foo(Opaque1);
}
void test1() {
foo(Opaque1()); // expected-error {{no matching function for call}}
}
extern "C++" {
using ns::foo;
}
void test2() {
foo(Opaque1());
}
}
// Make sure we detect invalid redeclarations that can't be detected
// until template instantiation.
namespace test2 {
template <class T> struct Base {
typedef Base type;
void foo();
};
template <class T> struct Derived : Base<T> {
// These are invalid redeclarations, detectable only after
// instantiation.
using Base<T>::foo; // expected-note {{previous using decl}}
using Base<T>::type::foo; //expected-error {{redeclaration of using decl}}
};
template struct Derived<int>; // expected-note {{in instantiation of template class}}
}