Commit 23d63b45 by Alexandre Oliva Committed by Alexandre Oliva

[C++] [PR84231] overload on cond_expr in template

A non-type-dependent COND_EXPR within a template is reconstructed with
the original operands, after one with non-dependent proxies is built to
determine its result type.  This is problematic because the operands of
a COND_EXPR determined to be an rvalue may have been converted to denote
their rvalue nature.  The reconstructed one, however, won't have such
conversions, so lvalue_kind may not recognize it as an rvalue, which may
lead to e.g. incorrect overload resolution decisions.

If we mistake such a COND_EXPR for an lvalue, overload resolution might
regard a conversion sequence that binds it to a non-const reference as
viable, and then select that over one that binds it to a const
reference.  Only after template substitution would we rebuild the
COND_EXPR, realize it is an rvalue, and conclude the reference binding
is ill-formed, but at that point we'd have long discarded any alternate
candidates we could have used.

This patch modifies the logic that determines whether a
(non-type-dependent) COND_EXPR in a template is an lvalue, to rely on
its type, more specifically, on the presence of a REFERENCE_TYPE
wrapper.  In order to avoid a type bootstrapping problem, the
REFERENCE_TYPE that wraps the type of some such COND_EXPRs is
introduced earlier, so that we don't have to test for lvalueness of
the expression using the very code that we wish to change.


for  gcc/cp/ChangeLog

	PR c++/84231
	* tree.c (lvalue_kind): Use presence/absence of REFERENCE_TYPE
	only while processing template decls.
	* typeck.c (build_x_conditional_expr): Move wrapping of
	reference type around type...
	* call.c (build_conditional_expr_1): ... here.  Rename
	is_lvalue to is_glvalue.
	* parser.c (cp_parser_fold_expression): Catch REFERENCE_REF_P
	INDIRECT_REF of COND_EXPR too.

for  gcc/testsuite/ChangeLog

	PR c++/84231
	* g++.dg/pr84231.C: New.

From-SVN: r258271
parent 2e1a7ecb
2018-03-06 Alexandre Oliva <aoliva@redhat.com> 2018-03-06 Alexandre Oliva <aoliva@redhat.com>
PR c++/84231
* tree.c (lvalue_kind): Use presence/absence of REFERENCE_TYPE
only while processing template decls.
* typeck.c (build_x_conditional_expr): Move wrapping of
reference type around type...
* call.c (build_conditional_expr_1): ... here. Rename
is_lvalue to is_glvalue.
* parser.c (cp_parser_fold_expression): Catch REFERENCE_REF_P
INDIRECT_REF of COND_EXPR too.
PR c++/84593 PR c++/84593
* init.c (build_zero_init_1): Zero-initialize references. * init.c (build_zero_init_1): Zero-initialize references.
......
...@@ -4782,7 +4782,7 @@ build_conditional_expr_1 (location_t loc, tree arg1, tree arg2, tree arg3, ...@@ -4782,7 +4782,7 @@ build_conditional_expr_1 (location_t loc, tree arg1, tree arg2, tree arg3,
tree arg3_type; tree arg3_type;
tree result = NULL_TREE; tree result = NULL_TREE;
tree result_type = NULL_TREE; tree result_type = NULL_TREE;
bool is_lvalue = true; bool is_glvalue = true;
struct z_candidate *candidates = 0; struct z_candidate *candidates = 0;
struct z_candidate *cand; struct z_candidate *cand;
void *p; void *p;
...@@ -5037,7 +5037,7 @@ build_conditional_expr_1 (location_t loc, tree arg1, tree arg2, tree arg3, ...@@ -5037,7 +5037,7 @@ build_conditional_expr_1 (location_t loc, tree arg1, tree arg2, tree arg3,
return error_mark_node; return error_mark_node;
} }
is_lvalue = false; is_glvalue = false;
goto valid_operands; goto valid_operands;
} }
/* [expr.cond] /* [expr.cond]
...@@ -5155,6 +5155,10 @@ build_conditional_expr_1 (location_t loc, tree arg1, tree arg2, tree arg3, ...@@ -5155,6 +5155,10 @@ build_conditional_expr_1 (location_t loc, tree arg1, tree arg2, tree arg3,
&& same_type_p (arg2_type, arg3_type)) && same_type_p (arg2_type, arg3_type))
{ {
result_type = arg2_type; result_type = arg2_type;
if (processing_template_decl)
/* Let lvalue_kind know this was a glvalue. */
result_type = cp_build_reference_type (result_type, xvalue_p (arg2));
arg2 = mark_lvalue_use (arg2); arg2 = mark_lvalue_use (arg2);
arg3 = mark_lvalue_use (arg3); arg3 = mark_lvalue_use (arg3);
goto valid_operands; goto valid_operands;
...@@ -5167,7 +5171,7 @@ build_conditional_expr_1 (location_t loc, tree arg1, tree arg2, tree arg3, ...@@ -5167,7 +5171,7 @@ build_conditional_expr_1 (location_t loc, tree arg1, tree arg2, tree arg3,
cv-qualified) class type, overload resolution is used to cv-qualified) class type, overload resolution is used to
determine the conversions (if any) to be applied to the operands determine the conversions (if any) to be applied to the operands
(_over.match.oper_, _over.built_). */ (_over.match.oper_, _over.built_). */
is_lvalue = false; is_glvalue = false;
if (!same_type_p (arg2_type, arg3_type) if (!same_type_p (arg2_type, arg3_type)
&& (CLASS_TYPE_P (arg2_type) || CLASS_TYPE_P (arg3_type))) && (CLASS_TYPE_P (arg2_type) || CLASS_TYPE_P (arg3_type)))
{ {
...@@ -5361,7 +5365,7 @@ build_conditional_expr_1 (location_t loc, tree arg1, tree arg2, tree arg3, ...@@ -5361,7 +5365,7 @@ build_conditional_expr_1 (location_t loc, tree arg1, tree arg2, tree arg3,
/* We can't use result_type below, as fold might have returned a /* We can't use result_type below, as fold might have returned a
throw_expr. */ throw_expr. */
if (!is_lvalue) if (!is_glvalue)
{ {
/* Expand both sides into the same slot, hopefully the target of /* Expand both sides into the same slot, hopefully the target of
the ?: expression. We used to check for TARGET_EXPRs here, the ?: expression. We used to check for TARGET_EXPRs here,
......
...@@ -4963,7 +4963,9 @@ cp_parser_fold_expression (cp_parser *parser, tree expr1) ...@@ -4963,7 +4963,9 @@ cp_parser_fold_expression (cp_parser *parser, tree expr1)
else if (is_binary_op (TREE_CODE (expr1))) else if (is_binary_op (TREE_CODE (expr1)))
error_at (location_of (expr1), error_at (location_of (expr1),
"binary expression in operand of fold-expression"); "binary expression in operand of fold-expression");
else if (TREE_CODE (expr1) == COND_EXPR) else if (TREE_CODE (expr1) == COND_EXPR
|| (REFERENCE_REF_P (expr1)
&& TREE_CODE (TREE_OPERAND (expr1, 0)) == COND_EXPR))
error_at (location_of (expr1), error_at (location_of (expr1),
"conditional expression in operand of fold-expression"); "conditional expression in operand of fold-expression");
...@@ -194,6 +194,21 @@ lvalue_kind (const_tree ref) ...@@ -194,6 +194,21 @@ lvalue_kind (const_tree ref)
break; break;
case COND_EXPR: case COND_EXPR:
if (processing_template_decl)
{
/* Within templates, a REFERENCE_TYPE will indicate whether
the COND_EXPR result is an ordinary lvalue or rvalueref.
Since REFERENCE_TYPEs are handled above, if we reach this
point, we know we got a plain rvalue. Unless we have a
type-dependent expr, that is, but we shouldn't be testing
lvalueness if we can't even tell the types yet! */
gcc_assert (!type_dependent_expression_p (CONST_CAST_TREE (ref)));
if (CLASS_TYPE_P (TREE_TYPE (ref))
|| TREE_CODE (TREE_TYPE (ref)) == ARRAY_TYPE)
return clk_class;
else
return clk_none;
}
op1_lvalue_kind = lvalue_kind (TREE_OPERAND (ref, 1) op1_lvalue_kind = lvalue_kind (TREE_OPERAND (ref, 1)
? TREE_OPERAND (ref, 1) ? TREE_OPERAND (ref, 1)
: TREE_OPERAND (ref, 0)); : TREE_OPERAND (ref, 0));
......
...@@ -6567,10 +6567,6 @@ build_x_conditional_expr (location_t loc, tree ifexp, tree op1, tree op2, ...@@ -6567,10 +6567,6 @@ build_x_conditional_expr (location_t loc, tree ifexp, tree op1, tree op2,
{ {
tree min = build_min_non_dep (COND_EXPR, expr, tree min = build_min_non_dep (COND_EXPR, expr,
orig_ifexp, orig_op1, orig_op2); orig_ifexp, orig_op1, orig_op2);
/* Remember that the result is an lvalue or xvalue. */
if (glvalue_p (expr) && !glvalue_p (min))
TREE_TYPE (min) = cp_build_reference_type (TREE_TYPE (min),
!lvalue_p (expr));
expr = convert_from_reference (min); expr = convert_from_reference (min);
} }
return expr; return expr;
......
2018-03-06 Alexandre Oliva <aoliva@redhat.com> 2018-03-06 Alexandre Oliva <aoliva@redhat.com>
PR c++/84231
* g++.dg/pr84231.C: New.
PR c++/84593 PR c++/84593
* g++.dg/cpp1y/pr84593.C: New. * g++.dg/cpp1y/pr84593.C: New.
......
// PR c++/84231 - overload resolution with cond_expr in a template
// { dg-do compile }
struct format {
template<typename T> format& operator%(const T&) { return *this; }
template<typename T> format& operator%(T&) { return *this; }
};
format f;
template <typename>
void function_template(bool b)
{
// Compiles OK with array lvalue:
f % (b ? "x" : "x");
// Used to fails with pointer rvalue:
f % (b ? "" : "x");
}
void normal_function(bool b)
{
// Both cases compile OK in non-template function:
f % (b ? "x" : "x");
f % (b ? "" : "x");
function_template<void>(b);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment