mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-04-15 18:50:45 +08:00
c++: Fix constexpr evaluation of pre-increment when !lval [PR99287]
Here, during cxx_eval_increment_expression (with lval=false) of ++__first where __first is &"mystr"[0], we correctly update __first to &"mystr"[1] but we end up returning &"mystr"[0] + 1 instead of &"mystr"[1]. This unreduced return value inhibits other pointer arithmetic folding during later constexpr evaluation, which ultimately causes the constexpr evaluation to fail. It turns out the simplification of &"mystr"[0] + 1 to &"mystr"[1] is performed by cxx_fold_pointer_plus_expression, not by fold_build2. So we perform this simplification during constexpr evaluation of the temporary MODIFY_EXPR (during which we assign to __first the simplified value), but then we return 'mod' which has only been folded via fold_build2 and hasn't gone through cxx_fold_pointer_plus_expression. This patch fixes this by updating 'mod' with the result of the MODIFY_EXPR evaluation appropriately, so that it captures any additional folding of the expression when !lval. We now need to be wary of this evaluation failing and returning e.g. the MODIFY_EXPR or NULL_TREE; it seems checking *non_constant_p should cover our bases here and is generally prudent. gcc/cp/ChangeLog: PR c++/99287 * constexpr.c (cxx_eval_increment_expression): Pass lval when evaluating the MODIFY_EXPR, and update 'mod' with the result of this evaluation. Check *non_constant_p afterwards. For prefix ops, just return 'mod'. gcc/testsuite/ChangeLog: PR c++/99287 * g++.dg/cpp2a/constexpr-99287.C: New test. Co-authored-by: Jakub Jelinek <jakub@redhat.com>
This commit is contained in:
parent
0cc54a68e3
commit
d1bba463bd
@ -5582,20 +5582,17 @@ cxx_eval_increment_expression (const constexpr_ctx *ctx, tree t,
|
||||
/* Storing the modified value. */
|
||||
tree store = build2_loc (cp_expr_loc_or_loc (t, input_location),
|
||||
MODIFY_EXPR, type, op, mod);
|
||||
cxx_eval_constant_expression (ctx, store,
|
||||
true, non_constant_p, overflow_p);
|
||||
mod = cxx_eval_constant_expression (ctx, store, lval,
|
||||
non_constant_p, overflow_p);
|
||||
ggc_free (store);
|
||||
if (*non_constant_p)
|
||||
return t;
|
||||
|
||||
/* And the value of the expression. */
|
||||
if (code == PREINCREMENT_EXPR || code == PREDECREMENT_EXPR)
|
||||
{
|
||||
/* Prefix ops are lvalues. */
|
||||
if (lval)
|
||||
return op;
|
||||
else
|
||||
/* But we optimize when the caller wants an rvalue. */
|
||||
return mod;
|
||||
}
|
||||
/* Prefix ops are lvalues, but the caller might want an rvalue;
|
||||
lval has already been taken into account in the store above. */
|
||||
return mod;
|
||||
else
|
||||
/* Postfix ops are rvalues. */
|
||||
return val;
|
||||
|
61
gcc/testsuite/g++.dg/cpp2a/constexpr-99287.C
Normal file
61
gcc/testsuite/g++.dg/cpp2a/constexpr-99287.C
Normal file
@ -0,0 +1,61 @@
|
||||
// PR c++/99287
|
||||
// { dg-do compile { target c++20 } }
|
||||
|
||||
namespace std {
|
||||
struct source_location {
|
||||
static consteval source_location
|
||||
current(const void *__p = __builtin_source_location()) {
|
||||
source_location __ret;
|
||||
__ret._M_impl = static_cast<const __impl *>(__p);
|
||||
return __ret;
|
||||
}
|
||||
constexpr const char *function_name() {
|
||||
return _M_impl ? _M_impl->_M_function_name : "";
|
||||
}
|
||||
struct __impl {
|
||||
const char *_M_file_name;
|
||||
const char *_M_function_name;
|
||||
unsigned _M_line;
|
||||
unsigned _M_column;
|
||||
} const *_M_impl;
|
||||
};
|
||||
struct char_traits {
|
||||
static constexpr long length(const char *__s) {
|
||||
return __builtin_strlen(__s);
|
||||
}
|
||||
};
|
||||
template <typename _CharT, typename _Traits = char_traits>
|
||||
class basic_string_view {
|
||||
public:
|
||||
using traits_type = _Traits;
|
||||
using size_type = unsigned long;
|
||||
constexpr basic_string_view(const _CharT *__str)
|
||||
: _M_len{traits_type::length(__str)}, _M_str{__str} {}
|
||||
constexpr size_type find(const _CharT *, size_type, size_type) const noexcept;
|
||||
constexpr size_type find(_CharT *__str) {
|
||||
long __trans_tmp_1 = traits_type::length(__str);
|
||||
return find(__str, 0, __trans_tmp_1);
|
||||
}
|
||||
long _M_len;
|
||||
const _CharT *_M_str;
|
||||
};
|
||||
using string_view = basic_string_view<const char>;
|
||||
template <typename _CharT, typename _Traits>
|
||||
constexpr unsigned long
|
||||
basic_string_view<_CharT, _Traits>::find(const _CharT *__str, size_type,
|
||||
size_type __n) const noexcept {
|
||||
int __trans_tmp_2;
|
||||
const _CharT *__first = _M_str;
|
||||
size_type __len = _M_len;
|
||||
while (__len >= __n) {
|
||||
__trans_tmp_2 = __builtin_memcmp(__first, __str, __n);
|
||||
if (__trans_tmp_2 == 0)
|
||||
return __first - _M_str;
|
||||
__len = _M_str - ++__first;
|
||||
}
|
||||
}
|
||||
} // namespace std
|
||||
template <typename> consteval auto f() {
|
||||
return std::string_view{std::source_location::current().function_name()};
|
||||
}
|
||||
int main() { constexpr auto s = f<int>().find("int"); }
|
Loading…
x
Reference in New Issue
Block a user