diff --git a/common/BUILD b/common/BUILD index c438381f5ff5..f7d08dabbb19 100644 --- a/common/BUILD +++ b/common/BUILD @@ -506,6 +506,19 @@ cc_library( ], ) +cc_test( + name = "ostream_test", + size = "small", + srcs = ["ostream_test.cpp"], + deps = [ + ":ostream", + ":raw_string_ostream", + "//testing/base:gtest_main", + "@googletest//:gtest", + "@llvm-project//llvm:Support", + ], +) + cc_library( name = "pretty_stack_trace_function", hdrs = ["pretty_stack_trace_function.h"], diff --git a/common/ostream.h b/common/ostream.h index d9fd397bacbc..35b2209b401c 100644 --- a/common/ostream.h +++ b/common/ostream.h @@ -7,6 +7,7 @@ // Libraries should include this header instead of raw_ostream. +#include #include #include #include @@ -17,11 +18,31 @@ namespace Carbon { -// CRTP base class for printable types. Children (DerivedT) must implement: +// CRTP base class for printable types. Derived classes (DerivedT) must +// implement: // - auto Print(llvm::raw_ostream& out) const -> void template // NOLINTNEXTLINE(bugprone-crtp-constructor-accessibility) class Printable { + // Comparisons of the base class itself, which is empty and so always compares + // equal, allowing derived classes to default their own comparison operators. + // + // These are templated so that they are only used when the types of the + // arguments are exactly `Printable`, rather than a derived class, and are + // hidden friends so that they aren't candidates for unrelated comparisons. + template + requires std::same_as + friend constexpr auto operator==(const T& /*lhs*/, const T& /*rhs*/) noexcept + -> bool { + return true; + } + template + requires std::same_as + friend constexpr auto operator<=>(const T& /*lhs*/, const T& /*rhs*/) noexcept + -> std::strong_ordering { + return std::strong_ordering::equal; + } + // Supports printing to llvm::raw_ostream. friend auto operator<<(llvm::raw_ostream& out, const DerivedT& obj) -> llvm::raw_ostream& { diff --git a/common/ostream_test.cpp b/common/ostream_test.cpp new file mode 100644 index 000000000000..8f6bba7eed5a --- /dev/null +++ b/common/ostream_test.cpp @@ -0,0 +1,253 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include "common/ostream.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "common/raw_string_ostream.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" + +namespace Carbon::Testing { +namespace { + +using ::testing::ElementsAre; + +// Whether two types can be compared with both `==` and `<=>`. +template +concept Comparable = requires(const LhsT& lhs, const RhsT& rhs) { + lhs == rhs; + lhs <=> rhs; +}; + +// A child that defaults its comparisons with member declarations. +struct Point : Printable { + int x; + int y; + + constexpr Point(int x, int y) : x(x), y(y) {} + + auto Print(llvm::raw_ostream& out) const -> void { + out << "(" << x << ", " << y << ")"; + } + + auto operator<=>(const Point& rhs) const = default; +}; + +// A child that defaults its comparisons with friend declarations, and whose +// comparisons are neither trivial nor `noexcept`. +struct Label : Printable