My earlier patch for N4268 didn't implement the notion of converted constant-expression, and as a result we weren't properly using constexpr user-defined conversions to produce values for template arguments.
Always performing these conversions led to some issues with convert_nontype_argument getting confused by the trees built up to express the conversion, especially for pointers to members, where we would immediately decay the PTRMEM_CST to an INTEGER_CST even when converting to the same type. Changing that ended up requiring a variety of other changes in pointer-to-member handling. Tested x86_64-pc-linux-gnu, applying to trunk.
commit 6cc0f94b412830c68f349f88c4c1384efd880fc3 Author: Jason Merrill <ja...@redhat.com> Date: Tue Jun 6 18:25:26 2017 -0700 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. diff --git a/gcc/cp/call.c b/gcc/cp/call.c index a4b6a95..ef99683 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 07da0cd..6d4d937 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 3460e13..631ff49 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 b537cb8..4d4484f 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 b517114..98bb502 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<a> b; // { dg-error "template argument|invalid type" } +B<a> 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 5bef101..8d97269 100644 --- a/gcc/testsuite/g++.dg/template/crash106.C +++ b/gcc/testsuite/g++.dg/template/crash106.C @@ -11,4 +11,4 @@ template<T N = 0, void (A::*)() = &A::foo<N> > 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 103e90a..b3099d9 100644 --- a/gcc/testsuite/g++.dg/template/crash84.C +++ b/gcc/testsuite/g++.dg/template/crash84.C @@ -14,7 +14,7 @@ void foo () { a<int> a1; // OK - a<int>::b<a,int> b1; // { dg-error "template argument" } + a<int>::b<a,int> 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 7b8bf4a..af81edb 100644 --- a/gcc/testsuite/g++.dg/template/crash87.C +++ b/gcc/testsuite/g++.dg/template/crash87.C @@ -17,7 +17,7 @@ template <bool name> class BUG2 : BUG { public: - typedef BUG1_5<name> ptr; // { dg-error "could not convert template argument" } + typedef BUG1_5<name> 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 a540e55..8fffbf8 100644 --- a/gcc/testsuite/g++.dg/template/dependent-args1.C +++ b/gcc/testsuite/g++.dg/template/dependent-args1.C @@ -10,4 +10,4 @@ template<int N, void (A::*)() = &A::foo<N> > struct B {}; B<int> 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 cf21908..f22551b 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 <fn_p> struct A { }; +constexpr fn_p f2() { return f; } + +struct B +{ + constexpr B() { } + constexpr operator const char *() { return s3; } +}; + int main() { Message<s1> m1; // OK (all versions) Message<s2> m2; // OK for clang since C++14, for gcc since C++17 Message<s3> m3; // OK for clang/gcc since C++11 + A<f2()> a1; // { dg-error "" "" { target c++14_down } } + static char const s4[] = "hi"; static constexpr char const s5[] = "hi"; // OK since C++11 Message<s4> m4; // { dg-error "no linkage" "" { target c++14_down } } Message<s5> m5; // { dg-error "no linkage" "" { target c++14_down } } Message<f()> m6; // { dg-error "" "" { target c++14_down } } + Message<B{}> m7; // { dg-error "" "" { target c++14_down } } + char const s8[] = "hi"; Message<s8> m8; // { dg-error "" } diff --git a/gcc/testsuite/g++.dg/template/nontype13.C b/gcc/testsuite/g++.dg/template/nontype13.C index d604da9..3250109 100644 --- a/gcc/testsuite/g++.dg/template/nontype13.C +++ b/gcc/testsuite/g++.dg/template/nontype13.C @@ -11,7 +11,7 @@ struct Dummy template<bool B> void tester() { - bar<evil>()(); // { dg-error "constant|template" } + bar<evil>()(); // { dg-error "constant|template|convert" } } template<bool B> struct bar diff --git a/gcc/testsuite/g++.dg/template/nontype21.C b/gcc/testsuite/g++.dg/template/nontype21.C index 69cab54..508f909 100644 --- a/gcc/testsuite/g++.dg/template/nontype21.C +++ b/gcc/testsuite/g++.dg/template/nontype21.C @@ -4,4 +4,4 @@ template<char const * const x> class Something { }; extern char const xyz; -class SomethingElse:public Something<xyz> { }; // { dg-error "xyz. is a variable" } +class SomethingElse:public Something<xyz> { }; // { dg-error "" } diff --git a/gcc/testsuite/g++.dg/template/nontype26.C b/gcc/testsuite/g++.dg/template/nontype26.C index 763d987..588ce1c 100644 --- a/gcc/testsuite/g++.dg/template/nontype26.C +++ b/gcc/testsuite/g++.dg/template/nontype26.C @@ -7,7 +7,7 @@ template<int& i> void doit() { template<const int& i> class X { public: void foo() { - doit<i>(); // { dg-error "cv-qualification|no matching" } + doit<i>(); // { 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 2d2453d..e17ed84 100644 --- a/gcc/testsuite/g++.dg/template/ptrmem20.C +++ b/gcc/testsuite/g++.dg/template/ptrmem20.C @@ -12,5 +12,5 @@ template<void (A::*)()> 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 85ffa4a..b759b70 100644 --- a/gcc/testsuite/g++.dg/template/ptrmem8.C +++ b/gcc/testsuite/g++.dg/template/ptrmem8.C @@ -15,10 +15,8 @@ template <int (D::*fun)() const> 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 } } commit 709f012d7340a8472fa40567c12a750cb7922e09 Author: Jason Merrill <ja...@redhat.com> Date: Fri Jun 9 16:16:20 2017 -0700 Overhaul pointer-to-member conversion and template argument handling. * call.c (standard_conversion): Avoid creating ck_pmem when the class type is the same. * cvt.c (can_convert_qual): Split from perform_qualification_conversions. * constexpr.c (cxx_eval_constant_expression): Check it. * typeck.c (convert_ptrmem): Only cplus_expand_constant if adjustment is necessary. * pt.c (check_valid_ptrmem_cst_expr): Compare class types. (convert_nontype_argument): Avoid redundant error. diff --git a/gcc/cp/call.c b/gcc/cp/call.c index 5e65bfb..a4b6a95 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -1262,14 +1262,16 @@ standard_conversion (tree to, tree from, tree expr, bool c_cast_p, tree fbase = TYPE_PTRMEM_CLASS_TYPE (from); tree tbase = TYPE_PTRMEM_CLASS_TYPE (to); - if (DERIVED_FROM_P (fbase, tbase) - && (same_type_ignoring_top_level_qualifiers_p - (from_pointee, to_pointee))) + if (same_type_p (fbase, tbase)) + /* No base conversion needed. */; + else if (DERIVED_FROM_P (fbase, tbase) + && (same_type_ignoring_top_level_qualifiers_p + (from_pointee, to_pointee))) { from = build_ptrmem_type (tbase, from_pointee); conv = build_conv (ck_pmem, from, conv); } - else if (!same_type_p (fbase, tbase)) + else return NULL; } else if (CLASS_TYPE_P (from_pointee) diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index 8bbe950..ae24e40 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -4399,7 +4399,8 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, if (TREE_CODE (op) == PTRMEM_CST && tcode == NOP_EXPR) { if (same_type_ignoring_top_level_qualifiers_p (type, - TREE_TYPE (op))) + TREE_TYPE (op)) + || can_convert_qual (type, op)) return cp_fold_convert (type, op); else { diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 550dbf2..07da0cd 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -6022,6 +6022,7 @@ extern tree convert_force (tree, tree, int, tsubst_flags_t); extern tree build_expr_type_conversion (int, tree, bool); extern tree type_promotes_to (tree); +extern bool can_convert_qual (tree, tree); extern tree perform_qualification_conversions (tree, tree); extern bool tx_safe_fn_type_p (tree); extern tree tx_unsafe_fn_variant (tree); diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c index 6b28ef6..3460e13 100644 --- a/gcc/cp/cvt.c +++ b/gcc/cp/cvt.c @@ -1890,6 +1890,26 @@ type_promotes_to (tree type) closely. Although they are used only in pt.c at the moment, they should presumably be used everywhere in the future. */ +/* True iff EXPR can be converted to TYPE via a qualification conversion. + Callers should check for identical types before calling this function. */ + +bool +can_convert_qual (tree type, tree expr) +{ + tree expr_type = TREE_TYPE (expr); + gcc_assert (!same_type_p (type, expr_type)); + + if (TYPE_PTR_P (type) && TYPE_PTR_P (expr_type)) + return comp_ptr_ttypes (TREE_TYPE (type), TREE_TYPE (expr_type)); + else if (TYPE_PTRMEM_P (type) && TYPE_PTRMEM_P (expr_type)) + return (same_type_p (TYPE_PTRMEM_CLASS_TYPE (type), + TYPE_PTRMEM_CLASS_TYPE (expr_type)) + && comp_ptr_ttypes (TYPE_PTRMEM_POINTED_TO_TYPE (type), + TYPE_PTRMEM_POINTED_TO_TYPE (expr_type))); + else + return false; +} + /* Attempt to perform qualification conversions on EXPR to convert it to TYPE. Return the resulting expression, or error_mark_node if the conversion was impossible. */ @@ -1903,14 +1923,7 @@ perform_qualification_conversions (tree type, tree expr) if (same_type_p (type, expr_type)) return expr; - else if (TYPE_PTR_P (type) && TYPE_PTR_P (expr_type) - && comp_ptr_ttypes (TREE_TYPE (type), TREE_TYPE (expr_type))) - return build_nop (type, expr); - else if (TYPE_PTRMEM_P (type) && TYPE_PTRMEM_P (expr_type) - && same_type_p (TYPE_PTRMEM_CLASS_TYPE (type), - TYPE_PTRMEM_CLASS_TYPE (expr_type)) - && comp_ptr_ttypes (TYPE_PTRMEM_POINTED_TO_TYPE (type), - TYPE_PTRMEM_POINTED_TO_TYPE (expr_type))) + else if (can_convert_qual (type, expr)) return build_nop (type, expr); else return error_mark_node; diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 40be3c1..b537cb8 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -6124,8 +6124,14 @@ static bool check_valid_ptrmem_cst_expr (tree type, tree expr, tsubst_flags_t complain) { + location_t loc = EXPR_LOC_OR_LOC (expr, input_location); + tree orig_expr = expr; STRIP_NOPS (expr); - if (expr && (null_ptr_cst_p (expr) || TREE_CODE (expr) == PTRMEM_CST)) + if (null_ptr_cst_p (expr)) + return true; + if (TREE_CODE (expr) == PTRMEM_CST + && same_type_p (TYPE_PTRMEM_CLASS_TYPE (type), + PTRMEM_CST_CLASS (expr))) return true; if (cxx_dialect >= cxx11 && null_member_pointer_value_p (expr)) return true; @@ -6135,9 +6141,12 @@ check_valid_ptrmem_cst_expr (tree type, tree expr, return true; if (complain & tf_error) { - error ("%qE is not a valid template argument for type %qT", - expr, type); - error ("it must be a pointer-to-member of the form %<&X::Y%>"); + error_at (loc, "%qE is not a valid template argument for type %qT", + orig_expr, type); + if (TREE_CODE (expr) != PTRMEM_CST) + inform (loc, "it must be a pointer-to-member of the form %<&X::Y%>"); + else + inform (loc, "because it is a member of %qT", PTRMEM_CST_CLASS (expr)); } return false; } @@ -6880,36 +6889,12 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain) expression must be a pointer-to-member constant. */ if (!value_dependent_expression_p (expr) && !check_valid_ptrmem_cst_expr (type, expr, complain)) - return error_mark_node; + return NULL_TREE; /* Repeated conversion can't deal with a conversion that turns PTRMEM_CST into a CONSTRUCTOR, so build up a new PTRMEM_CST instead. */ if (fnptr_conv_p (type, TREE_TYPE (expr))) expr = make_ptrmem_cst (type, PTRMEM_CST_MEMBER (expr)); - - /* There is no way to disable standard conversions in - resolve_address_of_overloaded_function (called by - instantiate_type). It is possible that the call succeeded by - converting &B::I to &D::I (where B is a base of D), so we need - to reject this conversion here. - - Actually, even if there was a way to disable standard conversions, - it would still be better to reject them here so that we can - provide a superior diagnostic. */ - if (!same_type_p (TREE_TYPE (expr), type)) - { - if (complain & tf_error) - { - error ("%qE is not a valid template argument for type %qT " - "because it is of type %qT", expr, type, - TREE_TYPE (expr)); - /* If we are just one standard conversion off, explain. */ - if (can_convert_standard (type, TREE_TYPE (expr), complain)) - inform (input_location, - "standard conversions are not allowed in this context"); - } - return NULL_TREE; - } } /* [temp.arg.nontype]/5, bullet 7 @@ -6921,7 +6906,7 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain) expression must be a pointer-to-member constant. */ if (!value_dependent_expression_p (expr) && !check_valid_ptrmem_cst_expr (type, expr, complain)) - return error_mark_node; + return NULL_TREE; expr = perform_qualification_conversions (type, expr); if (expr == error_mark_node) diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c index 334a6f5..34d475b 100644 --- a/gcc/cp/typeck.c +++ b/gcc/cp/typeck.c @@ -6710,12 +6710,13 @@ tree convert_ptrmem (tree type, tree expr, bool allow_inverse_p, bool c_cast_p, tsubst_flags_t complain) { + if (same_type_p (type, TREE_TYPE (expr))) + return expr; + if (TYPE_PTRDATAMEM_P (type)) { tree delta; - if (TREE_CODE (expr) == PTRMEM_CST) - expr = cplus_expand_constant (expr); delta = get_delta_difference (TYPE_PTRMEM_CLASS_TYPE (TREE_TYPE (expr)), TYPE_PTRMEM_CLASS_TYPE (type), allow_inverse_p, @@ -6727,6 +6728,8 @@ convert_ptrmem (tree type, tree expr, bool allow_inverse_p, { tree cond, op1, op2; + if (TREE_CODE (expr) == PTRMEM_CST) + expr = cplus_expand_constant (expr); cond = cp_build_binary_op (input_location, EQ_EXPR, expr,