Implement constexpr variable templates
authorBraden Obrzut <admin@maniacsvault.net>
Tue, 5 Aug 2014 18:24:06 +0000 (18:24 +0000)
committerJason Merrill <jason@gcc.gnu.org>
Tue, 5 Aug 2014 18:24:06 +0000 (14:24 -0400)
Implement constexpr variable templates
* decl.c (grokvardecl): Handle specializations of variable templates.
(grokdeclarator): Handle variable template id expressions and NULL_TREE
return from grokvardecl.
* decl2.c (check_member_template): Allow declaration of template member
variables.
* parser.c (cp_parser_template_id): Build a TEMPLATE_ID_EXPR for
variable templates.
* 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.
(determine_specialization): Accept variable templates.
(check_explicit_specialization): Handle and check for malformed
variable template specializations.
(lookup_template_variable): New.
(tsubst_decl): Handle variable template specializations.
(do_decl_instantiation): Handle template variables.
(instantiate_decl): Handle template variables.
* semantics.c (finish_template_variable): New.
(finish_id_expression): Instantiate variable templates.
* cp-tree.h (variable_template_p): New.

From-SVN: r213641

17 files changed:
gcc/cp/ChangeLog
gcc/cp/cp-tree.h
gcc/cp/decl.c
gcc/cp/decl2.c
gcc/cp/parser.c
gcc/cp/pt.c
gcc/cp/semantics.c
gcc/testsuite/g++.dg/cpp1y/pr59638.C
gcc/testsuite/g++.dg/cpp1y/var-templ1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1y/var-templ2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1y/var-templ3.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1y/var-templ4.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1y/var-templ5.C [new file with mode: 0644]
gcc/testsuite/g++.dg/parse/error50.C
gcc/testsuite/g++.dg/template/crash71.C
gcc/testsuite/g++.old-deja/g++.oliva/template10.C
gcc/testsuite/g++.old-deja/g++.pt/var1.C

index 9fdd4c7906b2bf1e0a36f7a645ba6ac62123da0a..d9ad73cdbde5413a4be43770855e7ac3aaa86b8a 100644 (file)
@@ -1,3 +1,28 @@
+2014-08-01  Braden Obrzut  <admin@maniacsvault.net>
+
+       Implement constexpr variable templates
+       * decl.c (grokvardecl): Handle specializations of variable templates.
+       (grokdeclarator): Handle variable template id expressions and NULL_TREE
+       return from grokvardecl.
+       * decl2.c (check_member_template): Allow declaration of template member
+       variables.
+       * parser.c (cp_parser_template_id): Build a TEMPLATE_ID_EXPR for
+       variable templates.
+       * 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.
+       (determine_specialization): Accept variable templates.
+       (check_explicit_specialization): Handle and check for malformed
+       variable template specializations.
+       (lookup_template_variable): New.
+       (tsubst_decl): Handle variable template specializations.
+       (do_decl_instantiation): Handle template variables.
+       (instantiate_decl): Handle template variables.
+       * semantics.c (finish_template_variable): New.
+       (finish_id_expression): Instantiate variable templates.
+       * cp-tree.h (variable_template_p): New.
+
 2014-08-02  Paolo Carlini  <paolo.carlini@oracle.com>
 
        PR c++/15339
index 622de9c42f426ea6d699caeda9e3d957cf822944..ffb44d11ae5e874f20337aa2a820f63244f5db85 100644 (file)
@@ -5045,6 +5045,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;
@@ -5572,6 +5583,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);
@@ -5834,6 +5846,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);
index d4dde6129aab6ab10db84959763a1bd94911f088..acc1192eacf5105f7636eefc33e0513a0f480ea7 100644 (file)
@@ -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);
@@ -7968,9 +7968,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;
@@ -8000,7 +8002,10 @@ 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.  */
+         || (orig_declarator
+             && 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);
@@ -8068,7 +8073,12 @@ grokvardecl (tree type,
   else
     DECL_INTERFACE_KNOWN (decl) = 1;
 
-  return decl;
+  // Handle explicit specializations and instantiations of variable templates.
+  if (orig_declarator)
+    decl = check_explicit_specialization (orig_declarator, decl,
+                                         template_count, 0);
+
+  return decl != error_mark_node ? decl : NULL_TREE;
 }
 
 /* Create and return a canonical pointer to member function type, for
@@ -8962,8 +8972,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.  */
@@ -10004,7 +10019,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);
@@ -10894,11 +10910,15 @@ 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);
+       if (decl == NULL_TREE)
+         return error_mark_node;
+
        bad_specifiers (decl, BSP_VAR, virtualp,
                        memfn_quals != TYPE_UNQUALIFIED,
                        inlinep, friendp, raises != NULL_TREE);
index 9ed763d97372722975eff96cb3d1ff2121fa8dc7..9375f3fa98fed5e7de26ec405ff6269cefb7c5b4 100644 (file)
@@ -524,6 +524,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);
 }
index be071a89557405eeefdb074823c6bb54ef29482c..78004678fe4f62af7fed53e37ac4fb1d6022e95a 100644 (file)
@@ -13646,6 +13646,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
index f030f30e14aad9fcd91812db20acfd7f1f0c6093..57e72168be7c6c7fcba49368ff5858515f0bf5fe 100644 (file)
@@ -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,9 @@ 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 +2315,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)",
@@ -2442,6 +2456,13 @@ check_explicit_specialization (tree declarator,
 
       /* Fall through.  */
     case tsk_expl_spec:
+      if (VAR_P (decl) && TREE_CODE (declarator) != TEMPLATE_ID_EXPR)
+        {
+           // In cases like template<> constexpr bool v = true;
+           error ("%qD is not a template variable", dname);
+           break;
+        }
+
       SET_DECL_TEMPLATE_SPECIALIZATION (decl);
       if (ctype)
        member_specialization = 1;
@@ -2481,7 +2502,10 @@ check_explicit_specialization (tree declarator,
       gcc_unreachable ();
     }
 
-  if (specialization || member_specialization)
+  if ((specialization || member_specialization)
+      /* This doesn't apply to variable templates.  */
+      && (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))
@@ -2566,6 +2590,10 @@ check_explicit_specialization (tree declarator,
       else if (ctype != NULL_TREE
               && (identifier_p (TREE_OPERAND (declarator, 0))))
        {
+         // Ignore variable templates.
+         if (VAR_P (decl))
+           return decl;
+
          /* Find the list of functions in ctype that have the same
             name as the declared function.  */
          tree name = TREE_OPERAND (declarator, 0);
@@ -2691,7 +2719,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 +2754,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.  */
@@ -2797,6 +2827,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)));
@@ -4741,6 +4772,15 @@ 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))
+            {
+              sorry ("template declaration of non-constexpr variable %qD",
+                    decl);
+              return error_mark_node;
+            }
+        }
       else
        {
          error ("template declaration of %q#D", decl);
@@ -7917,6 +7957,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);
+}
 \f
 struct pair_fn_data
 {
@@ -10484,7 +10532,7 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain)
        if (PRIMARY_TEMPLATE_P (t))
          DECL_PRIMARY_TEMPLATE (r) = r;
 
-       if (TREE_CODE (decl) != TYPE_DECL)
+       if (TREE_CODE (decl) != TYPE_DECL && TREE_CODE (decl) != VAR_DECL)
          /* Record this non-type partial instantiation.  */
          register_specialization (r, t,
                                   DECL_TI_ARGS (DECL_TEMPLATE_RESULT (r)),
@@ -19172,7 +19220,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
@@ -19201,7 +19253,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;
@@ -19906,10 +19958,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,
@@ -19921,7 +19975,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);
        }
 
@@ -20018,10 +20073,15 @@ instantiate_decl (tree d, int defer_ok,
       DECL_EXTERNAL (d) = 0;
 
       /* Enter the scope of D so that access-checking works correctly.  */
-      push_nested_class (DECL_CONTEXT (d));
+      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);
index 4868d69014fef3e7012ce535228bb774d4a971a1..859550b0f615014adb663753d57f04984dec0f4f 100644 (file)
@@ -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.)  */
@@ -3500,6 +3509,11 @@ finish_id_expression (tree id_expression,
             a call to its wrapper.  */
          decl = build_cxx_call (wrap, 0, NULL, tf_warning_or_error);
        }
+      else if (TREE_CODE (decl) == TEMPLATE_ID_EXPR
+              && variable_template_p (TREE_OPERAND (decl, 0)))
+       {
+         decl = finish_template_variable (decl);
+       }
       else if (scope)
        {
          decl = (adjust_result_of_qualified_name_lookup
index 0125bdcbb70706b9b10cfbfc1ab3a7cf1b0a240f..a4c63acee7648654722c326cdb1d612976f44123 100644 (file)
@@ -1,10 +1,11 @@
 // PR c++/59638
 // { dg-do compile { target c++1y } }
 // { dg-options "" }
+// { dg-excess-errors "sorry" }
 
-void (*a)(auto);         // { dg-error "template declaration" }
+void (*a)(auto);         // { dg-error "" "" { xfail *-*-* } }
 
-void (*b)(auto) = 0;     // { dg-error "template declaration" }
+void (*b)(auto) = 0;     // { dg-error "" "" { xfail *-*-* } }
 
 typedef void (*f)(auto); // { dg-error "template declaration" }
 
diff --git a/gcc/testsuite/g++.dg/cpp1y/var-templ1.C b/gcc/testsuite/g++.dg/cpp1y/var-templ1.C
new file mode 100644 (file)
index 0000000..9219303
--- /dev/null
@@ -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 (file)
index 0000000..315ac3e
--- /dev/null
@@ -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 (file)
index 0000000..d3fbad4
--- /dev/null
@@ -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 (file)
index 0000000..1d6cf1d
--- /dev/null
@@ -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 (file)
index 0000000..a8b7122
--- /dev/null
@@ -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<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 dbd8958c360c62303ab3515ba6c2c93d5b97255b..f9c42dd7e41a88de70980d0d504144bdd327a5a4 100644 (file)
@@ -15,4 +15,4 @@ struct B
   static T i;
 };
 
-template<> template <> int B<int>::i; // { dg-error "should be 1" }
+template<> template <> int B<int>::i; // { dg-error "template|should be 1" }
index 86aa1521d4dd129917cf16a5c2a5284ee9cb96c2..3ac862ed81b8af94628e497263f1050d61bb308b 100644 (file)
@@ -1,3 +1,3 @@
 // PR c++/30659
 
-extern "C" template A<char> foo(); // { dg-error "forbids|static data|expected" }
+extern "C" template A<char> foo(); // { dg-error "forbids|static data|expected|template" }
index 5c1204bddcf9aaecc0ed885f7533cf512e9d2612..34e7224be458642beddf4df1677c9e77a52fb623 100644 (file)
@@ -19,4 +19,4 @@ template<> struct A<int> {
 };
 
 bool A<int>::a = true; // ok
-template<> bool A<int>::b = false; // { dg-error "template header" } 
+template<> bool A<int>::b = false; // { dg-error "template (header|variable)" }
index a15743d3e56650091f74cd32c33c47ab923d7265..ec91bc476726da8693379d089d5ee374afa776ad 100644 (file)
@@ -1,4 +1,5 @@
 // { dg-do assemble  }
 // Origin: Jason Merrill <jason@cygnus.com>
+// { dg-excess-errors "sorry" }
 
-template <class T> T t; // { dg-error "" } template declaration of t
+template <class T> T t; // template declaration of t