diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 9fdd4c7906b..d9ad73cdbde 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,28 @@ +2014-08-01 Braden Obrzut + + Implement constexpr variable templates + * decl.c (grokvardecl): Handle specializations of variable templates. + (grokdeclarator): Handle variable template id expressions and NULL_TREE + return from grokvardecl. + * decl2.c (check_member_template): Allow declaration of template member + variables. + * parser.c (cp_parser_template_id): Build a TEMPLATE_ID_EXPR for + variable templates. + * pt.c (check_template_variable): Accept variable temploids at + non-class scope. + (push_template_decl_real): The current instantiation of a template + can be a VAR_DECL. + (determine_specialization): Accept variable templates. + (check_explicit_specialization): Handle and check for malformed + variable template specializations. + (lookup_template_variable): New. + (tsubst_decl): Handle variable template specializations. + (do_decl_instantiation): Handle template variables. + (instantiate_decl): Handle template variables. + * semantics.c (finish_template_variable): New. + (finish_id_expression): Instantiate variable templates. + * cp-tree.h (variable_template_p): New. + 2014-08-02 Paolo Carlini PR c++/15339 diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 622de9c42f4..ffb44d11ae5 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -5045,6 +5045,17 @@ class_of_this_parm (const_tree fntype) return TREE_TYPE (type_of_this_parm (fntype)); } +/* True if T designates a variable template declaration. */ +inline bool +variable_template_p (tree t) +{ + if (TREE_CODE (t) != TEMPLATE_DECL) + return false; + if (tree r = DECL_TEMPLATE_RESULT (t)) + return VAR_P (r); + return false; +} + /* A parameter list indicating for a function with no parameters, e.g "int f(void)". */ extern cp_parameter_declarator *no_parameters; @@ -5572,6 +5583,7 @@ extern bool redeclare_class_template (tree, tree); extern tree lookup_template_class (tree, tree, tree, tree, int, tsubst_flags_t); extern tree lookup_template_function (tree, tree); +extern tree lookup_template_variable (tree, tree); extern int uses_template_parms (tree); extern int uses_template_parms_level (tree, int); extern bool in_template_function (void); @@ -5834,6 +5846,7 @@ extern tree perform_koenig_lookup (tree, vec *, tsubst_flags_t); extern tree finish_call_expr (tree, vec **, bool, bool, tsubst_flags_t); +extern tree finish_template_variable (tree); extern tree finish_increment_expr (tree, enum tree_code); extern tree finish_this_expr (void); extern tree finish_pseudo_destructor_expr (tree, tree, tree, location_t); diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index d4dde6129aa..acc1192eacf 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -80,8 +80,8 @@ static int ambi_op_p (enum tree_code); static int unary_op_p (enum tree_code); static void push_local_name (tree); static tree grok_reference_init (tree, tree, tree, int); -static tree grokvardecl (tree, tree, const cp_decl_specifier_seq *, - int, int, tree); +static tree grokvardecl (tree, tree, tree, const cp_decl_specifier_seq *, + int, int, int, tree); static int check_static_variable_definition (tree, tree); static void record_unknown_type (tree, const char *); static tree builtin_function_1 (tree, tree, bool); @@ -7968,9 +7968,11 @@ set_linkage_for_static_data_member (tree decl) static tree grokvardecl (tree type, tree name, + tree orig_declarator, const cp_decl_specifier_seq *declspecs, int initialized, int constp, + int template_count, tree scope) { tree decl; @@ -8000,7 +8002,10 @@ grokvardecl (tree type, || (TREE_CODE (scope) == NAMESPACE_DECL && current_lang_name != lang_name_cplusplus) /* Similarly for static data members. */ - || TYPE_P (scope))) + || TYPE_P (scope) + /* Similarly for explicit specializations. */ + || (orig_declarator + && TREE_CODE (orig_declarator) == TEMPLATE_ID_EXPR))) decl = build_lang_decl (VAR_DECL, name, type); else decl = build_decl (input_location, VAR_DECL, name, type); @@ -8068,7 +8073,12 @@ grokvardecl (tree type, else DECL_INTERFACE_KNOWN (decl) = 1; - return decl; + // Handle explicit specializations and instantiations of variable templates. + if (orig_declarator) + decl = check_explicit_specialization (orig_declarator, decl, + template_count, 0); + + return decl != error_mark_node ? decl : NULL_TREE; } /* Create and return a canonical pointer to member function type, for @@ -8962,8 +8972,13 @@ grokdeclarator (const cp_declarator *declarator, dname = fns; if (!identifier_p (dname)) { - gcc_assert (is_overloaded_fn (dname)); - dname = DECL_NAME (get_first_fn (dname)); + if (variable_template_p (dname)) + dname = DECL_NAME (dname); + else + { + gcc_assert (is_overloaded_fn (dname)); + dname = DECL_NAME (get_first_fn (dname)); + } } } /* Fall through. */ @@ -10004,7 +10019,8 @@ grokdeclarator (const cp_declarator *declarator, if (unqualified_id && TREE_CODE (unqualified_id) == TEMPLATE_ID_EXPR && TREE_CODE (type) != FUNCTION_TYPE - && TREE_CODE (type) != METHOD_TYPE) + && TREE_CODE (type) != METHOD_TYPE + && !variable_template_p (TREE_OPERAND (unqualified_id, 0))) { error ("template-id %qD used as a declarator", unqualified_id); @@ -10894,11 +10910,15 @@ grokdeclarator (const cp_declarator *declarator, /* It's a variable. */ /* An uninitialized decl with `extern' is a reference. */ - decl = grokvardecl (type, unqualified_id, + decl = grokvardecl (type, dname, unqualified_id, declspecs, initialized, (type_quals & TYPE_QUAL_CONST) != 0, + template_count, ctype ? ctype : in_namespace); + if (decl == NULL_TREE) + return error_mark_node; + bad_specifiers (decl, BSP_VAR, virtualp, memfn_quals != TYPE_UNQUALIFIED, inlinep, friendp, raises != NULL_TREE); diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c index 9ed763d9737..9375f3fa98f 100644 --- a/gcc/cp/decl2.c +++ b/gcc/cp/decl2.c @@ -524,6 +524,8 @@ check_member_template (tree tmpl) with member templates. */ DECL_IGNORED_P (tmpl) = 1; } + else if (variable_template_p (tmpl)) + /* OK */; else error ("template declaration of %q#D", decl); } diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index be071a89557..78004678fe4 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -13646,6 +13646,10 @@ cp_parser_template_id (cp_parser *parser, template_id = finish_template_type (templ, arguments, entering_scope); } + else if (variable_template_p (templ)) + { + template_id = lookup_template_variable (templ, arguments); + } else { /* If it's not a class-template or a template-template, it should be diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index f030f30e14a..57e72168be7 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -1878,11 +1878,16 @@ determine_specialization (tree template_id, if (BASELINK_P (fns)) fns = BASELINK_FUNCTIONS (fns); - if (!is_overloaded_fn (fns)) + if (TREE_CODE (decl) == FUNCTION_DECL && !is_overloaded_fn (fns)) { error ("%qD is not a function template", fns); return error_mark_node; } + else if (VAR_P (decl) && !variable_template_p (fns)) + { + error ("%qD is not a variable template", fns); + return error_mark_node; + } /* Count the number of template headers specified for this specialization. */ @@ -1892,7 +1897,9 @@ determine_specialization (tree template_id, b = b->level_chain) ++header_count; - for (; fns; fns = OVL_NEXT (fns)) + if (variable_template_p (fns)) + templates = tree_cons (explicit_targs, fns, templates); + else for (; fns; fns = OVL_NEXT (fns)) { tree fn = OVL_CURRENT (fns); @@ -2308,9 +2315,16 @@ check_template_variable (tree decl) tree ctx = CP_DECL_CONTEXT (decl); int wanted = num_template_headers_for_class (ctx); if (!TYPE_P (ctx) || !CLASSTYPE_TEMPLATE_INFO (ctx)) - permerror (DECL_SOURCE_LOCATION (decl), - "%qD is not a static data member of a class template", decl); - else if (template_header_count > wanted) + { + if (cxx_dialect < cxx1y) + pedwarn (DECL_SOURCE_LOCATION (decl), 0, + "variable templates only available with " + "-std=c++1y or -std=gnu++1y"); + + // Namespace-scope variable templates should have a template header. + ++wanted; + } + if (template_header_count > wanted) { bool warned = pedwarn (DECL_SOURCE_LOCATION (decl), 0, "too many template headers for %D (should be %d)", @@ -2442,6 +2456,13 @@ check_explicit_specialization (tree declarator, /* Fall through. */ case tsk_expl_spec: + if (VAR_P (decl) && TREE_CODE (declarator) != TEMPLATE_ID_EXPR) + { + // In cases like template<> constexpr bool v = true; + error ("%qD is not a template variable", dname); + break; + } + SET_DECL_TEMPLATE_SPECIALIZATION (decl); if (ctype) member_specialization = 1; @@ -2481,7 +2502,10 @@ check_explicit_specialization (tree declarator, gcc_unreachable (); } - if (specialization || member_specialization) + if ((specialization || member_specialization) + /* This doesn't apply to variable templates. */ + && (TREE_CODE (TREE_TYPE (decl)) == FUNCTION_TYPE + || TREE_CODE (TREE_TYPE (decl)) == METHOD_TYPE)) { tree t = TYPE_ARG_TYPES (TREE_TYPE (decl)); for (; t; t = TREE_CHAIN (t)) @@ -2566,6 +2590,10 @@ check_explicit_specialization (tree declarator, else if (ctype != NULL_TREE && (identifier_p (TREE_OPERAND (declarator, 0)))) { + // Ignore variable templates. + if (VAR_P (decl)) + return decl; + /* Find the list of functions in ctype that have the same name as the declared function. */ tree name = TREE_OPERAND (declarator, 0); @@ -2691,7 +2719,8 @@ check_explicit_specialization (tree declarator, /* If we thought that the DECL was a member function, but it turns out to be specializing a static member function, make DECL a static member function as well. */ - if (DECL_STATIC_FUNCTION_P (tmpl) + if (DECL_FUNCTION_TEMPLATE_P (tmpl) + && DECL_STATIC_FUNCTION_P (tmpl) && DECL_NONSTATIC_MEMBER_FUNCTION_P (decl)) revert_static_member_fn (decl); @@ -2725,7 +2754,8 @@ check_explicit_specialization (tree declarator, /* Inherit default function arguments from the template DECL is specializing. */ - copy_default_args_to_explicit_spec (decl); + if (DECL_FUNCTION_TEMPLATE_P (tmpl)) + copy_default_args_to_explicit_spec (decl); /* This specialization has the same protection as the template it specializes. */ @@ -2797,6 +2827,7 @@ check_explicit_specialization (tree declarator, /* A 'structor should already have clones. */ gcc_assert (decl == error_mark_node + || variable_template_p (tmpl) || !(DECL_CONSTRUCTOR_P (decl) || DECL_DESTRUCTOR_P (decl)) || DECL_CLONED_FUNCTION_P (DECL_CHAIN (decl))); @@ -4741,6 +4772,15 @@ push_template_decl_real (tree decl, bool is_friend) && TYPE_DECL_ALIAS_P (decl)) /* alias-declaration */ gcc_assert (!DECL_ARTIFICIAL (decl)); + else if (VAR_P (decl)) + { + if (!DECL_DECLARED_CONSTEXPR_P (decl)) + { + sorry ("template declaration of non-constexpr variable %qD", + decl); + return error_mark_node; + } + } else { error ("template declaration of %q#D", decl); @@ -7917,6 +7957,14 @@ lookup_template_class (tree d1, tree arglist, tree in_decl, tree context, timevar_pop (TV_TEMPLATE_INST); return ret; } + +/* Return a TEMPLATE_ID_EXPR for the given variable template and ARGLIST. */ + +tree +lookup_template_variable (tree templ, tree arglist) +{ + return build2 (TEMPLATE_ID_EXPR, TREE_TYPE (templ), templ, arglist); +} struct pair_fn_data { @@ -10484,7 +10532,7 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain) if (PRIMARY_TEMPLATE_P (t)) DECL_PRIMARY_TEMPLATE (r) = r; - if (TREE_CODE (decl) != TYPE_DECL) + if (TREE_CODE (decl) != TYPE_DECL && TREE_CODE (decl) != VAR_DECL) /* Record this non-type partial instantiation. */ register_specialization (r, t, DECL_TI_ARGS (DECL_TEMPLATE_RESULT (r)), @@ -19172,7 +19220,11 @@ do_decl_instantiation (tree decl, tree storage) error ("explicit instantiation of non-template %q#D", decl); return; } - else if (VAR_P (decl)) + + bool var_templ = (DECL_TEMPLATE_INFO (decl) + && variable_template_p (DECL_TI_TEMPLATE (decl))); + + if (VAR_P (decl) && !var_templ) { /* There is an asymmetry here in the way VAR_DECLs and FUNCTION_DECLs are handled by grokdeclarator. In the case of @@ -19201,7 +19253,7 @@ do_decl_instantiation (tree decl, tree storage) return; } } - else if (TREE_CODE (decl) != FUNCTION_DECL) + else if (TREE_CODE (decl) != FUNCTION_DECL && !var_templ) { error ("explicit instantiation of %q#D", decl); return; @@ -19906,10 +19958,12 @@ instantiate_decl (tree d, int defer_ok, tree ns; tree init; bool const_init = false; + bool enter_context = DECL_CLASS_SCOPE_P (d); ns = decl_namespace_context (d); push_nested_namespace (ns); - push_nested_class (DECL_CONTEXT (d)); + if (enter_context) + push_nested_class (DECL_CONTEXT (d)); init = tsubst_expr (DECL_INITIAL (code_pattern), args, tf_warning_or_error, NULL_TREE, @@ -19921,7 +19975,8 @@ instantiate_decl (tree d, int defer_ok, cp_finish_decl (d, init, /*init_const_expr_p=*/const_init, /*asmspec_tree=*/NULL_TREE, LOOKUP_ONLYCONVERTING); - pop_nested_class (); + if (enter_context) + pop_nested_class (); pop_nested_namespace (ns); } @@ -20018,10 +20073,15 @@ instantiate_decl (tree d, int defer_ok, DECL_EXTERNAL (d) = 0; /* Enter the scope of D so that access-checking works correctly. */ - push_nested_class (DECL_CONTEXT (d)); + bool enter_context = DECL_CLASS_SCOPE_P (d); + if (enter_context) + push_nested_class (DECL_CONTEXT (d)); + const_init = DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (code_pattern); cp_finish_decl (d, init, const_init, NULL_TREE, 0); - pop_nested_class (); + + if (enter_context) + pop_nested_class (); } else if (TREE_CODE (d) == FUNCTION_DECL && DECL_DEFAULTED_FN (code_pattern)) synthesize_method (d); diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index 4868d69014f..859550b0f61 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -2418,6 +2418,15 @@ finish_call_expr (tree fn, vec **args, bool disallow_virtual, return result; } +/* Instantiate a variable declaration from a TEMPLATE_ID_EXPR for use. */ + +tree +finish_template_variable (tree var) +{ + return instantiate_template (TREE_OPERAND (var, 0), TREE_OPERAND (var, 1), + tf_error); +} + /* Finish a call to a postfix increment or decrement or EXPR. (Which is indicated by CODE, which should be POSTINCREMENT_EXPR or POSTDECREMENT_EXPR.) */ @@ -3500,6 +3509,11 @@ finish_id_expression (tree id_expression, a call to its wrapper. */ decl = build_cxx_call (wrap, 0, NULL, tf_warning_or_error); } + else if (TREE_CODE (decl) == TEMPLATE_ID_EXPR + && variable_template_p (TREE_OPERAND (decl, 0))) + { + decl = finish_template_variable (decl); + } else if (scope) { decl = (adjust_result_of_qualified_name_lookup diff --git a/gcc/testsuite/g++.dg/cpp1y/pr59638.C b/gcc/testsuite/g++.dg/cpp1y/pr59638.C index 0125bdcbb70..a4c63acee76 100644 --- a/gcc/testsuite/g++.dg/cpp1y/pr59638.C +++ b/gcc/testsuite/g++.dg/cpp1y/pr59638.C @@ -1,10 +1,11 @@ // PR c++/59638 // { dg-do compile { target c++1y } } // { dg-options "" } +// { dg-excess-errors "sorry" } -void (*a)(auto); // { dg-error "template declaration" } +void (*a)(auto); // { dg-error "" "" { xfail *-*-* } } -void (*b)(auto) = 0; // { dg-error "template declaration" } +void (*b)(auto) = 0; // { dg-error "" "" { xfail *-*-* } } typedef void (*f)(auto); // { dg-error "template declaration" } diff --git a/gcc/testsuite/g++.dg/cpp1y/var-templ1.C b/gcc/testsuite/g++.dg/cpp1y/var-templ1.C new file mode 100644 index 00000000000..9219303737c --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/var-templ1.C @@ -0,0 +1,22 @@ +// { dg-do run } +// { dg-options "-std=c++1y" } + +template + struct S1 + { + static constexpr int a = A; + static constexpr int b = B; + }; + +template + constexpr int var = T::a + T::b; + +int main () +{ + int v = var>/2; + return !( + var> == v + && var> == var>>> + && var> != 222 + ); +} diff --git a/gcc/testsuite/g++.dg/cpp1y/var-templ2.C b/gcc/testsuite/g++.dg/cpp1y/var-templ2.C new file mode 100644 index 00000000000..315ac3e048a --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/var-templ2.C @@ -0,0 +1,34 @@ +// { dg-do compile } +// { dg-options "-std=c++1y" } + +// Template variables and static member variables of template classes are +// often confused. + +template + struct S1 + { + static int n; + static int arr[]; + }; + +template + constexpr int var = sizeof (T); + +template + int S1::n = sizeof (T); + +template + int S1::arr[sizeof (T)]; + +template<> + int S1::n = 8; + +template<> + int S1::arr[8]; + +int main () +{ + S1 v1; + var>; + return 0; +} diff --git a/gcc/testsuite/g++.dg/cpp1y/var-templ3.C b/gcc/testsuite/g++.dg/cpp1y/var-templ3.C new file mode 100644 index 00000000000..d3fbad4bc88 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/var-templ3.C @@ -0,0 +1,19 @@ +// { dg-do run } +// { dg-options "-std=c++1y" } + +template + constexpr int var = sizeof (T); + +template + struct S1 + { + template + static constexpr int a = sizeof (U) + sizeof (T); + }; + +int main () +{ + return !( + var + var == S1::a + ); +} diff --git a/gcc/testsuite/g++.dg/cpp1y/var-templ4.C b/gcc/testsuite/g++.dg/cpp1y/var-templ4.C new file mode 100644 index 00000000000..1d6cf1d6918 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/var-templ4.C @@ -0,0 +1,16 @@ +// { dg-do run } +// { dg-options "-std=c++1y" } + +template + constexpr int var = sizeof (T); + +template<> + constexpr int var = 100000; + +int main () +{ + return !( + var == 100000 + && var == sizeof(char) + ); +} diff --git a/gcc/testsuite/g++.dg/cpp1y/var-templ5.C b/gcc/testsuite/g++.dg/cpp1y/var-templ5.C new file mode 100644 index 00000000000..a8b7122ddd8 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/var-templ5.C @@ -0,0 +1,22 @@ +// { dg-do run } +// { dg-options "-std=c++1y" } + +template + struct S1 + { + static constexpr int a = A; + static constexpr int b = B; + }; + +template + constexpr int var = T::a + T::b; + +template class T, int A> + constexpr int var2 = var> + A; + +int main () +{ + return !( + var2 == 120 + ); +} diff --git a/gcc/testsuite/g++.dg/parse/error50.C b/gcc/testsuite/g++.dg/parse/error50.C index dbd8958c360..f9c42dd7e41 100644 --- a/gcc/testsuite/g++.dg/parse/error50.C +++ b/gcc/testsuite/g++.dg/parse/error50.C @@ -15,4 +15,4 @@ struct B static T i; }; -template<> template <> int B::i; // { dg-error "should be 1" } +template<> template <> int B::i; // { dg-error "template|should be 1" } diff --git a/gcc/testsuite/g++.dg/template/crash71.C b/gcc/testsuite/g++.dg/template/crash71.C index 86aa1521d4d..3ac862ed81b 100644 --- a/gcc/testsuite/g++.dg/template/crash71.C +++ b/gcc/testsuite/g++.dg/template/crash71.C @@ -1,3 +1,3 @@ // PR c++/30659 -extern "C" template A foo(); // { dg-error "forbids|static data|expected" } +extern "C" template A foo(); // { dg-error "forbids|static data|expected|template" } diff --git a/gcc/testsuite/g++.old-deja/g++.oliva/template10.C b/gcc/testsuite/g++.old-deja/g++.oliva/template10.C index 5c1204bddcf..34e7224be45 100644 --- a/gcc/testsuite/g++.old-deja/g++.oliva/template10.C +++ b/gcc/testsuite/g++.old-deja/g++.oliva/template10.C @@ -19,4 +19,4 @@ template<> struct A { }; bool A::a = true; // ok -template<> bool A::b = false; // { dg-error "template header" } +template<> bool A::b = false; // { dg-error "template (header|variable)" } diff --git a/gcc/testsuite/g++.old-deja/g++.pt/var1.C b/gcc/testsuite/g++.old-deja/g++.pt/var1.C index a15743d3e56..ec91bc47672 100644 --- a/gcc/testsuite/g++.old-deja/g++.pt/var1.C +++ b/gcc/testsuite/g++.old-deja/g++.pt/var1.C @@ -1,4 +1,5 @@ // { dg-do assemble } // Origin: Jason Merrill +// { dg-excess-errors "sorry" } -template T t; // { dg-error "" } template declaration of t +template T t; // template declaration of t