It looks like things are coming together pretty well. What's your feeling about readiness to merge into the trunk? Is the branch down to no regressions?

See you on Monday!

@@ -4146,21 +4146,21 @@ build_new_function_call (tree fn, vec<tree, va_gc> 
**args, bool koenig_p,
       if (TREE_CODE (fn) == TEMPLATE_ID_EXPR)
         {
           /* If overload resolution selects a specialization of a
+             function concept for non-dependent template arguments,
+             the expression is true if the constraints are satisfied
+             and false otherwise.

              NOTE: This is an extension of Concepts Lite TS that
              allows constraints to be used in expressions. */
+          if (flag_concepts && !processing_template_decl)
             {
               tree tmpl = DECL_TI_TEMPLATE (cand->fn);
+              tree targs = DECL_TI_ARGS (cand->fn);
               tree decl = DECL_TEMPLATE_RESULT (tmpl);
+              if (DECL_DECLARED_CONCEPT_P (decl)
+                  && !uses_template_parms (targs)) {
+                return evaluate_function_concept (decl, targs);

If processing_template_decl is false, uses_template_parms should always be false as well.

+function_concept_check_p (tree t)

+  tree fn = CALL_EXPR_FN (t);
+  if (TREE_CODE (fn) == TEMPLATE_ID_EXPR
+      && TREE_CODE (TREE_OPERAND (fn, 0)) == OVERLOAD)
+    {
+      tree f1 = OVL_FUNCTION (TREE_OPERAND (fn, 0));

I think you want get_first_fn here.

   if (TEMPLATE_PARM_CONSTRAINTS (current_template_parms))
-    TYPE_CANONICAL (type) = type;
+    SET_TYPE_STRUCTURAL_EQUALITY (type);

This seems like papering over an underlying issue. What was the testcase that motivated this change?

@@ -11854,7 +11854,7 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain)
-           if (!spec)
+           if (!spec && DECL_LANG_SPECIFIC (t))
-       if (!local_p)
+       if (!local_p && DECL_LANG_SPECIFIC (r))

What motivated these changes? From the testcase, it seems that you're getting here with the decl for "using TD = int", which shouldn't happen.

@@ -1159,7 +1159,6 @@ check_noexcept_r (tree *tp, int * /*walk_subtrees*/, void 
* /*data*/)
-      tree type = TREE_TYPE (TREE_TYPE (fn));
-      if (!TYPE_NOTHROW_P (type))
+      if (!TYPE_NOTHROW_P (TREE_TYPE (fn)))

The old code was incorrectly assuming that CALL_EXPR_FN is always a function pointer, but your new code seems to be incorrectly assuming that it's always a function or an expression taking the address of a function; I think this will break on a call to a function pointer variable.

@@ -3481,13 +3481,27 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, 
tree t,
     case REQUIRES_EXPR:
+      if (!processing_template_decl)
+        return evaluate_constraint_expression (t, NULL_TREE);
+      else
+        *non_constant_p = true;
+        return t;

We shouldn't get here with a dependent REQUIRES_EXPR (or any dependent expression), so we shouldn't ever hit the else clause.

@@ -18063,18 +18063,41 @@ cp_parser_declarator (cp_parser* parser,
+  /* Function declarations may be followed by a trailing
+     requires-clause. Declarators for function declartions
+     are function declarators wrapping an id-declarator.
+     If the inner declarator is anything else, it does not
+     declare a function. These may also be reference or
+     pointer declarators enclosing such a function declarator.
+     In the declaration :
+
+        int *f(args)
+
+     the declarator is *f(args).
+
+     Abstract declarators cannot have a requires-clauses
+     because they do not declare functions. Here:

         void f() -> int& requires false

+     The trailing return type contains an abstract declarator,
+     and the requires-clause applies to the function
+     declaration and not the abstract declarator.  */
+  if (flag_concepts && dcl_kind != CP_PARSER_DECLARATOR_ABSTRACT)
     {
+      /* We could have things like *f(args) or &f(args).
+         Look inside references and pointers.  */
+      cp_declarator* p = declarator;
+      if (p->kind == cdk_reference || p->kind == cdk_pointer)
+        p = p->declarator;
+
+      /* Pointers or references with no name, or functions
+         with no name cannot have constraints.  */
+      if (!p || !p->declarator)
+        return declarator;
+
+      /* Look for f(args) but not (*f)(args).  */
+      if (p && p->kind == cdk_function && p->declarator->kind == cdk_id)

I think you can use function_declarator_p here.

+static inline bool
+pending_expansion_p (tree t)
+{
+  return (TREE_CODE (t) == PARM_DECL && CONSTRAINT_VAR_P (t)
+          && PACK_EXPANSION_P (TREE_TYPE (t)));
+}

What's the difference between this and function_parameter_pack_p?

+  /* A sentinel class that ensures that deferred access checks
+     are popped before a function returns.  */
+  struct deferring_access_check_sentinel
+  {
+    deferring_access_check_sentinel ()
+    {
+      push_deferring_access_checks (dk_deferred);
+    }
+    ~ deferring_access_check_sentinel ()
+    {
+      pop_deferring_access_checks ();
+    }
+  }

Let's put this with the other RAII sentinels in cp-tree.h.

+                       Lifting of concept definitions

Could we have a bit more description of what "lifting" means here?

+/*---------------------------------------------------------------------------
+                        Constraint normalization
+---------------------------------------------------------------------------*/

You have two of these headers; I guess the first one should be "transformation" and could use more description. On the second, I would retain the old comment

-// Normalize a template requirement to a logical formula written in terms of
-// atomic propositions, returing the new expression.  If the expression cannot
-// be normalized, a NULL_TREE is returned.

+check_implicit_conversion_constraint (tree t, tree args,
+                                      tsubst_flags_t complain, tree in_decl)
+{
+  tree expr = ICONV_CONSTR_EXPR (t);
+
+  /* Don't tsubst as if we're processing a template. If we try
+     to we can end up generating template-like expressions
+     (e.g., modop-exprs) that aren't properly typed. */
+  int saved_template_decl = processing_template_decl;
+  processing_template_decl = 0;

Why are we checking constraints when processing_template_decl is true?

+   Note that this is the only place that we instantiate the
+   constraints. */
+bool
+check_constraints (tree ci, tree args)

Except for tsubst_constraint?

+  ++processing_template_decl;
+  tree constr = transform_expression (lift_function_definition (fn, args));
+  --processing_template_decl;

Why do you need to set processing_template_decl here and in other calls to transform_expression? I don't notice anything that would be helped, especially now that you're using separate tree codes for constraints, though there is this in check_logical_expr:

+  /* Resolve the logical operator. Note that template processing is
+     disabled so we get the actual call or target expression back.
+     not_processing_template_sentinel sentinel.

I guess that isn't needed anymore?

+   FIXME: This is defined in pt.c because it's garbage collection
+   code is not being generated for constraint.cc. */
+static GTY (()) hash_table<constr_hasher> *decl_constraints;

I think you need to add constraint.cc to the gtfiles variable in cp/config-lang.in. It also looks like the GTFILES_H rule in gcc/Makefile.in assumes that all sources have the .c extension, so that may need adjustment.

@@ -2454,6 +2454,8 @@ finish_call_expr (tree fn, vec<tree, va_gc> **args, bool d
              next = OVL_CHAIN (fn);
+              if (flag_concepts)
+                remove_constraints (OVL_FUNCTION (fn));

I don't think we want to remove constraints here; this code is just discarding the OVERLOAD node, not the FUNCTION_DECL.

Jason

Reply via email to