From: Jason Merrill Date: Sat, 10 Jun 2017 00:40:50 +0000 (-0400) Subject: Missing bits from N4268, constant evaluation for all non-type args. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=e6d7d61861c8fa7207570abb6fc23c5a2921e4da;p=gcc.git Missing bits from N4268, constant evaluation for all non-type args. * call.c (build_converted_constant_expr): Rename from build_integral_nontype_arg_conv, handle all types. * pt.c (convert_nontype_argument): In C++17 call it for all types. Move NOP stripping inside pointer case, don't strip ADDR_EXPR. * cvt.c (strip_fnptr_conv): Also strip conversions to the same type. From-SVN: r249089 --- diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 4e966a2e26f..14eaa983327 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,5 +1,12 @@ 2017-06-09 Jason Merrill + Missing bits from N4268, constant evaluation for all non-type args. + * call.c (build_converted_constant_expr): Rename from + build_integral_nontype_arg_conv, handle all types. + * pt.c (convert_nontype_argument): In C++17 call it for all types. + Move NOP stripping inside pointer case, don't strip ADDR_EXPR. + * cvt.c (strip_fnptr_conv): Also strip conversions to the same type. + Overhaul pointer-to-member conversion and template argument handling. * call.c (standard_conversion): Avoid creating ck_pmem when the class type is the same. diff --git a/gcc/cp/call.c b/gcc/cp/call.c index a4b6a95f13e..ef9968340c6 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -4005,15 +4005,16 @@ build_user_type_conversion (tree totype, tree expr, int flags, /* Subroutine of convert_nontype_argument. - EXPR is an argument for a template non-type parameter of integral or - enumeration type. Do any necessary conversions (that are permitted for - non-type arguments) to convert it to the parameter type. + EXPR is an expression used in a context that requires a converted + constant-expression, such as a template non-type parameter. Do any + necessary conversions (that are permitted for converted + constant-expressions) to convert it to the desired type. If conversion is successful, returns the converted expression; otherwise, returns error_mark_node. */ tree -build_integral_nontype_arg_conv (tree type, tree expr, tsubst_flags_t complain) +build_converted_constant_expr (tree type, tree expr, tsubst_flags_t complain) { conversion *conv; void *p; @@ -4023,8 +4024,6 @@ build_integral_nontype_arg_conv (tree type, tree expr, tsubst_flags_t complain) if (error_operand_p (expr)) return error_mark_node; - gcc_assert (INTEGRAL_OR_ENUMERATION_TYPE_P (type)); - /* Get the high-water mark for the CONVERSION_OBSTACK. */ p = conversion_obstack_alloc (0); @@ -4032,36 +4031,86 @@ build_integral_nontype_arg_conv (tree type, tree expr, tsubst_flags_t complain) /*c_cast_p=*/false, LOOKUP_IMPLICIT, complain); - /* for a non-type template-parameter of integral or - enumeration type, integral promotions (4.5) and integral - conversions (4.7) are applied. */ - /* It should be sufficient to check the outermost conversion step, since - there are no qualification conversions to integer type. */ - if (conv) - switch (conv->kind) - { - /* A conversion function is OK. If it isn't constexpr, we'll - complain later that the argument isn't constant. */ - case ck_user: - /* The lvalue-to-rvalue conversion is OK. */ - case ck_rvalue: - case ck_identity: - break; + /* A converted constant expression of type T is an expression, implicitly + converted to type T, where the converted expression is a constant + expression and the implicit conversion sequence contains only + + * user-defined conversions, + * lvalue-to-rvalue conversions (7.1), + * array-to-pointer conversions (7.2), + * function-to-pointer conversions (7.3), + * qualification conversions (7.5), + * integral promotions (7.6), + * integral conversions (7.8) other than narrowing conversions (11.6.4), + * null pointer conversions (7.11) from std::nullptr_t, + * null member pointer conversions (7.12) from std::nullptr_t, and + * function pointer conversions (7.13), + + and where the reference binding (if any) binds directly. */ + + for (conversion *c = conv; + conv && c->kind != ck_identity; + c = next_conversion (c)) + { + switch (c->kind) + { + /* A conversion function is OK. If it isn't constexpr, we'll + complain later that the argument isn't constant. */ + case ck_user: + /* The lvalue-to-rvalue conversion is OK. */ + case ck_rvalue: + /* Array-to-pointer and function-to-pointer. */ + case ck_lvalue: + /* Function pointer conversions. */ + case ck_fnptr: + /* Qualification conversions. */ + case ck_qual: + break; - case ck_std: - t = next_conversion (conv)->type; - if (INTEGRAL_OR_ENUMERATION_TYPE_P (t)) + case ck_ref_bind: + if (c->need_temporary_p) + { + if (complain & tf_error) + error_at (loc, "initializing %qH with %qI in converted " + "constant expression does not bind directly", + type, next_conversion (c)->type); + conv = NULL; + } break; - if (complain & tf_error) - error_at (loc, "conversion from %qH to %qI not considered for " - "non-type template argument", t, type); - /* fall through. */ + case ck_base: + case ck_pmem: + case ck_ptr: + case ck_std: + t = next_conversion (c)->type; + if (INTEGRAL_OR_ENUMERATION_TYPE_P (t) + && INTEGRAL_OR_ENUMERATION_TYPE_P (type)) + /* Integral promotion or conversion. */ + break; + if (NULLPTR_TYPE_P (t)) + /* Conversion from nullptr to pointer or pointer-to-member. */ + break; - default: - conv = NULL; - break; - } + if (complain & tf_error) + error_at (loc, "conversion from %qH to %qI in a " + "converted constant expression", t, type); + /* fall through. */ + + default: + conv = NULL; + break; + } + } + + /* Avoid confusing convert_nontype_argument by introducing + a redundant conversion to the same reference type. */ + if (conv && conv->kind == ck_ref_bind + && REFERENCE_REF_P (expr)) + { + tree ref = TREE_OPERAND (expr, 0); + if (same_type_p (type, TREE_TYPE (ref))) + return ref; + } if (conv) expr = convert_like (conv, expr, complain); diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 07da0cda5e7..6d4d93702f8 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -5907,7 +5907,7 @@ extern bool reference_related_p (tree, tree); extern int remaining_arguments (tree); extern tree perform_implicit_conversion (tree, tree, tsubst_flags_t); extern tree perform_implicit_conversion_flags (tree, tree, tsubst_flags_t, int); -extern tree build_integral_nontype_arg_conv (tree, tree, tsubst_flags_t); +extern tree build_converted_constant_expr (tree, tree, tsubst_flags_t); extern tree perform_direct_initialization_if_possible (tree, tree, bool, tsubst_flags_t); extern tree in_charge_arg_for_name (tree); diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c index 3460e133429..631ff49673f 100644 --- a/gcc/cp/cvt.c +++ b/gcc/cp/cvt.c @@ -2020,8 +2020,8 @@ fnptr_conv_p (tree to, tree from) || can_convert_tx_safety (t, f)); } -/* Return FN with any NOP_EXPRs that represent function pointer - conversions stripped. */ +/* Return FN with any NOP_EXPRs stripped that represent function pointer + conversions or conversions to the same type. */ tree strip_fnptr_conv (tree fn) @@ -2029,7 +2029,10 @@ strip_fnptr_conv (tree fn) while (TREE_CODE (fn) == NOP_EXPR) { tree op = TREE_OPERAND (fn, 0); - if (fnptr_conv_p (TREE_TYPE (fn), TREE_TYPE (op))) + tree ft = TREE_TYPE (fn); + tree ot = TREE_TYPE (op); + if (same_type_p (ft, ot) + || fnptr_conv_p (ft, ot)) fn = op; else break; diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index b537cb8a85d..4d4484f919b 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -6430,6 +6430,8 @@ static tree convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain) { tree expr_type; + location_t loc = 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 @@ -6503,18 +6505,17 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain) argument for a parameter of pointer to member type, we just want to leave it in that form rather than lower it to a CONSTRUCTOR. */; - else if (INTEGRAL_OR_ENUMERATION_TYPE_P (type)) - /* Constant value checking is done later with type conversion. */; - else if (cxx_dialect >= cxx1z) + else if (INTEGRAL_OR_ENUMERATION_TYPE_P (type) + || cxx_dialect >= cxx1z) { - if (TREE_CODE (type) != REFERENCE_TYPE) - expr = maybe_constant_value (expr); - else if (REFERENCE_REF_P (expr)) - { - expr = TREE_OPERAND (expr, 0); - expr = maybe_constant_value (expr); - expr = convert_from_reference (expr); - } + /* C++17: A template-argument for a non-type template-parameter shall + be a converted constant expression (8.20) of the type of the + template-parameter. */ + expr = build_converted_constant_expr (type, expr, complain); + if (expr == error_mark_node) + return error_mark_node; + expr = maybe_constant_value (expr); + expr = convert_from_reference (expr); } else if (TYPE_PTR_OR_PTRMEM_P (type)) { @@ -6558,26 +6559,6 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain) } } - /* We could also generate a NOP_EXPR(ADDR_EXPR()) when the - parameter is a pointer to object, through decay and - qualification conversion. Let's strip everything. */ - else if (TREE_CODE (expr) == NOP_EXPR && TYPE_PTROBV_P (type)) - { - tree probe = expr; - STRIP_NOPS (probe); - if (TREE_CODE (probe) == ADDR_EXPR - && TYPE_PTR_P (TREE_TYPE (probe))) - { - /* Skip the ADDR_EXPR only if it is part of the decay for - an array. Otherwise, it is part of the original argument - in the source code. */ - if (TREE_CODE (TREE_TYPE (TREE_OPERAND (probe, 0))) == ARRAY_TYPE) - probe = TREE_OPERAND (probe, 0); - expr = probe; - expr_type = TREE_TYPE (expr); - } - } - /* [temp.arg.nontype]/5, bullet 1 For a non-type template-parameter of integral or enumeration type, @@ -6585,10 +6566,13 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain) (_conv.integral_) are applied. */ if (INTEGRAL_OR_ENUMERATION_TYPE_P (type)) { - tree t = build_integral_nontype_arg_conv (type, expr, complain); - t = maybe_constant_value (t); - if (t != error_mark_node) - expr = t; + if (cxx_dialect < cxx11) + { + tree t = build_converted_constant_expr (type, expr, complain); + t = maybe_constant_value (t); + if (t != error_mark_node) + expr = t; + } if (!same_type_ignoring_top_level_qualifiers_p (type, TREE_TYPE (expr))) return error_mark_node; @@ -6606,8 +6590,7 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain) return NULL_TREE; expr = cxx_constant_value (expr); if (errorcount > errs || warningcount + werrorcount > warns) - inform (EXPR_LOC_OR_LOC (expr, input_location), - "in template argument for type %qT ", type); + inform (loc, "in template argument for type %qT ", type); if (expr == error_mark_node) return NULL_TREE; /* else cxx_constant_value complained but gave us @@ -6629,6 +6612,23 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain) conversion (_conv.array_) are applied. */ else if (TYPE_PTROBV_P (type)) { + tree decayed = expr; + + /* Look through any NOP_EXPRs around an ADDR_EXPR, whether they come from + decay_conversion or an explicit cast. If it's a problematic cast, + we'll complain about it below. */ + if (TREE_CODE (expr) == NOP_EXPR) + { + tree probe = expr; + STRIP_NOPS (probe); + if (TREE_CODE (probe) == ADDR_EXPR + && TYPE_PTR_P (TREE_TYPE (probe))) + { + expr = probe; + expr_type = TREE_TYPE (expr); + } + } + /* [temp.arg.nontype]/1 (TC1 version, DR 49): A template-argument for a non-type, non-template template-parameter @@ -6648,15 +6648,14 @@ 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 - && TREE_CODE (expr_type) != ARRAY_TYPE) + 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", expr, expr); + "a variable", orig_expr, expr); return NULL_TREE; } if (POINTER_TYPE_P (expr_type)) @@ -6664,7 +6663,7 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain) 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); + orig_expr, type); return NULL_TREE; } /* Other values, like integer constants, might be valid @@ -6673,15 +6672,13 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain) } else { - tree decl; + tree decl = TREE_OPERAND (expr, 0); - decl = ((TREE_CODE (expr) == ADDR_EXPR) - ? TREE_OPERAND (expr, 0) : expr); 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); + "because %qE is not a variable", orig_expr, type, decl); return NULL_TREE; } else if (cxx_dialect < cxx11 && !DECL_EXTERNAL_LINKAGE_P (decl)) @@ -6689,7 +6686,7 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain) if (complain & tf_error) error ("%qE is not a valid template argument of type %qT " "because %qD does not have external linkage", - expr, type, decl); + orig_expr, type, decl); return NULL_TREE; } else if ((cxx_dialect >= cxx11 && cxx_dialect < cxx1z) @@ -6697,7 +6694,7 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain) { if (complain & tf_error) error ("%qE is not a valid template argument of type %qT " - "because %qD has no linkage", expr, type, decl); + "because %qD has no linkage", orig_expr, type, decl); return NULL_TREE; } /* C++17: For a non-type template-parameter of reference or pointer @@ -6734,9 +6731,7 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain) } } - expr = decay_conversion (expr, complain); - if (expr == error_mark_node) - return error_mark_node; + expr = decayed; expr = perform_qualification_conversions (type, expr); if (expr == error_mark_node) @@ -23858,7 +23853,7 @@ value_dependent_expression_p (tree expression) { tree t = TREE_OPERAND (expression, i); - /* In some cases, some of the operands may be missing.l + /* In some cases, some of the operands may be missing. (For example, in the case of PREDECREMENT_EXPR, the amount to increment by may be missing.) That doesn't make the expression dependent. */ diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-targ.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-targ.C index b5171141f94..98bb502249a 100644 --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-targ.C +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-targ.C @@ -10,4 +10,4 @@ struct B { }; constexpr A a { }; -B b; // { dg-error "template argument|invalid type" } +B b; // { dg-error "template argument|converted constant" } diff --git a/gcc/testsuite/g++.dg/template/crash106.C b/gcc/testsuite/g++.dg/template/crash106.C index 5bef101e598..8d972699900 100644 --- a/gcc/testsuite/g++.dg/template/crash106.C +++ b/gcc/testsuite/g++.dg/template/crash106.C @@ -11,4 +11,4 @@ template > struct B {}; // { dg-error "type| B<> b; // { dg-message "non-type" } -// { dg-prune-output "could not convert" } +// { dg-prune-output "(could not convert|no matches)" } diff --git a/gcc/testsuite/g++.dg/template/crash84.C b/gcc/testsuite/g++.dg/template/crash84.C index 103e90a7076..b3099d931c2 100644 --- a/gcc/testsuite/g++.dg/template/crash84.C +++ b/gcc/testsuite/g++.dg/template/crash84.C @@ -14,7 +14,7 @@ void foo () { a a1; // OK - a::b b1; // { dg-error "template argument" } + a::b b1; // { dg-error "template argument|converted constant" } } // { dg-prune-output "invalid type in declaration" } diff --git a/gcc/testsuite/g++.dg/template/crash87.C b/gcc/testsuite/g++.dg/template/crash87.C index 7b8bf4ada0d..af81edbfd80 100644 --- a/gcc/testsuite/g++.dg/template/crash87.C +++ b/gcc/testsuite/g++.dg/template/crash87.C @@ -17,7 +17,7 @@ template class BUG2 : BUG { public: - typedef BUG1_5 ptr; // { dg-error "could not convert template argument" } + typedef BUG1_5 ptr; // { dg-error "convert" } }; int main() diff --git a/gcc/testsuite/g++.dg/template/dependent-args1.C b/gcc/testsuite/g++.dg/template/dependent-args1.C index a540e55f4e5..8fffbf85317 100644 --- a/gcc/testsuite/g++.dg/template/dependent-args1.C +++ b/gcc/testsuite/g++.dg/template/dependent-args1.C @@ -10,4 +10,4 @@ template > struct B {}; B b; // { dg-error "type/value mismatch|expected a constant|invalid type" } -// { dg-prune-output "could not convert" } +// { dg-prune-output "(could not convert|no match)" } diff --git a/gcc/testsuite/g++.dg/template/nontype-array1.C b/gcc/testsuite/g++.dg/template/nontype-array1.C index cf21908481b..f22551b0f1a 100644 --- a/gcc/testsuite/g++.dg/template/nontype-array1.C +++ b/gcc/testsuite/g++.dg/template/nontype-array1.C @@ -10,17 +10,31 @@ constexpr char const s3[] = "hi"; // OK since C++11 constexpr char const * f() { return s3; } +using fn_p = char const * (*)(); +template struct A { }; +constexpr fn_p f2() { return f; } + +struct B +{ + constexpr B() { } + constexpr operator const char *() { return s3; } +}; + int main() { Message m1; // OK (all versions) Message m2; // OK for clang since C++14, for gcc since C++17 Message m3; // OK for clang/gcc since C++11 + A a1; // { dg-error "" "" { target c++14_down } } + static char const s4[] = "hi"; static constexpr char const s5[] = "hi"; // OK since C++11 Message m4; // { dg-error "no linkage" "" { target c++14_down } } Message m5; // { dg-error "no linkage" "" { target c++14_down } } Message m6; // { dg-error "" "" { target c++14_down } } + Message m7; // { dg-error "" "" { target c++14_down } } + char const s8[] = "hi"; Message m8; // { dg-error "" } diff --git a/gcc/testsuite/g++.dg/template/nontype13.C b/gcc/testsuite/g++.dg/template/nontype13.C index d604da94454..3250109aa4a 100644 --- a/gcc/testsuite/g++.dg/template/nontype13.C +++ b/gcc/testsuite/g++.dg/template/nontype13.C @@ -11,7 +11,7 @@ struct Dummy template void tester() { - bar()(); // { dg-error "constant|template" } + bar()(); // { dg-error "constant|template|convert" } } template struct bar diff --git a/gcc/testsuite/g++.dg/template/nontype21.C b/gcc/testsuite/g++.dg/template/nontype21.C index 69cab54f412..508f9090240 100644 --- a/gcc/testsuite/g++.dg/template/nontype21.C +++ b/gcc/testsuite/g++.dg/template/nontype21.C @@ -4,4 +4,4 @@ template class Something { }; extern char const xyz; -class SomethingElse:public Something { }; // { dg-error "xyz. is a variable" } +class SomethingElse:public Something { }; // { dg-error "" } diff --git a/gcc/testsuite/g++.dg/template/nontype26.C b/gcc/testsuite/g++.dg/template/nontype26.C index 763d987dd39..588ce1cc23a 100644 --- a/gcc/testsuite/g++.dg/template/nontype26.C +++ b/gcc/testsuite/g++.dg/template/nontype26.C @@ -7,7 +7,7 @@ template void doit() { template class X { public: void foo() { - doit(); // { dg-error "cv-qualification|no matching" } + doit(); // { dg-error "qualifi|template arg|no matching" } } }; diff --git a/gcc/testsuite/g++.dg/template/ptrmem20.C b/gcc/testsuite/g++.dg/template/ptrmem20.C index 2d2453d73db..e17ed84c739 100644 --- a/gcc/testsuite/g++.dg/template/ptrmem20.C +++ b/gcc/testsuite/g++.dg/template/ptrmem20.C @@ -12,5 +12,5 @@ template void bar(); // { dg-message "note" } void baz() { - bar<&B::foo>(); // { dg-error "not a valid template argument|no match" } + bar<&B::foo>(); // { dg-error "template argument|no match" } } diff --git a/gcc/testsuite/g++.dg/template/ptrmem8.C b/gcc/testsuite/g++.dg/template/ptrmem8.C index 85ffa4a6e8d..b759b7077d5 100644 --- a/gcc/testsuite/g++.dg/template/ptrmem8.C +++ b/gcc/testsuite/g++.dg/template/ptrmem8.C @@ -15,10 +15,8 @@ template int Get(); // { dg-message "note" } int main () { - Get<&B::I>(); // { dg-error "not a valid template argument" "not valid" } + Get<&B::I>(); // { dg-error "template argument|converted constant" "not valid" } // { dg-error "no match" "no match" { target *-*-* } .-1 } - // { dg-message "note" "note" { target *-*-* } .-2 } - Get<&D::I>(); // { dg-error "not a valid template argument" "not valid" } + Get<&D::I>(); // { dg-error "template argument|converted constant" "not valid" } // { dg-error "no match" "no match" { target *-*-* } .-1 } - // { dg-message "note" "note" { target *-*-* } .-2 } }