This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: Patch for constexpr variable templates


On 07/24/2014 04:56 PM, Jason Merrill wrote:

+      /* Variable templates will need to have the class context.  */
+      if (VAR_P (value))
+        DECL_CONTEXT (value) = current_class_type;

Why isn't this covered by grokvardecl?
Oops, as the changelog indicated, this was to satisfy the old version of variable_template_p which checked the scope. I forgot to apply Gaby's second patch initially. It's no longer needed so I've removed it.

+      else if (VAR_P (decl))
+        {
+          if (!DECL_DECLARED_CONSTEXPR_P (decl))
+ error ("template declaration of non-constexpr variable %qD", decl);
+        }

As Ed and Andrew pointed out, non-constexpr variable templates are fine.

This patch only covers constexpr variable templates, so it makes sense to produce an error for now. Otherwise more confusing linker errors will be thrown regarding duplicate or missing symbols. I do not believe it would be too difficult to extend this patch to do non-constexpr variables since it looks like the issue is assigning names, but it's outside of the scope of what is required for concepts which is what I'm assigned to for GSoC.

Other than that, the other issues you mentioned should be fixed.

- Braden Obrzut

2014-07-25  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.
    * 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 and
    change non static data member error to variable template warning.
    (lookup_template_variable): New.
    (do_decl_instantiation): Handle template variables.
    (instantiate_decl): Handle template variables.
    * semantics.c (finish_template_variable): New.

2014-07-25  Braden Obrzut  <admin@maniacsvault.net>

    * g++.dg/cpp1y/var-templ1.C: New.
    * g++.dg/cpp1y/var-templ2.C: New.
    * g++.dg/cpp1y/var-templ3.C: New.
    * g++.dg/cpp1y/var-templ4.C: New.
    * g++.dg/cpp1y/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.
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..eea2775 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,15 @@ 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 +8957,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 +10004,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 +10895,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..5f4a644 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);
 }
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 72f987e..0d6ad01 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..0c585c5 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,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,12 @@ 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 +2318,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)",
@@ -2481,7 +2498,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 +2711,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 +2746,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.  */
@@ -2794,6 +2816,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 +4761,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 +7927,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 +19182,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 +19215,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 +19925,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,
@@ -19900,7 +19942,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 +20039,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 = 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 a6d941b..8fd510c 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -2418,6 +2418,15 @@ 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/cpp1y/var-templ1.C b/gcc/testsuite/g++.dg/cpp1y/var-templ1.C
new file mode 100644
index 0000000..9219303
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/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/cpp1y/var-templ2.C b/gcc/testsuite/g++.dg/cpp1y/var-templ2.C
new file mode 100644
index 0000000..315ac3e
--- /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<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/cpp1y/var-templ3.C b/gcc/testsuite/g++.dg/cpp1y/var-templ3.C
new file mode 100644
index 0000000..d3fbad4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/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/cpp1y/var-templ4.C b/gcc/testsuite/g++.dg/cpp1y/var-templ4.C
new file mode 100644
index 0000000..1d6cf1d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/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/cpp1y/var-templ5.C b/gcc/testsuite/g++.dg/cpp1y/var-templ5.C
new file mode 100644
index 0000000..32d0ee1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/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
+  );
+}
+ 

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]