C++ PATCH for more N4268 bits, constant evaluation of all non-type args
Jason Merrill
jason@redhat.com
Sat Jun 10 00:39:00 GMT 2017
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.
-------------- next part --------------
commit 6cc0f94b412830c68f349f88c4c1384efd880fc3
Author: Jason Merrill <jason@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 <jason@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,
More information about the Gcc-patches
mailing list