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 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 diagnostic value from the given partial + /// diagnostic. + DiagnosticOr(PartialDiagnosticAt Diagnostic) : HasDiagnostic(true) { + new (getDiagnosticStorage()) PartialDiagnosticAt(std::move(Diagnostic)); + } + + /// Create an DiagnosticOr success value from the given OtherT value, which + /// must be convertible to T. + template + DiagnosticOr( + OtherT &&Val, + typename std::enable_if::value>::type * = + nullptr) + : HasDiagnostic(false) { + new (getStorage()) storage_type(std::forward(Val)); + } + + DiagnosticOr(DiagnosticOr &&Other) { moveConstruct(std::move(Other)); } + + DiagnosticOr &operator=(DiagnosticOr &&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 &&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 &&Other) { + this->~DiagnosticOr(); + new (this) DiagnosticOr(std::move(Other)); + } + + storage_type *getStorage() { + assert(!HasDiagnostic && "Cannot get value when a diagnostic exists!"); + return reinterpret_cast(TStorage.buffer); + } + + const storage_type *getStorage() const { + assert(!HasDiagnostic && "Cannot get value when a diagnostic exists!"); + return reinterpret_cast(TStorage.buffer); + } + + PartialDiagnosticAt *getDiagnosticStorage() { + assert(HasDiagnostic && "Cannot get diagnostic when a value exists!"); + return reinterpret_cast(DiagnosticStorage.buffer); + } + + const PartialDiagnosticAt *getDiagnosticStorage() const { + assert(HasDiagnostic && "Cannot get diagnostic when a value exists!"); + return reinterpret_cast( + DiagnosticStorage.buffer); + } + + union { + llvm::AlignedCharArrayUnion TStorage; + llvm::AlignedCharArrayUnion DiagnosticStorage; + }; + bool HasDiagnostic : 1; +}; + +} // end namespace clang + +#endif // LLVM_CLANG_BASIC_DIAGNOSTIC_OR_H 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> 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); +} }