public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [c++concepts] Reducing requirements
@ 2013-03-27 15:19 Andrew Sutton
  2013-03-29 16:42 ` Gabriel Dos Reis
  2013-03-29 16:45 ` Gabriel Dos Reis
  0 siblings, 2 replies; 3+ messages in thread
From: Andrew Sutton @ 2013-03-27 15:19 UTC (permalink / raw)
  To: gcc-patches, Gabriel Dos Reis, Jason Merrill

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

Implements reduction of requirements into the constraints language:
logical formulas comprised of atomic propositions. Calls to constraint
predicates are recursively inlined in the resulting expression. All
other calls are treated as atoms.

2013-03-01  Andrew Sutton  <andrew.n.sutton@gmail.com>

        * gcc/cp/Makefile.lang.in: Add constraints.o target.
        * gcc/cp/cp-tree.h (substitute_template_parameters): Declare.
        (instantiate_requirements): Declare.
        (conjoin_requirements): Declare.
        (disjoin_requirements): Declare.
        (reduce_requirements): Declare.
        * gcc/cp/pt.c (substitute_template_parameters): Define.
        (instantiate_requirements): Define.
        * gcc/cp/pt.c (finish_template_requirements): Call
        reduce_requirements to get constraints.
        * gcc/cp/parser.c (cp_parser_type_parameter): Restore saved
        constraints after parsing template template parameter decl.
        * gcc/cp/constraints.cc: New.


Andrew

[-- Attachment #2: reduce-req.patch --]
[-- Type: application/octet-stream, Size: 17193 bytes --]

Index: cp-tree.h
===================================================================
--- cp-tree.h	(revision 197118)
+++ cp-tree.h	(working copy)
@@ -5284,6 +5284,8 @@ extern tree begin_eh_spec_block			(void)
 extern void finish_eh_spec_block		(tree, tree);
 extern tree build_eh_type_type			(tree);
 extern tree cp_protect_cleanup_actions		(void);
+extern tree substitute_template_parameters      (tree, tree);
+extern tree instantiate_requirements            (tree, tree);
 
 /* in expr.c */
 extern tree cplus_expand_constant		(tree);
@@ -6073,6 +6075,12 @@ extern bool cxx_omp_privatize_by_referen
 extern void suggest_alternatives_for            (location_t, tree);
 extern tree strip_using_decl                    (tree);
 
+/* in constraint.c */
+extern tree conjoin_requirements                (tree, tree);
+extern tree disjoin_requirements                (tree, tree);
+extern tree reduce_requirements                 (tree);
+
+
 /* -- end of C++ */
 
 #endif /* ! GCC_CP_TREE_H */
Index: Make-lang.in
===================================================================
--- Make-lang.in	(revision 197118)
+++ Make-lang.in	(working copy)
@@ -80,7 +80,7 @@ CXX_AND_OBJCXX_OBJS = cp/call.o cp/decl.
  cp/typeck.o cp/cvt.o cp/except.o cp/friend.o cp/init.o cp/method.o \
  cp/search.o cp/semantics.o cp/tree.o cp/repo.o cp/dump.o cp/optimize.o \
  cp/mangle.o cp/cp-objcp-common.o cp/name-lookup.o cp/cxx-pretty-print.o \
- cp/cp-gimplify.o $(CXX_C_OBJS)
+ cp/constraint.o cp/cp-gimplify.o $(CXX_C_OBJS)
 
 # Language-specific object files for C++.
 CXX_OBJS = cp/cp-lang.o c-family/stub-objc.o $(CXX_AND_OBJCXX_OBJS)
@@ -343,5 +343,11 @@ cp/name-lookup.o: cp/name-lookup.c $(CON
 	$(TM_H) $(CXX_TREE_H) $(TIMEVAR_H) gt-cp-name-lookup.h $(PARAMS_H) \
 	$(DIAGNOSTIC_CORE_H) $(FLAGS_H) debug.h pointer-set.h
 
+cp/constraint.o: cp/constraint.cc $(CXX_TREE_H) $(TM_H) $(FLAGS_H) toplev.h \
+  $(DIAGNOSTIC_CORE_H) intl.h $(TARGET_H) langhooks.h $(TIMEVAR_H) \
+  c-family/c-objc.h
+
 cp/cxx-pretty-print.o: cp/cxx-pretty-print.c $(CXX_PRETTY_PRINT_H) \
   $(CONFIG_H) $(SYSTEM_H) $(TM_H) coretypes.h $(CXX_TREE_H) tree-pretty-print.h
+
+
Index: pt.c
===================================================================
--- pt.c	(revision 197118)
+++ pt.c	(working copy)
@@ -6549,6 +6549,7 @@ any_pack_expanson_args_p (tree args)
   return false;
 }
 
+
 /* Convert all template arguments to their appropriate types, and
    return a vector containing the innermost resulting template
    arguments.  If any error occurs, return error_mark_node. Error and
@@ -20867,4 +20868,24 @@ print_template_statistics (void)
 	   htab_collisions (type_specializations));
 }
 
+
+// Try to substitute ARGS into PARMS, returning the actual list of
+// arguments that have been substituted. If ARGS cannot be substituted,
+// return error_mark_node.
+tree
+substitute_template_parameters (tree parms, tree args)
+{
+  return coerce_template_parms (parms, args, NULL_TREE, tf_none, true, true);
+}
+
+// Substitute the template arguments ARGS into the requirement
+// expression REQS. Errors resulting from substitution are not
+// diagnosed.
+tree
+instantiate_requirements (tree reqs, tree args)
+{
+  return tsubst_expr (reqs, args, tf_none, NULL_TREE, true);
+}
+
+
 #include "gt-cp-pt.h"
Index: semantics.c
===================================================================
--- semantics.c	(revision 197118)
+++ semantics.c	(working copy)
@@ -9776,14 +9776,24 @@ is_lambda_ignored_entity (tree val)
   return false;
 }
 
-// Decompose the template requirements, given by EXPRESSION, into a set of
-// assumptions for the local scope.
+
+// Semantics for constraints.
+
+
+// Finish the template requirement, EXPRESSION, by first reducing it into
+// a logical formula written in terms of atomic propositions, and then
+// decomposing it into sets of those propositions.
 tree
 finish_template_requirements (tree expression)
 {
   if (expression == error_mark_node)
     return NULL_TREE;
 
+  tree reduced = reduce_requirements (expression);
+  
+  // TODO: Perform an initial left/right decomposition on the
+  // reduced requirements.
+
   sorry ("no template requirements yet");
   return NULL_TREE;
 }
Index: constraint.cc
===================================================================
--- constraint.cc	(revision 0)
+++ constraint.cc	(revision 0)
@@ -0,0 +1,450 @@
+/* Process declarations and variables for C++ compiler.
+   Copyright (C) 1988-2013 Free Software Foundation, Inc.
+   Contributed by Michael Tiemann (tiemann@cygnus.com)
+
+This file is part of GCC.
+
+GCC 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, or (at your option)
+any later version.
+
+GCC 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/>.  */
+
+// Components for process constraints and evaluating constraints.
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "tree.h"
+#include "cp-tree.h"
+#include "c-family/c-common.h"
+#include "c-family/c-objc.h"
+#include "tree-inline.h"
+#include "intl.h"
+#include "toplev.h"
+#include "flags.h"
+#include "timevar.h"
+#include "diagnostic.h"
+#include "cgraph.h"
+#include "tree-iterator.h"
+#include "vec.h"
+#include "target.h"
+#include "gimple.h"
+#include "bitmap.h"
+
+
+// -------------------------------------------------------------------------- //
+// Requirement Construction
+//
+// Facilities for building and manipulating template requirements. 
+//
+// TODO: Simply assinging boolean_type_node to the result type of the expression
+// seems right thing for constraints, but in the long-term we might want to be
+// more flexible (i.e., allow some form of overload resolution?).
+
+
+// Returns the conjunction of two requirements A and B, where A and B are
+// reduced terms in the constraints languaage. Returns NULL_TREE if either A or
+// B are NULL_TREE.
+tree
+conjoin_requirements (tree a, tree b)
+{
+  if (a && b)
+    return build_min (TRUTH_ANDIF_EXPR, boolean_type_node, a, b);
+  else
+    return NULL_TREE;
+}
+
+// Returns the disjunction of two requirements A and B, where A and B are
+// reduced terms in the constraints languaage. Returns NULL_TREE if either A or
+// B are NULL_TREE.
+tree
+disjoin_requirements (tree a, tree b)
+{
+  if (a && b)
+    return build_min (TRUTH_ORIF_EXPR, boolean_type_node, a, b);
+  else
+    return NULL_TREE;
+}
+
+
+// -------------------------------------------------------------------------- //
+// Constraint Resolution
+//
+// This facility is used to resolve constraint checks from requirement
+// expressions. A constraint check is a call to a constraint predicate:
+// a constexpr, nullary function teplate whose result can be converted
+// to bool.
+//
+// The result of resolution is a pair (a list node) whose value is the
+// matched declaration, and whose purpose contains the coerced template
+// arguments that can be substituted into the call.
+
+namespace {
+
+// Returns true if the function decl F is a constraint predicate.
+// It must be a constexpr, nullary function with a boolean result
+// type.
+static bool
+is_constraint (tree f)
+{
+  gcc_assert (TREE_CODE (f) == FUNCTION_DECL);
+
+  // A constraint is nullary.
+  if (DECL_ARGUMENTS (f))
+    return false;
+
+  // A constraint is declared constexpr
+  if (!DECL_DECLARED_CONSTEXPR_P (f))
+    return false;
+
+  // Whose result must be convertible to bool.
+  if (!can_convert (TREE_TYPE (TREE_TYPE (f)), boolean_type_node, tf_none))
+    return false;
+
+  // A constraint can only be checked if it is defined.
+  if (!DECL_SAVED_TREE (f))
+    return false;
+
+  return true;
+}
+
+// Given an OVL set of constraint candidates, try to find a unique definition
+// satisfying the requirements of a constraint.
+//
+// This function is not called for abitrary call expressions. In particul,
+// the call expression must be written with explicit template arguments
+// and no function arguments. For example:
+//
+//      f<T, U>()
+//
+// The overload set will contain only template declarations.
+//
+// If a single definition is found, this returns a list node whose VALUE
+// is the constraint function (not the template), and its PURPOSE is
+// the complete set of arguments substituted into the parameter list.
+static tree
+resolve_constraint_check (tree ovl, tree args)
+{
+  tree cands = NULL_TREE;
+  for (tree p = ovl; p != NULL_TREE; p = OVL_NEXT (p))
+    {
+      tree tmpl = OVL_FUNCTION (p);
+      tree parms = TREE_VALUE (DECL_TEMPLATE_PARMS (tmpl));
+
+      // Remember the candidate if we can deduce a substitution.
+      if (tree subst = substitute_template_parameters (parms, args))
+        if (subst != error_mark_node)
+          cands = tree_cons (subst, DECL_TEMPLATE_RESULT (tmpl), cands);
+    }
+
+  // If there are multiple candidates, then we have not found
+  // a unique definition.
+  if (TREE_CHAIN (cands))
+    return NULL_TREE;
+
+  if (!is_constraint (TREE_VALUE (cands)))
+    return NULL_TREE;
+
+  return cands;
+}
+
+// If the call express T is a check expression, return a singleton tree
+// list whose VALUE is the checked constraint predicate, and whose
+// PURPOSE contains substitution arguments for the constraint. If the 
+// call does not denote a check, return NULL_TREE.
+//
+// Note that a call expression is a check expression if it refers to a
+// unique, nullary function template via lightweight overload resolution.
+tree
+maybe_constraint_check (tree call)
+{
+  gcc_assert (TREE_CODE (call) == CALL_EXPR);
+
+  // A constraint check must be a call to a function template with 
+  // arguments given explicitly.
+  tree f = CALL_EXPR_FN (call);
+  if (TREE_CODE (f) != TEMPLATE_ID_EXPR)
+    return NULL_TREE;
+
+  // Also, make sure that there are no arguments to the call expression.
+  // If there are, then we are guaranteed that this is a regular call.
+  if (call_expr_nargs (call))
+    return NULL_TREE;
+
+  // Determine which constraint is actually referred to by the
+  // call expression.
+  tree tmpl = TREE_OPERAND (f, 0);
+  tree args = TREE_OPERAND (f, 1);
+  return resolve_constraint_check (tmpl, args);
+}
+  
+} // end namespace
+
+
+// -------------------------------------------------------------------------- //
+// Requirement Reduction
+//
+// Reduces a template requirement to a logical formula written in terms of
+// atomic propositions, returing the new expression.  If the expression cannot
+// be reduced, a NULL_TREE is returned, indicating failure to reduce the
+// original requirment. 
+
+namespace {
+
+// Helper functions
+static tree reduce_node (tree);
+static tree reduce_expr (tree);
+static tree reduce_stmt (tree);
+static tree reduce_decl (tree);
+static tree reduce_misc (tree);
+
+static tree reduce_logical     (tree);
+static tree reduce_call        (tree);
+static tree reduce_template_id (tree);
+static tree reduce_stmt_list   (tree);
+
+// Reduce the requirement T into a logical formula written in terms of
+// atomic propositions.
+tree 
+reduce_node (tree t)
+{
+  switch (TREE_CODE_CLASS (TREE_CODE (t))) 
+    {
+    case tcc_unary:
+    case tcc_binary:
+    case tcc_expression:
+    case tcc_vl_exp:
+      return reduce_expr (t);
+    
+    case tcc_statement:   
+      return reduce_stmt (t);
+    
+    case tcc_declaration: 
+      return reduce_decl (t);
+    
+    case tcc_exceptional: 
+      return reduce_misc (t);
+    
+    // These kinds of expressions are atomic.
+    case tcc_constant:
+    case tcc_reference:
+    case tcc_comparison:
+      return t;
+
+    default:
+      gcc_unreachable ();
+    }
+  return NULL_TREE;
+}
+
+// Reduction rules for the expression node T.
+tree
+reduce_expr (tree t)
+{
+  switch (TREE_CODE (t))
+    {
+    case TRUTH_ANDIF_EXPR:
+    case TRUTH_ORIF_EXPR:  
+      return reduce_logical (t);
+
+    case CALL_EXPR:        
+      return reduce_call (t);
+
+    case TEMPLATE_ID_EXPR: 
+      return reduce_template_id (t);
+
+    case CAST_EXPR:        
+      return reduce_node (TREE_VALUE (TREE_OPERAND (t, 0)));
+    
+    case BIND_EXPR:        
+      return reduce_node (BIND_EXPR_BODY (t));
+
+    // Do not recurse.
+    case TAG_DEFN:         
+      return NULL_TREE;
+
+    // Everything else is atomic.
+    default:
+      return t;
+    }
+}
+
+
+// Reduction rules for the statement T.
+tree
+reduce_stmt (tree t)
+{
+  switch (TREE_CODE (t))
+    {
+    // Reduce the returned expression.
+    case RETURN_EXPR:
+      return reduce_node (TREE_OPERAND (t, 0));
+
+    // These statements do not introduce propositions
+    // in the constraints language. Do not recurse.
+    case DECL_EXPR:
+    case USING_STMT:
+      return NULL_TREE;
+    
+    default:
+      gcc_unreachable ();
+    }
+  return NULL_TREE;
+}
+
+// Reduction rules for the declaration T.
+tree
+reduce_decl (tree t)
+{
+  switch (TREE_CODE (t))
+    {
+    // References to var decls are atomic.
+    case VAR_DECL:
+      return t;
+    
+    default:
+      gcc_unreachable ();
+    }
+  return NULL_TREE;
+}
+
+// Reduction rules for the node T.
+tree
+reduce_misc (tree t)
+{
+  switch (TREE_CODE (t))
+    {
+    // Errors and traits are atomic.
+    case ERROR_MARK:
+    case TRAIT_EXPR:
+      return t;
+
+    case STATEMENT_LIST:
+      return reduce_stmt_list (t);
+    
+    default:
+      gcc_unreachable ();
+    }
+  return NULL_TREE;
+}
+
+// Reduction rules for the binary logical expression T (&& and ||).
+//
+// Generate a new expression from the reduced operands. If either operand
+// cannot be reduced, then the resulting expression is null.
+tree
+reduce_logical (tree t)
+{
+  tree l = reduce_expr (TREE_OPERAND (t, 0));
+  tree r = reduce_expr (TREE_OPERAND (t, 1));
+  if (l && r)
+    {
+      t = copy_node (t);
+      TREE_OPERAND (t, 0) = l;
+      TREE_OPERAND (t, 1) = r;
+      return t;
+    }
+  else
+    return NULL_TREE;
+}
+
+// Reduction rules for the call expression T.
+//
+// If T is a call to a constraint instantiate it's definition and
+// recursively reduce its returned expression.
+tree
+reduce_call (tree t)
+{
+  // Is the function call actually a constraint check?
+  tree check = maybe_constraint_check (t);
+  if (!check)
+    return t;
+
+  tree fn = TREE_VALUE (check);
+  tree args = TREE_PURPOSE (check);
+
+  // Reduce the body of the function into the constriants language.
+  tree body = reduce_requirements (DECL_SAVED_TREE (fn));
+  if (!body)
+    {
+      error ("could not inline requirements from %qD", fn);
+      return error_mark_node;
+    }
+
+  // Instantiate the reduced results using the deduced args.
+  tree result = instantiate_requirements (body, args);
+  if (result == error_mark_node)
+    {
+      error ("could not instantiate requirements from %qD", fn);
+      return error_mark_node;
+    }
+  return result;
+}
+
+// Reduction rules for the template-id T.
+//
+// It turns out that we often get requirements being written like this:
+//
+//    template<typename T>
+//      requires Foo<T>
+//    void f()
+//
+// Where Foo<T> should actually be written as Foo<T>(). Generate an
+// error and suggest the improved writing.
+tree
+reduce_template_id (tree t)
+{
+  vec<tree, va_gc>* args = NULL;
+  tree c = finish_call_expr (t, &args, true, false, 0);
+  error ("invalid requirement");
+  inform (input_location, "did you mean %qE", c);
+  return NULL_TREE;
+}
+
+// Reduction rules for the statement list STMTS.
+//
+// Recursively reduce each statement in the list, concatenating each
+// reduced result into a conjunction of requirements. 
+//
+// A constexpr function may include statements other than a return
+// statement. The primary purpose of these rules is to filter those
+// non-return statements from the constraints language.
+tree
+reduce_stmt_list (tree stmts)
+{
+  tree lhs = NULL_TREE;
+  tree_stmt_iterator i = tsi_start (stmts);
+  while (!tsi_end_p (i))
+    {
+      if (tree rhs = reduce_node (tsi_stmt (i)))
+        {
+          if (!lhs)
+            lhs = rhs;
+          else
+            lhs = conjoin_requirements (lhs, rhs);
+        }
+      tsi_next (&i);
+    }
+  return lhs;
+}
+
+} // end namespace
+
+
+// Reduce the requirement T into a logical formula written in terms of
+// atomic propositions.
+tree
+reduce_requirements (tree reqs)
+{
+  return debug_tree (t);
+}
Index: parser.c
===================================================================
--- parser.c	(revision 197118)
+++ parser.c	(working copy)
@@ -12571,6 +12571,7 @@ cp_parser_type_parameter (cp_parser* par
 	parameter = finish_template_template_parm (class_type_node,
 						   identifier);
 
+        // Restore the saved constraints.
         current_template_reqs = saved_template_reqs;
 
 	/* If the next token is an `=', then there is a

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

* Re: [c++concepts] Reducing requirements
  2013-03-27 15:19 [c++concepts] Reducing requirements Andrew Sutton
@ 2013-03-29 16:42 ` Gabriel Dos Reis
  2013-03-29 16:45 ` Gabriel Dos Reis
  1 sibling, 0 replies; 3+ messages in thread
From: Gabriel Dos Reis @ 2013-03-29 16:42 UTC (permalink / raw)
  To: Andrew Sutton; +Cc: gcc-patches, Jason Merrill

Andrew Sutton <andrew.n.sutton@gmail.com> writes:

| Implements reduction of requirements into the constraints language:
| logical formulas comprised of atomic propositions. Calls to constraint
| predicates are recursively inlined in the resulting expression. All
| other calls are treated as atoms.
| 
| 2013-03-01  Andrew Sutton  <andrew.n.sutton@gmail.com>
| 
|         * gcc/cp/Makefile.lang.in: Add constraints.o target.

Wrong filename.  It should be gcc/cp/Make-lang.in.

[...]

Index: constraint.cc
===================================================================
--- constraint.cc	(revision 0)
+++ constraint.cc	(revision 0)
@@ -0,0 +1,450 @@
+/* Process declarations and variables for C++ compiler.
+   Copyright (C) 1988-2013 Free Software Foundation, Inc.
+   Contributed by Michael Tiemann (tiemann@cygnus.com)

This file is newly introduced, so the copyright date should be 2013.
It is being contributed by Andrew Sutton if I am not mistaken :-)

Patch OK to commit with these issues fixed.


-- Gaby

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

* Re: [c++concepts] Reducing requirements
  2013-03-27 15:19 [c++concepts] Reducing requirements Andrew Sutton
  2013-03-29 16:42 ` Gabriel Dos Reis
@ 2013-03-29 16:45 ` Gabriel Dos Reis
  1 sibling, 0 replies; 3+ messages in thread
From: Gabriel Dos Reis @ 2013-03-29 16:45 UTC (permalink / raw)
  To: Andrew Sutton; +Cc: gcc-patches, Jason Merrill

Andrew Sutton <andrew.n.sutton@gmail.com> writes:

| Implements reduction of requirements into the constraints language:
| logical formulas comprised of atomic propositions. Calls to constraint
| predicates are recursively inlined in the resulting expression. All
| other calls are treated as atoms.
| 
| 2013-03-01  Andrew Sutton  <andrew.n.sutton@gmail.com>

Forgot to add: the ChangeLog goes into ChangeLog.concepts.

-- Gaby

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

end of thread, other threads:[~2013-03-29 16:45 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-03-27 15:19 [c++concepts] Reducing requirements Andrew Sutton
2013-03-29 16:42 ` Gabriel Dos Reis
2013-03-29 16:45 ` Gabriel Dos Reis

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