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"
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)
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);
}
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)
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;
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),
/* 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
/* 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);
}
{
/* 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;
}
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. */
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"
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"
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:
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}
--- /dev/null
+// { 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" }