c++: non-dependent assignment checking [PR63198, PR18474]
This patch makes us recognize and check non-dependent simple assigments ahead of time, like we already do for compound assignments. This means the templated representation of such assignments will now usually have an implicit INDIRECT_REF (due to the reference return type), which the -Wparentheses code needs to handle. As a drive-by improvement, this patch also makes maybe_convert_cond issue -Wparentheses warnings ahead of time, and removes a seemingly unnecessary suppress_warning call in build_x_modify_expr. On the libstdc++ side, some tests were attempting to modify a data member from a uninstantiated const member function, which this patch minimally fixes by making the data member mutable. PR c++/63198 PR c++/18474 gcc/cp/ChangeLog: * semantics.cc (maybe_convert_cond): Look through implicit INDIRECT_REF when deciding whether to issue a -Wparentheses warning, and consider templated assignment expressions as well. (finish_parenthesized_expr): Look through implicit INDIRECT_REF when suppressing -Wparentheses warning. * typeck.cc (build_x_modify_expr): Check simple assignments ahead time too, not just compound assignments. Give the second operand of MODOP_EXPR a non-null type so that it's not considered always instantiation-dependent. Don't call suppress_warning. gcc/testsuite/ChangeLog: * g++.dg/cpp0x/static_assert15.C: Expect diagnostic for non-constant static_assert condition. * g++.dg/expr/unary2.C: Remove xfails. * g++.dg/template/init7.C: Make initializer type-dependent to preserve intent of test. * g++.dg/template/recurse3.C: Likewise for the erroneous statement. * g++.dg/template/non-dependent26.C: New test. * g++.dg/warn/Wparentheses-32.C: New test. libstdc++-v3/ChangeLog: * testsuite/26_numerics/random/discard_block_engine/cons/seed_seq2.cc: Make data member seed_seq::called mutable. * testsuite/26_numerics/random/independent_bits_engine/cons/seed_seq2.cc: Likewise. * testsuite/26_numerics/random/linear_congruential_engine/cons/seed_seq2.cc: Likewise. * testsuite/26_numerics/random/mersenne_twister_engine/cons/seed_seq2.cc: Likewise. * testsuite/26_numerics/random/shuffle_order_engine/cons/seed_seq2.cc: Likewise. * testsuite/26_numerics/random/subtract_with_carry_engine/cons/seed_seq2.cc: Likewise. * testsuite/ext/random/simd_fast_mersenne_twister_engine/cons/seed_seq2.cc: Likewise.
This commit is contained in:
parent
a6ac1fc64c
commit
6e92a6a2a7
15 changed files with 100 additions and 47 deletions
|
@ -881,13 +881,17 @@ maybe_convert_cond (tree cond)
|
|||
/* Do the conversion. */
|
||||
cond = convert_from_reference (cond);
|
||||
|
||||
if ((TREE_CODE (cond) == MODIFY_EXPR || is_assignment_op_expr_p (cond))
|
||||
tree inner = REFERENCE_REF_P (cond) ? TREE_OPERAND (cond, 0) : cond;
|
||||
if ((TREE_CODE (inner) == MODIFY_EXPR
|
||||
|| (TREE_CODE (inner) == MODOP_EXPR
|
||||
&& TREE_CODE (TREE_OPERAND (inner, 1)) == NOP_EXPR)
|
||||
|| is_assignment_op_expr_p (inner))
|
||||
&& warn_parentheses
|
||||
&& !warning_suppressed_p (cond, OPT_Wparentheses)
|
||||
&& warning_at (cp_expr_loc_or_input_loc (cond),
|
||||
&& !warning_suppressed_p (inner, OPT_Wparentheses)
|
||||
&& warning_at (cp_expr_loc_or_input_loc (inner),
|
||||
OPT_Wparentheses, "suggest parentheses around "
|
||||
"assignment used as truth value"))
|
||||
suppress_warning (cond, OPT_Wparentheses);
|
||||
suppress_warning (inner, OPT_Wparentheses);
|
||||
|
||||
return condition_conversion (cond);
|
||||
}
|
||||
|
@ -2155,8 +2159,11 @@ cp_expr
|
|||
finish_parenthesized_expr (cp_expr expr)
|
||||
{
|
||||
if (EXPR_P (expr))
|
||||
/* This inhibits warnings in c_common_truthvalue_conversion. */
|
||||
suppress_warning (expr, OPT_Wparentheses);
|
||||
{
|
||||
/* This inhibits warnings in c_common_truthvalue_conversion. */
|
||||
tree inner = REFERENCE_REF_P (expr) ? TREE_OPERAND (expr, 0) : *expr;
|
||||
suppress_warning (inner, OPT_Wparentheses);
|
||||
}
|
||||
|
||||
if (TREE_CODE (expr) == OFFSET_REF
|
||||
|| TREE_CODE (expr) == SCOPE_REF)
|
||||
|
|
|
@ -9721,13 +9721,13 @@ build_x_modify_expr (location_t loc, tree lhs, enum tree_code modifycode,
|
|||
if (lhs == error_mark_node || rhs == error_mark_node)
|
||||
return cp_expr (error_mark_node, loc);
|
||||
|
||||
tree op = build_min (modifycode, void_type_node, NULL_TREE, NULL_TREE);
|
||||
|
||||
if (processing_template_decl)
|
||||
{
|
||||
if (modifycode == NOP_EXPR
|
||||
|| type_dependent_expression_p (lhs)
|
||||
if (type_dependent_expression_p (lhs)
|
||||
|| type_dependent_expression_p (rhs))
|
||||
{
|
||||
tree op = build_min_nt_loc (loc, modifycode, NULL_TREE, NULL_TREE);
|
||||
tree rval = build_min_nt_loc (loc, MODOP_EXPR, lhs, op, rhs);
|
||||
if (modifycode != NOP_EXPR)
|
||||
TREE_TYPE (rval)
|
||||
|
@ -9739,29 +9739,24 @@ build_x_modify_expr (location_t loc, tree lhs, enum tree_code modifycode,
|
|||
rhs = build_non_dependent_expr (rhs);
|
||||
}
|
||||
|
||||
if (modifycode != NOP_EXPR)
|
||||
tree rval;
|
||||
if (modifycode == NOP_EXPR)
|
||||
rval = cp_build_modify_expr (loc, lhs, modifycode, rhs, complain);
|
||||
else
|
||||
rval = build_new_op (loc, MODIFY_EXPR, LOOKUP_NORMAL,
|
||||
lhs, rhs, op, lookups, &overload, complain);
|
||||
if (rval == error_mark_node)
|
||||
return error_mark_node;
|
||||
if (processing_template_decl)
|
||||
{
|
||||
tree op = build_nt (modifycode, NULL_TREE, NULL_TREE);
|
||||
tree rval = build_new_op (loc, MODIFY_EXPR, LOOKUP_NORMAL,
|
||||
lhs, rhs, op, lookups, &overload, complain);
|
||||
if (rval)
|
||||
{
|
||||
if (rval == error_mark_node)
|
||||
return rval;
|
||||
suppress_warning (rval /* What warning? */);
|
||||
if (processing_template_decl)
|
||||
{
|
||||
if (overload != NULL_TREE)
|
||||
return (build_min_non_dep_op_overload
|
||||
(MODIFY_EXPR, rval, overload, orig_lhs, orig_rhs));
|
||||
if (overload != NULL_TREE)
|
||||
return (build_min_non_dep_op_overload
|
||||
(MODIFY_EXPR, rval, overload, orig_lhs, orig_rhs));
|
||||
|
||||
return (build_min_non_dep
|
||||
(MODOP_EXPR, rval, orig_lhs, op, orig_rhs));
|
||||
}
|
||||
return rval;
|
||||
}
|
||||
return (build_min_non_dep
|
||||
(MODOP_EXPR, rval, orig_lhs, op, orig_rhs));
|
||||
}
|
||||
return cp_build_modify_expr (loc, lhs, modifycode, rhs, complain);
|
||||
return rval;
|
||||
}
|
||||
|
||||
/* Helper function for get_delta_difference which assumes FROM is a base
|
||||
|
|
|
@ -5,6 +5,6 @@ template<int x>
|
|||
struct a {
|
||||
constexpr void b() {
|
||||
int c;
|
||||
static_assert(c %= 1, "");
|
||||
static_assert(c %= 1, ""); // { dg-error "constant" }
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
// PR c++/18474
|
||||
// { dg-do compile }
|
||||
// Unary plus/minus are not lvalues.
|
||||
|
||||
// In templates we require an instantiation to emit the diagnostic. This
|
||||
// is wrong and it is PR 18474.
|
||||
|
||||
int n;
|
||||
|
||||
void f(void)
|
||||
|
@ -15,6 +13,6 @@ void f(void)
|
|||
template <int>
|
||||
void g(void)
|
||||
{
|
||||
-n = 0; // { dg-error "lvalue" "" { xfail *-*-* } }
|
||||
+n = 0; // { dg-error "lvalue" "" { xfail *-*-* } }
|
||||
-n = 0; // { dg-error "lvalue" "" }
|
||||
+n = 0; // { dg-error "lvalue" "" }
|
||||
}
|
||||
|
|
|
@ -6,4 +6,4 @@ template<typename> struct A
|
|||
static const int i=0;
|
||||
};
|
||||
|
||||
template<typename T> const int A<T>::i = 0=0; /* { dg-error "duplicate initialization" } */
|
||||
template<typename T> const int A<T>::i = T()=0; /* { dg-error "duplicate initialization" } */
|
||||
|
|
25
gcc/testsuite/g++.dg/template/non-dependent26.C
Normal file
25
gcc/testsuite/g++.dg/template/non-dependent26.C
Normal file
|
@ -0,0 +1,25 @@
|
|||
// Verify non-dependent assignment expressions are recognized as such
|
||||
// and are checked ahead of time.
|
||||
// PR c++/63198
|
||||
// { dg-do compile { target c++11 } }
|
||||
|
||||
struct X { using t1 = int; };
|
||||
struct Y { X operator=(const Y&); } y;
|
||||
template<class T> void f1(decltype(y = y)::t1);
|
||||
|
||||
int n;
|
||||
template<class T> void f2(decltype(n = n)::t1); // { dg-error "not a class" }
|
||||
template<class T> void f3(decltype(n += n)::t1); // { dg-error "not a class" }
|
||||
|
||||
template<class T>
|
||||
void g() {
|
||||
const int n;
|
||||
n = 42; // { dg-error "read-only" }
|
||||
|
||||
const X x;
|
||||
x = {}; // { dg-error "no match" }
|
||||
|
||||
const Y y;
|
||||
y = {}; // { dg-error "no match" }
|
||||
Y{} = X{}; // { dg-error "no match" }
|
||||
}
|
|
@ -1,14 +1,14 @@
|
|||
// PR c++/44609
|
||||
// { dg-options -ftemplate-depth=10 }
|
||||
|
||||
template<int N>
|
||||
template<class T, int N>
|
||||
void f()
|
||||
{
|
||||
0 = 0; // { dg-error "lvalue required" }
|
||||
f<N+1>(); // { dg-bogus "instantiation depth" }
|
||||
T(0) = 0; // { dg-error "lvalue required" }
|
||||
f<T, N+1>(); // { dg-bogus "instantiation depth" }
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
f<0>();
|
||||
f<int, 0>();
|
||||
}
|
||||
|
|
28
gcc/testsuite/g++.dg/warn/Wparentheses-32.C
Normal file
28
gcc/testsuite/g++.dg/warn/Wparentheses-32.C
Normal file
|
@ -0,0 +1,28 @@
|
|||
// Verify we issue -Wparentheses warnings at template definition time
|
||||
// (for suitable non-dependent expressions).
|
||||
// { dg-additional-options "-Wparentheses" }
|
||||
|
||||
struct X { operator bool(); };
|
||||
struct Y { Y& operator=(const Y&); operator bool(); };
|
||||
struct Z { int m; operator bool(); };
|
||||
|
||||
template<class T>
|
||||
void f() {
|
||||
int n, m;
|
||||
if (n = m) { } // { dg-warning "parentheses" }
|
||||
|
||||
X x1, x2;
|
||||
if (x1 = x2) { } // { dg-warning "parentheses" }
|
||||
|
||||
Y y1, y2;
|
||||
if (y1 = y2) { } // { dg-warning "parentheses" }
|
||||
|
||||
Z z1, z2;
|
||||
if (z1 = z2) { } // { dg-warning "parentheses" }
|
||||
|
||||
bool b;
|
||||
b = m = n; // { dg-warning "parentheses" "" { xfail *-*-* } }
|
||||
b = x1 = x2; // { dg-warning "parentheses" "" { xfail *-*-* } }
|
||||
b = y1 = y2; // { dg-warning "parentheses" "" { xfail *-*-* } }
|
||||
b = z1 = z2; // { dg-warning "parentheses" "" { xfail *-*-* } }
|
||||
}
|
|
@ -51,7 +51,7 @@ struct seed_seq
|
|||
// T is convertible to the engine's result_type:
|
||||
operator T() const noexcept { return T(); }
|
||||
|
||||
bool called = false;
|
||||
mutable bool called = false;
|
||||
};
|
||||
|
||||
using engine_type
|
||||
|
|
|
@ -51,7 +51,7 @@ struct seed_seq
|
|||
// T is convertible to the engine's result_type:
|
||||
operator T() const noexcept { return T(); }
|
||||
|
||||
bool called = false;
|
||||
mutable bool called = false;
|
||||
};
|
||||
|
||||
using engine_type
|
||||
|
|
|
@ -51,7 +51,7 @@ struct seed_seq
|
|||
// T is convertible to the engine's result_type:
|
||||
operator T() const noexcept { return T(); }
|
||||
|
||||
bool called = false;
|
||||
mutable bool called = false;
|
||||
};
|
||||
|
||||
using engine_type
|
||||
|
|
|
@ -51,7 +51,7 @@ struct seed_seq
|
|||
// T is convertible to the engine's result_type:
|
||||
operator T() const noexcept { return T(); }
|
||||
|
||||
bool called = false;
|
||||
mutable bool called = false;
|
||||
};
|
||||
|
||||
using engine_type
|
||||
|
|
|
@ -51,7 +51,7 @@ struct seed_seq
|
|||
// T is convertible to the engine's result_type:
|
||||
operator T() const noexcept { return T(); }
|
||||
|
||||
bool called = false;
|
||||
mutable bool called = false;
|
||||
};
|
||||
|
||||
using engine_type
|
||||
|
|
|
@ -51,7 +51,7 @@ struct seed_seq
|
|||
// T is convertible to the engine's result_type:
|
||||
operator T() const noexcept { return T(); }
|
||||
|
||||
bool called = false;
|
||||
mutable bool called = false;
|
||||
};
|
||||
|
||||
using engine_type
|
||||
|
|
|
@ -52,7 +52,7 @@ struct seed_seq
|
|||
// T is convertible to the engine's result_type:
|
||||
operator T() const noexcept { return T(); }
|
||||
|
||||
bool called = false;
|
||||
mutable bool called = false;
|
||||
};
|
||||
|
||||
using engine_type
|
||||
|
|
Loading…
Add table
Reference in a new issue