+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
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);
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]
/* 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)
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)
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;
}
{
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);
/* [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;
}
}
}
}
+/* 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
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 "
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);
--- /dev/null
+// 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 } }
// PR c++/16224
namespace io {
- template <typename> int foo(); // { dg-error "" }
+ template <typename> int foo();
}
using namespace io;
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" }
}
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; }
{
// 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" }
}
return a;
}
-template<> void bar::foo<int>() // { dg-error "different namespace" }
+template<> void bar::foo<int>() // { dg-error "different namespace" "" { target c++98_only } }
{
}
// the template
namespace N {
- template <class T> class foo; // { dg-error "" } referenced below
+ template <class T> class foo;
}
using namespace N;
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 } }
}
#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&>;
#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)>;
#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&>;
#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&>;
#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>;
// { 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>;
// { 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>;
// 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>;
// { 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>;
// 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>;
#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
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>;
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>;
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>;
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>;