forked from OSchip/llvm-project
[clang] p1099 using enum part 1
This adds support for p1099's 'using SCOPED_ENUM::MEMBER;' functionality, bringing a member of an enumerator into the current scope. The novel feature here, is that there need not be a class hierarchical relationship between the current scope and the scope of the SCOPED_ENUM. That's a new thing, the closest equivalent is a typedef or alias declaration. But this means that Sema::CheckUsingDeclQualifier needs adjustment. (a) one can't call it until one knows the set of decls that are being referenced -- if exactly one is an enumerator, we're in the new territory. Thus it needs calling later in some cases. Also (b) there are two ways we hold the set of such decls. During parsing (or instantiating a dependent scope) we have a lookup result, and during instantiation we have a set of shadow decls. Thus two optional arguments, at most one of which should be non-null. Differential Revision: https://reviews.llvm.org/D100276
This commit is contained in:
parent
09e92c607c
commit
012898b92c
|
@ -536,6 +536,10 @@ def err_using_dependent_value_is_type : Error<
|
|||
"dependent using declaration resolved to type without 'typename'">;
|
||||
def err_using_decl_nested_name_specifier_is_not_class : Error<
|
||||
"using declaration in class refers into '%0', which is not a class">;
|
||||
def warn_cxx17_compat_using_decl_non_member_enumerator : Warning<
|
||||
"member using declaration naming non-class '%0' enumerator is "
|
||||
"incompatible with C++ standards before C++20">, InGroup<CXXPre20Compat>,
|
||||
DefaultIgnore;
|
||||
def err_using_decl_nested_name_specifier_is_current_class : Error<
|
||||
"using declaration refers to its own class">;
|
||||
def err_using_decl_nested_name_specifier_is_not_base_class : Error<
|
||||
|
@ -544,6 +548,16 @@ def err_using_decl_constructor_not_in_direct_base : Error<
|
|||
"%0 is not a direct base of %1, cannot inherit constructors">;
|
||||
def err_using_decl_can_not_refer_to_class_member : Error<
|
||||
"using declaration cannot refer to class member">;
|
||||
def warn_cxx17_compat_using_decl_class_member_enumerator : Warning<
|
||||
"member using declaration naming a non-member enumerator is incompatible "
|
||||
"with C++ standards before C++20">, InGroup<CXXPre20Compat>, DefaultIgnore;
|
||||
def ext_using_decl_class_member_enumerator : ExtWarn<
|
||||
"member using declaration naming a non-member enumerator is "
|
||||
"a C++20 extension">, InGroup<CXX20>;
|
||||
def err_using_enum_lacks_definition : Error<
|
||||
"enum named by using-enum declaration lacks a definition">;
|
||||
def err_using_enum_is_dependent : Error<
|
||||
"using-enum cannot name a dependent type">;
|
||||
def err_ambiguous_inherited_constructor : Error<
|
||||
"constructor of %0 inherited from multiple base class subobjects">;
|
||||
def note_ambiguous_inherited_constructor_using : Note<
|
||||
|
@ -553,8 +567,12 @@ def note_using_decl_class_member_workaround : Note<
|
|||
"a const variable|a constexpr variable}0 instead">;
|
||||
def err_using_decl_can_not_refer_to_namespace : Error<
|
||||
"using declaration cannot refer to a namespace">;
|
||||
def err_using_decl_can_not_refer_to_scoped_enum : Error<
|
||||
"using declaration cannot refer to a scoped enumerator">;
|
||||
def warn_cxx17_compat_using_decl_scoped_enumerator: Warning<
|
||||
"using declaration naming a scoped enumerator is incompatible with "
|
||||
"C++ standards before C++20">, InGroup<CXXPre20Compat>, DefaultIgnore;
|
||||
def ext_using_decl_scoped_enumerator : ExtWarn<
|
||||
"using declaration naming a scoped enumerator is a C++20 extension">,
|
||||
InGroup<CXX20>;
|
||||
def err_using_decl_constructor : Error<
|
||||
"using declaration cannot refer to a constructor">;
|
||||
def warn_cxx98_compat_using_decl_constructor : Warning<
|
||||
|
|
|
@ -5714,11 +5714,12 @@ public:
|
|||
const CXXScopeSpec &SS,
|
||||
SourceLocation NameLoc,
|
||||
const LookupResult &Previous);
|
||||
bool CheckUsingDeclQualifier(SourceLocation UsingLoc,
|
||||
bool HasTypename,
|
||||
bool CheckUsingDeclQualifier(SourceLocation UsingLoc, bool HasTypename,
|
||||
const CXXScopeSpec &SS,
|
||||
const DeclarationNameInfo &NameInfo,
|
||||
SourceLocation NameLoc);
|
||||
SourceLocation NameLoc,
|
||||
const LookupResult *R = nullptr,
|
||||
const UsingDecl *UD = nullptr);
|
||||
|
||||
NamedDecl *BuildUsingDeclaration(Scope *S, AccessSpecifier AS,
|
||||
SourceLocation UsingLoc,
|
||||
|
|
|
@ -11659,7 +11659,7 @@ bool Sema::CheckUsingShadowDecl(BaseUsingDecl *BUD, NamedDecl *Orig,
|
|||
// 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
|
||||
// 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 (!getLangOpts().CPlusPlus11 && CurContext->isRecord())
|
||||
|
@ -12088,11 +12088,6 @@ NamedDecl *Sema::BuildUsingDeclaration(
|
|||
SS, IdentLoc, Previous))
|
||||
return nullptr;
|
||||
|
||||
// Check for bad qualifiers.
|
||||
if (CheckUsingDeclQualifier(UsingLoc, HasTypenameKeyword, SS, NameInfo,
|
||||
IdentLoc))
|
||||
return nullptr;
|
||||
|
||||
// 'using_if_exists' doesn't make sense on an inherited constructor.
|
||||
if (IsUsingIfExists && UsingName.getName().getNameKind() ==
|
||||
DeclarationName::CXXConstructorName) {
|
||||
|
@ -12104,6 +12099,11 @@ NamedDecl *Sema::BuildUsingDeclaration(
|
|||
NestedNameSpecifierLoc QualifierLoc = SS.getWithLocInContext(Context);
|
||||
if (!LookupContext || EllipsisLoc.isValid()) {
|
||||
NamedDecl *D;
|
||||
// Dependent scope, or an unexpanded pack
|
||||
if (!LookupContext && CheckUsingDeclQualifier(UsingLoc, HasTypenameKeyword,
|
||||
SS, NameInfo, IdentLoc))
|
||||
return nullptr;
|
||||
|
||||
if (HasTypenameKeyword) {
|
||||
// FIXME: not all declaration name kinds are legal here
|
||||
D = UnresolvedUsingTypenameDecl::Create(Context, CurContext,
|
||||
|
@ -12156,6 +12156,11 @@ NamedDecl *Sema::BuildUsingDeclaration(
|
|||
|
||||
LookupQualifiedName(R, LookupContext);
|
||||
|
||||
// Validate the context, now we have a lookup
|
||||
if (CheckUsingDeclQualifier(UsingLoc, HasTypenameKeyword, SS, NameInfo,
|
||||
IdentLoc, &R))
|
||||
return nullptr;
|
||||
|
||||
if (R.empty() && IsUsingIfExists)
|
||||
R.addDecl(UnresolvedUsingIfExistsDecl::Create(Context, CurContext, UsingLoc,
|
||||
UsingName.getName()),
|
||||
|
@ -12261,16 +12266,6 @@ NamedDecl *Sema::BuildUsingDeclaration(
|
|||
return BuildInvalid();
|
||||
}
|
||||
|
||||
// C++14 [namespace.udecl]p7:
|
||||
// A using-declaration shall not name a scoped enumerator.
|
||||
if (auto *ED = R.getAsSingle<EnumConstantDecl>()) {
|
||||
if (cast<EnumDecl>(ED->getDeclContext())->isScoped()) {
|
||||
Diag(IdentLoc, diag::err_using_decl_can_not_refer_to_scoped_enum)
|
||||
<< SS.getRange();
|
||||
return BuildInvalid();
|
||||
}
|
||||
}
|
||||
|
||||
UsingDecl *UD = BuildValid();
|
||||
|
||||
// Some additional rules apply to inheriting constructors.
|
||||
|
@ -12410,48 +12405,83 @@ bool Sema::CheckUsingDeclRedeclaration(SourceLocation UsingLoc,
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
/// Checks that the given nested-name qualifier used in a using decl
|
||||
/// 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,
|
||||
/// R is nullptr, if the caller has not (yet) done a lookup, otherwise it's the
|
||||
/// result of that lookup. UD is likewise nullptr, except when we have an
|
||||
/// already-populated UsingDecl whose shadow decls contain the same information
|
||||
/// (i.e. we're instantiating a UsingDecl with non-dependent scope).
|
||||
bool Sema::CheckUsingDeclQualifier(SourceLocation UsingLoc, bool HasTypename,
|
||||
const CXXScopeSpec &SS,
|
||||
const DeclarationNameInfo &NameInfo,
|
||||
SourceLocation NameLoc) {
|
||||
SourceLocation NameLoc,
|
||||
const LookupResult *R, const UsingDecl *UD) {
|
||||
DeclContext *NamedContext = computeDeclContext(SS);
|
||||
assert(bool(NamedContext) == (R || UD) && !(R && UD) &&
|
||||
"resolvable context must have exactly one set of decls");
|
||||
|
||||
// C++ 20 permits using an enumerator that does not have a class-hierarchy
|
||||
// relationship.
|
||||
bool Cxx20Enumerator = false;
|
||||
if (NamedContext) {
|
||||
EnumConstantDecl *EC = nullptr;
|
||||
if (R)
|
||||
EC = R->getAsSingle<EnumConstantDecl>();
|
||||
else if (UD && UD->shadow_size() == 1)
|
||||
EC = dyn_cast<EnumConstantDecl>(UD->shadow_begin()->getTargetDecl());
|
||||
if (EC)
|
||||
Cxx20Enumerator = getLangOpts().CPlusPlus20;
|
||||
|
||||
if (auto *ED = dyn_cast<EnumDecl>(NamedContext)) {
|
||||
// C++14 [namespace.udecl]p7:
|
||||
// A using-declaration shall not name a scoped enumerator.
|
||||
// C++20 p1099 permits enumerators.
|
||||
if (EC && R && ED->isScoped())
|
||||
Diag(SS.getBeginLoc(),
|
||||
getLangOpts().CPlusPlus20
|
||||
? diag::warn_cxx17_compat_using_decl_scoped_enumerator
|
||||
: diag::ext_using_decl_scoped_enumerator)
|
||||
<< SS.getRange();
|
||||
|
||||
// We want to consider the scope of the enumerator
|
||||
NamedContext = ED->getDeclContext();
|
||||
}
|
||||
}
|
||||
|
||||
if (!CurContext->isRecord()) {
|
||||
// C++03 [namespace.udecl]p3:
|
||||
// C++0x [namespace.udecl]p8:
|
||||
// A using-declaration for a class member shall be a member-declaration.
|
||||
// C++20 [namespace.udecl]p7
|
||||
// ... other than an enumerator ...
|
||||
|
||||
// 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 (RD && RequireCompleteDeclContext(const_cast<CXXScopeSpec&>(SS), RD))
|
||||
RD = nullptr;
|
||||
// dependent class or enumeration scope. If we have a 'typename' keyword,
|
||||
// the scope must resolve to a class type.
|
||||
if (NamedContext ? !NamedContext->getRedeclContext()->isRecord()
|
||||
: !HasTypename)
|
||||
return false; // OK
|
||||
|
||||
Diag(NameLoc, diag::err_using_decl_can_not_refer_to_class_member)
|
||||
Diag(NameLoc,
|
||||
Cxx20Enumerator
|
||||
? diag::warn_cxx17_compat_using_decl_class_member_enumerator
|
||||
: diag::err_using_decl_can_not_refer_to_class_member)
|
||||
<< SS.getRange();
|
||||
|
||||
// If we have a complete, non-dependent source type, try to suggest a
|
||||
// way to get the same effect.
|
||||
if (!RD)
|
||||
return true;
|
||||
if (Cxx20Enumerator)
|
||||
return false; // OK
|
||||
|
||||
// Find what this using-declaration was referring to.
|
||||
LookupResult R(*this, NameInfo, LookupOrdinaryName);
|
||||
R.setHideTags(false);
|
||||
R.suppressDiagnostics();
|
||||
LookupQualifiedName(R, RD);
|
||||
auto *RD = NamedContext
|
||||
? cast<CXXRecordDecl>(NamedContext->getRedeclContext())
|
||||
: nullptr;
|
||||
if (RD && !RequireCompleteDeclContext(const_cast<CXXScopeSpec &>(SS), RD)) {
|
||||
// See if there's a helpful fixit
|
||||
|
||||
if (R.getAsSingle<TypeDecl>()) {
|
||||
if (!R) {
|
||||
// We will have already diagnosed the problem on the template
|
||||
// definition, Maybe we should do so again?
|
||||
} else if (R->getAsSingle<TypeDecl>()) {
|
||||
if (getLangOpts().CPlusPlus11) {
|
||||
// Convert 'using X::Y;' to 'using Y = X::Y;'.
|
||||
Diag(SS.getBeginLoc(), diag::note_using_decl_class_member_workaround)
|
||||
|
@ -12468,7 +12498,7 @@ bool Sema::CheckUsingDeclQualifier(SourceLocation UsingLoc,
|
|||
<< FixItHint::CreateInsertion(
|
||||
InsertLoc, " " + NameInfo.getName().getAsString());
|
||||
}
|
||||
} else if (R.getAsSingle<VarDecl>()) {
|
||||
} else if (R->getAsSingle<VarDecl>()) {
|
||||
// Don't provide a fixit outside C++11 mode; we don't want to suggest
|
||||
// repeating the type of the static data member here.
|
||||
FixItHint FixIt;
|
||||
|
@ -12481,7 +12511,7 @@ bool Sema::CheckUsingDeclQualifier(SourceLocation UsingLoc,
|
|||
Diag(UsingLoc, diag::note_using_decl_class_member_workaround)
|
||||
<< 2 // reference declaration
|
||||
<< FixIt;
|
||||
} else if (R.getAsSingle<EnumConstantDecl>()) {
|
||||
} else if (R->getAsSingle<EnumConstantDecl>()) {
|
||||
// Don't provide a fixit outside C++11 mode; we don't want to suggest
|
||||
// repeating the type of the enumeration here, and we can't do so if
|
||||
// the type is anonymous.
|
||||
|
@ -12497,15 +12527,11 @@ bool Sema::CheckUsingDeclQualifier(SourceLocation UsingLoc,
|
|||
<< (getLangOpts().CPlusPlus11 ? 4 : 3) // const[expr] variable
|
||||
<< FixIt;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise, this might be valid.
|
||||
return false;
|
||||
return true; // Fail
|
||||
}
|
||||
|
||||
// The current scope is a record.
|
||||
|
||||
// If the named context is dependent, we can't decide much.
|
||||
if (!NamedContext) {
|
||||
// FIXME: in C++0x, we can diagnose if we can prove that the
|
||||
|
@ -12517,12 +12543,19 @@ bool Sema::CheckUsingDeclQualifier(SourceLocation UsingLoc,
|
|||
return false;
|
||||
}
|
||||
|
||||
// The current scope is a record.
|
||||
if (!NamedContext->isRecord()) {
|
||||
// Ideally this would point at the last name in the specifier,
|
||||
// but we don't have that level of source info.
|
||||
Diag(SS.getRange().getBegin(),
|
||||
diag::err_using_decl_nested_name_specifier_is_not_class)
|
||||
<< SS.getScopeRep() << SS.getRange();
|
||||
Diag(SS.getBeginLoc(),
|
||||
Cxx20Enumerator
|
||||
? diag::warn_cxx17_compat_using_decl_non_member_enumerator
|
||||
: diag::err_using_decl_nested_name_specifier_is_not_class)
|
||||
<< SS.getScopeRep() << SS.getRange();
|
||||
|
||||
if (Cxx20Enumerator)
|
||||
return false; // OK
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -12538,19 +12571,25 @@ bool Sema::CheckUsingDeclQualifier(SourceLocation UsingLoc,
|
|||
|
||||
if (cast<CXXRecordDecl>(CurContext)->isProvablyNotDerivedFrom(
|
||||
cast<CXXRecordDecl>(NamedContext))) {
|
||||
|
||||
if (Cxx20Enumerator) {
|
||||
Diag(NameLoc, diag::warn_cxx17_compat_using_decl_non_member_enumerator)
|
||||
<< SS.getRange();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (CurContext == NamedContext) {
|
||||
Diag(NameLoc,
|
||||
Diag(SS.getBeginLoc(),
|
||||
diag::err_using_decl_nested_name_specifier_is_current_class)
|
||||
<< SS.getRange();
|
||||
return true;
|
||||
<< SS.getRange();
|
||||
return !getLangOpts().CPlusPlus20;
|
||||
}
|
||||
|
||||
if (!cast<CXXRecordDecl>(NamedContext)->isInvalidDecl()) {
|
||||
Diag(SS.getRange().getBegin(),
|
||||
Diag(SS.getBeginLoc(),
|
||||
diag::err_using_decl_nested_name_specifier_is_not_base_class)
|
||||
<< SS.getScopeRep()
|
||||
<< cast<CXXRecordDecl>(CurContext)
|
||||
<< SS.getRange();
|
||||
<< SS.getScopeRep() << cast<CXXRecordDecl>(CurContext)
|
||||
<< SS.getRange();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -3067,10 +3067,9 @@ Decl *TemplateDeclInstantiator::VisitUsingDecl(UsingDecl *D) {
|
|||
}
|
||||
|
||||
if (!NewUD->isInvalidDecl() &&
|
||||
SemaRef.CheckUsingDeclQualifier(D->getUsingLoc(), D->hasTypename(),
|
||||
SS, NameInfo, D->getLocation()))
|
||||
SemaRef.CheckUsingDeclQualifier(D->getUsingLoc(), D->hasTypename(), SS,
|
||||
NameInfo, D->getLocation(), nullptr, D))
|
||||
NewUD->setInvalidDecl();
|
||||
|
||||
SemaRef.Context.setInstantiatedFromUsingDecl(NewUD, D);
|
||||
NewUD->setAccess(D->getAccess());
|
||||
Owner->addDecl(NewUD);
|
||||
|
@ -3079,6 +3078,8 @@ Decl *TemplateDeclInstantiator::VisitUsingDecl(UsingDecl *D) {
|
|||
if (NewUD->isInvalidDecl())
|
||||
return NewUD;
|
||||
|
||||
// If the using scope was dependent, or we had dependent bases, we need to
|
||||
// recheck the inheritance
|
||||
if (NameInfo.getName().getNameKind() == DeclarationName::CXXConstructorName)
|
||||
SemaRef.CheckInheritingConstructorUsingDecl(NewUD);
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ struct B {
|
|||
};
|
||||
|
||||
class C {
|
||||
public:
|
||||
int g();
|
||||
};
|
||||
|
||||
|
@ -42,7 +43,7 @@ class D2 : public B {
|
|||
#endif
|
||||
|
||||
using B::EC;
|
||||
using B::EC::ec; // expected-error {{not a class}} expected-warning 0-1 {{C++11}}
|
||||
using B::EC::ec; // expected-warning {{a C++20 extension}} expected-warning 0-1 {{C++11}}
|
||||
};
|
||||
|
||||
namespace test1 {
|
||||
|
|
|
@ -0,0 +1,271 @@
|
|||
// RUN: %clang_cc1 -fsyntax-only -std=c++17 -verify %s
|
||||
// RUN: %clang_cc1 -fsyntax-only -std=c++20 -verify %s
|
||||
|
||||
// p1099 'using SCOPEDENUM::MEMBER;'
|
||||
|
||||
namespace Zero {
|
||||
namespace Bob {
|
||||
enum class Kevin {
|
||||
Stuart,
|
||||
AlsoStuart
|
||||
#if __cplusplus >= 202002L
|
||||
// expected-note@-3{{target of using declaration}}
|
||||
// expected-note@-3{{target of using declaration}}
|
||||
#endif
|
||||
};
|
||||
} // namespace Bob
|
||||
|
||||
using Bob::Kevin::Stuart;
|
||||
#if __cplusplus < 202002L
|
||||
// expected-warning@-2{{using declaration naming a scoped enumerator is a C++20 extension}}
|
||||
#else
|
||||
using Bob::Kevin::Stuart;
|
||||
|
||||
auto b = Stuart;
|
||||
|
||||
namespace Foo {
|
||||
int Stuart; // expected-note{{conflicting declaration}}
|
||||
using Bob::Kevin::Stuart; // expected-error{{target of using declaration conflicts}}
|
||||
|
||||
using Bob::Kevin::AlsoStuart; // expected-note{{using declaration}}
|
||||
int AlsoStuart; // expected-error{{declaration conflicts with target}}
|
||||
} // namespace Foo
|
||||
#endif
|
||||
|
||||
} // namespace Zero
|
||||
|
||||
namespace One {
|
||||
|
||||
// derived from [namespace.udecl]/3
|
||||
enum class button { up,
|
||||
down };
|
||||
struct S {
|
||||
using button::up;
|
||||
#if __cplusplus < 202002L
|
||||
// expected-warning@-2{{a C++20 extension}}
|
||||
// expected-error@-3{{using declaration in class}}
|
||||
#else
|
||||
button b = up;
|
||||
#endif
|
||||
};
|
||||
|
||||
#if __cplusplus >= 202002L
|
||||
// some more
|
||||
struct T : S {
|
||||
button c = up;
|
||||
};
|
||||
#endif
|
||||
enum E2 { e2 };
|
||||
} // namespace One
|
||||
|
||||
namespace Two {
|
||||
enum class E1 { e1 };
|
||||
|
||||
struct S {
|
||||
using One::e2;
|
||||
#if __cplusplus < 202002L
|
||||
// expected-error@-2{{using declaration in class}}
|
||||
#else
|
||||
One::E2 c = e2;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace Two
|
||||
|
||||
namespace Three {
|
||||
|
||||
enum E3 { e3 };
|
||||
struct e3;
|
||||
|
||||
struct S {
|
||||
using Three::e3; // expected-error{{using declaration in class}}
|
||||
|
||||
enum class E4 { e4 };
|
||||
enum E5 { e5 };
|
||||
};
|
||||
|
||||
using S::e5;
|
||||
using S::E4::e4;
|
||||
#if __cplusplus < 202002L
|
||||
// expected-error@-3{{using declaration cannot refer to class member}}
|
||||
// expected-note@-4{{use a constexpr variable instead}}
|
||||
// expected-warning@-4{{a C++20 extension}}
|
||||
// expected-error@-5{{using declaration cannot refer to class member}}
|
||||
// expected-note@-6{{use a constexpr variable instead}}
|
||||
#else
|
||||
auto a = e4;
|
||||
auto b = e5;
|
||||
#endif
|
||||
} // namespace Three
|
||||
|
||||
namespace Four {
|
||||
|
||||
template <typename T>
|
||||
struct TPL {
|
||||
enum class E1 { e1 };
|
||||
struct IN {
|
||||
enum class E2 { e2 };
|
||||
};
|
||||
|
||||
protected:
|
||||
enum class E3 { e3 }; // expected-note{{declared protected here}}
|
||||
};
|
||||
|
||||
using TPL<int>::E1::e1;
|
||||
#if __cplusplus < 202002L
|
||||
// expected-warning@-2{{a C++20 extension}}
|
||||
// expected-error@-3{{using declaration cannot refer to class member}}
|
||||
// expected-note@-4{{use a constexpr variable instead}}
|
||||
#else
|
||||
using TPL<float>::IN::E2::e2;
|
||||
|
||||
auto a = e1;
|
||||
auto b = e2;
|
||||
#endif
|
||||
|
||||
enum class E4 { e4 };
|
||||
template <typename T>
|
||||
struct DER : TPL<int> {
|
||||
using TPL<T>::E1::e1;
|
||||
#if __cplusplus < 202002L
|
||||
// expected-warning@-2{{a C++20 extension}}
|
||||
// expected-warning@-3{{using declaration naming a scoped}}
|
||||
// expected-error@-4{{which is not a base}}
|
||||
#endif
|
||||
using TPL<T>::E3::e3; // expected-error{{is a protected member}}
|
||||
#if __cplusplus < 202002L
|
||||
// expected-warning@-2 2{{using declaration naming a scoped}}
|
||||
// expected-error@-3{{which is not a base}}
|
||||
#endif
|
||||
|
||||
using E4::e4;
|
||||
#if __cplusplus < 202002L
|
||||
// expected-warning@-2{{a C++20 extension}}
|
||||
// expected-error@-3{{which is not a class}}
|
||||
#else
|
||||
auto Foo() { return e1; }
|
||||
auto Bar() { return e2; }
|
||||
#endif
|
||||
};
|
||||
|
||||
DER<float> x; // expected-note{{requested here}}
|
||||
DER<int> y;
|
||||
#if __cplusplus < 202002L
|
||||
// expected-note@-2{{requested here}}
|
||||
#else
|
||||
auto y1 = y.Foo();
|
||||
auto y2 = y.Bar();
|
||||
#endif
|
||||
} // namespace Four
|
||||
|
||||
namespace Five {
|
||||
template <unsigned I, unsigned K>
|
||||
struct Quux {
|
||||
enum class Q : unsigned; // expected-note{{member is declared here}}
|
||||
enum class R : unsigned { i = I,
|
||||
k = K };
|
||||
};
|
||||
|
||||
using Quux<1, 2>::Q::nothing; // expected-error{{implicit instantiation of undefined}}
|
||||
using Quux<1, 2>::R::i;
|
||||
#if __cplusplus < 202002L
|
||||
// expected-warning@-2{{a C++20 extension}}
|
||||
// expected-error@-3{{using declaration cannot refer to class member}}
|
||||
// expected-note@-4{{use a constexpr variable instead}}
|
||||
#endif
|
||||
|
||||
} // namespace Five
|
||||
|
||||
namespace Six {
|
||||
template <unsigned I, unsigned K>
|
||||
struct Quux {
|
||||
enum class Q : unsigned; // expected-note{{member is declared here}}
|
||||
enum class R : unsigned { i = I,
|
||||
k = K };
|
||||
};
|
||||
|
||||
template <unsigned I> struct Fido {
|
||||
using Quux<I, I>::Q::nothing; // expected-error{{implicit instantiation of undefined}}
|
||||
};
|
||||
|
||||
Fido<2> a; // expected-note{{in instantiation}}
|
||||
|
||||
} // namespace Six
|
||||
|
||||
namespace Seven {
|
||||
template <unsigned I, unsigned K>
|
||||
struct Quux {
|
||||
enum class R : unsigned { i = I,
|
||||
k = K };
|
||||
};
|
||||
|
||||
template <unsigned I> struct Toto {
|
||||
using Quux<I, I>::R::i;
|
||||
#if __cplusplus < 202002L
|
||||
// expected-warning@-2{{a C++20 extension}}
|
||||
// expected-error@-3{{refers into}}
|
||||
#else
|
||||
static_assert(unsigned(i) == I);
|
||||
#endif
|
||||
};
|
||||
|
||||
Toto<2> b;
|
||||
#if __cplusplus < 202002L
|
||||
// expected-note@-2{{in instantiation}}
|
||||
#endif
|
||||
|
||||
} // namespace Seven
|
||||
|
||||
namespace Eight {
|
||||
struct Kevin {
|
||||
enum class B { a };
|
||||
enum a {};
|
||||
};
|
||||
|
||||
using Kevin::B::a;
|
||||
#if __cplusplus < 202002L
|
||||
// expected-warning@-2{{a C++20 extension}}
|
||||
// expected-error@-3{{using declaration cannot refer to class member}}
|
||||
// expected-note@-4{{use a constexpr variable instead}}
|
||||
#endif
|
||||
using Kevin::B::a;
|
||||
#if __cplusplus < 202002L
|
||||
// expected-warning@-2{{a C++20 extension}}
|
||||
// expected-error@-3{{using declaration cannot refer to class member}}
|
||||
// expected-note@-4{{use a constexpr variable instead}}
|
||||
#endif
|
||||
|
||||
class X : Kevin {
|
||||
using Kevin::B::a; // expected-note{{previous using declaration}}
|
||||
#if __cplusplus < 202002L
|
||||
// expected-warning@-2{{a C++20 extension}}
|
||||
#endif
|
||||
using Kevin::a;
|
||||
using Kevin::B::a; // expected-error{{redeclaration of using declaration}}
|
||||
};
|
||||
|
||||
} // namespace Eight
|
||||
|
||||
namespace Nine {
|
||||
namespace Q {
|
||||
enum class Bob { a };
|
||||
using Bob::a;
|
||||
#if __cplusplus < 202002L
|
||||
// expected-warning@-2{{a C++20 extension}}
|
||||
#endif
|
||||
} // namespace Q
|
||||
|
||||
using Q::a;
|
||||
using Q::Bob::a;
|
||||
#if __cplusplus < 202002L
|
||||
// expected-warning@-2{{a C++20 extension}}
|
||||
#endif
|
||||
|
||||
#if __cplusplus >= 202002L
|
||||
struct Foo {
|
||||
using Q::a; // expected-note{{previous using declaration}}
|
||||
using Q::Bob::a;
|
||||
using Q::a; // expected-error{{redeclaration of using declaration}}
|
||||
};
|
||||
#endif
|
||||
} // namespace Nine
|
|
@ -1,4 +1,11 @@
|
|||
// RUN: %clang_cc1 -std=c++11 -verify %s
|
||||
// RUN: %clang_cc1 -std=c++17 -verify %s
|
||||
// RUN: %clang_cc1 -std=c++20 -verify %s
|
||||
|
||||
enum class EC { ec };
|
||||
using EC::ec; // expected-error {{using declaration cannot refer to a scoped enumerator}}
|
||||
using EC::ec;
|
||||
#if __cplusplus < 202002
|
||||
// expected-warning@-2 {{using declaration naming a scoped enumerator is a C++20 extension}}
|
||||
#else
|
||||
// expected-no-diagnostics
|
||||
#endif
|
||||
|
|
|
@ -301,8 +301,8 @@ namespace PR18044 {
|
|||
int E::*p; // expected-error {{does not point into a class}}
|
||||
using E::f; // expected-error {{no member named 'f'}}
|
||||
|
||||
using E::a; // expected-error {{using declaration cannot refer to a scoped enumerator}}
|
||||
E b = a; // expected-error {{undeclared}}
|
||||
using E::a; // expected-warning {{using declaration naming a scoped enumerator is a C++20 extension}}
|
||||
E b = a;
|
||||
}
|
||||
|
||||
namespace test11 {
|
||||
|
|
Loading…
Reference in New Issue