arphaman created this revision.

Clang's `DiagnosticOr` type mimics the `Expected` type from LLVM. It either 
stores a partial diagnostic and its location or a value. I'll be using in 
https://reviews.llvm.org/D36075.


Repository:
  rL LLVM

https://reviews.llvm.org/D36969

Files:
  include/clang/Basic/DiagnosticOr.h
  unittests/Basic/DiagnosticTest.cpp

Index: unittests/Basic/DiagnosticTest.cpp
===================================================================
--- unittests/Basic/DiagnosticTest.cpp
+++ unittests/Basic/DiagnosticTest.cpp
@@ -9,6 +9,7 @@
 
 #include "clang/Basic/Diagnostic.h"
 #include "clang/Basic/DiagnosticIDs.h"
+#include "clang/Basic/DiagnosticOr.h"
 #include "gtest/gtest.h"
 
 using namespace llvm;
@@ -72,4 +73,21 @@
   }
 }
 
+TEST(DiagnosticTest, diagnosticOr) {
+  DiagnosticsEngine Diags(new DiagnosticIDs(), new DiagnosticOptions,
+                          new IgnoringDiagConsumer());
+  PartialDiagnostic::StorageAllocator Alloc;
+  DiagnosticOr<std::pair<int, int>> Value = PartialDiagnosticAt(
+      SourceLocation(), PartialDiagnostic(diag::err_cannot_open_file, Alloc)
+                            << "file"
+                            << "error");
+  EXPECT_TRUE(!Value);
+  EXPECT_EQ(Value.getDiagnostic().first, SourceLocation());
+  EXPECT_EQ(Value.getDiagnostic().second.getDiagID(),
+            diag::err_cannot_open_file);
+  Value = std::make_pair(20, 1);
+  EXPECT_FALSE(!Value);
+  EXPECT_EQ(*Value, std::make_pair(20, 1));
+  EXPECT_EQ(Value->first, 20);
+}
 }
Index: include/clang/Basic/DiagnosticOr.h
===================================================================
--- /dev/null
+++ include/clang/Basic/DiagnosticOr.h
@@ -0,0 +1,131 @@
+//===--- DiagnosticOr.h - A partial diagnostic or a value -------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Implements a DiagnosticOr variant that stores either a value or a
+/// partial diagnostic and its location.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_BASIC_DIAGNOSTIC_OR_H
+#define LLVM_CLANG_BASIC_DIAGNOSTIC_OR_H
+
+#include "clang/Basic/PartialDiagnostic.h"
+#include "llvm/Support/AlignOf.h"
+
+namespace clang {
+
+/// Tagged union holding either a T or a PartialDiagnosticAt.
+///
+/// This class parallels llvm::Expected, but replaces Error with
+/// PartialDiagnosticAt.
+template <class T> class LLVM_NODISCARD DiagnosticOr {
+private:
+  using storage_type = T;
+  using reference = T &;
+  using const_reference = const T &;
+  using pointer = T *;
+  using const_pointer = const T *;
+
+public:
+  /// Create an DiagnosticOr<T> diagnostic value from the given partial
+  /// diagnostic.
+  DiagnosticOr(PartialDiagnosticAt Diagnostic) : HasDiagnostic(true) {
+    new (getDiagnosticStorage()) PartialDiagnosticAt(std::move(Diagnostic));
+  }
+
+  /// Create an DiagnosticOr<T> success value from the given OtherT value, which
+  /// must be convertible to T.
+  template <typename OtherT>
+  DiagnosticOr(
+      OtherT &&Val,
+      typename std::enable_if<std::is_convertible<OtherT, T>::value>::type * =
+          nullptr)
+      : HasDiagnostic(false) {
+    new (getStorage()) storage_type(std::forward<OtherT>(Val));
+  }
+
+  DiagnosticOr(DiagnosticOr<T> &&Other) { moveConstruct(std::move(Other)); }
+
+  DiagnosticOr<T> &operator=(DiagnosticOr<T> &&Other) {
+    moveAssign(std::move(Other));
+    return *this;
+  }
+
+  ~DiagnosticOr() {
+    if (!HasDiagnostic)
+      getStorage()->~storage_type();
+    else
+      getDiagnosticStorage()->~PartialDiagnosticAt();
+  }
+
+  /// Returns false if there is a diagnostic.
+  explicit operator bool() { return !HasDiagnostic; }
+
+  PartialDiagnosticAt &getDiagnostic() { return *getDiagnosticStorage(); }
+
+  const PartialDiagnosticAt &getDiagnostic() const {
+    return *getDiagnosticStorage();
+  }
+
+  pointer operator->() { return getStorage(); }
+
+  const_pointer operator->() const { return getStorage(); }
+
+  reference operator*() { return *getStorage(); }
+
+  const_reference operator*() const { return *getStorage(); }
+
+private:
+  void moveConstruct(DiagnosticOr<T> &&Other) {
+    HasDiagnostic = Other.HasDiagnostic;
+
+    if (!HasDiagnostic)
+      new (getStorage()) storage_type(std::move(*Other.getStorage()));
+    else
+      new (getDiagnosticStorage())
+          PartialDiagnosticAt(std::move(*Other.getDiagnosticStorage()));
+  }
+
+  void moveAssign(DiagnosticOr<T> &&Other) {
+    this->~DiagnosticOr();
+    new (this) DiagnosticOr<T>(std::move(Other));
+  }
+
+  storage_type *getStorage() {
+    assert(!HasDiagnostic && "Cannot get value when a diagnostic exists!");
+    return reinterpret_cast<storage_type *>(TStorage.buffer);
+  }
+
+  const storage_type *getStorage() const {
+    assert(!HasDiagnostic && "Cannot get value when a diagnostic exists!");
+    return reinterpret_cast<const storage_type *>(TStorage.buffer);
+  }
+
+  PartialDiagnosticAt *getDiagnosticStorage() {
+    assert(HasDiagnostic && "Cannot get diagnostic when a value exists!");
+    return reinterpret_cast<PartialDiagnosticAt *>(DiagnosticStorage.buffer);
+  }
+
+  const PartialDiagnosticAt *getDiagnosticStorage() const {
+    assert(HasDiagnostic && "Cannot get diagnostic when a value exists!");
+    return reinterpret_cast<const PartialDiagnosticAt *>(
+        DiagnosticStorage.buffer);
+  }
+
+  union {
+    llvm::AlignedCharArrayUnion<storage_type> TStorage;
+    llvm::AlignedCharArrayUnion<PartialDiagnosticAt> DiagnosticStorage;
+  };
+  bool HasDiagnostic : 1;
+};
+
+} // end namespace clang
+
+#endif // LLVM_CLANG_BASIC_DIAGNOSTIC_OR_H
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to