diff options
-rw-r--r-- | gcc/cp/constraint.cc | 3 | ||||
-rw-r--r-- | gcc/cp/cp-trait.def | 1 | ||||
-rw-r--r-- | gcc/cp/cp-tree.h | 1 | ||||
-rw-r--r-- | gcc/cp/cxx-pretty-print.cc | 5 | ||||
-rw-r--r-- | gcc/cp/parser.cc | 20 | ||||
-rw-r--r-- | gcc/cp/pt.cc | 69 | ||||
-rw-r--r-- | gcc/cp/semantics.cc | 11 | ||||
-rw-r--r-- | gcc/cp/tree.cc | 2 | ||||
-rw-r--r-- | gcc/doc/extend.texi | 4 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/ext/is_deducible1.C | 31 |
10 files changed, 131 insertions, 16 deletions
diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc index 9374327..a28c851 100644 --- a/gcc/cp/constraint.cc +++ b/gcc/cp/constraint.cc @@ -3797,6 +3797,9 @@ diagnose_trait_expr (tree expr, tree args) inform (loc, " %qT is not a reference that binds to a temporary " "object of type %qT (copy-initialization)", t1, t2); break; + case CPTK_IS_DEDUCIBLE: + inform (loc, " %qD is not deducible from %qT", t1, t2); + break; #define DEFTRAIT_TYPE(CODE, NAME, ARITY) \ case CPTK_##CODE: #include "cp-trait.def" diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def index 823899a..e43fb46 100644 --- a/gcc/cp/cp-trait.def +++ b/gcc/cp/cp-trait.def @@ -84,6 +84,7 @@ DEFTRAIT_EXPR (IS_TRIVIALLY_COPYABLE, "__is_trivially_copyable", 1) DEFTRAIT_EXPR (IS_UNION, "__is_union", 1) DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY, "__reference_constructs_from_temporary", 2) DEFTRAIT_EXPR (REF_CONVERTS_FROM_TEMPORARY, "__reference_converts_from_temporary", 2) +DEFTRAIT_EXPR (IS_DEDUCIBLE, "__is_deducible", 2) DEFTRAIT_TYPE (REMOVE_CV, "__remove_cv", 1) DEFTRAIT_TYPE (REMOVE_REFERENCE, "__remove_reference", 1) diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index fb21c06..dfc1c84 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -7361,6 +7361,7 @@ extern tree fn_type_unification (tree, tree, tree, bool, bool); extern void mark_decl_instantiated (tree, int); extern int more_specialized_fn (tree, tree, int); +extern bool type_targs_deducible_from (tree, tree); extern void do_decl_instantiation (tree, tree); extern void do_type_instantiation (tree, tree, tsubst_flags_t); extern bool always_instantiate_p (tree); diff --git a/gcc/cp/cxx-pretty-print.cc b/gcc/cp/cxx-pretty-print.cc index bea52a6..7f4556d 100644 --- a/gcc/cp/cxx-pretty-print.cc +++ b/gcc/cp/cxx-pretty-print.cc @@ -2626,7 +2626,10 @@ pp_cxx_trait (cxx_pretty_printer *pp, tree t) } pp_cxx_left_paren (pp); - pp->type_id (type1); + if (TYPE_P (type1)) + pp->type_id (type1); + else + pp->expression (type1); if (type2) { if (TREE_CODE (type2) != TREE_LIST) diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index b00a6cd..5330419 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -10960,10 +10960,22 @@ cp_parser_trait (cp_parser* parser, enum rid keyword) matching_parens parens; parens.require_open (parser); - { - type_id_in_expr_sentinel s (parser); - type1 = cp_parser_type_id (parser); - } + if (kind == CPTK_IS_DEDUCIBLE) + { + const cp_token* token = cp_lexer_peek_token (parser->lexer); + type1 = cp_parser_id_expression (parser, + /*template_keyword_p=*/false, + /*check_dependency_p=*/true, + nullptr, + /*declarator_p=*/false, + /*optional_p=*/false); + type1 = cp_parser_lookup_name_simple (parser, type1, token->location); + } + else + { + type_id_in_expr_sentinel s (parser); + type1 = cp_parser_type_id (parser); + } if (type1 == error_mark_node) return error_mark_node; diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index aafc99d..e87cda2 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -21587,8 +21587,8 @@ tsubst_copy_and_build (tree t, case TRAIT_EXPR: { - tree type1 = tsubst (TRAIT_EXPR_TYPE1 (t), args, - complain, in_decl); + tree type1 = tsubst_copy (TRAIT_EXPR_TYPE1 (t), args, + complain, in_decl); tree type2 = tsubst (TRAIT_EXPR_TYPE2 (t), args, complain, in_decl); RETURN (finish_trait_expr (TRAIT_EXPR_LOCATION (t), @@ -29989,7 +29989,7 @@ alias_ctad_tweaks (tree tmpl, tree uguides) /* This implementation differs from the above in two significant ways: 1) We include all template parameters of A, not just some. - 2) The added constraint is same_type instead of deducible. + 2) [fixed] The added constraint is same_type instead of deducible. I believe that while it's probably possible to construct a testcase that behaves differently with this simplification, it should have the same @@ -30089,7 +30089,7 @@ alias_ctad_tweaks (tree tmpl, tree uguides) /* FIXME this should mean they don't compare as equivalent. */ || dependent_alias_template_spec_p (atype, nt_opaque)) { - tree same = finish_trait_expr (loc, CPTK_IS_SAME, atype, ret); + tree same = finish_trait_expr (loc, CPTK_IS_DEDUCIBLE, tmpl, ret); ci = append_constraint (ci, same); } @@ -30103,12 +30103,7 @@ alias_ctad_tweaks (tree tmpl, tree uguides) { /* For a non-template deduction guide, if the arguments of A aren't deducible from the return type, don't add the candidate. */ - tree targs = make_tree_vec (natparms); - int err = unify (atparms, targs, utype, ret, UNIFY_ALLOW_NONE, false); - for (unsigned i = 0; !err && i < natparms; ++i) - if (TREE_VEC_ELT (targs, i) == NULL_TREE) - err = true; - if (err) + if (!type_targs_deducible_from (tmpl, ret)) continue; } @@ -30118,6 +30113,60 @@ alias_ctad_tweaks (tree tmpl, tree uguides) return aguides; } +/* True iff template arguments for TMPL can be deduced from TYPE. + Used to implement CPTK_IS_DEDUCIBLE for alias CTAD according to + [over.match.class.deduct]. + + This check is specified in terms of partial specialization, so the behavior + should be parallel to that of get_partial_spec_bindings. */ + +bool +type_targs_deducible_from (tree tmpl, tree type) +{ + tree tparms = DECL_INNERMOST_TEMPLATE_PARMS (tmpl); + int len = TREE_VEC_LENGTH (tparms); + tree targs = make_tree_vec (len); + bool tried_array_deduction = (cxx_dialect < cxx17); + + /* If tmpl is a class template, this is trivial: it's deducible if TYPE is a + specialization of TMPL. */ + if (DECL_CLASS_TEMPLATE_P (tmpl)) + return (CLASS_TYPE_P (type) + && CLASSTYPE_TEMPLATE_INFO (type) + && CLASSTYPE_TI_TEMPLATE (type) == tmpl); + + /* Otherwise it's an alias template. */ + again: + if (unify (tparms, targs, TREE_TYPE (tmpl), type, + UNIFY_ALLOW_NONE, false)) + return false; + + /* We don't fail on an undeduced targ the second time through (like + get_partial_spec_bindings) because we're going to try defaults. */ + if (!tried_array_deduction) + for (int i = 0; i < len; ++i) + if (! TREE_VEC_ELT (targs, i)) + { + try_array_deduction (tparms, targs, TREE_TYPE (tmpl)); + tried_array_deduction = true; + if (TREE_VEC_ELT (targs, i)) + goto again; + } + + /* Maybe add in default template args. This seems like a flaw in the + specification in terms of partial specialization, since it says the + partial specialization has the the template parameter list of A, but a + partial specialization can't have default targs. */ + targs = coerce_template_parms (tparms, targs, tmpl, tf_none); + if (targs == error_mark_node) + return false; + + /* I believe we don't need the template_template_parm_bindings_ok_p call + because coerce_template_parms did coerce_template_template_parms. */ + + return constraints_satisfied_p (tmpl, targs); +} + /* Return artificial deduction guides built from the constructors of class template TMPL. */ diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc index db982d5..d67a9b2 100644 --- a/gcc/cp/semantics.cc +++ b/gcc/cp/semantics.cc @@ -12049,6 +12049,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2) case CPTK_REF_CONVERTS_FROM_TEMPORARY: return ref_xes_from_temporary (type1, type2, /*direct_init=*/false); + case CPTK_IS_DEDUCIBLE: + return type_targs_deducible_from (type1, type2); + #define DEFTRAIT_TYPE(CODE, NAME, ARITY) \ case CPTK_##CODE: #include "cp-trait.def" @@ -12206,6 +12209,14 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2) return error_mark_node; break; + case CPTK_IS_DEDUCIBLE: + if (!DECL_TYPE_TEMPLATE_P (type1)) + { + error ("%qD is not a class or alias template", type1); + return error_mark_node; + } + break; + #define DEFTRAIT_TYPE(CODE, NAME, ARITY) \ case CPTK_##CODE: #include "cp-trait.def" diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc index cd0dd96..de83d41 100644 --- a/gcc/cp/tree.cc +++ b/gcc/cp/tree.cc @@ -4235,7 +4235,7 @@ cp_tree_equal (tree t1, tree t2) case TRAIT_EXPR: if (TRAIT_EXPR_KIND (t1) != TRAIT_EXPR_KIND (t2)) return false; - return same_type_p (TRAIT_EXPR_TYPE1 (t1), TRAIT_EXPR_TYPE1 (t2)) + return cp_tree_equal (TRAIT_EXPR_TYPE1 (t1), TRAIT_EXPR_TYPE1 (t2)) && cp_tree_equal (TRAIT_EXPR_TYPE2 (t1), TRAIT_EXPR_TYPE2 (t2)); case NON_LVALUE_EXPR: diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index c112291..b64a857 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -25213,6 +25213,10 @@ type. A diagnostic is produced if this requirement is not met. If @code{type} is a cv-qualified class type, and not a union type ([basic.compound]) the trait is @code{true}, else it is @code{false}. +@item __is_deducible (template, type) +If template arguments for @code{template} can be deduced from +@code{type} or obtained from default template arguments. + @item __is_empty (type) If @code{__is_class (type)} is @code{false} then the trait is @code{false}. Otherwise @code{type} is considered empty if and only if: @code{type} diff --git a/gcc/testsuite/g++.dg/ext/is_deducible1.C b/gcc/testsuite/g++.dg/ext/is_deducible1.C new file mode 100644 index 0000000..30cbe08 --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/is_deducible1.C @@ -0,0 +1,31 @@ +// { dg-do compile { target c++20 } } + +template <class T> struct A { }; +template <class T> struct B { }; + +// Simple forms. +static_assert (__is_deducible (::A, A<int>)); +static_assert (__is_deducible (B, B<int>)); +static_assert (!__is_deducible (A, B<int>)); +static_assert (!__is_deducible (::B, A<int>)); + +// This is the interesting use case for alias CTAD. +template <class T> using AP = A<T*>; +static_assert (__is_deducible (AP, A<int*>)); +static_assert (!__is_deducible (AP, A<int>)); + +// Can't deduce a parameter not used on the RHS. +template <class T> using C = void; +static_assert (!__is_deducible (C, C<int>)); + +// But a default template argument counts. +template <class T = void> using D = void; +static_assert (__is_deducible (D, D<int>)); + +// P0127 array bound type deduction should work here. +template <class T, T N> using E = int[N]; +static_assert (__is_deducible (E, int[42])); + +// We don't try to support this. +template <class T> void f(T); +bool b = __is_deducible (f, void (int)); // { dg-error "class or alias" } |