While working on variable concepts, I noticed that one of the added
blocks of code was no longer needed due to a previous simplification and
caused problems. Also included a new test case which triggered the
problem, and fixed a minor formatting issue.
As before, if the plan is to revive the existing variable template
branch, I can redo the patch without Gabriel's changes.
2014-07-19 Braden Obrzut <ad...@maniacsvault.net>
* decl.c (grokvardecl): Handle specializations of variable templates.
(grokdeclarator): Handle variable template id expressions.
* decl2.c (check_member_template): Allow declaration of template member
variables.
(grokfield): Assign class context to template member variables in order
that variable_template_p to detect them properly.
* parser.c (cp_parser_postfix_expression): Resolve VAR_DECLs from
TEMPLATE_ID_EXPRs.
(cp_parser_template_id): Build a TEMPLATE_ID_EXPR for variable
templates.
* pt.c (register_specialization): Accept variable templates.
(determine_specialization): Accept variable templates.
(check_template_variable): Fixed wanted template header count.
(lookup_template_variable): New.
(do_decl_instantiation): Handle templat variables.
(instantiate_decl): Handle template variables.
* semantics.c (finish_template_variable): New.
2014-07-19 Braden Obrzut <ad...@maniacsvault.net>
* g++.dg/template/var-templ1.C: New.
* g++.dg/template/var-templ2.C: New.
* g++.dg/template/var-templ3.C: New.
* g++.dg/template/var-templ4.C: New.
* g++.dg/template/var-templ5.C: New.
2013-03-29 Gabriel Dos Reis <g...@integrable-solutions.net>
* cp-tree.h (variable_template_p): Do not check scope.
* pt.c (check_template_variable): Fix thinko from previous change.
(push_template_decl_real): Fix formatting.
2013-03-29 Gabriel Dos Reis <g...@integrable-solutions.net>
* cp-tree.h (variable_template_p): New.
* 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.
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 4a5cb98..caaaa6c 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5027,6 +5027,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;
@@ -5554,6 +5565,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);
@@ -5816,6 +5828,7 @@ extern tree perform_koenig_lookup (tree, vec<tree, va_gc> *,
tsubst_flags_t);
extern tree finish_call_expr (tree, vec<tree, va_gc> **, 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 dae85c2..0b6fa54 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);
@@ -7943,9 +7943,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;
@@ -7975,7 +7977,9 @@ 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. */
+ || 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);
@@ -8043,6 +8047,14 @@ grokvardecl (tree type,
else
DECL_INTERFACE_KNOWN (decl) = 1;
+ // Handle explicit specializations and instantiations of variable templates.
+ if (orig_declarator && (processing_template_decl
+ || TREE_CODE (orig_declarator) == TEMPLATE_ID_EXPR))
+ {
+ decl = check_explicit_specialization (orig_declarator, decl,
+ template_count, 0);
+ }
+
return decl;
}
@@ -8944,8 +8956,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. */
@@ -9986,7 +10003,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);
@@ -10876,10 +10894,11 @@ 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);
bad_specifiers (decl, BSP_VAR, virtualp,
memfn_quals != TYPE_UNQUALIFIED,
diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index 0926dbc..cd0c68e 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -529,6 +529,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);
}
@@ -987,6 +989,10 @@ grokfield (const cp_declarator *declarator,
if (processing_template_decl && VAR_OR_FUNCTION_DECL_P (value))
{
+ /* Variable templates will need to have the class context. */
+ if (VAR_P (value))
+ DECL_CONTEXT (value) = current_class_type;
+
value = push_template_decl (value);
if (error_operand_p (value))
return error_mark_node;
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 72f987e..e076a6c 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -6256,6 +6256,14 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
break;
default:
+ /* Convert variable template into VAR_DECL. */
+ if (TREE_CODE (postfix_expression) == TEMPLATE_ID_EXPR
+ && variable_template_p (TREE_OPERAND (postfix_expression, 0)))
+ {
+ postfix_expression = finish_template_variable (postfix_expression);
+ }
+
+
if (pidk_return != NULL)
* pidk_return = idk;
if (member_access_only_p)
@@ -13558,6 +13566,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 7b79280..d2a12b7 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -1353,7 +1353,7 @@ register_specialization (tree spec, tree tmpl, tree args, bool is_friend,
|| (TREE_CODE (tmpl) == FIELD_DECL
&& TREE_CODE (spec) == NONTYPE_ARGUMENT_PACK));
- if (TREE_CODE (spec) == FUNCTION_DECL
+ if ((TREE_CODE (spec) == FUNCTION_DECL || TREE_CODE (spec) == VAR_DECL)
&& uses_template_parms (DECL_TI_ARGS (spec)))
/* This is the FUNCTION_DECL for a partial instantiation. Don't
register it; we want the corresponding TEMPLATE_DECL instead.
@@ -1878,7 +1878,8 @@ determine_specialization (tree template_id,
if (BASELINK_P (fns))
fns = BASELINK_FUNCTIONS (fns);
- if (!is_overloaded_fn (fns))
+ bool var_templ = variable_template_p (fns);
+ if (!is_overloaded_fn (fns) && !var_templ)
{
error ("%qD is not a function template", fns);
return error_mark_node;
@@ -1892,7 +1893,12 @@ determine_specialization (tree template_id,
b = b->level_chain)
++header_count;
- for (; fns; fns = OVL_NEXT (fns))
+ if (var_templ)
+ {
+ templates = tree_cons (explicit_targs, fns, templates);
+ }
+ else
+ for (; fns; fns = OVL_NEXT (fns))
{
tree fn = OVL_CURRENT (fns);
@@ -2308,9 +2314,15 @@ 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)
+ permerror (DECL_SOURCE_LOCATION (decl),
+ "%qD is not a static data member of a class template", decl);
+
+ // 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)",
@@ -2481,7 +2493,10 @@ check_explicit_specialization (tree declarator,
gcc_unreachable ();
}
- if (specialization || member_specialization)
+ if ((specialization || member_specialization)
+ /* Variable templates don't apply. */
+ && (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))
@@ -2691,7 +2706,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 (!variable_template_p (tmpl)
+ && DECL_STATIC_FUNCTION_P (tmpl)
&& DECL_NONSTATIC_MEMBER_FUNCTION_P (decl))
revert_static_member_fn (decl);
@@ -2725,7 +2741,8 @@ check_explicit_specialization (tree declarator,
/* Inherit default function arguments from the template
DECL is specializing. */
- copy_default_args_to_explicit_spec (decl);
+ if (!variable_template_p (tmpl))
+ copy_default_args_to_explicit_spec (decl);
/* This specialization has the same protection as the
template it specializes. */
@@ -2794,6 +2811,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)));
@@ -4738,6 +4756,11 @@ 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))
+ error ("template declaration of non-constexpr variable %qD", decl);
+ }
else
{
error ("template declaration of %q#D", decl);
@@ -7899,6 +7922,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
{
@@ -19146,7 +19177,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
@@ -19175,7 +19210,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;
@@ -19885,10 +19920,12 @@ instantiate_decl (tree d, int defer_ok,
tree ns;
tree init;
bool const_init = false;
+ bool enter_context = CLASS_TYPE_P (DECL_CONTEXT (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,
@@ -19900,7 +19937,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);
}
@@ -19996,11 +20034,16 @@ instantiate_decl (tree d, int defer_ok,
we have a chance to determine linkage. */
DECL_EXTERNAL (d) = 0;
- /* Enter the scope of D so that access-checking works correctly. */
- push_nested_class (DECL_CONTEXT (d));
+ /* Enter the scope of D so that access-checking works correctly. */
+ bool enter_context = CLASS_TYPE_P (DECL_CONTEXT (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 a6d941b..da8766a 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -2418,6 +2418,14 @@ finish_call_expr (tree fn, vec<tree, va_gc> **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.) */
diff --git a/gcc/testsuite/g++.dg/template/var-templ1.C b/gcc/testsuite/g++.dg/template/var-templ1.C
new file mode 100644
index 0000000..9219303
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/var-templ1.C
@@ -0,0 +1,22 @@
+// { dg-do run }
+// { dg-options "-std=c++1y" }
+
+template<int A, int B>
+ struct S1
+ {
+ static constexpr int a = A;
+ static constexpr int b = B;
+ };
+
+template<typename T>
+ constexpr int var = T::a + T::b;
+
+int main ()
+{
+ int v = var<S1<199, 23>>/2;
+ return !(
+ var<S1<11, 100>> == v
+ && var<S1<50, 120>> == var<S1<150, var<S1<10, 10>>>>
+ && var<S1<53, 23>> != 222
+ );
+}
diff --git a/gcc/testsuite/g++.dg/template/var-templ2.C b/gcc/testsuite/g++.dg/template/var-templ2.C
new file mode 100644
index 0000000..315ac3e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/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<typename T>
+ struct S1
+ {
+ static int n;
+ static int arr[];
+ };
+
+template<typename T>
+ constexpr int var = sizeof (T);
+
+template<typename T>
+ int S1<T>::n = sizeof (T);
+
+template<typename T>
+ int S1<T>::arr[sizeof (T)];
+
+template<>
+ int S1<int>::n = 8;
+
+template<>
+ int S1<int>::arr[8];
+
+int main ()
+{
+ S1<int> v1;
+ var<S1<int>>;
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/template/var-templ3.C b/gcc/testsuite/g++.dg/template/var-templ3.C
new file mode 100644
index 0000000..d3fbad4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/var-templ3.C
@@ -0,0 +1,19 @@
+// { dg-do run }
+// { dg-options "-std=c++1y" }
+
+template<typename T>
+ constexpr int var = sizeof (T);
+
+template<typename T>
+ struct S1
+ {
+ template<typename U>
+ static constexpr int a = sizeof (U) + sizeof (T);
+ };
+
+int main ()
+{
+ return !(
+ var<int> + var<char> == S1<int>::a<char>
+ );
+}
diff --git a/gcc/testsuite/g++.dg/template/var-templ4.C b/gcc/testsuite/g++.dg/template/var-templ4.C
new file mode 100644
index 0000000..1d6cf1d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/var-templ4.C
@@ -0,0 +1,16 @@
+// { dg-do run }
+// { dg-options "-std=c++1y" }
+
+template<typename T>
+ constexpr int var = sizeof (T);
+
+template<>
+ constexpr int var<int> = 100000;
+
+int main ()
+{
+ return !(
+ var<int> == 100000
+ && var<char> == sizeof(char)
+ );
+}
diff --git a/gcc/testsuite/g++.dg/template/var-templ5.C b/gcc/testsuite/g++.dg/template/var-templ5.C
new file mode 100644
index 0000000..32d0ee1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/var-templ5.C
@@ -0,0 +1,23 @@
+// { dg-do run }
+// { dg-options "-std=c++1y" }
+
+template<int A, int B>
+ struct S1
+ {
+ static constexpr int a = A;
+ static constexpr int b = B;
+ };
+
+template<class T>
+ constexpr int var = T::a + T::b;
+
+template<template<int,int> class T, int A>
+ constexpr int var2 = var<T<A, A>> + A;
+
+int main ()
+{
+ return !(
+ var2<S1, 40> == 120
+ );
+}
+