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]

partial-concept-ids


Add support for partial concept ids. Mostly this just refactors the
basic support for concept names to also allow a template and extra
arguments.

Also added the missing .exp file for the test suite.

2014-06-12  Andrew Sutton  <andrew.n.sutton@gmail.com>
        * gcc/cp/constraint.cc (deduce_constrained_parameter): Refactor
        common deduction framework into separate function.
        (build_call_check): New.
        (build_concept_check): Take additional arguments to support the
        creation of constrained-type-specifiers from partial-concept-ids.
        (build_constrained_parameter): Take arguments from a partial-concept-id.
        * gcc/cp/cp-tree.h (build_concept_check, biuld_constrained_parameter):
        Take a template argument list, defaulting to NULL_TREE.
        * gcc/cp/parser.c (cp_parser_template_id): Check to see if a
        template-id is a concept check.
        (cp_check_type_concept): Reorder arguments
        (cp_parser_allows_constrained_type_specifier): New. Check contexts
        where a constrained-type-specifier is allowed.
        (cp_maybe_constrained_type_specifier): New. Refactored common rules
        for concept name checks.
        (cp_maybe_partial_concept_id): New. Check for
        constrained-type-specifiers.
        * gcc/testuite/g++.dg/concepts/partial.C: New tests.
        * gcc/testuite/g++.dg/concepts/partial-err.C: New tests.
        * gcc/testuite/g++.dg/concepts/concepts.exp: Add missing test driver.

Andrew Sutton
Index: parser.c
===================================================================
--- parser.c	(revision 211585)
+++ parser.c	(working copy)
@@ -2523,7 +2523,10 @@ static tree cp_parser_make_typename_type
 static cp_declarator * cp_parser_make_indirect_declarator
  (enum tree_code, tree, cp_cv_quals, cp_declarator *, tree);
 
+/* Concept-related syntactic transformations */
 
+static tree cp_maybe_concept_name       (cp_parser *, tree);
+static tree cp_maybe_partial_concept_id (cp_parser *, tree, tree);
 
 // -------------------------------------------------------------------------- //
 // Unevaluated Operand Guard
@@ -13775,6 +13778,11 @@ cp_parser_template_id (cp_parser *parser
 		   || TREE_CODE (templ) == OVERLOAD
 		   || BASELINK_P (templ)));
 
+      // If the template + args designate a concept, then return
+      // something else.
+      if (tree id = cp_maybe_partial_concept_id (parser, templ, arguments))
+        return id;
+
       template_id = lookup_template_function (templ, arguments);
     }
 
@@ -14995,7 +15003,8 @@ cp_parser_simple_type_specifier (cp_pars
 	}
       /* Otherwise, look for a type-name.  */
       else
-	type = cp_parser_type_name (parser);
+        type = cp_parser_type_name (parser);
+      
       /* Keep track of all name-lookups performed in class scopes.  */
       if (type
 	  && !global_p
@@ -15071,6 +15080,7 @@ cp_parser_simple_type_specifier (cp_pars
    
    type-name:
      concept-name
+     partial-concept-id
 
    concept-name:
      identifier
@@ -15092,6 +15102,7 @@ cp_parser_type_name (cp_parser* parser)
 				    /*check_dependency_p=*/true,
 				    /*class_head_p=*/false,
 				    /*is_declaration=*/false);
+
   /* If it's not a class-name, keep looking.  */
   if (!cp_parser_parse_definitely (parser))
     {
@@ -15107,6 +15118,7 @@ cp_parser_type_name (cp_parser* parser)
 					 /*check_dependency_p=*/true,
 					 none_type,
 					 /*is_declaration=*/false);
+
       /* Note that this must be an instantiation of an alias template
 	 because [temp.names]/6 says:
 	 
@@ -15135,7 +15147,7 @@ cp_parser_type_name (cp_parser* parser)
 /// Returns true if proto is a type parameter, but not a template template
 /// parameter.
 static bool
-cp_check_type_concept (tree proto, tree fn) 
+cp_check_type_concept (tree fn, tree proto) 
 {
   if (TREE_CODE (proto) != TYPE_DECL) 
     {
@@ -15145,57 +15157,58 @@ cp_check_type_concept (tree proto, tree
   return true;
 }
 
-// If DECL refers to a concept, return a TYPE_DECL representing the result
-// of using the constrained type specifier in the current context. 
-//
-// DECL refers to a concept if
-//   - it is an overload set containing a function concept taking a single
-//     type argument, or
-//   - it is a variable concept taking a single type argument
-//
-//
-// TODO: DECL could be a variable concept.
+/// Returns true if the parser is in a context that allows the
+/// use of a constrained type specifier.
+static inline bool
+cp_parser_allows_constrained_type_specifier (cp_parser *parser)
+{
+  return flag_concepts 
+        && (processing_template_parmlist
+            || parser->auto_is_implicit_function_template_parm_p
+            || parser->in_result_type_constraint_p);
+}
+
+// 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 placeholder for any kind of template argument. If ARGS
+// is NULL, then we try to form a concept check of the form DEC<?>.
 static tree
-cp_check_concept_name (cp_parser* parser, tree decl)
+cp_maybe_constrained_type_specifier (cp_parser *parser, tree decl, tree args)
 {
   gcc_assert (TREE_CODE (decl) == OVERLOAD);
+  gcc_assert (args ? TREE_CODE (args) == TREE_VEC : true);
+
+  // Don't do any heavy lifting if we know we're not in a context
+  // where it could succeed.
+  if (!cp_parser_allows_constrained_type_specifier (parser))
+    return NULL_TREE;
 
   // Try to build a call expression that evaluates the concept. This
   // can fail if the overload set refers only to non-templates.
-  tree call = build_concept_check (decl, build_nt(PLACEHOLDER_EXPR));
+  tree call = build_concept_check (decl, build_nt(PLACEHOLDER_EXPR), args);
   if (call == error_mark_node)
     return NULL_TREE;
   
-  // Resolve the constraint check to deduce the declared parameter.
-  tree check = resolve_constraint_check (call);
-  if (!check)
+  // Deduce the checked constraint and the prototype parameter.
+  tree fn;
+  tree proto;
+  if (!deduce_constrained_parameter (call, fn, proto))
     return NULL_TREE;
 
-  // Get function and argument from the resolved check expression. If 
-  // the argument was a pack expansion, then get the first element 
-  // of that pack.
-  tree fn = TREE_VALUE (check);
-  tree arg = TREE_VEC_ELT (TREE_PURPOSE (check), 0);
-  if (ARGUMENT_PACK_P (arg))
-    arg = TREE_VEC_ELT (ARGUMENT_PACK_ARGS (arg), 0);
-
-  // Get the protyping parameter bound to the placeholder.
-  tree proto = TREE_TYPE (arg);
-
   // In template paramteer scope, this results in a constrained parameter.
   // Return a descriptor of that parm.
   if (processing_template_parmlist)
-    return build_constrained_parameter (proto, fn);
+    return build_constrained_parameter (fn, proto, args);
 
   // In any other context, a concept must be a type concept.
-  if (!cp_check_type_concept (proto, fn))
+  if (!cp_check_type_concept (fn, 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)
     {
-      tree x = build_constrained_parameter (proto, fn);
+      tree x = build_constrained_parameter (fn, proto, args);
       tree r = synthesize_implicit_template_parm (parser, x);
       return r;
     }
@@ -15208,11 +15221,35 @@ cp_check_concept_name (cp_parser* parser
   if (parser->in_result_type_constraint_p)
       return make_auto();
 
-  // FIXME: What other contexts accept a constrained-type-specifier?
-  // - variable declarations
-  // - trailing return types
+  return NULL_TREE;  
+}
+
+
+// If DECL refers to a concept, return a TYPE_DECL representing the result
+// of using the constrained type specifier in the current context. 
+//
+// DECL refers to a concept if
+//   - it is an overload set containing a function concept taking a single
+//     type argument, or
+//   - it is a variable concept taking a single type argument
+//
+// TODO: DECL could be a variable concept.
+static tree
+cp_maybe_concept_name (cp_parser* parser, tree decl)
+{
+  return cp_maybe_constrained_type_specifier (parser, decl, NULL_TREE);
+}
 
-  return NULL_TREE;
+// Check if DECL and ARGS forms a partial concept id. Let C be the name
+// of the overload set denoted by TMPL, Args the sequence of ARGS, and
+// ? denote an unspecified template argument. If the template-id C<?, Args>
+// is valid, this denotes a partial-concept-id to be acted on.
+//
+// TODO: TMPL could be a variable concept.
+tree
+cp_maybe_partial_concept_id (cp_parser *parser, tree decl, tree args)
+{
+  return cp_maybe_constrained_type_specifier (parser, decl, args);
 }
 
 /* Parse a non-class type-name, that is, either an enum-name, a typedef-name,
@@ -15251,7 +15288,7 @@ cp_parser_nonclass_name (cp_parser* pars
   if (flag_concepts && TREE_CODE (type_decl) == OVERLOAD)
   {
     // Determine whether the overload refers to a concept.
-    if (tree decl = cp_check_concept_name (parser, type_decl))
+    if (tree decl = cp_maybe_concept_name (parser, type_decl))
       return decl;
   }
 
Index: cp-tree.h
===================================================================
--- cp-tree.h	(revision 211476)
+++ cp-tree.h	(working copy)
@@ -841,12 +841,12 @@ check_constraint_info (tree t)
 // Access the logical constraints on the template parameters introduced 
 // at a given template parameter list level indicated by NODE.
 #define TEMPLATE_PARMS_CONSTRAINTS(NODE) \
-  TREE_TYPE(TREE_LIST_CHECK(NODE))
+  TREE_TYPE (TREE_LIST_CHECK (NODE))
 
 // Access the logical constraints on the template parameter declaration
 // indicatd by NODE.
 #define TEMPLATE_PARM_CONSTRAINTS(NODE) \
-  TREE_TYPE(TREE_LIST_CHECK(NODE))
+  TREE_TYPE (TREE_LIST_CHECK (NODE))
 
 enum cp_tree_node_structure_enum {
   TS_CP_GENERIC,
@@ -6358,8 +6358,9 @@ extern tree make_constraints
 extern tree get_constraints                     (tree);
 extern tree get_shorthand_requirements          (tree);
 
-extern tree build_concept_check                 (tree, tree);
-extern tree build_constrained_parameter         (tree, tree);
+extern tree build_concept_check                 (tree, tree, tree = NULL_TREE);
+extern tree build_constrained_parameter         (tree, tree, tree = NULL_TREE);
+extern bool deduce_constrained_parameter        (tree, tree&, tree&);
 extern tree resolve_constraint_check            (tree);
 
 extern tree finish_concept_name                 (tree);
Index: constraint.cc
===================================================================
--- constraint.cc	(revision 211476)
+++ constraint.cc	(working copy)
@@ -192,7 +192,31 @@ resolve_constraint_check (tree call)
   tree args = TREE_OPERAND (target, 1);
   return resolve_constraint_check (ovl, args);
 }
-  
+
+// Given a call expression to a concept, possibly including a placeholder
+// argument, deduce the concept being checked and the prototype paraemter.
+// Returns true if the constraint and prototype can be deduced and false
+// otherwise. Note that the CHECK and PROTO arguments are set to NULL_TREE
+// if this returns false.
+bool
+deduce_constrained_parameter (tree call, tree& check, tree& proto)
+{
+  // Resolve the constraint check to deduce the declared parameter.
+  if (tree info = resolve_constraint_check (call))
+    {
+      // Get function and argument from the resolved check expression and
+      // the prototype parameter. Note that if the first argument was a
+      // pack, we need to extract the first element ot get the prototype.
+      check = TREE_VALUE (info);
+      tree arg = TREE_VEC_ELT (TREE_PURPOSE (info), 0);
+      if (ARGUMENT_PACK_P (arg))
+        arg = TREE_VEC_ELT (ARGUMENT_PACK_ARGS (arg), 0);
+      proto = TREE_TYPE (arg);
+      return true;
+    }
+  check = proto = NULL_TREE;
+  return false;
+}
 
 // -------------------------------------------------------------------------- //
 // Requirement Reduction
@@ -812,31 +836,43 @@ check_constrained_friend (tree fn, tree
     }
 }
 
-// Given an overload set, OVL, and a template argument or placeholder, ARG,
-// synthesize a call expression that resolves to a concept check of
-// the expression the form OVL<ARG>().
+namespace {
+  // Build a new call expression, but don't actually generate a new 
+  // function call. We just want the tree, not the semantics.
+inline tree
+build_call_check (tree id)
+{
+  ++processing_template_decl;
+  vec<tree, va_gc> *fargs = make_tree_vector();
+  tree call = finish_call_expr (id, &fargs, false, false, tf_none);
+  --processing_template_decl;
+  return call;
+}
+} // namespace
+
+// Construct a concept check for the overloaded function, where the
+// template arguments are the list given by ARG and REST. That is, it
+// build the call expression OVL<ARG, REST>(). If REST is null, then
+// the resulting constraint is OVL<ARG>().
 //
 // TODO: Extend this to take a variable concept also.
 tree
-build_concept_check (tree ovl, tree arg)
+build_concept_check (tree ovl, tree arg, tree rest) 
 {
   gcc_assert (TREE_CODE (ovl) == OVERLOAD);
+  gcc_assert (rest ? TREE_CODE (rest) == TREE_VEC : true);
 
   // Build a template-id that acts as the call target using OVL as
   // the template and ARG as the only explicit argument.
-  tree targs = make_tree_vec (1);
+  int n = rest ? TREE_VEC_LENGTH (rest) : 0;
+  tree targs = make_tree_vec (n + 1);
   TREE_VEC_ELT (targs, 0) = arg;
-  SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs, 1);
+  if (rest)
+    for (int i = 0; i < n; ++i)
+      TREE_VEC_ELT (targs, i + 1) = TREE_VEC_ELT (rest, i);
+  SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs, n + 1);
   tree id = lookup_template_function (ovl, targs);
-
-  // Build a new call expression, but don't actually generate a new 
-  // function call. We just want the tree, not the semantics.
-  ++processing_template_decl;
-  vec<tree, va_gc> *fargs = make_tree_vector();
-  tree call = finish_call_expr (id, &fargs, false, false, tf_none);
-  --processing_template_decl;
-  
-  return call;
+  return build_call_check (id);
 }
 
 // Returns a TYPE_DECL that contains sufficient information to build
@@ -844,14 +880,18 @@ build_concept_check (tree ovl, tree arg)
 // by the concept declaration FN. PROTO is saved as the initializer of
 // the new type decl, and the constraining function is saved in
 // DECL_SIZE_UNIT.
+//
+// If specified ARGS provides additional arguments to the constraint
+// check. These are stored in the DECL_SIZE field.
 tree
-build_constrained_parameter (tree proto, tree fn) 
+build_constrained_parameter (tree fn, tree proto, tree args) 
 {
   tree name = DECL_NAME (fn);
   tree type = TREE_TYPE (proto);
   tree decl = build_decl (input_location, TYPE_DECL, name, type);
   DECL_INITIAL (decl) = proto;  // Describing parameter
   DECL_SIZE_UNIT (decl) = fn;   // Constraining function declaration
+  DECL_SIZE (decl) = args;      // Extra template arguments.
   return decl;
 }
 
@@ -871,6 +911,7 @@ finish_shorthand_requirement (tree decl,
 
   tree proto = DECL_INITIAL (constr); // The prototype declaration
   tree con = DECL_SIZE_UNIT (constr); // The concept declaration
+  tree args = DECL_SIZE (constr);     // Extra template arguments
 
   // If the parameter declaration is variadic, but the concept is not 
   // then we need to apply the concept to every element in the pack.
@@ -888,7 +929,7 @@ finish_shorthand_requirement (tree decl,
   // to all elements of the parameter pack, then expand make the constraint
   // an expansion.
   tree ovl = build_overload (DECL_TI_TEMPLATE (con), NULL_TREE);
-  tree check = build_concept_check (ovl, arg);
+  tree check = build_concept_check (ovl, arg, args);
   if (apply_to_all_p)
     {
       check = make_pack_expansion (check);
Index: partial-err.C
===================================================================
--- partial-err.C	(revision 0)
+++ partial-err.C	(revision 0)
@@ -0,0 +1,21 @@
+// { dg-options "-std=c++1y" }
+
+template<typename T>
+  concept bool Type() { return true; }
+
+template<typename T, typename U>
+  concept bool Same() { return __is_same_as(T, U); }
+
+template<Same<int> T> struct S1 { };
+template<typename T, Same<T> U> struct S2 { };
+
+void f(Same<int> q) { }
+void g(Type a, Same<decltype(a)> b) { }
+
+int main() {
+  S1<char> s1;      // { dg-error "deduction|invalid" }
+  S2<int, char> s2; // { dg-error "deduction|invalid" }
+
+  f('a');    // { dg-error "matching" }
+  g(0, 'a'); // { dg-error "matching" }
+}
Index: concepts.exp
===================================================================
--- concepts.exp	(revision 0)
+++ concepts.exp	(revision 0)
@@ -0,0 +1,35 @@
+#   Copyright (C) 2002-2013 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3.  If not see
+# <http://www.gnu.org/licenses/>.
+
+# GCC testsuite that uses the `dg.exp' driver.
+
+# Load support procs.
+load_lib g++-dg.exp
+
+# If a testcase doesn't have special options, use these.
+global DEFAULT_CXXFLAGS
+if ![info exists DEFAULT_CXXFLAGS] then {
+    set DEFAULT_CXXFLAGS " -pedantic-errors -Wno-long-long"
+}
+
+# Initialize `dg'.
+dg-init
+
+# Main loop.
+g++-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.C]] $DEFAULT_CXXFLAGS
+
+# All done.
+dg-finish
Index: partial.C
===================================================================
--- partial.C	(revision 0)
+++ partial.C	(revision 0)
@@ -0,0 +1,33 @@
+// { dg-options "-std=c++1y" }
+
+template<typename T>
+  concept bool Type() { return true; }
+
+template<typename T, typename U>
+  concept bool Same() { return __is_same_as(T, U); }
+
+template<typename T, typename U>
+  concept bool C1() { return true; }
+
+template<typename T, typename... Args>
+  concept bool C2() { return true; }
+
+template<Same<int> T> struct S1 { };
+template<typename T, Same<T> U> struct S2 { };
+
+void f(Same<int> q) { }
+void g(Type a, Same<decltype(a)> b) { }
+
+void h0(Same<int>* a) { }
+void h1(C1<int>* a) { }
+void h2(C2<char, short, int, long>* a) { }
+
+int main() {
+  S1<int> s1;
+  S2<int, int> s2;
+  f(0);
+  g(0, 1);
+  h0((int*)0);
+  h1((int*)0);
+  h2((int*)0);
+}

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