Skip to content

Commit 47e9a21

Browse files
committed
[ADT] Add streaming operators for llvm::Optional
Summary: The operators simply print the underlying value or "None". The trickier part of this patch is making sure the streaming operators work even in unit tests (which was my primary motivation, though I can also see them being useful elsewhere). Since the stream operator was a template, implicit conversions did not kick in, and our gtest glue code was explicitly introducing an implicit conversion to make sure other implicit conversions do not kick in :P. I resolve that by specializing llvm_gtest::StreamSwitch for llvm:Optional<T>. Reviewers: sammccall, dblaikie Reviewed By: sammccall Subscribers: mgorny, dexonsmith, kristina, llvm-commits Differential Revision: https://reviews.llvm.org/D56795 llvm-svn: 351548
1 parent 5e36433 commit 47e9a21

File tree

5 files changed

+95
-2
lines changed

5 files changed

+95
-2
lines changed

llvm/include/llvm/ADT/Optional.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727

2828
namespace llvm {
2929

30+
class raw_ostream;
31+
3032
namespace optional_detail {
3133
/// Storage for any type.
3234
template <typename T, bool = isPodLike<T>::value> struct OptionalStorage {
@@ -323,6 +325,18 @@ template <typename T> bool operator>=(const T &X, const Optional<T> &Y) {
323325
return !(X < Y);
324326
}
325327

328+
raw_ostream &operator<<(raw_ostream &OS, NoneType);
329+
330+
template <typename T, typename = decltype(std::declval<raw_ostream &>()
331+
<< std::declval<const T &>())>
332+
raw_ostream &operator<<(raw_ostream &OS, const Optional<T> &O) {
333+
if (O)
334+
OS << *O;
335+
else
336+
OS << None;
337+
return OS;
338+
}
339+
326340
} // end namespace llvm
327341

328342
#endif // LLVM_ADT_OPTIONAL_H

llvm/lib/Support/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ add_llvm_library(LLVMSupport
106106
MemoryBuffer.cpp
107107
MD5.cpp
108108
NativeFormatting.cpp
109+
Optional.cpp
109110
Options.cpp
110111
Parallel.cpp
111112
PluginLoader.cpp

llvm/lib/Support/Optional.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//===- Optional.cpp - Optional values ---------------------------*- C++ -*-===//
2+
//
3+
// The LLVM Compiler Infrastructure
4+
//
5+
// This file is distributed under the University of Illinois Open Source
6+
// License. See LICENSE.TXT for details.
7+
//
8+
//===----------------------------------------------------------------------===//
9+
10+
#include "llvm/ADT/Optional.h"
11+
#include "llvm/Support/raw_ostream.h"
12+
13+
llvm::raw_ostream &llvm::operator<<(raw_ostream &OS, NoneType) {
14+
return OS << "None";
15+
}

llvm/unittests/ADT/OptionalTest.cpp

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
//===----------------------------------------------------------------------===//
99

1010
#include "llvm/ADT/Optional.h"
11+
#include "llvm/ADT/SmallString.h"
12+
#include "llvm/Support/raw_ostream.h"
13+
#include "gtest/gtest-spi.h"
1114
#include "gtest/gtest.h"
1215

1316
using namespace llvm;
@@ -518,5 +521,52 @@ TEST_F(OptionalTest, OperatorGreaterEqual) {
518521
CheckRelation<GreaterEqual>(InequalityLhs, InequalityRhs, !IsLess);
519522
}
520523

521-
} // end anonymous namespace
524+
struct ComparableAndStreamable {
525+
friend bool operator==(ComparableAndStreamable,
526+
ComparableAndStreamable) LLVM_ATTRIBUTE_USED {
527+
return true;
528+
}
529+
530+
friend raw_ostream &operator<<(raw_ostream &OS, ComparableAndStreamable) {
531+
return OS << "ComparableAndStreamable";
532+
}
522533

534+
static Optional<ComparableAndStreamable> get() {
535+
return ComparableAndStreamable();
536+
}
537+
};
538+
539+
TEST_F(OptionalTest, StreamOperator) {
540+
auto to_string = [](Optional<ComparableAndStreamable> O) {
541+
SmallString<16> S;
542+
raw_svector_ostream OS(S);
543+
OS << O;
544+
return S;
545+
};
546+
EXPECT_EQ("ComparableAndStreamable",
547+
to_string(ComparableAndStreamable::get()));
548+
EXPECT_EQ("None", to_string(None));
549+
}
550+
551+
struct Comparable {
552+
friend bool operator==(Comparable, Comparable) LLVM_ATTRIBUTE_USED {
553+
return true;
554+
}
555+
static Optional<Comparable> get() { return Comparable(); }
556+
};
557+
558+
TEST_F(OptionalTest, UseInUnitTests) {
559+
// Test that we invoke the streaming operators when pretty-printing values in
560+
// EXPECT macros.
561+
EXPECT_NONFATAL_FAILURE(EXPECT_EQ(llvm::None, ComparableAndStreamable::get()),
562+
R"(Expected: llvm::None
563+
Which is: None
564+
To be equal to: ComparableAndStreamable::get()
565+
Which is: ComparableAndStreamable)");
566+
567+
// Test that it is still possible to compare objects which do not have a
568+
// custom streaming operator.
569+
EXPECT_NONFATAL_FAILURE(EXPECT_EQ(llvm::None, Comparable::get()), "object");
570+
}
571+
572+
} // end anonymous namespace

llvm/utils/unittest/googletest/include/gtest/internal/custom/raw-ostream.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,9 @@ auto printable(const T &V) -> decltype(StreamSwitch<T>::printable(V)) {
4242
// If raw_ostream support is enabled, we specialize for types with operator<<
4343
// that takes a raw_ostream.
4444
#if !GTEST_NO_LLVM_RAW_OSTREAM
45-
#include "llvm/Support/raw_ostream.h"
45+
#include "llvm/ADT/Optional.h"
4646
#include "llvm/Support/raw_os_ostream.h"
47+
#include "llvm/Support/raw_ostream.h"
4748
#include <ostream>
4849
namespace llvm_gtest {
4950

@@ -68,6 +69,18 @@ struct StreamSwitch<T, decltype((void)(std::declval<llvm::raw_ostream &>()
6869
<< ConvertibleTo<const T &>()))> {
6970
static const RawStreamProxy<T> printable(const T &V) { return {V}; }
7071
};
72+
73+
// llvm::Optional has a template operator<<, which means it will not accept any
74+
// implicit conversions, so we need to special-case it here.
75+
template <typename T>
76+
struct StreamSwitch<llvm::Optional<T>,
77+
decltype((void)(std::declval<llvm::raw_ostream &>()
78+
<< std::declval<llvm::Optional<T>>()))> {
79+
static const RawStreamProxy<llvm::Optional<T>>
80+
printable(const llvm::Optional<T> &V) {
81+
return {V};
82+
}
83+
};
7184
} // namespace llvm_gtest
7285
#endif // !GTEST_NO_LLVM_RAW_OSTREAM
7386

0 commit comments

Comments
 (0)