diff mbox

[c++-concepts] variable concepts

Message ID 53EB12C0.9050208@maniacsvault.net
State New
Headers show

Commit Message

Braden Obrzut Aug. 13, 2014, 7:24 a.m. UTC
This patch adds support for variable concepts.

There is a known issue that prevents concept variables from having 
requires expressions that have parameters.  This issue is not within the 
scope of this patch as it affects adhoc requires expressions as well.

2014-08-13  Braden Obrzut <admin@maniacsvault.net>

     * gcc/cp/constraint.cc (deduce_constrained_parameter): Deduce concept
     from variable concept template-id expressions.
     (normalize_var): New.
     (normalize_template_id): Identify variable concepts.
     (build_concept_check): Handle variable concepts.
     (finish_shorthand_requirement): Handle variable concepts.
     (diagnose_var): New.
     (diagnose_node): Identify variable concepts.
     * gcc/cp/decl.c (grokvardecl): Pass concept flag through to
     check_explicit_specialization.
     (grokdeclarator): Allow variable concepts and pass concept flag through
     grokvardecl.
     * gcc/cp/parser.c (cp_is_constrained_parameter): Accept variable
     concepts.
     (cp_parser_nonclass_name): Accept variable concepts.
     (get_concept_from_constraint): Handle variable concepts.
     * gcc/cp/pt.c (tsubst_copy_and_build): Lookup variable templates.
     (value_dependent_expression_p): Check requires expressions for value
     dependence.
     * gcc/cp/semantics.c (finish_call_expr): Don't instantiate variable
     templates if processing a template declaration.
     * gcc/testsuite/g++.dg/concepts/decl-diagnose.C: Change expected error
     as variable concepts are now handled.
     * gcc/testsuite/g++.dg/concepts/var-concepts1.C: New test.
     * gcc/testsuite/g++.dg/concepts/var-concepts2.C: New test.

Comments

Andrew Sutton Aug. 13, 2014, 12:25 p.m. UTC | #1
Committed.

Andrew Sutton


On Wed, Aug 13, 2014 at 3:24 AM, Braden Obrzut <admin@maniacsvault.net> wrote:
> This patch adds support for variable concepts.
>
> There is a known issue that prevents concept variables from having requires
> expressions that have parameters.  This issue is not within the scope of
> this patch as it affects adhoc requires expressions as well.
>
> 2014-08-13  Braden Obrzut <admin@maniacsvault.net>
>
>     * gcc/cp/constraint.cc (deduce_constrained_parameter): Deduce concept
>     from variable concept template-id expressions.
>     (normalize_var): New.
>     (normalize_template_id): Identify variable concepts.
>     (build_concept_check): Handle variable concepts.
>     (finish_shorthand_requirement): Handle variable concepts.
>     (diagnose_var): New.
>     (diagnose_node): Identify variable concepts.
>     * gcc/cp/decl.c (grokvardecl): Pass concept flag through to
>     check_explicit_specialization.
>     (grokdeclarator): Allow variable concepts and pass concept flag through
>     grokvardecl.
>     * gcc/cp/parser.c (cp_is_constrained_parameter): Accept variable
>     concepts.
>     (cp_parser_nonclass_name): Accept variable concepts.
>     (get_concept_from_constraint): Handle variable concepts.
>     * gcc/cp/pt.c (tsubst_copy_and_build): Lookup variable templates.
>     (value_dependent_expression_p): Check requires expressions for value
>     dependence.
>     * gcc/cp/semantics.c (finish_call_expr): Don't instantiate variable
>     templates if processing a template declaration.
>     * gcc/testsuite/g++.dg/concepts/decl-diagnose.C: Change expected error
>     as variable concepts are now handled.
>     * gcc/testsuite/g++.dg/concepts/var-concepts1.C: New test.
>     * gcc/testsuite/g++.dg/concepts/var-concepts2.C: New test.
>
diff mbox

Patch

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 5cbd6e6..8a28023 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -198,29 +198,44 @@  resolve_constraint_check (tree call)
   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.
+// Given a call expression or template-id expression to a concept, EXPR,
+// 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)
+deduce_constrained_parameter (tree expr, tree& check, tree& proto)
 {
-  // Resolve the constraint check to deduce the declared parameter.
-  if (tree info = resolve_constraint_check (call))
+  if (TREE_CODE (expr) == TEMPLATE_ID_EXPR)
     {
-      // 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);
+      // Get the check and prototype parameter from the variable template.
+      tree decl = TREE_OPERAND (expr, 0);
+      tree parms = DECL_TEMPLATE_PARMS (decl);
+
+      check = DECL_TEMPLATE_RESULT (decl);
+      proto = TREE_VALUE (TREE_VEC_ELT (TREE_VALUE (parms), 0));
       return true;
     }
-  check = proto = NULL_TREE;
-  return false;
+  else if (TREE_CODE (expr) == CALL_EXPR)
+    {
+      // Resolve the constraint check to deduce the prototype parameter.
+      if (tree info = resolve_constraint_check (expr))
+        {
+          // 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;
+    }
+  else
+    gcc_unreachable ();
 }
 
 // -------------------------------------------------------------------------- //
@@ -246,6 +261,7 @@  tree normalize_requires (tree);
 tree normalize_expr_req (tree);
 tree normalize_type_req (tree);
 tree normalize_nested_req (tree);
+tree normalize_var (tree);
 tree normalize_template_id (tree);
 tree normalize_stmt_list (tree);
 
@@ -439,6 +455,36 @@  normalize_call (tree t)
   return result;
 }
 
+// Reduction rules for a variable template-id T.
+//
+// If T is a constraint, instantiate its initializer and recursively reduce its
+// expression.
+tree
+normalize_var (tree t)
+{
+  tree decl = DECL_TEMPLATE_RESULT (TREE_OPERAND (t, 0));
+  if (!DECL_DECLARED_CONCEPT_P (decl))
+    return t;
+
+  // Reduce the initializer of the variable into the constriants language.
+  tree body = normalize_constraints (DECL_INITIAL (decl));
+  if (!body)
+   {
+     error ("could not inline requirements from %qD", decl);
+     return error_mark_node;
+   }
+
+  // Instantiate the reduced results.
+  tree result = tsubst_constraint_expr (body, TREE_OPERAND (t, 1), false);
+  if (result == error_mark_node)
+    {
+      error ("could not instantiate requirements from %qD", decl);
+      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:
@@ -452,15 +498,20 @@  normalize_call (tree t)
 tree
 normalize_template_id (tree t)
 {
-  vec<tree, va_gc>* args = NULL;
-  tree c = finish_call_expr (t, &args, true, false, 0);
+  if (variable_template_p (TREE_OPERAND (t, 0)))
+    return normalize_var (t);
+  else
+    {
+      vec<tree, va_gc>* args = NULL;
+      tree c = finish_call_expr (t, &args, true, false, 0);
 
-  // FIXME: input_location is probably wrong, but there's not necessarly
-  // an expr location with the tree.
-  error_at (input_location, "invalid requirement");
-  inform (input_location, "did you mean %qE", c);
+      // FIXME: input_location is probably wrong, but there's not necessarly
+      // an expr location with the tree.
+      error_at (input_location, "invalid requirement");
+      inform (input_location, "did you mean %qE", c);
 
-  return error_mark_node;
+      return error_mark_node;
+    }
 }
 
 
@@ -954,8 +1005,6 @@  build_call_check (tree id)
 // a function (overload set or baselink reffering to an overload set),
 // then ths builds the call expression  TARGET<ARG, REST>(). If REST is 
 // NULL_TREE, then the resulting check is just TARGET<ARG>().
-//
-// TODO: Allow TARGET to be a variable concept.
 tree
 build_concept_check (tree target, tree arg, tree rest) 
 {
@@ -970,8 +1019,16 @@  build_concept_check (tree target, tree arg, tree 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 (target, targs);
-  return build_call_check (id);
+  if (variable_template_p (target))
+    {
+      tree id = lookup_template_variable (target, targs);
+      return id;
+    }
+  else
+    {
+      tree id = lookup_template_function (target, targs);
+      return build_call_check (id);
+    }
 }
 
 // Returns a TYPE_DECL that contains sufficient information to build
@@ -1027,8 +1084,16 @@  finish_shorthand_constraint (tree decl, tree constr)
   // Build the concept check. If it the constraint needs to be applied
   // 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, args);
+  tree check;
+  if (TREE_CODE (con) == VAR_DECL)
+    {
+      check = build_concept_check (DECL_TI_TEMPLATE (con), arg, args);
+    }
+  else
+    {
+      tree ovl = build_overload (DECL_TI_TEMPLATE (con), NULL_TREE);
+      check = build_concept_check (ovl, arg, args);
+    }
   if (apply_to_all_p)
     {
       check = make_pack_expansion (check);
@@ -1518,6 +1583,31 @@  diagnose_call (location_t loc, tree t, tree args)
     inform (loc, "  %qE evaluated to false", t);
 }
 
+// Diagnose constraint failures in a variable concept.
+void
+diagnose_var (location_t loc, tree t, tree args)
+{
+  // If the template-id isn't a variable template, it can't be a
+  // valid constraint.
+  if (!variable_template_p (TREE_OPERAND (t, 0)))
+    {
+      inform (loc, "  invalid constraint %qE", t);
+      return;
+    }
+
+  if (check_diagnostic_constraints (t, args))
+    return;
+
+  tree var = DECL_TEMPLATE_RESULT (TREE_OPERAND (t, 0));
+  tree body = DECL_INITIAL (var);
+  tree targs = TREE_OPERAND (t, 1);
+  tree subst = tsubst_constraint_expr (body, targs, true);
+
+  inform (loc, "  failure in constraint %q#D", DECL_TI_TEMPLATE (var));
+
+  diagnose_node (loc, subst, args);
+}
+
 // Diagnose specific constraint failures.
 void
 diagnose_requires (location_t loc, tree t, tree args)
@@ -1641,6 +1731,10 @@  diagnose_node (location_t loc, tree t, tree args)
       diagnose_noexcept (loc, t, args);
       break;
 
+    case TEMPLATE_ID_EXPR:
+      diagnose_var (loc, t, args);
+      break;
+
     default:
       diagnose_other (loc, t, args);
       break;
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 0147fed..b56915f 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -8132,7 +8132,7 @@  grokvardecl (tree type,
 	     tree orig_declarator,
 	     const cp_decl_specifier_seq *declspecs,
 	     int initialized,
-	     int constp,
+	     int flags,
 	     int template_count,
 	     tree scope)
 {
@@ -8141,6 +8141,9 @@  grokvardecl (tree type,
 
   gcc_assert (!name || identifier_p (name));
 
+  bool constp = flags&1;
+  bool conceptp = flags&2;
+
   /* Compute the scope in which to place the variable, but remember
      whether or not that scope was explicitly specified by the user.   */
   explicit_scope = scope;
@@ -8237,7 +8240,7 @@  grokvardecl (tree type,
   // Handle explicit specializations and instantiations of variable templates.
   if (orig_declarator)
     decl = check_explicit_specialization (orig_declarator, decl,
-					  template_count, 0);
+					  template_count, conceptp * 8);
 
   return decl != error_mark_node ? decl : NULL_TREE;
 }
@@ -11104,21 +11107,12 @@  grokdeclarator (const cp_declarator *declarator,
     else
       {
 	/* It's a variable.  */
-
-	// TODO: This needs to be revisited once variable
-	// templates are supported
-	if (concept_p)
-	  {
-	    error ("variable %qE declared %<concept%>",
-		   unqualified_id);
-	    return error_mark_node;
-	  }
 	
 	/* An uninitialized decl with `extern' is a reference.  */
 	decl = grokvardecl (type, dname, unqualified_id,
 			    declspecs,
 			    initialized,
-			    (type_quals & TYPE_QUAL_CONST) != 0,
+			    ((type_quals & TYPE_QUAL_CONST) != 0) | (2 * concept_p),
 			    template_count,
 			    ctype ? ctype : in_namespace);
 	if (decl == NULL_TREE)
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 9015e72..b9b38f6 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -13231,7 +13231,8 @@  cp_is_constrained_parameter (cp_parameter_declarator *parm)
           && TREE_CODE (decl) == TYPE_DECL 
           && DECL_INITIAL (decl)
           && DECL_SIZE_UNIT (decl)
-          && TREE_CODE (DECL_SIZE_UNIT (decl)) == FUNCTION_DECL);
+          && (TREE_CODE (DECL_SIZE_UNIT (decl)) == FUNCTION_DECL
+              || TREE_CODE (DECL_SIZE_UNIT (decl)) == VAR_DECL));
 }
 
 
@@ -15441,10 +15442,10 @@  cp_parser_nonclass_name (cp_parser* parser)
 
   // If we found an overload set, then it may refer to a concept-name.
   //
-  // TODO: The name could also refer to a variable template or an
-  // introduction (if followed by '{').
+  // TODO: The name could also refer to an introduction (if followed by '{').
   if (flag_concepts && 
-        (TREE_CODE (type_decl) == OVERLOAD || BASELINK_P (type_decl)))
+        (TREE_CODE (type_decl) == OVERLOAD || BASELINK_P (type_decl)
+         || variable_template_p (type_decl)))
   {
     // Determine whether the overload refers to a concept.
     if (tree decl = cp_maybe_concept_name (parser, type_decl))
@@ -33162,11 +33163,18 @@  tree_type_is_auto_or_concept (const_tree t)
 static tree
 get_concept_from_constraint (tree t)
 {
-  gcc_assert (TREE_CODE (t) == CALL_EXPR);
-  tree fn = CALL_EXPR_FN (t);
-  tree ovl = TREE_OPERAND (fn, 0);
-  tree tmpl = OVL_FUNCTION (ovl);
-  return DECL_TEMPLATE_RESULT (tmpl);
+  gcc_assert (TREE_CODE (t) == CALL_EXPR || TREE_CODE (t) == TEMPLATE_ID_EXPR);
+
+  // Variable concepts will be a TEMPLATE_ID_EXPR.
+  if (TREE_CODE (t) == TEMPLATE_ID_EXPR)
+    return DECL_TEMPLATE_RESULT (TREE_OPERAND (t, 0));
+  else
+    {
+      tree fn = CALL_EXPR_FN (t);
+      tree ovl = TREE_OPERAND (fn, 0);
+      tree tmpl = OVL_FUNCTION (ovl);
+      return DECL_TEMPLATE_RESULT (tmpl);
+    }
 }
 
 /* Add an implicit template type parameter to the CURRENT_TEMPLATE_PARMS
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 444e4ef..bc9ba86 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -14778,7 +14778,13 @@  tsubst_copy_and_build (tree t,
 	  }
 	else
 	  object = NULL_TREE;
-	templ = lookup_template_function (templ, targs);
+	if (variable_template_p (templ))
+	  {
+	    templ = lookup_template_variable (templ, targs);
+	    RETURN (finish_template_variable (templ));
+	  }
+	else
+	  templ = lookup_template_function (templ, targs);
 
 	if (object)
 	  RETURN (build3 (COMPONENT_REF, TREE_TYPE (templ),
@@ -21360,6 +21366,34 @@  value_dependent_expression_p (tree expression)
 		|| has_value_dependent_address (op));
       }
 
+    case REQUIRES_EXPR:
+      {
+        // Check if any parts of a requires expression are dependent.
+        tree req = TREE_OPERAND (expression, 1);
+        while (req != NULL_TREE)
+          {
+            tree op = TREE_OPERAND (TREE_VALUE (req), 0);
+
+            if (TREE_CODE (op) == TREE_LIST)
+              {
+                if (any_value_dependent_elements_p (op))
+                  return true;
+              }
+            else
+              {
+                if (value_dependent_expression_p (op))
+                  return true;
+              }
+            req = TREE_CHAIN (req);
+          }
+        return false;
+      }
+
+    case TYPE_REQ:
+    case VALIDEXPR_EXPR:
+    case VALIDTYPE_EXPR:
+      return type_dependent_expression_p (TREE_OPERAND (expression, 0));
+
     case CALL_EXPR:
       {
 	tree fn = get_callee_fndecl (expression);
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 5a81b43..7f8ebe5 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -2423,6 +2423,9 @@  finish_call_expr (tree fn, vec<tree, va_gc> **args, bool disallow_virtual,
 tree
 finish_template_variable (tree var)
 {
+  if (processing_template_decl)
+    return var;
+
   return instantiate_template (TREE_OPERAND (var, 0), TREE_OPERAND (var, 1),
                                tf_error);
 }
diff --git a/gcc/testsuite/g++.dg/concepts/decl-diagnose.C b/gcc/testsuite/g++.dg/concepts/decl-diagnose.C
index 8780c8b..8302f3f 100644
--- a/gcc/testsuite/g++.dg/concepts/decl-diagnose.C
+++ b/gcc/testsuite/g++.dg/concepts/decl-diagnose.C
@@ -18,4 +18,4 @@  struct X
   concept X(); // { dg-error "a constructor cannot be 'concept'" }
 };
 
-concept bool X2; // { dg-error "declared 'concept'" }
+concept bool X2; // { dg-error "uninitialized" }
diff --git a/gcc/testsuite/g++.dg/concepts/var-concepts1.C b/gcc/testsuite/g++.dg/concepts/var-concepts1.C
new file mode 100644
index 0000000..dd50364
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/var-concepts1.C
@@ -0,0 +1,21 @@ 
+// { dg-do run }
+// { dg-options "-std=c++1z" }
+
+template<typename T>
+  concept bool C1 = __is_class(T);
+
+void f1(C1, C1);
+
+struct S1 {};
+
+int main ()
+{
+  f1(S1(), S1());
+  return 0;
+}
+ 
+template<typename U>
+  requires C1<U>
+  void f1(U, U)
+  {
+  }
diff --git a/gcc/testsuite/g++.dg/concepts/var-concepts2.C b/gcc/testsuite/g++.dg/concepts/var-concepts2.C
new file mode 100644
index 0000000..36f52b5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/var-concepts2.C
@@ -0,0 +1,19 @@ 
+// { dg-options "-std=c++1z" }
+
+template<typename T>
+  concept bool C1 = __is_class(T);
+
+template<typename U>
+  requires C1<U>() // { dg-error "function" }
+  void f1(U, U)
+  {
+  }
+
+void f2(C1) {}
+
+int main ()
+{
+  f1(0, 0);
+  f2(1); // { dg-error "cannot call" }
+  return 0;
+}