Patchwork Patch for constexpr variable templates

login
register
mail settings
Submitter Braden Obrzut
Date July 19, 2014, 6:42 a.m.
Message ID <53CA134F.4090608@maniacsvault.net>
Download mbox | patch
Permalink /patch/371773/
State New
Headers show

Comments

Braden Obrzut - July 19, 2014, 6:42 a.m.
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  <admin@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  <admin@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  <gdr@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  <gdr@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.

Patch

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
+  );
+}
+