When diagnosing C++ [temp.expl.spec]p3 in C++98/03 mode, downgrade the

error to a warning if we're in a case that would be allowed in
C++0x. This "fixes" PR8084 by making Clang accept more code than GCC
and (non-strict) EDG do. 

Also, add the missing test case for the C++0x semantics, which should
have been in r113717.

llvm-svn: 113718
This commit is contained in:
Douglas Gregor 2010-09-12 05:24:55 +00:00
parent b1aab43887
commit 8ce63154d0
6 changed files with 267 additions and 14 deletions

View File

@ -1459,10 +1459,18 @@ def err_template_spec_decl_out_of_scope_global : Error<
"%select{class template|class template partial|function template|member "
"function|static data member|member class}0 specialization of %1 must "
"originally be declared in the global scope">;
def ext_template_spec_decl_out_of_scope_global : ExtWarn<
"%select{class template|class template partial|function template|member "
"function|static data member|member class}0 specialization of %1 must "
"originally be declared in the global scope; accepted as a C++0x extension">;
def err_template_spec_decl_out_of_scope : Error<
"%select{class template|class template partial|function template|member "
"function|static data member|member class}0 specialization of %1 must "
"originally be declared in namespace %2">;
def ext_template_spec_decl_out_of_scope : ExtWarn<
"%select{class template|class template partial|function template|member "
"function|static data member|member class}0 specialization of %1 must "
"originally be declared in namespace %2; accepted as a C++0x extension">;
def err_template_spec_redecl_out_of_scope : Error<
"%select{class template|class template partial|function template|member "
"function|static data member|member class}0 specialization of %1 not in a "

View File

@ -3454,13 +3454,19 @@ static bool CheckTemplateSpecializationScope(Sema &S,
// the specialized template.
if (!DC->InEnclosingNamespaceSetOf(SpecializedContext) &&
!(S.getLangOptions().CPlusPlus0x && DC->Encloses(SpecializedContext))) {
bool IsCPlusPlus0xExtension
= !S.getLangOptions().CPlusPlus0x && DC->Encloses(SpecializedContext);
if (isa<TranslationUnitDecl>(SpecializedContext))
S.Diag(Loc, diag::err_template_spec_decl_out_of_scope_global)
<< EntityKind << Specialized;
S.Diag(Loc, IsCPlusPlus0xExtension
? diag::ext_template_spec_decl_out_of_scope_global
: diag::err_template_spec_decl_out_of_scope_global)
<< EntityKind << Specialized;
else if (isa<NamespaceDecl>(SpecializedContext))
S.Diag(Loc, diag::err_template_spec_decl_out_of_scope)
<< EntityKind << Specialized
<< cast<NamedDecl>(SpecializedContext);
S.Diag(Loc, IsCPlusPlus0xExtension
? diag::ext_template_spec_decl_out_of_scope
: diag::err_template_spec_decl_out_of_scope)
<< EntityKind << Specialized
<< cast<NamedDecl>(SpecializedContext);
S.Diag(Specialized->getLocation(), diag::note_specialized_entity);
ComplainedAboutScope = true;

View File

@ -0,0 +1,239 @@
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++0x %s
// This test creates cases where implicit instantiations of various entities
// would cause a diagnostic, but provides expliict specializations for those
// entities that avoid the diagnostic. The specializations are alternately
// declarations and definitions, and the intent of this test is to verify
// that we allow specializations only in the appropriate namespaces (and
// nowhere else).
struct NonDefaultConstructible {
NonDefaultConstructible(int);
};
// C++ [temp.expl.spec]p1:
// An explicit specialization of any of the following:
// -- function template
namespace N0 {
template<typename T> void f0(T) {
T t;
}
template<> void f0(NonDefaultConstructible) { }
void test_f0(NonDefaultConstructible NDC) {
f0(NDC);
}
template<> void f0(int);
template<> void f0(long);
}
template<> void N0::f0(int) { } // okay
namespace N1 {
template<> void N0::f0(long) { } // expected-error{{not in a namespace enclosing}}
}
template<> void N0::f0(double) { }
struct X1 {
template<typename T> void f(T);
template<> void f(int); // expected-error{{in class scope}}
};
// -- class template
namespace N0 {
template<typename T>
struct X0 { // expected-note {{here}}
static T member;
void f1(T t) {
t = 17;
}
struct Inner : public T { }; // expected-note 2{{here}}
template<typename U>
struct InnerTemplate : public T { }; // expected-note 1{{explicitly specialized}} \
// expected-error{{base specifier}}
template<typename U>
void ft1(T t, U u);
};
}
template<typename T>
template<typename U>
void N0::X0<T>::ft1(T t, U u) {
t = u;
}
template<typename T> T N0::X0<T>::member;
template<> struct N0::X0<void> { };
N0::X0<void> test_X0;
namespace N1 {
template<> struct N0::X0<const void> { }; // expected-error{{class template specialization of 'X0' must originally be declared in namespace 'N0'}}
}
namespace N0 {
template<> struct X0<volatile void>;
}
template<> struct N0::X0<volatile void> {
void f1(void *);
};
// -- member function of a class template
template<> void N0::X0<void*>::f1(void *) { }
void test_spec(N0::X0<void*> xvp, void *vp) {
xvp.f1(vp);
}
namespace N0 {
template<> void X0<volatile void>::f1(void *) { } // expected-error{{no function template matches}}
template<> void X0<const volatile void*>::f1(const volatile void*);
}
void test_x0_cvvoid(N0::X0<const volatile void*> x0, const volatile void *cvp) {
x0.f1(cvp); // okay: we've explicitly specialized
}
// -- static data member of a class template
namespace N0 {
// This actually tests p15; the following is a declaration, not a definition.
template<>
NonDefaultConstructible X0<NonDefaultConstructible>::member;
template<> long X0<long>::member = 17;
template<> float X0<float>::member;
template<> double X0<double>::member;
}
NonDefaultConstructible &get_static_member() {
return N0::X0<NonDefaultConstructible>::member;
}
template<> int N0::X0<int>::member;
template<> float N0::X0<float>::member = 3.14f;
namespace N1 {
template<> double N0::X0<double>::member = 3.14; // expected-error{{not in a namespace enclosing}}
}
// -- member class of a class template
namespace N0 {
template<>
struct X0<void*>::Inner { };
template<>
struct X0<int>::Inner { };
template<>
struct X0<unsigned>::Inner;
template<>
struct X0<float>::Inner;
template<>
struct X0<double>::Inner; // expected-note{{forward declaration}}
}
template<>
struct N0::X0<long>::Inner { };
template<>
struct N0::X0<float>::Inner { };
namespace N1 {
template<>
struct N0::X0<unsigned>::Inner { }; // expected-error{{member class specialization}}
template<>
struct N0::X0<unsigned long>::Inner { }; // expected-error{{member class specialization}}
};
N0::X0<void*>::Inner inner0;
N0::X0<int>::Inner inner1;
N0::X0<long>::Inner inner2;
N0::X0<float>::Inner inner3;
N0::X0<double>::Inner inner4; // expected-error{{incomplete}}
// -- member class template of a class template
namespace N0 {
template<>
template<>
struct X0<void*>::InnerTemplate<int> { };
template<> template<>
struct X0<int>::InnerTemplate<int>; // expected-note{{forward declaration}}
template<> template<>
struct X0<int>::InnerTemplate<long>;
template<> template<>
struct X0<int>::InnerTemplate<double>;
}
template<> template<>
struct N0::X0<int>::InnerTemplate<long> { }; // okay
template<> template<>
struct N0::X0<int>::InnerTemplate<float> { };
namespace N1 {
template<> template<>
struct N0::X0<int>::InnerTemplate<double> { }; // expected-error{{enclosing}}
}
N0::X0<void*>::InnerTemplate<int> inner_template0;
N0::X0<int>::InnerTemplate<int> inner_template1; // expected-error{{incomplete}}
N0::X0<int>::InnerTemplate<long> inner_template2;
N0::X0<int>::InnerTemplate<unsigned long> inner_template3; // expected-note{{instantiation}}
// -- member function template of a class template
namespace N0 {
template<>
template<>
void X0<void*>::ft1(void*, const void*) { }
template<> template<>
void X0<void*>::ft1(void *, int);
template<> template<>
void X0<void*>::ft1(void *, unsigned);
template<> template<>
void X0<void*>::ft1(void *, long);
}
template<> template<>
void N0::X0<void*>::ft1(void *, unsigned) { } // okay
template<> template<>
void N0::X0<void*>::ft1(void *, float) { }
namespace N1 {
template<> template<>
void N0::X0<void*>::ft1(void *, long) { } // expected-error{{enclosing}}
}
void test_func_template(N0::X0<void *> xvp, void *vp, const void *cvp,
int i, unsigned u) {
xvp.ft1(vp, cvp);
xvp.ft1(vp, i);
xvp.ft1(vp, u);
}

View File

@ -36,7 +36,7 @@ namespace N1 {
template<> void N0::f0(long) { } // expected-error{{not in a namespace enclosing}}
}
template<> void N0::f0(double) { } // expected-error{{originally be declared}}
template<> void N0::f0(double) { } // expected-warning{{originally be declared}}
struct X1 {
template<typename T> void f(T);
@ -75,7 +75,7 @@ void N0::X0<T>::ft1(T t, U u) {
template<typename T> T N0::X0<T>::member;
template<> struct N0::X0<void> { }; // expected-error{{originally}}
template<> struct N0::X0<void> { }; // expected-warning{{originally}}
N0::X0<void> test_X0;
namespace N1 {
@ -91,7 +91,7 @@ template<> struct N0::X0<volatile void> {
};
// -- member function of a class template
template<> void N0::X0<void*>::f1(void *) { } // expected-error{{member function specialization}}
template<> void N0::X0<void*>::f1(void *) { } // expected-warning{{member function specialization}}
void test_spec(N0::X0<void*> xvp, void *vp) {
xvp.f1(vp);
@ -124,7 +124,7 @@ NonDefaultConstructible &get_static_member() {
return N0::X0<NonDefaultConstructible>::member;
}
template<> int N0::X0<int>::member; // expected-error{{originally}}
template<> int N0::X0<int>::member; // expected-warning{{originally}}
template<> float N0::X0<float>::member = 3.14f;
@ -152,7 +152,7 @@ namespace N0 {
}
template<>
struct N0::X0<long>::Inner { }; // expected-error{{originally}}
struct N0::X0<long>::Inner { }; // expected-warning{{originally}}
template<>
struct N0::X0<float>::Inner { };
@ -191,7 +191,7 @@ template<> template<>
struct N0::X0<int>::InnerTemplate<long> { }; // okay
template<> template<>
struct N0::X0<int>::InnerTemplate<float> { }; // expected-error{{class template specialization}}
struct N0::X0<int>::InnerTemplate<float> { }; // expected-warning{{class template specialization}}
namespace N1 {
template<> template<>
@ -223,7 +223,7 @@ template<> template<>
void N0::X0<void*>::ft1(void *, unsigned) { } // okay
template<> template<>
void N0::X0<void*>::ft1(void *, float) { } // expected-error{{function template specialization}}
void N0::X0<void*>::ft1(void *, float) { } // expected-warning{{function template specialization}}
namespace N1 {
template<> template<>

View File

@ -86,7 +86,7 @@ namespace N {
template<> struct N::B<int> { }; // okay
template<> struct N::B<float> { }; // expected-error{{originally}}
template<> struct N::B<float> { }; // expected-warning{{originally}}
namespace M {
template<> struct ::N::B<short> { }; // expected-error{{class template specialization of 'B' not in a namespace enclosing 'N'}}

View File

@ -9,7 +9,7 @@ namespace N {
}
template<typename T>
struct N::M::A<T*> { }; // expected-error{{originally}}
struct N::M::A<T*> { }; // expected-warning{{originally}}
// C++ [temp.class.spec]p9
// bullet 1