public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* C++ PATCH for non-type constrained-type-specifiers
@ 2015-11-06 15:52 Jason Merrill
  2015-11-06 22:01 ` Andrew Sutton
  0 siblings, 1 reply; 2+ messages in thread
From: Jason Merrill @ 2015-11-06 15:52 UTC (permalink / raw)
  To: gcc-patches List; +Cc: Andrew Sutton

[-- Attachment #1: Type: text/plain, Size: 897 bytes --]

I started looking at allowing non-type constrained-type-specifiers in 
auto deduction and then realized that we didn't handle them in function 
parameters either.  Fixing that brought home to me the oddity of having 
a type-specifier stand in for a non-type.  Mind weighing in on that on 
the core reflector?

I also wonder why we have two different ways of expressing a 
constrained-type-specifier in the implementation: a constrained 
template-parameter (TYPE_DECL, is_constrained_parameter) and a 
constrained auto (TEMPLATE_TYPE_PARM).  Why not represent them the same way?

This patch doesn't mess with this duality, but extends 
equivalent_placeholder_constraints to deal with both kinds, and then 
uses that for comparing constrained-type-specifiers.  And also handles 
non-type constrained-type-specifiers in abbreviated function templates.

Tested x86_64-pc-linux-gnu, applying to trunk.

[-- Attachment #2: non-type.patch --]
[-- Type: text/x-patch, Size: 13500 bytes --]

commit 030c2ba7fc87d486041766979999c8dcf1bf48da
Author: Jason Merrill <jason@redhat.com>
Date:   Fri Nov 6 00:17:57 2015 -0500

    	Support non-type constrained-type-specifiers.
    	* parser.c (check_type_concept): Remove.
    	(cp_parser_maybe_constrained_type_specifier): Don't call it.
    	(synthesize_implicit_template_parm): Handle non-type and template
    	template parameters.  Also compare extra args.  Return the decl.
    	(cp_parser_template_argument): Handle constrained-type-specifiers for
    	non-type template parameters.
    	(finish_constrained_template_template_parm): Split out from
    	cp_parser_constrained_template_template_parm.
    	(cp_parser_nonclass_name): Move some logic into
    	cp_parser_maybe_concept_name.
    	(cp_parser_init_declarator): Fix error recovery.
    	(get_concept_from_constraint): Remove.
    	(cp_parser_simple_type_specifier): Adjust for
    	synthesize_implicit_template_parm returning the decl.
    	* constraint.cc (placeholder_extract_concept_and_args)
    	(equivalent_placeholder_constraints): Also handle TYPE_DECL
    	constrained parms.

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index a1fbf17..c6eaf75 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -1379,12 +1379,21 @@ make_constrained_auto (tree con, tree args)
   return decl;
 }
 
-/* Given the predicate constraint T from a placeholder type, extract its
-   TMPL and ARGS.  */
+/* Given the predicate constraint T from a constrained-type-specifier, extract
+   its TMPL and ARGS.  FIXME why do we need two different forms of
+   constrained-type-specifier?  */
 
 void
 placeholder_extract_concept_and_args (tree t, tree &tmpl, tree &args)
 {
+  if (TREE_CODE (t) == TYPE_DECL)
+    {
+      /* A constrained parameter.  */
+      tmpl = DECL_TI_TEMPLATE (CONSTRAINED_PARM_CONCEPT (t));
+      args = CONSTRAINED_PARM_EXTRA_ARGS (t);
+      return;
+    }
+
   gcc_assert (TREE_CODE (t) == PRED_CONSTR);
   t = PRED_CONSTR_EXPR (t);
   gcc_assert (TREE_CODE (t) == CALL_EXPR
@@ -1418,9 +1427,10 @@ placeholder_extract_concept_and_args (tree t, tree &tmpl, tree &args)
 bool
 equivalent_placeholder_constraints (tree c1, tree c2)
 {
-  if (TREE_CODE (c1) == TEMPLATE_TYPE_PARM)
+  if (c1 && TREE_CODE (c1) == TEMPLATE_TYPE_PARM)
+    /* A constrained auto.  */
     c1 = PLACEHOLDER_TYPE_CONSTRAINTS (c1);
-  if (TREE_CODE (c2) == TEMPLATE_TYPE_PARM)
+  if (c2 && TREE_CODE (c2) == TEMPLATE_TYPE_PARM)
     c2 = PLACEHOLDER_TYPE_CONSTRAINTS (c2);
 
   if (c1 == c2)
@@ -1434,14 +1444,21 @@ equivalent_placeholder_constraints (tree c1, tree c2)
 
   if (t1 != t2)
     return false;
-  int len = TREE_VEC_LENGTH (a1);
-  if (len != TREE_VEC_LENGTH (a2))
-    return false;
+
   /* Skip the first argument to avoid infinite recursion on the
      placeholder auto itself.  */
-  for (int i = len-1; i > 0; --i)
-    if (!cp_tree_equal (TREE_VEC_ELT (a1, i),
-			TREE_VEC_ELT (a2, i)))
+  bool skip1 = (TREE_CODE (c1) == PRED_CONSTR);
+  bool skip2 = (TREE_CODE (c2) == PRED_CONSTR);
+
+  int len1 = (a1 ? TREE_VEC_LENGTH (a1) : 0) - skip1;
+  int len2 = (a2 ? TREE_VEC_LENGTH (a2) : 0) - skip2;
+
+  if (len1 != len2)
+    return false;
+
+  for (int i = 0; i < len1; ++i)
+    if (!cp_tree_equal (TREE_VEC_ELT (a1, i + skip1),
+			TREE_VEC_ELT (a2, i + skip2)))
       return false;
   return true;
 }
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index c6f5729..d1f4970 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -13871,18 +13871,9 @@ cp_parser_constrained_type_template_parm (cp_parser *parser,
     return error_mark_node;
 }
 
-/* Finish parsing/processing a template template parameter by borrowing
-   the template parameter list from the prototype parameter.  */
-
 static tree
-cp_parser_constrained_template_template_parm (cp_parser *parser,
-                                              tree proto,
-                                              tree id,
-                                              cp_parameter_declarator *parmdecl)
+finish_constrained_template_template_parm (tree proto, tree id)
 {
-  if (!cp_parser_check_constrained_type_parm (parser, parmdecl))
-    return error_mark_node;
-
   /* FIXME: This should probably be copied, and we may need to adjust
      the template parameter depths.  */
   tree saved_parms = current_template_parms;
@@ -13896,6 +13887,20 @@ cp_parser_constrained_template_template_parm (cp_parser *parser,
   return parm;
 }
 
+/* Finish parsing/processing a template template parameter by borrowing
+   the template parameter list from the prototype parameter.  */
+
+static tree
+cp_parser_constrained_template_template_parm (cp_parser *parser,
+                                              tree proto,
+                                              tree id,
+                                              cp_parameter_declarator *parmdecl)
+{
+  if (!cp_parser_check_constrained_type_parm (parser, parmdecl))
+    return error_mark_node;
+  return finish_constrained_template_template_parm (proto, id);
+}
+
 /* Create a new non-type template parameter from the given PARM
    declarator.  */
 
@@ -14950,8 +14955,12 @@ cp_parser_template_argument (cp_parser* parser)
 					  /*check_dependency=*/true,
 					  /*ambiguous_decls=*/NULL,
 					  argument_start_token->location);
-      if (TREE_CODE (argument) != TEMPLATE_DECL
-	  && TREE_CODE (argument) != UNBOUND_CLASS_TEMPLATE)
+      /* Handle a constrained-type-specifier for a non-type template
+	 parameter.  */
+      if (tree decl = cp_parser_maybe_concept_name (parser, argument))
+	argument = decl;
+      else if (TREE_CODE (argument) != TEMPLATE_DECL
+	       && TREE_CODE (argument) != UNBOUND_CLASS_TEMPLATE)
 	cp_parser_error (parser, "expected template-name");
     }
   if (cp_parser_parse_definitely (parser))
@@ -15630,7 +15639,10 @@ cp_parser_simple_type_specifier (cp_parser* parser,
 	    }
 
 	  if (cxx_dialect >= cxx14)
-	    type = synthesize_implicit_template_parm (parser, NULL_TREE);
+	    {
+	      type = synthesize_implicit_template_parm (parser, NULL_TREE);
+	      type = TREE_TYPE (type);
+	    }
 	  else
 	    type = error_mark_node;
 
@@ -15949,19 +15961,6 @@ cp_parser_type_name (cp_parser* parser, bool typename_keyword_p)
   return type_decl;
 }
 
-/* Returns true if proto is a type parameter, but not a template
-   template parameter.  */
-static bool
-check_type_concept (tree fn, tree proto)
-{
-  if (TREE_CODE (proto) != TYPE_DECL)
-    {
-      error ("invalid use of non-type concept %qD", fn);
-      return false;
-    }
-  return true;
-}
-
 /*  Check if DECL and ARGS can form a constrained-type-specifier.
     If ARGS is non-null, we try to form a concept check of the
     form DECL<?, ARGS> where ? is a wildcard that matches any
@@ -16009,13 +16008,6 @@ cp_parser_maybe_constrained_type_specifier (cp_parser *parser,
   if (processing_template_parmlist)
     return build_constrained_parameter (conc, proto, args);
 
-  /* In any other context, a concept must be a type concept.
-
-     FIXME: A constrained-type-specifier can be a placeholder
-     of any kind.  */
-  if (!check_type_concept (conc, proto))
-    return error_mark_node;
-
   /* In a parameter-declaration-clause, constrained-type
      specifiers result in invented template parameters.  */
   if (parser->auto_is_implicit_function_template_parm_p)
@@ -16046,7 +16038,13 @@ cp_parser_maybe_constrained_type_specifier (cp_parser *parser,
 static tree
 cp_parser_maybe_concept_name (cp_parser* parser, tree decl)
 {
-  return cp_parser_maybe_constrained_type_specifier (parser, decl, NULL_TREE);
+  if (flag_concepts
+      && (TREE_CODE (decl) == OVERLOAD
+	  || BASELINK_P (decl)
+	  || variable_concept_p (decl)))
+    return cp_parser_maybe_constrained_type_specifier (parser, decl, NULL_TREE);
+  else
+    return NULL_TREE;
 }
 
 /* Check if DECL and ARGS form a partial-concept-id.  If so,
@@ -16093,15 +16091,8 @@ cp_parser_nonclass_name (cp_parser* parser)
   type_decl = strip_using_decl (type_decl);
   
   /* If we found an overload set, then it may refer to a concept-name. */
-  if (flag_concepts
-      && (TREE_CODE (type_decl) == OVERLOAD
-	  || BASELINK_P (type_decl)
-	  || variable_concept_p (type_decl)))
-  {
-    /* Determine whether the overload refers to a concept. */
-    if (tree decl = cp_parser_maybe_concept_name (parser, type_decl))
-      return decl;
-  }
+  if (tree decl = cp_parser_maybe_concept_name (parser, type_decl))
+    type_decl = decl;
 
   if (TREE_CODE (type_decl) != TYPE_DECL
       && (objc_is_id (identifier) || objc_is_class_name (identifier)))
@@ -18183,7 +18174,7 @@ cp_parser_init_declarator (cp_parser* parser,
 	       "attributes after parenthesized initializer ignored");
 
   /* And now complain about a non-function implicit template.  */
-  if (bogus_implicit_tmpl)
+  if (bogus_implicit_tmpl && decl != error_mark_node)
     error_at (DECL_SOURCE_LOCATION (decl),
 	      "non-function %qD declared as implicit template", decl);
 
@@ -36681,17 +36672,6 @@ tree_type_is_auto_or_concept (const_tree t)
   return TREE_TYPE (t) && is_auto_or_concept (TREE_TYPE (t));
 }
 
-/* Returns the template declaration being called or evaluated as
-   part of the constraint check. Note that T must be a predicate
-   constraint (it can't be any other kind of constraint). */
-static tree
-get_concept_from_constraint (tree t)
-{
-  tree tmpl, args;
-  placeholder_extract_concept_and_args (t, tmpl, args);
-  return DECL_TEMPLATE_RESULT (tmpl);
-}
-
 /* Add an implicit template type parameter to the CURRENT_TEMPLATE_PARMS
    (creating a new template parameter list if necessary).  Returns the newly
    created template type parm.  */
@@ -36711,9 +36691,14 @@ synthesize_implicit_template_parm  (cp_parser *parser, tree constr)
       tree t = parser->implicit_template_parms;
       while (t)
         {
-          tree c = get_concept_from_constraint (TREE_TYPE (t));
-          if (c == CONSTRAINED_PARM_CONCEPT (constr))
-            return TREE_VALUE (t);
+          if (equivalent_placeholder_constraints (TREE_TYPE (t), constr))
+	    {
+	      tree d = TREE_VALUE (t);
+	      if (TREE_CODE (d) == PARM_DECL)
+		/* Return the TEMPLATE_PARM_INDEX.  */
+		d = DECL_INITIAL (d);
+	      return d;
+	    }
           t = TREE_CHAIN (t);
         }
     }
@@ -36823,9 +36808,23 @@ synthesize_implicit_template_parm  (cp_parser *parser, tree constr)
   /* Synthesize a new template parameter and track the current template
      parameter chain with implicit_template_parms.  */
 
+  tree proto = constr ? DECL_INITIAL (constr) : NULL_TREE;
   tree synth_id = make_generic_type_name ();
-  tree synth_tmpl_parm = finish_template_type_parm (class_type_node,
-						    synth_id);
+  tree synth_tmpl_parm;
+  bool non_type = false;
+
+  if (proto == NULL_TREE || TREE_CODE (proto) == TYPE_DECL)
+    synth_tmpl_parm
+      = finish_template_type_parm (class_type_node, synth_id);
+  else if (TREE_CODE (proto) == TEMPLATE_DECL)
+    synth_tmpl_parm
+      = finish_constrained_template_template_parm (proto, synth_id);
+  else
+    {
+      synth_tmpl_parm = copy_decl (proto);
+      DECL_NAME (synth_tmpl_parm) = synth_id;
+      non_type = true;
+    }
 
   // Attach the constraint to the parm before processing.
   tree node = build_tree_list (NULL_TREE, synth_tmpl_parm);
@@ -36834,7 +36833,7 @@ synthesize_implicit_template_parm  (cp_parser *parser, tree constr)
     = process_template_parm (parser->implicit_template_parms,
 			     input_location,
 			     node,
-			     /*non_type=*/false,
+			     /*non_type=*/non_type,
 			     /*param_pack=*/false);
 
   // Chain the new parameter to the list of implicit parameters.
@@ -36844,7 +36843,10 @@ synthesize_implicit_template_parm  (cp_parser *parser, tree constr)
   else
     parser->implicit_template_parms = new_parm;
 
-  tree new_type = TREE_TYPE (getdecls ());
+  tree new_decl = getdecls ();
+  if (non_type)
+    /* Return the TEMPLATE_PARM_INDEX, not the PARM_DECL.  */
+    new_decl = DECL_INITIAL (new_decl);
 
   /* If creating a fully implicit function template, start the new implicit
      template parameter list with this synthesized type, otherwise grow the
@@ -36878,7 +36880,7 @@ synthesize_implicit_template_parm  (cp_parser *parser, tree constr)
 
   current_binding_level = entry_scope;
 
-  return new_type;
+  return new_decl;
 }
 
 /* Finish the declaration of a fully implicit function template.  Such a
diff --git a/gcc/testsuite/g++.dg/concepts/generic-fn-err.C b/gcc/testsuite/g++.dg/concepts/generic-fn-err.C
index c6b7457..1e97510 100644
--- a/gcc/testsuite/g++.dg/concepts/generic-fn-err.C
+++ b/gcc/testsuite/g++.dg/concepts/generic-fn-err.C
@@ -11,8 +11,8 @@ template<template<typename> class X>
 
 struct S { };
 
-void f1(Int) { }      // { dg-error "invalid" }
-void f2(Template) { } // { dg-error "invalid" }
+void f1(Int) { }      // { dg-error "" }
+void f2(Template) { } // { dg-error "" }
 
 struct S1 {
   void f1(auto x) { }
diff --git a/gcc/testsuite/g++.dg/concepts/placeholder6.C b/gcc/testsuite/g++.dg/concepts/placeholder6.C
new file mode 100644
index 0000000..7f2e67a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/placeholder6.C
@@ -0,0 +1,10 @@
+// { dg-options -std=c++1z }
+
+template <int I> struct B { static const int i = I; };
+template <int I> concept bool Few = I < 10;
+
+constexpr int g(B<Few> b) { return b.i; }
+
+#define SA(X) static_assert((X),#X)
+SA(g(B<2>{}) == 2);
+SA(g(B<10>{}) == 10); 		// { dg-error "" }

^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: C++ PATCH for non-type constrained-type-specifiers
  2015-11-06 15:52 C++ PATCH for non-type constrained-type-specifiers Jason Merrill
@ 2015-11-06 22:01 ` Andrew Sutton
  0 siblings, 0 replies; 2+ messages in thread
From: Andrew Sutton @ 2015-11-06 22:01 UTC (permalink / raw)
  To: Jason Merrill, gcc-patches

> I started looking at allowing non-type constrained-type-specifiers in auto
> deduction and then realized that we didn't handle them in function
> parameters either.  Fixing that brought home to me the oddity of having a
> type-specifier stand in for a non-type.  Mind weighing in on that on the
> core reflector?

That is a little weird. The non-type placeholders should be
id-expressions that name a concept. And template placeholders would be
template-names.

I'll create an issue for it. I need to email Mike Miller and figure
out how issues processing will work. But not until I'm out of the
weeds for the semester.


> I also wonder why we have two different ways of expressing a
> constrained-type-specifier in the implementation: a constrained
> template-parameter (TYPE_DECL, is_constrained_parameter) and a constrained
> auto (TEMPLATE_TYPE_PARM).  Why not represent them the same way?

It's probably a historical distinction. We didn't get concepts as
placeholders until much later in the standardization process.

But with auto template parameters on the horizon, it might be
worthwhile to maintain the distinction. That feature adds a wonderful
little ambiguity:

template<auto X> struct S;

would declare a non-type template parm whose type is deduced from a
template argument.

template<typename T> concept bool C = true;
template<C X> struct S;

Is C a constrained placeholder (as per a reasonable interpretation of
parameter-declarations), or does it declare a type parameter (as per
the TS)? It's going to end up being the latter.

Combining the representations might make it difficult to tease out
intent later. But that's just me speculating.


Andrew

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2015-11-06 22:01 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-11-06 15:52 C++ PATCH for non-type constrained-type-specifiers Jason Merrill
2015-11-06 22:01 ` Andrew Sutton

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).