Implement P0732R2, class types in non-type template parameters.
There is one significant piece of this that is not implemented yet: the reliance on defaulted operator<=>, which someone else has been working on. So, for the moment those lines are commented out of the testcases. One tricky bit was treating template parameters of classtype as const lvalues without making their decltype const; for this I used a VIEW_CONVERT_EXPR wrapper, which previously could only appear in templates as location wrappers. The user-defined literal parts of P0732R2 are in the next patch. gcc/cp/ * error.c (dump_simple_decl): Look through a template parm object. * mangle.c (write_template_arg): Likewise. (mangle_template_parm_object): New. * pt.c (template_parm_object_p, get_template_parm_object): New. (invalid_tparm_referent_p): Factor from convert_nontype_argument. (convert_nontype_argument, invalid_nontype_parm_type_p): Handle class-type template arguments. * tree.c (lvalue_kind): Likewise. gcc/c-family/ * c-cppbuiltin.c (c_cpp_builtins): Add __cpp_nontype_template_parameter_class. libiberty/ * cp-demangle.c (d_dump, d_make_comp, d_count_templates_scopes) (d_print_comp_inner): Handle DEMANGLE_COMPONENT_TPARM_OBJ. (d_special_name): Handle TA. (d_expresion_1): Fix demangling of brace-enclosed initializer list. include/ * demangle.h (enum demangle_component_type): Add DEMANGLE_COMPONENT_TPARM_OBJ. From-SVN: r265789
This commit is contained in:
parent
5dab8b11c4
commit
4be5c72cf3
27 changed files with 494 additions and 121 deletions
|
@ -1,3 +1,8 @@
|
|||
2018-10-19 Jason Merrill <jason@redhat.com>
|
||||
|
||||
* c-cppbuiltin.c (c_cpp_builtins): Add
|
||||
__cpp_nontype_template_parameter_class.
|
||||
|
||||
2018-10-31 Nathan Sidwell <nathan@acm.org>
|
||||
|
||||
* c-opts.c (c_finish_options): Force command line macro
|
||||
|
|
|
@ -979,6 +979,7 @@ c_cpp_builtins (cpp_reader *pfile)
|
|||
{
|
||||
/* Set feature test macros for C++2a. */
|
||||
cpp_define (pfile, "__cpp_explicit_bool=201806");
|
||||
cpp_define (pfile, "__cpp_nontype_template_parameter_class=201806");
|
||||
}
|
||||
if (flag_concepts)
|
||||
cpp_define (pfile, "__cpp_concepts=201507");
|
||||
|
|
|
@ -1,5 +1,15 @@
|
|||
2018-11-04 Jason Merrill <jason@redhat.com>
|
||||
|
||||
Implement P0732R2, class types in non-type template parameters.
|
||||
* error.c (dump_simple_decl): Look through a template parm object.
|
||||
* mangle.c (write_template_arg): Likewise.
|
||||
(mangle_template_parm_object): New.
|
||||
* pt.c (template_parm_object_p, get_template_parm_object): New.
|
||||
(invalid_tparm_referent_p): Factor from convert_nontype_argument.
|
||||
(convert_nontype_argument, invalid_nontype_parm_type_p): Handle
|
||||
class-type template arguments.
|
||||
* tree.c (lvalue_kind): Likewise.
|
||||
|
||||
* cvt.c (ocp_convert): Don't wrap a CONSTRUCTOR in a NOP_EXPR.
|
||||
* constexpr.c (initialized_type): Fix AGGR_INIT_EXPR handling.
|
||||
(cxx_eval_vec_init_1): Correct type of AGGR_INIT_EXPR.
|
||||
|
|
|
@ -6746,6 +6746,7 @@ extern bool variable_template_specialization_p (tree);
|
|||
extern bool alias_type_or_template_p (tree);
|
||||
extern bool alias_template_specialization_p (const_tree);
|
||||
extern bool dependent_alias_template_spec_p (const_tree);
|
||||
extern bool template_parm_object_p (const_tree);
|
||||
extern bool explicit_class_specialization_p (tree);
|
||||
extern bool push_tinst_level (tree);
|
||||
extern bool push_tinst_level_loc (tree, location_t);
|
||||
|
@ -7446,6 +7447,7 @@ extern tree mangle_tls_init_fn (tree);
|
|||
extern tree mangle_tls_wrapper_fn (tree);
|
||||
extern bool decl_tls_wrapper_p (tree);
|
||||
extern tree mangle_ref_init_variable (tree);
|
||||
extern tree mangle_template_parm_object (tree);
|
||||
extern char * get_mangled_vtable_map_var_name (tree);
|
||||
extern bool mangle_return_type_p (tree);
|
||||
extern tree mangle_decomp (tree, vec<tree> &);
|
||||
|
|
|
@ -1006,6 +1006,9 @@ dump_global_iord (cxx_pretty_printer *pp, tree t)
|
|||
static void
|
||||
dump_simple_decl (cxx_pretty_printer *pp, tree t, tree type, int flags)
|
||||
{
|
||||
if (template_parm_object_p (t))
|
||||
return dump_expr (pp, DECL_INITIAL (t), flags);
|
||||
|
||||
if (flags & TFF_DECL_SPECIFIERS)
|
||||
{
|
||||
if (VAR_P (t) && DECL_DECLARED_CONSTEXPR_P (t))
|
||||
|
|
|
@ -3437,6 +3437,10 @@ write_template_arg (tree node)
|
|||
}
|
||||
}
|
||||
|
||||
if (template_parm_object_p (node))
|
||||
/* We want to mangle the argument, not the var we stored it in. */
|
||||
node = DECL_INITIAL (node);
|
||||
|
||||
/* Strip a conversion added by convert_nontype_argument. */
|
||||
if (TREE_CODE (node) == IMPLICIT_CONV_EXPR)
|
||||
node = TREE_OPERAND (node, 0);
|
||||
|
@ -4257,6 +4261,19 @@ mangle_ref_init_variable (const tree variable)
|
|||
write_unsigned_number (temp_count++);
|
||||
return finish_mangling_get_identifier ();
|
||||
}
|
||||
|
||||
/* Return an identifier for the mangled name of a C++20 template parameter
|
||||
object for template argument EXPR. */
|
||||
|
||||
tree
|
||||
mangle_template_parm_object (tree expr)
|
||||
{
|
||||
start_mangling (expr);
|
||||
write_string ("_ZTAX");
|
||||
write_expression (expr);
|
||||
write_char ('E');
|
||||
return finish_mangling_get_identifier ();
|
||||
}
|
||||
|
||||
/* Given a CLASS_TYPE, such as a record for std::bad_exception this
|
||||
function generates a mangled name for the vtable map variable of
|
||||
|
|
352
gcc/cp/pt.c
352
gcc/cp/pt.c
|
@ -6584,6 +6584,169 @@ unify_template_argument_mismatch (bool explain_p, tree parm, tree arg)
|
|||
return unify_invalid (explain_p);
|
||||
}
|
||||
|
||||
/* True if T is a C++20 template parameter object to store the argument for a
|
||||
template parameter of class type. */
|
||||
|
||||
bool
|
||||
template_parm_object_p (const_tree t)
|
||||
{
|
||||
return (TREE_CODE (t) == VAR_DECL && DECL_ARTIFICIAL (t) && DECL_NAME (t)
|
||||
&& !strncmp (IDENTIFIER_POINTER (DECL_NAME (t)), "_ZTA", 4));
|
||||
}
|
||||
|
||||
/* Subroutine of convert_nontype_argument, to check whether EXPR, as an
|
||||
argument for TYPE, points to an unsuitable object. */
|
||||
|
||||
static bool
|
||||
invalid_tparm_referent_p (tree type, tree expr, tsubst_flags_t complain)
|
||||
{
|
||||
switch (TREE_CODE (expr))
|
||||
{
|
||||
CASE_CONVERT:
|
||||
return invalid_tparm_referent_p (type, TREE_OPERAND (expr, 0),
|
||||
complain);
|
||||
|
||||
case TARGET_EXPR:
|
||||
return invalid_tparm_referent_p (type, TARGET_EXPR_INITIAL (expr),
|
||||
complain);
|
||||
|
||||
case CONSTRUCTOR:
|
||||
{
|
||||
unsigned i; tree elt;
|
||||
FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (expr), i, elt)
|
||||
if (invalid_tparm_referent_p (TREE_TYPE (elt), elt, complain))
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case ADDR_EXPR:
|
||||
{
|
||||
tree decl = TREE_OPERAND (expr, 0);
|
||||
|
||||
if (!VAR_P (decl))
|
||||
{
|
||||
if (complain & tf_error)
|
||||
error ("%qE is not a valid template argument of type %qT "
|
||||
"because %qE is not a variable", expr, type, decl);
|
||||
return true;
|
||||
}
|
||||
else if (cxx_dialect < cxx11 && !DECL_EXTERNAL_LINKAGE_P (decl))
|
||||
{
|
||||
if (complain & tf_error)
|
||||
error ("%qE is not a valid template argument of type %qT "
|
||||
"in C++98 because %qD does not have external linkage",
|
||||
expr, type, decl);
|
||||
return true;
|
||||
}
|
||||
else if ((cxx_dialect >= cxx11 && cxx_dialect < cxx17)
|
||||
&& decl_linkage (decl) == lk_none)
|
||||
{
|
||||
if (complain & tf_error)
|
||||
error ("%qE is not a valid template argument of type %qT "
|
||||
"because %qD has no linkage", expr, type, decl);
|
||||
return true;
|
||||
}
|
||||
/* C++17: For a non-type template-parameter of reference or pointer
|
||||
type, the value of the constant expression shall not refer to (or
|
||||
for a pointer type, shall not be the address of):
|
||||
* a subobject (4.5),
|
||||
* a temporary object (15.2),
|
||||
* a string literal (5.13.5),
|
||||
* the result of a typeid expression (8.2.8), or
|
||||
* a predefined __func__ variable (11.4.1). */
|
||||
else if (DECL_ARTIFICIAL (decl))
|
||||
{
|
||||
if (complain & tf_error)
|
||||
error ("the address of %qD is not a valid template argument",
|
||||
decl);
|
||||
return true;
|
||||
}
|
||||
else if (!same_type_ignoring_top_level_qualifiers_p
|
||||
(strip_array_types (TREE_TYPE (type)),
|
||||
strip_array_types (TREE_TYPE (decl))))
|
||||
{
|
||||
if (complain & tf_error)
|
||||
error ("the address of the %qT subobject of %qD is not a "
|
||||
"valid template argument", TREE_TYPE (type), decl);
|
||||
return true;
|
||||
}
|
||||
else if (!TREE_STATIC (decl) && !DECL_EXTERNAL (decl))
|
||||
{
|
||||
if (complain & tf_error)
|
||||
error ("the address of %qD is not a valid template argument "
|
||||
"because it does not have static storage duration",
|
||||
decl);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (!INDIRECT_TYPE_P (type))
|
||||
/* We're only concerned about pointers and references here. */;
|
||||
else if (cxx_dialect >= cxx11 && integer_zerop (expr))
|
||||
/* Null pointer values are OK in C++11. */;
|
||||
else
|
||||
{
|
||||
if (VAR_P (expr))
|
||||
{
|
||||
if (complain & tf_error)
|
||||
error ("%qD is not a valid template argument "
|
||||
"because %qD is a variable, not the address of "
|
||||
"a variable", expr, expr);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (complain & tf_error)
|
||||
error ("%qE is not a valid template argument for %qT "
|
||||
"because it is not the address of a variable",
|
||||
expr, type);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/* Return a VAR_DECL for the C++20 template parameter object corresponding to
|
||||
template argument EXPR. */
|
||||
|
||||
static tree
|
||||
get_template_parm_object (tree expr, tsubst_flags_t complain)
|
||||
{
|
||||
if (TREE_CODE (expr) == TARGET_EXPR)
|
||||
expr = TARGET_EXPR_INITIAL (expr);
|
||||
|
||||
if (!TREE_CONSTANT (expr))
|
||||
{
|
||||
if ((complain & tf_error)
|
||||
&& require_rvalue_constant_expression (expr))
|
||||
cxx_constant_value (expr);
|
||||
return error_mark_node;
|
||||
}
|
||||
if (invalid_tparm_referent_p (TREE_TYPE (expr), expr, complain))
|
||||
return error_mark_node;
|
||||
|
||||
tree name = mangle_template_parm_object (expr);
|
||||
tree decl = get_global_binding (name);
|
||||
if (decl)
|
||||
return decl;
|
||||
|
||||
tree type = cp_build_qualified_type (TREE_TYPE (expr), TYPE_QUAL_CONST);
|
||||
decl = create_temporary_var (type);
|
||||
TREE_STATIC (decl) = true;
|
||||
DECL_DECLARED_CONSTEXPR_P (decl) = true;
|
||||
TREE_READONLY (decl) = true;
|
||||
DECL_NAME (decl) = name;
|
||||
SET_DECL_ASSEMBLER_NAME (decl, name);
|
||||
DECL_CONTEXT (decl) = global_namespace;
|
||||
comdat_linkage (decl);
|
||||
pushdecl_top_level_and_finish (decl, expr);
|
||||
return decl;
|
||||
}
|
||||
|
||||
/* Attempt to convert the non-type template parameter EXPR to the
|
||||
indicated TYPE. If the conversion is successful, return the
|
||||
converted value. If the conversion is unsuccessful, return
|
||||
|
@ -6609,15 +6772,13 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
|
|||
{
|
||||
tree expr_type;
|
||||
location_t loc = cp_expr_loc_or_loc (expr, input_location);
|
||||
tree orig_expr = expr;
|
||||
|
||||
/* Detect immediately string literals as invalid non-type argument.
|
||||
This special-case is not needed for correctness (we would easily
|
||||
catch this later), but only to provide better diagnostic for this
|
||||
common user mistake. As suggested by DR 100, we do not mention
|
||||
linkage issues in the diagnostic as this is not the point. */
|
||||
/* FIXME we're making this OK. */
|
||||
if (TREE_CODE (expr) == STRING_CST)
|
||||
if (TREE_CODE (expr) == STRING_CST && !CLASS_TYPE_P (type))
|
||||
{
|
||||
if (complain & tf_error)
|
||||
error ("%qE is not a valid template argument for type %qT "
|
||||
|
@ -6841,88 +7002,13 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
|
|||
;
|
||||
else if (cxx_dialect >= cxx11 && integer_zerop (expr))
|
||||
/* Null pointer values are OK in C++11. */;
|
||||
else if (TREE_CODE (expr) != ADDR_EXPR)
|
||||
{
|
||||
if (VAR_P (expr))
|
||||
{
|
||||
if (complain & tf_error)
|
||||
error ("%qD is not a valid template argument "
|
||||
"because %qD is a variable, not the address of "
|
||||
"a variable", orig_expr, expr);
|
||||
return NULL_TREE;
|
||||
}
|
||||
if (INDIRECT_TYPE_P (expr_type))
|
||||
{
|
||||
if (complain & tf_error)
|
||||
error ("%qE is not a valid template argument for %qT "
|
||||
"because it is not the address of a variable",
|
||||
orig_expr, type);
|
||||
return NULL_TREE;
|
||||
}
|
||||
/* Other values, like integer constants, might be valid
|
||||
non-type arguments of some other type. */
|
||||
return error_mark_node;
|
||||
}
|
||||
else
|
||||
{
|
||||
tree decl = TREE_OPERAND (expr, 0);
|
||||
|
||||
if (!VAR_P (decl))
|
||||
{
|
||||
if (complain & tf_error)
|
||||
error ("%qE is not a valid template argument of type %qT "
|
||||
"because %qE is not a variable", orig_expr, type, decl);
|
||||
return NULL_TREE;
|
||||
}
|
||||
else if (cxx_dialect < cxx11 && !DECL_EXTERNAL_LINKAGE_P (decl))
|
||||
{
|
||||
if (complain & tf_error)
|
||||
error ("%qE is not a valid template argument of type %qT "
|
||||
"because %qD does not have external linkage",
|
||||
orig_expr, type, decl);
|
||||
return NULL_TREE;
|
||||
}
|
||||
else if ((cxx_dialect >= cxx11 && cxx_dialect < cxx17)
|
||||
&& decl_linkage (decl) == lk_none)
|
||||
{
|
||||
if (complain & tf_error)
|
||||
error ("%qE is not a valid template argument of type %qT "
|
||||
"because %qD has no linkage", orig_expr, type, decl);
|
||||
return NULL_TREE;
|
||||
}
|
||||
/* C++17: For a non-type template-parameter of reference or pointer
|
||||
type, the value of the constant expression shall not refer to (or
|
||||
for a pointer type, shall not be the address of):
|
||||
* a subobject (4.5),
|
||||
* a temporary object (15.2),
|
||||
* a string literal (5.13.5),
|
||||
* the result of a typeid expression (8.2.8), or
|
||||
* a predefined __func__ variable (11.4.1). */
|
||||
else if (DECL_ARTIFICIAL (decl))
|
||||
{
|
||||
if (complain & tf_error)
|
||||
error ("the address of %qD is not a valid template argument",
|
||||
decl);
|
||||
return NULL_TREE;
|
||||
}
|
||||
else if (!same_type_ignoring_top_level_qualifiers_p
|
||||
(strip_array_types (TREE_TYPE (type)),
|
||||
strip_array_types (TREE_TYPE (decl))))
|
||||
{
|
||||
if (complain & tf_error)
|
||||
error ("the address of the %qT subobject of %qD is not a "
|
||||
"valid template argument", TREE_TYPE (type), decl);
|
||||
return NULL_TREE;
|
||||
}
|
||||
else if (!TREE_STATIC (decl) && !DECL_EXTERNAL (decl))
|
||||
{
|
||||
if (complain & tf_error)
|
||||
error ("the address of %qD is not a valid template argument "
|
||||
"because it does not have static storage duration",
|
||||
decl);
|
||||
return NULL_TREE;
|
||||
}
|
||||
}
|
||||
else if (TREE_CODE (expr) != ADDR_EXPR
|
||||
&& !INDIRECT_TYPE_P (expr_type))
|
||||
/* Other values, like integer constants, might be valid
|
||||
non-type arguments of some other type. */
|
||||
return error_mark_node;
|
||||
else if (invalid_tparm_referent_p (type, expr, complain))
|
||||
return NULL_TREE;
|
||||
|
||||
expr = decayed;
|
||||
|
||||
|
@ -6985,27 +7071,10 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
|
|||
itself value-dependent, since what we want here is its address. */;
|
||||
else
|
||||
{
|
||||
if (!DECL_P (expr))
|
||||
{
|
||||
if (complain & tf_error)
|
||||
error ("%qE is not a valid template argument for type %qT "
|
||||
"because it is not an object with linkage",
|
||||
expr, type);
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* DR 1155 allows internal linkage in C++11 and up. */
|
||||
linkage_kind linkage = decl_linkage (expr);
|
||||
if (linkage < (cxx_dialect >= cxx11 ? lk_internal : lk_external))
|
||||
{
|
||||
if (complain & tf_error)
|
||||
error ("%qE is not a valid template argument for type %qT "
|
||||
"because object %qD does not have linkage",
|
||||
expr, type, expr);
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
expr = build_address (expr);
|
||||
|
||||
if (invalid_tparm_referent_p (type, expr, complain))
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
if (!same_type_p (type, TREE_TYPE (expr)))
|
||||
|
@ -7111,6 +7180,14 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
|
|||
}
|
||||
return expr;
|
||||
}
|
||||
else if (CLASS_TYPE_P (type))
|
||||
{
|
||||
/* Replace the argument with a reference to the corresponding template
|
||||
parameter object. */
|
||||
expr = get_template_parm_object (expr, complain);
|
||||
if (expr == error_mark_node)
|
||||
return NULL_TREE;
|
||||
}
|
||||
/* A template non-type parameter must be one of the above. */
|
||||
else
|
||||
gcc_unreachable ();
|
||||
|
@ -15593,9 +15670,31 @@ tsubst_copy (tree t, tree args, tsubst_flags_t complain, tree in_decl)
|
|||
first, *then* reusing the resulting type. Doing the type
|
||||
first ensures that we handle template parameters and
|
||||
parameter pack expansions. */
|
||||
gcc_assert (location_wrapper_p (t));
|
||||
tree op0 = tsubst_copy (TREE_OPERAND (t, 0), args, complain, in_decl);
|
||||
return maybe_wrap_with_location (op0, EXPR_LOCATION (t));
|
||||
if (location_wrapper_p (t))
|
||||
{
|
||||
tree op0 = tsubst_copy (TREE_OPERAND (t, 0), args,
|
||||
complain, in_decl);
|
||||
return maybe_wrap_with_location (op0, EXPR_LOCATION (t));
|
||||
}
|
||||
tree op = TREE_OPERAND (t, 0);
|
||||
if (code == VIEW_CONVERT_EXPR
|
||||
&& TREE_CODE (op) == TEMPLATE_PARM_INDEX)
|
||||
{
|
||||
/* Wrapper to make a C++20 template parameter object const. */
|
||||
op = tsubst_copy (op, args, complain, in_decl);
|
||||
if (TREE_CODE (op) == TEMPLATE_PARM_INDEX)
|
||||
{
|
||||
tree type = tsubst (TREE_TYPE (t), args, complain, in_decl);
|
||||
return build1 (code, type, op);
|
||||
}
|
||||
else
|
||||
{
|
||||
gcc_assert (CP_TYPE_CONST_P (TREE_TYPE (op)));
|
||||
return op;
|
||||
}
|
||||
}
|
||||
/* We shouldn't see any other uses of these in templates. */
|
||||
gcc_unreachable ();
|
||||
}
|
||||
|
||||
case CAST_EXPR:
|
||||
|
@ -19042,10 +19141,9 @@ tsubst_copy_and_build (tree t,
|
|||
|
||||
case NON_LVALUE_EXPR:
|
||||
case VIEW_CONVERT_EXPR:
|
||||
/* We should only see these for location wrapper nodes, or within
|
||||
instantiate_non_dependent_expr (when args is NULL_TREE). */
|
||||
gcc_assert (location_wrapper_p (t) || args == NULL_TREE);
|
||||
if (location_wrapper_p (t))
|
||||
/* We need to do this here as well as in tsubst_copy so we get the
|
||||
other tsubst_copy_and_build semantics for a PARM_DECL operand. */
|
||||
RETURN (maybe_wrap_with_location (RECUR (TREE_OPERAND (t, 0)),
|
||||
EXPR_LOCATION (t)));
|
||||
/* fallthrough. */
|
||||
|
@ -24566,6 +24664,33 @@ invalid_nontype_parm_type_p (tree type, tsubst_flags_t complain)
|
|||
else if (cxx_dialect >= cxx11
|
||||
&& TREE_CODE (type) == BOUND_TEMPLATE_TEMPLATE_PARM)
|
||||
return false;
|
||||
else if (CLASS_TYPE_P (type))
|
||||
{
|
||||
if (cxx_dialect < cxx2a)
|
||||
{
|
||||
error ("non-type template parameters of class type only available "
|
||||
"with -std=c++2a or -std=gnu++2a");
|
||||
return true;
|
||||
}
|
||||
if (!complete_type_or_else (type, NULL_TREE))
|
||||
return true;
|
||||
if (!literal_type_p (type))
|
||||
{
|
||||
error ("%qT is not a valid type for a template non-type parameter "
|
||||
"because it is not literal", type);
|
||||
explain_non_literal_class (type);
|
||||
return true;
|
||||
}
|
||||
if (cp_has_mutable_p (type))
|
||||
{
|
||||
error ("%qT is not a valid type for a template non-type parameter "
|
||||
"because it has a mutable member", type);
|
||||
return true;
|
||||
}
|
||||
/* FIXME check op<=> and strong structural equality once spaceship is
|
||||
implemented. */
|
||||
return false;
|
||||
}
|
||||
|
||||
if (complain & tf_error)
|
||||
{
|
||||
|
@ -25361,7 +25486,10 @@ instantiation_dependent_r (tree *tp, int *walk_subtrees,
|
|||
return NULL_TREE;
|
||||
|
||||
case TEMPLATE_PARM_INDEX:
|
||||
return *tp;
|
||||
if (dependent_type_p (TREE_TYPE (*tp)))
|
||||
return *tp;
|
||||
/* We'll check value-dependence separately. */
|
||||
return NULL_TREE;
|
||||
|
||||
/* Handle expressions with type operands. */
|
||||
case SIZEOF_EXPR:
|
||||
|
|
|
@ -3648,8 +3648,17 @@ finish_id_expression (tree id_expression,
|
|||
*idk = CP_ID_KIND_NONE;
|
||||
if (TREE_CODE (decl) == TEMPLATE_PARM_INDEX)
|
||||
decl = TEMPLATE_PARM_DECL (decl);
|
||||
r = convert_from_reference (DECL_INITIAL (decl));
|
||||
|
||||
r = DECL_INITIAL (decl);
|
||||
if (CLASS_TYPE_P (TREE_TYPE (r)) && !CP_TYPE_CONST_P (TREE_TYPE (r)))
|
||||
{
|
||||
/* If the entity is a template parameter object for a template
|
||||
parameter of type T, the type of the expression is const T. */
|
||||
tree ctype = TREE_TYPE (r);
|
||||
ctype = cp_build_qualified_type (ctype, (cp_type_quals (ctype)
|
||||
| TYPE_QUAL_CONST));
|
||||
r = build1 (VIEW_CONVERT_EXPR, ctype, r);
|
||||
}
|
||||
r = convert_from_reference (r);
|
||||
if (integral_constant_expression_p
|
||||
&& !dependent_type_p (TREE_TYPE (decl))
|
||||
&& !(INTEGRAL_OR_ENUMERATION_TYPE_P (TREE_TYPE (r))))
|
||||
|
@ -8802,7 +8811,8 @@ finish_decltype_type (tree expr, bool id_expression_or_member_access_p,
|
|||
if (identifier_p (expr))
|
||||
expr = lookup_name (expr);
|
||||
|
||||
if (INDIRECT_REF_P (expr))
|
||||
if (INDIRECT_REF_P (expr)
|
||||
|| TREE_CODE (expr) == VIEW_CONVERT_EXPR)
|
||||
/* This can happen when the expression is, e.g., "a.b". Just
|
||||
look at the underlying operand. */
|
||||
expr = TREE_OPERAND (expr, 0);
|
||||
|
|
|
@ -280,6 +280,12 @@ lvalue_kind (const_tree ref)
|
|||
case PAREN_EXPR:
|
||||
return lvalue_kind (TREE_OPERAND (ref, 0));
|
||||
|
||||
case TEMPLATE_PARM_INDEX:
|
||||
if (CLASS_TYPE_P (TREE_TYPE (ref)))
|
||||
/* A template parameter object is an lvalue. */
|
||||
return clk_ordinary;
|
||||
return clk_none;
|
||||
|
||||
default:
|
||||
default_:
|
||||
if (!TREE_TYPE (ref))
|
||||
|
|
|
@ -416,6 +416,12 @@
|
|||
# error "__cpp_variadic_using != 201611"
|
||||
#endif
|
||||
|
||||
// C++20 features
|
||||
|
||||
#if __cpp_nontype_template_parameter_class != 201806
|
||||
# error "__cpp_nontype_template_parameter_class != 201806"
|
||||
#endif
|
||||
|
||||
#ifdef __has_cpp_attribute
|
||||
|
||||
# if ! __has_cpp_attribute(maybe_unused)
|
||||
|
|
33
gcc/testsuite/g++.dg/cpp2a/nontype-class1.C
Normal file
33
gcc/testsuite/g++.dg/cpp2a/nontype-class1.C
Normal file
|
@ -0,0 +1,33 @@
|
|||
// { dg-do compile { target c++2a } }
|
||||
|
||||
struct A
|
||||
{
|
||||
int i;
|
||||
constexpr A (int i): i(i) {}
|
||||
// auto operator<=> (const A&) = default;
|
||||
};
|
||||
|
||||
template <A a>
|
||||
struct B
|
||||
{
|
||||
static constexpr int i = a.i;
|
||||
static constexpr A const* ap = &a;
|
||||
};
|
||||
|
||||
template <A a>
|
||||
struct C
|
||||
{
|
||||
static constexpr A const* ap = &a;
|
||||
};
|
||||
|
||||
static_assert(B<1>::i == 1);
|
||||
static_assert(B<2>::i == 2);
|
||||
static_assert(B<1>::ap == C<1>::ap);
|
||||
static_assert(B<1>::ap != C<2>::ap);
|
||||
|
||||
// { dg-final { scan-assembler "_Z1fP1BIXtl1ALi1EEEE" } }
|
||||
// { dg-final { scan-assembler "_ZTAXtl1ALi1EEE" } }
|
||||
const void* f(B<1> *p) {
|
||||
constexpr int i = p->ap->i;
|
||||
return p->ap;
|
||||
}
|
17
gcc/testsuite/g++.dg/cpp2a/nontype-class2.C
Normal file
17
gcc/testsuite/g++.dg/cpp2a/nontype-class2.C
Normal file
|
@ -0,0 +1,17 @@
|
|||
// { dg-do compile { target c++2a } }
|
||||
|
||||
struct A {
|
||||
int i;
|
||||
// auto operator<=> (const A&) = default;
|
||||
};
|
||||
template <typename T, T t> void f()
|
||||
{
|
||||
g(t); // { dg-error "not declared" }
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
f<A,A{1}>();
|
||||
}
|
||||
|
||||
// { dg-message "T t = A{1}" "" { target *-*-* } 0 }
|
16
gcc/testsuite/g++.dg/cpp2a/nontype-class3.C
Normal file
16
gcc/testsuite/g++.dg/cpp2a/nontype-class3.C
Normal file
|
@ -0,0 +1,16 @@
|
|||
// { dg-do compile { target c++2a } }
|
||||
|
||||
struct A {
|
||||
int i;
|
||||
// auto operator<=> (const A&) = default;
|
||||
};
|
||||
template <A a> void g();
|
||||
template <auto t> void f()
|
||||
{
|
||||
g<t>();
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
f<A{1}>();
|
||||
}
|
18
gcc/testsuite/g++.dg/cpp2a/nontype-class4.C
Normal file
18
gcc/testsuite/g++.dg/cpp2a/nontype-class4.C
Normal file
|
@ -0,0 +1,18 @@
|
|||
// { dg-do compile { target c++2a } }
|
||||
|
||||
template <class T>
|
||||
struct A {
|
||||
constexpr A(T) {}
|
||||
// auto operator<=> (const A&) = default;
|
||||
};
|
||||
template <A a> void f();
|
||||
|
||||
int main()
|
||||
{
|
||||
constexpr A a = 1;
|
||||
f<a>();
|
||||
f<1>();
|
||||
}
|
||||
|
||||
// { dg-final { scan-assembler "_Z1fIXtl1AIiEEEEvv" } }
|
||||
// { dg-final { scan-assembler-not "_Z1fIXtlK1AIiEEEEvv" } }
|
12
gcc/testsuite/g++.dg/cpp2a/nontype-class5.C
Normal file
12
gcc/testsuite/g++.dg/cpp2a/nontype-class5.C
Normal file
|
@ -0,0 +1,12 @@
|
|||
// Example from P0732
|
||||
// { dg-do compile { target c++2a } }
|
||||
|
||||
template<class T, T p> class X {
|
||||
/* ... */
|
||||
};
|
||||
|
||||
struct A {
|
||||
constexpr A(const char*) {}
|
||||
// auto operator<=> (const A&) = default;
|
||||
};
|
||||
X<A, "Pyrophoricity"> x3; // OK: string literal is a constructor argument to A
|
26
gcc/testsuite/g++.dg/cpp2a/nontype-class7.C
Normal file
26
gcc/testsuite/g++.dg/cpp2a/nontype-class7.C
Normal file
|
@ -0,0 +1,26 @@
|
|||
// Example from P0732.
|
||||
// { dg-do compile { target c++2a } }
|
||||
|
||||
namespace std {
|
||||
using size_t = decltype(sizeof(1));
|
||||
template <typename CharT, std::size_t N>
|
||||
struct basic_fixed_string
|
||||
{
|
||||
constexpr basic_fixed_string(const CharT (&foo)[N+1])
|
||||
: m_data()
|
||||
{
|
||||
for (int i = 0; i <= N; ++i)
|
||||
m_data[i] = foo[i];
|
||||
}
|
||||
// auto operator<=>(const basic_fixed_string &) = default;
|
||||
CharT m_data[N+1];
|
||||
};
|
||||
template <typename CharT, std::size_t N>
|
||||
basic_fixed_string(const CharT (&str)[N])->basic_fixed_string<CharT, N-1>;
|
||||
template <std::size_t N>
|
||||
using fixed_string = basic_fixed_string<char, N>;
|
||||
}
|
||||
|
||||
template <std::basic_fixed_string Str>
|
||||
struct A {};
|
||||
using hello_A = A<"hello">;
|
26
gcc/testsuite/g++.dg/cpp2a/nontype-class8.C
Normal file
26
gcc/testsuite/g++.dg/cpp2a/nontype-class8.C
Normal file
|
@ -0,0 +1,26 @@
|
|||
// If the entity is a template parameter object for a template parameter of
|
||||
// type T, the type of the expression is const T.
|
||||
|
||||
// { dg-do compile { target c++2a } }
|
||||
|
||||
template <class T, class U> struct same;
|
||||
template <class T> struct same<T,T> {};
|
||||
|
||||
struct A {
|
||||
int i;
|
||||
// auto operator<=> (const A&) = default;
|
||||
};
|
||||
void f(A&) = delete;
|
||||
void f(const A&) { }
|
||||
|
||||
template < A a > struct B
|
||||
{
|
||||
B()
|
||||
{
|
||||
f(a);
|
||||
same<A,decltype(a)> s;
|
||||
same<const A&,decltype((a))> s2;
|
||||
}
|
||||
};
|
||||
|
||||
B<A{42}> b;
|
|
@ -1,6 +1,6 @@
|
|||
//PR c++/27668
|
||||
|
||||
template<typename class T, T = T()> // { dg-error "nested-name-specifier|two or more|valid type" }
|
||||
template<typename class T, T = T()> // { dg-error "nested-name-specifier|two or more|class type|incomplete" }
|
||||
// { dg-error "cast" "" { target c++98_only } .-1 }
|
||||
struct A {};
|
||||
|
||||
|
|
|
@ -5,5 +5,5 @@ template<int> struct A
|
|||
template<typename> void foo();
|
||||
};
|
||||
|
||||
template<> template<struct T> void A<0>::foo() {} // { dg-error "not a valid type" }
|
||||
template<> template<struct T> void A<0>::foo() {} // { dg-error "class type|incomplete" }
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
template <int> struct A
|
||||
{
|
||||
typedef A<0> B; // { dg-message "previous declaration" }
|
||||
template <B> struct B {}; // { dg-error "not a valid type|typedef" }
|
||||
template <B> struct B {}; // { dg-error "class type|incomplete|typedef" }
|
||||
};
|
||||
|
||||
A<0> a;
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
template <int> struct A
|
||||
{
|
||||
typedef A<0> B;
|
||||
template <B> struct C {}; // { dg-error "not a valid type" }
|
||||
template <B> struct C {}; // { dg-error "class type|incomplete" }
|
||||
};
|
||||
|
||||
A<0> a;
|
||||
|
|
|
@ -3,6 +3,6 @@
|
|||
|
||||
struct A {};
|
||||
|
||||
template<A, typename T> int operator-(A, T); // { dg-error "not a valid type" }
|
||||
template<A, typename T> int operator-(A, T); // { dg-error "class type" "" { target c++17_down } }
|
||||
|
||||
int i = A() - 0; // { dg-error "no match" }
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
2018-10-19 Jason Merrill <jason@redhat.com>
|
||||
|
||||
* demangle.h (enum demangle_component_type): Add
|
||||
DEMANGLE_COMPONENT_TPARM_OBJ.
|
||||
|
||||
2018-10-29 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
* unique-ptr.h (gnu::move): Generalize so it applies to all
|
||||
|
|
|
@ -392,6 +392,9 @@ enum demangle_component_type
|
|||
template argument, and the right subtree is either NULL or
|
||||
another TEMPLATE_ARGLIST node. */
|
||||
DEMANGLE_COMPONENT_TEMPLATE_ARGLIST,
|
||||
/* A template parameter object (C++20). The left subtree is the
|
||||
corresponding template argument. */
|
||||
DEMANGLE_COMPONENT_TPARM_OBJ,
|
||||
/* An initializer list. The left subtree is either an explicit type or
|
||||
NULL, and the right subtree is a DEMANGLE_COMPONENT_ARGLIST. */
|
||||
DEMANGLE_COMPONENT_INITIALIZER_LIST,
|
||||
|
|
|
@ -1,3 +1,11 @@
|
|||
2018-10-23 Jason Merrill <jason@redhat.com>
|
||||
|
||||
Implement P0732R2, class types in non-type template parameters.
|
||||
* cp-demangle.c (d_dump, d_make_comp, d_count_templates_scopes)
|
||||
(d_print_comp_inner): Handle DEMANGLE_COMPONENT_TPARM_OBJ.
|
||||
(d_special_name): Handle TA.
|
||||
(d_expresion_1): Fix demangling of brace-enclosed initializer list.
|
||||
|
||||
2018-10-31 Joseph Myers <joseph@codesourcery.com>
|
||||
|
||||
PR bootstrap/82856
|
||||
|
|
|
@ -625,6 +625,9 @@ d_dump (struct demangle_component *dc, int indent)
|
|||
case DEMANGLE_COMPONENT_TEMPLATE_PARAM:
|
||||
printf ("template parameter %ld\n", dc->u.s_number.number);
|
||||
return;
|
||||
case DEMANGLE_COMPONENT_TPARM_OBJ:
|
||||
printf ("template parameter object\n");
|
||||
break;
|
||||
case DEMANGLE_COMPONENT_FUNCTION_PARAM:
|
||||
printf ("function parameter %ld\n", dc->u.s_number.number);
|
||||
return;
|
||||
|
@ -1007,6 +1010,7 @@ d_make_comp (struct d_info *di, enum demangle_component_type type,
|
|||
case DEMANGLE_COMPONENT_GLOBAL_DESTRUCTORS:
|
||||
case DEMANGLE_COMPONENT_NULLARY:
|
||||
case DEMANGLE_COMPONENT_TRINARY_ARG2:
|
||||
case DEMANGLE_COMPONENT_TPARM_OBJ:
|
||||
if (left == NULL)
|
||||
return NULL;
|
||||
break;
|
||||
|
@ -2007,6 +2011,7 @@ d_java_resource (struct d_info *di)
|
|||
::= TT <type>
|
||||
::= TI <type>
|
||||
::= TS <type>
|
||||
::= TA <template-arg>
|
||||
::= GV <(object) name>
|
||||
::= T <call-offset> <(base) encoding>
|
||||
::= Tc <call-offset> <call-offset> <(base) encoding>
|
||||
|
@ -2099,6 +2104,10 @@ d_special_name (struct d_info *di)
|
|||
return d_make_comp (di, DEMANGLE_COMPONENT_TLS_WRAPPER,
|
||||
d_name (di), NULL);
|
||||
|
||||
case 'A':
|
||||
return d_make_comp (di, DEMANGLE_COMPONENT_TPARM_OBJ,
|
||||
d_template_arg (di), NULL);
|
||||
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
@ -3327,11 +3336,11 @@ d_expression_1 (struct d_info *di)
|
|||
{
|
||||
/* Brace-enclosed initializer list, untyped or typed. */
|
||||
struct demangle_component *type = NULL;
|
||||
d_advance (di, 2);
|
||||
if (peek == 't')
|
||||
type = cplus_demangle_type (di);
|
||||
if (!d_peek_next_char (di))
|
||||
return NULL;
|
||||
d_advance (di, 2);
|
||||
return d_make_comp (di, DEMANGLE_COMPONENT_INITIALIZER_LIST,
|
||||
type, d_exprlist (di, 'E'));
|
||||
}
|
||||
|
@ -4101,6 +4110,7 @@ d_count_templates_scopes (int *num_templates, int *num_scopes,
|
|||
case DEMANGLE_COMPONENT_VECTOR_TYPE:
|
||||
case DEMANGLE_COMPONENT_ARGLIST:
|
||||
case DEMANGLE_COMPONENT_TEMPLATE_ARGLIST:
|
||||
case DEMANGLE_COMPONENT_TPARM_OBJ:
|
||||
case DEMANGLE_COMPONENT_INITIALIZER_LIST:
|
||||
case DEMANGLE_COMPONENT_CAST:
|
||||
case DEMANGLE_COMPONENT_CONVERSION:
|
||||
|
@ -4872,6 +4882,11 @@ d_print_comp_inner (struct d_print_info *dpi, int options,
|
|||
}
|
||||
return;
|
||||
|
||||
case DEMANGLE_COMPONENT_TPARM_OBJ:
|
||||
d_append_string (dpi, "template parameter object for ");
|
||||
d_print_comp (dpi, options, d_left (dc));
|
||||
return;
|
||||
|
||||
case DEMANGLE_COMPONENT_CTOR:
|
||||
d_print_comp (dpi, options, dc->u.s_ctor.name);
|
||||
return;
|
||||
|
|
|
@ -4667,6 +4667,12 @@ void eat<int*, Foo()::{lambda(auto:1*, auto:2*)#6}>(int*&, Foo()::{lambda(auto:1
|
|||
_Z3eatIPiZ3BarIsEvvEUlPsPT_PT0_E0_EvRS3_RS5_
|
||||
void eat<int*, Bar<short>()::{lambda(short*, auto:1*, auto:2*)#2}>(int*&, Bar<short>()::{lambda(short*, auto:1*, auto:2*)#2}&)
|
||||
|
||||
_Z1fP1BIXtl1ALi1EEEE
|
||||
f(B<A{1}>*)
|
||||
|
||||
_ZTAXtl1ALi1EEE
|
||||
template parameter object for A{1}
|
||||
|
||||
# PR 77489
|
||||
_ZZ3foovE8localVar_9
|
||||
foo()::localVar
|
||||
|
|
Loading…
Add table
Reference in a new issue