DR 374 - specialization in outer namespace

PR c++/56840
	* pt.c (check_specialization_namespace): Allow any enclosing
	namespace.
	(check_unqualified_spec_or_inst): New.
	(check_explicit_specialization): Call it.
	* parser.c (cp_parser_elaborated_type_specifier)
	(cp_parser_class_head): Call it.

From-SVN: r242348
This commit is contained in:
Jason Merrill 2016-11-13 01:51:23 -05:00 committed by Jason Merrill
parent 478eca6464
commit ba1e69c03f
28 changed files with 190 additions and 132 deletions

View file

@ -1,3 +1,14 @@
2016-11-12 Jason Merrill <jason@redhat.com>
DR 374
PR c++/56840
* pt.c (check_specialization_namespace): Allow any enclosing
namespace.
(check_unqualified_spec_or_inst): New.
(check_explicit_specialization): Call it.
* parser.c (cp_parser_elaborated_type_specifier)
(cp_parser_class_head): Call it.
2016-11-10 Jason Merrill <jason@redhat.com>
PR c++/77337

View file

@ -6082,6 +6082,7 @@ extern void reset_specialization (void);
extern void end_specialization (void);
extern void begin_explicit_instantiation (void);
extern void end_explicit_instantiation (void);
extern void check_unqualified_spec_or_inst (tree, location_t);
extern tree check_explicit_specialization (tree, tree, int, int);
extern int num_template_headers_for_class (tree);
extern void check_template_variable (tree);

View file

@ -7868,8 +7868,7 @@ check_class_member_definition_namespace (tree decl)
diagnostics. */
if (processing_specialization)
return;
/* There are no restrictions on the placement of
explicit instantiations. */
/* We check this in check_explicit_instantiation_namespace. */
if (processing_explicit_instantiation)
return;
/* [class.mfct]

View file

@ -3558,7 +3558,7 @@ set_decl_namespace (tree decl, tree scope, bool friendp)
/* Since decl is a function, old should contain a function decl. */
if (!is_overloaded_fn (old))
goto complain;
/* A template can be explicitly specialized in any namespace. */
/* We handle these in check_explicit_instantiation_namespace. */
if (processing_explicit_instantiation)
return;
if (processing_template_decl || processing_specialization)

View file

@ -17004,24 +17004,28 @@ cp_parser_elaborated_type_specifier (cp_parser* parser,
globalscope = cp_parser_global_scope_opt (parser,
/*current_scope_valid_p=*/false);
/* Look for the nested-name-specifier. */
tree nested_name_specifier;
if (tag_type == typename_type && !globalscope)
{
if (!cp_parser_nested_name_specifier (parser,
nested_name_specifier
= cp_parser_nested_name_specifier (parser,
/*typename_keyword_p=*/true,
/*check_dependency_p=*/true,
/*type_p=*/true,
is_declaration))
is_declaration);
if (!nested_name_specifier)
return error_mark_node;
}
else
/* Even though `typename' is not present, the proposed resolution
to Core Issue 180 says that in `class A<T>::B', `B' should be
considered a type-name, even if `A<T>' is dependent. */
cp_parser_nested_name_specifier_opt (parser,
/*typename_keyword_p=*/true,
/*check_dependency_p=*/true,
/*type_p=*/true,
is_declaration);
nested_name_specifier
= cp_parser_nested_name_specifier_opt (parser,
/*typename_keyword_p=*/true,
/*check_dependency_p=*/true,
/*type_p=*/true,
is_declaration);
/* For everything but enumeration types, consider a template-id.
For an enumeration type, consider only a plain identifier. */
if (tag_type != enum_type)
@ -17069,8 +17073,18 @@ cp_parser_elaborated_type_specifier (cp_parser* parser,
else if (tag_type == typename_type && TREE_CODE (decl) != TYPE_DECL)
;
else if (TREE_CODE (decl) == TYPE_DECL)
type = check_elaborated_type_specifier (tag_type, decl,
/*allow_template_p=*/true);
{
type = check_elaborated_type_specifier (tag_type, decl,
/*allow_template_p=*/true);
/* If the next token is a semicolon, this must be a specialization,
instantiation, or friend declaration. Check the scope while we
still know whether or not we had a nested-name-specifier. */
if (type != error_mark_node
&& !nested_name_specifier && !is_friend
&& cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON))
check_unqualified_spec_or_inst (type, token->location);
}
else if (decl == error_mark_node)
type = error_mark_node;
}
@ -22336,6 +22350,11 @@ cp_parser_class_head (cp_parser* parser,
{
type = TREE_TYPE (id);
type = maybe_process_partial_specialization (type);
/* Check the scope while we still know whether or not we had a
nested-name-specifier. */
if (type != error_mark_node)
check_unqualified_spec_or_inst (type, type_start_token->location);
}
if (nested_name_specifier)
pushed_scope = push_scope (nested_name_specifier);

View file

@ -772,28 +772,29 @@ check_specialization_namespace (tree tmpl)
/* [tmpl.expl.spec]
An explicit specialization shall be declared in the namespace of
which the template is a member, or, for member templates, in the
namespace of which the enclosing class or enclosing class
template is a member. An explicit specialization of a member
function, member class or static data member of a class template
shall be declared in the namespace of which the class template is
a member. */
An explicit specialization shall be declared in a namespace enclosing the
specialized template. An explicit specialization whose declarator-id is
not qualified shall be declared in the nearest enclosing namespace of the
template, or, if the namespace is inline (7.3.1), any namespace from its
enclosing namespace set. */
if (current_scope() != DECL_CONTEXT (tmpl)
&& !at_namespace_scope_p ())
{
error ("specialization of %qD must appear at namespace scope", tmpl);
return false;
}
if (is_associated_namespace (current_namespace, tpl_ns))
/* Same or super-using namespace. */
if (cxx_dialect < cxx11
? is_associated_namespace (current_namespace, tpl_ns)
: is_ancestor (current_namespace, tpl_ns))
/* Same or enclosing namespace. */
return true;
else
{
permerror (input_location,
"specialization of %qD in different namespace", tmpl);
permerror (DECL_SOURCE_LOCATION (tmpl),
" from definition of %q#D", tmpl);
inform (DECL_SOURCE_LOCATION (tmpl),
" from definition of %q#D", tmpl);
return false;
}
}
@ -2586,6 +2587,36 @@ check_template_variable (tree decl)
}
}
/* An explicit specialization whose declarator-id or class-head-name is not
qualified shall be declared in the nearest enclosing namespace of the
template, or, if the namespace is inline (7.3.1), any namespace from its
enclosing namespace set.
If the name declared in the explicit instantiation is an unqualified name,
the explicit instantiation shall appear in the namespace where its template
is declared or, if that namespace is inline (7.3.1), any namespace from its
enclosing namespace set. */
void
check_unqualified_spec_or_inst (tree t, location_t loc)
{
tree tmpl = most_general_template (t);
if (DECL_NAMESPACE_SCOPE_P (tmpl)
&& !is_associated_namespace (current_namespace,
CP_DECL_CONTEXT (tmpl)))
{
if (processing_specialization)
permerror (loc, "explicit specialization of %qD outside its "
"namespace must use a nested-name-specifier", tmpl);
else if (processing_explicit_instantiation
&& cxx_dialect >= cxx11)
/* This was allowed in C++98, so only pedwarn. */
pedwarn (loc, OPT_Wpedantic, "explicit instantiation of %qD "
"outside its namespace must use a nested-name-"
"specifier", tmpl);
}
}
/* Check to see if the function just declared, as indicated in
DECLARATOR, and in DECL, is a specialization of a function
template. We may also discover that the declaration is an explicit
@ -2949,15 +2980,8 @@ check_explicit_specialization (tree declarator,
return error_mark_node;
else
{
if (!ctype && !was_template_id
&& (specialization || member_specialization
|| explicit_instantiation)
&& !is_associated_namespace (CP_DECL_CONTEXT (decl),
CP_DECL_CONTEXT (tmpl)))
error ("%qD is not declared in %qD",
tmpl, current_namespace);
else if (TREE_CODE (decl) == FUNCTION_DECL
&& DECL_HIDDEN_FRIEND_P (tmpl))
if (TREE_CODE (decl) == FUNCTION_DECL
&& DECL_HIDDEN_FRIEND_P (tmpl))
{
if (pedwarn (DECL_SOURCE_LOCATION (decl), 0,
"friend declaration %qD is not visible to "
@ -2965,6 +2989,9 @@ check_explicit_specialization (tree declarator,
inform (DECL_SOURCE_LOCATION (tmpl),
"friend declaration here");
}
else if (!ctype && !is_friend
&& CP_DECL_CONTEXT (decl) == current_namespace)
check_unqualified_spec_or_inst (tmpl, DECL_SOURCE_LOCATION (decl));
tree gen_tmpl = most_general_template (tmpl);

View file

@ -0,0 +1,13 @@
// In C++11 explicit instantiation without a nested-name-specifier must be in
// the same namespace.
namespace N {
template <class T> class foo {};
template <class T> class bar {};
}
using N::bar;
template class bar<int>; // { dg-error "" "" { target c++11 } }
using namespace N;
template class foo<int>; // { dg-error "" "" { target c++11 } }

View file

@ -1,7 +1,7 @@
// PR c++/16224
namespace io {
template <typename> int foo(); // { dg-error "" }
template <typename> int foo();
}
using namespace io;

View file

@ -1,10 +1,10 @@
namespace N {
template <typename T>
struct S {
void f() {} // { dg-error "definition" }
void f() {}
};
}
namespace K {
template <> void N::S<char>::f() {} // { dg-error "different namespace" }
template <> void N::S<char>::f() {} // { dg-error "namespace" }
}

View file

@ -8,9 +8,9 @@ struct basic_string
namespace MyNS {
class MyClass {
template <typename T>
T test() { } /* { dg-error "from definition" } */
T test() { } /* { dg-message "from definition" "" { target c++98_only } } */
};
}
template <>
basic_string MyNS::MyClass::test() /* { dg-error "specialization of" } */
basic_string MyNS::MyClass::test() /* { dg-error "specialization of" "" { target c++98_only } }*/
{ return 1; }

View file

@ -4,7 +4,7 @@ namespace bar
{
// trick it to provide some prior declaration
template<class T>
void foo(); // { dg-error "definition" }
void foo();
template<class T>class X; // { dg-message "note: previous declaration" }
}
@ -15,7 +15,7 @@ bar::foo(T const &a) // { dg-error "" "" { xfail *-*-* } } not declared in b
return a;
}
template<> void bar::foo<int>() // { dg-error "different namespace" }
template<> void bar::foo<int>() // { dg-error "different namespace" "" { target c++98_only } }
{
}

View file

@ -7,7 +7,7 @@
// the template
namespace N {
template <class T> class foo; // { dg-error "" } referenced below
template <class T> class foo;
}
using namespace N;

View file

@ -13,8 +13,8 @@ namespace Outer {
namespace Core = Core_Real;
namespace Core_Real {
template<class T> void Foo (T *) {} // { dg-error "definition" }
template<class T> void Foo (T *) {}
}
template<> void Core::Foo<> (Render_Real::Type *) {} // { dg-error "" }
template<> void Core::Foo<> (Render_Real::Type *) {} // { dg-error "" "" { target c++98_only } }
}

View file

@ -25,9 +25,8 @@
#include <testsuite_tr1.h>
using namespace __gnu_test;
using std::future;
template class future<int>;
template class future<int&>;
template class future<void>;
template class future<ClassType>;
template class future<ClassType&>;
template class std::future<int>;
template class std::future<int&>;
template class std::future<void>;
template class std::future<ClassType>;
template class std::future<ClassType&>;

View file

@ -25,9 +25,8 @@
#include <testsuite_tr1.h>
using namespace __gnu_test;
using std::packaged_task;
template class packaged_task<int()>;
template class packaged_task<int&()>;
template class packaged_task<void()>;
template class packaged_task<ClassType(int)>;
template class packaged_task<AbstractClass&(int)>;
template class std::packaged_task<int()>;
template class std::packaged_task<int&()>;
template class std::packaged_task<void()>;
template class std::packaged_task<ClassType(int)>;
template class std::packaged_task<AbstractClass&(int)>;

View file

@ -25,9 +25,8 @@
#include <testsuite_tr1.h>
using namespace __gnu_test;
using std::promise;
template class promise<int>;
template class promise<int&>;
template class promise<void>;
template class promise<ClassType>;
template class promise<ClassType&>;
template class std::promise<int>;
template class std::promise<int&>;
template class std::promise<void>;
template class std::promise<ClassType>;
template class std::promise<ClassType&>;

View file

@ -25,9 +25,8 @@
#include <testsuite_tr1.h>
using namespace __gnu_test;
using std::shared_future;
template class shared_future<int>;
template class shared_future<int&>;
template class shared_future<void>;
template class shared_future<ClassType>;
template class shared_future<ClassType&>;
template class std::shared_future<int>;
template class std::shared_future<int&>;
template class std::shared_future<void>;
template class std::shared_future<ClassType>;
template class std::shared_future<ClassType&>;

View file

@ -21,7 +21,6 @@
#include <ext/numeric_traits.h>
using __gnu_cxx::__numeric_traits;
template struct __numeric_traits<short>;
template struct __numeric_traits<unsigned short>;
template struct __numeric_traits<double>;
template struct __gnu_cxx::__numeric_traits<short>;
template struct __gnu_cxx::__numeric_traits<unsigned short>;
template struct __gnu_cxx::__numeric_traits<double>;

View file

@ -23,8 +23,7 @@
// { dg-do compile }
using namespace __gnu_test;
using std::tr1::enable_shared_from_this;
template class enable_shared_from_this<int>;
template class enable_shared_from_this<void>;
template class enable_shared_from_this<ClassType>;
template class enable_shared_from_this<IncompleteClass>;
template class std::tr1::enable_shared_from_this<int>;
template class std::tr1::enable_shared_from_this<void>;
template class std::tr1::enable_shared_from_this<ClassType>;
template class std::tr1::enable_shared_from_this<IncompleteClass>;

View file

@ -23,8 +23,7 @@
// { dg-do compile }
using namespace __gnu_test;
using std::tr1::shared_ptr;
template class shared_ptr<int>;
template class shared_ptr<void>;
template class shared_ptr<ClassType>;
template class shared_ptr<IncompleteClass>;
template class std::tr1::shared_ptr<int>;
template class std::tr1::shared_ptr<void>;
template class std::tr1::shared_ptr<ClassType>;
template class std::tr1::shared_ptr<IncompleteClass>;

View file

@ -26,8 +26,7 @@
// library this checks the templates can be instantiated for non-default
// lock policy, for a single-threaded lib this is redundant but harmless.
using namespace __gnu_test;
using std::tr1::__shared_ptr;
using std::tr1::_S_single;
template class __shared_ptr<int, _S_single>;
template class __shared_ptr<ClassType, _S_single>;
template class __shared_ptr<IncompleteClass, _S_single>;
template class std::tr1::__shared_ptr<int, _S_single>;
template class std::tr1::__shared_ptr<ClassType, _S_single>;
template class std::tr1::__shared_ptr<IncompleteClass, _S_single>;

View file

@ -23,8 +23,7 @@
// { dg-do compile }
using namespace __gnu_test;
using std::tr1::weak_ptr;
template class weak_ptr<int>;
template class weak_ptr<void>;
template class weak_ptr<ClassType>;
template class weak_ptr<IncompleteClass>;
template class std::tr1::weak_ptr<int>;
template class std::tr1::weak_ptr<void>;
template class std::tr1::weak_ptr<ClassType>;
template class std::tr1::weak_ptr<IncompleteClass>;

View file

@ -26,9 +26,8 @@
// library this checks the templates can be instantiated for non-default
// lock policy, for a single-threaded lib this is redundant but harmless.
using namespace __gnu_test;
using std::tr1::__weak_ptr;
using std::tr1::_S_single;
template class __weak_ptr<int, _S_single>;
template class __weak_ptr<void, _S_single>;
template class __weak_ptr<ClassType, _S_single>;
template class __weak_ptr<IncompleteClass, _S_single>;
template class std::tr1::__weak_ptr<int, _S_single>;
template class std::tr1::__weak_ptr<void, _S_single>;
template class std::tr1::__weak_ptr<ClassType, _S_single>;
template class std::tr1::__weak_ptr<IncompleteClass, _S_single>;

View file

@ -24,27 +24,25 @@
#include <string>
#include <tr1/functional>
using namespace std::tr1;
// Verify that we can instantiate hash for every required type.
template class hash<bool>;
template class hash<char>;
template class hash<signed char>;
template class hash<unsigned char>;
template class hash<short>;
template class hash<int>;
template class hash<long>;
template class hash<unsigned short>;
template class hash<unsigned int>;
template class hash<unsigned long>;
template class hash<float>;
template class hash<double>;
template class hash<long double>;
template class hash<void*>;
template class hash<std::string>;
template class std::tr1::hash<bool>;
template class std::tr1::hash<char>;
template class std::tr1::hash<signed char>;
template class std::tr1::hash<unsigned char>;
template class std::tr1::hash<short>;
template class std::tr1::hash<int>;
template class std::tr1::hash<long>;
template class std::tr1::hash<unsigned short>;
template class std::tr1::hash<unsigned int>;
template class std::tr1::hash<unsigned long>;
template class std::tr1::hash<float>;
template class std::tr1::hash<double>;
template class std::tr1::hash<long double>;
template class std::tr1::hash<void*>;
template class std::tr1::hash<std::string>;
#ifdef _GLIBCXX_USE_WCHAR_T
template class hash<wchar_t>;
template class hash<std::wstring>;
template class std::tr1::hash<wchar_t>;
template class std::tr1::hash<std::wstring>;
#endif

View file

@ -30,13 +30,13 @@ using std::allocator;
using std::pair;
using std::equal_to;
template class unordered_map<string, float>;
template class unordered_map<string, int,
template class std::tr1::unordered_map<string, float>;
template class std::tr1::unordered_map<string, int,
hash<string>, equal_to<string>,
allocator<pair<const string, int> > >;
template class unordered_map<string, float,
template class std::tr1::unordered_map<string, float,
hash<string>, equal_to<string>,
allocator<char> >;
template class __unordered_map<string, int,
template class std::tr1::__unordered_map<string, int,
hash<string>, equal_to<string>,
allocator<pair<const string, int> >, true>;

View file

@ -30,13 +30,13 @@ using std::equal_to;
using std::allocator;
using std::pair;
template class unordered_multimap<string, float>;
template class unordered_multimap<string, int,
template class std::tr1::unordered_multimap<string, float>;
template class std::tr1::unordered_multimap<string, int,
hash<string>, equal_to<string>,
allocator<pair<const string, int> > >;
template class unordered_multimap<string, float,
template class std::tr1::unordered_multimap<string, float,
hash<string>, equal_to<string>,
allocator<char> >;
template class __unordered_multimap<string, int,
template class std::tr1::__unordered_multimap<string, int,
hash<string>, equal_to<string>,
allocator<pair<const string, int> >, true>;

View file

@ -27,10 +27,10 @@ using namespace std::tr1;
using std::equal_to;
using std::allocator;
template class unordered_multiset<int>;
template class unordered_multiset<float, hash<float>, equal_to<float>,
template class std::tr1::unordered_multiset<int>;
template class std::tr1::unordered_multiset<float, hash<float>, equal_to<float>,
allocator<float> >;
template class unordered_multiset<int, hash<int>, equal_to<int>,
template class std::tr1::unordered_multiset<int, hash<int>, equal_to<int>,
allocator<char> >;
template class __unordered_multiset<float, hash<float>, equal_to<float>,
template class std::tr1::__unordered_multiset<float, hash<float>, equal_to<float>,
allocator<float>, true>;

View file

@ -27,10 +27,10 @@ using namespace std::tr1;
using std::equal_to;
using std::allocator;
template class unordered_set<int>;
template class unordered_set<float, hash<float>, equal_to<float>,
allocator<float> >;
template class unordered_set<int, hash<int>, equal_to<int>,
allocator<char> >;
template class __unordered_set<float, hash<float>, equal_to<float>,
allocator<float>, true>;
template class std::tr1::unordered_set<int>;
template class std::tr1::unordered_set<float, hash<float>, equal_to<float>,
allocator<float> >;
template class std::tr1::unordered_set<int, hash<int>, equal_to<int>,
allocator<char> >;
template class std::tr1::__unordered_set<float, hash<float>, equal_to<float>,
allocator<float>, true>;