diff mbox

Patch for constexpr variable templates

Message ID 53CDD522.9040508@maniacsvault.net
State New
Headers show

Commit Message

Braden Obrzut July 22, 2014, 3:06 a.m. UTC
Sure, test cases moved.

My FSF paperwork is in place.  I don't have write access to the 
repository though of course.

- Braden Obrzut

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

On 07/21/2014 07:39 AM, Ed Smith-Rowland wrote:
> Braden,
>
> I've played with this and it seems to work nicely.
> Only one comment:  Could you put the test cases in the C++14 
> subdirectory?
>
> g++.dg/template/cpp1y/var-templ1.C
>
>                 ^^^^^
> We should CC Jason on all this.
>
> Also, do you have your FSF paperwork in place?
>
> Thanks,
>
> Ed
>

Comments

Ed Smith-Rowland July 23, 2014, 2:28 p.m. UTC | #1
Braden,

Great work on this.  In addition to helping with constraints there is at 
least one new library feature that depends on constexpr variable templates.

For my two cents it would be good to get just the constexpr variable 
templates, aka n3651, in now as a first stage - it's quite usable.

But someday we'll want non-constexpr too. I have only seen a sentence 
somewhere stating that non-constexpr variable templates are allowed but 
have seen no paper or wording anywhere.  Does anyone know where such exists?

Ed
Andrew Sutton July 23, 2014, 2:44 p.m. UTC | #2
> But someday we'll want non-constexpr too. I have only seen a sentence
> somewhere stating that non-constexpr variable templates are allowed but have
> seen no paper or wording anywhere.  Does anyone know where such exists?

When variable templates were proposed, EWG decided they wanted
non-constexpr variable templates also. I think that the current draft
reflects that, since I can't find anything that says "variable
templates shall be declared constexpr".

There's also an implied requirement for partial specialization of
variable templates (14.3.3p2), but no elaboration of that. Core issue
1711 specifically addresses this. A quick search shows that Clang
already implements it.

Andrew
Jason Merrill July 24, 2014, 8:56 p.m. UTC | #3
First of all, thanks a lot for taking this on!  A few nitpicks:

On 07/21/2014 11:06 PM, Braden Obrzut wrote:
>  grokvardecl (tree type,
>              tree name,
> +                tree orig_declarator,
>              const cp_decl_specifier_seq *declspecs,
>              int initialized,
>              int constp,
> +                int template_count,

Indentation mismatch.

> +  if (orig_declarator && (processing_template_decl
> +      || TREE_CODE (orig_declarator) == TEMPLATE_ID_EXPR))

The indentation is wrong, since the || is part of the inner 
parenthesized expression.

> +      /* 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?

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

I think here we should check whether 'fns' and 'decl' are both variables 
or both functions so that we can give a better diagnostic.

> +      if (cxx_dialect < cxx1y)
> +        permerror (DECL_SOURCE_LOCATION (decl),
> +                   "%qD is not a static data member of a class template", decl);

It's customary to use pedwarn and mention the relevant -std= flag.

> +  if ((specialization || member_specialization)
> +         /* Variable templates don't apply.  */
> +         && (TREE_CODE (TREE_TYPE (decl)) == FUNCTION_TYPE
> +             || TREE_CODE (TREE_TYPE (decl)) == METHOD_TYPE))

Indentation mismatch.

> +      // Namespace scope variable templates should have a template header.

"Namespace-scope"

> +         if (!variable_template_p (tmpl)
> +                 && DECL_STATIC_FUNCTION_P (tmpl)
...
> +         if (!variable_template_p (tmpl))
> +           copy_default_args_to_explicit_spec (decl);

I'd prefer to check for a function template here rather than "not a 
variable template".

> +      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.

> +  bool var_templ = DECL_TEMPLATE_INFO (decl)
> +                   && variable_template_p (DECL_TI_TEMPLATE (decl));

An expression on multiple lines should be parenthesized to preserve 
indentation.

> +         bool enter_context = CLASS_TYPE_P (DECL_CONTEXT (d));

You can use DECL_CLASS_SCOPE_P.

> +  return instantiate_template (TREE_OPERAND (var, 0), TREE_OPERAND (var, 1), tf_error);

Line too long.

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