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
///
/// 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;
/// \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);
SourceLocation NameLoc,
const LookupResult &Previous);
bool CheckUsingDeclQualifier(SourceLocation UsingLoc,
+ bool HasTypename,
const CXXScopeSpec &SS,
const DeclarationNameInfo &NameInfo,
SourceLocation NameLoc);
}
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;
}
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;
}
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.
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);
= 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?
/// 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) {
// 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;
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)
return true;
}
- // Otherwise, everything is known to be fine.
+ // Otherwise, this might be valid.
return false;
}
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
}
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);
/*instantiation*/ true,
/*typename*/ true, D->getTypenameLoc());
if (UD)
- SemaRef.Context.setInstantiatedFromUsingDecl(cast<UsingDecl>(UD), D);
+ SemaRef.Context.setInstantiatedFromUsingDecl(UD, D);
return UD;
}
/*instantiation*/ true,
/*typename*/ false, SourceLocation());
if (UD)
- SemaRef.Context.setInstantiatedFromUsingDecl(cast<UsingDecl>(UD), D);
+ SemaRef.Context.setInstantiatedFromUsingDecl(UD, D);
return UD;
}
}
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);
}
// 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);
}
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),
}
}
- 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>
// 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.
#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;