ilya-biryukov updated this revision to Diff 126373. ilya-biryukov added a comment.
- Removed ContextBuilder and TypedValueMap. - Updated the docs. Repository: rCTE Clang Tools Extra https://reviews.llvm.org/D40485 Files: clangd/CMakeLists.txt clangd/Context.cpp clangd/Context.h unittests/clangd/CMakeLists.txt unittests/clangd/ContextTests.cpp
Index: unittests/clangd/ContextTests.cpp =================================================================== --- /dev/null +++ unittests/clangd/ContextTests.cpp @@ -0,0 +1,57 @@ +//===-- ContextTests.cpp - Context tests ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Context.h" + +#include "gtest/gtest.h" + +namespace clang { +namespace clangd { + +TEST(ContextTests, Simple) { + Key<int> IntParam; + Key<int> ExtraIntParam; + + Context Ctx = Context::empty().derive(IntParam, 10).derive(ExtraIntParam, 20); + + EXPECT_EQ(*Ctx.get(IntParam), 10); + EXPECT_EQ(*Ctx.get(ExtraIntParam), 20); +} + +TEST(ContextTests, MoveOps) { + Key<std::unique_ptr<int>> Param; + + Context Ctx = Context::empty().derive(Param, llvm::make_unique<int>(10)); + EXPECT_EQ(**Ctx.get(Param), 10); + + Context NewCtx = std::move(Ctx); + EXPECT_EQ(**NewCtx.get(Param), 10); +} + +TEST(ContextTests, Builders) { + Key<int> ParentParam; + Key<int> ParentAndChildParam; + Key<int> ChildParam; + + Context ParentCtx = + Context::empty().derive(ParentParam, 10).derive(ParentAndChildParam, 20); + Context ChildCtx = + ParentCtx.derive(ParentAndChildParam, 30).derive(ChildParam, 40); + + EXPECT_EQ(*ParentCtx.get(ParentParam), 10); + EXPECT_EQ(*ParentCtx.get(ParentAndChildParam), 20); + EXPECT_EQ(ParentCtx.get(ChildParam), nullptr); + + EXPECT_EQ(*ChildCtx.get(ParentParam), 10); + EXPECT_EQ(*ChildCtx.get(ParentAndChildParam), 30); + EXPECT_EQ(*ChildCtx.get(ChildParam), 40); +} + +} // namespace clangd +} // namespace clang Index: unittests/clangd/CMakeLists.txt =================================================================== --- unittests/clangd/CMakeLists.txt +++ unittests/clangd/CMakeLists.txt @@ -11,6 +11,7 @@ add_extra_unittest(ClangdTests ClangdTests.cpp CodeCompleteTests.cpp + ContextTests.cpp FuzzyMatchTests.cpp JSONExprTests.cpp TestFS.cpp Index: clangd/Context.h =================================================================== --- /dev/null +++ clangd/Context.h @@ -0,0 +1,183 @@ +//===--- Context.h - Mechanism for passing implicit data --------*- C++-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Context for storing and retrieving implicit data. Useful for passing implicit +// parameters on a per-request basis. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CONTEXT_H_ +#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CONTEXT_H_ + +#include "llvm/ADT/STLExtras.h" +#include <memory> +#include <type_traits> + +namespace clang { +namespace clangd { + +/// A key for a value of type \p Type, stored inside a context. Keys are +/// non-movable and non-copyable. See documentation of the Context class for +/// more details and usage examples. +template <class Type> class Key { +public: + static_assert(!std::is_reference<Type>::value, + "Reference arguments to Key<> are not allowed"); + + Key() = default; + + Key(Key const &) = delete; + Key &operator=(Key const &) = delete; + Key(Key &&) = delete; + Key &operator=(Key &&) = delete; +}; + +/// A context is an immutable container for per-request data that must be +/// propagated through layers that don't care about it. An example is a request +/// ID that we may want to use when logging. +/// +/// Conceptually, a context is a heterogeneous map<Key<T>, T>. Each key has +/// an associated value type, which allows the map to be typesafe. +/// +/// You can't add data to an existing context, instead you create a new +/// immutable context derived from it with extra data added. When you retrieve +/// data, the context will walk up the parent chain until the key is found. +/// +/// Contexts should be: +/// - passed by reference when calling synchronous functions +/// - passed by value (move) when calling asynchronous functions. The result +/// callback of async operations will receive the context again. +/// - cloned only when 'forking' an asynchronous computation that we don't wait +/// for. +/// +/// Copy operations for this class are deleted, use an explicit clone() method +/// when you need a copy of the context instead. +/// +/// To derive a child context use derive() function, e.g. +/// Context ChildCtx = ParentCtx.derive(RequestIdKey, 123); +/// +/// To create a new root context, derive() from empty Context. +/// e.g.: +/// Context Ctx = Context::empty().derive(RequestIdKey, 123); +/// +/// Values in the context are indexed by typed keys (instances of Key<T> class). +/// Key<T> serves two purposes: +/// - it provides a lookup key for the context (each instance of a key is +/// unique), +/// - it keeps the type information about the value stored in the context map +/// in the template arguments. +/// This provides a type-safe interface to store and access values of multiple +/// types inside a single context. +/// For example, +/// Key<int> RequestID; +/// Key<int> Version; +/// +/// Context Ctx = Context::empty().derive(RequestID, 10).derive(Version, 3); +/// assert(*Ctx.get(RequestID) == 10); +/// assert(*Ctx.get(Version) == 3); +/// +/// Keys are typically used across multiple functions, so most of the time you +/// would want to make them static class members or global variables. +class Context { +public: + /// Returns an empty context that contains no data. Useful for calling + /// functions that require a context when no explicit context is available. + static const Context &empty(); + +private: + struct ContextData; + Context(std::shared_ptr<const ContextData> DataPtr); + +public: + /// Move-only. + Context(Context const &) = delete; + Context &operator=(const Context &) = delete; + + Context(Context &&) = default; + Context &operator=(Context &&) = default; + + /// Get data stored for a typed \p Key. If values are not found + /// \returns Pointer to the data associated with \p Key. If no data is + /// specified for \p Key, return null. + template <class Type> const Type *get(const Key<Type> &Key) const { + const ContextData *DataPtr = this->DataPtr.get(); + while (DataPtr != nullptr) { + if (DataPtr->KeyPtr == &Key) + return static_cast<const Type *>(DataPtr->Value->getValuePtr()); + DataPtr = DataPtr->Parent.get(); + } + return nullptr; + } + + /// A helper to get a reference to a \p Key that must exist in the map. + /// Must not be called for keys that are not in the map. + template <class Type> const Type &getExisting(const Key<Type> &Key) const { + auto Val = get(Key); + assert(Val && "Key does not exist"); + return *Val; + } + + /// Derives a child context + /// It is safe to move or destroy a parent context after calling derive() from + /// it. The child context will continue to have access to the data stored in + /// the parent context. + template <class Type, class... Args> + Context derive(const Key<Type> &Key, Args &&... As) const & { + return Context(std::make_shared<ContextData>(ContextData{ + /*Parent=*/DataPtr, &Key, + llvm::make_unique<TypedAnyStorage<Type>>(std::forward<Args>(As)...)})); + } + + template <class Type, class... Args> + Context derive(const Key<Type> &Key, Args &&... As) && /* takes ownership */ { + return Context(std::make_shared<ContextData>(ContextData{ + /*Parent=*/std::move(DataPtr), &Key, + llvm::make_unique<TypedAnyStorage<Type>>(std::forward<Args>(As)...)})); + } + + /// Clone this context object. + Context clone() const; + +private: + class AnyStorage { + public: + virtual ~AnyStorage() = default; + virtual void *getValuePtr() = 0; + }; + + template <class T> class TypedAnyStorage : public Context::AnyStorage { + static_assert(std::is_same<typename std::decay<T>::type, T>::value, + "Argument to TypedAnyStorage must be decayed"); + + public: + template <class... Args> + TypedAnyStorage(Args &&... As) : Value(std::forward<Args>(As)...) {} + + void *getValuePtr() override { return &Value; } + + private: + T Value; + }; + + struct ContextData { + // We need to make sure Parent outlives the Value, so the order of members + // is important. We do that to allow classes stored in Context's child + // layers to store references to the data in the parent layers. + std::shared_ptr<const ContextData> Parent; + const void *KeyPtr; + std::unique_ptr<AnyStorage> Value; + }; + + std::shared_ptr<const ContextData> DataPtr; +}; // namespace clangd + +} // namespace clangd +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_CONTEXT_H_ Index: clangd/Context.cpp =================================================================== --- /dev/null +++ clangd/Context.cpp @@ -0,0 +1,27 @@ +//===--- Context.cpp -----------------------------------------*- C++-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===---------------------------------------------------------------------===// + +#include "Context.h" +#include <cassert> + +namespace clang { +namespace clangd { + +const Context &Context::empty() { + static Context Empty(/*ContextData=*/nullptr); + return Empty; +} + +Context::Context(std::shared_ptr<const ContextData> DataPtr) + : DataPtr(std::move(DataPtr)) {} + +Context Context::clone() const { return Context(DataPtr); } + +} // namespace clangd +} // namespace clang Index: clangd/CMakeLists.txt =================================================================== --- clangd/CMakeLists.txt +++ clangd/CMakeLists.txt @@ -8,6 +8,7 @@ ClangdUnit.cpp ClangdUnitStore.cpp CodeComplete.cpp + Context.cpp Compiler.cpp DraftStore.cpp FuzzyMatch.cpp
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits