Improve the representation of <compare>'s zero-only type.

* Use an empty struct instead of a member pointer to represent this
  type, so that we don't actually pass a zero member pointer at runtime.

* Mark the constructor as consteval to ensure that no code is emitted
  for it whenever possible.

* Add a honeypot constructor to reject all non-int arguments, so that
  the only argument that can arrive at the real constructor is the
  literal 0.

This results in better generated code, and rejecting invalid comparisons
against nullptr, 0L, and so on, while also rejecting invalid comparisons
against (1-1) and similar that would be allowed if we required an
integer constant expression with value 0.

Differential Revision: https://reviews.llvm.org/D85051
This commit is contained in:
Richard Smith 2020-07-31 15:03:21 -07:00
parent d256797c90
commit bf434a5f17
3 changed files with 73 additions and 2 deletions

View File

@ -837,6 +837,12 @@ typedef unsigned int char32_t;
# define _LIBCPP_CONSTEXPR constexpr
#endif
#ifndef __cpp_consteval
# define _LIBCPP_CONSTEVAL _LIBCPP_CONSTEXPR
#else
# define _LIBCPP_CONSTEVAL consteval
#endif
#ifdef _LIBCPP_CXX03_LANG
# define _LIBCPP_DEFAULT {}
#else

View File

@ -154,8 +154,13 @@ enum class _LIBCPP_ENUM_VIS _NCmpResult : signed char {
__unordered = -127
};
struct _CmpUnspecifiedType;
using _CmpUnspecifiedParam = void (_CmpUnspecifiedType::*)();
struct _CmpUnspecifiedParam {
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEVAL
_CmpUnspecifiedParam(int _CmpUnspecifiedParam::*) {}
template<typename _Tp, typename = _VSTD::enable_if_t<!_VSTD::is_same_v<_Tp, int>>>
_CmpUnspecifiedParam(_Tp) = delete;
};
class weak_equality {
_LIBCPP_INLINE_VISIBILITY

View File

@ -0,0 +1,60 @@
// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
// <compare>
// Ensure we reject all cases where an argument other than a literal 0 is used
// for a comparison against a comparison category type.
#include <compare>
#define TEST_OP(v, op) \
void(v op 0L); \
void(0L op v); \
void(v op nullptr); \
void(nullptr op v); \
void(v op(1 - 1)); \
void((1 - 1) op v);
template <typename T>
void test_category(T v) {
TEST_OP(v, ==); // expected-error 18 {{}}
TEST_OP(v, !=); // expected-error 18 {{}}
TEST_OP(v, <); // expected-error 18 {{}}
TEST_OP(v, <=); // expected-error 18 {{}}
TEST_OP(v, >); // expected-error 18 {{}}
TEST_OP(v, >=); // expected-error 18 {{}}
TEST_OP(v, <=>); // expected-error 18 {{}}
void(v == 0);
void(0 == v);
void(v != 0);
void(0 != v);
void(v < 0);
void(0 < v);
void(v <= 0);
void(0 <= v);
void(v > 0);
void(0 > v);
void(v >= 0);
void(0 >= v);
#ifndef _LIBCPP_HAS_NO_THREE_WAY_COMPARISON
void(v <=> 0); // expected-error 3 {{}}
void(0 <=> v); // expected-error 3 {{}}
#endif
}
int main(int, char**) {
test_category(std::strong_ordering::equivalent);
test_category(std::weak_ordering::equivalent);
test_category(std::partial_ordering::equivalent);
return 0;
}