From: Richard Smith Date: Mon, 26 Mar 2012 20:28:16 +0000 (+0000) Subject: Add a special-case diagnostic for one of the more obnoxious special cases of X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=0724b7c43007d978c46f890dcd2ab3c8d3c22920;p=clang Add a special-case diagnostic for one of the more obnoxious special cases of unscoped enumeration members: an enumerator name which is visible in the out-of-class definition of a member of a templated class might not actually exist in the instantiation of that class, if the enumeration is also lexically defined outside the class definition and is explicitly specialized. Depending on the result of a CWG discussion, we may have a different resolution for a class of problems in this area, but this fixes the immediate issue of a crash-on-invalid / accepts-invalid (depending on +Asserts). Thanks to Johannes Schaub for digging into the standard wording to find how this case is currently specified to behave. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@153461 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 36232b1e98..7df189d5f9 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -3542,6 +3542,11 @@ def err_member_not_yet_instantiated : Error< def note_non_instantiated_member_here : Note< "not-yet-instantiated member is declared here">; +def err_enumerator_does_not_exist : Error< + "enumerator %0 does not exist in instantiation of %1">; +def note_enum_specialized_here : Note< + "enum %0 was explicitly specialized here">; + def err_member_redeclared : Error<"class member cannot be redeclared">; def err_member_name_of_class : Error<"member %0 has the same name as its class">; def err_member_def_undefined_record : Error< diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index 8729a39f9b..e767f6a7d9 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -3289,6 +3289,20 @@ NamedDecl *Sema::FindInstantiatedDecl(SourceLocation Loc, NamedDecl *D, << D->getDeclName() << Context.getTypeDeclType(cast(ParentDC)); Diag(D->getLocation(), diag::note_non_instantiated_member_here); + } else if (EnumConstantDecl *ED = dyn_cast(D)) { + // This enumeration constant was found when the template was defined, + // but can't be found in the instantiation. This can happen if an + // unscoped enumeration member is explicitly specialized. + EnumDecl *Enum = cast(ED->getLexicalDeclContext()); + EnumDecl *Spec = cast(FindInstantiatedDecl(Loc, Enum, + TemplateArgs)); + assert(Spec->getTemplateSpecializationKind() == + TSK_ExplicitSpecialization); + Diag(Loc, diag::err_enumerator_does_not_exist) + << D->getDeclName() + << Context.getTypeDeclType(cast(Spec->getDeclContext())); + Diag(Spec->getLocation(), diag::note_enum_specialized_here) + << Context.getTypeDeclType(Spec); } else { // We should have found something, but didn't. llvm_unreachable("Unable to find instantiation of declaration!"); diff --git a/test/SemaCXX/enum-unscoped-nonexistent.cpp b/test/SemaCXX/enum-unscoped-nonexistent.cpp new file mode 100644 index 0000000000..d49800caa6 --- /dev/null +++ b/test/SemaCXX/enum-unscoped-nonexistent.cpp @@ -0,0 +1,39 @@ +// RUN: %clang_cc1 -std=c++11 -verify %s + +struct Base { + static const int a = 1; +}; +template struct S : Base { + enum E : int; + constexpr int f(); + constexpr int g(); // expected-note {{declared here}} + void h(); +}; +template<> enum S::E : int {}; // expected-note {{enum 'S::E' was explicitly specialized here}} +template<> enum S::E : int { b = 2 }; +template<> enum S::E : int { a = 4 }; +template enum S::E : int { b = 8 }; + +// The unqualified-id here names a member of the non-dependent base class Base +// and not the injected enumerator name 'a' from the specialization. +template constexpr int S::f() { return a; } +static_assert(S().f() == 1, ""); +static_assert(S().f() == 1, ""); + +// The unqualified-id here names a member of the current instantiation, which +// bizarrely might not exist in some instantiations. +template constexpr int S::g() { return b; } // expected-error {{enumerator 'b' does not exist in instantiation of 'S'}} +static_assert(S().g() == 1, ""); // expected-note {{here}} expected-error {{not an integral constant expression}} expected-note {{undefined}} +static_assert(S().g() == 2, ""); +static_assert(S().g() == 8, ""); + +// 'b' is type-dependent, so these assertions should not fire before 'h' is +// instantiated. +template void S::h() { + char c[S::b]; + static_assert(b != 8, ""); + static_assert(sizeof(c) != 8, ""); +} +void f() { + S().h(); // ok, b == 2 +}