Fix some interactions between C++11 and C++14 features and using-declarations:

* a dependent non-type using-declaration within a function template can be
   valid, as it can refer to an enumerator, so don't reject it in the template
   definition
 * we can partially substitute into a dependent using-declaration if it appears
   within a (local class in a) generic lambda within a function template, which
   means an UnresolvedUsing*Decl doesn't necessarily instantiate to a UsingDecl.

llvm-svn: 290071
This commit is contained in:
Richard Smith 2016-12-18 21:39:37 +00:00
parent 601d5bafb2
commit d8a9e37558
7 changed files with 244 additions and 47 deletions

View File

@ -398,11 +398,11 @@ private:
llvm::DenseMap<const VarDecl *, TemplateOrSpecializationInfo>
TemplateOrInstantiation;
/// \brief Keeps track of the declaration from which a UsingDecl was
/// \brief Keeps track of the declaration from which a using declaration was
/// created during instantiation.
///
/// The source declaration is always a UsingDecl, an UnresolvedUsingValueDecl,
/// or an UnresolvedUsingTypenameDecl.
/// The source and target declarations are always a UsingDecl, an
/// UnresolvedUsingValueDecl, or an UnresolvedUsingTypenameDecl.
///
/// For example:
/// \code
@ -421,7 +421,7 @@ private:
///
/// This mapping will contain an entry that maps from the UsingDecl in
/// B<int> to the UnresolvedUsingDecl in B<T>.
llvm::DenseMap<UsingDecl *, NamedDecl *> InstantiatedFromUsingDecl;
llvm::DenseMap<NamedDecl *, NamedDecl *> InstantiatedFromUsingDecl;
llvm::DenseMap<UsingShadowDecl*, UsingShadowDecl*>
InstantiatedFromUsingShadowDecl;
@ -849,11 +849,11 @@ public:
/// \brief If the given using decl \p Inst is an instantiation of a
/// (possibly unresolved) using decl from a template instantiation,
/// return it.
NamedDecl *getInstantiatedFromUsingDecl(UsingDecl *Inst);
NamedDecl *getInstantiatedFromUsingDecl(NamedDecl *Inst);
/// \brief Remember that the using decl \p Inst is an instantiation
/// of the using decl \p Pattern of a class template.
void setInstantiatedFromUsingDecl(UsingDecl *Inst, NamedDecl *Pattern);
void setInstantiatedFromUsingDecl(NamedDecl *Inst, NamedDecl *Pattern);
void setInstantiatedFromUsingShadowDecl(UsingShadowDecl *Inst,
UsingShadowDecl *Pattern);

View File

@ -4317,6 +4317,7 @@ public:
SourceLocation NameLoc,
const LookupResult &Previous);
bool CheckUsingDeclQualifier(SourceLocation UsingLoc,
bool HasTypename,
const CXXScopeSpec &SS,
const DeclarationNameInfo &NameInfo,
SourceLocation NameLoc);

View File

@ -1270,9 +1270,8 @@ void ASTContext::setClassScopeSpecializationPattern(FunctionDecl *FD,
}
NamedDecl *
ASTContext::getInstantiatedFromUsingDecl(UsingDecl *UUD) {
llvm::DenseMap<UsingDecl *, NamedDecl *>::const_iterator Pos
= InstantiatedFromUsingDecl.find(UUD);
ASTContext::getInstantiatedFromUsingDecl(NamedDecl *UUD) {
auto Pos = InstantiatedFromUsingDecl.find(UUD);
if (Pos == InstantiatedFromUsingDecl.end())
return nullptr;
@ -1280,11 +1279,15 @@ ASTContext::getInstantiatedFromUsingDecl(UsingDecl *UUD) {
}
void
ASTContext::setInstantiatedFromUsingDecl(UsingDecl *Inst, NamedDecl *Pattern) {
ASTContext::setInstantiatedFromUsingDecl(NamedDecl *Inst, NamedDecl *Pattern) {
assert((isa<UsingDecl>(Pattern) ||
isa<UnresolvedUsingValueDecl>(Pattern) ||
isa<UnresolvedUsingTypenameDecl>(Pattern)) &&
"pattern decl is not a using decl");
assert((isa<UsingDecl>(Inst) ||
isa<UnresolvedUsingValueDecl>(Inst) ||
isa<UnresolvedUsingTypenameDecl>(Inst)) &&
"instantiation did not produce a using decl");
assert(!InstantiatedFromUsingDecl[Inst] && "pattern already exists");
InstantiatedFromUsingDecl[Inst] = Pattern;
}

View File

@ -9010,8 +9010,23 @@ NamedDecl *Sema::BuildUsingDeclaration(Scope *S, AccessSpecifier AS,
F.done();
} else {
assert(IsInstantiation && "no scope in non-instantiation");
assert(CurContext->isRecord() && "scope not record in instantiation");
LookupQualifiedName(Previous, CurContext);
if (CurContext->isRecord())
LookupQualifiedName(Previous, CurContext);
else {
// No redeclaration check is needed here; in non-member contexts we
// diagnosed all possible conflicts with other using-declarations when
// building the template:
//
// For a dependent non-type using declaration, the only valid case is
// if we instantiate to a single enumerator. We check for conflicts
// between shadow declarations we introduce, and we check in the template
// definition for conflicts between a non-type using declaration and any
// other declaration, which together covers all cases.
//
// A dependent typename using declaration will never successfully
// instantiate, since it will always name a class member, so we reject
// that in the template definition.
}
}
// Check for invalid redeclarations.
@ -9020,7 +9035,8 @@ NamedDecl *Sema::BuildUsingDeclaration(Scope *S, AccessSpecifier AS,
return nullptr;
// Check for bad qualifiers.
if (CheckUsingDeclQualifier(UsingLoc, SS, NameInfo, IdentLoc))
if (CheckUsingDeclQualifier(UsingLoc, HasTypenameKeyword, SS, NameInfo,
IdentLoc))
return nullptr;
DeclContext *LookupContext = computeDeclContext(SS);
@ -9259,7 +9275,19 @@ bool Sema::CheckUsingDeclRedeclaration(SourceLocation UsingLoc,
= dyn_cast<UnresolvedUsingTypenameDecl>(D)) {
DTypename = true;
DQual = UD->getQualifier();
} else continue;
} else if (!isa<TypeDecl>(D) && Qual->isDependent() &&
!HasTypenameKeyword) {
// A dependent qualifier outside a class can only ever resolve to an
// enumeration type. Therefore it conflicts with any other non-type
// declaration in the same scope.
// FIXME: How should we check for dependent type-type conflicts at block
// scope?
Diag(NameLoc, diag::err_redefinition_different_kind)
<< Prev.getLookupName();
Diag(D->getLocation(), diag::note_previous_definition);
return true;
}
else continue;
// using decls differ if one says 'typename' and the other doesn't.
// FIXME: non-dependent using decls?
@ -9285,6 +9313,7 @@ bool Sema::CheckUsingDeclRedeclaration(SourceLocation UsingLoc,
/// in the current context is appropriately related to the current
/// scope. If an error is found, diagnoses it and returns true.
bool Sema::CheckUsingDeclQualifier(SourceLocation UsingLoc,
bool HasTypename,
const CXXScopeSpec &SS,
const DeclarationNameInfo &NameInfo,
SourceLocation NameLoc) {
@ -9295,9 +9324,11 @@ bool Sema::CheckUsingDeclQualifier(SourceLocation UsingLoc,
// C++0x [namespace.udecl]p8:
// A using-declaration for a class member shall be a member-declaration.
// If we weren't able to compute a valid scope, it must be a
// dependent class scope.
if (!NamedContext || NamedContext->getRedeclContext()->isRecord()) {
// If we weren't able to compute a valid scope, it might validly be a
// dependent class scope or a dependent enumeration unscoped scope. If
// we have a 'typename' keyword, the scope must resolve to a class type.
if ((HasTypename && !NamedContext) ||
(NamedContext && NamedContext->getRedeclContext()->isRecord())) {
auto *RD = NamedContext
? cast<CXXRecordDecl>(NamedContext->getRedeclContext())
: nullptr;
@ -9357,7 +9388,8 @@ bool Sema::CheckUsingDeclQualifier(SourceLocation UsingLoc,
if (getLangOpts().CPlusPlus11) {
// Convert 'using X::Y;' to 'auto &Y = X::Y;'.
FixIt = FixItHint::CreateReplacement(
UsingLoc, "constexpr auto " + NameInfo.getName().getAsString() + " = ");
UsingLoc,
"constexpr auto " + NameInfo.getName().getAsString() + " = ");
}
Diag(UsingLoc, diag::note_using_decl_class_member_workaround)
@ -9367,7 +9399,7 @@ bool Sema::CheckUsingDeclQualifier(SourceLocation UsingLoc,
return true;
}
// Otherwise, everything is known to be fine.
// Otherwise, this might be valid.
return false;
}

View File

@ -987,10 +987,17 @@ Sema::CheckOverload(Scope *S, FunctionDecl *New, const LookupResult &Old,
assert(Old.getLookupKind() == LookupUsingDeclName);
} else if (isa<TagDecl>(OldD)) {
// We can always overload with tags by hiding them.
} else if (isa<UnresolvedUsingValueDecl>(OldD)) {
} else if (auto *UUD = dyn_cast<UnresolvedUsingValueDecl>(OldD)) {
// Optimistically assume that an unresolved using decl will
// overload; if it doesn't, we'll have to diagnose during
// template instantiation.
//
// Exception: if the scope is dependent and this is not a class
// member, the using declaration can only introduce an enumerator.
if (UUD->getQualifier()->isDependent() && !UUD->isCXXClassMember()) {
Match = *I;
return Ovl_NonFunction;
}
} else {
// (C++ 13p1):
// Only function declarations can be overloaded; object and type

View File

@ -2430,8 +2430,8 @@ Decl *TemplateDeclInstantiator::VisitUsingDecl(UsingDecl *D) {
}
if (!NewUD->isInvalidDecl() &&
SemaRef.CheckUsingDeclQualifier(D->getUsingLoc(), SS, NameInfo,
D->getLocation()))
SemaRef.CheckUsingDeclQualifier(D->getUsingLoc(), D->hasTypename(),
SS, NameInfo, D->getLocation()))
NewUD->setInvalidDecl();
SemaRef.Context.setInstantiatedFromUsingDecl(NewUD, D);
@ -2515,7 +2515,7 @@ Decl * TemplateDeclInstantiator
/*instantiation*/ true,
/*typename*/ true, D->getTypenameLoc());
if (UD)
SemaRef.Context.setInstantiatedFromUsingDecl(cast<UsingDecl>(UD), D);
SemaRef.Context.setInstantiatedFromUsingDecl(UD, D);
return UD;
}
@ -2539,7 +2539,7 @@ Decl * TemplateDeclInstantiator
/*instantiation*/ true,
/*typename*/ false, SourceLocation());
if (UD)
SemaRef.Context.setInstantiatedFromUsingDecl(cast<UsingDecl>(UD), D);
SemaRef.Context.setInstantiatedFromUsingDecl(UD, D);
return UD;
}
@ -4520,13 +4520,13 @@ static bool isInstantiationOf(UsingDecl *Pattern,
}
static bool isInstantiationOf(UnresolvedUsingValueDecl *Pattern,
UsingDecl *Instance,
NamedDecl *Instance,
ASTContext &C) {
return declaresSameEntity(C.getInstantiatedFromUsingDecl(Instance), Pattern);
}
static bool isInstantiationOf(UnresolvedUsingTypenameDecl *Pattern,
UsingDecl *Instance,
NamedDecl *Instance,
ASTContext &C) {
return declaresSameEntity(C.getInstantiatedFromUsingDecl(Instance), Pattern);
}
@ -4550,15 +4550,13 @@ static bool isInstantiationOfStaticDataMember(VarDecl *Pattern,
// D is the prospective pattern
static bool isInstantiationOf(ASTContext &Ctx, NamedDecl *D, Decl *Other) {
if (D->getKind() != Other->getKind()) {
if (UnresolvedUsingTypenameDecl *UUD
= dyn_cast<UnresolvedUsingTypenameDecl>(D)) {
if (auto *UUD = dyn_cast<UnresolvedUsingTypenameDecl>(D)) {
if (UsingDecl *UD = dyn_cast<UsingDecl>(Other)) {
return isInstantiationOf(UUD, UD, Ctx);
}
}
if (UnresolvedUsingValueDecl *UUD
= dyn_cast<UnresolvedUsingValueDecl>(D)) {
if (auto *UUD = dyn_cast<UnresolvedUsingValueDecl>(D)) {
if (UsingDecl *UD = dyn_cast<UsingDecl>(Other)) {
return isInstantiationOf(UUD, UD, Ctx);
}
@ -4567,31 +4565,31 @@ static bool isInstantiationOf(ASTContext &Ctx, NamedDecl *D, Decl *Other) {
return false;
}
if (CXXRecordDecl *Record = dyn_cast<CXXRecordDecl>(Other))
if (auto *Record = dyn_cast<CXXRecordDecl>(Other))
return isInstantiationOf(cast<CXXRecordDecl>(D), Record);
if (FunctionDecl *Function = dyn_cast<FunctionDecl>(Other))
if (auto *Function = dyn_cast<FunctionDecl>(Other))
return isInstantiationOf(cast<FunctionDecl>(D), Function);
if (EnumDecl *Enum = dyn_cast<EnumDecl>(Other))
if (auto *Enum = dyn_cast<EnumDecl>(Other))
return isInstantiationOf(cast<EnumDecl>(D), Enum);
if (VarDecl *Var = dyn_cast<VarDecl>(Other))
if (auto *Var = dyn_cast<VarDecl>(Other))
if (Var->isStaticDataMember())
return isInstantiationOfStaticDataMember(cast<VarDecl>(D), Var);
if (ClassTemplateDecl *Temp = dyn_cast<ClassTemplateDecl>(Other))
if (auto *Temp = dyn_cast<ClassTemplateDecl>(Other))
return isInstantiationOf(cast<ClassTemplateDecl>(D), Temp);
if (FunctionTemplateDecl *Temp = dyn_cast<FunctionTemplateDecl>(Other))
if (auto *Temp = dyn_cast<FunctionTemplateDecl>(Other))
return isInstantiationOf(cast<FunctionTemplateDecl>(D), Temp);
if (ClassTemplatePartialSpecializationDecl *PartialSpec
= dyn_cast<ClassTemplatePartialSpecializationDecl>(Other))
if (auto *PartialSpec =
dyn_cast<ClassTemplatePartialSpecializationDecl>(Other))
return isInstantiationOf(cast<ClassTemplatePartialSpecializationDecl>(D),
PartialSpec);
if (FieldDecl *Field = dyn_cast<FieldDecl>(Other)) {
if (auto *Field = dyn_cast<FieldDecl>(Other)) {
if (!Field->getDeclName()) {
// This is an unnamed field.
return declaresSameEntity(Ctx.getInstantiatedFromUnnamedFieldDecl(Field),
@ -4599,14 +4597,20 @@ static bool isInstantiationOf(ASTContext &Ctx, NamedDecl *D, Decl *Other) {
}
}
if (UsingDecl *Using = dyn_cast<UsingDecl>(Other))
if (auto *Using = dyn_cast<UsingDecl>(Other))
return isInstantiationOf(cast<UsingDecl>(D), Using, Ctx);
if (UsingShadowDecl *Shadow = dyn_cast<UsingShadowDecl>(Other))
if (auto *Using = dyn_cast<UnresolvedUsingValueDecl>(Other))
return isInstantiationOf(cast<UnresolvedUsingValueDecl>(D), Using, Ctx);
if (auto *Using = dyn_cast<UnresolvedUsingTypenameDecl>(Other))
return isInstantiationOf(cast<UnresolvedUsingTypenameDecl>(D), Using, Ctx);
if (auto *Shadow = dyn_cast<UsingShadowDecl>(Other))
return isInstantiationOf(cast<UsingShadowDecl>(D), Shadow, Ctx);
return D->getDeclName() && isa<NamedDecl>(Other) &&
D->getDeclName() == cast<NamedDecl>(Other)->getDeclName();
return D->getDeclName() &&
D->getDeclName() == cast<NamedDecl>(Other)->getDeclName();
}
template<typename ForwardIterator>

View File

@ -1,5 +1,6 @@
// RUN: %clang_cc1 -fsyntax-only -std=c++98 -verify %s
// RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify %s
// RUN: %clang_cc1 -fsyntax-only -std=c++14 -verify %s
// RUN: not %clang_cc1 -fsyntax-only -std=c++98 -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck --check-prefix=CXX98 %s
// RUN: not %clang_cc1 -fsyntax-only -std=c++11 -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck --check-prefix=CXX11 %s
// C++0x N2914.
@ -44,10 +45,159 @@ void f() {
#endif
}
template <typename T>
struct PR21933 : T {
static void StaticFun() { using T::member; } // expected-error{{using declaration cannot refer to class member}}
};
namespace PR21933 {
struct A { int member; };
struct B { static int member; };
enum C { member };
template <typename T>
struct X {
static void StaticFun() {
using T::member; // expected-error 2{{class member}} expected-note {{use a reference instead}}
#if __cplusplus < 201103L
// expected-error@-2 {{cannot be used prior to '::'}}
#endif
(void)member;
}
};
template<typename T>
struct Y : T {
static void StaticFun() {
using T::member; // expected-error 2{{class member}} expected-note {{use a reference instead}}
(void)member;
}
};
void f() {
X<A>::StaticFun(); // expected-note {{instantiation of}}
X<B>::StaticFun(); // expected-note {{instantiation of}}
X<C>::StaticFun();
#if __cplusplus < 201103L
// expected-note@-2 {{instantiation of}}
#endif
Y<A>::StaticFun(); // expected-note {{instantiation of}}
Y<B>::StaticFun(); // expected-note {{instantiation of}}
}
template<typename T, typename U> void value_vs_value() {
using T::a; // expected-note {{previous}}
#if __cplusplus < 201103L
// expected-error@-2 {{cannot be used prior to '::'}}
#endif
extern int a(); // expected-error {{different kind of symbol}}
a();
extern int b();
using T::b;
b();
using T::c;
using U::c;
c();
}
template<typename T, typename U> void value_vs_type() {
using T::Xt; // expected-note {{previous}}
typedef struct {} Xt; // expected-error {{different kind of symbol}}
(void)Xt;
using T::Xs; // expected-note {{candidate}}
struct Xs {}; // expected-note {{candidate}}
// FIXME: This is wrong, the using declaration hides the type.
Xs xs; // expected-error {{ambiguous}}
using T::Xe; // expected-note {{candidate}}
enum Xe {}; // expected-note {{candidate}}
// FIXME: This is wrong, the using declaration hides the type.
Xe xe; // expected-error {{ambiguous}}
typedef struct {} Yt; // expected-note {{candidate}}
using T::Yt; // eypected-error {{different kind of symbol}} expected-note {{candidate}}
Yt yt; // expected-error {{ambiguous}}
struct Ys {}; // expected-note {{candidate}}
using T::Ys; // expected-note {{candidate}}
// FIXME: This is wrong, the using declaration hides the type.
Ys ys; // expected-error {{ambiguous}}
enum Ye {}; // expected-note {{candidate}}
using T::Ye; // expected-note {{candidate}}
// FIXME: This is wrong, the using declaration hides the type.
Ye ye; // expected-error {{ambiguous}}
}
template<typename T> void type() {
// Must be a class member because T:: can only name a class or enum,
// and an enum cannot have a type member.
using typename T::X; // expected-error {{cannot refer to class member}}
}
namespace N1 { enum E { a, b, c }; }
namespace N2 { enum E { a, b, c }; }
void g() { value_vs_value<N1::E, N2::E>(); }
#if __cplusplus < 201103L
// expected-note@-2 {{in instantiation of}}
#endif
#if __cplusplus >= 201402L
namespace partial_substitute {
template<typename T> auto f() {
return [](auto x) {
using A = typename T::template U<decltype(x)>;
using A::E::e;
struct S : A {
using A::f;
using typename A::type;
type f(int) { return e; }
};
return S();
};
}
enum Enum { e };
struct X {
template<typename T> struct U {
int f(int, int);
using type = int;
using E = Enum;
};
};
int test() {
auto s = f<X>()(0);
return s.f(0) + s.f(0, 0);
}
template<typename T, typename U> auto g() {
return [](auto x) {
using X = decltype(x);
struct S : T::template Q<X>, U::template Q<X> {
using T::template Q<X>::f;
using U::template Q<X>::f;
void h() { f(); }
void h(int n) { f(n); }
};
return S();
};
}
struct A { template<typename> struct Q { int f(); }; };
struct B { template<typename> struct Q { int f(int); }; };
int test2() {
auto s = g<A, B>()(0);
s.f();
s.f(0);
s.h();
s.h(0);
}
}
#endif
template<typename T, typename U> struct RepeatedMember : T, U {
// FIXME: This is the wrong error: we should complain that a member type
// cannot be redeclared at class scope.
using typename T::type; // expected-note {{candidate}}
using typename U::type; // expected-note {{candidate}}
type x; // expected-error {{ambiguous}}
};
}
struct S {
static int n;