diff mbox

[c++-concepts] explicit instantiation and specialization

Message ID CANq5Syt_i9bexAkD_pocZX3PcAe3Wv3e2qzA17YMo4DisQ-t6Q@mail.gmail.com
State New
Headers show

Commit Message

Andrew Sutton Aug. 13, 2014, 7:49 p.m. UTC
This patch re-implements explicit instantiation and specialization.
The latter isn't fully tested just yet, but it's close.

Constraints for explicit instantiations and specializations are
deduced from their candidates, not explicitly specified as part of the
declaration.

2014-08-13  Andrew Sutton  <andrew.n.sutton@gmail.com>

        Implement deduction-based explicit instantiation and specialization.
        * gcc/cp/call.c (joust): Allow all non-templates to be ordered by
        constraints.
        * gcc/cp/pt.c (get_class_bindings): Remove superfluous parameter and
        move constraint check into most_specialized_class.
        (most_constrained_function): Order functions with the same signatures
        by their constraints.
        (determine_specialization): Candidates must satisfy constraints. Also,
        order non-template candidates by constraints. Improve diagnostics
        for instances where candidates are rejected.
        (more_specialized_inst): New. Compare function templates.
        (most_specialized_instantiation): Refactor to use
        more_specialized_inst and order by constraints.
        (most_specialized_class): Candidates must satisfy constraints.
        * gcc/cp/decl.c (various) Cosmetic fixes.
        (adjust_fn_constraints): Rewrite so that class template constraints
        are not imposed on member function declarations.
        * gcc/testsuite/g++.dg/concepts: New tests.

Andrew Sutton
diff mbox

Patch

Index: gcc/cp/decl.c
===================================================================
--- gcc/cp/decl.c	(revision 213909)
+++ gcc/cp/decl.c	(working copy)
@@ -991,11 +991,6 @@  decls_match (tree newdecl, tree olddecl)
       if (t1 != t2)
 	return 0;
 
-      // Normal functions can be constraind. Two functions with the
-      // same type and different constraints are different functions.
-      tree c1 = get_constraints (newdecl);
-      tree c2 = get_constraints (olddecl);
-
       if (CP_DECL_CONTEXT (newdecl) != CP_DECL_CONTEXT (olddecl)
 	  && ! (DECL_EXTERN_C_P (newdecl)
 		&& DECL_EXTERN_C_P (olddecl)))
@@ -1014,6 +1009,11 @@  decls_match (tree newdecl, tree olddecl)
 	 type for declaration matching.  */
       r2 = fndecl_declared_return_type (olddecl);
 
+      // Normal functions can be constraind. Two functions with the
+      // same type and different constraints are different functions.
+      tree c1 = get_constraints (newdecl);
+      tree c2 = get_constraints (olddecl);
+
       if (same_type_p (TREE_TYPE (f1), r2))
 	{
 	  if (!prototype_p (f2) && DECL_EXTERN_C_P (olddecl)
@@ -1041,7 +1041,7 @@  decls_match (tree newdecl, tree olddecl)
 	      TREE_TYPE (newdecl) = TREE_TYPE (olddecl);
 	    }
 #endif
-	  else {
+	  else
 	    types_match =
 	      compparms (p1, p2)
 	      && type_memfn_rqual (f1) == type_memfn_rqual (f2)
@@ -1049,7 +1049,6 @@  decls_match (tree newdecl, tree olddecl)
 	      && (TYPE_ATTRIBUTES (TREE_TYPE (newdecl)) == NULL_TREE
 	          || comp_type_attributes (TREE_TYPE (newdecl),
 					   TREE_TYPE (olddecl)) != 0);
-          }
 	}
       else
 	types_match = 0;
@@ -7564,24 +7563,54 @@  get_leading_constraints ()
 // parameter list. The adjustment makes them part of the current
 // template requirements.
 static void
-adjust_out_of_class_fn_requirements (tree ctype)
+adjust_fn_constraints (tree ctype)
 {
+  // When grokking a member function template, we are processing
+  // template decl at a depth greater than that of the member's
+  // enclosing class. That is, this case corresponds to the
+  // following declarations:
+  //
+  //    template<C T>
+  //    struct S {
+  //      template<D U> void f(U);
+  //    };
+  //
+  //    template<C T> template <D U> void S<T>::f(U) { }
+  //
+  // In both decls, the constraint D<U> is not the current leading
+  // constraint. Make it so.
+  //
+  // Note that for normal member functions, there are no leading
+  // requirements we need to gather.
   if (ctype && processing_template_decl > template_class_depth (ctype))
     {
       if (tree ci = TEMPLATE_PARMS_CONSTRAINTS (current_template_parms))
         {
+          // TODO: When do I ever have leading requirements for a
+          // member function template?
           tree reqs = CI_LEADING_REQS (ci);
           if (reqs && !get_leading_constraints ())
             current_template_reqs = save_leading_constraints (reqs);
         }
     }
-  else if (current_template_parms)
+
+  // Otherwise, this is just a regular function template. Like so:
+  //
+  //    template<C T> void f();
+  //
+  // Note that the constraint C<T> is not the current leading requirement
+  // at this point; it was stashed before the declarator was parsed.
+  // Make it the leading constraint.
+  else if (!ctype && current_template_parms)
   {
     if (tree ci = TEMPLATE_PARMS_CONSTRAINTS (current_template_parms))
       {
         tree r1 = CI_LEADING_REQS (ci);
         if (current_template_reqs)
           {
+            // TODO: As with above, when do I ever have leading
+            // requirements that aren't part of the template
+            // constraint.
             tree r2 = CI_LEADING_REQS (current_template_reqs);
             CI_LEADING_REQS (current_template_reqs) = 
                 conjoin_constraints (r1, r2);
@@ -7649,9 +7678,8 @@  grokfndecl (tree ctype,
   // Possibly adjust the template requirements for out-of-class
   // function definitions. This guarantees that current_template_reqs
   // will be fully completed before calling finish_template_constraints.
-  // verbatim ("%d", processing_template_decl);
   if (flag_concepts)
-    adjust_out_of_class_fn_requirements (ctype);
+    adjust_fn_constraints (ctype);
       
   // Check and normalize the template requirements for the declared
   // function. Note that these constraints are multiply associated
Index: gcc/cp/pt.c
===================================================================
--- gcc/cp/pt.c	(revision 213909)
+++ gcc/cp/pt.c	(working copy)
@@ -144,7 +144,7 @@  static int unify (tree, tree, tree, tree
 static void add_pending_template (tree);
 static tree reopen_tinst_level (struct tinst_level *);
 static tree tsubst_initializer_list (tree, tree);
-static tree get_class_bindings (tree, tree, tree, tree, tree);
+static tree get_class_bindings (tree, tree, tree, tree);
 static tree coerce_template_parms (tree, tree, tree, tsubst_flags_t,
 				   bool, bool);
 static tree coerce_innermost_template_parms (tree, tree, tree, tsubst_flags_t,
@@ -1544,6 +1544,7 @@  register_specialization (tree spec, tree
 		 there were no definition, and vice versa.  */
 	      DECL_INITIAL (fn) = NULL_TREE;
 	      duplicate_decls (spec, fn, is_friend);
+
 	      /* The call to duplicate_decls will have applied
 		 [temp.expl.spec]:
 
@@ -1918,6 +1919,34 @@  print_candidates (tree fns)
   gcc_assert (str == NULL);
 }
 
+// Among candidates having the same signature, return the most constrained
+// or NULL_TREE if there is no best candidate. If the signatures of candidates 
+// vary (e.g., template specialization vs. member function), then there can
+// be no most constrained.
+static tree
+most_constrained_function (tree candidates)
+{
+  // Try to find the best candidate in a first pass.
+  tree champ = candidates;
+  for (tree c = TREE_CHAIN (champ); c; c = TREE_CHAIN (c))
+    {
+      int winner = more_constrained (TREE_VALUE (champ), TREE_VALUE (c));
+      if (winner == -1)
+        champ = c; // The candidate is more constrained
+      else if (winner == 0)
+        return NULL_TREE; // Neither is more constrained
+    }
+
+  // Verify that the champ is better than previous candidates.
+  for (tree c = candidates; c != champ; c = TREE_CHAIN (c)) {
+    if (!more_constrained (TREE_VALUE (champ), TREE_VALUE (c)))
+      return NULL_TREE;
+  }
+
+  return champ;
+}
+
+
 /* Returns the template (one of the functions given by TEMPLATE_ID)
    which can be specialized to match the indicated DECL with the
    explicit template args given in TEMPLATE_ID.  The DECL may be
@@ -1954,6 +1983,8 @@  determine_specialization (tree template_
   tree targs;
   tree explicit_targs;
   tree candidates = NULL_TREE;
+  tree rejections = NULL_TREE;
+
   /* A TREE_LIST of templates of which DECL may be a specialization.
      The TREE_VALUE of each node is a TEMPLATE_DECL.  The
      corresponding TREE_PURPOSE is the set of template arguments that,
@@ -2045,6 +2076,7 @@  determine_specialization (tree template_
 	     Notice that if header_count is zero, this is not a
 	     specialization but rather a template instantiation, so there
 	     is no check we can perform here.  */
+
 	  if (header_count && header_count != template_count + 1)
 	    continue;
 
@@ -2082,7 +2114,11 @@  determine_specialization (tree template_
 	  /* Function templates cannot be specializations; there are
 	     no partial specializations of functions.  Therefore, if
 	     the type of DECL does not match FN, there is no
-	     match.  */
+	     match.  
+
+             Note that it should never be the case that we have both
+             candidates added here, and for regular member functions
+             below. */
 	  if (tsk == tsk_template)
 	    {
 	      if (compparms (fn_arg_types, decl_arg_types))
@@ -2103,11 +2139,8 @@  determine_specialization (tree template_
 	       specialize TMPL will produce DECL.  */
 	    continue;
 
-	  // Make sure that the deduced arguments actually work. First,
-          // check that any template constraints are satisfied.
-          //
-          // TODO: Make sure that we get reasonable diagnostics for these
-          // kinds of failures.
+	  // The deduced arguments must satisfy the template constraints
+          // in order to be viable.
           if (!check_template_constraints (fn, targs))
             continue;
 
@@ -2158,6 +2191,7 @@  determine_specialization (tree template_
 	       of a template class.  This is not a candidate.  */
 	    continue;
 
+
 	  if (!same_type_p (TREE_TYPE (TREE_TYPE (decl)),
 			    TREE_TYPE (TREE_TYPE (fn))))
 	    /* The return types differ.  */
@@ -2169,11 +2203,23 @@  determine_specialization (tree template_
 	      && DECL_NONSTATIC_MEMBER_FUNCTION_P (decl))
 	    decl_arg_types = TREE_CHAIN (decl_arg_types);
 
-	  if (compparms (TYPE_ARG_TYPES (TREE_TYPE (fn)),
+	  if (!compparms (TYPE_ARG_TYPES (TREE_TYPE (fn)),
 			 decl_arg_types))
-	    /* They match!  */
-	    candidates = tree_cons (NULL_TREE, fn, candidates);
-	}
+            continue;
+
+          // If the deduced arguments do not satisfy the constraints,
+          // this is not a candidate. If it fails, record the
+          // rejected candidate.
+          tree targs = DECL_TI_ARGS (fn);
+          if (!check_template_constraints (fn, targs))
+          {
+            rejections = tree_cons (NULL_TREE, fn, rejections);
+            continue;
+          }
+
+          // Add the candidate.
+          candidates = tree_cons (NULL_TREE, fn, candidates);
+        }
     }
 
   if (templates && TREE_CHAIN (templates))
@@ -2217,11 +2263,26 @@  determine_specialization (tree template_
 	}
     }
 
+  // Concepts allows multiple declarations of member functions
+  // with the same signature. Like above, we need to rely on
+  // on the partial ordering of those candidates to determine which
+  // is the best.
+  if (flag_concepts && candidates && TREE_CHAIN (candidates))
+    {
+      if (tree cand = most_constrained_function (candidates))
+        {
+          candidates = cand;
+          TREE_CHAIN (cand) = NULL_TREE;
+        }
+    }
+
   if (templates == NULL_TREE && candidates == NULL_TREE)
     {
       error ("template-id %qD for %q+D does not match any template "
 	     "declaration", template_id, decl);
-      if (header_count && header_count != template_count + 1)
+      if (rejections)
+        print_candidates (rejections);
+      else if (header_count && header_count != template_count + 1)
 	inform (input_location, "saw %d %<template<>%>, need %d for "
 		"specializing a member function template",
 		header_count, template_count + 1);
@@ -2243,10 +2304,15 @@  determine_specialization (tree template_
     {
       tree fn = TREE_VALUE (candidates);
       *targs_out = copy_node (DECL_TI_ARGS (fn));
+
+      // Propagate the candidate's constraints to the declaration.
+      set_constraints (decl, get_constraints (fn));
+
       /* DECL is a re-declaration or partial instantiation of a template
 	 function.  */
       if (TREE_CODE (fn) == TEMPLATE_DECL)
 	return fn;
+
       /* It was a specialization of an ordinary member function in a
 	 template class.  */
       return DECL_TI_TEMPLATE (fn);
@@ -2263,6 +2329,11 @@  determine_specialization (tree template_
     }
   else
     *targs_out = TREE_PURPOSE (templates);
+  
+  // Associate the deduced constraints with the declaration.
+  if (tree ci = get_constraints (TREE_VALUE (templates)))
+    set_constraints (decl, tsubst_constraint_info (ci, *targs_out));
+
   return TREE_VALUE (templates);
 }
 
@@ -19184,7 +19255,7 @@  more_specialized_class (tree tmpl, tree
      types in the arguments, and we need our dependency check functions
      to behave correctly.  */
   ++processing_template_decl;
-  targs = get_class_bindings (tmpl, tmpl1, TREE_VALUE (pat1),
+  targs = get_class_bindings (tmpl, TREE_VALUE (pat1),
 			      CLASSTYPE_TI_ARGS (type1),
 			      CLASSTYPE_TI_ARGS (type2));
   if (targs)
@@ -19193,7 +19264,7 @@  more_specialized_class (tree tmpl, tree
       any_deductions = true;
     }
 
-  targs = get_class_bindings (tmpl, tmpl2, TREE_VALUE (pat2),
+  targs = get_class_bindings (tmpl, TREE_VALUE (pat2),
 			      CLASSTYPE_TI_ARGS (type2),
 			      CLASSTYPE_TI_ARGS (type1));
   if (targs)
@@ -19228,10 +19299,12 @@  more_specialized_class (tree tmpl, tree
         return -1;
     }
 
+
   // If still tied at this point, the most specialized is also
   // the most constrained. 
   if (!winner)
     return more_constrained (tmpl1, tmpl2);
+
   return winner;
 }
 
@@ -19293,8 +19366,7 @@  get_bindings (tree fn, tree decl, tree e
    is bound to `double'.  */
 
 static tree
-get_class_bindings (tree tmpl, tree spec_tmpl, tree tparms, 
-                    tree spec_args, tree args)
+get_class_bindings (tree tmpl, tree tparms, tree spec_args, tree args)
 {
   int i, ntparms = TREE_VEC_LENGTH (tparms);
   tree deduced_args;
@@ -19352,15 +19424,37 @@  get_class_bindings (tree tmpl, tree spec
   if (!template_template_parm_bindings_ok_p (tparms, deduced_args))
     return NULL_TREE;
 
-  // Having computed and checked the binding, make sure that the
-  // deduced arguments actually satisfy the template constraints.
-  // If not, it is equivalent to having failed to compute the binding.
-  if (!check_template_constraints (spec_tmpl, deduced_args))
-    return NULL_TREE;  
-
   return deduced_args;
 }
 
+// Compare two function templates T! and T2 by deducing bindings 
+// from one against the other. If both deductions succeed, compare 
+// constraints to see which is more constrained.
+static int
+more_specialized_inst (tree t1, tree t2) 
+{
+  int fate = 0;
+  int count = 0;
+
+  if (get_bindings (t1, DECL_TEMPLATE_RESULT (t2), NULL_TREE, true))
+    {
+      --fate;
+      ++count;
+    }
+
+  if (get_bindings (t2, DECL_TEMPLATE_RESULT (t1), NULL_TREE, true))
+    {
+      ++fate;
+      ++count;
+    }
+
+  // If both deductions succeed, then one may be more constrained.
+  if (count == 2 && fate == 0)
+    fate = more_constrained (t1, t2);
+
+  return fate;
+}
+
 /* TEMPLATES is a TREE_LIST.  Each TREE_VALUE is a TEMPLATE_DECL.
    Return the TREE_LIST node with the most specialized template, if
    any.  If there is no most specialized template, the error_mark_node
@@ -19382,18 +19476,7 @@  most_specialized_instantiation (tree tem
   champ = templates;
   for (fn = TREE_CHAIN (templates); fn; fn = TREE_CHAIN (fn))
     {
-      int fate = 0;
-
-      if (get_bindings (TREE_VALUE (champ),
-			DECL_TEMPLATE_RESULT (TREE_VALUE (fn)),
-			NULL_TREE, /*check_ret=*/true))
-	fate--;
-
-      if (get_bindings (TREE_VALUE (fn),
-			DECL_TEMPLATE_RESULT (TREE_VALUE (champ)),
-			NULL_TREE, /*check_ret=*/true))
-	fate++;
-
+      int fate = more_specialized_inst (TREE_VALUE (champ), TREE_VALUE (fn));
       if (fate == -1)
 	champ = fn;
       else if (!fate)
@@ -19410,17 +19493,13 @@  most_specialized_instantiation (tree tem
   if (champ)
     /* Now verify that champ is better than everything earlier in the
        instantiation list.  */
-    for (fn = templates; fn != champ; fn = TREE_CHAIN (fn))
-      if (get_bindings (TREE_VALUE (champ),
-			DECL_TEMPLATE_RESULT (TREE_VALUE (fn)),
-			NULL_TREE, /*check_ret=*/true)
-	  || !get_bindings (TREE_VALUE (fn),
-			    DECL_TEMPLATE_RESULT (TREE_VALUE (champ)),
-			    NULL_TREE, /*check_ret=*/true))
-	{
-	  champ = NULL_TREE;
-	  break;
-	}
+    for (fn = templates; fn != champ; fn = TREE_CHAIN (fn)) {
+      if (more_specialized_inst (TREE_VALUE (champ), TREE_VALUE (fn)) != 1)
+      {
+        champ = NULL_TREE;
+        break;
+      }
+    }
 
   processing_template_decl--;
 
@@ -19555,15 +19634,18 @@  most_specialized_class (tree type, tsubs
 	return error_mark_node;
 
       tree parms = DECL_INNERMOST_TEMPLATE_PARMS (spec_tmpl);
-      spec_args = get_class_bindings (tmpl, spec_tmpl, parms,
-				      partial_spec_args,
-				      args);
+      spec_args = get_class_bindings (tmpl, parms, partial_spec_args, args);
       if (spec_args)
 	{
 	  if (outer_args)
 	    spec_args = add_to_template_args (outer_args, spec_args);
-	  list = tree_cons (spec_args, orig_parms, list);
-	  TREE_TYPE (list) = TREE_TYPE (t);
+
+          // Keep the candidate only if the constraints are satisfied.
+          if (check_template_constraints (spec_tmpl, spec_args))
+            {
+              list = tree_cons (spec_args, orig_parms, list);
+              TREE_TYPE (list) = TREE_TYPE (t);
+            }
 	}
     }
 
Index: gcc/cp/call.c
===================================================================
--- gcc/cp/call.c	(revision 213667)
+++ gcc/cp/call.c	(working copy)
@@ -9010,14 +9010,10 @@  joust (struct z_candidate *cand1, struct
     }
 
     // C++ Concepts
-    // or, if not that,
-    // F1 and F2 are member functions of a class template specialization
-    // T, and M1 and M2 are member functions in the template of T
-    // corresponding to F1 and F2, and M1 is more constrained according
-    // to the partial ordering rules for constraints.
-    if (m1 && m2)
+    // or, if not that, F1 is more constrained than F2. 
+    if (flag_concepts)
       {
-        winner = more_constrained (m1, m2);
+        winner = more_constrained (cand1->fn, cand2->fn);
         if (winner)
           return winner;
       }
Index: gcc/cp/constraint.cc
===================================================================
--- gcc/cp/constraint.cc	(revision 213909)
+++ gcc/cp/constraint.cc	(working copy)
@@ -597,8 +597,8 @@  normalize_constraints (tree reqs)
 // The following functions are called by the parser and substitution rules
 // to create and evaluate constraint-related nodes.
 
-// Returns the template constraints of declaration T. If T is not a
-// template, this return NULL_TREE. Note that T must be non-null.
+// Returns the template constraints of declaration T. Note that 
+// T must be non-null.
 tree
 get_constraints (tree t)
 {