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.sut...@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); +}