diff mbox

Patch for constexpr variable templates

Message ID 53C205E0.6070907@maniacsvault.net
State New
Headers show

Commit Message

Braden Obrzut July 13, 2014, 4:06 a.m. UTC
Here is a patch that expands on Gabriel Dos Reis's initial var-template 
patch ( https://gcc.gnu.org/ml/gcc-patches/2013-03/msg01295.html ).  I 
just noticed now that those patches were committed to a branch, so if 
need be I can resubmit the patch with Gaby's changes not included.

The purpose of the patch is to implement the subset of variable 
templates that is a prerequisite for concept variables in the 
c++-concepts branch.  With that said, only constexpr variables are 
supported (both in namespace and class scopes).  There is no handling 
for generating symbols for these variables so they are only supported in 
contexts where they can be evaluated at compile time (I believe that 
would be only as an rvalue).  On that note, explicit instantiations of 
variable templates do work, but will generate duplicate symbol errors if 
more than one exists in the file.  Other than those issues, the should 
work as expected.

I would like to call attention to the changes in parser.c.  Andrew 
pointed out to me that it may be a good idea to have 
cp_parser_template_id return the instantiated VAR_DECL directly instead 
of deferring it to cp_parser_postfix_expression like function 
templates.  Since this would produce a new possible return type for the 
function, I've found that would require more widespread changes 
(although I can't accurately say by how much).  By deferring the 
instantiation, some code can be shared between function templates and 
variable templates.

2014-07-12  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.
     (instantiate_template_1): Evaluate initializer for variable templates.
     (do_decl_instantiation): Handle templat variables.
     (instantiate_decl): Handle template variables.
     * semantics.c (finish_template_variable): New.

2014-07-12  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.

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.
diff mbox

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..62cc256 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..1744375 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
 {
@@ -15692,6 +15723,19 @@  instantiate_template_1 (tree tmpl, tree orig_args, tsubst_flags_t complain)
      template, not the most general template.  */
   DECL_TI_TEMPLATE (fndecl) = tmpl;
 
+  // For variable templates we need to check out the initializer
+  if (VAR_P (fndecl))
+  {
+	 tree init = tsubst_expr (
+		DECL_INITIAL (DECL_TEMPLATE_RESULT (gen_tmpl)),
+		targ_ptr, complain, gen_tmpl, true);
+
+	 // Evaluate the initializer and store it.
+	 vec<tree, va_gc> *cleanups = NULL;
+     store_init_value (fndecl, init, &cleanups, LOOKUP_ALREADY_DIGESTED);
+     gcc_assert (!cleanups);
+  }
+
   /* Now we know the specialization, compute access previously
      deferred.  */
   push_access_scope (fndecl);
@@ -19146,7 +19190,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 +19223,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 +19933,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 +19950,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 +20047,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)
+  );
+}