The gist of the issue is that with -std=c++03 or earlier a NON_DEPENDENT_EXPR is considered to always be an lvalue according to lvalue_kind(). This causes overload resolution to malfunction during template processing because it sometimes chooses an overload that expects an lvalue argument even though the expression underlying the NON_DEPENDENT_EXPR is not an lvalue. My patch for PR c++/21802 exacerbated this issue by having operator overload resolution get performed only once, at template processing time (where we see NON_DEPENDENT_EXPRs), instead of twice, at template processing time and then at instantiation time (where we don't see NON_DEPENDENT_EXPRs). The issue though is not restricted to operator overloads, it also manifests itself in regular function overloads (see pr70610-3.C which currently fails with an ambiguous overload error).
>From what I can tell the special casing done in lvalue_kind() for older cxx_dialects is no longer necessary, so this patch removes it to have lvalue_kind() unconditionally recurse into NON_DEPENDENT_EXPRs. This however caused a c++98 regression in g++.dg/template/lvalue2.C which is fixed by removing the guard against cxx_dialect in build_x_conditional_expr which also no longer seems necessary. Although I haven't looked too carefully... Bootstrapped + regtested on x86_64-pc-linux-gnu, does this look OK to commit? gcc/cp/ChangeLog: PR c++/70610 * tree.c (lvalue_kind) [NON_DEPENDENT_EXPR]: Unconditionally recurse into it. * typeck.c (build_x_conditional_expr): Unconditionally remember that the result is an lvalue or xvalue. gcc/testsuite/ChangeLog: PR c++/70610 * g++.dg/template/pr70610.C: New test. * g++.dg/template/pr70610-2.C: New test. * g++.dg/template/pr70610-3.C: New test. * g++.dg/template/pr70610-4.C: New test. --- gcc/cp/tree.c | 8 +------- gcc/cp/typeck.c | 6 ++---- gcc/testsuite/g++.dg/template/pr70610-2.C | 21 +++++++++++++++++++++ gcc/testsuite/g++.dg/template/pr70610-3.C | 21 +++++++++++++++++++++ gcc/testsuite/g++.dg/template/pr70610-4.C | 19 +++++++++++++++++++ gcc/testsuite/g++.dg/template/pr70610.C | 21 +++++++++++++++++++++ 6 files changed, 85 insertions(+), 11 deletions(-) create mode 100644 gcc/testsuite/g++.dg/template/pr70610-2.C create mode 100644 gcc/testsuite/g++.dg/template/pr70610-3.C create mode 100644 gcc/testsuite/g++.dg/template/pr70610-4.C create mode 100644 gcc/testsuite/g++.dg/template/pr70610.C diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c index 5d9de34..df2981f 100644 --- a/gcc/cp/tree.c +++ b/gcc/cp/tree.c @@ -224,13 +224,7 @@ lvalue_kind (const_tree ref) return lvalue_kind (BASELINK_FUNCTIONS (CONST_CAST_TREE (ref))); case NON_DEPENDENT_EXPR: - /* We just return clk_ordinary for NON_DEPENDENT_EXPR in C++98, but - in C++11 lvalues don't bind to rvalue references, so we need to - work harder to avoid bogus errors (c++/44870). */ - if (cxx_dialect < cxx11) - return clk_ordinary; - else - return lvalue_kind (TREE_OPERAND (ref, 0)); + return lvalue_kind (TREE_OPERAND (ref, 0)); default: if (!TREE_TYPE (ref)) diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c index 4dc1b55..a62bae4 100644 --- a/gcc/cp/typeck.c +++ b/gcc/cp/typeck.c @@ -6275,10 +6275,8 @@ build_x_conditional_expr (location_t loc, tree ifexp, tree op1, tree op2, { tree min = build_min_non_dep (COND_EXPR, expr, orig_ifexp, orig_op1, orig_op2); - /* In C++11, remember that the result is an lvalue or xvalue. - In C++98, lvalue_kind can just assume lvalue in a template. */ - if (cxx_dialect >= cxx11 - && lvalue_or_rvalue_with_address_p (expr) + /* Remember that the result is an lvalue or xvalue. */ + if (lvalue_or_rvalue_with_address_p (expr) && !lvalue_or_rvalue_with_address_p (min)) TREE_TYPE (min) = cp_build_reference_type (TREE_TYPE (min), !real_lvalue_p (expr)); diff --git a/gcc/testsuite/g++.dg/template/pr70610-2.C b/gcc/testsuite/g++.dg/template/pr70610-2.C new file mode 100644 index 0000000..3368a5e --- /dev/null +++ b/gcc/testsuite/g++.dg/template/pr70610-2.C @@ -0,0 +1,21 @@ +// PR c++/70610 +// { dg-do link } + +struct A { }; + +void operator+ (const A &, A &); +void operator+ (A &, const A &); +void operator+ (const A &, const A &) { } + +template <typename T> +void +foo () +{ + A () + A (); +} + +int +main () +{ + foo<int> (); +} diff --git a/gcc/testsuite/g++.dg/template/pr70610-3.C b/gcc/testsuite/g++.dg/template/pr70610-3.C new file mode 100644 index 0000000..4be458c --- /dev/null +++ b/gcc/testsuite/g++.dg/template/pr70610-3.C @@ -0,0 +1,21 @@ +// PR c++/70610 +// { dg-do link } + +void bar (const int &, int &); +void bar (int &, const int &); +void bar (const int &, const int &) { } + +int a, b; + +template <typename T> +void +foo () +{ + bar (a + 1, b + 2); +} + +int +main () +{ + foo<int> (); +} diff --git a/gcc/testsuite/g++.dg/template/pr70610-4.C b/gcc/testsuite/g++.dg/template/pr70610-4.C new file mode 100644 index 0000000..127abdc --- /dev/null +++ b/gcc/testsuite/g++.dg/template/pr70610-4.C @@ -0,0 +1,19 @@ +// PR c++/70610 +// { dg-do link } + +struct A { void operator+ (const A &) { }; }; + +void operator+ (const A &, A &); + +template <typename T> +void +foo () +{ + A () + A (); +} + +int +main () +{ + foo<int> (); +} diff --git a/gcc/testsuite/g++.dg/template/pr70610.C b/gcc/testsuite/g++.dg/template/pr70610.C new file mode 100644 index 0000000..c7dde1c --- /dev/null +++ b/gcc/testsuite/g++.dg/template/pr70610.C @@ -0,0 +1,21 @@ +// PR c++/70610 +// { dg-do link } + +struct A { }; + +void operator+ (A &); +void operator+ (const A &) { } + + +template <typename T> +void +foo () +{ + +A (); +} + +int +main () +{ + foo<int> (); +} -- 2.8.1.135.ga04ac66