From 865c1f74a2fcec1bc7620d40f12ca4275bc9be14 Mon Sep 17 00:00:00 2001 From: Cameron Hart Date: Sun, 10 Jul 2016 23:55:53 +1000 Subject: [PATCH 01/25] Add accessors for MCSubtargetInfo CPU and Feature tables This is needed for `-C target-cpu=help` and `-C target-feature=help` in rustc --- llvm/include/llvm/MC/MCSubtargetInfo.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/llvm/include/llvm/MC/MCSubtargetInfo.h b/llvm/include/llvm/MC/MCSubtargetInfo.h index b3ce523d9c0ca4..91a459f0a9b581 100644 --- a/llvm/include/llvm/MC/MCSubtargetInfo.h +++ b/llvm/include/llvm/MC/MCSubtargetInfo.h @@ -172,10 +172,19 @@ class MCSubtargetInfo { return Found != ProcDesc.end() && StringRef(Found->Key) == CPU; } + /// Returns string representation of scheduler comment virtual std::string getSchedInfoStr(MCInst const &MCI) const { return {}; } + + ArrayRef getCPUTable() const { + return ProcDesc; + } + + ArrayRef getFeatureTable() const { + return ProcFeatures; + } }; } // end namespace llvm From 122c3dcecb36a3ff890bd52a795c51cf0ae32abd Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 28 Jul 2017 11:41:14 -0700 Subject: [PATCH 02/25] Fix compile on dist-i686-linux builder If this lines are present then we apparently get errors [1] when compiling in the current [2] dist-i686-linux container. Attempts to upgrade both gcc and binutils did not fix the error, so it appears that this may just be a bug in the super old glibc we're using on the dist-i686-linux container. We don't actually need this code anyway, so just work around these issues by removing references to the `*64` functions. This'll get things compiling locally and shouldn't be a regression in functionality. [1]: https://travis-ci.org/rust-lang/rust/jobs/257578199 [2]: https://github.com/rust-lang/rust/tree/eba9d7f08ce5c90549ee52337aca0010ad566f0d/src/ci/docker/dist-i686-linux --- .../lib/ExecutionEngine/RuntimeDyld/RTDyldMemoryManager.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/llvm/lib/ExecutionEngine/RuntimeDyld/RTDyldMemoryManager.cpp b/llvm/lib/ExecutionEngine/RuntimeDyld/RTDyldMemoryManager.cpp index 75d4c2b5134e12..b5d5f1b9772737 100644 --- a/llvm/lib/ExecutionEngine/RuntimeDyld/RTDyldMemoryManager.cpp +++ b/llvm/lib/ExecutionEngine/RuntimeDyld/RTDyldMemoryManager.cpp @@ -243,9 +243,9 @@ RTDyldMemoryManager::getSymbolAddressInProcess(const std::string &Name) { if (Name == "stat") return (uint64_t)&stat; if (Name == "fstat") return (uint64_t)&fstat; if (Name == "lstat") return (uint64_t)&lstat; - if (Name == "stat64") return (uint64_t)&stat64; - if (Name == "fstat64") return (uint64_t)&fstat64; - if (Name == "lstat64") return (uint64_t)&lstat64; + // if (Name == "stat64") return (uint64_t)&stat64; + // if (Name == "fstat64") return (uint64_t)&fstat64; + // if (Name == "lstat64") return (uint64_t)&lstat64; if (Name == "atexit") return (uint64_t)&atexit; if (Name == "mknod") return (uint64_t)&mknod; From b73884a7f2339181dab0f3d77b30bb1a73e33023 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 24 Jan 2018 15:28:57 -0800 Subject: [PATCH 03/25] Disable checks for libatomic for now For whatever reason this is failing the i686-freebsd builder in the Rust repo as-of this red-hot moment. The build seems to work fine without it so let's just remove it for now and pray there's a better fix later. Although if you're reading this and know of a better fix, we'd love to remove this! --- llvm/cmake/modules/CheckAtomic.cmake | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/llvm/cmake/modules/CheckAtomic.cmake b/llvm/cmake/modules/CheckAtomic.cmake index 9a4cdf12a6223d..de8e54153a741a 100644 --- a/llvm/cmake/modules/CheckAtomic.cmake +++ b/llvm/cmake/modules/CheckAtomic.cmake @@ -62,19 +62,20 @@ else() check_working_cxx_atomics64(HAVE_CXX_ATOMICS64_WITHOUT_LIB) endif() -# If not, check if the library exists, and atomics work with it. -if(NOT HAVE_CXX_ATOMICS64_WITHOUT_LIB) - check_library_exists(atomic __atomic_load_8 "" HAVE_CXX_LIBATOMICS64) - if(HAVE_CXX_LIBATOMICS64) - list(APPEND CMAKE_REQUIRED_LIBRARIES "atomic") - check_working_cxx_atomics64(HAVE_CXX_ATOMICS64_WITH_LIB) - if (NOT HAVE_CXX_ATOMICS64_WITH_LIB) - message(FATAL_ERROR "Host compiler must support std::atomic!") - endif() - else() - message(FATAL_ERROR "Host compiler appears to require libatomic, but cannot find it.") - endif() -endif() +# RUST-SPECIFIC - commented out, see commit message +# # If not, check if the library exists, and atomics work with it. +# if(NOT HAVE_CXX_ATOMICS64_WITHOUT_LIB) +# check_library_exists(atomic __atomic_load_8 "" HAVE_CXX_LIBATOMICS64) +# if(HAVE_CXX_LIBATOMICS64) +# list(APPEND CMAKE_REQUIRED_LIBRARIES "atomic") +# check_working_cxx_atomics64(HAVE_CXX_ATOMICS64_WITH_LIB) +# if (NOT HAVE_CXX_ATOMICS64_WITH_LIB) +# message(FATAL_ERROR "Host compiler must support std::atomic!") +# endif() +# else() +# message(FATAL_ERROR "Host compiler appears to require libatomic, but cannot find it.") +# endif() +# endif() ## TODO: This define is only used for the legacy atomic operations in ## llvm's Atomic.h, which should be replaced. Other code simply From 392947a2113172586ea59d2ab658f8b7f31be621 Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Sat, 3 Jun 2017 18:55:08 +0300 Subject: [PATCH 04/25] Add knowledge of __rust_{alloc,realloc,dealloc} --- .../llvm/Analysis/TargetLibraryInfo.def | 10 +++++++++ llvm/lib/Analysis/MemoryBuiltins.cpp | 8 +++++-- llvm/lib/Analysis/TargetLibraryInfo.cpp | 22 +++++++++++++++++++ 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/llvm/include/llvm/Analysis/TargetLibraryInfo.def b/llvm/include/llvm/Analysis/TargetLibraryInfo.def index 518a85ee1a0169..c67d6cae9ddd70 100644 --- a/llvm/include/llvm/Analysis/TargetLibraryInfo.def +++ b/llvm/include/llvm/Analysis/TargetLibraryInfo.def @@ -352,6 +352,16 @@ TLI_DEFINE_STRING_INTERNAL("__powf_finite") /// long double __powl_finite(long double x, long double y); TLI_DEFINE_ENUM_INTERNAL(powl_finite) TLI_DEFINE_STRING_INTERNAL("__powl_finite") + +TLI_DEFINE_ENUM_INTERNAL(rust_alloc) +TLI_DEFINE_STRING_INTERNAL("__rust_alloc") + +TLI_DEFINE_ENUM_INTERNAL(rust_dealloc) +TLI_DEFINE_STRING_INTERNAL("__rust_dealloc") + +TLI_DEFINE_ENUM_INTERNAL(rust_realloc) +TLI_DEFINE_STRING_INTERNAL("__rust_realloc") + /// double __sincospi_stret(double x); TLI_DEFINE_ENUM_INTERNAL(sincospi_stret) TLI_DEFINE_STRING_INTERNAL("__sincospi_stret") diff --git a/llvm/lib/Analysis/MemoryBuiltins.cpp b/llvm/lib/Analysis/MemoryBuiltins.cpp index 686ad294378c9c..a694be8965aab6 100644 --- a/llvm/lib/Analysis/MemoryBuiltins.cpp +++ b/llvm/lib/Analysis/MemoryBuiltins.cpp @@ -105,7 +105,10 @@ static const std::pair AllocationFnData[] = { {LibFunc_realloc, {ReallocLike, 2, 1, -1}}, {LibFunc_reallocf, {ReallocLike, 2, 1, -1}}, {LibFunc_strdup, {StrDupLike, 1, -1, -1}}, - {LibFunc_strndup, {StrDupLike, 2, 1, -1}} + {LibFunc_strndup, {StrDupLike, 2, 1, -1}}, + + {LibFunc_rust_alloc, {MallocLike, 2, 0, -1}}, + {LibFunc_rust_realloc, {ReallocLike, 4, 3, -1}}, // TODO: Handle "int posix_memalign(void **, size_t, size_t)" }; @@ -399,7 +402,8 @@ const CallInst *llvm::isFreeCall(const Value *I, const TargetLibraryInfo *TLI) { TLIFn == LibFunc_msvc_delete_array_ptr64_nothrow) // delete[](void*, nothrow) ExpectedNumParams = 2; else if (TLIFn == LibFunc_ZdaPvSt11align_val_tRKSt9nothrow_t || // delete(void*, align_val_t, nothrow) - TLIFn == LibFunc_ZdlPvSt11align_val_tRKSt9nothrow_t) // delete[](void*, align_val_t, nothrow) + TLIFn == LibFunc_ZdlPvSt11align_val_tRKSt9nothrow_t || // delete[](void*, align_val_t, nothrow) + TLIFn == LibFunc_rust_dealloc) ExpectedNumParams = 3; else return nullptr; diff --git a/llvm/lib/Analysis/TargetLibraryInfo.cpp b/llvm/lib/Analysis/TargetLibraryInfo.cpp index 4643f75da42d14..5c6ed816c581ea 100644 --- a/llvm/lib/Analysis/TargetLibraryInfo.cpp +++ b/llvm/lib/Analysis/TargetLibraryInfo.cpp @@ -1382,6 +1382,28 @@ bool TargetLibraryInfoImpl::isValidProtoForLibFunc(const FunctionType &FTy, else return false; } + + case LibFunc_rust_alloc: + return (NumParams == 3 && FTy.getReturnType()->isPointerTy() && + FTy.getParamType(0)->isIntegerTy() && + FTy.getParamType(1)->isIntegerTy() && + FTy.getParamType(2)->isPointerTy()); + + case LibFunc_rust_dealloc: + return (NumParams == 3 && FTy.getReturnType()->isVoidTy() && + FTy.getParamType(0)->isPointerTy() && + FTy.getParamType(1)->isIntegerTy() && + FTy.getParamType(2)->isIntegerTy()); + + case LibFunc_rust_realloc: + return (NumParams == 6 && FTy.getReturnType()->isPointerTy() && + FTy.getParamType(0)->isPointerTy() && + FTy.getParamType(1)->isIntegerTy() && + FTy.getParamType(2)->isIntegerTy() && + FTy.getParamType(3)->isIntegerTy() && + FTy.getParamType(4)->isIntegerTy() && + FTy.getParamType(5)->isPointerTy()); + case LibFunc::NumLibFuncs: break; } From bd18bd853b0b30e89955da3802b18448c087dd66 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 2 Jul 2018 15:28:26 -0700 Subject: [PATCH 05/25] Fix compile on dist-x86_64-linux builder Apparently glibc is so old it doesn't have the _POSIX_ARG_MAX constant. This shouldn't affect anything we use anyway though. https://travis-ci.org/rust-lang/rust/jobs/399333071 --- llvm/lib/Support/Unix/Program.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/lib/Support/Unix/Program.inc b/llvm/lib/Support/Unix/Program.inc index d0abc3763e821b..24962ba52533f1 100644 --- a/llvm/lib/Support/Unix/Program.inc +++ b/llvm/lib/Support/Unix/Program.inc @@ -459,7 +459,7 @@ bool llvm::sys::commandLineFitsWithinSystemLimits(StringRef Program, static long ArgMax = sysconf(_SC_ARG_MAX); // POSIX requires that _POSIX_ARG_MAX is 4096, which is the lowest possible // value for ARG_MAX on a POSIX compliant system. - static long ArgMin = _POSIX_ARG_MAX; + static long ArgMin = 4096; // This the same baseline used by xargs. long EffectiveArgMax = 128 * 1024; From b1ec4cb6b0ae5420dc486a8f41796cbfe1647864 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sat, 10 Feb 2018 17:21:38 -0800 Subject: [PATCH 06/25] Compile with /MT on MSVC Can't seem to figure out how to do this without this patch... --- lld/CMakeLists.txt | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lld/CMakeLists.txt b/lld/CMakeLists.txt index e2fbdbfbbb47f7..ccf33f8fd28817 100644 --- a/lld/CMakeLists.txt +++ b/lld/CMakeLists.txt @@ -210,6 +210,19 @@ if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY) ) endif() +if (MSVC) + FOREACH(flag + CMAKE_C_FLAGS_RELEASE CMAKE_C_FLAGS_RELWITHDEBINFO + CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_DEBUG_INIT + CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_RELWITHDEBINFO + CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_DEBUG_INIT) + if (MSVC) + STRING(REPLACE "/MD" "/MT" "${flag}" "${${flag}}") + SET("${flag}" "${${flag}}") + endif (MSVC) + ENDFOREACH() +endif() + add_subdirectory(Common) add_subdirectory(lib) add_subdirectory(tools/lld) From 1ecf8edfb6250a21d34d313f1cae75c0eab0ef61 Mon Sep 17 00:00:00 2001 From: Tom Tromey Date: Mon, 25 Jun 2018 12:13:42 -0600 Subject: [PATCH 07/25] Add Rust support to Mangled This adds Rust support to Mangled. I am not completely certain that this is needed (or alternatively that it does enough, maybe Mangled::GuessLanguage needs a Rust case). This should be checked before attempting to upstream. --- lldb/source/Core/Mangled.cpp | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/lldb/source/Core/Mangled.cpp b/lldb/source/Core/Mangled.cpp index 943df008edadd7..20fea1db2f1526 100644 --- a/lldb/source/Core/Mangled.cpp +++ b/lldb/source/Core/Mangled.cpp @@ -124,6 +124,35 @@ get_demangled_name_without_arguments(ConstString mangled, return g_last_mangled; } +static void remove_rust_hash(char *str) { + char *iter = str + strlen(str) - 1; + size_t hashcount = 0; + while (true) { + if (iter == str) { + // Hit the start of the string too early. + return; + } + if ((*iter >= '0' && *iter <= '9') || + (*iter >= 'a' && *iter <= 'f')) { + ++hashcount; + --iter; + if (hashcount > 16) { + // Too many hash chars. + return; + } + } else { + // Not a hash char. + break; + } + } + if (*iter != 'h' || hashcount < 5 || str + 2 >= iter || + iter[-1] != ':' || iter[-2] != ':') { + return; + } + iter[-2] = '\0'; +} + + #pragma mark Mangled //---------------------------------------------------------------------- // Default constructor @@ -387,6 +416,11 @@ Mangled::GetDemangledName(lldb::LanguageType language) const { break; case eManglingSchemeItanium: { demangled_name = GetItaniumDemangledStr(mangled_name); + + if (language == lldb::eLanguageTypeRust) { + remove_rust_hash(demangled_name); + } + break; } case eManglingSchemeNone: From 9fa3339d25fdcfbd17c30c43a9d3e734cfa61da9 Mon Sep 17 00:00:00 2001 From: Tom Tromey Date: Mon, 25 Jun 2018 12:15:20 -0600 Subject: [PATCH 08/25] Add DIERef::operator== This was needed for the Rust plugin --- lldb/source/Plugins/SymbolFile/DWARF/DIERef.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DIERef.h b/lldb/source/Plugins/SymbolFile/DWARF/DIERef.h index cb28c890c25a14..df17e1465adefe 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DIERef.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/DIERef.h @@ -49,6 +49,8 @@ struct DIERef { return cu_offset != DW_INVALID_OFFSET || die_offset != DW_INVALID_OFFSET; } + bool operator==(const DIERef &ref) const { return die_offset == ref.die_offset; } + dw_offset_t cu_offset = DW_INVALID_OFFSET; dw_offset_t die_offset = DW_INVALID_OFFSET; }; From e6a745229083b48319649ba8182cb891963ec24f Mon Sep 17 00:00:00 2001 From: Tom Tromey Date: Mon, 25 Jun 2018 12:16:12 -0600 Subject: [PATCH 09/25] Add a missing TypeAndOrName constructor Add a TypeAndOrName constructor that was declared but not defined. This is used in the Rust plugin. See https://reviews.llvm.org/D44752 --- lldb/source/Symbol/Type.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lldb/source/Symbol/Type.cpp b/lldb/source/Symbol/Type.cpp index e966c269408a4d..15589b83249b0d 100644 --- a/lldb/source/Symbol/Type.cpp +++ b/lldb/source/Symbol/Type.cpp @@ -694,6 +694,13 @@ TypeAndOrName::TypeAndOrName(const TypeAndOrName &rhs) TypeAndOrName::TypeAndOrName(ConstString &in_type_const_string) : m_type_name(in_type_const_string) {} +TypeAndOrName::TypeAndOrName(const CompilerType &type) + : m_type_pair(type) +{ + if (m_type_pair) + m_type_name = m_type_pair.GetName(); +} + TypeAndOrName &TypeAndOrName::operator=(const TypeAndOrName &rhs) { if (this != &rhs) { m_type_name = rhs.m_type_name; From ea4ac97ff824ca655fbe9ab340ae4783c0c999a1 Mon Sep 17 00:00:00 2001 From: Tom Tromey Date: Mon, 25 Jun 2018 12:16:52 -0600 Subject: [PATCH 10/25] Add Rust support to the Python test harness --- .../Python/lldbsuite/test/decorators.py | 11 +++++++++++ lldb/packages/Python/lldbsuite/test/lldbtest.py | 17 +++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/lldb/packages/Python/lldbsuite/test/decorators.py b/lldb/packages/Python/lldbsuite/test/decorators.py index 2458a6f8745ee5..806d5cd190f162 100644 --- a/lldb/packages/Python/lldbsuite/test/decorators.py +++ b/lldb/packages/Python/lldbsuite/test/decorators.py @@ -595,6 +595,17 @@ def skipUnlessDarwin(func): return skipUnlessPlatform(lldbplatformutil.getDarwinOSTriples())(func) +def skipUnlessRustInstalled(func): + """Decorate the item to skip tests when no Rust compiler is available.""" + + def is_rust_missing(self): + compiler = self.getRustCompilerVersion() + if not compiler: + return "skipping because rust compiler not found" + return None + return skipTestIfFn(is_rust_missing)(func) + + def skipIfHostIncompatibleWithRemote(func): """Decorate the item to skip tests if binaries built on this host are incompatible.""" diff --git a/lldb/packages/Python/lldbsuite/test/lldbtest.py b/lldb/packages/Python/lldbsuite/test/lldbtest.py index 161e8c61349d10..c97afd7a484e42 100644 --- a/lldb/packages/Python/lldbsuite/test/lldbtest.py +++ b/lldb/packages/Python/lldbsuite/test/lldbtest.py @@ -1309,6 +1309,18 @@ def getCompilerVersion(self): version = m.group(1) return version + def getRustCompilerVersion(self): + """ Returns a string that represents the rust compiler version, or None if rust is not found. + """ + compiler = which("rustc") + if compiler: + version_output = system([[compiler, "--version"]])[0] + for line in version_output.split(os.linesep): + m = re.search('rustc ([0-9\.]+)', line) + if m: + return m.group(1) + return None + def platformIsDarwin(self): """Returns true if the OS triple for the selected platform is any valid apple OS""" return lldbplatformutil.platformIsDarwin() @@ -1577,6 +1589,11 @@ def buildGModules( dictionary, testdir, testname): raise Exception("Don't know how to build binary with gmodules") + def buildRust(self): + """Build the default rust binary. + """ + system([[which('rustc'), '-g main.rs']]) + def signBinary(self, binary_path): if sys.platform.startswith("darwin"): codesign_cmd = "codesign --force --sign \"%s\" %s" % ( From df47965789ab0ce3fc1ef2c549aded6a0721d8a1 Mon Sep 17 00:00:00 2001 From: Tom Tromey Date: Mon, 25 Jun 2018 12:20:40 -0600 Subject: [PATCH 11/25] The Rust plugin --- .../Expression/ExpressionTypeSystemHelper.h | 1 + .../lldb/Expression/ExpressionVariable.h | 2 +- lldb/include/lldb/Symbol/RustASTContext.h | 464 ++++ lldb/include/lldb/Symbol/TypeSystem.h | 1 + lldb/include/lldb/lldb-forward.h | 2 + .../test/lang/rust/calls/TestRustCalls.py | 82 + .../lldbsuite/test/lang/rust/calls/main.rs | 86 + .../rust/expressions/TestRustExpressions.py | 164 ++ .../test/lang/rust/expressions/main.rs | 86 + .../test/lang/rust/names/TestRustNames.py | 88 + .../lldbsuite/test/lang/rust/names/main.rs | 69 + .../lang/rust/types/TestRustASTContext.py | 172 ++ .../lldbsuite/test/lang/rust/types/main.rs | 76 + lldb/source/API/SystemInitializerFull.cpp | 9 + lldb/source/Core/CMakeLists.txt | 2 + .../Plugins/ExpressionParser/CMakeLists.txt | 1 + .../ExpressionParser/Rust/CMakeLists.txt | 19 + .../Plugins/ExpressionParser/Rust/RustAST.h | 826 ++++++ .../Rust/RustFunctionCaller.cpp | 200 ++ .../Rust/RustFunctionCaller.h | 46 + .../Plugins/ExpressionParser/Rust/RustLex.cpp | 654 +++++ .../Plugins/ExpressionParser/Rust/RustLex.h | 168 ++ .../ExpressionParser/Rust/RustParse.cpp | 2284 +++++++++++++++++ .../Plugins/ExpressionParser/Rust/RustParse.h | 90 + .../Rust/RustUserExpression.cpp | 68 + .../Rust/RustUserExpression.h | 63 + lldb/source/Plugins/Language/CMakeLists.txt | 1 + .../Plugins/Language/Rust/CMakeLists.txt | 11 + .../Plugins/Language/Rust/RustLanguage.cpp | 60 + .../Plugins/Language/Rust/RustLanguage.h | 50 + .../Plugins/LanguageRuntime/CMakeLists.txt | 1 + .../LanguageRuntime/Rust/CMakeLists.txt | 10 + .../Rust/RustLanguageRuntime.cpp | 116 + .../Rust/RustLanguageRuntime.h | 78 + .../Plugins/SymbolFile/DWARF/CMakeLists.txt | 1 + .../SymbolFile/DWARF/DWARFASTParserRust.cpp | 1177 +++++++++ .../SymbolFile/DWARF/DWARFASTParserRust.h | 134 + .../SymbolFile/DWARF/SymbolFileDWARF.h | 1 + lldb/source/Symbol/CMakeLists.txt | 1 + lldb/source/Symbol/ClangASTContext.cpp | 2 - lldb/source/Symbol/RustASTContext.cpp | 2169 ++++++++++++++++ lldb/unittests/CMakeLists.txt | 1 + lldb/unittests/Rust/CMakeLists.txt | 8 + lldb/unittests/Rust/RustLexTest.cpp | 171 ++ lldb/unittests/Rust/RustParseTest.cpp | 127 + 45 files changed, 9839 insertions(+), 3 deletions(-) create mode 100644 lldb/include/lldb/Symbol/RustASTContext.h create mode 100644 lldb/packages/Python/lldbsuite/test/lang/rust/calls/TestRustCalls.py create mode 100644 lldb/packages/Python/lldbsuite/test/lang/rust/calls/main.rs create mode 100644 lldb/packages/Python/lldbsuite/test/lang/rust/expressions/TestRustExpressions.py create mode 100644 lldb/packages/Python/lldbsuite/test/lang/rust/expressions/main.rs create mode 100644 lldb/packages/Python/lldbsuite/test/lang/rust/names/TestRustNames.py create mode 100644 lldb/packages/Python/lldbsuite/test/lang/rust/names/main.rs create mode 100644 lldb/packages/Python/lldbsuite/test/lang/rust/types/TestRustASTContext.py create mode 100644 lldb/packages/Python/lldbsuite/test/lang/rust/types/main.rs create mode 100644 lldb/source/Plugins/ExpressionParser/Rust/CMakeLists.txt create mode 100644 lldb/source/Plugins/ExpressionParser/Rust/RustAST.h create mode 100644 lldb/source/Plugins/ExpressionParser/Rust/RustFunctionCaller.cpp create mode 100644 lldb/source/Plugins/ExpressionParser/Rust/RustFunctionCaller.h create mode 100644 lldb/source/Plugins/ExpressionParser/Rust/RustLex.cpp create mode 100644 lldb/source/Plugins/ExpressionParser/Rust/RustLex.h create mode 100644 lldb/source/Plugins/ExpressionParser/Rust/RustParse.cpp create mode 100644 lldb/source/Plugins/ExpressionParser/Rust/RustParse.h create mode 100644 lldb/source/Plugins/ExpressionParser/Rust/RustUserExpression.cpp create mode 100644 lldb/source/Plugins/ExpressionParser/Rust/RustUserExpression.h create mode 100644 lldb/source/Plugins/Language/Rust/CMakeLists.txt create mode 100644 lldb/source/Plugins/Language/Rust/RustLanguage.cpp create mode 100644 lldb/source/Plugins/Language/Rust/RustLanguage.h create mode 100644 lldb/source/Plugins/LanguageRuntime/Rust/CMakeLists.txt create mode 100644 lldb/source/Plugins/LanguageRuntime/Rust/RustLanguageRuntime.cpp create mode 100644 lldb/source/Plugins/LanguageRuntime/Rust/RustLanguageRuntime.h create mode 100644 lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserRust.cpp create mode 100644 lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserRust.h create mode 100644 lldb/source/Symbol/RustASTContext.cpp create mode 100644 lldb/unittests/Rust/CMakeLists.txt create mode 100644 lldb/unittests/Rust/RustLexTest.cpp create mode 100644 lldb/unittests/Rust/RustParseTest.cpp diff --git a/lldb/include/lldb/Expression/ExpressionTypeSystemHelper.h b/lldb/include/lldb/Expression/ExpressionTypeSystemHelper.h index ffcad54fb9f9b4..04f0b1c038d62f 100644 --- a/lldb/include/lldb/Expression/ExpressionTypeSystemHelper.h +++ b/lldb/include/lldb/Expression/ExpressionTypeSystemHelper.h @@ -32,6 +32,7 @@ class ExpressionTypeSystemHelper { eKindClangHelper, eKindSwiftHelper, eKindGoHelper, + eKindRustHelper, kNumKinds }; diff --git a/lldb/include/lldb/Expression/ExpressionVariable.h b/lldb/include/lldb/Expression/ExpressionVariable.h index 01e9c416e7c01e..de99d9ed690dc5 100644 --- a/lldb/include/lldb/Expression/ExpressionVariable.h +++ b/lldb/include/lldb/Expression/ExpressionVariable.h @@ -29,7 +29,7 @@ class ExpressionVariable //---------------------------------------------------------------------- // See TypeSystem.h for how to add subclasses to this. //---------------------------------------------------------------------- - enum LLVMCastKind { eKindClang, eKindSwift, eKindGo, kNumKinds }; + enum LLVMCastKind { eKindClang, eKindSwift, eKindGo, eKindRust, kNumKinds }; LLVMCastKind getKind() const { return m_kind; } diff --git a/lldb/include/lldb/Symbol/RustASTContext.h b/lldb/include/lldb/Symbol/RustASTContext.h new file mode 100644 index 00000000000000..939572d5f4f98f --- /dev/null +++ b/lldb/include/lldb/Symbol/RustASTContext.h @@ -0,0 +1,464 @@ +//===-- RustASTContext.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RustASTContext_h_ +#define liblldb_RustASTContext_h_ + +// C Includes +// C++ Includes +#include +#include +#include +#include +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/Symbol/CompilerType.h" +#include "lldb/Symbol/TypeSystem.h" +#include "lldb/Utility/ConstString.h" + +namespace lldb_private { + +class RustDecl; +class RustDeclContext; +class RustType; + +class RustASTContext : public TypeSystem { +public: + RustASTContext(); + ~RustASTContext() override; + + //------------------------------------------------------------------ + // PluginInterface functions + //------------------------------------------------------------------ + ConstString GetPluginName() override; + + uint32_t GetPluginVersion() override; + + static ConstString GetPluginNameStatic(); + + static lldb::TypeSystemSP CreateInstance(lldb::LanguageType language, + Module *module, Target *target); + + static void EnumerateSupportedLanguages( + std::set &languages_for_types, + std::set &languages_for_expressions); + + static void Initialize(); + + static void Terminate(); + + DWARFASTParser *GetDWARFParser() override; + + void SetAddressByteSize(int byte_size) { m_pointer_byte_size = byte_size; } + + //------------------------------------------------------------------ + // llvm casting support + //------------------------------------------------------------------ + static bool classof(const TypeSystem *ts) { + return ts->getKind() == TypeSystem::eKindRust; + } + + //---------------------------------------------------------------------- + // CompilerDecl functions + //---------------------------------------------------------------------- + ConstString DeclGetName(void *opaque_decl) override; + ConstString DeclGetMangledName(void *opaque_decl) override; + CompilerDeclContext DeclGetDeclContext(void *opaque_decl) override; + + + //---------------------------------------------------------------------- + // CompilerDeclContext functions + //---------------------------------------------------------------------- + + std::vector + DeclContextFindDeclByName(void *opaque_decl_ctx, ConstString name, + const bool ignore_imported_decls) override; + bool DeclContextIsStructUnionOrClass(void *opaque_decl_ctx) override; + ConstString DeclContextGetName(void *opaque_decl_ctx) override; + ConstString DeclContextGetScopeQualifiedName(void *opaque_decl_ctx) override; + bool DeclContextIsClassMethod(void *opaque_decl_ctx, lldb::LanguageType *language_ptr, + bool *is_instance_method_ptr, + ConstString *language_object_name_ptr) override; + + //---------------------------------------------------------------------- + // Creating Types + //---------------------------------------------------------------------- + + CompilerType CreateVoidType(); + CompilerType CreateBoolType(const lldb_private::ConstString &name); + CompilerType CreateIntegralType(const lldb_private::ConstString &name, + bool is_signed, uint64_t byte_size, + bool is_char_type = false); + CompilerType CreateIntrinsicIntegralType(bool is_signed, uint64_t byte_size); + CompilerType CreateCharType(); + CompilerType CreateFloatType(const lldb_private::ConstString &name, + uint64_t byte_size); + CompilerType CreatePointerType(const lldb_private::ConstString &name, + const CompilerType &pointee_type, + uint32_t byte_size); + + CompilerType CreateArrayType(const CompilerType &element_type, + uint64_t length); + + CompilerType CreateTypedefType(const ConstString &name, CompilerType impl); + + CompilerType CreateFunctionType(const lldb_private::ConstString &name, + const CompilerType &return_type, + const std::vector &¶ms); + + CompilerType CreateStructType(const ConstString &name, uint32_t byte_size, + bool has_discriminant); + CompilerType CreateTupleType(const ConstString &name, uint32_t byte_size, + bool has_discriminant); + CompilerType CreateUnionType(const ConstString &name, uint32_t byte_size); + CompilerType CreateCLikeEnumType(const lldb_private::ConstString &name, + const CompilerType &underlying_type, + std::map &&values); + CompilerType CreateEnumType(const lldb_private::ConstString &name, + uint64_t byte_size, uint32_t discr_offset, + uint32_t discr_byte_size); + + void AddFieldToStruct(const CompilerType &struct_type, + const ConstString &name, const CompilerType &field_type, + uint32_t byte_offset, + bool is_default, uint64_t discriminant); + void FinishAggregateInitialization(const CompilerType &type); + + bool TypeHasDiscriminant(const CompilerType &type); + bool IsTupleType(const CompilerType &type); + + // Return true and set the out params if the type is a Rust enum; + // return false otherwise. + bool GetEnumDiscriminantLocation(const CompilerType &type, uint64_t &discr_offset, + uint64_t &discr_byte_size); + + // Given an actual discriminant value, find the correct enum variant + // type. + CompilerType FindEnumVariant(const CompilerType &type, uint64_t discriminant); + + //---------------------------------------------------------------------- + // Tests + //---------------------------------------------------------------------- + + bool IsArrayType(lldb::opaque_compiler_type_t type, + CompilerType *element_type, uint64_t *size, + bool *is_incomplete) override; + + bool IsAggregateType(lldb::opaque_compiler_type_t type) override; + + bool IsCharType(lldb::opaque_compiler_type_t type) override; + + bool IsCompleteType(lldb::opaque_compiler_type_t type) override; + + bool IsDefined(lldb::opaque_compiler_type_t type) override; + + bool IsFloatingPointType(lldb::opaque_compiler_type_t type, uint32_t &count, + bool &is_complex) override; + + bool IsFunctionType(lldb::opaque_compiler_type_t type, + bool *is_variadic_ptr = nullptr) override; + + size_t + GetNumberOfFunctionArguments(lldb::opaque_compiler_type_t type) override; + + CompilerType GetFunctionArgumentAtIndex(lldb::opaque_compiler_type_t type, + const size_t index) override; + + bool IsFunctionPointerType(lldb::opaque_compiler_type_t type) override; + + bool IsBlockPointerType(lldb::opaque_compiler_type_t type, + CompilerType *function_pointer_type_ptr) override; + + bool IsIntegerType(lldb::opaque_compiler_type_t type, + bool &is_signed) override; + + bool IsPossibleDynamicType(lldb::opaque_compiler_type_t type, + CompilerType *target_type, // Can pass nullptr + bool check_cplusplus, bool check_objc) override; + + bool IsPointerType(lldb::opaque_compiler_type_t type, + CompilerType *pointee_type = nullptr) override; + + bool IsScalarType(lldb::opaque_compiler_type_t type) override; + + bool IsVoidType(lldb::opaque_compiler_type_t type) override; + + bool IsBooleanType(lldb::opaque_compiler_type_t type); + + bool SupportsLanguage(lldb::LanguageType language) override; + + //---------------------------------------------------------------------- + // Type Completion + //---------------------------------------------------------------------- + + bool GetCompleteType(lldb::opaque_compiler_type_t type) override; + + //---------------------------------------------------------------------- + // AST related queries + //---------------------------------------------------------------------- + + uint32_t GetPointerByteSize() override; + + //---------------------------------------------------------------------- + // Accessors + //---------------------------------------------------------------------- + + ConstString GetTypeName(lldb::opaque_compiler_type_t type) override; + + uint32_t GetTypeInfo( + lldb::opaque_compiler_type_t type, + CompilerType *pointee_or_element_compiler_type = nullptr) override; + + lldb::LanguageType + GetMinimumLanguage(lldb::opaque_compiler_type_t type) override; + + lldb::TypeClass GetTypeClass(lldb::opaque_compiler_type_t type) override; + + //---------------------------------------------------------------------- + // Creating related types + //---------------------------------------------------------------------- + + CompilerType GetArrayElementType(lldb::opaque_compiler_type_t type, + uint64_t *stride = nullptr) override; + + CompilerType GetCanonicalType(lldb::opaque_compiler_type_t type) override; + + // Returns -1 if this isn't a function of if the function doesn't have a + // prototype + // Returns a value >= 0 if there is a prototype. + int GetFunctionArgumentCount(lldb::opaque_compiler_type_t type) override; + + CompilerType GetFunctionArgumentTypeAtIndex(lldb::opaque_compiler_type_t type, + size_t idx) override; + + CompilerType + GetFunctionReturnType(lldb::opaque_compiler_type_t type) override; + + size_t GetNumMemberFunctions(lldb::opaque_compiler_type_t type) override; + + TypeMemberFunctionImpl + GetMemberFunctionAtIndex(lldb::opaque_compiler_type_t type, + size_t idx) override; + + CompilerType GetPointeeType(lldb::opaque_compiler_type_t type) override; + + CompilerType GetPointerType(lldb::opaque_compiler_type_t type) override; + + //---------------------------------------------------------------------- + // Exploring the type + //---------------------------------------------------------------------- + + uint64_t GetBitSize(lldb::opaque_compiler_type_t type, + ExecutionContextScope *exe_scope) override; + + lldb::Encoding GetEncoding(lldb::opaque_compiler_type_t type, + uint64_t &count) override; + + lldb::Format GetFormat(lldb::opaque_compiler_type_t type) override; + + uint32_t GetNumChildren(lldb::opaque_compiler_type_t type, + bool omit_empty_base_classes) override; + + lldb::BasicType + GetBasicTypeEnumeration(lldb::opaque_compiler_type_t type) override; + + CompilerType GetBuiltinTypeForEncodingAndBitSize(lldb::Encoding encoding, + size_t bit_size) override; + + uint32_t GetNumFields(lldb::opaque_compiler_type_t type) override; + + CompilerType GetFieldAtIndex(lldb::opaque_compiler_type_t type, size_t idx, + std::string &name, uint64_t *bit_offset_ptr, + uint32_t *bitfield_bit_size_ptr, + bool *is_bitfield_ptr) override; + + uint32_t GetNumDirectBaseClasses(lldb::opaque_compiler_type_t type) override { + return 0; + } + + uint32_t + GetNumVirtualBaseClasses(lldb::opaque_compiler_type_t type) override { + return 0; + } + + CompilerType GetDirectBaseClassAtIndex(lldb::opaque_compiler_type_t type, + size_t idx, + uint32_t *bit_offset_ptr) override { + return CompilerType(); + } + + CompilerType GetVirtualBaseClassAtIndex(lldb::opaque_compiler_type_t type, + size_t idx, + uint32_t *bit_offset_ptr) override { + return CompilerType(); + } + + CompilerType GetChildCompilerTypeAtIndex( + lldb::opaque_compiler_type_t type, ExecutionContext *exe_ctx, size_t idx, + bool transparent_pointers, bool omit_empty_base_classes, + bool ignore_array_bounds, std::string &child_name, + uint32_t &child_byte_size, int32_t &child_byte_offset, + uint32_t &child_bitfield_bit_size, uint32_t &child_bitfield_bit_offset, + bool &child_is_base_class, bool &child_is_deref_of_parent, + ValueObject *valobj, uint64_t &language_flags) override; + + // Lookup a child given a name. This function will match base class names + // and member member names in "clang_type" only, not descendants. + uint32_t GetIndexOfChildWithName(lldb::opaque_compiler_type_t type, + const char *name, + bool omit_empty_base_classes) override; + + // Lookup a child member given a name. This function will match member names + // only and will descend into "clang_type" children in search for the first + // member in this class, or any base class that matches "name". + // TODO: Return all matches for a given name by returning a + // vector> + // so we catch all names that match a given child name, not just the first. + size_t + GetIndexOfChildMemberWithName(lldb::opaque_compiler_type_t type, + const char *name, bool omit_empty_base_classes, + std::vector &child_indexes) override; + + size_t GetNumTemplateArguments(lldb::opaque_compiler_type_t type) override { + return 0; + } + + //---------------------------------------------------------------------- + // Dumping types + //---------------------------------------------------------------------- + void DumpValue(lldb::opaque_compiler_type_t type, ExecutionContext *exe_ctx, + Stream *s, lldb::Format format, const DataExtractor &data, + lldb::offset_t data_offset, size_t data_byte_size, + uint32_t bitfield_bit_size, uint32_t bitfield_bit_offset, + bool show_types, bool show_summary, bool verbose, + uint32_t depth) override; + + bool DumpTypeValue(lldb::opaque_compiler_type_t type, Stream *s, + lldb::Format format, const DataExtractor &data, + lldb::offset_t data_offset, size_t data_byte_size, + uint32_t bitfield_bit_size, uint32_t bitfield_bit_offset, + ExecutionContextScope *exe_scope) override; + + void DumpTypeDescription( + lldb::opaque_compiler_type_t type) override; // Dump to stdout + + void DumpTypeDescription(lldb::opaque_compiler_type_t type, + Stream *s) override; + + bool IsRuntimeGeneratedType(lldb::opaque_compiler_type_t type) override; + + void DumpSummary(lldb::opaque_compiler_type_t type, ExecutionContext *exe_ctx, + Stream *s, const DataExtractor &data, + lldb::offset_t data_offset, size_t data_byte_size) override; + + // Converts "s" to a floating point value and place resulting floating + // point bytes in the "dst" buffer. + size_t ConvertStringToFloatValue(lldb::opaque_compiler_type_t type, + const char *s, uint8_t *dst, + size_t dst_size) override; + + bool IsPointerOrReferenceType(lldb::opaque_compiler_type_t type, + CompilerType *pointee_type = nullptr) override; + + unsigned GetTypeQualifiers(lldb::opaque_compiler_type_t type) override; + + bool IsCStringType(lldb::opaque_compiler_type_t type, + uint32_t &length) override; + + size_t GetTypeBitAlign(lldb::opaque_compiler_type_t type) override; + + CompilerType GetBasicTypeFromAST(lldb::BasicType basic_type) override; + + bool IsBeingDefined(lldb::opaque_compiler_type_t type) override; + + bool IsConst(lldb::opaque_compiler_type_t type) override; + + uint32_t IsHomogeneousAggregate(lldb::opaque_compiler_type_t type, + CompilerType *base_type_ptr) override; + + bool IsPolymorphicClass(lldb::opaque_compiler_type_t type) override; + + bool IsTypedefType(lldb::opaque_compiler_type_t type) override; + + // If the current object represents a typedef type, get the underlying type + CompilerType GetTypedefedType(lldb::opaque_compiler_type_t type) override; + + bool IsVectorType(lldb::opaque_compiler_type_t type, + CompilerType *element_type, uint64_t *size) override; + + CompilerType + GetFullyUnqualifiedType(lldb::opaque_compiler_type_t type) override; + + CompilerType GetNonReferenceType(lldb::opaque_compiler_type_t type) override; + + bool IsReferenceType(lldb::opaque_compiler_type_t type, + CompilerType *pointee_type = nullptr, + bool *is_rvalue = nullptr) override; + + CompilerDeclContext GetTranslationUnitDecl(); + CompilerDeclContext GetNamespaceDecl(CompilerDeclContext parent, const ConstString &name); + CompilerDeclContext GetDeclContextDeclContext(CompilerDeclContext child); + CompilerDecl GetDecl(CompilerDeclContext parent, const ConstString &name, + const ConstString &mangled); + + // When emitting a call we need to emit tags for the aggregate + // types, so that we can avoid trying to define a type in a function + // parameter. This class manages the names. + struct TypeNameMap { + std::map name_map; + unsigned counter = 0; + // Holds the source code for the typedefs themselves. + std::string typedefs; + + bool Tag(RustType *type, std::string *tagname) { + auto iter = name_map.find(type); + if (iter == name_map.end()) { + *tagname = "tag" + std::to_string(counter++); + name_map[type] = *tagname; + return true; + } + *tagname = iter->second; + return false; + } + }; + + bool GetCABITypeDeclaration(CompilerType type, const std::string &varname, + TypeNameMap *name_map, std::string *result); + +private: + int m_pointer_byte_size; + std::map> m_types; + std::set> m_anon_types; + std::unique_ptr m_dwarf_ast_parser_ap; + + std::unique_ptr m_tu_decl; + + RustType *FindCachedType(const lldb_private::ConstString &name); + CompilerType CacheType(const lldb_private::ConstString &name, RustType *new_type); + + RustASTContext(const RustASTContext &) = delete; + const RustASTContext &operator=(const RustASTContext &) = delete; +}; + +class RustASTContextForExpr : public RustASTContext { +public: + RustASTContextForExpr(lldb::TargetSP target) : m_target_wp(target) {} + UserExpression * + GetUserExpression(llvm::StringRef expr, llvm::StringRef prefix, + lldb::LanguageType language, + Expression::ResultType desired_type, + const EvaluateExpressionOptions &options) override; + +private: + lldb::TargetWP m_target_wp; +}; +} +#endif // liblldb_RustASTContext_h_ diff --git a/lldb/include/lldb/Symbol/TypeSystem.h b/lldb/include/lldb/Symbol/TypeSystem.h index 6afbd188a2342b..bacb09b9381951 100644 --- a/lldb/include/lldb/Symbol/TypeSystem.h +++ b/lldb/include/lldb/Symbol/TypeSystem.h @@ -69,6 +69,7 @@ class TypeSystem : public PluginInterface { eKindClang, eKindSwift, eKindOCaml, + eKindRust, kNumKinds }; diff --git a/lldb/include/lldb/lldb-forward.h b/lldb/include/lldb/lldb-forward.h index 92aa5bb611c3c5..3a55a491fcda28 100644 --- a/lldb/include/lldb/lldb-forward.h +++ b/lldb/include/lldb/lldb-forward.h @@ -107,6 +107,7 @@ class File; class FileSpec; class FileSpecList; class Flags; +class RustASTContext; class TypeCategoryImpl; class FormatManager; class FormattersMatchCandidate; @@ -354,6 +355,7 @@ typedef std::shared_ptr FileSP; typedef std::shared_ptr FunctionSP; typedef std::shared_ptr FunctionCallerSP; typedef std::shared_ptr FuncUnwindersSP; +typedef std::unique_ptr RustASTContextUP; typedef std::shared_ptr InlineFunctionInfoSP; typedef std::shared_ptr InstructionSP; typedef std::shared_ptr diff --git a/lldb/packages/Python/lldbsuite/test/lang/rust/calls/TestRustCalls.py b/lldb/packages/Python/lldbsuite/test/lang/rust/calls/TestRustCalls.py new file mode 100644 index 00000000000000..fb78da1a4112b2 --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/lang/rust/calls/TestRustCalls.py @@ -0,0 +1,82 @@ +"""Test Rust function calls.""" + +import os +import time +import unittest2 +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + +class TestRustExpressions(TestBase): + mydir = TestBase.compute_mydir(__file__) + + @add_test_categories(['pyapi']) + @no_debug_info_test + @skipUnlessRustInstalled + def test_with_dsym_and_python_api(self): + """Test Rust function calls.""" + self.buildRust() + self.launchProcess() + self.rust_calls() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers to break inside main(). + self.main_source = "main.rs" + self.break_line = line_number(self.main_source, '// breakpoint') + + def launchProcess(self): + exe = os.path.join(os.getcwd(), "main") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + bpt = target.BreakpointCreateByLocation(self.main_source, self.break_line) + self.assertTrue(bpt, VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple(None, None, self.get_process_working_directory()) + + self.assertTrue(process, PROCESS_IS_VALID) + + # The stop reason of the thread should be breakpoint. + thread_list = lldbutil.get_threads_stopped_at_breakpoint(process, bpt) + + # Make sure we stopped at the first breakpoint. + self.assertTrue( + len(thread_list) != 0, + "No thread stopped at our breakpoint.") + self.assertTrue(len(thread_list) == 1, + "More than one thread stopped at our breakpoint.") + + frame = thread_list[0].GetFrameAtIndex(0) + self.assertTrue(frame, "Got a valid frame 0 frame.") + + def rust_calls(self): + frame = self.frame() + v = frame.EvaluateExpression("not(true)") + self.assertEqual("(bool) = false", str(v)) + v = frame.EvaluateExpression("not(false)") + self.assertEqual("(bool) = true", str(v)) + v = frame.EvaluateExpression("constant()") + self.assertEqual("(i64) = -23", str(v)) + v = frame.EvaluateExpression("cst2(zzq)") + self.assertEqual("(i16) = -24", str(v)) + v = frame.EvaluateExpression("nil()") + self.assertEqual("(()) = {}", str(v)) + v = frame.EvaluateExpression("add1(7)") + self.assertEqual("(u32) = 8", str(v)) + v = frame.EvaluateExpression("add1d(74.0)") + self.assertEqual("(f64) = 75", str(v)) + v = frame.EvaluateExpression("add1s(Struct{field:7}).field") + self.assertEqual("(u8) field = 8", str(v)) + v = frame.EvaluateExpression("add1ts(TupleStruct(99)).0") + self.assertEqual("(u8) = 100", str(v)) + # v = frame.EvaluateExpression("unifyplus1(SimpleEnum::One{f1:98}).0") + # self.assertEqual("(u16) = 99", str(v)) + # v = frame.EvaluateExpression("add1ue(UnivariantEnum::Single(17)).0") + # self.assertEqual("(u8) = 18", str(v)) + v = frame.EvaluateExpression("sum(0, 1, 2, 3.0, 4.0)") + self.assertEqual("(f64) = 10", str(v)) diff --git a/lldb/packages/Python/lldbsuite/test/lang/rust/calls/main.rs b/lldb/packages/Python/lldbsuite/test/lang/rust/calls/main.rs new file mode 100644 index 00000000000000..6f088b46e78c29 --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/lang/rust/calls/main.rs @@ -0,0 +1,86 @@ +#![allow(dead_code)] +#![allow(unused_variables)] +#![allow(unused_assignments)] + +fn do_nothing() { } + +pub fn not(x: bool) -> bool { + !x +} + +pub fn constant() -> i64 { + -23 +} + +pub fn cst2(x: ()) -> i16 { + -24 +} + +pub fn nil() -> () { + () +} + +pub fn add1(x: u32) -> u32 { + x + 1 +} + +pub fn add1d(x: f64) -> f64 { + x + 1f64 +} + +pub struct Struct { + field: u8 +} + +pub fn add1s(x: Struct) -> Struct { + Struct{field: x.field + 1} +} + +pub struct TupleStruct(u8); + +pub fn add1ts(x: TupleStruct) -> TupleStruct { + TupleStruct(x.0 + 1) +} + +pub enum SimpleEnum { + One{f1: u16}, + Two(u16) +} + +pub fn unifyplus1(x: SimpleEnum) -> SimpleEnum { + match x { + SimpleEnum::One{f1: v} => SimpleEnum::Two(v + 1), + SimpleEnum::Two(v) => SimpleEnum::Two(v + 1) + } +} + +pub enum UnivariantEnum { + Single(u8) +} + +pub fn add1ue(x: UnivariantEnum) -> u8 { + match x { + UnivariantEnum::Single(v) => v + 1 + } +} + +pub fn sum(a: u8, b: u16, c: u32, d: f32, e: f64) -> f64 { + a as f64 + b as f64 + c as f64 + d as f64 + e +} + +fn main() { + let a = not(false); + let b = constant(); + let zzq = (); + let c = cst2(zzq); + let d = nil(); + let e = add1(7); + let f = add1d(7.0); + let g = add1s(Struct{field:8}); + let h = add1ts(TupleStruct(72)); + let i = unifyplus1(SimpleEnum::Two(11)); + let j = add1ue(UnivariantEnum::Single(99)); + let k = sum(0, 1, 2, 3.0, 4.0); + + do_nothing(); // breakpoint +} diff --git a/lldb/packages/Python/lldbsuite/test/lang/rust/expressions/TestRustExpressions.py b/lldb/packages/Python/lldbsuite/test/lang/rust/expressions/TestRustExpressions.py new file mode 100644 index 00000000000000..5ab5c7f6334d2f --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/lang/rust/expressions/TestRustExpressions.py @@ -0,0 +1,164 @@ +"""Test the Rust expression parser and evaluator.""" + +import os +import time +import unittest2 +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + +class TestRustExpressions(TestBase): + mydir = TestBase.compute_mydir(__file__) + + @add_test_categories(['pyapi']) + @no_debug_info_test + @skipUnlessRustInstalled + def test_with_dsym_and_python_api(self): + """Test Rust expression parser and evaluator.""" + self.buildRust() + self.launchProcess() + self.rust_expressions() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers to break inside main(). + self.main_source = "main.rs" + self.break_line = line_number(self.main_source, '// breakpoint') + + def launchProcess(self): + exe = os.path.join(os.getcwd(), "main") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + bpt = target.BreakpointCreateByLocation(self.main_source, self.break_line) + self.assertTrue(bpt, VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple(None, None, self.get_process_working_directory()) + + self.assertTrue(process, PROCESS_IS_VALID) + + # The stop reason of the thread should be breakpoint. + thread_list = lldbutil.get_threads_stopped_at_breakpoint(process, bpt) + + # Make sure we stopped at the first breakpoint. + self.assertTrue( + len(thread_list) != 0, + "No thread stopped at our breakpoint.") + self.assertTrue(len(thread_list) == 1, + "More than one thread stopped at our breakpoint.") + + frame = thread_list[0].GetFrameAtIndex(0) + self.assertTrue(frame, "Got a valid frame 0 frame.") + + def rust_expressions(self): + frame = self.frame() + v = frame.EvaluateExpression("1") + self.assertEqual("(i32) = 1", str(v)) + v = frame.EvaluateExpression("1.25") + self.assertEqual("(f64) = 1.25", str(v)) + v = frame.EvaluateExpression("1 + 2") + self.assertEqual("(i32) = 3", str(v)) + v = frame.EvaluateExpression("+ 2 + - 1") + self.assertEqual("(i32) = 1", str(v)) + v = frame.EvaluateExpression("3 + 1 / 4") + self.assertEqual("(i32) = 3", str(v)) + v = frame.EvaluateExpression("(3 + 1) / 4") + self.assertEqual("(i32) = 1", str(v)) + v = frame.EvaluateExpression("5 % 4") + self.assertEqual("(i32) = 1", str(v)) + v = frame.EvaluateExpression("sizeof(4u8)") + self.assertEqual("(usize) = 1", str(v)) + v = frame.EvaluateExpression("!0xffu16") + self.assertEqual("(u16) = 65280", str(v)) + v = frame.EvaluateExpression("1 << 3") + self.assertEqual("(i32) = 8", str(v)) + v = frame.EvaluateExpression("-1 < 3") + self.assertEqual("(bool) = true", str(v)) + v = frame.EvaluateExpression("3 <= 3") + self.assertEqual("(bool) = true", str(v)) + v = frame.EvaluateExpression("3 >= 3") + self.assertEqual("(bool) = true", str(v)) + v = frame.EvaluateExpression("-9 > -11") + self.assertEqual("(bool) = true", str(v)) + v = frame.EvaluateExpression("-27 != -17") + self.assertEqual("(bool) = true", str(v)) + v = frame.EvaluateExpression("-27 == -17") + self.assertEqual("(bool) = false", str(v)) + v = frame.EvaluateExpression("5.0 / 4") + self.assertEqual("(f64) = 1.25", str(v)) + v = frame.EvaluateExpression("'c'") + self.assertEqual("(char) = 'c'", str(v)) + v = frame.EvaluateExpression("true") + self.assertEqual("(bool) = true", str(v)) + v = frame.EvaluateExpression("false") + self.assertEqual("(bool) = false", str(v)) + v = frame.EvaluateExpression("!true") + self.assertEqual("(bool) = false", str(v)) + v = frame.EvaluateExpression("vstruct.field1") + self.assertEqual("(u8) field1 = 23", str(v)) + v = frame.EvaluateExpression("vtuplestruct.0") + self.assertEqual("(u8) = 23", str(v)) + v = frame.EvaluateExpression("vtuple.0") + self.assertEqual("(u8) = 23", str(v)) + v = frame.EvaluateExpression("vunion.field2") + self.assertEqual("(char) field2 = 'Q'", str(v)) + v = frame.EvaluateExpression("vi8array[2]") + self.assertEqual("(i8) [2] = 3", str(v)) + v = frame.EvaluateExpression("*vboolpointer") + self.assertEqual("(bool) *vboolpointer = true", str(v)) + v = frame.EvaluateExpression("*vcharpointer") + self.assertEqual("(char) *vcharpointer = 'Q'", str(v)) + v = frame.EvaluateExpression("*vi8ref") + self.assertEqual("(i8) *vi8ref = -23", str(v)) + v = frame.EvaluateExpression("*&vi8") + self.assertEqual("(i8) *&vi8 = -23", str(v)) + v = frame.EvaluateExpression("*vu8ref") + self.assertEqual("(u8) *vu8ref = 23", str(v)) + v = frame.EvaluateExpression("vsimpleenum", lldb.eDynamicDontRunTarget) + self.assertEqual("(main::SimpleEnum::Two) vsimpleenum = (83, 92)", str(v)) + v = frame.EvaluateExpression("vsimpleenum.1") + self.assertEqual("(u16) = 92", str(v)) + v = frame.EvaluateExpression("vsimpleenum1.f2") + self.assertEqual("(u8) f2 = 83", str(v)) + v = frame.EvaluateExpression("vi8 = 7") + self.assertEqual("(i8) vi8 = 7", str(v)) + # Double check. + v = frame.EvaluateExpression("*&vi8") + self.assertEqual("(i8) *&vi8 = 7", str(v)) + v = frame.EvaluateExpression("vi8 += 7") + self.assertEqual("(i8) vi8 = 14", str(v)) + # Double check. + v = frame.EvaluateExpression("*&vi8") + self.assertEqual("(i8) *&vi8 = 14", str(v)) + v = frame.EvaluateExpression("[23i64; 5]") + self.assertEqual("([i64; 5]) = ([0] = 23, [1] = 23, [2] = 23, [3] = 23, [4] = 23)", str(v)) + v = frame.EvaluateExpression("23 as u8") + self.assertEqual("(u8) = 23", str(v)) + v = frame.EvaluateExpression('b"hi"') + self.assertEqual("([u8; 2]) * = ([0] = 104, [1] = 105)", str(v)) + # FIXME need pretty-printing for &str + # v = frame.EvaluateExpression('"hi"') + # self.assertEqual("fixme", str(v)) + v = frame.EvaluateExpression("Struct { field1: 8, field2: 'c'}") + self.assertEqual("(main::Struct) * = (field1 = 8, field2 = 'c')", str(v)) + v = frame.EvaluateExpression("Struct { field1: 8, .. vstruct}") + self.assertEqual("(main::Struct) * = (field1 = 8, field2 = 'Q')", str(v)) + v = frame.EvaluateExpression("TupleStruct(24, 'R')") + self.assertEqual("(main::TupleStruct) * = (24, 'R')", str(v)) + v = frame.EvaluateExpression("0..5") + self.assertEqual("(core::ops::range::Range) * = (start = 0, end = 5)", str(v)) + # v = frame.EvaluateExpression("0..=5") + # self.assertEqual("(core::ops::range::RangeInclusive) * = (start = 0, end = 5)", str(v)) + v = frame.EvaluateExpression("..5") + self.assertEqual("(core::ops::range::RangeTo) * = (end = 5)", str(v)) + # v = frame.EvaluateExpression("..=5") + # self.assertEqual("(core::ops::range::RangeToInclusive * = (end = 5)", str(v)) + v = frame.EvaluateExpression("0..") + self.assertEqual("(core::ops::range::RangeFrom) * = (start = 0)", str(v)) + # Can't allocate a zero-length object + # v = frame.EvaluateExpression("..") + # self.assertEqual("(core::ops::range::RangeFull) * = ()", str(v)) diff --git a/lldb/packages/Python/lldbsuite/test/lang/rust/expressions/main.rs b/lldb/packages/Python/lldbsuite/test/lang/rust/expressions/main.rs new file mode 100644 index 00000000000000..e656066e38bf2f --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/lang/rust/expressions/main.rs @@ -0,0 +1,86 @@ +#![allow(dead_code)] +#![allow(unused_variables)] +#![allow(unused_assignments)] + +fn do_nothing() { } + +pub struct Struct { + field1: u8, + field2: char, +} + +struct TupleStruct(u8, char); + +pub union Union { + field1: u8, + field2: char, +} + +pub enum CLikeEnum { + MinusOne = -1, + Zero, + One, +} + +pub enum SimpleEnum { + One{f1: u8, f2: u8}, + Two(u16, u16) +} + +pub enum OptimizedEnum { + Null, + NonNull(Box) +} + +fn main() { + let vbool: bool = true; + + let mut vchar: char = 'Q'; + + let vi8: i8 = -23; + let mut vu8: u8 = 23; + let vi16: i16 = -2323; + let vu16: u16 = 2323; + let vi32: i32 = -232323; + let vu32: u32 = 232323; + let vi64: i64 = -23232323; + let vu64: u64 = 23232323; + + let visize: isize = -23232323; + let vusize: usize = 23232323; + + let vf32: f32 = 5.25; + let vf64: f64 = 7.5; + + let vi8array : [i8; 4] = [1,2,3,4]; + + let empty = (); + + let vstruct = Struct { field1: 23, field2: 'Q' }; + let vtuplestruct = TupleStruct(23, 'Q'); + let vtuple = (23u8, 'Q'); + let vunion = Union { field2: 'Q' }; + + let vboolpointer = &vbool as *const bool; + let vcharpointer = &mut vchar as *mut char; + let vi8ref = &vi8; + let vu8ref = &mut vu8; + + let vclikeenum = CLikeEnum::MinusOne; + + let vsimpleenum = SimpleEnum::Two(83, 92); + let vsimpleenum1 = SimpleEnum::One{f1: 92, f2: 83}; + let voptenum = OptimizedEnum::Null; + + let vbstr = b"bytes"; + let vstr = "string"; + + let vsimpleenum_ref = &vsimpleenum; + + let vrange = 0..5; + let vrange_from = 0..; + let vrange_to = ..5; + let vrange_full = ..; + + do_nothing(); // breakpoint +} diff --git a/lldb/packages/Python/lldbsuite/test/lang/rust/names/TestRustNames.py b/lldb/packages/Python/lldbsuite/test/lang/rust/names/TestRustNames.py new file mode 100644 index 00000000000000..c7dacdca5e21c3 --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/lang/rust/names/TestRustNames.py @@ -0,0 +1,88 @@ +"""Test name lookup for Rust.""" + +from __future__ import print_function + +import lldb +import os + +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class TestRustNames(TestBase): + mydir = TestBase.compute_mydir(__file__) + + @add_test_categories(['pyapi']) + @no_debug_info_test + @skipUnlessRustInstalled + def test_with_dsym_and_python_api(self): + """Test Rust name lookup.""" + self.buildRust() + self.launchProcess() + self.check_names() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers to break inside main(). + self.main_source = "main.rs" + self.break_line = line_number(self.main_source, '// breakpoint') + + def launchProcess(self): + exe = os.path.join(os.getcwd(), "main") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + bpt = target.BreakpointCreateByLocation(self.main_source, self.break_line) + self.assertTrue(bpt, VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple(None, None, self.get_process_working_directory()) + + self.assertTrue(process, PROCESS_IS_VALID) + + # The stop reason of the thread should be breakpoint. + thread_list = lldbutil.get_threads_stopped_at_breakpoint(process, bpt) + + # Make sure we stopped at the first breakpoint. + self.assertTrue( + len(thread_list) != 0, + "No thread stopped at our breakpoint.") + self.assertTrue(len(thread_list) == 1, + "More than one thread stopped at our breakpoint.") + + frame = thread_list[0].GetFrameAtIndex(0) + self.assertTrue(frame, "Got a valid frame 0 frame.") + + def check_names(self): + frame = self.frame() + namelist = [ + ('::VALUE', 7777, 'v0'), + ('::m1::VALUE', 1, 'v1'), + ('::m1::m1_1::VALUE', 11, 'v1_1'), + ('::m1::m1_2::VALUE', 12, 'v1_2'), + ('::m2::VALUE', 2, 'v2'), + ('::m2::m2_1::VALUE', 21, 'v2_1'), + ('::m2::m2_1::m2_1_2::VALUE', 212, 'v2_1_2'), + ('::m2::m2_2::VALUE', 22, 'v22'), + + ('VALUE', 212, 'v'), + ('self::VALUE', 212, 'svalue'), + ('super::VALUE', 21, 'suvalue'), + ('self::super::VALUE', 21, 'ssuvalue'), + ('super::super::VALUE', 2, 'susuvalue'), + ('self::super::super::VALUE', 2, 'ssusuvalue'), + ('super::super::super::VALUE', 7777, 'sususuvalue'), + ('self::super::super::super::VALUE', 7777, 'ssususuvalue'), + + ('m2_1_2_1::VALUE', 2121, 'rv'), + ('self::m2_1_2_1::VALUE', 2121, 'srv'), + ('super::m2_1_2::m2_1_2_1::VALUE', 2121, 'srv'), + ] + for (name, value, local) in namelist: + c = frame.EvaluateExpression(local) + self.assertEqual(c.GetValueAsSigned(), value, 'checking compiler ' + name) + v = frame.EvaluateExpression(name) + self.assertEqual(v.GetValueAsSigned(), value, 'checking ' + name) diff --git a/lldb/packages/Python/lldbsuite/test/lang/rust/names/main.rs b/lldb/packages/Python/lldbsuite/test/lang/rust/names/main.rs new file mode 100644 index 00000000000000..6556d3304c3067 --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/lang/rust/names/main.rs @@ -0,0 +1,69 @@ +#![allow(dead_code)] +#![allow(unused_variables)] +#![allow(unused_assignments)] + +fn do_nothing() { } + +pub static VALUE : u16 = 7777; + +pub mod m1 { + pub static VALUE : u16 = 1; + + pub mod m1_1 { + pub static VALUE : u16 = 11; + } + + pub mod m1_2 { + pub static VALUE : u16 = 12; + } +} + +pub mod m2 { + pub static VALUE : u16 = 2; + + pub mod m2_1 { + pub static VALUE : u16 = 21; + + pub mod m2_1_2 { + pub static VALUE : u16 = 212; + + pub mod m2_1_2_1 { + pub static VALUE : u16 = 2121; + } + + pub fn f() { + let v0 = ::VALUE; + let v1 = ::m1::VALUE; + let v1_1 = ::m1::m1_1::VALUE; + let v1_2 = ::m1::m1_2::VALUE; + let v2 = ::m2::VALUE; + let v2_1 = ::m2::m2_1::VALUE; + let v2_1_2 = ::m2::m2_1::m2_1_2::VALUE; + let v22 = ::m2::m2_2::VALUE; + + let v = VALUE; + let svalue = self::VALUE; + let suvalue = super::VALUE; + let ssuvalue = self::super::VALUE; + let susuvalue = super::super::VALUE; + let ssusuvalue = self::super::super::VALUE; + let sususuvalue = super::super::super::VALUE; + let ssususuvalue = self::super::super::super::VALUE; + + let rv = m2_1_2_1::VALUE; + let srv = self::m2_1_2_1::VALUE; + let ssrv = super::m2_1_2::m2_1_2_1::VALUE; + + ::do_nothing(); // breakpoint + } + } + } + + pub mod m2_2 { + pub static VALUE : u16 = 22; + } +} + +fn main() { + m2::m2_1::m2_1_2::f(); +} diff --git a/lldb/packages/Python/lldbsuite/test/lang/rust/types/TestRustASTContext.py b/lldb/packages/Python/lldbsuite/test/lang/rust/types/TestRustASTContext.py new file mode 100644 index 00000000000000..f5704a01d7101a --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/lang/rust/types/TestRustASTContext.py @@ -0,0 +1,172 @@ +"""Test DWARF type parsing for Rust.""" + +from __future__ import print_function + +import lldb +import os + +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class TestRustASTContext(TestBase): + mydir = TestBase.compute_mydir(__file__) + + @add_test_categories(['pyapi']) + @no_debug_info_test + @skipUnlessRustInstalled + def test_with_dsym_and_python_api(self): + """Test RustASTContext DWARF parsing.""" + self.buildRust() + self.launchProcess() + self.init_typelist() + self.check_types() + self.check_main_vars() + self.check_main_function() + self.check_structs() + self.check_enums() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers to break inside main(). + self.main_source = "main.rs" + self.break_line = line_number(self.main_source, '// breakpoint') + + def launchProcess(self): + exe = os.path.join(os.getcwd(), "main") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + bpt = target.BreakpointCreateByLocation(self.main_source, self.break_line) + self.assertTrue(bpt, VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple(None, None, self.get_process_working_directory()) + + self.assertTrue(process, PROCESS_IS_VALID) + + # The stop reason of the thread should be breakpoint. + thread_list = lldbutil.get_threads_stopped_at_breakpoint(process, bpt) + + # Make sure we stopped at the first breakpoint. + self.assertTrue( + len(thread_list) != 0, + "No thread stopped at our breakpoint.") + self.assertTrue(len(thread_list) == 1, + "More than one thread stopped at our breakpoint.") + + frame = thread_list[0].GetFrameAtIndex(0) + self.assertTrue(frame, "Got a valid frame 0 frame.") + + def init_typelist(self): + address_size = self.target().GetAddressByteSize() + self._typelist = [] + for (name, size, value) in [ + ('bool', 1, 'true'), + ('char', 4, "'Q'"), + ('i8', 1, '-23'), + ('u8', 1, '23'), + ('i16', 2, '-2323'), + ('u16', 2, '2323'), + ('i32', 4, '-232323'), + ('u32', 4, '232323'), + ('i64', 8, '-23232323'), + ('u64', 8, '23232323'), + ('isize', address_size, '-23232323'), + ('usize', address_size, '23232323'), + ('f32', 4, '5.25'), + ('f64', 8, '7.5'), + ]: + self._typelist.append((name, 'v' + name, size, value)) + + def check_type(self, name, size, typeclass): + tl = self.target().FindTypes(name) + self.assertTrue(len(tl) > 0) + t = list(tl)[0] + self.assertEqual(name, t.name) + self.assertEqual(typeclass, t.type) + self.assertEqual(size, t.size) + + def check_types(self): + for (name, vname, size, value) in self._typelist: + self.check_type(name, size, lldb.eTypeClassBuiltin) + + def var(self, name): + var = self.frame().FindVariable(name) + self.assertTrue(var.IsValid(), "%s %s" % (VALID_VARIABLE, name)) + return var + + def check_main_vars(self): + mytypelist = self._typelist[:] + # Not in _typelist because it isn't eTypeClassBuiltin; and + # note that the output of "{}" can't be changed. + mytypelist.append(('()', 'empty', 0, '{}')) + # Note there doesn't seem to be a way to customize the array + # formatting to be more rust-like. + mytypelist.append(('[i8; 4]', 'vi8array', 4, '([0] = 1, [1] = 2, [2] = 3, [3] = 4)')) + address_size = self.target().GetAddressByteSize() + mytypelist.append(('*const bool', 'vboolpointer', address_size, None)) + mytypelist.append(('*mut char', 'vcharpointer', address_size, None)) + mytypelist.append(('&i8', 'vi8ref', address_size, None)) + mytypelist.append(('&mut u8', 'vu8ref', address_size, None)) + mytypelist.append(('main::CLikeEnum', 'vclikeenum', 1, 'main::CLikeEnum::MinusOne')) + + for (name, vname, size, value) in mytypelist: + v = self.var(vname) + self.assertEqual(name, v.GetType().name) + self.assertEqual(size, v.GetType().GetByteSize()) + # Some values can't really be checked. + if value is not None: + expected = "(" + name + ") " + vname + " = " + value + self.assertEqual(expected, str(v)) + # Handy for debugging. + # else: + # print("GOT === " + str(v)) + + def check_main_function(self): + fn_type = self.frame().GetFunction().GetType() + self.assertTrue(fn_type.IsFunctionType()) + self.assertEqual(len(fn_type.GetFunctionArgumentTypes()), 0) + self.assertEqual(fn_type.GetFunctionReturnType().name, '()') + + def check_structs(self): + for (vname, typename, m0name, m1name, desc) in [ + ('vstruct', 'main::Struct', 'field1', 'field2', + 'struct main::Struct {\n field1: u8,\n field2: char\n}'), + ('vtuplestruct', 'main::TupleStruct', None, None, + 'struct main::TupleStruct (\n u8,\n char\n)'), + ('vtuple', '(u8, char)', None, None, + '(\n u8,\n char\n)'), + ('vunion', 'main::Union', 'field1', 'field2', + 'union main::Union {\n field1: u8,\n field2: char\n}'), + ]: + v = self.var(vname) + vtype = v.GetType() + self.assertEqual(str(vtype), desc) + self.assertEqual(typename, vtype.name) + self.assertTrue(vtype.IsTypeComplete()) + self.assertEqual(vtype.GetNumberOfFields(), 2) + m0 = vtype.GetFieldAtIndex(0) + self.assertEqual(m0.GetType().name, 'u8') + self.assertEqual(m0.GetName(), m0name) + m1 = vtype.GetFieldAtIndex(1) + self.assertEqual(m1.GetType().name, 'char') + self.assertEqual(m1.GetName(), m1name) + + def check_enums(self): + address_size = self.target().GetAddressByteSize() + mytypelist = [] + mytypelist.append(('main::SimpleEnum::Two', 'vsimpleenum', 6, '(83, 92)')) + mytypelist.append(('main::OptimizedEnum::Null', 'voptenum', address_size, '{}')) + mytypelist.append(('main::OptimizedEnum::NonNull', 'voptenum2', address_size, None)) + for (name, vname, size, value) in mytypelist: + v = self.var(vname).dynamic + self.assertEqual(name, v.GetType().name) + self.assertEqual(size, v.GetType().GetByteSize()) + # Some values can't really be checked. + if value is not None: + expected = "(" + name + ") " + vname + " = " + value + self.assertEqual(expected, str(v.dynamic)) diff --git a/lldb/packages/Python/lldbsuite/test/lang/rust/types/main.rs b/lldb/packages/Python/lldbsuite/test/lang/rust/types/main.rs new file mode 100644 index 00000000000000..797a412e5ccc99 --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/lang/rust/types/main.rs @@ -0,0 +1,76 @@ +#![allow(dead_code)] +#![allow(unused_variables)] +#![allow(unused_assignments)] + +fn do_nothing() { } + +pub struct Struct { + field1: u8, + field2: char, +} + +struct TupleStruct(u8, char); + +pub union Union { + field1: u8, + field2: char, +} + +pub enum CLikeEnum { + MinusOne = -1, + Zero, + One, +} + +pub enum SimpleEnum { + One(u8, u8), + Two(u16, u16) +} + +pub enum OptimizedEnum { + Null, + NonNull(Box) +} + +fn main() { + let vbool: bool = true; + + let mut vchar: char = 'Q'; + + let vi8: i8 = -23; + let mut vu8: u8 = 23; + let vi16: i16 = -2323; + let vu16: u16 = 2323; + let vi32: i32 = -232323; + let vu32: u32 = 232323; + let vi64: i64 = -23232323; + let vu64: u64 = 23232323; + + let visize: isize = -23232323; + let vusize: usize = 23232323; + + let vf32: f32 = 5.25; + let vf64: f64 = 7.5; + + let vi8array : [i8; 4] = [1,2,3,4]; + + let empty = (); + + let vstruct = Struct { field1: 23, field2: 'Q' }; + let vtuplestruct = TupleStruct(23, 'Q'); + let vtuple = (23u8, 'Q'); + let vunion = Union { field2: 'Q' }; + + let vboolpointer = &vbool as *const bool; + let vcharpointer = &mut vchar as *mut char; + let vi8ref = &vi8; + let vu8ref = &mut vu8; + + let vclikeenum = CLikeEnum::MinusOne; + + let vsimpleenum = SimpleEnum::Two(83, 92); + let voptenum = OptimizedEnum::Null; + let voptenum2 = OptimizedEnum::NonNull(Box::new(7)); + + do_nothing(); // breakpoint +} diff --git a/lldb/source/API/SystemInitializerFull.cpp b/lldb/source/API/SystemInitializerFull.cpp index 42dea6a01abad3..fa10d3fdf6a603 100644 --- a/lldb/source/API/SystemInitializerFull.cpp +++ b/lldb/source/API/SystemInitializerFull.cpp @@ -24,6 +24,7 @@ #include "lldb/Initialization/SystemInitializerCommon.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/RustASTContext.h" #include "lldb/Utility/Timer.h" #include "Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.h" @@ -58,10 +59,12 @@ #include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h" #include "Plugins/Language/ObjC/ObjCLanguage.h" #include "Plugins/Language/ObjCPlusPlus/ObjCPlusPlusLanguage.h" +#include "Plugins/Language/Rust/RustLanguage.h" #include "Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h" #include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.h" #include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h" #include "Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.h" +#include "Plugins/LanguageRuntime/Rust/RustLanguageRuntime.h" #include "Plugins/MemoryHistory/asan/MemoryHistoryASan.h" #include "Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.h" #include "Plugins/ObjectFile/ELF/ObjectFileELF.h" @@ -312,6 +315,7 @@ SystemInitializerFull::Initialize(const InitializerOptions &options) { llvm::InitializeAllDisassemblers(); ClangASTContext::Initialize(); + RustASTContext::Initialize(); ABIMacOSX_i386::Initialize(); ABIMacOSX_arm::Initialize(); @@ -358,10 +362,12 @@ SystemInitializerFull::Initialize(const InitializerOptions &options) { AppleObjCRuntimeV1::Initialize(); SystemRuntimeMacOSX::Initialize(); RenderScriptRuntime::Initialize(); + RustLanguageRuntime::Initialize(); CPlusPlusLanguage::Initialize(); ObjCLanguage::Initialize(); ObjCPlusPlusLanguage::Initialize(); + RustLanguage::Initialize(); #if defined(_WIN32) ProcessWindows::Initialize(); @@ -443,6 +449,7 @@ void SystemInitializerFull::Terminate() { PluginManager::Terminate(); ClangASTContext::Terminate(); + RustASTContext::Terminate(); ArchitectureArm::Terminate(); ArchitectureMips::Terminate(); @@ -487,10 +494,12 @@ void SystemInitializerFull::Terminate() { AppleObjCRuntimeV1::Terminate(); SystemRuntimeMacOSX::Terminate(); RenderScriptRuntime::Terminate(); + RustLanguageRuntime::Terminate(); CPlusPlusLanguage::Terminate(); ObjCLanguage::Terminate(); ObjCPlusPlusLanguage::Terminate(); + RustLanguage::Terminate(); #if defined(__APPLE__) DynamicLoaderDarwinKernel::Terminate(); diff --git a/lldb/source/Core/CMakeLists.txt b/lldb/source/Core/CMakeLists.txt index 15f2dcf6ed9991..1c23ba7445f410 100644 --- a/lldb/source/Core/CMakeLists.txt +++ b/lldb/source/Core/CMakeLists.txt @@ -66,6 +66,8 @@ add_lldb_library(lldbCore lldbPluginProcessUtility lldbPluginCPlusPlusLanguage lldbPluginObjCLanguage + lldbPluginExpressionParserRust + lldbPluginLanguageRuntimeRust ${LLDB_CURSES_LIBS} LINK_COMPONENTS diff --git a/lldb/source/Plugins/ExpressionParser/CMakeLists.txt b/lldb/source/Plugins/ExpressionParser/CMakeLists.txt index 17c40aee44cc27..78db827bae5a42 100644 --- a/lldb/source/Plugins/ExpressionParser/CMakeLists.txt +++ b/lldb/source/Plugins/ExpressionParser/CMakeLists.txt @@ -1 +1,2 @@ add_subdirectory(Clang) +add_subdirectory(Rust) diff --git a/lldb/source/Plugins/ExpressionParser/Rust/CMakeLists.txt b/lldb/source/Plugins/ExpressionParser/Rust/CMakeLists.txt new file mode 100644 index 00000000000000..bddec8cf207675 --- /dev/null +++ b/lldb/source/Plugins/ExpressionParser/Rust/CMakeLists.txt @@ -0,0 +1,19 @@ +if(NOT LLDB_BUILT_STANDALONE) + set(tablegen_deps intrinsics_gen) +endif() + +add_lldb_library(lldbPluginExpressionParserRust PLUGIN + RustUserExpression.cpp RustLex.cpp RustParse.cpp RustFunctionCaller.cpp + + LINK_LIBS + lldbCore + lldbExpression + lldbSymbol + lldbTarget + + DEPENDS + ${tablegen_deps} + + LINK_COMPONENTS + Support + ) diff --git a/lldb/source/Plugins/ExpressionParser/Rust/RustAST.h b/lldb/source/Plugins/ExpressionParser/Rust/RustAST.h new file mode 100644 index 00000000000000..e3a0b5ddc6b9de --- /dev/null +++ b/lldb/source/Plugins/ExpressionParser/Rust/RustAST.h @@ -0,0 +1,826 @@ +//===-- RustAST.h -------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RustAST_h +#define liblldb_RustAST_h + +#include + +#include "lldb/lldb-forward.h" +#include "lldb/lldb-private.h" +#include "lldb/Utility/Scalar.h" +#include "lldb/Symbol/CompilerType.h" +#include "lldb/Symbol/Variable.h" +#include "lldb/Utility/Stream.h" +#include "RustLex.h" + +namespace lldb_private { + +// Functions which are used in the templates below to construct +// various expression nodes. +namespace rust { + +lldb::ValueObjectSP UnaryDereference(ExecutionContext &exe_ctx, lldb::ValueObjectSP addr, + Status &error); +lldb::ValueObjectSP UnaryAddr(ExecutionContext &exe_ctx, lldb::ValueObjectSP val, + Status &error); +lldb::ValueObjectSP UnaryPlus(ExecutionContext &exe_ctx, lldb::ValueObjectSP val, + Status &error); +lldb::ValueObjectSP UnaryNegate(ExecutionContext &exe_ctx, lldb::ValueObjectSP val, + Status &error); +lldb::ValueObjectSP UnaryComplement(ExecutionContext &exe_ctx, lldb::ValueObjectSP val, + Status &error); +lldb::ValueObjectSP UnarySizeof(ExecutionContext &exe_ctx, lldb::ValueObjectSP val, + Status &error); + +template +lldb::ValueObjectSP BinaryOperation (ExecutionContext &exe_ctx, lldb::ValueObjectSP left, + lldb::ValueObjectSP right, Status &error); + +template +lldb::ValueObjectSP Comparison (ExecutionContext &exe_ctx, lldb::ValueObjectSP left, + lldb::ValueObjectSP right, Status &error); + +lldb::ValueObjectSP ArrayIndex (ExecutionContext &exe_ctx, lldb::ValueObjectSP left, + lldb::ValueObjectSP right, Status &error); + +} + +class RustExpression; +typedef std::unique_ptr RustExpressionUP; + +class RustTypeExpression; +typedef std::unique_ptr RustTypeExpressionUP; + +class RustPath; +typedef std::unique_ptr RustPathUP; + +Stream &operator<< (Stream &stream, const RustExpressionUP &expr); +Stream &operator<< (Stream &stream, const RustTypeExpressionUP &type); +Stream &operator<< (Stream &stream, const Scalar &value); +Stream &operator<< (Stream &stream, const std::pair &value); + +template +Stream &operator<< (Stream &stream, const std::vector &items) { + bool first = true; + for (const T &item : items) { + if (!first) { + stream << ", "; + } + first = false; + stream << item; + } + return stream; +} + +class RustPath { +public: + + RustPath(bool self, bool relative, int supers, std::vector &&path, + std::vector &&generic_params, + bool turbofish = false) + : m_self(self), + m_relative(relative), + m_supers(supers), + m_path(std::move(path)), + m_generic_params(std::move(generic_params)), + m_turbofish(turbofish) + { + } + + void print(Stream &stream) { + if (m_self) { + stream << "self::"; + } else if (!m_relative) { + stream << "::"; + } + for (int i = 0; i < m_supers; ++i) { + stream << "super::"; + } + bool first = true; + for (const std::string &str : m_path) { + if (!first) { + stream << "::"; + } + first = false; + stream << str; + } + + if (!m_generic_params.empty()) { + if (m_turbofish) { + stream << "::"; + } + stream << "<" << m_generic_params << ">"; + } + } + + bool FindDecl(ExecutionContext &exe_ctx, Status &error, + lldb::VariableSP *var, lldb_private::Function **function, + std::string *base_name); + + CompilerType EvaluateAsType(ExecutionContext &exe_ctx, Status &error); + +private: + + std::string Name(ExecutionContext &exe_ctx, Status &error); + CompilerDeclContext FrameDeclContext(ExecutionContext &exe_ctx, Status &error); + bool GetDeclContext(ExecutionContext &exe_ctx, Status &error, + CompilerDeclContext *result, bool *simple_name); + bool AppendGenerics(ExecutionContext &exe_ctx, Status &error, std::string *name); + + bool m_self; + bool m_relative; + int m_supers; + std::vector m_path; + std::vector m_generic_params; + bool m_turbofish; +}; + +class RustPathExpression; + +class RustExpression { +protected: + + RustExpression() { } + +public: + + virtual ~RustExpression() { } + + virtual void print(Stream &stream) = 0; + + virtual lldb::ValueObjectSP Evaluate(ExecutionContext &exe_ctx, Status &error) = 0; + + virtual RustPathExpression *AsPath() { + return nullptr; + } +}; + +typedef lldb::ValueObjectSP (*RustUnaryOperator)(ExecutionContext &, lldb::ValueObjectSP, + Status &error); + +template +class RustUnaryExpression : public RustExpression { +public: + + explicit RustUnaryExpression(RustExpressionUP &&expr) + : m_expr(std::move(expr)) + { + } + + void print(Stream &stream) override { + stream << TAG << " (" << m_expr << ")"; + } + + lldb::ValueObjectSP Evaluate(ExecutionContext &exe_ctx, Status &error) override { + lldb::ValueObjectSP value = m_expr->Evaluate(exe_ctx, error); + if (!value) + return value; + return OP(exe_ctx, value, error); + } + +private: + + RustExpressionUP m_expr; +}; + +typedef lldb::ValueObjectSP (*RustBinaryOperator)(ExecutionContext &, + lldb::ValueObjectSP, lldb::ValueObjectSP, + Status &error); + +template +class RustBinaryExpression : public RustExpression { +public: + + RustBinaryExpression(RustExpressionUP &&left, + RustExpressionUP &&right) + : m_left(std::move(left)), + m_right(std::move(right)) + { + } + + void print(Stream &stream) override { + stream << "(" << m_left << " "; + lldb_private::rust::PrintTokenKind(stream, TAG); + stream << " " << m_right << ")"; + } + + lldb::ValueObjectSP Evaluate(ExecutionContext &exe_ctx, Status &error) override { + lldb::ValueObjectSP left = m_left->Evaluate(exe_ctx, error); + if (!left) + return left; + lldb::ValueObjectSP right = m_right->Evaluate(exe_ctx, error); + if (!right) + return right; + return OP(exe_ctx, left, right, error); + } + +private: + + RustExpressionUP m_left; + RustExpressionUP m_right; +}; + +template +class RustAssignExpression : public RustExpression { +public: + + void print(Stream &stream) override { + stream << "(" << m_left << " "; + lldb_private::rust::PrintTokenKind(stream, TAG); + stream << " " << m_right << ")"; + } + + RustAssignExpression(RustExpressionUP &&left, + RustExpressionUP &&right) + : m_left(std::move(left)), + m_right(std::move(right)) + { + } + + lldb::ValueObjectSP Evaluate(ExecutionContext &exe_ctx, Status &error) override { + lldb::ValueObjectSP left = m_left->Evaluate(exe_ctx, error); + if (!left) + return left; + lldb::ValueObjectSP right = m_right->Evaluate(exe_ctx, error); + if (!right) + return right; + return OP(exe_ctx, left, right, error); + } + +private: + + RustExpressionUP m_left; + RustExpressionUP m_right; +}; + +class RustAssignment : public RustExpression { +public: + + RustAssignment(RustExpressionUP &&left, + RustExpressionUP &&right) + : m_left(std::move(left)), + m_right(std::move(right)) + { + } + + void print(Stream &stream) override { + stream << "(" << m_left << " = " << m_right << ")"; + } + + lldb::ValueObjectSP Evaluate(ExecutionContext &exe_ctx, Status &error) override; + +private: + + RustExpressionUP m_left; + RustExpressionUP m_right; +}; + +class RustAndAndExpression : public RustExpression { +public: + + RustAndAndExpression(RustExpressionUP &&left, + RustExpressionUP &&right) + : m_left(std::move(left)), + m_right(std::move(right)) + { + } + + void print(Stream &stream) override { + stream << "(" << m_left << " && " << m_right << ")"; + } + + lldb::ValueObjectSP Evaluate(ExecutionContext &exe_ctx, Status &error) override; + +private: + + RustExpressionUP m_left; + RustExpressionUP m_right; +}; + +class RustOrOrExpression : public RustExpression { +public: + + RustOrOrExpression(RustExpressionUP &&left, + RustExpressionUP &&right) + : m_left(std::move(left)), + m_right(std::move(right)) + { + } + + void print(Stream &stream) override { + stream << "(" << m_left << " || " << m_right << ")"; + } + + lldb::ValueObjectSP Evaluate(ExecutionContext &exe_ctx, Status &error) override; + +private: + + RustExpressionUP m_left; + RustExpressionUP m_right; +}; + +class RustRangeExpression : public RustExpression { +public: + + // Either or both can be NULL here. + RustRangeExpression(RustExpressionUP &&left, + RustExpressionUP &&right, + bool inclusive) + : m_left(std::move(left)), + m_right(std::move(right)), + m_inclusive(inclusive) + { + // Inclusive ranges require an upper bound. + assert(m_right || !m_inclusive); + } + + void print(Stream &stream) override { + stream << "(" << m_left << (m_inclusive ? " ..= " : " .. ") << m_right << ")"; + } + + lldb::ValueObjectSP Evaluate(ExecutionContext &exe_ctx, Status &error) override; + +private: + + RustExpressionUP m_left; + RustExpressionUP m_right; + bool m_inclusive; +}; + +class RustFieldExpression : public RustExpression { +public: + + RustFieldExpression(RustExpressionUP &&left, llvm::StringRef field) + : m_left(std::move(left)), + m_field(field.str()) + { + } + + void print(Stream &stream) override { + stream << m_left << "." << m_field; + } + + lldb::ValueObjectSP Evaluate(ExecutionContext &exe_ctx, Status &error) override; + +private: + + RustExpressionUP m_left; + std::string m_field; +}; + +class RustTupleFieldExpression : public RustExpression { +public: + + RustTupleFieldExpression(RustExpressionUP &&left, uint32_t field) + : m_left(std::move(left)), + m_field(field) + { + } + + lldb::ValueObjectSP Evaluate(ExecutionContext &exe_ctx, Status &error) override; + +private: + + void print(Stream &stream) override { + stream << m_left << "." << int(m_field); + } + + RustExpressionUP m_left; + uint32_t m_field; +}; + +class RustLiteral : public RustExpression { +public: + + RustLiteral(Scalar value, RustTypeExpressionUP &&type) + : m_value(value), + m_type(std::move(type)) + { + } + + void print(Stream &stream) override { + stream << m_value; + } + + lldb::ValueObjectSP Evaluate(ExecutionContext &exe_ctx, Status &error) override; + +private: + + Scalar m_value; + RustTypeExpressionUP m_type; +}; + +class RustBooleanLiteral : public RustExpression { +public: + + RustBooleanLiteral(bool value) + : m_value(value) + { + } + + void print(Stream &stream) override { + stream << (m_value ? "true" : "false"); + } + + lldb::ValueObjectSP Evaluate(ExecutionContext &exe_ctx, Status &error) override; + +private: + + bool m_value; +}; + +class RustCharLiteral : public RustExpression { +public: + + RustCharLiteral(uint32_t value, bool is_byte) + : m_value(value), + m_is_byte(is_byte) + { + } + + void print(Stream &stream) override { + stream << (m_is_byte ? "b'" : "'"); + if (m_value >= ' ' && m_value < 128) { + stream << char(m_value); + } else { + if (m_is_byte) { + stream << "\\x"; + stream.PutHex8(m_value); + } else { + stream << "\\u{"; + stream.PutHex32(m_value); + stream << "}"; + } + } + stream << "'"; + } + + lldb::ValueObjectSP Evaluate(ExecutionContext &exe_ctx, Status &error) override; + +private: + + uint32_t m_value; + bool m_is_byte; +}; + +class RustStringLiteral : public RustExpression { +public: + + RustStringLiteral(std::string &&value, bool is_byte) + : m_value(std::move(value)), + m_is_byte(is_byte) + { + } + + void print(Stream &stream) override { + stream << (m_is_byte ? "b\"" : "\"") << m_value << "\""; + } + + lldb::ValueObjectSP Evaluate(ExecutionContext &exe_ctx, Status &error) override; + +private: + + std::string m_value; + bool m_is_byte; +}; + +class RustTupleExpression : public RustExpression { +public: + + explicit RustTupleExpression(std::vector &&exprs) + : m_exprs(std::move(exprs)) + { + } + + void print(Stream &stream) override { + // Maybe emit an extra "," to differentiate from (expr). + stream << "(" << m_exprs << (m_exprs.empty() ? "" : ", ") << ")"; + } + + lldb::ValueObjectSP Evaluate(ExecutionContext &exe_ctx, Status &error) override; + +private: + + std::vector m_exprs; + +}; + +class RustArrayLiteral : public RustExpression { +public: + + explicit RustArrayLiteral(std::vector &&exprs) + : m_exprs(std::move(exprs)) + { + } + + void print(Stream &stream) override { + stream << "[" << m_exprs << "]"; + } + + lldb::ValueObjectSP Evaluate(ExecutionContext &exe_ctx, Status &error) override; + +private: + + std::vector m_exprs; + +}; + +class RustArrayWithLength : public RustExpression { +public: + + RustArrayWithLength(RustExpressionUP &&value, RustExpressionUP &&length) + : m_value(std::move(value)), + m_length(std::move(length)) + { + } + + void print(Stream &stream) override { + stream << "[" << m_value << "; " << m_length << "]"; + } + + lldb::ValueObjectSP Evaluate(ExecutionContext &exe_ctx, Status &error) override; + +private: + + RustExpressionUP m_value; + RustExpressionUP m_length; +}; + +// Note that this may also represent a tuple-struct expression if the +// "function" is a path referring to a tuple type. +class RustCall : public RustExpression { +public: + + RustCall(RustExpressionUP &&func, std::vector &&exprs) + : m_func(std::move(func)), + m_exprs(std::move(exprs)) + { + } + + void print(Stream &stream) override { + stream << m_func << " (" << m_exprs << ")"; + } + + lldb::ValueObjectSP Evaluate(ExecutionContext &exe_ctx, Status &error) override; + +private: + + lldb::ValueObjectSP MaybeEvalTupleStruct(ExecutionContext &exe_ctx, Status &error); + + RustExpressionUP m_func; + std::vector m_exprs; + +}; + +class RustCast : public RustExpression { +public: + + RustCast(RustTypeExpressionUP &&type, RustExpressionUP &&expr) + : m_type(std::move(type)), + m_expr(std::move(expr)) + { + } + + void print(Stream &stream) override { + stream << "(" << m_expr << " as " << m_type << ")"; + } + + lldb::ValueObjectSP Evaluate(ExecutionContext &exe_ctx, Status &error) override; + +private: + + RustTypeExpressionUP m_type; + RustExpressionUP m_expr; +}; + +class RustPathExpression : public RustExpression { +public: + + RustPathExpression(RustPathUP &&path) + : m_path(std::move(path)) + { + } + + explicit RustPathExpression(std::string &&item) + { + std::vector names; + names.push_back(std::move(item)); + + m_path = llvm::make_unique(false, true, 0, std::move(names), + std::vector()); + } + + void print(Stream &stream) override { + m_path->print(stream); + } + + CompilerType EvaluateAsType(ExecutionContext &exe_ctx, Status &error) { + return m_path->EvaluateAsType(exe_ctx, error); + } + + lldb::ValueObjectSP Evaluate(ExecutionContext &exe_ctx, Status &error) override; + + RustPathExpression *AsPath() override { + return this; + } + +private: + + RustPathUP m_path; +}; + +class RustStructExpression : public RustExpression { +public: + + // Note that |copy| can be null if no '..' form was seen. + RustStructExpression(RustTypeExpressionUP &&path, + std::vector> &&inits, + RustExpressionUP &©) + : m_path(std::move(path)), + m_inits(std::move(inits)), + m_copy(std::move(copy)) + { + } + + void print(Stream &stream) override { + stream << m_path << " { " << m_inits; + if (m_copy) { + stream << ", .. " << m_copy; + } + stream << " }"; + } + + lldb::ValueObjectSP Evaluate(ExecutionContext &exe_ctx, Status &error) override; + +private: + + RustTypeExpressionUP m_path; + std::vector> m_inits; + RustExpressionUP m_copy; +}; + + +class RustTypeExpression { +protected: + + RustTypeExpression() { } + +public: + + virtual ~RustTypeExpression() { } + + virtual void print(Stream &stream) = 0; + + virtual CompilerType Evaluate(ExecutionContext &exe_ctx, Status &error) = 0; +}; + +class RustPathTypeExpression : public RustTypeExpression { +public: + + RustPathTypeExpression(RustPathUP &&path) + : m_path(std::move(path)) + { + } + + explicit RustPathTypeExpression(std::string &&item) + { + std::vector names; + names.push_back(std::move(item)); + + m_path = llvm::make_unique(false, true, 0, std::move(names), + std::vector()); + } + + void print(Stream &stream) override { + m_path->print(stream); + } + + CompilerType Evaluate(ExecutionContext &exe_ctx, Status &error) override { + return m_path->EvaluateAsType(exe_ctx, error); + } + +private: + + RustPathUP m_path; +}; + +class RustArrayTypeExpression : public RustTypeExpression { +public: + + RustArrayTypeExpression(RustTypeExpressionUP &&element, uint64_t len) + : m_element(std::move(element)), + m_len(len) + { + } + + void print(Stream &stream) override { + stream << "[" << m_element << "; " << int64_t(m_len) << "]"; + } + + CompilerType Evaluate(ExecutionContext &exe_ctx, Status &error) override; + +private: + + RustTypeExpressionUP m_element; + uint64_t m_len; +}; + +class RustSliceTypeExpression : public RustTypeExpression { +public: + + RustSliceTypeExpression(RustTypeExpressionUP &&element, bool is_mut) + : m_element(std::move(element)), + m_is_mut(is_mut) + { + } + + void print(Stream &stream) override { + stream << "&" << (m_is_mut ? "mut " : "") << "[" << m_element << "]"; + } + + CompilerType Evaluate(ExecutionContext &exe_ctx, Status &error) override; + +private: + + RustTypeExpressionUP m_element; + bool m_is_mut; +}; + +class RustPointerTypeExpression : public RustTypeExpression { +public: + + RustPointerTypeExpression(RustTypeExpressionUP &&target, bool is_ref, bool is_mut = false) + : m_target(std::move(target)), + m_is_ref(is_ref), + m_is_mut(is_mut) + { + } + + void print(Stream &stream) override { + if (m_is_ref) { + stream << "&" << (m_is_ref ? "mut " : "") << m_target; + } else { + stream << "*" << (m_is_mut ? "mut " : "const ") << m_target; + } + } + + CompilerType Evaluate(ExecutionContext &exe_ctx, Status &error) override; + +private: + + RustTypeExpressionUP m_target; + bool m_is_ref; + bool m_is_mut; +}; + +class RustFunctionTypeExpression : public RustTypeExpression { +public: + + RustFunctionTypeExpression(RustTypeExpressionUP &&result, + std::vector &&arguments) + : m_result(std::move(result)), + m_arguments(std::move(arguments)) + { + } + + void print(Stream &stream) override { + stream << "fn (" << m_arguments << ") -> " << m_result; + } + + CompilerType Evaluate(ExecutionContext &exe_ctx, Status &error) override; + +private: + + RustTypeExpressionUP m_result; + std::vector m_arguments; +}; + +class RustTupleTypeExpression : public RustTypeExpression { +public: + + RustTupleTypeExpression(std::vector &&arguments) + : m_arguments(std::move(arguments)) + { + } + + void print(Stream &stream) override { + stream << "(" << m_arguments << ")"; + } + + CompilerType Evaluate(ExecutionContext &exe_ctx, Status &error) override; + +private: + + std::vector m_arguments; +}; + +} // namespace lldb_private + +#endif // liblldb_RustAST_h diff --git a/lldb/source/Plugins/ExpressionParser/Rust/RustFunctionCaller.cpp b/lldb/source/Plugins/ExpressionParser/Rust/RustFunctionCaller.cpp new file mode 100644 index 00000000000000..09789248a87201 --- /dev/null +++ b/lldb/source/Plugins/ExpressionParser/Rust/RustFunctionCaller.cpp @@ -0,0 +1,200 @@ +//===-- RustFunctionCaller.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Plugins/ExpressionParser/Clang/ASTStructExtractor.h" +#include "RustFunctionCaller.h" + +#include "Plugins/ExpressionParser/Clang/ClangExpressionParser.h" + +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Triple.h" +#include "llvm/ExecutionEngine/ExecutionEngine.h" +#include "llvm/IR/Module.h" + +#include "lldb/Core/Module.h" +#include "lldb/Core/State.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectList.h" +#include "lldb/Expression/DiagnosticManager.h" +#include "lldb/Expression/IRExecutionUnit.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Symbol/RustASTContext.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlan.h" +#include "lldb/Target/ThreadPlanCallFunction.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/Log.h" + +using namespace lldb_private; + +//---------------------------------------------------------------------- +// RustFunctionCaller constructor +//---------------------------------------------------------------------- +RustFunctionCaller::RustFunctionCaller(ExecutionContextScope &exe_scope, + const CompilerType &function_type, + const CompilerType &return_type, + const Address &functionAddress, + const ValueList &arg_value_list, + const char *name) + : ClangFunctionCaller(exe_scope, return_type, functionAddress, arg_value_list, name), + m_function_type(function_type) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +RustFunctionCaller::~RustFunctionCaller() {} + +static bool +AppendType(std::string *output, RustASTContext *ast, RustASTContext::TypeNameMap *name_map, + const std::string &varname, CompilerType type) { + std::string value; + if (!ast->GetCABITypeDeclaration(type, varname, name_map, &value)) { + return false; + } + output->append(" "); + output->append(value); + output->append(";\n"); + return true; +} + +unsigned RustFunctionCaller::CompileFunction(lldb::ThreadSP thread_to_use_sp, + DiagnosticManager &diagnostic_manager) { + if (m_compiled) + return 0; + + // Compilation might call code, make sure to keep on the thread the caller + // indicated. + ThreadList::ExpressionExecutionThreadPusher execution_thread_pusher( + thread_to_use_sp); + + RustASTContext *ast = + llvm::dyn_cast_or_null(m_function_return_type.GetTypeSystem()); + if (!ast) { + diagnostic_manager.PutString(eDiagnosticSeverityError, "not in a Rust context!?"); + return 1; + } + + // Cons up the function we're going to wrap our call in, then compile it... + // We declare the function "extern "C"" because the compiler might be in C++ + // mode which would mangle the name and then we couldn't find it again... + m_wrapper_function_text.clear(); + + m_wrapper_function_text.append("extern \"C\" void "); + m_wrapper_function_text.append(m_wrapper_function_name); + m_wrapper_function_text.append(" (void *input)\n{\n"); + + // For the Itanium ABI we want to generate a non-standard-layout + // type, so that ASTStructExtractor can see the length of the type + // without padding, so that the size of the final element is + // correct. FIXME this seems horrible, maybe a fix in + // ASTStructExtractor is more appropriate. + m_wrapper_function_text.append(" struct empty { };\n"); + m_wrapper_function_text.append(" struct a : empty { };\n"); + m_wrapper_function_text.append(" struct b : empty { };\n"); + + RustASTContext::TypeNameMap name_map; + std::string code; + code.append(" struct "); + code.append(m_wrapper_struct_name); + code.append(" : a, b {\n"); + + // ASTStructExtractor requires the first argument to be the + // function. + if (!AppendType(&code, ast, &name_map, "fn_ptr", m_function_type)) { + diagnostic_manager.PutString(eDiagnosticSeverityError, + "could not compute Rust type declaration"); + return 1; + } + + // This was ensured by the caller. + assert(unsigned(m_function_type.GetNumberOfFunctionArguments()) == m_arg_values.GetSize()); + + std::string arguments; + for (int i = 0; i < m_function_type.GetFunctionArgumentCount(); ++i) { + // We use the actual argument types in this structure so that + // copy-in always preserves values, and then we let the C++ + // compiler do any scalar conversions. Rust doesn't have + // coercions like this, but it has type inferencing, which we + // don't, so this is handy for users who want to write "32" + // instead of "32f32". + CompilerType arg_type = m_arg_values.GetValueAtIndex(i)->GetCompilerType(); + + // FIXME work around a FunctionCaller problem. Note that the + // actual argument is already a pointer at this point, see + // RustParse. + bool is_aggregate = m_function_type.GetFunctionArgumentTypeAtIndex(i).IsAggregateType(); + + std::string argname = "__arg_" + std::to_string(i); + if (!AppendType(&code, ast, &name_map, argname, arg_type)) { + diagnostic_manager.PutString(eDiagnosticSeverityError, + "could not compute Rust type declaration"); + return 1; + } + if (i > 0) { + arguments.append(", "); + } + if (is_aggregate) { + arguments.append("*"); + } + arguments.append("__lldb_fn_data->"); + arguments.append(argname); + } + + // ASTStructExtractor requires that the last field hold the result. + if (!AppendType(&code, ast, &name_map, "result", + m_function_type.GetFunctionReturnType())) { + diagnostic_manager.PutString(eDiagnosticSeverityError, + "could not compute Rust type declaration"); + return 1; + } + + m_wrapper_function_text.append(name_map.typedefs); + m_wrapper_function_text.append(code); + + m_wrapper_function_text.append(" };\n"); + + m_wrapper_function_text.append(" "); + m_wrapper_function_text.append(m_wrapper_struct_name); + m_wrapper_function_text.append(" *__lldb_fn_data = ("); + m_wrapper_function_text.append(m_wrapper_struct_name); + m_wrapper_function_text.append(" *) input;\n"); + + m_wrapper_function_text.append(" __lldb_fn_data->result = __lldb_fn_data->fn_ptr("); + m_wrapper_function_text.append(arguments); + m_wrapper_function_text.append(");\n}\n"); + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + if (log) + log->Printf("Expression: \n\n%s\n\n", m_wrapper_function_text.c_str()); + + // Okay, now compile this expression + + lldb::ProcessSP jit_process_sp(m_jit_process_wp.lock()); + unsigned num_errors; + if (jit_process_sp) { + m_parser.reset(new ClangExpressionParser(jit_process_sp.get(), *this, true)); + + num_errors = m_parser->Parse(diagnostic_manager); + } else { + diagnostic_manager.PutString(eDiagnosticSeverityError, + "no process - unable to inject function"); + num_errors = 1; + } + + m_compiled = (num_errors == 0); + return num_errors; +} diff --git a/lldb/source/Plugins/ExpressionParser/Rust/RustFunctionCaller.h b/lldb/source/Plugins/ExpressionParser/Rust/RustFunctionCaller.h new file mode 100644 index 00000000000000..1a5c54e46f54c2 --- /dev/null +++ b/lldb/source/Plugins/ExpressionParser/Rust/RustFunctionCaller.h @@ -0,0 +1,46 @@ +//===-- RustFunctionCaller.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RustFunctionCaller_h_ +#define liblldb_RustFunctionCaller_h_ + +#include "lldb/Core/Address.h" +#include "lldb/Core/Value.h" +#include "lldb/Core/ValueObjectList.h" +#include "lldb/Expression/FunctionCaller.h" +#include "lldb/Symbol/CompilerType.h" +#include "lldb/Target/Process.h" +#include "Plugins/ExpressionParser/Clang/ClangFunctionCaller.h" + +namespace lldb_private { + +// Derive from ClangFunctionCaller so we don't have to reimplement +// ASTStructExtractor. This is very naughty. +class RustFunctionCaller : public ClangFunctionCaller { + +public: + RustFunctionCaller(ExecutionContextScope &exe_scope, + const CompilerType &function_type, + const CompilerType &return_type, + const Address &function_address, + const ValueList &arg_value_list, const char *name); + + ~RustFunctionCaller() override; + + unsigned CompileFunction(lldb::ThreadSP thread_to_use_sp, + DiagnosticManager &diagnostic_manager) override; + +private: + + CompilerType m_function_type; +}; + +} // namespace lldb_private + +#endif // liblldb_RustFunctionCaller_h_ diff --git a/lldb/source/Plugins/ExpressionParser/Rust/RustLex.cpp b/lldb/source/Plugins/ExpressionParser/Rust/RustLex.cpp new file mode 100644 index 00000000000000..99a6cb7b558650 --- /dev/null +++ b/lldb/source/Plugins/ExpressionParser/Rust/RustLex.cpp @@ -0,0 +1,654 @@ +//===-- RustLex.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "RustLex.h" +#include "llvm/Support/ConvertUTF.h" +#include "llvm/ADT/APInt.h" + +using namespace lldb_private::rust; +using namespace lldb_private; +using namespace lldb; +using namespace llvm; + +void lldb_private::rust::PrintTokenKind(Stream &stream, int kind) { + if (kind < STRING) { + stream << char(kind); + } else { + switch (kind) { + case STRING: + case BYTESTRING: + case CHAR: + case BYTE: + case FLOAT: + case INTEGER: + case AS: + case TRUE: + case FALSE: + case SUPER: + case SELF: + case MUT: + case CONST: + case FN: + case SIZEOF: + case IDENTIFIER: + case INVALID: + case THATSALLFOLKS: + // May want to clean this up someday. + stream << "[TOKEN=" << kind << "]"; + break; + + case DOTDOT: + stream << ".."; + break; + + case DOTDOTEQ: + stream << "..="; + break; + + case OROR: + stream << "||"; + break; + + case ANDAND: + stream << "&&"; + break; + + case EQEQ: + stream << "=="; + break; + + case NOTEQ: + stream << "!="; + break; + + case LTEQ: + stream << "<="; + break; + + case GTEQ: + stream << ">="; + break; + + case LSH: + stream << "<<"; + break; + + case RSH: + stream << ">>"; + break; + + case PLUS_EQ: + stream << "+="; + break; + + case MINUS_EQ: + stream << "-="; + break; + + case SLASH_EQ: + stream << "/="; + break; + + case STAR_EQ: + stream << "*="; + break; + + case PERCENT_EQ: + stream << "%="; + break; + + case RSH_EQ: + stream << ">>="; + break; + + case LSH_EQ: + stream << "<<="; + break; + + case AND_EQ: + stream << "&="; + break; + + case OR_EQ: + stream << "|="; + break; + + case XOR_EQ: + stream << "^="; + break; + + case COLONCOLON: + stream << "::"; + break; + + case ARROW: + stream << "->"; + break; + + default: + stream << "!!!OOPS!!!"; + break; + } + } +} + +llvm::StringMap *Lexer::m_keywords; + +Token Lexer::Next() { + // Skip whitespace. + while (m_iter != m_end && + // FIXME is it possible to see newlines here? + (*m_iter == ' ' || *m_iter == '\t')) + ++m_iter; + if (m_iter == m_end) { + return Token(THATSALLFOLKS); + } + + char c = *m_iter; + if (c >= '0' && c <= '9') { + return Number(); + } else if (c == 'b') { + return MaybeByteLiteral(); + } else if (c == 'r') { + return MaybeRawString(); + } else if (c == '"') { + return String(); + } else if (c == '\'') { + return Character(); + } else { + return Operator(); + } +} + +bool Lexer::Lookup(const ::llvm::StringRef &str, int *result) { + ::llvm::StringMap *map = Keywords(); + const auto &iter = map->find(str); + if (iter == map->end()) { + return false; + } + *result = iter->second; + return true; +} + +Token Lexer::Operator() { + int result; + + if (Remaining() >= 3 && Lookup(::llvm::StringRef(m_iter, 3), &result)) { + m_iter += 3; + return Token(result); + } + + if (Remaining() >= 2 && Lookup(::llvm::StringRef(m_iter, 2), &result)) { + m_iter += 2; + return Token(result); + } + + if (strchr(".,;|&=!<>+-*/%:[](){}", *m_iter) != nullptr) { + return Token(*m_iter++); + } + + return Identifier(); +} + +bool Lexer::BasicInteger(int *radix_out, std::string *value) { + int radix = 10; + + assert (m_iter != m_end); + assert (*m_iter >= '0' && *m_iter <= '9'); + + bool need_digit = false; + if (radix_out != nullptr && *m_iter == '0') { + // Ignore this digit and see if we have a non-decimal integer. + ++m_iter; + if (m_iter == m_end) { + // Plain "0". + value->push_back('0'); + if (radix_out) { + *radix_out = radix; + } + return true; + } + + if (*m_iter == 'x') { + radix = 16; + need_digit = true; + ++m_iter; + } else if (*m_iter == 'b') { + radix = 2; + need_digit = true; + ++m_iter; + } else if (*m_iter == 'o') { + radix = 8; + need_digit = true; + ++m_iter; + } else { + value->push_back('0'); + } + } + + for (; m_iter != m_end; ++m_iter) { + if (*m_iter == '_') { + continue; + } + if ((radix == 10 || radix == 16) && *m_iter >= '0' && *m_iter <= '9') { + // Ok. + } else if (radix == 2 && *m_iter >= '0' && *m_iter <= '1') { + // Ok. + } else if (radix == 8 && *m_iter >= '0' && *m_iter <= '7') { + // Ok. + } else if (radix == 16 && *m_iter >= 'a' && *m_iter <= 'f') { + // Ok. + } else if (radix == 16 && *m_iter >= 'A' && *m_iter <= 'F') { + // Ok. + } else { + break; + } + + value->push_back(*m_iter); + need_digit = false; + } + + if (radix_out) { + *radix_out = radix; + } + return !need_digit; +} + +const char *Lexer::CheckSuffix(const char *const *suffixes) { + const char *suffix = nullptr; + + size_t left = Remaining(); + for (int i = 0; suffixes[i]; ++i) { + size_t len = strlen(suffixes[i]); + if (left >= len) { + ::llvm::StringRef text(m_iter, len); + if (text == suffixes[i]) { + suffix = suffixes[i]; + m_iter += len; + break; + } + } + } + + return suffix; +} + +int Lexer::Float(std::string *value) { + assert(m_iter != m_end && (*m_iter == '.' || *m_iter == 'e' || *m_iter == 'E')); + + if (*m_iter == '.') { + ++m_iter; + if (m_iter == m_end || !(*m_iter >= '0' && *m_iter <= '9')) { + // Not a floating-point number. + --m_iter; + return INTEGER; + } + + value->push_back('.'); + BasicInteger(nullptr, value); + } + + if (m_iter == m_end || (*m_iter != 'e' && *m_iter != 'E')) { + return FLOAT; + } + + value->push_back(*m_iter++); + if (m_iter == m_end) { + return INVALID; + } + + if (*m_iter == '+' || *m_iter == '-') { + value->push_back(*m_iter++); + if (m_iter == m_end) { + return INVALID; + } + } + + if (!(*m_iter >= '0' && *m_iter <= '9')) { + return INVALID; + } + BasicInteger(nullptr, value); + return FLOAT; +} + +Token Lexer::Number() { + std::string number; + int radix; + + if (!BasicInteger(&radix, &number)) { + return Token(INVALID); + } + + if (m_iter != m_end && radix == 10 && + (*m_iter == '.' || *m_iter == 'e' || *m_iter == 'E')) { + int kind = Float(&number); + if (kind == INVALID) { + return Token(INVALID); + } + if (kind == FLOAT) { + // Actually a float. + ::llvm::StringRef sref(number); + double dval; + if (sref.getAsDouble(dval)) { + return Token(INVALID); + } + + static const char * const float_suffixes[] = { + "f32", + "f64", + nullptr + }; + + const char *suffix = CheckSuffix(float_suffixes); + + return Token(FLOAT, dval, suffix); + } + + // Floating-point lex failed but we still have an integer. + assert(kind == INTEGER); + } + + static const char * const int_suffixes[] = { + "u8", + "i8", + "u16", + "i16", + "u32", + "i32", + "u64", + "i64", + "usize", + "isize", + nullptr + }; + + APInt value; + ::llvm::StringRef sref(number); + if (sref.getAsInteger(radix, value)) { + return Token(INVALID); + } + // FIXME maybe we should just leave it as an APInt through the whole + // process. + if (value.getNumWords() > 1) { + return Token(INVALID); + } + + const char *suffix = CheckSuffix(int_suffixes); + + return Token(INTEGER, value.getLimitedValue(), suffix); +} + +Token Lexer::Identifier() { + assert(m_iter != m_end); + + ::llvm::StringRef::iterator start = m_iter; + char c = *m_iter; + if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_')) { + return Token(INVALID); + } + + for (++m_iter; m_iter != m_end; ++m_iter) { + char c = *m_iter; + if (! ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || + (c >= '0' && c <= '9'))) { + break; + } + } + + ::llvm::StringRef text(start, m_iter - start); + int result; + if (Lookup(text, &result)) { + return Token(result); + } + + return Token(IDENTIFIER, text.str()); +} + +Token Lexer::MaybeByteLiteral() { + assert(*m_iter == 'b'); + + if (Remaining() < 2) { + return Identifier(); + } + if (m_iter[1] == 'r') { + return MaybeRawString(true); + } else if (m_iter[1] == '"') { + ++m_iter; + return String(true); + } else if (m_iter[1] == '\'') { + ++m_iter; + return Character(true); + } + return Identifier(); +} + +bool Lexer::ParseHex(uint64_t *result, int min_digits, int max_digits) { + *result = 0; + int i; + for (i = 0; m_iter != m_end && i < max_digits; ++i, ++m_iter) { + uint64_t digit; + if (*m_iter >= 'a' && *m_iter <= 'f') { + digit = *m_iter - 'a' + 10; + } else if (*m_iter >= 'A' && *m_iter <= 'F') { + digit = *m_iter - 'A' + 10; + } else if (*m_iter >= '0' && *m_iter <= '9') { + digit = *m_iter - '0'; + } else { + break; + } + *result = *result * 16 + digit; + } + + return i >= min_digits; +} + +bool Lexer::ParseEscape(uint64_t *result, bool is_byte) { + assert(*m_iter == '\\'); + ++m_iter; + + if (m_iter == m_end) { + return false; + } + switch (*m_iter++) { + case 'x': + return ParseHex(result, 2, 2); + + case 'u': { + if (is_byte) { + return false; + } + if (m_iter == m_end || *m_iter++ != '{') { + return false; + } + if (!ParseHex(result, 1, 6)) { + return false; + } + if (m_iter == m_end || *m_iter++ != '}') { + return false; + } + break; + } + + case 'n': + *result = '\n'; + break; + case 'r': + *result = '\r'; + break; + case 't': + *result = '\t'; + break; + case '\\': + *result = '\\'; + break; + case '0': + *result = 0; + break; + case '\'': + *result = '\''; + break; + case '"': + *result = '"'; + break; + + default: + return false; + } + + return true; +} + +bool Lexer::AppendEscape(std::string *result, bool is_byte) { + uint64_t value; + if (!ParseEscape(&value, is_byte)) { + return false; + } + + char utf8[10]; + char *out = utf8; + if (!ConvertCodePointToUTF8(value, out)) { + return false; + } + + result->append(utf8, out); + return true; +} + +Token Lexer::Character(bool is_byte) { + assert(*m_iter == '\''); + + if (++m_iter == m_end) { + return Token(INVALID); + } + + uint64_t result; + if (*m_iter == '\\') { + if (!ParseEscape(&result, is_byte)) { + return Token(INVALID); + } + } else { + result = *m_iter++; + } + + if (m_iter == m_end || *m_iter++ != '\'') { + return Token(INVALID); + } + + return Token(is_byte ? BYTE : CHAR, result); +} + +Token Lexer::MaybeRawString(bool is_byte) { + // Use a local copy so we can backtrack if need be. + ::llvm::StringRef::iterator iter = m_iter; + + if (is_byte) { + assert(*iter == 'b'); + ++iter; + } + + assert(*iter == 'r'); + ++iter; + + ::llvm::StringRef::iterator before_hashes = iter; + while (iter != m_end && *iter == '#') { + ++iter; + } + if (iter == m_end || *iter != '"') { + return Identifier(); + } + + size_t n_hashes = iter - before_hashes; + ::llvm::StringRef::iterator string_start = ++iter; + + for (; iter != m_end; ++iter) { + if (*iter == '"' && + (n_hashes == 0 || + (size_t(m_end - iter + 1) > n_hashes && + strncmp(iter + 1, before_hashes, n_hashes) == 0))) { + break; + } + } + + m_iter = iter; + if (iter == m_end) { + return Token(INVALID); + } + + assert(*m_iter == '"'); + ++m_iter; + assert(Remaining() >= n_hashes); + m_iter += n_hashes; + + return Token(is_byte ? BYTESTRING : STRING, std::string(string_start, iter)); +} + +Token Lexer::String(bool is_byte) { + assert(*m_iter == '"'); + ++m_iter; + + std::string text; + while (m_iter != m_end && *m_iter != '"') { + if (*m_iter == '\\') { + if (!AppendEscape(&text, is_byte)) { + return Token(INVALID); + } + } else { + text += *m_iter++; + } + } + + if (m_iter == m_end) { + return Token(INVALID); + } + assert(*m_iter == '"'); + ++m_iter; + + return Token(is_byte ? BYTESTRING : STRING, std::move(text)); +} + +::llvm::StringMap *Lexer::Keywords() { + if (m_keywords == nullptr) { + m_keywords = new ::llvm::StringMap; + ::llvm::StringMap &m = *m_keywords; + + m["as"] = AS; + m["true"] = TRUE; + m["false"] = FALSE; + m["super"] = SUPER; + m["self"] = SELF; + m["mut"] = MUT; + m["const"] = CONST; + m["fn"] = FN; + m["sizeof"] = SIZEOF; + m[".."] = DOTDOT; + m["..="] = DOTDOTEQ; + m["||"] = OROR; + m["|="] = OR_EQ; + m["&&"] = ANDAND; + m["&="] = AND_EQ; + m["^="] = XOR_EQ; + m["=="] = EQEQ; + m["!="] = NOTEQ; + m["<="] = LTEQ; + m[">="] = GTEQ; + m["<<"] = LSH; + m[">>"] = RSH; + m["+="] = PLUS_EQ; + m["-="] = MINUS_EQ; + m["*="] = STAR_EQ; + m["/="] = SLASH_EQ; + m["%="] = PERCENT_EQ; + m["<<="] = LSH_EQ; + m[">>="] = RSH_EQ; + m["::"] = COLONCOLON; + m["->"] = ARROW; + } + + return m_keywords; +} diff --git a/lldb/source/Plugins/ExpressionParser/Rust/RustLex.h b/lldb/source/Plugins/ExpressionParser/Rust/RustLex.h new file mode 100644 index 00000000000000..84bbbd984c6f8f --- /dev/null +++ b/lldb/source/Plugins/ExpressionParser/Rust/RustLex.h @@ -0,0 +1,168 @@ +//===-- RustLex.h -------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RustLex_h +#define liblldb_RustLex_h + +#include + +#include "lldb/lldb-forward.h" +#include "lldb/lldb-private.h" + +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringRef.h" +#include "lldb/Utility/Stream.h" + +namespace lldb_private { + +namespace rust { + +// Note that single-character tokens are represented by the character +// itself. +enum TokenKind { + STRING = 128, + BYTESTRING, + CHAR, + BYTE, + FLOAT, + INTEGER, + AS, + TRUE, + FALSE, + SUPER, + SELF, + MUT, + CONST, + FN, + SIZEOF, + DOTDOT, + DOTDOTEQ, + OROR, + ANDAND, + EQEQ, + NOTEQ, + LTEQ, + GTEQ, + LSH, + RSH, + PLUS_EQ, + MINUS_EQ, + SLASH_EQ, + STAR_EQ, + PERCENT_EQ, + RSH_EQ, + LSH_EQ, + AND_EQ, + OR_EQ, + XOR_EQ, + COLONCOLON, + ARROW, + IDENTIFIER, + INVALID, + THATSALLFOLKS +}; + +void PrintTokenKind(Stream &stream, int kind); + +struct Token { + int kind; + + llvm::Optional uinteger; + llvm::Optional dvalue; + // This can be NULL if no suffix was specified. + const char *number_suffix = nullptr; + std::string str; + + explicit Token(int kind_) + : kind(kind_) + { + } + + Token(int kind_, uint64_t val_, const char *suffix_ = nullptr) + : kind(kind_), + uinteger(val_), + number_suffix(suffix_) + { + } + + Token(int kind_, double val_, const char *suffix_ = nullptr) + : kind(kind_), + dvalue(val_), + number_suffix(suffix_) + { + } + + Token(int kind_, std::string &&str_) + : kind(kind_), + str(std::move(str_)) + { + } + + bool operator==(const Token &other) const { + if (kind != other.kind || + uinteger != other.uinteger || + dvalue != other.dvalue || + str != other.str) { + return false; + } + + if (number_suffix == nullptr) { + return other.number_suffix == nullptr; + } + if (other.number_suffix == nullptr) { + return false; + } + + return strcmp(number_suffix, other.number_suffix) == 0; + } +}; + +class Lexer { +public: + + explicit Lexer(llvm::StringRef ref) + : m_iter(ref.begin()), + m_end(ref.end()) + { + } + + Token Next(); + +private: + + const char *CheckSuffix(const char *const *suffixes); + bool BasicInteger(int *radix_out, std::string *value); + Token MaybeByteLiteral(); + Token MaybeRawString(bool is_byte = false); + Token String(bool is_byte = false); + Token Character(bool is_byte = false); + Token Operator(); + int Float(std::string *value); + Token Number(); + Token Identifier(); + bool AppendEscape(std::string *result, bool is_byte); + bool ParseHex(uint64_t *result, int min_digits, int max_digits); + bool ParseEscape(uint64_t *result, bool is_byte); + bool Lookup(const ::llvm::StringRef &str, int *result); + + llvm::StringRef::iterator m_iter; + llvm::StringRef::iterator m_end; + + size_t Remaining() const { return m_end - m_iter; } + + static llvm::StringMap *Keywords(); + + static llvm::StringMap *m_keywords; +}; + +} // namespace rust + +} // namespace lldb_private + +#endif // liblldb_RustLex_h diff --git a/lldb/source/Plugins/ExpressionParser/Rust/RustParse.cpp b/lldb/source/Plugins/ExpressionParser/Rust/RustParse.cpp new file mode 100644 index 00000000000000..96f6942ec3ef0f --- /dev/null +++ b/lldb/source/Plugins/ExpressionParser/Rust/RustParse.cpp @@ -0,0 +1,2284 @@ +//===-- RustParse.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "RustParse.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Expression/DiagnosticManager.h" +#include "lldb/Symbol/Block.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/RustASTContext.h" +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Symbol/TypeList.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/Status.h" +#include +#include + +#include "Plugins/ExpressionParser/Clang/ASTStructExtractor.h" +#include "RustFunctionCaller.h" + +using namespace lldb_private::rust; +using namespace lldb_private; +using namespace lldb; +using namespace llvm; + +static std::set primitive_type_names + { + "bool", "char", + "u8", "u16", "u32", "u64", "u128", + "i8", "i16", "i32", "i64", "i128", + "f32", "f64" + }; + +static RustASTContext * +GetASTContext(CompilerType type, Status &error) { + RustASTContext *result = llvm::dyn_cast_or_null(type.GetTypeSystem()); + if (!result) { + error.SetErrorString("not a Rust type!?"); + } + return result; +} + +static RustASTContext * +GetASTContext(ValueObjectSP val, Status &error) { + return GetASTContext(val->GetCompilerType(), error); +} + +static RustASTContext * +GetASTContext(ExecutionContext &ctxt, Status &error) { + Target *target = ctxt.GetTargetPtr(); + TypeSystem *sys = target->GetScratchTypeSystemForLanguage(&error, eLanguageTypeRust); + if (!sys) { + return nullptr; + } + RustASTContext *result = llvm::dyn_cast_or_null(sys); + if (!result) { + error.SetErrorString("not a Rust type!?"); + } + return result; +} + +static ValueObjectSP +CreateValueFromScalar(ExecutionContext &exe_ctx, Scalar &scalar, CompilerType type, + Status &error) { + DataExtractor data; + if (!scalar.GetData(data)) { + error.SetErrorString("could not get data from scalar"); + return ValueObjectSP(); + } + ValueObjectSP result = ValueObject::CreateValueObjectFromData("", data, exe_ctx, type); + if (!result) { + error.SetErrorString("could not create value object"); + } + return result; +} + +static ValueObjectSP +CreateValueInMemory(ExecutionContext &exe_ctx, CompilerType type, Status &error) { + if (!exe_ctx.HasProcessScope()) { + error.SetErrorString("need a running inferior to evaluate this"); + return ValueObjectSP(); + } + + Process *proc = exe_ctx.GetProcessPtr(); + uint64_t size = type.GetByteSize(proc); + addr_t addr = proc->AllocateMemory(size, + lldb::ePermissionsWritable | lldb::ePermissionsReadable, + error); + if (addr == LLDB_INVALID_ADDRESS) { + return ValueObjectSP(); + } + + return ValueObject::CreateValueObjectFromAddress("", addr, exe_ctx, type); +} + +static bool +SetField(const ValueObjectSP &object, const char *name, uint64_t value, Status &error) { + Scalar scalar(value); + DataExtractor data; + if (!scalar.GetData(data)) { + error.SetErrorString("could not get data from scalar"); + return false; + } + + ValueObjectSP child = object->GetChildMemberWithName(ConstString(name), true); + if (!child) { + error.SetErrorStringWithFormat("could not find child named \"%s\"", name); + return false; + } + return child->SetData(data, error); +} + +static bool +SetField(const ValueObjectSP &object, const char *name, const ValueObjectSP &value, + Status &error) { + DataExtractor data; + if (!value->GetData(data, error)) { + return false; + } + + ValueObjectSP child = object->GetChildMemberWithName(ConstString(name), true); + if (!child) { + error.SetErrorStringWithFormat("could not find child named \"%s\"", name); + return false; + } + return child->SetData(data, error); +} + +static CompilerType +GetTypeByName(ExecutionContext &exe_ctx, const char *name, Status &error) { + Target *target = exe_ctx.GetTargetPtr(); + if (!target) { + error.SetErrorString("could not get target to look up type"); + return CompilerType(); + } + + SymbolContext sc; + TypeList type_list; + llvm::DenseSet searched_symbol_files; + uint32_t num_matches = target->GetImages().FindTypes(sc, ConstString(name), true, + 2, searched_symbol_files, type_list); + if (num_matches > 0) { + return type_list.GetTypeAtIndex(0)->GetFullCompilerType(); + } + error.SetErrorStringWithFormat("could not find type \"%s\"", name); + return CompilerType(); +} + +ValueObjectSP +lldb_private::rust::UnaryDereference(ExecutionContext &exe_ctx, ValueObjectSP addr, Status &error) { + return addr->Dereference(error); +} + +ValueObjectSP +lldb_private::rust::UnaryAddr(ExecutionContext &exe_ctx, ValueObjectSP val, Status &error) { + return val->AddressOf(error); +} + +ValueObjectSP +lldb_private::rust::UnaryPlus(ExecutionContext &exe_ctx, ValueObjectSP val, Status &error) { + if (RustASTContext *ast = GetASTContext(val, error)) { + CompilerType type = val->GetCompilerType(); + if (type.IsScalarType() && !ast->IsBooleanType(type.GetOpaqueQualType())) { + return val; + } + error.SetErrorString("not a scalar type"); + } + return ValueObjectSP(); +} + +ValueObjectSP +lldb_private::rust::UnaryNegate(ExecutionContext &exe_ctx, ValueObjectSP val, Status &error) { + if (RustASTContext *ast = GetASTContext(val, error)) { + CompilerType type = val->GetCompilerType(); + if (!type.IsScalarType() || ast->IsBooleanType(type.GetOpaqueQualType())) { + error.SetErrorString("not a scalar type"); + return ValueObjectSP(); + } + + Scalar scalar; + if (!val->ResolveValue(scalar)) { + error.SetErrorString("could not resolve scalar value"); + return ValueObjectSP(); + } + if (!scalar.UnaryNegate()) { + error.SetErrorString("could not negate scalar value"); + return ValueObjectSP(); + } + + return CreateValueFromScalar(exe_ctx, scalar, type, error); + } + return ValueObjectSP(); +} + +ValueObjectSP +lldb_private::rust::UnaryComplement(ExecutionContext &exe_ctx, ValueObjectSP val, Status &error) { + RustASTContext *ast = GetASTContext(val, error); + if (!ast) { + return ValueObjectSP(); + } + + CompilerType type = val->GetCompilerType(); + if (!type.IsScalarType()) { + error.SetErrorString("not a scalar type"); + return ValueObjectSP(); + } + + Scalar scalar; + if (!val->ResolveValue(scalar)) { + error.SetErrorString("could not resolve scalar value"); + return ValueObjectSP(); + } + + if (ast->IsBooleanType(type.GetOpaqueQualType())) { + scalar = Scalar(int(scalar.IsZero())); + } else if (!scalar.OnesComplement()) { + error.SetErrorString("could not negate scalar value"); + return ValueObjectSP(); + } + + return CreateValueFromScalar(exe_ctx, scalar, type, error); +} + +ValueObjectSP +lldb_private::rust::UnarySizeof(ExecutionContext &exe_ctx, ValueObjectSP val, Status &error) { + if (RustASTContext *ast = GetASTContext(val, error)) { + uint32_t ptr_size = ast->GetPointerByteSize(); + CompilerType type = ast->CreateIntegralType(ConstString("usize"), false, ptr_size); + Scalar size (val->GetByteSize()); + return CreateValueFromScalar(exe_ctx, size, type, error); + } + return ValueObjectSP(); +} + +template +ValueObjectSP +lldb_private::rust::BinaryOperation (ExecutionContext &exe_ctx, lldb::ValueObjectSP left, + lldb::ValueObjectSP right, Status &error) { + RustASTContext *ast = GetASTContext(left, error); + if (!ast) { + return ValueObjectSP(); + } + + if (!left->GetCompilerType().IsScalarType() || !right->GetCompilerType().IsScalarType()) { + error.SetErrorString("not a scalar type"); + return ValueObjectSP(); + } + + Scalar sleft, sright; + if (!left->ResolveValue(sleft) || !right->ResolveValue(sright)) { + error.SetErrorString("could not resolve scalar value"); + return ValueObjectSP(); + } + + Scalar result = T()(sleft, sright); + if (result.GetType() == Scalar::e_void) { + error.SetErrorString("could not resolve scalar value"); + return ValueObjectSP(); + } + + size_t byte_size = result.GetByteSize(); + CompilerType type; + + // FIXME there has to be a better way. + switch (result.GetType()) { + case Scalar::e_sint: + case Scalar::e_slong: + case Scalar::e_slonglong: + type = ast->CreateIntrinsicIntegralType(true, byte_size); + break; + + case Scalar::e_uint: + case Scalar::e_ulong: + case Scalar::e_ulonglong: + type = ast->CreateIntrinsicIntegralType(false, byte_size); + break; + + case Scalar::e_float: + case Scalar::e_double: + if (byte_size == 4) { + type = ast->CreateFloatType(ConstString("f32"), byte_size); + break; + } else if (byte_size == 8) { + type = ast->CreateFloatType(ConstString("f64"), byte_size); + break; + } + /* FALL THROUGH */ + + default: + error.SetErrorString("unknown type resulting from binary operation"); + return ValueObjectSP(); + } + + ValueObjectSP result_obj = CreateValueFromScalar(exe_ctx, result, type, error); + + if (ASSIGN) { + DataExtractor data; + result_obj->GetData(data, error); + if (error.Fail()) { + return ValueObjectSP(); + } + + if (!left->SetData(data, error)) { + return ValueObjectSP(); + } + + result_obj = left; + } + + return result_obj; +} + +template +ValueObjectSP +lldb_private::rust::Comparison (ExecutionContext &exe_ctx, lldb::ValueObjectSP left, + lldb::ValueObjectSP right, Status &error) { + RustASTContext *ast = GetASTContext(left, error); + if (!ast) { + return ValueObjectSP(); + } + + if (!left->GetCompilerType().IsScalarType() || !right->GetCompilerType().IsScalarType()) { + error.SetErrorString("not a scalar type"); + return ValueObjectSP(); + } + + Scalar sleft, sright; + if (!left->ResolveValue(sleft) || !right->ResolveValue(sright)) { + error.SetErrorString("could not resolve scalar value"); + return ValueObjectSP(); + } + + bool result = T()(sleft, sright); + Scalar value = int(result); + + CompilerType type = ast->CreateBoolType(ConstString("bool")); + return CreateValueFromScalar(exe_ctx, value, type, error); +} + +ValueObjectSP +lldb_private::rust::ArrayIndex (ExecutionContext &exe_ctx, lldb::ValueObjectSP left, + lldb::ValueObjectSP right, Status &error) { + if (!right->GetCompilerType().IsScalarType()) { + error.SetErrorString("not a scalar type"); + return ValueObjectSP(); + } + if (RustASTContext *ast = GetASTContext(right, error)) { + CompilerType type = right->GetCompilerType(); + if (ast->IsBooleanType(type.GetOpaqueQualType())) { + error.SetErrorString("not a scalar type"); + return ValueObjectSP(); + } + } + + Scalar sright; + if (!right->ResolveValue(sright)) { + error.SetErrorString("could not resolve scalar value"); + return ValueObjectSP(); + } + unsigned long index = sright.ULong(-1); + + ValueObjectSP result = left->GetChildAtIndex(index, true); + if (!result) { + error.SetErrorString("array index out of bounds"); + } + return result; +} + +CompilerDeclContext +RustPath::FrameDeclContext(ExecutionContext &exe_ctx, Status &error) { + StackFrame *frame = exe_ctx.GetFramePtr(); + if (frame == nullptr) { + // FIXME? + error.SetErrorString("no frame when looking up item"); + return CompilerDeclContext(); + } + + SymbolContext sym_ctx = frame->GetSymbolContext(lldb::eSymbolContextFunction | + lldb::eSymbolContextBlock); + + if (sym_ctx.block) { + CompilerDeclContext frame_decl_context = sym_ctx.block->GetDeclContext(); + if (frame_decl_context) { + return frame_decl_context; + } + } + + error.SetErrorString("could not find frame's decl context"); + return CompilerDeclContext(); +} + +bool +RustPath::GetDeclContext(ExecutionContext &exe_ctx, Status &error, + CompilerDeclContext *result, bool *simple_name) { + *simple_name = true; + + RustASTContext *ast = GetASTContext(exe_ctx, error); + if (!ast) { + return false; + } + + CompilerDeclContext decl_ctx = FrameDeclContext(exe_ctx, error); + if (!decl_ctx) { + return false; + } + + if (!m_relative || m_self || m_supers > 0) { + for (int i = 0; !m_relative || i < m_supers; ++i) { + CompilerDeclContext next = ast->GetDeclContextDeclContext(decl_ctx); + if (next.GetName().IsEmpty()) { + if (!m_relative) { + break; + } + error.SetErrorString("too many 'super's"); + return false; + } + decl_ctx = next; + } + + *simple_name = false; + *result = decl_ctx; + } + + *result = decl_ctx; + return true; +} + +bool +RustPath::AppendGenerics(ExecutionContext &exe_ctx, Status &error, std::string *name) { + if (!m_generic_params.empty()) { + *name += "<"; + bool first = true; + for (const RustTypeExpressionUP ¶m : m_generic_params) { + CompilerType type = param->Evaluate(exe_ctx, error); + if (!type) { + return false; + } + if (!first) { + *name += ", "; + } + first = false; + *name += type.GetTypeName().AsCString(); + } + *name += ">"; + } + return true; +} + +bool +RustPath::FindDecl(ExecutionContext &exe_ctx, Status &error, + lldb::VariableSP *var, + lldb_private::Function **function, + std::string *base_name) { + bool simple_name; + CompilerDeclContext decl_ctx; + if (!GetDeclContext(exe_ctx, error, &decl_ctx, &simple_name)) { + return false; + } + + if (m_path.size() > 1) { + simple_name = false; + } + + std::string name = m_path.back(); + if (!AppendGenerics(exe_ctx, error, &name)) { + return false; + } + + if (simple_name) { + *base_name = name; + // ... but still look in the current context. + } + + RustASTContext *ast = GetASTContext(exe_ctx, error); + if (!ast) { + return false; + } + + // Construct the fully-qualified name. + std::vector fullname; + while (decl_ctx.GetName()) { + fullname.push_back(decl_ctx.GetName()); + decl_ctx = ast->GetDeclContextDeclContext(decl_ctx); + } + std::reverse(fullname.begin(), fullname.end()); + + auto end_iter = m_path.end() - 1; + for (auto iter = m_path.begin(); iter != end_iter; ++iter) { + fullname.push_back(ConstString(iter->c_str())); + } + + // Now try to find this name in each Module. + Target *target = exe_ctx.GetTargetPtr(); + if (!target) { + error.SetErrorString("could not get target to look up item"); + return false; + } + + VariableList var_list; + *function = nullptr; + ConstString cs_name(name.c_str()); + const ModuleList &module_list = target->GetImages(); + module_list.ForEach( + [&](const ModuleSP &mod) { + TypeSystem *ts = mod->GetTypeSystemForLanguage(eLanguageTypeRust); + if (!ts) { + return true; + } + SymbolFile *symbol_file = ts->GetSymbolFile(); + if (!symbol_file) { + return true; + } + + SymbolContext null_sc; + CompilerDeclContext found_ns; + for (const ConstString &ns_name : fullname) { + found_ns = symbol_file->FindNamespace(null_sc, ns_name, &found_ns); + if (!found_ns) { + break; + } + } + + if (found_ns) { + mod->FindGlobalVariables(cs_name, &found_ns, 1, var_list); + + SymbolContextList context_list; + mod->FindFunctions(cs_name, &found_ns, eFunctionNameTypeBase, false, false, + true, context_list); + for (size_t i = 0; i < context_list.GetSize(); ++i) { + SymbolContext sym_context; + if (context_list.GetContextAtIndex(i, sym_context) && sym_context.function) { + *function = sym_context.function; + break; + } + } + } + + return var_list.GetSize() == 0 && *function == nullptr; + }); + + if (var_list.GetSize() != 0) { + *var = var_list.GetVariableAtIndex(0); + } else if (*function != nullptr) { + // Ok. + } else { + if (base_name->empty()) { + error.SetErrorStringWithFormat("could not find decl \"%s\"", name.c_str()); + } + return false; + } + + return true; +} + +CompilerType +RustPath::EvaluateAsType(ExecutionContext &exe_ctx, Status &error) { + std::string fullname = Name(exe_ctx, error); + if (error.Fail()) { + return CompilerType(); + } + return GetTypeByName(exe_ctx, fullname.c_str(), error); +} + +std::string +RustPath::Name(ExecutionContext &exe_ctx, Status &error) { + std::string name; + + CompilerDeclContext decl_ctx; + bool ignore; + if (!GetDeclContext(exe_ctx, error, &decl_ctx, &ignore)) { + return std::string(); + } + + if (m_path.size() == 1 && + primitive_type_names.find(m_path[0]) != primitive_type_names.end()) { + // Primitive. + return m_path[0]; + } + + // FIXME local types + name += "::"; + name += decl_ctx.GetScopeQualifiedName().AsCString(); + name += "::"; + + { + bool first = true; + for (const std::string &str : m_path) { + if (!first) { + name += "::"; + } + first = false; + name += str; + } + } + + if (!AppendGenerics(exe_ctx, error, &name)) { + return std::string(); + } + + return name; +} + +lldb::ValueObjectSP +RustLiteral::Evaluate(ExecutionContext &exe_ctx, Status &error) { + CompilerType type = m_type->Evaluate(exe_ctx, error); + if (!type) { + return ValueObjectSP(); + } + return CreateValueFromScalar(exe_ctx, m_value, type, error); +} + +lldb::ValueObjectSP +RustBooleanLiteral::Evaluate(ExecutionContext &exe_ctx, Status &error) { + RustASTContext *ast = GetASTContext(exe_ctx, error); + if (!ast) { + return ValueObjectSP(); + } + + CompilerType type = ast->CreateBoolType(ConstString("bool")); + Scalar val(m_value); + return CreateValueFromScalar(exe_ctx, val, type, error); +} + +lldb::ValueObjectSP +RustCharLiteral::Evaluate(ExecutionContext &exe_ctx, Status &error) { + RustASTContext *ast = GetASTContext(exe_ctx, error); + if (!ast) { + return ValueObjectSP(); + } + + CompilerType type; + if (m_is_byte) { + type = ast->CreateIntegralType(ConstString("u8"), false, 1); + } else { + type = ast->CreateCharType(); + } + Scalar val(m_value); + return CreateValueFromScalar(exe_ctx, val, type, error); +} + +lldb::ValueObjectSP +RustStringLiteral::Evaluate(ExecutionContext &exe_ctx, Status &error) { + RustASTContext *ast = GetASTContext(exe_ctx, error); + if (!ast) { + return ValueObjectSP(); + } + + CompilerType u8 = ast->CreateIntegralType(ConstString("u8"), false, 1); + CompilerType array_type = ast->CreateArrayType(u8, m_value.size()); + if (!array_type) { + error.SetErrorString("could not create array type"); + return ValueObjectSP(); + } + + // Byte order and address size don't matter here. + DataExtractor data(m_value.c_str(), m_value.size(), eByteOrderInvalid, 4); + ValueObjectSP array = CreateValueInMemory(exe_ctx, array_type, error); + if (!array) { + return array; + } + + if (!array->SetData(data, error)) { + return ValueObjectSP(); + } + + if (m_is_byte) { + return array; + } + + CompilerType str_type = GetTypeByName(exe_ctx, "&str", error); + if (!str_type) { + return ValueObjectSP(); + } + + ValueObjectSP str_val = CreateValueInMemory(exe_ctx, str_type, error); + if (!str_val) { + return str_val; + } + + if (!SetField(str_val, "data_ptr", array->GetAddressOf(), error) || + !SetField(str_val, "length", m_value.size(), error)) { + return ValueObjectSP(); + } + + return str_val; +} + +lldb::ValueObjectSP +RustPathExpression::Evaluate(ExecutionContext &exe_ctx, Status &error) { + Target *target = exe_ctx.GetTargetPtr(); + if (!target) { + error.SetErrorString("could not get target to look up item"); + return ValueObjectSP(); + } + + StackFrame *frame = exe_ctx.GetFramePtr(); + if (frame == nullptr) { + // FIXME? + error.SetErrorString("no frame when looking up item"); + return ValueObjectSP(); + } + + std::string name; + VariableSP decl; + Function *function; + m_path->FindDecl(exe_ctx, error, &decl, &function, &name); + if (error.Fail()) { + return ValueObjectSP(); + } + + if (!name.empty()) { + VariableListSP frame_vars = frame->GetInScopeVariableList(false); + if (frame_vars) { + if (VariableSP var = frame_vars->FindVariable(ConstString(name.c_str()))) { + // FIXME dynamic? should come from the options, which we aren't + // passing in. + return frame->GetValueObjectForFrameVariable(var, eDynamicDontRunTarget); + } + } + } + if (decl) { + return frame->TrackGlobalVariable(decl, eDynamicDontRunTarget); + } + + if (function) { + Address addr = function->GetAddressRange().GetBaseAddress(); + addr_t address = addr.GetCallableLoadAddress(target); + CompilerType type = function->GetCompilerType().GetPointerType(); + Scalar saddr(address); + return CreateValueFromScalar(exe_ctx, saddr, type, error); + } + + // FIXME use the name + error.SetErrorStringWithFormat("could not find item"); + return ValueObjectSP(); +} + +lldb::ValueObjectSP +RustAndAndExpression::Evaluate(ExecutionContext &exe_ctx, Status &error) { + ValueObjectSP vleft = m_left->Evaluate(exe_ctx, error); + if (!vleft) { + return vleft; + } + + if (RustASTContext *ast = GetASTContext(vleft, error)) { + CompilerType type = vleft->GetCompilerType(); + if (!ast->IsBooleanType(type.GetOpaqueQualType())) { + error.SetErrorString("not a boolean type"); + return ValueObjectSP(); + } + + Scalar sleft; + if (!vleft->ResolveValue(sleft)) { + error.SetErrorString("could not resolve scalar value"); + return ValueObjectSP(); + } + if (sleft.IsZero()) { + return vleft; + } + } else { + return ValueObjectSP(); + } + + ValueObjectSP vright = m_right->Evaluate(exe_ctx, error); + if (!vright) { + return vright; + } + + // FIXME should probably error out if not boolean. + return vright; +} + +lldb::ValueObjectSP +RustOrOrExpression::Evaluate(ExecutionContext &exe_ctx, Status &error) { + ValueObjectSP vleft = m_left->Evaluate(exe_ctx, error); + if (!vleft) { + return vleft; + } + + if (RustASTContext *ast = GetASTContext(vleft, error)) { + CompilerType type = vleft->GetCompilerType(); + if (!ast->IsBooleanType(type.GetOpaqueQualType())) { + error.SetErrorString("not a boolean type"); + return ValueObjectSP(); + } + + Scalar sleft; + if (!vleft->ResolveValue(sleft)) { + error.SetErrorString("could not resolve scalar value"); + return ValueObjectSP(); + } + if (!sleft.IsZero()) { + return vleft; + } + } else { + return ValueObjectSP(); + } + + ValueObjectSP vright = m_right->Evaluate(exe_ctx, error); + if (!vright) { + return vright; + } + + // FIXME should probably error out if not boolean. + return vright; +} + +lldb::ValueObjectSP +RustFieldExpression::Evaluate(ExecutionContext &exe_ctx, Status &error) { + ValueObjectSP left = m_left->Evaluate(exe_ctx, error); + if (!left) { + return left; + } + + // We always want to let users see the real type, because in Rust + // only trait objects and enums can be dynamic. + ValueObjectSP dynamic = left->GetDynamicValue(eDynamicCanRunTarget); + if (dynamic) { + left = dynamic; + } + + ValueObjectSP result = left->GetChildMemberWithName(ConstString(m_field.c_str()), true); + if (!result) { + error.SetErrorStringWithFormat("no field named %s", m_field.c_str()); + } + return result; +} + +lldb::ValueObjectSP +RustTupleFieldExpression::Evaluate(ExecutionContext &exe_ctx, Status &error) { + ValueObjectSP left = m_left->Evaluate(exe_ctx, error); + if (!left) { + return left; + } + + // We always want to let users see the real type, because in Rust + // only trait objects and enums can be dynamic. + ValueObjectSP dynamic = left->GetDynamicValue(eDynamicCanRunTarget); + if (dynamic) { + left = dynamic; + } + + ValueObjectSP result = left->GetChildAtIndex(m_field, true); + if (!result) { + error.SetErrorStringWithFormat("no field number %d", m_field); + } + return result; +} + +lldb::ValueObjectSP +RustAssignment::Evaluate(ExecutionContext &exe_ctx, Status &error) { + ValueObjectSP left = m_left->Evaluate(exe_ctx, error); + if (!left) { + return left; + } + + ValueObjectSP right = m_right->Evaluate(exe_ctx, error); + if (!right) { + return right; + } + + DataExtractor data; + right->GetData(data, error); + if (error.Fail()) { + return ValueObjectSP(); + } + + if (!left->SetData(data, error)) { + return ValueObjectSP(); + } + + return left; +} + +lldb::ValueObjectSP +RustTupleExpression::Evaluate(ExecutionContext &exe_ctx, Status &error) { + error.SetErrorString("tuple expressions unimplemented"); + return ValueObjectSP(); +} + +lldb::ValueObjectSP +RustArrayLiteral::Evaluate(ExecutionContext &exe_ctx, Status &error) { + error.SetErrorString("array literals unimplemented"); + return ValueObjectSP(); +} + +lldb::ValueObjectSP +RustArrayWithLength::Evaluate(ExecutionContext &exe_ctx, Status &error) { + ValueObjectSP value = m_value->Evaluate(exe_ctx, error); + if (!value) { + return value; + } + ValueObjectSP length = m_length->Evaluate(exe_ctx, error); + if (!length) { + return length; + } + Scalar slength; + if (!length->ResolveValue(slength)) { + error.SetErrorString("could not resolve scalar value"); + return ValueObjectSP(); + } + + RustASTContext *ast = GetASTContext(value, error); + if (!ast) { + return ValueObjectSP(); + } + CompilerType type = ast->CreateArrayType(value->GetCompilerType(), slength.UInt()); + if (!type) { + error.SetErrorString("could not create array type"); + return ValueObjectSP(); + } + + DataExtractor data; + value->GetData(data, error); + if (error.Fail()) { + return ValueObjectSP(); + } + + DataExtractor array_contents; + for (unsigned i = 0; i < slength.UInt(); ++i) { + if (!array_contents.Append(data)) { + error.SetErrorString("could not create array contents"); + return ValueObjectSP(); + } + } + + ValueObjectSP result = ValueObject::CreateValueObjectFromData("", array_contents, exe_ctx, type); + if (!result) { + error.SetErrorString("could not create array value object"); + } + return result; +} + +lldb::ValueObjectSP +RustCall::MaybeEvalTupleStruct(ExecutionContext &exe_ctx, Status &error) { + RustPathExpression *path_expr = m_func->AsPath(); + if (!path_expr) { + return ValueObjectSP(); + } + + CompilerType type = path_expr->EvaluateAsType(exe_ctx, error); + if (!type) { + // If we didn't find this as a type, keep trying as a function + // call. + error.Clear(); + return ValueObjectSP(); + } + + // After this point, all errors are real. + + RustASTContext *context = GetASTContext(type, error); + if (!context) { + return ValueObjectSP(); + } + if (!context->IsTupleType(type)) { + error.SetErrorString("not a tuple type"); + return ValueObjectSP(); + } + + if (m_exprs.size() < type.GetNumFields()) { + error.SetErrorString("not enough initializers for tuple"); + return ValueObjectSP(); + } else if (m_exprs.size() > type.GetNumFields()) { + error.SetErrorString("too many initializers for tuple"); + return ValueObjectSP(); + } + + ValueObjectSP result = CreateValueInMemory(exe_ctx, type, error); + if (!result) { + return result; + } + + for (size_t i = 0; i < m_exprs.size(); ++i) { + ValueObjectSP init_val = m_exprs[i]->Evaluate(exe_ctx, error); + if (!init_val) { + return init_val; + } + + DataExtractor data; + if (!init_val->GetData(data, error)) { + error.SetErrorString("could not get data from value"); + return ValueObjectSP(); + } + + ValueObjectSP child = result->GetChildAtIndex(i, true); + if (!child) { + error.SetErrorStringWithFormat("could not find child at index \"%d\"", int(i)); + return ValueObjectSP(); + } + if (!child->SetData(data, error)) { + return ValueObjectSP(); + } + } + + return result; +} + +lldb::ValueObjectSP +RustCall::Evaluate(ExecutionContext &exe_ctx, Status &error) { + error.Clear(); + ValueObjectSP result = MaybeEvalTupleStruct(exe_ctx, error); + if (result) { + return result; + } else if (error.Fail()) { + // This looked like a tuple struct expression but failed. + return result; + } + // This was not a tuple struct expression, so fall through to + // function call. + + ValueObjectSP func = m_func->Evaluate(exe_ctx, error); + if (!func) { + return func; + } + if (!func->GetCompilerType().IsFunctionPointerType()) { + error.SetErrorString("not calling a function"); + return ValueObjectSP(); + } + CompilerType function_type = func->GetCompilerType().GetPointeeType(); + CompilerType return_type = function_type.GetFunctionReturnType(); + + if (m_exprs.size() < function_type.GetNumberOfFunctionArguments()) { + error.SetErrorString("too few arguments to function"); + return ValueObjectSP(); + } else if (m_exprs.size() > function_type.GetNumberOfFunctionArguments()) { + error.SetErrorString("too many arguments to function"); + return ValueObjectSP(); + } + + addr_t addr = func->GetPointerValue(); + Address func_addr(addr); + + std::vector hold; + ValueList args; + for (auto &&arg : m_exprs) { + ValueObjectSP varg = arg->Evaluate(exe_ctx, error); + if (!varg) { + return varg; + } + hold.push_back(varg); + + // FIXME - mega hack to work around FunctionCaller limitation. + // Ideally this would be hidden in RustFunctionCaller at least. + if (varg->GetCompilerType().IsAggregateType()) { + varg = varg->AddressOf(error); + if (!varg) { + return varg; + } + hold.push_back(varg); + } + + args.PushValue(varg->GetValue()); + } + + // FIXME must cast each argument to the correct type here. + + // FIXME might be nice to stick the name in there. + RustFunctionCaller call(*exe_ctx.GetBestExecutionContextScope(), function_type, + return_type, func_addr, args, nullptr); + DiagnosticManager diags; + Value results; + ExpressionResults ef_result = + call.ExecuteFunction(exe_ctx, nullptr, EvaluateExpressionOptions(), diags, results); + + if (ef_result != eExpressionCompleted) { + // FIXME use the diagnostics. + error.SetErrorString("function call failed"); + return ValueObjectSP(); + } + + DataExtractor data; + if (!results.GetData(data)) { + error.SetErrorString("could not extract return value"); + return ValueObjectSP(); + } + + result = ValueObject::CreateValueObjectFromData("", data, exe_ctx, return_type); + if (!result) { + error.SetErrorString("could not create function return value object"); + } + return result; +} + +lldb::ValueObjectSP +RustCast::Evaluate(ExecutionContext &exe_ctx, Status &error) { + CompilerType type = m_type->Evaluate(exe_ctx, error); + if (!type) { + return ValueObjectSP(); + } + + ValueObjectSP value = m_expr->Evaluate(exe_ctx, error); + if (!value) { + return value; + } + + return value->Cast(type); +} + +lldb::ValueObjectSP +RustStructExpression::Evaluate(ExecutionContext &exe_ctx, Status &error) { + CompilerType type = m_path->Evaluate(exe_ctx, error); + if (!type) { + return ValueObjectSP(); + } + RustASTContext *context = GetASTContext(type, error); + if (!context) { + return ValueObjectSP(); + } + // FIXME could tighten this to really ensure it is a struct and not + // an enum. + if (!type.IsAggregateType() || + type.IsArrayType(nullptr, nullptr, nullptr) || + context->IsTupleType(type)) { + error.SetErrorStringWithFormat("type \"%s\" is not a structure type", + type.GetDisplayTypeName().AsCString()); + return ValueObjectSP(); + } + + ValueObjectSP result = CreateValueInMemory(exe_ctx, type, error); + if (!result) { + return result; + } + + if (m_copy) { + ValueObjectSP copy = m_copy->Evaluate(exe_ctx, error); + if (!copy) { + return copy; + } + + DataExtractor data; + copy->GetData(data, error); + if (error.Fail()) { + return ValueObjectSP(); + } + + if (!result->SetData(data, error)) { + return ValueObjectSP(); + } + } else { + if (m_inits.size() != type.GetNumFields()) { + error.SetErrorStringWithFormat("some initializers missing for \"%s\"", + type.GetDisplayTypeName().AsCString()); + return ValueObjectSP(); + } + } + + for (const auto &init : m_inits) { + ValueObjectSP init_val = init.second->Evaluate(exe_ctx, error); + if (!init_val) { + return init_val; + } + if (!SetField(result, init.first.c_str(), init_val, error)) { + return ValueObjectSP(); + } + } + + return result; +} + +lldb::ValueObjectSP +RustRangeExpression::Evaluate(ExecutionContext &exe_ctx, Status &error) { + ValueObjectSP left, right; + + if (m_left) { + left = m_left->Evaluate(exe_ctx, error); + if (!left) { + return left; + } + } + if (m_right) { + right = m_right->Evaluate(exe_ctx, error); + if (!right) { + return right; + } + } + + std::string name; + CompilerType element_type; + if (left) { + if (right) { + name = m_inclusive ? "::core::ops::range::RangeInclusive" : "::core::ops::range::Range"; + // FIXME this check seems wrong + if (left->GetCompilerType() != right->GetCompilerType()) { + // FIXME also we could be friendlier about integer promotion + // here. + error.SetErrorString("operands of \"..\" do not have same type"); + return ValueObjectSP(); + } + } else { + name = "::core::ops::range::RangeFrom"; + } + element_type = left->GetCompilerType(); + } else if (right) { + name = m_inclusive ? "::core::ops::range::RangeToInclusive" : "::core::ops::range::RangeTo"; + element_type = right->GetCompilerType(); + } else { + name = "::core::ops::range::RangeFull"; + } + assert(!name.empty()); + + if (element_type) { + name = name + "<" + element_type.GetTypeName().AsCString() + ">"; + } + + CompilerType range_type = GetTypeByName(exe_ctx, name.c_str(), error); + if (!range_type) { + return ValueObjectSP(); + } + + ValueObjectSP result = CreateValueInMemory(exe_ctx, range_type, error); + if (!result) { + return result; + } + + if (left && !SetField(result, "start", left, error)) { + return ValueObjectSP(); + } + if (right && !SetField(result, "end", right, error)) { + return ValueObjectSP(); + } + + return result; +} + +//////////////////////////////////////////////////////////////// +// Types + +CompilerType +RustArrayTypeExpression::Evaluate(ExecutionContext &exe_ctx, Status &error) { + RustASTContext *context = GetASTContext(exe_ctx, error); + if (!context) { + return CompilerType(); + } + + CompilerType element = m_element->Evaluate(exe_ctx, error); + if (!element) { + return element; + } + + return context->CreateArrayType(element, m_len); +} + +CompilerType +RustPointerTypeExpression::Evaluate(ExecutionContext &exe_ctx, Status &error) { + CompilerType target = m_target->Evaluate(exe_ctx, error); + if (!target) { + return target; + } + // FIXME references + return target.GetPointerType(); +} + +CompilerType +RustSliceTypeExpression::Evaluate(ExecutionContext &exe_ctx, Status &error) { + error.SetErrorString("slice type lookup unimplemented"); + return CompilerType(); +} + +CompilerType +RustFunctionTypeExpression::Evaluate(ExecutionContext &exe_ctx, Status &error) { + RustASTContext *context = GetASTContext(exe_ctx, error); + if (!context) { + return CompilerType(); + } + + CompilerType ret = m_result->Evaluate(exe_ctx, error); + if (!ret) { + return ret; + } + + std::vector args; + for (const RustTypeExpressionUP &arg : m_arguments) { + CompilerType argtype = arg->Evaluate(exe_ctx, error); + if (!argtype) { + return argtype; + } + args.push_back(argtype); + } + + return context->CreateFunctionType(ConstString(""), ret, std::move(args)); +} + +CompilerType +RustTupleTypeExpression::Evaluate(ExecutionContext &exe_ctx, Status &error) { + error.SetErrorString("tuple type lookup unimplemented"); + return CompilerType(); +} + +//////////////////////////////////////////////////////////////// +// Output + +Stream &lldb_private::operator<< (Stream &stream, const RustExpressionUP &expr) { + if (expr) { + expr->print(stream); + } + return stream; +} + +Stream &lldb_private::operator<< (Stream &stream, const RustTypeExpressionUP &type) { + type->print(stream); + return stream; +} + +Stream &lldb_private::operator<< (Stream &stream, const Scalar &value) { + value.GetValue(&stream, false); + return stream; +} + +Stream &lldb_private::operator<< (Stream &stream, + const std::pair &value) { + return stream << value.first << ": " << value.second; +} + +//////////////////////////////////////////////////////////////// +// The parser + +template +RustExpressionUP Parser::Unary(Status &error) { + Advance(); + RustExpressionUP result = Term(error); + if (!result) { + return result; + } + return llvm::make_unique>(std::move(result)); +} + +bool Parser::ExprList(std::vector *exprs, Status &error) { + while (true) { + RustExpressionUP expr = Expr(error); + if (!expr) { + return false; + } + + exprs->push_back(std::move(expr)); + if (CurrentToken().kind != ',') { + break; + } + Advance(); + } + + return true; +} + +// This handles both a parenthesized expression and a tuple +// expression. +RustExpressionUP Parser::Parens(Status &error) { + assert(CurrentToken().kind == '('); + Advance(); + + if (CurrentToken().kind == ')') { + // Unit tuple. + Advance(); + return llvm::make_unique(std::vector()); + } + + RustExpressionUP expr = Expr(error); + if (!expr) { + return expr; + } + + if (CurrentToken().kind == ')') { + // Parenthesized expression. + Advance(); + return expr; + } else if (CurrentToken().kind != ',') { + error.SetErrorString("expected ')' or ','"); + return RustExpressionUP(); + } + + std::vector exprs; + exprs.push_back(std::move(expr)); + Advance(); + + if (CurrentToken().kind != ')') { + if (!ExprList(&exprs, error)) { + return RustExpressionUP(); + } + } + + if (CurrentToken().kind != ')') { + error.SetErrorString("expected ')'"); + return RustExpressionUP(); + } + Advance(); + + return llvm::make_unique(std::move(exprs)); +} + +RustExpressionUP Parser::Array(Status &error) { + assert(CurrentToken().kind == '['); + Advance(); + + // This doesn't affect us, so parse and ignore. + if (CurrentToken().kind == MUT) { + Advance(); + } + + RustExpressionUP expr = Expr(error); + if (!expr) { + return expr; + } + + RustExpressionUP result; + if (CurrentToken().kind == ';') { + Advance(); + + RustExpressionUP length = Expr(error); + if (!length) { + return length; + } + + result = llvm::make_unique(std::move(expr), std::move(length)); + } else if (CurrentToken().kind == ',') { + Advance(); + std::vector exprs; + exprs.push_back(std::move(expr)); + + if (ExprList(&exprs, error)) { + result = llvm::make_unique(std::move(exprs)); + } + } else { + error.SetErrorString("expected ',' or ';'"); + return result; + } + + if (CurrentToken().kind != ']') { + error.SetErrorString("expected ']'"); + result.reset(); + } else { + Advance(); + } + + return result; +} + +RustExpressionUP Parser::Field(RustExpressionUP &&lhs, Status &error) { + assert(CurrentToken().kind == '.'); + Advance(); + + RustExpressionUP result; + if (CurrentToken().kind == IDENTIFIER) { + result = llvm::make_unique(std::move(lhs), + CurrentToken().str); + Advance(); + } else if (CurrentToken().kind == INTEGER) { + result = llvm::make_unique(std::move(lhs), + CurrentToken().uinteger.getValue()); + Advance(); + } else { + error.SetErrorString("identifier or integer expected"); + } + + return result; +} + +RustExpressionUP Parser::Call(RustExpressionUP &&func, Status &error) { + assert(CurrentToken().kind == '('); + Advance(); + + std::vector exprs; + if (CurrentToken().kind != ')') { + if (!ExprList(&exprs, error)) { + return RustExpressionUP(); + } + } + + if (CurrentToken().kind != ')') { + error.SetErrorString("expected ')'"); + return RustExpressionUP(); + } + Advance(); + + return llvm::make_unique(std::move(func), std::move(exprs)); +} + +RustExpressionUP Parser::Index(RustExpressionUP &&array, Status &error) { + assert(CurrentToken().kind == '['); + Advance(); + + RustExpressionUP idx = Expr(error); + if (!idx) { + return idx; + } + + if (CurrentToken().kind != ']') { + error.SetErrorString("expected ']'"); + return RustExpressionUP(); + } + Advance(); + + return llvm::make_unique>(std::move(array), + std::move(idx)); +} + +RustExpressionUP Parser::Struct(RustTypeExpressionUP &&path, Status &error) { + assert(CurrentToken().kind == '{'); + Advance(); + + std::vector> inits; + RustExpressionUP copy; + while (CurrentToken().kind != '}') { + if (CurrentToken().kind == IDENTIFIER) { + std::string field = std::move(CurrentToken().str); + Advance(); + + RustExpressionUP value; + if (CurrentToken().kind == ',' || CurrentToken().kind == '}') { + // Plain "field". + std::string field_copy = field; + value = llvm::make_unique(std::move(field_copy)); + } else if (CurrentToken().kind != ':') { + error.SetErrorString("':' expected"); + return RustExpressionUP(); + } else { + Advance(); + + value = Expr(error); + if (!value) { + return value; + } + } + + // FIXME look for duplicates. + + inits.emplace_back(std::move(field), std::move(value)); + } else if (CurrentToken().kind == DOTDOT) { + // FIXME technically this can't occur first - a field + // initializer is needed. + Advance(); + copy = Expr(error); + if (!copy) { + return copy; + } + break; + } else { + error.SetErrorString("identifier or '..' expected"); + return RustExpressionUP(); + } + + if (CurrentToken().kind != ',') { + break; + } + Advance(); + } + + if (CurrentToken().kind != '}') { + error.SetErrorString("'}' expected"); + return RustExpressionUP(); + } + Advance(); + + return llvm::make_unique(std::move(path), std::move(inits), + std::move(copy)); +} + +RustExpressionUP Parser::Path(Status &error) { + bool relative = true; + int supers = 0; + + bool saw_self = CurrentToken().kind == SELF; + if (saw_self) { + Advance(); + if (CurrentToken().kind != COLONCOLON) { + // This one can't be a struct expression, so we just return + // directly. + return llvm::make_unique(std::string("self")); + } + } + + if (CurrentToken().kind == COLONCOLON) { + if (!saw_self) { + relative = false; + } + Advance(); + } + + if (relative) { + while (CurrentToken().kind == SUPER) { + ++supers; + Advance(); + if (CurrentToken().kind != COLONCOLON) { + error.SetErrorString("'::' expected after 'super'"); + return RustExpressionUP(); + } + Advance(); + } + } + + std::vector path; + while (CurrentToken().kind == IDENTIFIER) { + path.emplace_back(std::move(CurrentToken().str)); + Advance(); + if (CurrentToken().kind != COLONCOLON) { + break; + } + Advance(); + if (CurrentToken().kind != IDENTIFIER && CurrentToken().kind != '<') { + error.SetErrorString("identifier or '<' expected"); + return RustExpressionUP(); + } + } + + std::vector type_list; + if (CurrentToken().kind == '<') { + if (!BracketTypeList(&type_list, error)) { + return RustExpressionUP(); + } + } + + if (path.empty()) { + error.SetErrorString("identifier expected"); + return RustExpressionUP(); + } + + if (CurrentToken().kind == '{') { + RustPathUP name_path = llvm::make_unique(saw_self, relative, supers, + std::move(path), std::move(type_list), + true); + RustTypeExpressionUP type_path = + llvm::make_unique(std::move(name_path)); + return Struct(std::move(type_path), error); + } + + RustPathUP name_path = llvm::make_unique(saw_self, relative, supers, + std::move(path), std::move(type_list)); + return llvm::make_unique(std::move(name_path)); +} + +RustExpressionUP Parser::Sizeof(Status &error) { + assert(CurrentToken().kind == SIZEOF); + Advance(); + + if (CurrentToken().kind != '(') { + error.SetErrorString("'(' expected"); + return RustExpressionUP(); + } + Advance(); + + RustExpressionUP expr = Expr(error); + if (!expr) { + return expr; + } + + if (CurrentToken().kind != ')') { + error.SetErrorString("')' expected"); + return RustExpressionUP(); + } + Advance(); + + return llvm::make_unique>(std::move(expr)); +} + +bool Parser::StartsTerm() { + // Must be kept in parallel with the switch in Term. + switch (CurrentToken().kind) { + case INTEGER: + case FLOAT: + case STRING: + case BYTESTRING: + case CHAR: + case BYTE: + case TRUE: + case FALSE: + case '[': + case '(': + case SUPER: + case SELF: + case IDENTIFIER: + case COLONCOLON: + case SIZEOF: + case '*': + case '+': + case '-': + case '!': + case '&': + return true; + + default: + return false; + } +} + +RustExpressionUP Parser::Term(Status &error) { + RustExpressionUP term; + + // Double-check StartsTerm. + bool starts = StartsTerm(); + + switch (CurrentToken().kind) { + case INTEGER: { + const char *suffix = CurrentToken().number_suffix; + if (!suffix) { + suffix = "i32"; + } + RustTypeExpressionUP type = llvm::make_unique(suffix); + term = llvm::make_unique(CurrentToken().uinteger.getValue(), std::move(type)); + Advance(); + break; + } + + case FLOAT: { + const char *suffix = CurrentToken().number_suffix; + if (!suffix) { + suffix = "f64"; + } + RustTypeExpressionUP type = llvm::make_unique(suffix); + term = llvm::make_unique(CurrentToken().dvalue.getValue(), std::move(type)); + Advance(); + break; + } + + case STRING: + case BYTESTRING: + term = llvm::make_unique(std::move(CurrentToken().str), + CurrentToken().kind == BYTESTRING); + Advance(); + break; + + case CHAR: + case BYTE: + term = llvm::make_unique(CurrentToken().uinteger.getValue(), + CurrentToken().kind == BYTE); + Advance(); + break; + + case TRUE: + case FALSE: + term = llvm::make_unique(CurrentToken().kind == TRUE); + Advance(); + break; + + case '[': + term = Array(error); + break; + + case '(': + term = Parens(error); + break; + + case SUPER: + case SELF: + case IDENTIFIER: + case COLONCOLON: + term = Path(error); + break; + + case SIZEOF: + assert(starts); + return Sizeof(error); + + case '*': + term = Unary<'*', UnaryDereference>(error); + break; + case '+': + term = Unary<'+', UnaryPlus>(error); + break; + case '-': + term = Unary<'-', UnaryNegate>(error); + break; + case '!': + term = Unary<'!', UnaryComplement>(error); + break; + + case '&': + // FIXME should handle slices here. + term = Unary<'&', UnaryAddr>(error); + break; + + case INVALID: + assert(!starts); + error.SetErrorString(CurrentToken().str); + return RustExpressionUP(); + + case THATSALLFOLKS: + assert(!starts); + error.SetErrorString("unexpected EOF"); + return RustExpressionUP(); + + default: + assert(!starts); + error.SetErrorString("unexpected token"); + return RustExpressionUP(); + } + + assert(starts); + + bool done = false; + while (!done) { + switch (CurrentToken().kind) { + case AS: { + Advance(); + RustTypeExpressionUP type = Type(error); + if (!type) { + return RustExpressionUP(); + } + term = llvm::make_unique(std::move(type), std::move(term)); + break; + } + + case '.': + term = Field(std::move(term), error); + break; + + case '[': + term = Index(std::move(term), error); + break; + + case '(': + term = Call(std::move(term), error); + break; + + default: + done = true; + break; + } + + if (!term) { + return term; + } + } + + return term; +} + +#define BINOP(Tag, What) \ + RustBinaryExpression< Tag, BinaryOperation< What, false > > +#define COMP(Tag, What) \ + RustBinaryExpression< Tag, Comparison< What > > +#define ASSIGN(Tag, What) \ + RustAssignExpression< Tag, BinaryOperation< What, true > > + +// Couldn't find these in . + +template +class left_shift { +public: + T operator()(const T &l, const T&r) { + return l << r; + } +}; + +template +class right_shift { +public: + T operator()(const T &l, const T&r) { + return l >> r; + } +}; + + +// Binary operators. Each line has the form: +// DEFINE(token, precedence, type) +#define BINARY_OPERATORS \ + DEFINE(OROR, 3, RustOrOrExpression) \ + DEFINE(ANDAND, 4, RustAndAndExpression) \ + DEFINE(EQEQ, 5, COMP(EQEQ, std::equal_to)) \ + DEFINE(NOTEQ, 5, COMP(NOTEQ, std::not_equal_to)) \ + DEFINE(LTEQ, 5, COMP(LTEQ, std::less_equal)) \ + DEFINE(GTEQ, 5, COMP(GTEQ, std::greater_equal)) \ + DEFINE(LSH, 9, BINOP(LSH, left_shift)) \ + DEFINE(RSH, 9, BINOP(RSH, right_shift)) \ + DEFINE(PLUS_EQ, 1, ASSIGN(PLUS_EQ, std::plus)) \ + DEFINE(MINUS_EQ, 1, ASSIGN(MINUS_EQ, std::minus)) \ + DEFINE(SLASH_EQ, 1, ASSIGN(SLASH_EQ, std::divides)) \ + DEFINE(STAR_EQ, 1, ASSIGN(STAR_EQ, std::multiplies)) \ + DEFINE(PERCENT_EQ, 1, ASSIGN(PERCENT_EQ, std::modulus)) \ + DEFINE(RSH_EQ, 1, ASSIGN(RSH_EQ, right_shift)) \ + DEFINE(LSH_EQ, 1, ASSIGN(LSH_EQ, left_shift)) \ + DEFINE(AND_EQ, 1, ASSIGN(AND_EQ, std::bit_and)) \ + DEFINE(OR_EQ, 1, ASSIGN(OR_EQ, std::bit_or)) \ + DEFINE(XOR_EQ, 1, ASSIGN(XOR_EQ, std::bit_xor)) \ + DEFINE('|', 6, BINOP('|', std::bit_or)) \ + DEFINE('&', 8, BINOP('&', std::bit_and)) \ + DEFINE('=', 1, RustAssignment) \ + DEFINE('<', 5, COMP('<', std::less)) \ + DEFINE('>', 5, COMP('>', std::greater)) \ + DEFINE('+', 10, BINOP('+', std::plus)) \ + DEFINE('-', 10, BINOP('-', std::minus)) \ + DEFINE('*', 11, BINOP('*', std::multiplies)) \ + DEFINE('/', 11, BINOP('/', std::divides)) \ + DEFINE('%', 11, BINOP('%', std::modulus)) + +RustExpressionUP Parser::Binary(Status &error) { + struct Operation { + int precedence; + int op; + RustExpressionUP term; + + Operation(int precedence_, int op_, RustExpressionUP &&term_) + : precedence(precedence_), + op(op_), + term(std::move(term_)) + { + } + }; + + RustExpressionUP term = Term(error); + if (!term) { + return term; + } + + std::vector operations; + operations.emplace_back(0, -1, std::move(term)); + + bool done = false; + while (!done) { + int precedence; + int kind = CurrentToken().kind; + + switch (kind) { +#define DEFINE(Token, Prec, Type) \ + case Token: precedence = Prec; Advance(); break; + + BINARY_OPERATORS +#undef DEFINE + + case INVALID: + error.SetErrorString(CurrentToken().str); + return RustExpressionUP(); + + case THATSALLFOLKS: + default: + // Not a binary operator, so we're going to return. + done = true; + // Arrange to pop all the operations now. + precedence = 0; + break; + } + + RustExpressionUP rhs; + if (!done) { + rhs = Term(error); + if (!rhs) { + return rhs; + } + } + + while (precedence < operations.back().precedence) { + Operation top = std::move(operations.back()); + operations.pop_back(); + + assert(!operations.empty()); + Operation &lhs = operations.back(); + + switch (top.op) { +#define DEFINE(Token, Prec, Type) \ + case Token: \ + lhs.term = llvm::make_unique(std::move(lhs.term), std::move(top.term)); \ + break; + + BINARY_OPERATORS +#undef DEFINE + + default: + assert(0); + } + } + + // This won't be true in the "done" case. + if (rhs) { + operations.emplace_back(precedence, kind, std::move(rhs)); + } + } + + assert(operations.size() == 1); + return std::move(operations.back().term); +} + +RustExpressionUP Parser::Range(Status &error) { + RustExpressionUP lhs; + if (CurrentToken().kind != DOTDOT && CurrentToken().kind != DOTDOTEQ) { + lhs = Binary(error); + if (!lhs) { + return lhs; + } + } + + if (CurrentToken().kind != DOTDOT && CurrentToken().kind != DOTDOTEQ) { + return lhs; + } + bool is_inclusive = CurrentToken().kind == DOTDOTEQ; + Advance(); + + // An inclusive range requires an expression, but an exclusive range + // does not. + RustExpressionUP rhs; + if (is_inclusive || StartsTerm()) { + rhs = Binary(error); + if (!rhs) { + return rhs; + } + } + + return llvm::make_unique(std::move(lhs), std::move(rhs), is_inclusive); +} + +//////////////////////////////////////////////////////////////// +// Type parsing + +RustTypeExpressionUP Parser::ArrayType(Status &error) { + assert(CurrentToken().kind == '['); + Advance(); + + RustTypeExpressionUP element = Type(error); + if (!element) { + return element; + } + + if (CurrentToken().kind != ';') { + error.SetErrorString("';' expected"); + return RustTypeExpressionUP(); + } + Advance(); + + if (CurrentToken().kind != INTEGER) { + error.SetErrorString("integer expected"); + return RustTypeExpressionUP(); + } + + uint64_t len = CurrentToken().uinteger.getValue(); + Advance(); + + if (CurrentToken().kind != ']') { + error.SetErrorString("']' expected"); + return RustTypeExpressionUP(); + } + Advance(); + + return llvm::make_unique(std::move(element), len); +} + +RustTypeExpressionUP Parser::ReferenceType(Status &error) { + assert(CurrentToken().kind == '&'); + Advance(); + + bool is_mut = false; + if (CurrentToken().kind == MUT) { + is_mut = true; + Advance(); + } + + bool is_slice = false; + if (CurrentToken().kind == '[') { + is_slice = true; + Advance(); + } + + RustTypeExpressionUP target = Type(error); + if (!target) { + return target; + } + + if (is_slice) { + if (CurrentToken().kind != ']') { + error.SetErrorString("']' expected"); + return RustTypeExpressionUP(); + } + Advance(); + + return llvm::make_unique(std::move(target), is_mut); + } + + return llvm::make_unique(std::move(target), true, is_mut); +} + +RustTypeExpressionUP Parser::PointerType(Status &error) { + assert(CurrentToken().kind == '*'); + Advance(); + + bool is_mut = false; + if (CurrentToken().kind == MUT) { + is_mut = true; + } else if (CurrentToken().kind != CONST) { + error.SetErrorString("expected 'mut' or 'const'"); + return RustTypeExpressionUP(); + } + Advance(); + + RustTypeExpressionUP target = Type(error); + if (!target) { + return target; + } + + return llvm::make_unique(std::move(target), false, is_mut); +} + +bool Parser::TypeList(std::vector *type_list, Status &error) { + while (true) { + RustTypeExpressionUP t = Type(error); + if (!t) { + return false; + } + type_list->push_back(std::move(t)); + if (CurrentToken().kind != ',') { + break; + } + Advance(); + } + + return true; +} + +bool Parser::ParenTypeList(std::vector *type_list, Status &error) { + if (CurrentToken().kind != '(') { + error.SetErrorStringWithFormat("'(' expected"); + return false; + } + Advance(); + + if (CurrentToken().kind != ')') { + if (!TypeList(type_list, error)) { + return false; + } + } + + if (CurrentToken().kind != ')') { + error.SetErrorStringWithFormat("')' expected"); + return false; + } + Advance(); + + return true; +} + +bool Parser::BracketTypeList(std::vector *type_list, Status &error) { + if (CurrentToken().kind != '<') { + error.SetErrorStringWithFormat("'<' expected"); + return false; + } + Advance(); + + if (CurrentToken().kind != '>' && CurrentToken().kind != RSH) { + if (!TypeList(type_list, error)) { + return false; + } + } + + if (CurrentToken().kind == RSH) { + ReplaceTokenKind('>'); + } else if (CurrentToken().kind != '>') { + error.SetErrorStringWithFormat("'>' expected"); + return false; + } else { + Advance(); + } + + return true; +} + +RustTypeExpressionUP Parser::FunctionType(Status &error) { + assert(CurrentToken().kind == FN); + Advance(); + + std::vector type_list; + if (!ParenTypeList(&type_list, error)) { + return RustTypeExpressionUP(); + } + + if (CurrentToken().kind != ARROW) { + error.SetErrorString("'->' expected"); + return RustTypeExpressionUP(); + } + Advance(); + + RustTypeExpressionUP return_type = Type(error); + if (!return_type) { + return return_type; + } + + return llvm::make_unique(std::move(return_type), + std::move(type_list)); +} + +RustTypeExpressionUP Parser::TupleType(Status &error) { + assert(CurrentToken().kind == '('); + // Don't advance here, ParenTypeList is going to deal with the open + // paren. + + std::vector type_list; + if (!ParenTypeList(&type_list, error)) { + return RustTypeExpressionUP(); + } + + return llvm::make_unique(std::move(type_list)); +} + +RustTypeExpressionUP Parser::TypePath(Status &error) { + bool relative = true; + int supers = 0; + + bool saw_self = CurrentToken().kind == SELF; + if (saw_self) { + Advance(); + if (CurrentToken().kind != COLONCOLON) { + error.SetErrorString("'::' expected"); + return RustTypeExpressionUP(); + } + } + + if (CurrentToken().kind == COLONCOLON) { + if (!saw_self) { + relative = false; + } + Advance(); + } + + if (relative) { + while (CurrentToken().kind == SUPER) { + ++supers; + Advance(); + if (CurrentToken().kind != COLONCOLON) { + error.SetErrorString("'::' expected after 'super'"); + return RustTypeExpressionUP(); + } + Advance(); + } + } + + std::vector path; + while (CurrentToken().kind == IDENTIFIER) { + path.emplace_back(std::move(CurrentToken().str)); + Advance(); + if (CurrentToken().kind != COLONCOLON) { + break; + } + Advance(); + if (CurrentToken().kind != IDENTIFIER && CurrentToken().kind != '<') { + error.SetErrorString("identifier or '<' expected"); + return RustTypeExpressionUP(); + } + } + + std::vector type_list; + if (CurrentToken().kind == '<') { + if (!BracketTypeList(&type_list, error)) { + return RustTypeExpressionUP(); + } + } + + if (path.empty()) { + error.SetErrorString("identifier expected"); + return RustTypeExpressionUP(); + } + + RustPathUP name_path = llvm::make_unique(saw_self, relative, supers, std::move(path), + std::move(type_list)); + return llvm::make_unique(std::move(name_path)); +} + +RustTypeExpressionUP Parser::Type(Status &error) { + switch (CurrentToken().kind) { + case '[': + return ArrayType(error); + + case '&': + return ReferenceType(error); + + case '*': + return PointerType(error); + + case FN: + return FunctionType(error); + + case '(': + return TupleType(error); + + case SUPER: + case SELF: + case IDENTIFIER: + case COLONCOLON: + return TypePath(error); + + default: + error.SetErrorString("expected type"); + return RustTypeExpressionUP(); + } +} diff --git a/lldb/source/Plugins/ExpressionParser/Rust/RustParse.h b/lldb/source/Plugins/ExpressionParser/Rust/RustParse.h new file mode 100644 index 00000000000000..e9b6eb488e2293 --- /dev/null +++ b/lldb/source/Plugins/ExpressionParser/Rust/RustParse.h @@ -0,0 +1,90 @@ +//===-- RustParse.h -------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RustParse_h +#define liblldb_RustParse_h + +#include "lldb/lldb-forward.h" +#include "lldb/lldb-private.h" +#include "RustLex.h" +#include "RustAST.h" + +namespace lldb_private { + +namespace rust { + +class Parser { +public: + + Parser(lldb::TargetSP target, llvm::StringRef ref) + : m_target(target), + m_lexer(ref), + // fixme this seems not good + m_current(m_lexer.Next()) + { + } + + RustExpressionUP ParseFully(Status &error) { + RustExpressionUP result = Expr(error); + if (CurrentToken().kind != THATSALLFOLKS) { + error.SetErrorString("extra tokens at EOF"); + return RustExpressionUP(); + } + return result; + } + +private: + + bool StartsTerm(); + template RustExpressionUP Unary(Status &error); + bool ExprList(std::vector *exprs, Status &error); + RustExpressionUP Parens(Status &error); + RustExpressionUP Path(Status &error); + RustExpressionUP Array(Status &error); + RustExpressionUP Field(RustExpressionUP &&lhs, Status &error); + RustExpressionUP Call(RustExpressionUP &&func, Status &error); + RustExpressionUP Index(RustExpressionUP &&array, Status &error); + RustExpressionUP Term(Status &error); + RustExpressionUP Binary(Status &error); + RustExpressionUP Sizeof(Status &error); + RustExpressionUP Struct(RustTypeExpressionUP &&path, Status &error); + RustExpressionUP Range(Status &error); + + RustExpressionUP Expr(Status &error) { + return Range(error); + } + + + RustTypeExpressionUP Type(Status &error); + RustTypeExpressionUP ArrayType(Status &error); + RustTypeExpressionUP ReferenceType(Status &error); + RustTypeExpressionUP PointerType(Status &error); + bool TypeList(std::vector *type_list, Status &error); + bool ParenTypeList(std::vector *type_list, Status &error); + bool BracketTypeList(std::vector *type_list, Status &error); + RustTypeExpressionUP FunctionType(Status &error); + RustTypeExpressionUP TupleType(Status &error); + RustTypeExpressionUP TypePath(Status &error); + + Token &CurrentToken() { return m_current; } + void Advance() { m_current = m_lexer.Next(); } + + // There's one case where we need to push-back, but we don't need + // full generality so there's just this little hack. + void ReplaceTokenKind(int k) { m_current.kind = k; } + + lldb::TargetSP m_target; + Lexer m_lexer; + Token m_current; +}; + +} // namespace rust +} // namespace lldb_private + +#endif // liblldb_RustParse_h diff --git a/lldb/source/Plugins/ExpressionParser/Rust/RustUserExpression.cpp b/lldb/source/Plugins/ExpressionParser/Rust/RustUserExpression.cpp new file mode 100644 index 00000000000000..cdea659bd5fb05 --- /dev/null +++ b/lldb/source/Plugins/ExpressionParser/Rust/RustUserExpression.cpp @@ -0,0 +1,68 @@ +//===-- RustUserExpression.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "RustUserExpression.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Expression/DiagnosticManager.h" +#include "lldb/Expression/ExpressionVariable.h" +#include "lldb/Symbol/RustASTContext.h" +#include "lldb/Target/Target.h" +#include "Plugins/ExpressionParser/Rust/RustParse.h" + +using namespace lldb_private::rust; +using namespace lldb_private; +using namespace lldb; + +bool RustUserExpression::Parse(DiagnosticManager &diagnostic_manager, + ExecutionContext &exe_ctx, + lldb_private::ExecutionPolicy execution_policy, + bool keep_result_in_memory, bool generate_debug_info) +{ + InstallContext(exe_ctx); + + Parser parser(exe_ctx.GetTargetSP(), m_expr_text); + Status status; + m_expr = parser.ParseFully(status); + if (!m_expr) { + diagnostic_manager.PutString(eDiagnosticSeverityError, status.AsCString()); + return false; + } + + return true; +} + +lldb::ExpressionResults RustUserExpression::DoExecute(DiagnosticManager &diagnostic_manager, + ExecutionContext &exe_ctx, + const EvaluateExpressionOptions &options, + lldb::UserExpressionSP &shared_ptr_to_me, + lldb::ExpressionVariableSP &result) +{ + Status error; + ValueObjectSP value = m_expr->Evaluate(exe_ctx, error); + m_expr.reset(); + + if (!value) { + diagnostic_manager.PutString(eDiagnosticSeverityError, error.AsCString()); + return lldb::eExpressionDiscarded; + } + + result.reset(new ExpressionVariable(ExpressionVariable::eKindRust)); + result->m_live_sp = result->m_frozen_sp = value; + result->m_flags |= ExpressionVariable::EVIsProgramReference; + Target *target = exe_ctx.GetTargetPtr(); + PersistentExpressionState *pv = + target->GetPersistentExpressionStateForLanguage(eLanguageTypeRust); + if (pv != nullptr) { + result->SetName(pv->GetNextPersistentVariableName(*target, + pv->GetPersistentVariablePrefix())); + pv->AddVariable(result); + } + + return lldb::eExpressionCompleted; +} diff --git a/lldb/source/Plugins/ExpressionParser/Rust/RustUserExpression.h b/lldb/source/Plugins/ExpressionParser/Rust/RustUserExpression.h new file mode 100644 index 00000000000000..13cc8ad4fe5859 --- /dev/null +++ b/lldb/source/Plugins/ExpressionParser/Rust/RustUserExpression.h @@ -0,0 +1,63 @@ +//===-- RustUserExpression.h -------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RustUserExpression_h +#define liblldb_RustUserExpression_h + +#include +#include "lldb/Expression/UserExpression.h" +#include "Plugins/ExpressionParser/Rust/RustAST.h" + +namespace lldb_private { + +class RustUserExpression : public UserExpression { +public: + + RustUserExpression(ExecutionContextScope &exe_scope, llvm::StringRef expr, + llvm::StringRef prefix, lldb::LanguageType language, + ResultType desired_type, + const EvaluateExpressionOptions &options) + : UserExpression(exe_scope, expr, prefix, language, desired_type, options) + { + } + + bool Parse(DiagnosticManager &diagnostic_manager, + ExecutionContext &exe_ctx, + lldb_private::ExecutionPolicy execution_policy, + bool keep_result_in_memory, bool generate_debug_info) override; + + bool CanInterpret() override { + return true; + } + + // FIXME - what is this supposed to do. + bool FinalizeJITExecution(DiagnosticManager &diagnostic_manager, + ExecutionContext &exe_ctx, + lldb::ExpressionVariableSP &result, + lldb::addr_t function_stack_bottom = LLDB_INVALID_ADDRESS, + lldb::addr_t function_stack_top = LLDB_INVALID_ADDRESS) override { + return true; + } + +protected: + + lldb::ExpressionResults DoExecute(DiagnosticManager &diagnostic_manager, + ExecutionContext &exe_ctx, + const EvaluateExpressionOptions &options, + lldb::UserExpressionSP &shared_ptr_to_me, + lldb::ExpressionVariableSP &result) override; + +private: + + RustExpressionUP m_expr; +}; + +} // namespace lldb_private + +#endif // liblldb_RustUserExpression_h diff --git a/lldb/source/Plugins/Language/CMakeLists.txt b/lldb/source/Plugins/Language/CMakeLists.txt index 7869074566d1e7..c58d89618f6ed7 100644 --- a/lldb/source/Plugins/Language/CMakeLists.txt +++ b/lldb/source/Plugins/Language/CMakeLists.txt @@ -2,3 +2,4 @@ add_subdirectory(ClangCommon) add_subdirectory(CPlusPlus) add_subdirectory(ObjC) add_subdirectory(ObjCPlusPlus) +add_subdirectory(Rust) diff --git a/lldb/source/Plugins/Language/Rust/CMakeLists.txt b/lldb/source/Plugins/Language/Rust/CMakeLists.txt new file mode 100644 index 00000000000000..4ad4166235e7b9 --- /dev/null +++ b/lldb/source/Plugins/Language/Rust/CMakeLists.txt @@ -0,0 +1,11 @@ +add_lldb_library(lldbPluginRustLanguage PLUGIN + RustLanguage.cpp + + LINK_LIBS + lldbCore + lldbDataFormatters + lldbSymbol + lldbTarget + LINK_COMPONENTS + Support +) diff --git a/lldb/source/Plugins/Language/Rust/RustLanguage.cpp b/lldb/source/Plugins/Language/Rust/RustLanguage.cpp new file mode 100644 index 00000000000000..b3d5b11bfdfe15 --- /dev/null +++ b/lldb/source/Plugins/Language/Rust/RustLanguage.cpp @@ -0,0 +1,60 @@ +//===-- RustLanguage.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +#include +// C++ Includes +#include +#include + +// Other libraries and framework includes +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Threading.h" + +// Project includes +#include "RustLanguage.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/DataFormatters/DataVisualization.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/Symbol/RustASTContext.h" +#include "lldb/Utility/ConstString.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +void RustLanguage::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), "Rust Language", + CreateInstance); +} + +void RustLanguage::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} + +lldb_private::ConstString RustLanguage::GetPluginNameStatic() { + static ConstString g_name("Rust"); + return g_name; +} + +lldb_private::ConstString RustLanguage::GetPluginName() { + return GetPluginNameStatic(); +} + +uint32_t RustLanguage::GetPluginVersion() { return 1; } + +Language *RustLanguage::CreateInstance(lldb::LanguageType language) { + if (language == eLanguageTypeRust) + return new RustLanguage(); + return nullptr; +} + +bool RustLanguage::IsSourceFile(llvm::StringRef file_path) const { + return file_path.endswith(".rs"); +} diff --git a/lldb/source/Plugins/Language/Rust/RustLanguage.h b/lldb/source/Plugins/Language/Rust/RustLanguage.h new file mode 100644 index 00000000000000..bd073d8f7f8adc --- /dev/null +++ b/lldb/source/Plugins/Language/Rust/RustLanguage.h @@ -0,0 +1,50 @@ +//===-- RustLanguage.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RustLanguage_h_ +#define liblldb_RustLanguage_h_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +#include "llvm/ADT/StringRef.h" + +// Project includes +#include "lldb/Target/Language.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/lldb-private.h" + +namespace lldb_private { + +class RustLanguage : public Language { +public: + lldb::LanguageType GetLanguageType() const override { + return lldb::eLanguageTypeRust; + } + + static void Initialize(); + + static void Terminate(); + + static lldb_private::Language *CreateInstance(lldb::LanguageType language); + + static lldb_private::ConstString GetPluginNameStatic(); + + bool IsSourceFile(llvm::StringRef file_path) const override; + + ConstString GetPluginName() override; + + uint32_t GetPluginVersion() override; +}; + +} // namespace lldb_private + +#endif // liblldb_RustLanguage_h_ diff --git a/lldb/source/Plugins/LanguageRuntime/CMakeLists.txt b/lldb/source/Plugins/LanguageRuntime/CMakeLists.txt index c62791445a9a2c..9b7036e71fbd7b 100644 --- a/lldb/source/Plugins/LanguageRuntime/CMakeLists.txt +++ b/lldb/source/Plugins/LanguageRuntime/CMakeLists.txt @@ -1,3 +1,4 @@ add_subdirectory(CPlusPlus) add_subdirectory(ObjC) add_subdirectory(RenderScript) +add_subdirectory(Rust) diff --git a/lldb/source/Plugins/LanguageRuntime/Rust/CMakeLists.txt b/lldb/source/Plugins/LanguageRuntime/Rust/CMakeLists.txt new file mode 100644 index 00000000000000..17d4c558ae81e8 --- /dev/null +++ b/lldb/source/Plugins/LanguageRuntime/Rust/CMakeLists.txt @@ -0,0 +1,10 @@ +add_lldb_library(lldbPluginLanguageRuntimeRust PLUGIN + RustLanguageRuntime.cpp + + LINK_LIBS + lldbCore + lldbSymbol + lldbTarget + LINK_COMPONENTS + Support + ) diff --git a/lldb/source/Plugins/LanguageRuntime/Rust/RustLanguageRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/Rust/RustLanguageRuntime.cpp new file mode 100644 index 00000000000000..7df8fd6e4e623e --- /dev/null +++ b/lldb/source/Plugins/LanguageRuntime/Rust/RustLanguageRuntime.cpp @@ -0,0 +1,116 @@ +//===-- RustLanguageRuntime.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "RustLanguageRuntime.h" + +#include "lldb/Core/PluginManager.h" +#include "lldb/Symbol/RustASTContext.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Symbol/TypeList.h" +#include "lldb/Target/SectionLoadList.h" +#include "lldb/Target/Target.h" +#include "llvm/ADT/StringRef.h" + +using namespace lldb; +using namespace lldb_private; + +RustLanguageRuntime::RustLanguageRuntime(Process *process) + : LanguageRuntime(process) +{ +} + +LanguageRuntime * +RustLanguageRuntime::CreateInstance(Process *process, + lldb::LanguageType language) { + if (language == eLanguageTypeRust) + return new RustLanguageRuntime(process); + return nullptr; +} + +void RustLanguageRuntime::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), "Rust language runtime", + CreateInstance); +} + +void RustLanguageRuntime::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} + +lldb_private::ConstString RustLanguageRuntime::GetPluginNameStatic() { + static ConstString g_name("rust"); + return g_name; +} + +lldb_private::ConstString RustLanguageRuntime::GetPluginName() { + return GetPluginNameStatic(); +} + +uint32_t RustLanguageRuntime::GetPluginVersion() { + return 1; +} + +bool RustLanguageRuntime::CouldHaveDynamicValue(ValueObject &in_value) { + return in_value.GetCompilerType().IsPossibleDynamicType(nullptr, false, false); +} + +bool RustLanguageRuntime::GetDynamicTypeAndAddress( + ValueObject &in_value, lldb::DynamicValueType use_dynamic, + TypeAndOrName &class_type_or_name, Address &dynamic_address, + Value::ValueType &value_type) { + class_type_or_name.Clear(); + value_type = Value::ValueType::eValueTypeScalar; + + CompilerType type = in_value.GetCompilerType(); + RustASTContext *ast = llvm::dyn_cast_or_null(type.GetTypeSystem()); + + if (!ast) { + return false; + } + + uint64_t discr_offset, discr_byte_size; + if (ast->GetEnumDiscriminantLocation(type, discr_offset, discr_byte_size)) { + lldb::addr_t original_ptr = in_value.GetAddressOf(false); // FIXME? + if (original_ptr == LLDB_INVALID_ADDRESS) { + return false; + } + + ExecutionContext exe_ctx(in_value.GetExecutionContextRef()); + Process *process = exe_ctx.GetProcessPtr(); + if (process == nullptr) { + return false; + } + + Status error; + uint64_t discriminant = + process->ReadUnsignedIntegerFromMemory(original_ptr + discr_offset, discr_byte_size, + 0, error); + if (!error.Success()) { + return false; + } + + CompilerType variant_type = ast->FindEnumVariant(type, discriminant); + class_type_or_name = TypeAndOrName(variant_type); + // The address doesn't change. + dynamic_address.SetLoadAddress(original_ptr, exe_ctx.GetTargetPtr()); + value_type = Value::ValueType::eValueTypeLoadAddress; + + return true; + } + + return false; +} + +TypeAndOrName +RustLanguageRuntime::FixUpDynamicType(const TypeAndOrName &type_and_or_name, + ValueObject &static_value) { + return type_and_or_name; +} diff --git a/lldb/source/Plugins/LanguageRuntime/Rust/RustLanguageRuntime.h b/lldb/source/Plugins/LanguageRuntime/Rust/RustLanguageRuntime.h new file mode 100644 index 00000000000000..c8ff5a19529959 --- /dev/null +++ b/lldb/source/Plugins/LanguageRuntime/Rust/RustLanguageRuntime.h @@ -0,0 +1,78 @@ +//===-- RustLanguageRuntime.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RustLanguageRuntime_h_ +#define liblldb_RustLanguageRuntime_h_ + +// C Includes +// C++ Includes +#include +// Other libraries and framework includes +// Project includes +#include "lldb/Core/PluginInterface.h" +#include "lldb/Target/LanguageRuntime.h" +#include "lldb/lldb-private.h" + +namespace lldb_private { + +class RustLanguageRuntime : public LanguageRuntime { +public: + static void Initialize(); + + static void Terminate(); + + static lldb_private::LanguageRuntime * + CreateInstance(Process *process, lldb::LanguageType language); + + static lldb_private::ConstString GetPluginNameStatic(); + + lldb_private::ConstString GetPluginName() override; + + uint32_t GetPluginVersion() override; + + lldb::LanguageType GetLanguageType() const override { + return lldb::eLanguageTypeRust; + } + + bool GetObjectDescription(Stream &str, ValueObject &object) override { + return false; + } + + bool GetObjectDescription(Stream &str, Value &value, + ExecutionContextScope *exe_scope) override { + return false; + } + + lldb::BreakpointResolverSP CreateExceptionResolver(Breakpoint *bkpt, + bool catch_bp, + bool throw_bp) override { + return nullptr; + } + + TypeAndOrName FixUpDynamicType(const TypeAndOrName &type_and_or_name, + ValueObject &static_value) override; + + bool CouldHaveDynamicValue(ValueObject &in_value) override; + + bool GetDynamicTypeAndAddress(ValueObject &in_value, + lldb::DynamicValueType use_dynamic, + TypeAndOrName &class_type_or_name, + Address &address, + Value::ValueType &value_type) override; + +protected: + RustLanguageRuntime(Process *process); + +private: + DISALLOW_COPY_AND_ASSIGN(RustLanguageRuntime); +}; + +} // namespace lldb_private + +#endif // liblldb_RustLanguageRuntime_h_ diff --git a/lldb/source/Plugins/SymbolFile/DWARF/CMakeLists.txt b/lldb/source/Plugins/SymbolFile/DWARF/CMakeLists.txt index 0e47ee34fe51b9..0a376e23fdaffa 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/CMakeLists.txt +++ b/lldb/source/Plugins/SymbolFile/DWARF/CMakeLists.txt @@ -4,6 +4,7 @@ add_lldb_library(lldbPluginSymbolFileDWARF PLUGIN DIERef.cpp DWARFAbbreviationDeclaration.cpp DWARFASTParserClang.cpp + DWARFASTParserRust.cpp DWARFAttribute.cpp DWARFBaseDIE.cpp DWARFCompileUnit.cpp diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserRust.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserRust.cpp new file mode 100644 index 00000000000000..2d56d5d8b3fbae --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserRust.cpp @@ -0,0 +1,1177 @@ +//===-- DWARFASTParserRust.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFASTParserRust.h" + +#include "DWARFASTParserRust.h" +#include "DWARFCompileUnit.h" +#include "DWARFDIE.h" +#include "DWARFDIECollection.h" +#include "DWARFDebugInfo.h" +#include "DWARFDeclContext.h" +#include "DWARFDefines.h" +#include "SymbolFileDWARF.h" +#include "SymbolFileDWARFDebugMap.h" +#include "UniqueDWARFASTType.h" + +#include "clang/Basic/Specifiers.h" + +#include "lldb/Core/Module.h" +#include "lldb/Core/Value.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/TypeList.h" + +using namespace lldb; +using namespace lldb_private; + +#define INVALID_ATTR dw_attr_t(-1) + +// A way to iterate over DIE attrs. +class IterableDIEAttrs +{ +public: + IterableDIEAttrs(const DWARFDIE &die) + { + m_size = die.GetAttributes(m_attrs); + } + + IterableDIEAttrs(const IterableDIEAttrs &) = delete; + IterableDIEAttrs &operator=(const IterableDIEAttrs &) = delete; + + class iterator + { + public: + + iterator(const IterableDIEAttrs *die, size_t offset) + : m_die(die) + , m_offset(offset) + { + } + + iterator(const iterator &other) + : m_die(other.m_die) + , m_offset(other.m_offset) + { + } + + iterator &operator=(const iterator &other) + { + m_die = other.m_die; + m_offset = other.m_offset; + return *this; + } + + iterator &operator++() + { + ++m_offset; + return *this; + } + + bool operator!=(const iterator &other) const + { + return m_die != other.m_die || m_offset != other.m_offset; + } + + std::pair operator*() const + { + dw_attr_t attr = m_die->m_attrs.AttributeAtIndex(m_offset); + DWARFFormValue value; + if (!m_die->m_attrs.ExtractFormValueAtIndex(m_offset, value)) + attr = INVALID_ATTR; + return std::make_pair(attr, value); + } + + private: + + const IterableDIEAttrs *m_die; + size_t m_offset; + }; + + iterator begin() const + { + return iterator(this, 0); + } + + iterator end() const + { + return iterator(this, m_size); + } + +private: + + size_t m_size; + DWARFAttributes m_attrs; +}; + +// A way to iterate over a DIE's direct children. +class IterableDIEChildren +{ +public: + IterableDIEChildren(const DWARFDIE &die) + : m_die(die) + { + } + + IterableDIEChildren(const IterableDIEChildren &) = delete; + IterableDIEChildren &operator=(const IterableDIEChildren &) = delete; + + class iterator + { + public: + + iterator(const DWARFDIE &die) + : m_die(die) + { + } + + ~iterator() + { + } + + iterator(const iterator &other) + : m_die(other.m_die) + { + } + + iterator &operator=(const iterator &other) + { + m_die = other.m_die; + return *this; + } + + iterator &operator++() + { + m_die = m_die.GetSibling(); + return *this; + } + + bool operator!=(const iterator &other) const + { + return m_die != other.m_die; + } + + DWARFDIE operator*() const + { + return m_die; + } + + private: + + DWARFDIE m_die; + }; + + iterator begin() const + { + return iterator(m_die.GetFirstChild()); + } + + iterator end() const + { + return iterator(DWARFDIE()); + } + +private: + + DWARFDIE m_die; +}; + +ConstString DWARFASTParserRust::FullyQualify(const ConstString &name, const DWARFDIE &die) { + SymbolFileDWARF *dwarf = die.GetDWARF(); + lldb::user_id_t id = die.GetID(); + CompilerDeclContext ctx = dwarf->GetDeclContextContainingUID(id); + ConstString ctx_name = ctx.GetScopeQualifiedName(); + if (!ctx_name) { + return name; + } + std::string qual_name = std::string(ctx_name.AsCString()) + "::" + name.AsCString(); + return ConstString(qual_name.c_str()); +} + +TypeSP DWARFASTParserRust::ParseSimpleType(lldb_private::Log *log, const DWARFDIE &die) { + lldb::user_id_t encoding_uid = LLDB_INVALID_UID; + const char *type_name_cstr = NULL; + ConstString type_name_const_str; + uint64_t byte_size = 0; + uint64_t encoding = 0; + + for (auto &&value : IterableDIEAttrs(die)) { + switch (value.first) { + case DW_AT_name: + type_name_cstr = value.second.AsCString(); + if (type_name_cstr) + type_name_const_str.SetCString(type_name_cstr); + break; + case DW_AT_byte_size: + byte_size = value.second.Unsigned(); + break; + case DW_AT_encoding: + encoding = value.second.Unsigned(); + break; + case DW_AT_type: + encoding_uid = value.second.Reference(); + break; + } + } + + SymbolFileDWARF *dwarf = die.GetDWARF(); + Type::ResolveState resolve_state = Type::eResolveStateUnresolved; + CompilerType compiler_type; + Type::EncodingDataType encoding_data_type = Type::eEncodingIsUID; + switch (die.Tag()) { + case DW_TAG_unspecified_type: + resolve_state = Type::eResolveStateFull; + compiler_type = m_ast.CreateVoidType(); + break; + + case DW_TAG_base_type: + resolve_state = Type::eResolveStateFull; + if (encoding == DW_ATE_boolean) + compiler_type = m_ast.CreateBoolType(type_name_const_str); + else if (encoding == DW_ATE_float) + compiler_type = m_ast.CreateFloatType(type_name_const_str, byte_size); + else if (encoding == DW_ATE_signed || encoding == DW_ATE_unsigned || + // DW_ATE_UCS seems to be less used (perhaps + // Fortran-specific?) and since I'm not planning to have + // rustc emit it, we ignore it here. + encoding == DW_ATE_unsigned_char || encoding == DW_ATE_UTF) + compiler_type = m_ast.CreateIntegralType(type_name_const_str, + encoding == DW_ATE_signed, + byte_size, + (encoding == DW_ATE_unsigned_char || + encoding == DW_ATE_UTF)); + else + dwarf->GetObjectFile()->GetModule()->LogMessage( + log, "DWARFASTParserRust::ParseSimpleType (die = 0x%8.8x) %s " + "unrecognized encoding '%d')", + die.GetOffset(), DW_TAG_value_to_name(die.Tag()), int(encoding)); + break; + + // Note that, currently, rustc does not emit DW_TAG_reference_type + // - references are distinguished by name; and also we don't want + // to treat Rust references as CompilerType references. + case DW_TAG_typedef: + type_name_const_str = FullyQualify(type_name_const_str, die); + // Fall through. + case DW_TAG_pointer_type: + case DW_TAG_template_type_parameter: { + Type *type = dwarf->ResolveTypeUID(encoding_uid); + if (type) { + CompilerType impl = type->GetForwardCompilerType(); + if (die.Tag() == DW_TAG_pointer_type) { + int byte_size = die.GetCU()->GetAddressByteSize(); + m_ast.SetAddressByteSize(byte_size); + compiler_type = m_ast.CreatePointerType(type_name_const_str, impl, byte_size); + encoding_data_type = Type::eEncodingIsPointerUID; + } else { + compiler_type = m_ast.CreateTypedefType(type_name_const_str, impl); + encoding_data_type = Type::eEncodingIsTypedefUID; + } + } + break; + } + + default: + // Should have been filtered by the caller. + assert(0); + } + + return TypeSP(new Type(die.GetID(), dwarf, type_name_const_str, + byte_size, NULL, encoding_uid, + encoding_data_type, Declaration(), compiler_type, + resolve_state)); +} + +TypeSP DWARFASTParserRust::ParseArrayType(const DWARFDIE &die) { + lldb::user_id_t type_die_offset = DW_INVALID_OFFSET; + + for (auto &&value : IterableDIEAttrs(die)) { + switch (value.first) { + case DW_AT_type: + type_die_offset = value.second.Reference(); + break; + } + } + + SymbolFileDWARF *dwarf = die.GetDWARF(); + Type *element_type = dwarf->ResolveTypeUID(type_die_offset); + if (!element_type) + return TypeSP(nullptr); + + CompilerType compiler_type; + uint64_t count = 0; + + for (auto &&child_die : IterableDIEChildren(die)) { + if (child_die.Tag() == DW_TAG_subrange_type) { + for (auto &&value : IterableDIEAttrs(child_die)) { + if (value.first == DW_AT_count) { + count = value.second.Unsigned(); + break; + } + } + break; + } + } + + CompilerType array_element_type = element_type->GetForwardCompilerType(); + compiler_type = m_ast.CreateArrayType(array_element_type, count); + + ConstString type_name_const_str = compiler_type.GetTypeName(); + TypeSP type_sp(new Type(die.GetID(), dwarf, type_name_const_str, + element_type->GetByteSize(), NULL, type_die_offset, + Type::eEncodingIsUID, Declaration(), compiler_type, + Type::eResolveStateFull)); + type_sp->SetEncodingType(element_type); + return type_sp; +} + +TypeSP DWARFASTParserRust::ParseFunctionType(const DWARFDIE &die) { + clang::StorageClass storage = clang::SC_None; //, Extern, Static, PrivateExtern + const char *type_name_cstr = NULL; + ConstString type_name_const_str; + Declaration decl; + + CompilerType return_type; + for (auto &&attr : IterableDIEAttrs(die)) { + switch (attr.first) { + case DW_AT_name: + type_name_cstr = attr.second.AsCString(); + type_name_const_str.SetCString(type_name_cstr); + break; + + case DW_AT_external: + if (attr.second.Unsigned()) { + if (storage == clang::SC_None) + storage = clang::SC_Extern; + else + storage = clang::SC_PrivateExtern; + } + break; + + case DW_AT_type: { + Type *type = die.ResolveTypeUID(DIERef(attr.second)); + if (type) { + return_type = type->GetForwardCompilerType(); + } + break; + } + } + } + + if (!return_type) { + return_type = m_ast.CreateVoidType(); + } + + std::vector function_param_types; + for (auto &&child_die : IterableDIEChildren(die)) { + if (child_die.Tag() == DW_TAG_formal_parameter) { + for (auto &&attr : IterableDIEAttrs(child_die)) { + if (attr.first == DW_AT_type) { + Type *type = die.ResolveTypeUID(DIERef(attr.second)); + if (type) { + function_param_types.push_back(type->GetForwardCompilerType()); + } + break; + } + } + } + } + + CompilerType compiler_type = m_ast.CreateFunctionType(type_name_const_str, return_type, + std::move(function_param_types)); + + SymbolFileDWARF *dwarf = die.GetDWARF(); + TypeSP type_sp(new Type(die.GetID(), dwarf, type_name_const_str, 0, NULL, + LLDB_INVALID_UID, Type::eEncodingIsUID, &decl, + compiler_type, Type::eResolveStateFull)); + + return type_sp; +} + +static bool starts_with(const char *str, const char *prefix) { + return strncmp(str, prefix, strlen(prefix)) == 0; +} + +#define RUST_ENCODED_PREFIX "RUST$ENCODED$ENUM$" + +std::vector DWARFASTParserRust::ParseDiscriminantPath(const char **in_str) { + std::vector result; + const char *str = *in_str; + + assert(starts_with(str, RUST_ENCODED_PREFIX)); + str += strlen(RUST_ENCODED_PREFIX); + + // We're going to push a synthetic unit struct field as the enum + // type's first member, so the resulting discriminant path always + // starts with 1. + result.push_back(1); + + while (*str >= '0' && *str <= '9') { + char *next; + unsigned long value = strtoul(str, &next, 10); + result.push_back(value); + str = next; + if (*str != '$') { + // Report an error? + *in_str = nullptr; + result.clear(); + return result; + } + ++str; + } + + // At this point, STR points to the name of the elided member type. + *in_str = str; + return result; +} + +void DWARFASTParserRust::FindDiscriminantLocation(CompilerType type, + std::vector &&path, + uint64_t &offset, + uint64_t &byte_size) { + offset = 0; + + for (size_t index : path) { + std::string ignore_name; + uint32_t bitsize_ignore, bitoffset_ignore; + bool isbase_ignore, isderef_ignore; + uint64_t lang_flags_ignore; + + uint32_t this_size; + int32_t this_offset; + + type = m_ast.GetChildCompilerTypeAtIndex(type.GetOpaqueQualType(), nullptr, index, + false, false, true, + ignore_name, + this_size, this_offset, + bitsize_ignore, bitoffset_ignore, + isbase_ignore, isderef_ignore, + nullptr, lang_flags_ignore); + offset += this_offset; + // The last time this is done, it will hold the size of the final + // field, which is what we want. + byte_size = this_size; + } +} + +bool DWARFASTParserRust::IsPossibleEnumVariant(const DWARFDIE &die) { + if (die.Tag() != DW_TAG_structure_type) { + // Only structures can be enum variants. + return false; + } + + for (auto &&child_die : IterableDIEChildren(die)) { + if (child_die.Tag() == DW_TAG_member) { + for (auto &&attr : IterableDIEAttrs(child_die)) { + if (attr.first == DW_AT_name) { + return strcmp(attr.second.AsCString(), "RUST$ENUM$DISR") == 0; + } + } + // No name, so whatever it is, it isn't an enum variant. + return false; + } + } + + // We didn't see a member, and an empty structure might well be an + // enum variant. + return true; +} + +std::vector +DWARFASTParserRust::ParseFields(const DWARFDIE &die, std::vector &discriminant_path, + bool &is_tuple, + uint64_t &discr_offset, uint64_t &discr_byte_size, + bool &saw_discr) { + SymbolFileDWARF *dwarf = die.GetDWARF(); + + // We construct a list of fields and then apply them later so that + // we can analyze the fields to see what sort of structure this + // really is. + std::vector fields; + unsigned field_index = 0; + bool numeric_names = true; + + // We might have recursed in here with a variant part. If so, we + // want to handle the discriminant and variants specially. + bool is_variant = die.Tag() == DW_TAG_variant_part; + DWARFDIE discriminant_die; + if (is_variant) { + discriminant_die = die.GetReferencedDIE(DW_AT_discr); + } + + // For old-style ("RUST$ENUM$DISR"-using) enums that don't have the + // NonZero optimization applied, variants are listed in order of + // discriminant. We track that value here. + uint64_t naive_discriminant = 0; + + bool could_be_enum = die.Tag() == DW_TAG_union_type; + bool encoded_enum = false; + + ModuleSP module_sp = die.GetModule(); + for (auto &&child_die : IterableDIEChildren(die)) { + Field new_field; + + // If this isn't correct for this particular enum, that's ok, + // because the correct value will be computed below. + new_field.discriminant = naive_discriminant++; + + if (is_variant && child_die.Tag() == DW_TAG_variant) { + // Find the discriminant, if it exists. + for (auto &&attr : IterableDIEAttrs(child_die)) { + if (attr.first == DW_AT_discr_value) { + new_field.discriminant = attr.second.Unsigned(); + break; + } + } + + // Use the child that is a member. + bool found = false; + for (auto &&variant_child_die : IterableDIEChildren(child_die)) { + if (variant_child_die.Tag() == DW_TAG_member) { + found = true; + child_die = variant_child_die; + break; + } + } + if (!found) { + // Just ignore this variant. + continue; + } + + // Fall through and process the variant's child as if it were a + // child of the structure. + } + + if (child_die.Tag() == DW_TAG_member) { + for (auto &&attr : IterableDIEAttrs(child_die)) { + switch (attr.first) { + case DW_AT_name: + new_field.name = attr.second.AsCString(); + if (fields.size() == 0) { + if (strcmp(new_field.name, "RUST$ENUM$DISR") == 0) + new_field.is_discriminant = true; + else if (starts_with(new_field.name, RUST_ENCODED_PREFIX)) { + // The "non-zero" optimization has been applied. + // In this case, we'll see a single field like: + // RUST$ENCODED$ENUM$n0$n1...$Name + // Here n0, n1, ... are integers that describe the path + // to the discriminant. When the discriminant (and + // integer) is 0, the enum has the value Name, a + // unit-like struct. However when it is non-zero, the + // enum has the value of this field's type. + + // Here we're going to push an initial field for the + // unit-like struct. Note that the constructor sets the + // discriminant to the correct value -- zero. + Field unit_field; + unit_field.name = new_field.name; + discriminant_path = ParseDiscriminantPath(&unit_field.name); + unit_field.is_elided = true; + fields.push_back(unit_field); + + // The actual field is the default variant. + new_field.is_default = true; + new_field.name = nullptr; + encoded_enum = true; + } + } + break; + case DW_AT_type: + new_field.type = attr.second; + if (could_be_enum && !encoded_enum) { + could_be_enum = IsPossibleEnumVariant(dwarf->GetDIE(DIERef(new_field.type))); + } + break; + case DW_AT_data_member_location: + if (attr.second.BlockData()) { + Value initialValue(0); + Value memberOffset(0); + const DWARFDataExtractor &debug_info_data = + child_die.GetDWARF()->get_debug_info_data(); + uint32_t block_length = attr.second.Unsigned(); + uint32_t block_offset = attr.second.BlockData() - debug_info_data.GetDataStart(); + if (DWARFExpression::Evaluate( + NULL, // ExecutionContext * + NULL, // RegisterContext * + module_sp, debug_info_data, die.GetCU(), block_offset, + block_length, eRegisterKindDWARF, &initialValue, NULL, + memberOffset, NULL)) { + new_field.byte_offset = memberOffset.ResolveValue(NULL).UInt(); + } + } else { + new_field.byte_offset = attr.second.Unsigned(); + } + break; + } + } + + if (child_die == discriminant_die) { + // This field is the discriminant, so don't push it, but instead + // record this for the caller. + saw_discr = true; + discr_offset = new_field.byte_offset; + discr_byte_size = m_ast.GetBitSize(new_field.compiler_type.GetOpaqueQualType(), + nullptr) / 8; + } else if (child_die.Tag() == DW_TAG_variant_part) { + // New-style enum representation -- nothing useful is in the + // enclosing struct, so we can just recurse here. + return ParseFields(child_die, discriminant_path, is_tuple, + discr_offset, discr_byte_size, saw_discr); + } else { + if (new_field.is_discriminant) { + // Don't check this field name, and don't increment field_index. + // When we see a tuple with fields like + // RUST$ENUM$DISR + // __0 + // __1 + // etc + // ... it means the tuple is a member type of an enum. + } else if (numeric_names) { + char buf[32]; + snprintf (buf, sizeof (buf), "__%u", field_index); + if (!new_field.name || strcmp(new_field.name, buf) != 0) + numeric_names = false; + ++field_index; + } + + fields.push_back(new_field); + } + } + } + + if (!numeric_names) { + // If the field name checking failed, maybe we don't have a tuple + // after all, somehow. + is_tuple = false; + } else if (!is_tuple) { + // If we saw numeric names in sequence, we have a tuple struct; + // but if there were no fields, then we can't tell and so we + // arbitrarily choose an empty struct. + is_tuple = field_index > 0; + } + + // If we saw a Rust enum, correctly arrange the scope of the various + // sub-types. This is needed to work around the way that the Rust + // compiler emits the types: it emits each enum variant's type as a + // sibling of the enum type, whereas logically it ought to be a + // child. + if (could_be_enum) { + for (auto &&field : fields) { + m_reparent_map[dwarf->GetDIE(DIERef(field.type)).GetDIE()] = die; + } + } + + return fields; +} + +TypeSP DWARFASTParserRust::ParseStructureType(const DWARFDIE &die) { + const bool is_union = die.Tag() == DW_TAG_union_type; + + bool byte_size_valid = false; + uint64_t byte_size = 0; + const char *type_name_cstr = NULL; + ConstString type_name_const_str; + SymbolFileDWARF *dwarf = die.GetDWARF(); + Declaration decl; + + for (auto &&attr : IterableDIEAttrs(die)) { + switch (attr.first) { + case DW_AT_name: + type_name_cstr = attr.second.AsCString(); + type_name_const_str.SetCString(type_name_cstr); + break; + + case DW_AT_byte_size: + byte_size = attr.second.Unsigned(); + byte_size_valid = true; + break; + } + } + + UniqueDWARFASTType ast_entry; + TypeSP type_sp; + + // Only try and unique the type if it has a name. + if (type_name_const_str && + dwarf->GetUniqueDWARFASTTypeMap().Find(type_name_const_str, die, &decl, + byte_size_valid ? byte_size : -1, ast_entry)) { + // We have already parsed this type. + type_sp = ast_entry.m_type_sp; + if (type_sp) { + dwarf->m_die_to_type[die.GetDIE()] = type_sp.get(); + return type_sp; + } + } + + // Currently, rustc emits tuples with a name starting with "("; but + // there's no way to distinguish a zero-length struct from a + // zero-length tuple struct. This decision might be changed by + // ParseFields. + bool is_tuple = type_name_cstr && type_name_cstr[0] == '('; + // We might see a tuple struct, and we want to differentiate the two + // when qualifying names. + bool is_anon_tuple = is_tuple; + bool saw_discr = false; + uint64_t discr_offset, discr_byte_size; + std::vector discriminant_path; + std::vector fields = ParseFields(die, discriminant_path, is_tuple, + discr_offset, discr_byte_size, saw_discr); + + // This is true if this is a union, there are multiple fields and + // each field's type has a discriminant. + bool all_have_discriminants = is_union && fields.size() > 0; + // This is true if the current type has a discriminant. + // all_have_discriminants records whether the outer type is a Rust + // enum; this records whether the current type is one variant type + // of the enum. + bool has_discriminant = fields.size() > 0 && fields[0].is_discriminant; + + // See the comment by m_discriminant to understand this. + DIERef save_discr = m_discriminant; + if (has_discriminant) + m_discriminant = DIERef(fields[0].type); + + // Have to resolve the field types before creating the outer type, + // so that we can see whether or not this is an enum. + for (auto &&field : fields) { + if (field.is_elided) { + // A unit-like struct with the given name. The byte size + // probably doesn't matter. + ConstString name = FullyQualify(type_name_const_str, die); + name = ConstString((std::string(name.AsCString()) + "::" + field.name).c_str()); + field.compiler_type = m_ast.CreateStructType(name, 1, false); + } else { + Type *type = die.ResolveTypeUID(DIERef(field.type)); + if (type) { + field.compiler_type = type->GetFullCompilerType(); + if (all_have_discriminants) + all_have_discriminants = m_ast.TypeHasDiscriminant(field.compiler_type); + } + } + + // Fix up the field's name by taking it from the type if necessary. + if (field.name == nullptr) { + field.name = field.compiler_type.GetTypeName().AsCString(); + } + } + + m_discriminant = save_discr; + + bool compiler_type_was_created = false; + CompilerType compiler_type(&m_ast, + dwarf->m_forward_decl_die_to_clang_type.lookup(die.GetDIE())); + if (!compiler_type) { + compiler_type_was_created = true; + + if (!is_anon_tuple) { + type_name_const_str = FullyQualify(type_name_const_str, die); + } + + if (saw_discr) { + compiler_type = m_ast.CreateEnumType(type_name_const_str, byte_size, + discr_offset, discr_byte_size); + } else if (all_have_discriminants) { + // In this case, the discriminant is easily computed as the 0th + // field of the 0th field. + discriminant_path = std::vector { 0 }; + + FindDiscriminantLocation(fields[0].compiler_type, std::move(discriminant_path), + discr_offset, discr_byte_size); + + compiler_type = m_ast.CreateEnumType(type_name_const_str, byte_size, + discr_offset, discr_byte_size); + } else if (!discriminant_path.empty()) { + CompilerType start_type = fields[discriminant_path[0]].compiler_type; + discriminant_path.erase(discriminant_path.begin()); + + FindDiscriminantLocation(start_type, std::move(discriminant_path), + discr_offset, discr_byte_size); + + compiler_type = m_ast.CreateEnumType(type_name_const_str, byte_size, + discr_offset, discr_byte_size); + } else if (is_union) + compiler_type = m_ast.CreateUnionType(type_name_const_str, byte_size); + else if (is_tuple) + compiler_type = m_ast.CreateTupleType(type_name_const_str, byte_size, has_discriminant); + else + compiler_type = m_ast.CreateStructType(type_name_const_str, byte_size, has_discriminant); + } + + type_sp.reset(new Type(die.GetID(), dwarf, type_name_const_str, + byte_size, NULL, LLDB_INVALID_UID, + Type::eEncodingIsUID, &decl, compiler_type, + Type::eResolveStateForward)); + + // Now add the fields. + for (auto &&field : fields) { + if (field.compiler_type) { + ConstString name(is_tuple ? "" : field.name); + m_ast.AddFieldToStruct(compiler_type, name, field.compiler_type, field.byte_offset, + field.is_default, field.discriminant); + } + } + + m_ast.FinishAggregateInitialization(compiler_type); + + // Add our type to the unique type map so we don't + // end up creating many copies of the same type over + // and over in the ASTContext for our module + ast_entry.m_type_sp = type_sp; + ast_entry.m_die = die; + ast_entry.m_declaration = decl; + ast_entry.m_byte_size = byte_size; + dwarf->GetUniqueDWARFASTTypeMap().Insert(type_name_const_str, ast_entry); + + if (compiler_type_was_created) { + // Leave this as a forward declaration until we need + // to know the details of the type. lldb_private::Type + // will automatically call the SymbolFile virtual function + // "SymbolFileDWARF::CompleteType(Type *)" + // When the definition needs to be defined. + dwarf->m_forward_decl_die_to_clang_type[die.GetDIE()] = + compiler_type.GetOpaqueQualType(); + dwarf->m_forward_decl_clang_type_to_die[compiler_type.GetOpaqueQualType()] = + die.GetDIERef(); + } + + return type_sp; +} + +TypeSP DWARFASTParserRust::ParseCLikeEnum(lldb_private::Log *log, const DWARFDIE &die) { + const char *type_name_cstr = NULL; + ConstString type_name_const_str; + SymbolFileDWARF *dwarf = die.GetDWARF(); + CompilerType underlying_type; + + for (auto &&attr : IterableDIEAttrs(die)) { + switch (attr.first) { + case DW_AT_name: + type_name_cstr = attr.second.AsCString(); + type_name_const_str.SetCString(type_name_cstr); + break; + + case DW_AT_type: + if (Type *type = die.ResolveTypeUID(DIERef(attr.second))) { + underlying_type = type->GetFullCompilerType(); + } + break; + } + } + + // See the comment by m_discriminant to understand this; but this + // allows registering two types of the same name when reading a Rust + // enum. + if (die.GetDIERef() == m_discriminant) { + type_name_const_str.Clear(); + } else { + type_name_const_str = FullyQualify(type_name_const_str, die); + } + + std::map values; + for (auto &&child_die : IterableDIEChildren(die)) { + if (child_die.Tag() != DW_TAG_enumerator) { + continue; + } + + bool saw_value = false; + uint64_t value; + std::string name; + for (auto &&attr : IterableDIEAttrs(child_die)) { + switch (attr.first) { + case DW_AT_name: + name = attr.second.AsCString(); + break; + case DW_AT_const_value: + saw_value = true; + value = attr.second.Unsigned(); + break; + } + + if (saw_value && !name.empty()) { + values[value] = name; + } else { + dwarf->GetObjectFile()->GetModule()->LogMessage( + log, "DWARFASTParserRust::ParseCLikeEnum (die = 0x%8.8x) %s " + "is invalid)", + child_die.GetOffset(), DW_TAG_value_to_name(die.Tag())); + } + } + } + + Declaration decl; + CompilerType compiler_type = m_ast.CreateCLikeEnumType(type_name_const_str, + underlying_type, + std::move(values)); + TypeSP type_sp(new Type(die.GetID(), dwarf, type_name_const_str, 0, NULL, + LLDB_INVALID_UID, Type::eEncodingIsUID, &decl, + compiler_type, Type::eResolveStateFull)); + + return type_sp; +} + +TypeSP DWARFASTParserRust::ParseTypeFromDWARF( + const lldb_private::SymbolContext &sc, const DWARFDIE &die, + lldb_private::Log *log, bool *type_is_new_ptr) { + TypeSP type_sp; + + if (type_is_new_ptr) + *type_is_new_ptr = false; + + if (die) { + SymbolFileDWARF *dwarf = die.GetDWARF(); + if (log) { + dwarf->GetObjectFile()->GetModule()->LogMessage( + log, "DWARFASTParserRust::ParseTypeFromDWARF (die = 0x%8.8x) %s name = " + "'%s')", + die.GetOffset(), DW_TAG_value_to_name(die.Tag()), die.GetName()); + } + + Type *type_ptr = dwarf->m_die_to_type.lookup(die.GetDIE()); + TypeList *type_list = dwarf->GetTypeList(); + if (type_ptr == NULL) { + if (type_is_new_ptr) + *type_is_new_ptr = true; + + const dw_tag_t tag = die.Tag(); + + // Set a bit that lets us know that we are currently parsing this + dwarf->m_die_to_type[die.GetDIE()] = DIE_IS_BEING_PARSED; + + switch (tag) { + case DW_TAG_base_type: + case DW_TAG_pointer_type: + case DW_TAG_typedef: + case DW_TAG_template_type_parameter: + case DW_TAG_unspecified_type: + type_sp = ParseSimpleType(log, die); + break; + + case DW_TAG_union_type: + case DW_TAG_structure_type: + type_sp = ParseStructureType(die); + break; + + case DW_TAG_subprogram: + case DW_TAG_subroutine_type: + type_sp = ParseFunctionType(die); + break; + + case DW_TAG_array_type: + type_sp = ParseArrayType(die); + break; + + case DW_TAG_enumeration_type: + type_sp = ParseCLikeEnum(log, die); + break; + + default: + dwarf->GetObjectFile()->GetModule()->ReportError( + "{0x%8.8x}: unhandled type tag 0x%4.4x (%s), " + "please file a bug and attach the file at the " + "start of this error message", + die.GetOffset(), tag, DW_TAG_value_to_name(tag)); + break; + } + + if (type_sp.get()) { + DWARFDIE sc_parent_die = + SymbolFileDWARF::GetParentSymbolContextDIE(die); + dw_tag_t sc_parent_tag = sc_parent_die.Tag(); + + SymbolContextScope *symbol_context_scope = NULL; + if (sc_parent_tag == DW_TAG_compile_unit) { + symbol_context_scope = sc.comp_unit; + } else if (sc.function != NULL && sc_parent_die) { + symbol_context_scope = + sc.function->GetBlock(true).FindBlockByID(sc_parent_die.GetID()); + if (symbol_context_scope == NULL) + symbol_context_scope = sc.function; + } + + if (symbol_context_scope != NULL) { + type_sp->SetSymbolContextScope(symbol_context_scope); + } + + // We are ready to put this type into the uniqued list up at the module + // level + type_list->Insert(type_sp); + } + dwarf->m_die_to_type[die.GetDIE()] = type_sp.get(); + } else if (type_ptr != DIE_IS_BEING_PARSED) { + type_sp = type_ptr->shared_from_this(); + } + } + return type_sp; +} + +bool DWARFASTParserRust::CompleteTypeFromDWARF(const DWARFDIE &die, + lldb_private::Type *type, + CompilerType &compiler_type) { + // We don't currently use type completion for Rust. + return bool(die); +} + +Function *DWARFASTParserRust::ParseFunctionFromDWARF(const SymbolContext &sc, + const DWARFDIE &die) { + DWARFRangeList func_ranges; + const char *name = NULL; + const char *mangled = NULL; + int decl_file = 0; + int decl_line = 0; + int decl_column = 0; + int call_file = 0; + int call_line = 0; + int call_column = 0; + DWARFExpression frame_base(die.GetCU()); + + assert(die.Tag() == DW_TAG_subprogram); + + if (die.GetDIENamesAndRanges(name, mangled, func_ranges, decl_file, decl_line, + decl_column, call_file, call_line, call_column, + &frame_base)) { + // Union of all ranges in the function DIE (if the function is + // discontiguous) + AddressRange func_range; + lldb::addr_t lowest_func_addr = func_ranges.GetMinRangeBase(0); + lldb::addr_t highest_func_addr = func_ranges.GetMaxRangeEnd(0); + if (lowest_func_addr != LLDB_INVALID_ADDRESS && + lowest_func_addr <= highest_func_addr) { + ModuleSP module_sp(die.GetModule()); + func_range.GetBaseAddress().ResolveAddressUsingFileSections( + lowest_func_addr, module_sp->GetSectionList()); + if (func_range.GetBaseAddress().IsValid()) + func_range.SetByteSize(highest_func_addr - lowest_func_addr); + } + + if (func_range.GetBaseAddress().IsValid()) { + Mangled func_name; + func_name.SetValue(ConstString(name), false); + + FunctionSP func_sp; + std::unique_ptr decl_ap; + if (decl_file != 0 || decl_line != 0 || decl_column != 0) + decl_ap.reset(new Declaration( + sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex(decl_file), + decl_line, decl_column)); + + SymbolFileDWARF *dwarf = die.GetDWARF(); + // Supply the type _only_ if it has already been parsed + Type *func_type = dwarf->m_die_to_type.lookup(die.GetDIE()); + + assert(func_type == NULL || func_type != DIE_IS_BEING_PARSED); + + if (dwarf->FixupAddress(func_range.GetBaseAddress())) { + const user_id_t func_user_id = die.GetID(); + func_sp.reset(new Function(sc.comp_unit, + func_user_id, // UserID is the DIE offset + func_user_id, func_name, func_type, + func_range)); // first address range + + if (func_sp.get() != NULL) { + if (frame_base.IsValid()) + func_sp->GetFrameBaseExpression() = frame_base; + sc.comp_unit->AddFunction(func_sp); + return func_sp.get(); + } + } + } + } + return NULL; +} + +lldb_private::CompilerDeclContext +DWARFASTParserRust::GetDeclContextForUIDFromDWARF(const DWARFDIE &die) { + auto iter = m_decl_contexts.find(die.GetDIE()); + if (iter != m_decl_contexts.end()) { + return iter->second; + } + + CompilerDeclContext result; + switch (die.Tag()) { + case DW_TAG_compile_unit: + result = m_ast.GetTranslationUnitDecl(); + break; + + case DW_TAG_union_type: + case DW_TAG_structure_type: + case DW_TAG_namespace: { + const char *name = die.GetName(); + if (name) { + CompilerDeclContext parent = GetDeclContextContainingUIDFromDWARF(die); + result = m_ast.GetNamespaceDecl(parent, ConstString(name)); + } + break; + } + + case DW_TAG_lexical_block: + case DW_TAG_subprogram: + result = GetDeclContextContainingUIDFromDWARF(die); + break; + + default: + break; + } + + if (result) { + m_decl_contexts[die.GetDIE()] = result; + m_decl_contexts_to_die.emplace(result, die); + } + + return result; +} + +lldb_private::CompilerDeclContext +DWARFASTParserRust::GetDeclContextContainingUIDFromDWARF(const DWARFDIE &die) { + DWARFDIE decl_ctx_die; + + DWARFDebugInfoEntry *ptr = die.GetDIE(); + auto iter = m_reparent_map.find(ptr); + if (iter != m_reparent_map.end()) { + decl_ctx_die = iter->second; + } else { + SymbolFileDWARF *dwarf = die.GetDWARF(); + decl_ctx_die = dwarf->GetDeclContextDIEContainingDIE(die); + } + return GetDeclContextForUIDFromDWARF(decl_ctx_die); +} + +lldb_private::CompilerDecl +DWARFASTParserRust::GetDeclForUIDFromDWARF(const DWARFDIE &die) { + auto iter = m_decls.find(die.GetDIE()); + if (iter != m_decls.end()) { + return iter->second; + } + + CompilerDecl result; + if (die.Tag() == DW_TAG_variable || die.Tag() == DW_TAG_constant) { + const char *name = die.GetName(); + if (name) { + const char *mangled = die.GetMangledName(); + CompilerDeclContext parent = GetDeclContextContainingUIDFromDWARF(die); + result = m_ast.GetDecl(parent, ConstString(name), ConstString(mangled)); + + if (result) { + m_decls[die.GetDIE()] = result; + } + } + } + + return result; +} + +std::vector +DWARFASTParserRust::GetDIEForDeclContext(lldb_private::CompilerDeclContext decl_context) { + std::vector result; + for (auto it = m_decl_contexts_to_die.find(decl_context); + it != m_decl_contexts_to_die.end(); + ++it) + result.push_back(it->second); + return result; +} diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserRust.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserRust.h new file mode 100644 index 00000000000000..e2d54931954a12 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserRust.h @@ -0,0 +1,134 @@ +//===-- DWARFASTParserRust.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef SymbolFileDWARF_DWARFASTParserRust_h_ +#define SymbolFileDWARF_DWARFASTParserRust_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallVector.h" +#include "lldb/Utility/ConstString.h" + +// Project includes +#include "DWARFASTParser.h" +#include "DWARFDIE.h" +#include "DWARFDebugInfoEntry.h" +#include "DWARFDefines.h" +#include "DIERef.h" +#include "lldb/Core/PluginInterface.h" +#include "lldb/Symbol/RustASTContext.h" +#include "DWARFFormValue.h" + +class DWARFDebugInfoEntry; +class DWARFDIECollection; + +class DWARFASTParserRust : public DWARFASTParser { +public: + DWARFASTParserRust(lldb_private::RustASTContext &ast) + : m_ast(ast) + { + } + + lldb::TypeSP ParseTypeFromDWARF(const lldb_private::SymbolContext &sc, + const DWARFDIE &die, lldb_private::Log *log, + bool *type_is_new_ptr) override; + + lldb_private::Function * + ParseFunctionFromDWARF(const lldb_private::SymbolContext &sc, + const DWARFDIE &die) override; + + bool CompleteTypeFromDWARF(const DWARFDIE &die, lldb_private::Type *type, + lldb_private::CompilerType &rust_type) override; + + lldb_private::CompilerDeclContext + GetDeclContextForUIDFromDWARF(const DWARFDIE &die) override; + + lldb_private::CompilerDeclContext + GetDeclContextContainingUIDFromDWARF(const DWARFDIE &die) override; + + lldb_private::CompilerDecl GetDeclForUIDFromDWARF(const DWARFDIE &die) override; + + std::vector GetDIEForDeclContext(lldb_private::CompilerDeclContext decl_context) + override; + +private: + lldb::TypeSP ParseSimpleType(lldb_private::Log *log, const DWARFDIE &die); + lldb::TypeSP ParseArrayType(const DWARFDIE &die); + lldb::TypeSP ParseFunctionType(const DWARFDIE &die); + lldb::TypeSP ParseStructureType(const DWARFDIE &die); + lldb::TypeSP ParseCLikeEnum(lldb_private::Log *log, const DWARFDIE &die); + lldb_private::ConstString FullyQualify(const lldb_private::ConstString &name, + const DWARFDIE &die); + + std::vector ParseDiscriminantPath(const char **in_str); + void FindDiscriminantLocation(lldb_private::CompilerType type, + std::vector &&path, + uint64_t &offset, uint64_t &byte_size); + bool IsPossibleEnumVariant(const DWARFDIE &die); + + struct Field { + Field() + : is_discriminant(false), + is_elided(false), + name(nullptr), + byte_offset(-1), + is_default(false), + discriminant(0) + { + } + + bool is_discriminant; + // True if this field is the field that was elided by the non-zero + // optimization. + bool is_elided; + const char *name; + DWARFFormValue type; + lldb_private::CompilerType compiler_type; + uint32_t byte_offset; + + // These are used if this is a member of an enum type. + bool is_default; + uint64_t discriminant; + }; + + std::vector ParseFields(const DWARFDIE &die, + std::vector &discriminant_path, + bool &is_tuple, + uint64_t &discr_offset, uint64_t &discr_byte_size, + bool &saw_discr); + + lldb_private::RustASTContext &m_ast; + + // The Rust compiler will emit a DW_TAG_enumeration_type for the + // type of an enum discriminant. However, this type will have the + // same name as the enum type itself. So, when we expect to read + // the enumeration type, we set this member, and ParseCLikeEnum + // avoids giving the name to the enumeration type. + DIERef m_discriminant; + + // When reading a Rust enum, we set this temporarily when reading + // the field types, so that they can get the correct scoping. + DWARFDIE m_rust_enum_die; + + // The Rust compiler emits the variants of an enum type as siblings + // to the DW_TAG_union_type that (currently) represents the enum. + // However, conceptually these ought to be nested. This map tracks + // DIEs involved in this situation so that the enum variants can be + // given correctly-scoped names. + llvm::DenseMap m_reparent_map; + + llvm::DenseMap m_decl_contexts; + llvm::DenseMap m_decls; + std::multimap m_decl_contexts_to_die; +}; + +#endif // SymbolFileDWARF_DWARFASTParserRust_h_ diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h index d351289f8b51b0..bfe97443671454 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h @@ -69,6 +69,7 @@ class SymbolFileDWARF : public lldb_private::SymbolFile, friend class DWARFUnit; friend class DWARFDIE; friend class DWARFASTParserClang; + friend class DWARFASTParserRust; //------------------------------------------------------------------ // Static Functions diff --git a/lldb/source/Symbol/CMakeLists.txt b/lldb/source/Symbol/CMakeLists.txt index 56562d32248584..d1b4390aa5def3 100644 --- a/lldb/source/Symbol/CMakeLists.txt +++ b/lldb/source/Symbol/CMakeLists.txt @@ -19,6 +19,7 @@ add_lldb_library(lldbSymbol LineEntry.cpp LineTable.cpp ObjectFile.cpp + RustASTContext.cpp Symbol.cpp SymbolContext.cpp SymbolFile.cpp diff --git a/lldb/source/Symbol/ClangASTContext.cpp b/lldb/source/Symbol/ClangASTContext.cpp index 85436b630b7d7d..868daa1d484bfa 100644 --- a/lldb/source/Symbol/ClangASTContext.cpp +++ b/lldb/source/Symbol/ClangASTContext.cpp @@ -115,8 +115,6 @@ ClangASTContextSupportsLanguage(lldb::LanguageType language) { Language::LanguageIsCPlusPlus(language) || Language::LanguageIsObjC(language) || Language::LanguageIsPascal(language) || - // Use Clang for Rust until there is a proper language plugin for it - language == eLanguageTypeRust || language == eLanguageTypeExtRenderScript || // Use Clang for D until there is a proper language plugin for it language == eLanguageTypeD || diff --git a/lldb/source/Symbol/RustASTContext.cpp b/lldb/source/Symbol/RustASTContext.cpp new file mode 100644 index 00000000000000..c5f010c9a3c051 --- /dev/null +++ b/lldb/source/Symbol/RustASTContext.cpp @@ -0,0 +1,2169 @@ +//===-- RustASTContext.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include +#include +#include + +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/UniqueCStringMap.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/StringPrinter.h" +#include "lldb/Symbol/CompilerType.h" +#include "lldb/Symbol/RustASTContext.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Core/DumpDataExtractor.h" + +#include "llvm/Support/Threading.h" + +#include "Plugins/ExpressionParser/Rust/RustUserExpression.h" +#include "Plugins/SymbolFile/DWARF/DWARFASTParserRust.h" + +#include + +using namespace lldb; + +namespace lldb_private { + +class RustAggregateBase; +class RustArray; +class RustBool; +class RustCLikeEnum; +class RustEnum; +class RustFunction; +class RustIntegral; +class RustPointer; +class RustStruct; +class RustTuple; +class RustTypedef; + +class RustType { +protected: + + RustType(const ConstString &name) : m_name(name) {} + DISALLOW_COPY_AND_ASSIGN (RustType); + +public: + + virtual ~RustType() {} + + ConstString Name() const { return m_name; } + + virtual lldb::Format Format() const { + return eFormatBytes; + } + + virtual std::string GetCABITypeDeclaration(RustASTContext::TypeNameMap *name_map, + const std::string &varname) = 0; + + virtual uint32_t TypeInfo(CompilerType *element_type) const = 0; + virtual lldb::TypeClass TypeClass() const = 0; + virtual uint64_t ByteSize() const = 0; + + virtual RustAggregateBase *AsAggregate() { return nullptr; } + virtual RustArray *AsArray() { return nullptr; } + virtual RustBool *AsBool() { return nullptr; } + virtual RustCLikeEnum *AsCLikeEnum() { return nullptr; } + virtual RustEnum *AsEnum() { return nullptr; } + virtual RustFunction *AsFunction() { return nullptr; } + virtual RustIntegral *AsInteger () { return nullptr; } + virtual RustPointer *AsPointer () { return nullptr; } + virtual RustTuple *AsTuple() { return nullptr; } + virtual RustTypedef *AsTypedef() { return nullptr; } + + virtual bool IsAggregateType() const { return false; } + virtual bool IsCharType() const { return false; } + virtual bool IsFloatType() const { return false; } + +private: + ConstString m_name; +}; + +class RustBool : public RustType { +public: + RustBool(const ConstString &name) : RustType(name) {} + DISALLOW_COPY_AND_ASSIGN(RustBool); + + RustBool *AsBool() override { + return this; + } + + lldb::Format Format() const override { + return eFormatBoolean; + } + + uint32_t TypeInfo(CompilerType *) const override { + return eTypeIsBuiltIn | eTypeHasValue | eTypeIsScalar; + } + + lldb::TypeClass TypeClass() const override { + return eTypeClassBuiltin; + } + + uint64_t ByteSize() const override { + return 1; + } + + std::string GetCABITypeDeclaration(RustASTContext::TypeNameMap *name_map, + const std::string &varname) override { + return "bool " + varname; + } +}; + +class RustIntegral : public RustType { +public: + RustIntegral(const ConstString &name, bool is_signed, uint64_t byte_size, + bool is_char = false) + : RustType(name), + m_is_signed(is_signed), + m_byte_size(byte_size), + m_is_char(is_char) + {} + DISALLOW_COPY_AND_ASSIGN(RustIntegral); + + lldb::Format Format() const override { + if (m_is_char) + return eFormatUnicode32; + return m_is_signed ? eFormatDecimal : eFormatUnsigned; + } + + bool IsSigned() const { return m_is_signed; } + uint64_t ByteSize() const override { return m_byte_size; } + + RustIntegral *AsInteger () override { return this; } + + bool IsCharType() const override { return m_is_char; } + + uint32_t TypeInfo(CompilerType *) const override { + uint32_t result = eTypeIsBuiltIn | eTypeHasValue | eTypeIsScalar | eTypeIsInteger; + if (m_is_signed) + result |= eTypeIsSigned; + return result; + } + + lldb::TypeClass TypeClass() const override { + return eTypeClassBuiltin; + } + + std::string GetCABITypeDeclaration(RustASTContext::TypeNameMap *name_map, + const std::string &varname) override { + // These names are predefined by clang. + std::string result = "__"; + if (!m_is_signed) { + result += "U"; + } + result += "INT" + std::to_string(8 * m_byte_size) + "_TYPE__ " + varname; + return result; + } + +private: + + bool m_is_signed; + uint64_t m_byte_size; + bool m_is_char; +}; + +class RustCLikeEnum : public RustType { +public: + RustCLikeEnum(const ConstString &name, const CompilerType &underlying_type, + std::map &&values) + : RustType(name), + m_underlying_type(underlying_type), + m_values(std::move(values)) + { + } + DISALLOW_COPY_AND_ASSIGN(RustCLikeEnum); + + RustCLikeEnum *AsCLikeEnum() override { return this; } + + lldb::Format Format() const override { + return eFormatEnum; + } + + uint32_t TypeInfo(CompilerType *) const override { + return eTypeHasValue | eTypeIsEnumeration | eTypeIsScalar; + } + + lldb::TypeClass TypeClass() const override { + return eTypeClassEnumeration; + } + + uint64_t ByteSize() const override { + return m_underlying_type.GetByteSize(nullptr); + } + + bool IsSigned() const { + bool is_signed; + return m_underlying_type.IsIntegerType(is_signed) && is_signed; + } + + bool FindName(uint64_t val, std::string &name) { + auto iter = m_values.find(val); + if (iter == m_values.end()) { + return false; + } + name = iter->second; + return true; + } + + std::string GetCABITypeDeclaration(RustASTContext::TypeNameMap *name_map, + const std::string &varname) override { + RustType *type = (RustType *) m_underlying_type.GetOpaqueQualType(); + return type->GetCABITypeDeclaration(name_map, varname); + } + +private: + + CompilerType m_underlying_type; + std::map m_values; +}; + +class RustFloat : public RustType { +public: + RustFloat(const ConstString &name, uint64_t byte_size) + : RustType(name), + m_byte_size(byte_size) + {} + DISALLOW_COPY_AND_ASSIGN(RustFloat); + + lldb::Format Format() const override { + return eFormatFloat; + } + + bool IsFloatType() const override { return true; } + + uint32_t TypeInfo(CompilerType *) const override { + return eTypeIsBuiltIn | eTypeHasValue | eTypeIsFloat; + } + + lldb::TypeClass TypeClass() const override { + return eTypeClassBuiltin; + } + + uint64_t ByteSize() const override { return m_byte_size; } + + std::string GetCABITypeDeclaration(RustASTContext::TypeNameMap *name_map, + const std::string &varname) override { + return (m_byte_size == 4 ? "float " : "double ") + varname; + } + +private: + + uint64_t m_byte_size; +}; + +class RustPointer : public RustType { +public: + // Pointers and references are handled similarly. + RustPointer(const ConstString &name, const CompilerType &pointee, uint64_t byte_size) + : RustType(name), + m_pointee(pointee), + m_byte_size(byte_size) + {} + DISALLOW_COPY_AND_ASSIGN(RustPointer); + + lldb::Format Format() const override { + return eFormatPointer; + } + + CompilerType PointeeType() const { return m_pointee; } + + RustPointer *AsPointer() override { return this; } + + uint32_t TypeInfo(CompilerType *elem) const override { + if (elem) + *elem = m_pointee; + return eTypeIsBuiltIn | eTypeHasValue | eTypeIsPointer; + } + + lldb::TypeClass TypeClass() const override { + return eTypeClassPointer; + } + + uint64_t ByteSize() const override { + return m_byte_size; + } + + std::string GetCABITypeDeclaration(RustASTContext::TypeNameMap *name_map, + const std::string &varname) override { + RustType *p_type = (RustType *) m_pointee.GetOpaqueQualType(); + if (p_type->AsFunction()) { + // This does the right thing, see the implementation. + return p_type->GetCABITypeDeclaration(name_map, varname); + } + return p_type->GetCABITypeDeclaration(name_map, "") + "* " + varname; + } + +private: + + CompilerType m_pointee; + uint64_t m_byte_size; +}; + +class RustArray : public RustType { +public: + RustArray(const ConstString &name, uint64_t length, const CompilerType &elem) + : RustType(name), + m_length(length), + m_elem(elem) + {} + DISALLOW_COPY_AND_ASSIGN(RustArray); + + uint64_t Length() const { return m_length; } + RustArray *AsArray() override { return this; } + CompilerType ElementType() const { return m_elem; } + bool IsAggregateType() const override { return true; } + + uint32_t TypeInfo(CompilerType *elem) const override { + if (elem) + *elem = m_elem; + return eTypeHasChildren | eTypeIsArray; + } + + lldb::TypeClass TypeClass() const override { + return eTypeClassArray; + } + + uint64_t ByteSize() const override { + return m_elem.GetByteSize(nullptr) * m_length; + } + + std::string GetCABITypeDeclaration(RustASTContext::TypeNameMap *name_map, + const std::string &varname) override { + RustType *type = (RustType *) m_elem.GetOpaqueQualType(); + return type->GetCABITypeDeclaration(name_map, varname) + + "[" + std::to_string(m_length) + "]"; + } + +private: + uint64_t m_length; + CompilerType m_elem; +}; + +// Base type for struct, tuple, and tuple struct. +class RustAggregateBase : public RustType { +protected: + RustAggregateBase(const ConstString &name, uint64_t byte_size, bool has_discriminant = false) + : RustType(name), + m_byte_size(byte_size), + m_has_discriminant(has_discriminant) + {} + + DISALLOW_COPY_AND_ASSIGN(RustAggregateBase); + +public: + + RustAggregateBase *AsAggregate() override { return this; } + + bool IsAggregateType() const override { return true; } + + size_t FieldCount() const { return m_fields.size(); } + + uint32_t TypeInfo(CompilerType *) const override { + return eTypeHasChildren | eTypeIsStructUnion; + } + + lldb::TypeClass TypeClass() const override { + return eTypeClassStruct; + } + + uint64_t ByteSize() const override { + return m_byte_size; + } + + struct Field { + Field(const ConstString &name, const CompilerType &type, uint64_t offset) + : m_name(name), + m_type(type), + m_offset(offset) + { + } + + ConstString m_name; + CompilerType m_type; + uint64_t m_offset; + }; + + void AddField(const ConstString &name, const CompilerType &type, uint64_t offset) { + m_fields.emplace_back(name, type, offset); + } + + virtual void FinishInitialization() { + } + + bool HasDiscriminant() const { + return m_has_discriminant; + } + + // With the old-style enum encoding, after the discriminant's + // location is computed the member types no longer need to have + // theirs, so they are dropped. + void DropDiscriminant() { + if (m_has_discriminant) { + m_has_discriminant = false; + m_fields.erase(m_fields.begin()); + } + } + + const Field *FieldAt(size_t idx) { + if (idx >= m_fields.size()) + return nullptr; + return &m_fields[idx]; + } + + typedef std::vector::const_iterator const_iterator; + + const_iterator begin() const { + return m_fields.begin(); + } + + const_iterator end() const { + return m_fields.end(); + } + + // Type-printing support. + virtual const char *Tag() const = 0; + + virtual const char *TagName() const { + return Name().AsCString(); + } + + virtual const char *Opener() const = 0; + virtual const char *Closer() const = 0; + +protected: + + std::string GetFieldsCABITypeDeclaration(RustASTContext::TypeNameMap *name_map) { + int argno = 0; + std::string result; + for (const Field &f : m_fields) { + RustType *rtype = static_cast(f.m_type.GetOpaqueQualType()); + std::string name; + if (f.m_name.IsEmpty()) { + name = "__" + std::to_string(argno++); + } else { + name = f.m_name.AsCString(); + } + result += rtype->GetCABITypeDeclaration(name_map, name) + "; "; + } + return result; + } + +private: + + uint64_t m_byte_size; + std::vector m_fields; + bool m_has_discriminant; +}; + +class RustTuple : public RustAggregateBase { +public: + RustTuple(const ConstString &name, uint64_t byte_size, bool has_discriminant) + : RustAggregateBase(name, byte_size, has_discriminant) + {} + + DISALLOW_COPY_AND_ASSIGN(RustTuple); + + RustTuple *AsTuple() override { return this; } + + void AddField(const CompilerType &type, uint64_t offset) { + RustAggregateBase::AddField(ConstString(), type, offset); + } + + const char *Tag() const override { + return IsTuple() ? "" : "struct "; + } + const char *TagName() const override { + if (IsTuple()) { + return ""; + } + return Name().AsCString(); + } + const char *Opener() const override { + return "("; + } + const char *Closer() const override { + return ")"; + } + + std::string GetCABITypeDeclaration(RustASTContext::TypeNameMap *name_map, + const std::string &varname) override { + std::string tagname; + if (name_map->Tag(this, &tagname)) { + std::string def = " struct " + tagname + "{" + + GetFieldsCABITypeDeclaration(name_map) + " };\n"; + name_map->typedefs.append(def); + } + return tagname + " " + varname; + } + +private: + + // As opposed to a tuple struct. + bool IsTuple() const { + ConstString name = Name(); + // For the time being we must examine the name, because the DWARF + // doesn't provide anything else. + return name.IsEmpty() || name.AsCString()[0] == '('; + } +}; + +class RustStruct : public RustAggregateBase { +public: + RustStruct(const ConstString &name, uint64_t byte_size, bool has_discriminant) + : RustAggregateBase(name, byte_size, has_discriminant) + {} + + DISALLOW_COPY_AND_ASSIGN(RustStruct); + + const char *Tag() const override { + return "struct "; + } + const char *Opener() const override { + return "{"; + } + const char *Closer() const override { + return "}"; + } + + std::string GetCABITypeDeclaration(RustASTContext::TypeNameMap *name_map, + const std::string &varname) override { + std::string tagname; + if (name_map->Tag(this, &tagname)) { + std::string def = " struct " + tagname + "{" + + GetFieldsCABITypeDeclaration(name_map) + " };\n"; + name_map->typedefs.append(def); + } + return tagname + " " + varname; + } +}; + +class RustUnion : public RustAggregateBase { +public: + RustUnion(const ConstString &name, uint64_t byte_size) + : RustAggregateBase(name, byte_size) + {} + + DISALLOW_COPY_AND_ASSIGN(RustUnion); + + const char *Tag() const override { + return "union "; + } + const char *Opener() const override { + return "{"; + } + const char *Closer() const override { + return "}"; + } + + std::string GetCABITypeDeclaration(RustASTContext::TypeNameMap *name_map, + const std::string &varname) override { + std::string tagname; + if (name_map->Tag(this, &tagname)) { + std::string def = " union " + tagname + "{" + + GetFieldsCABITypeDeclaration(name_map) + " };\n"; + name_map->typedefs.append(def); + } + return tagname + " " + varname; + } +}; + +// A Rust enum, not a C-like enum. +class RustEnum : public RustAggregateBase { +public: + RustEnum(const ConstString &name, uint64_t byte_size, + uint32_t discr_offset, uint32_t discr_byte_size) + : RustAggregateBase(name, byte_size), + m_discr_offset(discr_offset), + m_discr_byte_size(discr_byte_size), + m_default(-1) + {} + + DISALLOW_COPY_AND_ASSIGN(RustEnum); + + RustEnum *AsEnum() override { return this; } + + const char *Tag() const override { + return "enum "; + } + const char *Opener() const override { + return "{"; + } + const char *Closer() const override { + return "}"; + } + + // Record the discriminant for the most recently added field. + void RecordDiscriminant(bool is_default, uint64_t discriminant) { + int value = int(FieldCount() - 1); + if (is_default) { + m_default = value; + } else { + m_discriminants[discriminant] = value; + } + } + + void GetDiscriminantLocation(uint64_t &discr_offset, uint64_t &discr_byte_size) { + discr_offset = m_discr_offset; + discr_byte_size = m_discr_byte_size; + } + + CompilerType FindEnumVariant(uint64_t discriminant) { + auto iter = m_discriminants.find(discriminant); + int idx = m_default; + if (iter != m_discriminants.end()) { + idx = iter->second; + } + return FieldAt(idx)->m_type; + } + + void FinishInitialization() override { + for (auto&& iter : *this) { + RustType *rtype = static_cast(iter.m_type.GetOpaqueQualType()); + if (RustAggregateBase* agg = rtype->AsAggregate()) { + agg->DropDiscriminant(); + } + } + } + + std::string GetCABITypeDeclaration(RustASTContext::TypeNameMap *name_map, + const std::string &varname) override { + std::string tagname; + if (name_map->Tag(this, &tagname)) { + std::string def = "struct " + tagname + "{ "; + // If the discriminant comes first, then it is a hidden field, + // which we'll emit. Otherwise, it is in a hole somewhere, or + // perhaps overlaid with some other field, so we don't bother. + // (This is unwarranted compiler knowledge - FIXME.) If there are + // zero or one fields then there is no discriminant. + if (FieldCount() > 1 && m_discr_offset == 0) { + def += "int" + std::to_string(8 * m_discr_byte_size) + "_t __discr; "; + } + def += GetFieldsCABITypeDeclaration(name_map) + " };\n"; + name_map->typedefs.append(def); + } + return tagname + " " + varname; + } + +private: + + // The offset and byte size of the discriminant. Note that, as a + // special case, if there is only a single field then the + // discriminant will be assumed not to exist. + uint32_t m_discr_offset; + uint32_t m_discr_byte_size; + + // The index in m_fields of the default variant. -1 if there is no + // default variant. + int m_default; + + // This maps from discriminant values to indices in m_fields. This + // is used to find the correct variant given a discriminant value. + std::unordered_map m_discriminants; +}; + +class RustFunction : public RustType { +public: + RustFunction (const ConstString &name, uint64_t byte_size, + const CompilerType &return_type, + const std::vector &&arguments) + : RustType(name), + m_byte_size(byte_size), + m_return_type(return_type), + m_arguments(std::move(arguments)) + { + } + DISALLOW_COPY_AND_ASSIGN(RustFunction); + + // do we care about the names? + void AddArgument(const CompilerType &type) { + m_arguments.push_back(type); + } + + RustFunction *AsFunction() override { return this; } + + CompilerType ReturnType() const { return m_return_type; } + size_t ArgumentCount() { return m_arguments.size(); } + CompilerType Argument(size_t i) { return m_arguments[i]; } + + uint32_t TypeInfo(CompilerType *) const override { + return eTypeIsFuncPrototype | eTypeHasValue; + } + + lldb::TypeClass TypeClass() const override { + return eTypeClassFunction; + } + + uint64_t ByteSize() const override { + return m_byte_size; + } + + std::string GetCABITypeDeclaration(RustASTContext::TypeNameMap *name_map, + const std::string &varname) override { + RustType *type = (RustType *) m_return_type.GetOpaqueQualType(); + + std::string result = type->GetCABITypeDeclaration(name_map, "") + " (*" + + varname + ")("; + bool first = true; + for (CompilerType &iter : m_arguments) { + RustType *type = (RustType *) iter.GetOpaqueQualType(); + if (!first) { + result += ", "; + } + first = false; + result += type->GetCABITypeDeclaration(name_map, ""); + } + + return result + ")"; + } + +private: + + uint64_t m_byte_size; + CompilerType m_return_type; + std::vector m_arguments; +}; + +class RustTypedef : public RustType { +public: + + RustTypedef(const ConstString &name, const CompilerType &type) + : RustType(name), + m_type(type) + { + } + + DISALLOW_COPY_AND_ASSIGN(RustTypedef); + + RustTypedef *AsTypedef() override { return this; } + CompilerType UnderlyingType() const { return m_type; } + + uint32_t TypeInfo(CompilerType *) const override { + return eTypeIsTypedef; + } + + lldb::TypeClass TypeClass() const override { + return eTypeClassTypedef; + } + + uint64_t ByteSize() const override { + return m_type.GetByteSize(nullptr); + } + + std::string GetCABITypeDeclaration(RustASTContext::TypeNameMap *name_map, + const std::string &varname) override { + RustType *type = (RustType *) m_type.GetOpaqueQualType(); + return type->GetCABITypeDeclaration(name_map, varname); + } + +private: + CompilerType m_type; +}; + +class RustDecl; +class RustDeclContext; + +class RustDeclBase { +public: + + ConstString Name() const { + return m_name; + } + + ConstString QualifiedName() { + if (!m_parent) { + return m_name; + } + if (!m_full_name) { + ConstString basename = m_parent->QualifiedName(); + if (basename) { + std::string qual = std::string(basename.AsCString()) + "::" + m_name.AsCString(); + m_full_name = ConstString(qual.c_str()); + } else { + m_full_name = m_name; + } + } + return m_full_name; + } + + RustDeclContext *Context() const { + // Always succeeds. + return m_parent->AsDeclContext(); + } + + virtual RustDecl *AsDecl() { return nullptr; } + virtual RustDeclContext *AsDeclContext() { return nullptr; } + + virtual ~RustDeclBase() { } + +protected: + + RustDeclBase(const ConstString &name, RustDeclBase *parent) + : m_name(name), + m_parent(parent) + { + } + +private: + + ConstString m_name; + // This is really a RustDeclContext. + RustDeclBase *m_parent; + ConstString m_full_name; +}; + +class RustDeclContext : public RustDeclBase { +public: + RustDeclContext(const ConstString &name, RustDeclContext *parent) + : RustDeclBase(name, parent) + { + } + + RustDeclContext *AsDeclContext() override { return this; } + + RustDeclBase *FindByName(const ConstString &name) { + auto iter = m_decls.find(name); + if (iter == m_decls.end()) { + return nullptr; + } + return iter->second.get(); + } + + void AddItem(std::unique_ptr &&item) { + ConstString name = item->Name(); + m_decls[name] = std::move(item); + } + +private: + std::map> m_decls; +}; + +class RustDecl : public RustDeclBase { +public: + RustDecl(const ConstString &name, const ConstString &mangled, RustDeclContext *parent) + : RustDeclBase(name, parent), + m_mangled(mangled) + { + assert(parent); + } + + RustDecl *AsDecl() override { return this; } + + ConstString MangledName() const { + return m_mangled; + } + +private: + + ConstString m_mangled; +}; + +} // namespace lldb_private +using namespace lldb_private; + +RustASTContext::RustASTContext() + : TypeSystem(eKindRust), + m_pointer_byte_size(0) +{ +} + +RustASTContext::~RustASTContext() {} + +//------------------------------------------------------------------ +// PluginInterface functions +//------------------------------------------------------------------ + +ConstString RustASTContext::GetPluginNameStatic() { + return ConstString("rust"); +} + +ConstString RustASTContext::GetPluginName() { + return RustASTContext::GetPluginNameStatic(); +} + +uint32_t RustASTContext::GetPluginVersion() { + return 1; +} + +lldb::TypeSystemSP RustASTContext::CreateInstance(lldb::LanguageType language, + Module *module, + Target *target) { + if (language == eLanguageTypeRust) { + ArchSpec arch; + std::shared_ptr astc; + if (module) { + arch = module->GetArchitecture(); + astc = std::shared_ptr(new RustASTContext); + } else if (target) { + arch = target->GetArchitecture(); + astc = std::shared_ptr( + new RustASTContextForExpr(target->shared_from_this())); + } + + if (arch.IsValid()) { + astc->SetAddressByteSize(arch.GetAddressByteSize()); + return astc; + } + } + return lldb::TypeSystemSP(); +} + +void RustASTContext::EnumerateSupportedLanguages( + std::set &languages_for_types, + std::set &languages_for_expressions) { + static std::vector s_supported_languages_for_types( + {lldb::eLanguageTypeRust}); + + static std::vector s_supported_languages_for_expressions( + {}); + + languages_for_types.insert(s_supported_languages_for_types.begin(), + s_supported_languages_for_types.end()); + languages_for_expressions.insert( + s_supported_languages_for_expressions.begin(), + s_supported_languages_for_expressions.end()); +} + +void RustASTContext::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), "Rust AST context plug-in", + CreateInstance, EnumerateSupportedLanguages); +} + +void RustASTContext::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} + +//---------------------------------------------------------------------- +// Tests +//---------------------------------------------------------------------- + +bool RustASTContext::IsArrayType(lldb::opaque_compiler_type_t type, + CompilerType *element_type, uint64_t *size, + bool *is_incomplete) { + if (element_type) + element_type->Clear(); + if (size) + *size = 0; + if (is_incomplete) + *is_incomplete = false; + RustArray *array = static_cast(type)->AsArray(); + if (array) { + if (size) + *size = array->Length(); + if (element_type) + *element_type = array->ElementType(); + return true; + } + return false; +} + +bool RustASTContext::IsVectorType(lldb::opaque_compiler_type_t type, + CompilerType *element_type, uint64_t *size) { + if (element_type) + element_type->Clear(); + if (size) + *size = 0; + return false; +} + +bool RustASTContext::IsAggregateType(lldb::opaque_compiler_type_t type) { + return static_cast(type)->IsAggregateType(); +} + +bool RustASTContext::IsBeingDefined(lldb::opaque_compiler_type_t type) { + return false; +} + +bool RustASTContext::IsCharType(lldb::opaque_compiler_type_t type) { + return static_cast(type)->IsCharType(); +} + +bool RustASTContext::IsCompleteType(lldb::opaque_compiler_type_t type) { + return bool(type); +} + +bool RustASTContext::IsConst(lldb::opaque_compiler_type_t type) { + return false; +} + +bool RustASTContext::IsCStringType(lldb::opaque_compiler_type_t type, + uint32_t &length) { + return false; +} + +bool RustASTContext::IsDefined(lldb::opaque_compiler_type_t type) { + return type != nullptr; +} + +bool RustASTContext::IsFloatingPointType(lldb::opaque_compiler_type_t type, + uint32_t &count, bool &is_complex) { + is_complex = false; + if (static_cast(type)->IsFloatType()) { + count = 1; + return true; + } + count = 0; + return false; +} + +bool RustASTContext::IsFunctionType(lldb::opaque_compiler_type_t type, + bool *is_variadic_ptr) { + if (is_variadic_ptr) + *is_variadic_ptr = false; + return static_cast(type)->AsFunction() != nullptr; +} + +uint32_t RustASTContext::IsHomogeneousAggregate(lldb::opaque_compiler_type_t type, + CompilerType *base_type_ptr) { + // FIXME should detect "homogeneous floating-point aggregates". + return false; +} + +size_t +RustASTContext::GetNumberOfFunctionArguments(lldb::opaque_compiler_type_t type) { + RustFunction *func = static_cast(type)->AsFunction(); + if (func) { + return func->ArgumentCount(); + } + return -1; +} + +CompilerType +RustASTContext::GetFunctionArgumentAtIndex(lldb::opaque_compiler_type_t type, + const size_t index) { + RustFunction *func = static_cast(type)->AsFunction(); + if (func) { + return func->Argument(index); + } + return CompilerType(); +} + +bool RustASTContext::IsFunctionPointerType(lldb::opaque_compiler_type_t type) { + CompilerType pointee; + if (!IsPointerType(type, &pointee)) { + return false; + } + return pointee.IsFunctionType(); +} + +bool RustASTContext::IsBlockPointerType(lldb::opaque_compiler_type_t type, + CompilerType *function_pointer_type_ptr) { + return false; +} + +bool RustASTContext::IsIntegerType(lldb::opaque_compiler_type_t type, + bool &is_signed) { + if (!type) + return false; + + RustIntegral *inttype = static_cast(type)->AsInteger(); + if (inttype) { + is_signed = inttype->IsSigned(); + return true; + } + return false; +} + +bool RustASTContext::IsPolymorphicClass(lldb::opaque_compiler_type_t type) { + return false; +} + +bool RustASTContext::IsPossibleDynamicType(lldb::opaque_compiler_type_t type, + CompilerType *target_type, // Can pass NULL + bool check_cplusplus, bool check_objc) { + if (target_type) + target_type->Clear(); + // FIXME eventually we'll handle trait object pointers here + if (static_cast(type)->AsEnum()) { + return true; + } + return false; +} + +bool RustASTContext::IsRuntimeGeneratedType(lldb::opaque_compiler_type_t type) { + return false; +} + +bool RustASTContext::IsPointerType(lldb::opaque_compiler_type_t type, + CompilerType *pointee_type) { + if (!type) + return false; + if (RustPointer *ptr = static_cast(type)->AsPointer()) { + if (pointee_type) + *pointee_type = ptr->PointeeType(); + return true; + } + return false; +} + +bool RustASTContext::IsPointerOrReferenceType(lldb::opaque_compiler_type_t type, + CompilerType *pointee_type) { + return IsPointerType(type, pointee_type); +} + +bool RustASTContext::IsReferenceType(lldb::opaque_compiler_type_t type, + CompilerType *pointee_type, + bool *is_rvalue) { + return false; +} + +bool RustASTContext::IsScalarType(lldb::opaque_compiler_type_t type) { + return !IsAggregateType(type); +} + +bool RustASTContext::IsTypedefType(lldb::opaque_compiler_type_t type) { + if (type) + return static_cast(type)->AsTypedef() != nullptr; + return false; +} + +bool RustASTContext::IsBooleanType(lldb::opaque_compiler_type_t type) { + if (type) + return static_cast(type)->AsBool() != nullptr; + return false; +} + +bool RustASTContext::IsVoidType(lldb::opaque_compiler_type_t type) { + if (!type) + return false; + RustTuple *tuple = static_cast(type)->AsTuple(); + return tuple && !tuple->Name().IsEmpty() && + strcmp(tuple->Name().AsCString(), "()") == 0 && tuple->FieldCount() == 0; +} + +bool RustASTContext::SupportsLanguage(lldb::LanguageType language) { + return language == eLanguageTypeRust; +} + +//---------------------------------------------------------------------- +// Type Completion +//---------------------------------------------------------------------- + +bool RustASTContext::GetCompleteType(lldb::opaque_compiler_type_t type) { + return bool(type); +} + +//---------------------------------------------------------------------- +// AST related queries +//---------------------------------------------------------------------- + +uint32_t RustASTContext::GetPointerByteSize() { + return m_pointer_byte_size; +} + +//---------------------------------------------------------------------- +// Accessors +//---------------------------------------------------------------------- + +ConstString RustASTContext::GetTypeName(lldb::opaque_compiler_type_t type) { + if (type) + return static_cast(type)->Name(); + return ConstString(); +} + +uint32_t +RustASTContext::GetTypeInfo(lldb::opaque_compiler_type_t type, + CompilerType *pointee_or_element_compiler_type) { + if (pointee_or_element_compiler_type) + pointee_or_element_compiler_type->Clear(); + if (!type) + return 0; + return static_cast(type)->TypeInfo(pointee_or_element_compiler_type); +} + +lldb::TypeClass RustASTContext::GetTypeClass(lldb::opaque_compiler_type_t type) { + if (!type) + return eTypeClassInvalid; + return static_cast(type)->TypeClass(); +} + +lldb::BasicType +RustASTContext::GetBasicTypeEnumeration(lldb::opaque_compiler_type_t type) { + ConstString name = GetTypeName(type); + if (name.IsEmpty()) { + // Nothing. + } else if (strcmp(name.AsCString(), "()") == 0) { + return eBasicTypeVoid; + } else if (strcmp(name.AsCString(), "bool") == 0) { + return eBasicTypeBool; + } + return eBasicTypeInvalid; +} + +lldb::LanguageType +RustASTContext::GetMinimumLanguage(lldb::opaque_compiler_type_t type) { + return lldb::eLanguageTypeRust; +} + +unsigned RustASTContext::GetTypeQualifiers(lldb::opaque_compiler_type_t type) { + return 0; +} + +//---------------------------------------------------------------------- +// Creating related types +//---------------------------------------------------------------------- + +CompilerType +RustASTContext::GetArrayElementType(lldb::opaque_compiler_type_t type, + uint64_t *stride) { + RustArray *array = static_cast(type)->AsArray(); + if (array) { + if (stride) { + *stride = array->ElementType().GetByteSize(nullptr); + } + return array->ElementType(); + } + return CompilerType(); +} + +CompilerType RustASTContext::GetCanonicalType(lldb::opaque_compiler_type_t type) { + RustTypedef *t = static_cast(type)->AsTypedef(); + if (t) + return t->UnderlyingType(); + return CompilerType(this, type); +} + +CompilerType +RustASTContext::GetFullyUnqualifiedType(lldb::opaque_compiler_type_t type) { + return CompilerType(this, type); +} + +// Returns -1 if this isn't a function or if the function doesn't have a +// prototype. +// Returns a value >= 0 if there is a prototype. +int RustASTContext::GetFunctionArgumentCount(lldb::opaque_compiler_type_t type) { + return GetNumberOfFunctionArguments(type); +} + +CompilerType +RustASTContext::GetFunctionArgumentTypeAtIndex(lldb::opaque_compiler_type_t type, + size_t idx) { + return GetFunctionArgumentAtIndex(type, idx); +} + +CompilerType +RustASTContext::GetFunctionReturnType(lldb::opaque_compiler_type_t type) { + if (type) { + RustFunction *t = static_cast(type)->AsFunction(); + if (t) { + return t->ReturnType(); + } + } + return CompilerType(); +} + +size_t RustASTContext::GetNumMemberFunctions(lldb::opaque_compiler_type_t type) { + return 0; +} + +TypeMemberFunctionImpl +RustASTContext::GetMemberFunctionAtIndex(lldb::opaque_compiler_type_t type, + size_t idx) { + return TypeMemberFunctionImpl(); +} + +CompilerType +RustASTContext::GetNonReferenceType(lldb::opaque_compiler_type_t type) { + return CompilerType(this, type); +} + +CompilerType RustASTContext::GetPointeeType(lldb::opaque_compiler_type_t type) { + if (!type) + return CompilerType(); + RustPointer *p = static_cast(type)->AsPointer(); + if (p) + return p->PointeeType(); + return CompilerType(); +} + +CompilerType RustASTContext::GetPointerType(lldb::opaque_compiler_type_t type) { + ConstString type_name = GetTypeName(type); + // Arbitrarily look for a raw pointer here. + ConstString pointer_name(std::string("*mut ") + type_name.GetCString()); + return CreatePointerType(pointer_name, CompilerType(this, type), m_pointer_byte_size); +} + +// If the current object represents a typedef type, get the underlying type +CompilerType RustASTContext::GetTypedefedType(lldb::opaque_compiler_type_t type) { + if (type) { + RustTypedef *t = static_cast(type)->AsTypedef(); + if (t) + return t->UnderlyingType(); + } + return CompilerType(); +} + +//---------------------------------------------------------------------- +// Create related types using the current type's AST +//---------------------------------------------------------------------- +CompilerType RustASTContext::GetBasicTypeFromAST(lldb::BasicType basic_type) { + return CompilerType(); +} + +CompilerType +RustASTContext::GetBuiltinTypeForEncodingAndBitSize(lldb::Encoding encoding, + size_t bit_size) { + return CompilerType(); +} + +//---------------------------------------------------------------------- +// Exploring the type +//---------------------------------------------------------------------- + +uint64_t RustASTContext::GetBitSize(lldb::opaque_compiler_type_t type, + ExecutionContextScope *exe_scope) { + if (!type) + return 0; + RustType *t = static_cast(type); + return t->ByteSize() * 8; +} + +lldb::Encoding RustASTContext::GetEncoding(lldb::opaque_compiler_type_t type, + uint64_t &count) { + count = 1; + bool is_signed; + if (IsIntegerType(type, is_signed)) { + return is_signed ? eEncodingSint : eEncodingUint; + } + if (IsBooleanType(type)) { + return eEncodingUint; + } + bool is_complex; + uint32_t complex_count; + if (IsFloatingPointType(type, complex_count, is_complex)) { + count = complex_count; + return eEncodingIEEE754; + } + if (IsPointerType(type)) + return eEncodingUint; + return eEncodingInvalid; +} + +lldb::Format RustASTContext::GetFormat(lldb::opaque_compiler_type_t type) { + if (!type) + return eFormatDefault; + return static_cast(type)->Format(); +} + +size_t RustASTContext::GetTypeBitAlign(lldb::opaque_compiler_type_t type) { + return 0; +} + +uint32_t RustASTContext::GetNumChildren(lldb::opaque_compiler_type_t type, + bool omit_empty_base_classes) { + if (!type) + return 0; + + RustType *t = static_cast(type); + uint32_t result = 0; + if (RustPointer *ptr = t->AsPointer()) { + result = ptr->PointeeType().GetNumChildren(omit_empty_base_classes); + // If the pointee is not an aggregate, return 1 because the + // pointer has a child. Not totally sure this makes sense. + if (result == 0) + result = 1; + } else if (RustArray *array = t->AsArray()) { + result = array->Length(); + } else if (RustTypedef *typ = t->AsTypedef()) { + result = typ->UnderlyingType().GetNumChildren(omit_empty_base_classes); + } else if (RustAggregateBase *agg = t->AsAggregate()) { + result = agg->FieldCount(); + } + + return result; +} + +uint32_t RustASTContext::GetNumFields(lldb::opaque_compiler_type_t type) { + if (!type) + return 0; + RustType *t = static_cast(type); + if (RustTypedef *tdef = t->AsTypedef()) + return tdef->UnderlyingType().GetNumFields(); + if (RustAggregateBase *a = t->AsAggregate()) + return a->FieldCount(); + return 0; +} + +CompilerType RustASTContext::GetFieldAtIndex(lldb::opaque_compiler_type_t type, + size_t idx, std::string &name, + uint64_t *bit_offset_ptr, + uint32_t *bitfield_bit_size_ptr, + bool *is_bitfield_ptr) { + if (bit_offset_ptr) + *bit_offset_ptr = 0; + if (bitfield_bit_size_ptr) + *bitfield_bit_size_ptr = 0; + if (is_bitfield_ptr) + *is_bitfield_ptr = false; + + if (!type || !GetCompleteType(type)) + return CompilerType(); + + RustType *t = static_cast(type); + if (RustTypedef *typ = t->AsTypedef()) + return typ->UnderlyingType().GetFieldAtIndex( + idx, name, bit_offset_ptr, bitfield_bit_size_ptr, is_bitfield_ptr); + + if (RustAggregateBase *s = t->AsAggregate()) { + const auto *field = s->FieldAt(idx); + if (field) { + name = field->m_name.GetStringRef(); + if (bit_offset_ptr) + *bit_offset_ptr = field->m_offset * 8; + return field->m_type; + } + } + return CompilerType(); +} + +CompilerType RustASTContext::GetChildCompilerTypeAtIndex( + lldb::opaque_compiler_type_t type, ExecutionContext *exe_ctx, size_t idx, + bool transparent_pointers, bool omit_empty_base_classes, + bool ignore_array_bounds, std::string &child_name, + uint32_t &child_byte_size, int32_t &child_byte_offset, + uint32_t &child_bitfield_bit_size, uint32_t &child_bitfield_bit_offset, + bool &child_is_base_class, bool &child_is_deref_of_parent, + ValueObject *valobj, uint64_t &language_flags) { + child_name.clear(); + child_byte_size = 0; + child_byte_offset = 0; + child_bitfield_bit_size = 0; + child_bitfield_bit_offset = 0; + child_is_base_class = false; + child_is_deref_of_parent = false; + language_flags = 0; + + if (!type || !GetCompleteType(type)) + return CompilerType(); + + RustType *t = static_cast(type); + if (t->AsAggregate()) { + uint64_t bit_offset; + CompilerType ret = + GetFieldAtIndex(type, idx, child_name, &bit_offset, nullptr, nullptr); + child_byte_size = ret.GetByteSize( + exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr); + child_byte_offset = bit_offset / 8; + return ret; + } else if (RustPointer *ptr = t->AsPointer()) { + CompilerType pointee = ptr->PointeeType(); + if (!pointee.IsValid() || pointee.IsVoidType()) + return CompilerType(); + if (transparent_pointers && pointee.IsAggregateType()) { + bool tmp_child_is_deref_of_parent = false; + return pointee.GetChildCompilerTypeAtIndex( + exe_ctx, idx, transparent_pointers, omit_empty_base_classes, + ignore_array_bounds, child_name, child_byte_size, child_byte_offset, + child_bitfield_bit_size, child_bitfield_bit_offset, + child_is_base_class, tmp_child_is_deref_of_parent, valobj, + language_flags); + } else { + child_is_deref_of_parent = true; + const char *parent_name = valobj ? valobj->GetName().GetCString() : NULL; + if (parent_name) { + child_name.assign(1, '*'); + child_name += parent_name; + } + + // We have a pointer to an simple type + if (idx == 0 && pointee.GetCompleteType()) { + child_byte_size = pointee.GetByteSize( + exe_ctx ? exe_ctx->GetBestExecutionContextScope() : NULL); + child_byte_offset = 0; + return pointee; + } + } + } else if (RustArray *a = t->AsArray()) { + if (ignore_array_bounds || idx < a->Length()) { + CompilerType element_type = a->ElementType(); + if (element_type.GetCompleteType()) { + char element_name[64]; + ::snprintf(element_name, sizeof(element_name), "[%zu]", idx); + child_name.assign(element_name); + child_byte_size = element_type.GetByteSize( + exe_ctx ? exe_ctx->GetBestExecutionContextScope() : NULL); + child_byte_offset = (int32_t)idx * (int32_t)child_byte_size; + return element_type; + } + } + } else if (RustTypedef *typ = t->AsTypedef()) { + return typ->UnderlyingType().GetChildCompilerTypeAtIndex( + exe_ctx, idx, transparent_pointers, omit_empty_base_classes, + ignore_array_bounds, child_name, child_byte_size, child_byte_offset, + child_bitfield_bit_size, child_bitfield_bit_offset, child_is_base_class, + child_is_deref_of_parent, valobj, language_flags); + } + return CompilerType(); +} + +// Lookup a child given a name. This function will match base class names +// and member member names in "clang_type" only, not descendants. +uint32_t +RustASTContext::GetIndexOfChildWithName(lldb::opaque_compiler_type_t type, + const char *name, + bool omit_empty_base_classes) { + if (!type || !GetCompleteType(type)) + return UINT_MAX; + + RustType *t = static_cast(type); + if (RustAggregateBase *agg = t->AsAggregate()) { + for (uint32_t i = 0; i < agg->FieldCount(); ++i) { + const RustAggregateBase::Field *f = agg->FieldAt(i); + if (f->m_name.GetStringRef() == name) + return i; + } + } else if (RustPointer *typ = t->AsPointer()) { + return typ->PointeeType().GetIndexOfChildWithName(name, omit_empty_base_classes); + } + return UINT_MAX; +} + +// Lookup a child member given a name. This function will match member names +// only and will descend into "clang_type" children in search for the first +// member in this class, or any base class that matches "name". +// TODO: Return all matches for a given name by returning a +// vector> +// so we catch all names that match a given child name, not just the first. +size_t RustASTContext::GetIndexOfChildMemberWithName( + lldb::opaque_compiler_type_t type, const char *name, + bool omit_empty_base_classes, std::vector &child_indexes) { + uint32_t index = GetIndexOfChildWithName(type, name, omit_empty_base_classes); + if (index == UINT_MAX) + return 0; + child_indexes.push_back(index); + return 1; +} + +// Converts "s" to a floating point value and place resulting floating +// point bytes in the "dst" buffer. +size_t +RustASTContext::ConvertStringToFloatValue(lldb::opaque_compiler_type_t type, + const char *s, uint8_t *dst, + size_t dst_size) { + assert(false); + return 0; +} + +//---------------------------------------------------------------------- +// Dumping types +//---------------------------------------------------------------------- +#define DEPTH_INCREMENT 2 + +void RustASTContext::DumpValue(lldb::opaque_compiler_type_t type, + ExecutionContext *exe_ctx, Stream *s, + lldb::Format format, const DataExtractor &data, + lldb::offset_t data_byte_offset, + size_t data_byte_size, uint32_t bitfield_bit_size, + uint32_t bitfield_bit_offset, bool show_types, + bool show_summary, bool verbose, uint32_t depth) { + // This doesn't seem to be needed. + assert(false && "Not implemented"); +} + +bool RustASTContext::DumpTypeValue(lldb::opaque_compiler_type_t type, Stream *s, + lldb::Format format, const DataExtractor &data, + lldb::offset_t byte_offset, size_t byte_size, + uint32_t bitfield_bit_size, + uint32_t bitfield_bit_offset, + ExecutionContextScope *exe_scope) { + if (!type) + return false; + if (IsAggregateType(type)) { + return false; + } else { + RustType *t = static_cast(type); + if (RustTypedef *typ = t->AsTypedef()) { + CompilerType typedef_compiler_type = typ->UnderlyingType(); + if (format == eFormatDefault) + format = typedef_compiler_type.GetFormat(); + uint64_t typedef_byte_size = typedef_compiler_type.GetByteSize(exe_scope); + + return typedef_compiler_type.DumpTypeValue( + s, + format, // The format with which to display the element + data, // Data buffer containing all bytes for this type + byte_offset, // Offset into "data" where to grab value from + typedef_byte_size, // Size of this type in bytes + bitfield_bit_size, // Size in bits of a bitfield value, if zero don't + // treat as a bitfield + bitfield_bit_offset, // Offset in bits of a bitfield value if + // bitfield_bit_size != 0 + exe_scope); + } + + if (format == eFormatEnum || format == eFormatDefault) { + if (RustCLikeEnum *clike = t->AsCLikeEnum()) { + uint64_t value; + if (clike->IsSigned()) { + int64_t svalue = data.GetMaxS64Bitfield(&byte_offset, byte_size, + bitfield_bit_size, + bitfield_bit_offset); + value = uint64_t(svalue); + } else { + value = data.GetMaxU64Bitfield(&byte_offset, byte_size, + bitfield_bit_size, + bitfield_bit_offset); + } + + std::string name; + if (clike->FindName(value, name)) { + s->Printf("%s::%s", clike->Name().AsCString(), name.c_str()); + } else { + // If the value couldn't be found, then something went wrong + // we should inform the user. + s->Printf("(invalid enum value) %" PRIu64, value); + } + return true; + } + } else if (format == eFormatUnicode32) { + if (RustIntegral *intlike = t->AsInteger()) { + if (intlike->IsCharType()) { + uint64_t value = data.GetMaxU64Bitfield(&byte_offset, byte_size, + bitfield_bit_size, + bitfield_bit_offset); + switch (value) { + case '\n': + s->PutCString("'\\n'"); + break; + case '\r': + s->PutCString("'\\r'"); + break; + case '\t': + s->PutCString("'\\t'"); + break; + case '\\': + s->PutCString("'\\\\'"); + break; + case '\0': + s->PutCString("'\\0'"); + break; + case '\'': + s->PutCString("'\\''"); + break; + + default: + if (value < 128 && isprint(value)) { + s->Printf("'%c'", char(value)); + } else { + s->Printf("'\\u{%x}'", unsigned(value)); + } + break; + } + + return true; + } + } + } + + uint32_t item_count = 1; + switch (format) { + default: + case eFormatBoolean: + case eFormatBinary: + case eFormatComplex: + case eFormatCString: + case eFormatDecimal: + case eFormatEnum: + case eFormatHex: + case eFormatHexUppercase: + case eFormatFloat: + case eFormatOctal: + case eFormatOSType: + case eFormatUnsigned: + case eFormatPointer: + case eFormatVectorOfChar: + case eFormatVectorOfSInt8: + case eFormatVectorOfUInt8: + case eFormatVectorOfSInt16: + case eFormatVectorOfUInt16: + case eFormatVectorOfSInt32: + case eFormatVectorOfUInt32: + case eFormatVectorOfSInt64: + case eFormatVectorOfUInt64: + case eFormatVectorOfFloat32: + case eFormatVectorOfFloat64: + case eFormatVectorOfUInt128: + break; + + case eFormatChar: + case eFormatCharPrintable: + case eFormatCharArray: + case eFormatBytes: + case eFormatBytesWithASCII: + item_count = byte_size; + byte_size = 1; + break; + + case eFormatUnicode16: + item_count = byte_size / 2; + byte_size = 2; + break; + + case eFormatUnicode32: + item_count = byte_size / 4; + byte_size = 4; + break; + } + return DumpDataExtractor(data, s, byte_offset, format, byte_size, item_count, + UINT32_MAX, LLDB_INVALID_ADDRESS, bitfield_bit_size, + bitfield_bit_offset, exe_scope); + } + return 0; +} + +void RustASTContext::DumpSummary(lldb::opaque_compiler_type_t type, + ExecutionContext *exe_ctx, Stream *s, + const DataExtractor &data, + lldb::offset_t data_offset, + size_t data_byte_size) { + // Apparently there is nothing to do here. +} + +void RustASTContext::DumpTypeDescription(lldb::opaque_compiler_type_t type) { + // Dump to stdout + StreamFile s(stdout, false); + DumpTypeDescription(type, &s); +} + +void RustASTContext::DumpTypeDescription(lldb::opaque_compiler_type_t type, Stream *s) { + if (!type) + return; + ConstString name = GetTypeName(type); + RustType *t = static_cast(type); + + if (RustAggregateBase *agg = t->AsAggregate()) { + s->PutCString(agg->Tag()); + const char *name = agg->TagName(); + s->PutCString(name); + if (*name) { + s->PutCString(" "); + } + s->PutCString(agg->Opener()); + if (agg->FieldCount() == 0) { + s->PutCString(agg->Closer()); + return; + } + s->IndentMore(); + // A trailing comma looks weird for tuples, so we keep track and + // don't emit it. + bool first = true; + for (auto &&field : *agg) { + if (!first) { + s->PutChar(','); + } + first = false; + s->PutChar('\n'); + s->Indent(); + if (!field.m_name.IsEmpty()) { + s->PutCString(field.m_name.AsCString()); + s->PutCString(": "); + } + s->PutCString(field.m_type.GetTypeName().AsCString()); + } + s->IndentLess(); + s->PutChar('\n'); + s->Indent(agg->Closer()); + return; + } + + s->PutCString(name.AsCString()); +} + +RustType *RustASTContext::FindCachedType(const lldb_private::ConstString &name) { + if (name.IsEmpty()) + return nullptr; + auto result = m_types.find(name); + if (result == m_types.end ()) + return nullptr; + return result->second.get(); +} + +CompilerType RustASTContext::CacheType(const ConstString &name, RustType *new_type) { + if (name.IsEmpty()) { + // Be sure to keep nameless types alive. + m_anon_types.insert(std::unique_ptr(new_type)); + } else { + m_types[name].reset(new_type); + } + return CompilerType(this, new_type); +} + +CompilerType RustASTContext::CreateBoolType(const lldb_private::ConstString &name) { + if (RustType *cached = FindCachedType(name)) + return CompilerType(this, cached); + RustType *type = new RustBool(name); + return CacheType(name, type); +} + +CompilerType RustASTContext::CreateIntegralType(const lldb_private::ConstString &name, + bool is_signed, + uint64_t byte_size, + bool is_char_type) { + if (RustType *cached = FindCachedType(name)) + return CompilerType(this, cached); + RustType *type = new RustIntegral(name, is_signed, byte_size, is_char_type); + return CacheType(name, type); +} + +CompilerType RustASTContext::CreateIntrinsicIntegralType(bool is_signed, uint64_t byte_size) { + char name[100]; + snprintf(name, sizeof(name), "%s%d", is_signed ? "i" : "u", int(byte_size * 8)); + + ConstString cname(name); + return CreateIntegralType(cname, is_signed, byte_size); +} + +CompilerType RustASTContext::CreateCharType() { + ConstString cname("char"); + return CreateIntegralType(cname, false, 4, true); +} + +CompilerType RustASTContext::CreateFloatType(const lldb_private::ConstString &name, + uint64_t byte_size) { + if (RustType *cached = FindCachedType(name)) + return CompilerType(this, cached); + RustType *type = new RustFloat(name, byte_size); + return CacheType(name, type); +} + +CompilerType RustASTContext::CreateArrayType(const CompilerType &element_type, + uint64_t length) { + std::string name = std::string("[") + element_type.GetTypeName().AsCString(); + if (length != 0) { + name = name + "; " + std::to_string(length); + } + name += "]"; + ConstString newname(name); + + if (RustType *cached = FindCachedType(newname)) + return CompilerType(this, cached); + RustType *type = new RustArray(newname, length, element_type); + return CacheType(newname, type); +} + +CompilerType RustASTContext::CreateTypedefType(const ConstString &name, CompilerType impl) { + if (RustType *cached = FindCachedType(name)) + return CompilerType(this, cached); + RustType *type = new RustTypedef(name, impl); + return CacheType(name, type); +} + +CompilerType +RustASTContext::CreateStructType(const lldb_private::ConstString &name, uint32_t byte_size, + bool has_discriminant) { + if (RustType *cached = FindCachedType(name)) + return CompilerType(this, cached); + RustType *type = new RustStruct(name, byte_size, has_discriminant); + return CacheType(name, type); +} + +CompilerType +RustASTContext::CreateTupleType(const lldb_private::ConstString &name, uint32_t byte_size, + bool has_discriminant) { + if (RustType *cached = FindCachedType(name)) + return CompilerType(this, cached); + RustType *type = new RustTuple(name, byte_size, has_discriminant); + return CacheType(name, type); +} + +CompilerType +RustASTContext::CreateUnionType(const lldb_private::ConstString &name, uint32_t byte_size) { + if (RustType *cached = FindCachedType(name)) + return CompilerType(this, cached); + RustType *type = new RustUnion(name, byte_size); + return CacheType(name, type); +} + +CompilerType +RustASTContext::CreatePointerType(const lldb_private::ConstString &name, + const CompilerType &pointee_type, + uint32_t byte_size) { + if (RustType *cached = FindCachedType(name)) + return CompilerType(this, cached); + RustType *type = new RustPointer(name, pointee_type, byte_size); + return CacheType(name, type); +} + +void RustASTContext::AddFieldToStruct(const CompilerType &struct_type, + const lldb_private::ConstString &name, + const CompilerType &field_type, + uint32_t byte_offset, + bool is_default, uint64_t discriminant) { + if (!struct_type) + return; + RustASTContext *ast = + llvm::dyn_cast_or_null(struct_type.GetTypeSystem()); + if (!ast) + return; + RustType *type = static_cast(struct_type.GetOpaqueQualType()); + if (RustAggregateBase *a = type->AsAggregate()) { + a->AddField(name, field_type, byte_offset); + if (RustEnum *e = type->AsEnum()) { + e->RecordDiscriminant(is_default, discriminant); + } + } +} + +CompilerType +RustASTContext::CreateFunctionType(const lldb_private::ConstString &name, + const CompilerType &return_type, + const std::vector &¶ms) { + if (RustType *cached = FindCachedType(name)) + return CompilerType(this, cached); + RustType *type = new RustFunction(name, m_pointer_byte_size, return_type, std::move(params)); + return CacheType(name, type); +} + +CompilerType +RustASTContext::CreateVoidType() { + ConstString name("()"); + if (RustType *cached = FindCachedType(name)) + return CompilerType(this, cached); + RustType *type = new RustTuple(name, 0, false); + return CacheType(name, type); +} + +CompilerType +RustASTContext::CreateEnumType(const lldb_private::ConstString &name, + uint64_t byte_size, uint32_t discr_offset, + uint32_t discr_byte_size) { + if (RustType *cached = FindCachedType(name)) + return CompilerType(this, cached); + RustType *type = new RustEnum(name, byte_size, discr_offset, discr_byte_size); + return CacheType(name, type); +} + +CompilerType +RustASTContext::CreateCLikeEnumType(const lldb_private::ConstString &name, + const CompilerType &underlying_type, + std::map &&values) { + if (RustType *cached = FindCachedType(name)) + return CompilerType(this, cached); + RustType *type = new RustCLikeEnum(name, underlying_type, std::move(values)); + return CacheType(name, type); +} + +bool +RustASTContext::IsTupleType(const CompilerType &type) { + if (!type) + return false; + RustASTContext *ast = llvm::dyn_cast_or_null(type.GetTypeSystem()); + if (!ast) + return false; + RustType *rtype = static_cast(type.GetOpaqueQualType()); + return bool(rtype->AsTuple()); +} + +bool +RustASTContext::TypeHasDiscriminant(const CompilerType &type) { + if (!type) + return false; + RustASTContext *ast = llvm::dyn_cast_or_null(type.GetTypeSystem()); + if (!ast) + return false; + RustType *rtype = static_cast(type.GetOpaqueQualType()); + if (RustAggregateBase *a = rtype->AsAggregate()) + return a->HasDiscriminant(); + return false; +} + +bool +RustASTContext::GetEnumDiscriminantLocation(const CompilerType &type, uint64_t &discr_offset, + uint64_t &discr_byte_size) { + if (!type) + return false; + RustASTContext *ast = llvm::dyn_cast_or_null(type.GetTypeSystem()); + if (!ast) + return false; + RustType *rtype = static_cast(type.GetOpaqueQualType()); + if (RustEnum *e = rtype->AsEnum()) { + e->GetDiscriminantLocation(discr_offset, discr_byte_size); + return true; + } + return false; +} + +CompilerType +RustASTContext::FindEnumVariant(const CompilerType &type, uint64_t discriminant) { + if (!type) + return CompilerType(); + RustASTContext *ast = llvm::dyn_cast_or_null(type.GetTypeSystem()); + if (!ast) + return CompilerType(); + RustType *rtype = static_cast(type.GetOpaqueQualType()); + if (RustEnum *e = rtype->AsEnum()) { + return e->FindEnumVariant(discriminant); + } + return CompilerType(); +} + +void +RustASTContext::FinishAggregateInitialization(const CompilerType &type) { + if (!type) + return; + RustASTContext *ast = llvm::dyn_cast_or_null(type.GetTypeSystem()); + if (!ast) + return; + RustType *rtype = static_cast(type.GetOpaqueQualType()); + if (RustAggregateBase *a = rtype->AsAggregate()) + a->FinishInitialization(); +} + +DWARFASTParser *RustASTContext::GetDWARFParser() { + if (!m_dwarf_ast_parser_ap) + m_dwarf_ast_parser_ap.reset(new DWARFASTParserRust(*this)); + return m_dwarf_ast_parser_ap.get(); +} + +UserExpression *RustASTContextForExpr::GetUserExpression( + llvm::StringRef expr, llvm::StringRef prefix, lldb::LanguageType language, + Expression::ResultType desired_type, + const EvaluateExpressionOptions &options) { + TargetSP target = m_target_wp.lock(); + if (target) + return new RustUserExpression(*target, expr, prefix, language, desired_type, + options); + return nullptr; +} + +ConstString RustASTContext::DeclGetName(void *opaque_decl) { + RustDecl *dc = (RustDecl *) opaque_decl; + return dc->Name(); +} + +ConstString RustASTContext::DeclGetMangledName(void *opaque_decl) { + RustDecl *dc = (RustDecl *) opaque_decl; + return dc->MangledName(); +} + +CompilerDeclContext RustASTContext::DeclGetDeclContext(void *opaque_decl) { + RustDecl *dc = (RustDecl *) opaque_decl; + return CompilerDeclContext(this, dc->Context()); +} + +ConstString RustASTContext::DeclContextGetName(void *opaque_decl_ctx) { + RustDeclContext *dc = (RustDeclContext *) opaque_decl_ctx; + return dc->Name(); +} + +ConstString RustASTContext::DeclContextGetScopeQualifiedName(void *opaque_decl_ctx) { + RustDeclContext *dc = (RustDeclContext *) opaque_decl_ctx; + return dc->QualifiedName(); +} + +bool RustASTContext::DeclContextIsStructUnionOrClass(void *opaque_decl_ctx) { + // This is not actually correct -- for example an enum arm is nested + // in its containing enum -- but as far as I can tell this result + // doesn't matter for Rust. + return false; +} + +bool RustASTContext::DeclContextIsClassMethod(void *opaque_decl_ctx, + lldb::LanguageType *language_ptr, + bool *is_instance_method_ptr, + ConstString *language_object_name_ptr) { + return false; +} + +std::vector +RustASTContext::DeclContextFindDeclByName(void *opaque_decl_ctx, ConstString name, + const bool ignore_imported_decls) { + std::vector result; + SymbolFile *symbol_file = GetSymbolFile(); + if (symbol_file) { + symbol_file->ParseDeclsForContext(CompilerDeclContext(this, opaque_decl_ctx)); + + RustDeclContext *dc = (RustDeclContext *) opaque_decl_ctx; + RustDeclBase *base = dc->FindByName(name); + if (RustDecl *decl = base ? base->AsDecl() : nullptr) { + result.push_back(CompilerDecl(this, decl)); + } + } + return result; +} + +CompilerDeclContext RustASTContext::GetTranslationUnitDecl() { + if (!m_tu_decl) { + m_tu_decl.reset(new RustDeclContext(ConstString(""), nullptr)); + } + return CompilerDeclContext(this, m_tu_decl.get()); +} + +CompilerDeclContext +RustASTContext::GetNamespaceDecl(CompilerDeclContext parent, const ConstString &name) { + if (!parent) + return CompilerDeclContext(); + RustASTContext *ast = llvm::dyn_cast_or_null(parent.GetTypeSystem()); + if (!ast) + return CompilerDeclContext(); + + RustDeclContext *dc = (RustDeclContext *) parent.GetOpaqueDeclContext(); + RustDeclBase *base = dc->FindByName(name); + if (base) { + if (RustDeclContext *ctx = base->AsDeclContext()) { + return CompilerDeclContext(this, ctx); + } + } + + RustDeclContext *new_ns = new RustDeclContext(name, dc); + dc->AddItem(std::unique_ptr(new_ns)); + return CompilerDeclContext(this, new_ns); +} + +CompilerDeclContext +RustASTContext::GetDeclContextDeclContext(CompilerDeclContext child) { + if (!child) + return CompilerDeclContext(); + RustASTContext *ast = llvm::dyn_cast_or_null(child.GetTypeSystem()); + if (!ast) + return CompilerDeclContext(); + + RustDeclContext *dc = (RustDeclContext *) child.GetOpaqueDeclContext(); + return CompilerDeclContext(this, dc->Context()); +} + +CompilerDecl RustASTContext::GetDecl(CompilerDeclContext parent, const ConstString &name, + const ConstString &mangled) { + if (!parent) + return CompilerDecl(); + RustASTContext *ast = llvm::dyn_cast_or_null(parent.GetTypeSystem()); + if (!ast) + return CompilerDecl(); + + RustDeclContext *dc = (RustDeclContext *) parent.GetOpaqueDeclContext(); + RustDeclBase *base = dc->FindByName(name); + if (base) { + if (RustDecl *ctx = base->AsDecl()) { + return CompilerDecl(this, ctx); + } + } + + RustDecl *new_ns = new RustDecl(name, mangled, dc); + dc->AddItem(std::unique_ptr(new_ns)); + return CompilerDecl(this, new_ns); +} + +bool RustASTContext::GetCABITypeDeclaration(CompilerType type, const std::string &varname, + RustASTContext::TypeNameMap *name_map, + std::string *result) { + if (!type) + return false; + RustASTContext *ast = llvm::dyn_cast_or_null(type.GetTypeSystem()); + if (!ast) + return false; + RustType *rtype = static_cast(type.GetOpaqueQualType()); + *result = rtype->GetCABITypeDeclaration(name_map, varname); + return true; +} diff --git a/lldb/unittests/CMakeLists.txt b/lldb/unittests/CMakeLists.txt index 1ffae834dfb2fc..90041b45b0375d 100644 --- a/lldb/unittests/CMakeLists.txt +++ b/lldb/unittests/CMakeLists.txt @@ -69,6 +69,7 @@ add_subdirectory(Language) add_subdirectory(ObjectFile) add_subdirectory(Platform) add_subdirectory(Process) +add_subdirectory(Rust) add_subdirectory(ScriptInterpreter) add_subdirectory(Signals) add_subdirectory(Symbol) diff --git a/lldb/unittests/Rust/CMakeLists.txt b/lldb/unittests/Rust/CMakeLists.txt new file mode 100644 index 00000000000000..7bf33d9031cba9 --- /dev/null +++ b/lldb/unittests/Rust/CMakeLists.txt @@ -0,0 +1,8 @@ +add_lldb_unittest(RustTests + RustLexTest.cpp RustParseTest.cpp + + LINK_LIBS + lldbPluginExpressionParserRust + lldbPluginLanguageRuntimeRust + lldbCore + ) diff --git a/lldb/unittests/Rust/RustLexTest.cpp b/lldb/unittests/Rust/RustLexTest.cpp new file mode 100644 index 00000000000000..e2e4cc7f5c6a45 --- /dev/null +++ b/lldb/unittests/Rust/RustLexTest.cpp @@ -0,0 +1,171 @@ +//===-- RustLexTest.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" +#include +#include "Plugins/ExpressionParser/Rust/RustLex.h" +#include "lldb/Utility/Status.h" + +using namespace lldb_private; +using namespace lldb_private::rust; + +template +static void +TestLex(const char *input, const std::array &expected) { + Lexer lexer(input); + + for (std::size_t i = 0; i < N; ++i) { + EXPECT_EQ(lexer.Next(), expected[i]) << "tokens differ at index " << i + << " for \"" << input << "\""; + } + EXPECT_EQ(lexer.Next(), Token(THATSALLFOLKS)) << "EOF expected for input \"" + << input << "\""; +} + +static void +TestSingle(const char *input, const Token &token) { + std::array tokens { token }; + TestLex(input, tokens); +} + +TEST(RustLexTest, Keywords) { + TestSingle("as", Token(AS)); + TestSingle("true", Token(TRUE)); + TestSingle("false", Token(FALSE)); + TestSingle("super", Token(SUPER)); + TestSingle("self", Token(SELF)); + TestSingle("mut", Token(MUT)); + TestSingle("const", Token(CONST)); + TestSingle("fn", Token(FN)); + TestSingle("sizeof", Token(SIZEOF)); + TestSingle("..", Token(DOTDOT)); + TestSingle("..=", Token(DOTDOTEQ)); + TestSingle("||", Token(OROR)); + TestSingle("|=", Token(OR_EQ)); + TestSingle("&&", Token(ANDAND)); + TestSingle("&=", Token(AND_EQ)); + TestSingle("^=", Token(XOR_EQ)); + TestSingle("==", Token(EQEQ)); + TestSingle("!=", Token(NOTEQ)); + TestSingle("<=", Token(LTEQ)); + TestSingle(">=", Token(GTEQ)); + TestSingle("<<", Token(LSH)); + TestSingle(">>", Token(RSH)); + TestSingle("+=", Token(PLUS_EQ)); + TestSingle("-=", Token(MINUS_EQ)); + TestSingle("*=", Token(STAR_EQ)); + TestSingle("/=", Token(SLASH_EQ)); + TestSingle("%=", Token(PERCENT_EQ)); + TestSingle("<<=", Token(LSH_EQ)); + TestSingle(">>=", Token(RSH_EQ)); + TestSingle("::", Token(COLONCOLON)); + TestSingle("->", Token(ARROW)); + TestSingle(".", Token('.')); + TestSingle("|", Token('|')); + TestSingle("&", Token('&')); + TestSingle("=", Token('=')); + TestSingle("!", Token('!')); + TestSingle("<", Token('<')); + TestSingle(">", Token('>')); + TestSingle("+", Token('+')); + TestSingle("-", Token('-')); + TestSingle("*", Token('*')); + TestSingle("/", Token('/')); + TestSingle("%", Token('%')); + TestSingle(":", Token(':')); + TestSingle("[", Token('[')); + TestSingle("]", Token(']')); + TestSingle("(", Token('(')); + TestSingle(")", Token(')')); + TestSingle("{", Token('{')); + TestSingle("}", Token('}')); +} + +TEST(RustLexTest, Identifiers) { + TestSingle("one", Token(IDENTIFIER, "one")); + TestSingle("o_e", Token(IDENTIFIER, "o_e")); + TestSingle("_E001__Z", Token(IDENTIFIER, "_E001__Z")); + TestSingle("b", Token(IDENTIFIER, "b")); + TestSingle("br", Token(IDENTIFIER, "br")); + TestSingle("brrrr", Token(IDENTIFIER, "brrrr")); +} + +TEST(RustLexTest, Integers) { + TestSingle("1", Token(INTEGER, uint64_t(1))); + TestSingle("1usize", Token(INTEGER, uint64_t(1), "usize")); + TestSingle("2_33", Token(INTEGER, uint64_t(233))); + TestSingle("2_33i64", Token(INTEGER, uint64_t(233), "i64")); + TestSingle("0x_ff", Token(INTEGER, uint64_t(255))); + TestSingle("0xFf", Token(INTEGER, uint64_t(255))); + TestSingle("0b_1111_1111", Token(INTEGER, uint64_t(255))); + TestSingle("0o11_u64", Token(INTEGER, uint64_t(9), "u64")); +} + +TEST(RustLexTest, Floats) { + TestSingle("1.0", Token(FLOAT, 1.0)); + TestSingle("1.5f32", Token(FLOAT, 1.5, "f32")); + TestSingle("1.5E+02", Token(FLOAT, 150.0)); + TestSingle("1.5e+03", Token(FLOAT, 1500.0)); + TestSingle("1e+03f64", Token(FLOAT, 1000.0, "f64")); +} + +TEST(RustLexTest, ByteLiteral) { + TestSingle("b'b'", Token(BYTE, uint64_t('b'))); + TestSingle("b'\\''", Token(BYTE, uint64_t('\''))); + TestSingle("b'\\x9A'", Token(BYTE, uint64_t(154))); + TestSingle("b'\\x9A", Token(INVALID)); + TestSingle("b'", Token(INVALID)); + // TestSingle("b'\\u{9A}'", Token(INVALID)); +} + +TEST(RustLexTest, Characters) { + TestSingle("'b'", Token(CHAR, uint64_t('b'))); + TestSingle("'\\''", Token(CHAR, uint64_t('\''))); + TestSingle("'\\x9A'", Token(CHAR, uint64_t(154))); + TestSingle("'\\x9A'", Token(CHAR, uint64_t(154))); + TestSingle("'\\x9A", Token(INVALID)); + TestSingle("'\\u{9A}'", Token(CHAR, uint64_t(154))); + TestSingle("'\\u{00009A}'", Token(CHAR, uint64_t(154))); + TestSingle("'\\u{0000fff", Token(INVALID)); + TestSingle("'\\u{0000ff}", Token(INVALID)); + TestSingle("'", Token(INVALID)); +} + +TEST(RustLexTest, Strings) { + TestSingle("\"hi\"", Token(STRING, "hi")); + TestSingle("\"\\u{68}\\u{000069}\"", Token(STRING, "hi")); + TestSingle("\"\\x68\\x69\"", Token(STRING, "hi")); +} + +TEST(RustLexTest, RawStrings) { + TestSingle("r\"hi\"", Token(STRING, "hi")); + TestSingle("r########\"hi\"########", Token(STRING, "hi")); + TestSingle("r########\"h\"#######i\"########", Token(STRING, "h\"#######i")); + TestSingle("r########\"\\'\"########", Token(STRING, "\\'")); +} + +TEST(RustLexTest, RawByteStrings) { + TestSingle("br\"hi\"", Token(BYTESTRING, "hi")); + TestSingle("br########\"hi\"########", Token(BYTESTRING, "hi")); + TestSingle("br########\"h\"#######i\"########", Token(BYTESTRING, "h\"#######i")); + TestSingle("br########\"\\'\"########", Token(BYTESTRING, "\\'")); +} + +TEST(RustLexTest, Streams) { + TestLex("57 as i64", std::array { + Token(INTEGER, uint64_t(57)), + Token(AS), + Token(IDENTIFIER, "i64") + }); + + TestLex("0..", std::array { + Token(INTEGER, uint64_t(0)), + Token(DOTDOT) + }); +} diff --git a/lldb/unittests/Rust/RustParseTest.cpp b/lldb/unittests/Rust/RustParseTest.cpp new file mode 100644 index 00000000000000..af9c6d95c7f407 --- /dev/null +++ b/lldb/unittests/Rust/RustParseTest.cpp @@ -0,0 +1,127 @@ +//===-- RustParseTest.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" +#include +#include "Plugins/ExpressionParser/Rust/RustParse.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/StreamString.h" + +using namespace lldb_private; +using namespace lldb_private::rust; + +static void +TestParse(const char *input, const char *expected) { + Parser parser(nullptr, input); + + Status error; + RustExpressionUP result = parser.ParseFully(error); + + if (expected == nullptr) { + EXPECT_EQ(result.get(), nullptr) << "expected failure for " + << input << ": " << error.AsCString(); + EXPECT_NE(error.AsCString(nullptr), nullptr) << "expected error message for " << input; + } else { + ASSERT_NE(result.get(), nullptr) << "unexpected parse failure for " + << input << ": " << error.AsCString(); + + StreamString str; + str << result; + + EXPECT_STREQ(str.GetData(), expected) << "incorrect parse for " + << input << ": " << error.AsCString(); + } +} + +TEST(RustParseTest, Literals) { + TestParse("1", "1"); + + TestParse("[1,2,3]", "[1, 2, 3]"); + TestParse("[72*3; 8]", "[(72 * 3); 8]"); + TestParse("[72*3; 8*9]", "[(72 * 3); (8 * 9)]"); + + TestParse("()", "()"); + TestParse("(1,)", "(1, )"); + TestParse("(1,2,3)", "(1, 2, 3, )"); + TestParse("true || false", "(true || false)"); + + TestParse("\"hi\"", "\"hi\""); + TestParse("b\"hi\"", "b\"hi\""); + TestParse("r##\"hi\"##", "\"hi\""); + TestParse("br##\"hi\"##", "b\"hi\""); + + TestParse("'c'", "'c'"); + TestParse("b'c'", "b'c'"); + + TestParse("Struct{}", "Struct { }"); + TestParse("Struct{name}", "Struct { name: name }"); + TestParse("Struct{x: 57}", "Struct { x: 57 }"); + TestParse("Struct{x: 57, y: 88, z: 'b'}", "Struct { x: 57, y: 88, z: 'b' }"); + TestParse("Struct{x: 57, ..z}", "Struct { x: 57, .. z }"); +} + +TEST(RustParseTest, Simple) { + TestParse("1 + 2", "(1 + 2)"); + TestParse("1+2*3", "(1 + (2 * 3))"); + TestParse("(1+2)*3", "((1 + 2) * 3)"); + TestParse("1++2", "(1 + + (2))"); + TestParse("1--2", "(1 - - (2))"); + TestParse("1-+-2", "(1 - + (- (2)))"); + TestParse("[1,2,3][5]", "([1, 2, 3] @ 5)"); + // We don't serialize sizeof all that nicely. + TestParse("sizeof(())", "@ (())"); +} + +TEST(RustParseTest, Members) { + TestParse("something.57", "something.57"); + TestParse("something.field", "something.field"); +} + +TEST(RustParseTest, Calls) { + TestParse("func()", "func ()"); + TestParse("func(1,2,'b')", "func (1, 2, 'b')"); + TestParse("s.f(7)", "s.f (7)"); + TestParse("23.mumble(8)", "23.mumble (8)"); +} + +TEST(RustParseTest, Paths) { + TestParse("self", "self"); + TestParse("self::super::hi", "self::super::hi"); + TestParse("self::hi::there", "self::hi::there"); + TestParse("self::super::super::super::hi", "self::super::super::super::hi"); + TestParse("::hi::there", "::hi::there"); + TestParse("hi::there", "hi::there"); + // These lose the turbofish but that doesn't seem very important, as + // this is only visible when dumping the AST. + TestParse("hi::there::", "hi::there"); + TestParse("hi::there::>", "hi::there>"); +} + +TEST(RustParseTest, Types) { + TestParse("32 as usize", "(32 as usize)"); + TestParse("x as (u32, u32, u32)", "(x as (u32, u32, u32))"); + TestParse("x as [bool;7]", "(x as [bool; 7])"); + TestParse("x as &[f64]", "(x as &[f64])"); + TestParse("x as &mut [f64]", "(x as &mut [f64])"); + TestParse("x as &mut f64", "(x as &mut f64)"); + TestParse("x as *mut f64", "(x as *mut f64)"); + TestParse("x as fn(u32, u32)->()", "(x as fn (u32, u32) -> ())"); + TestParse("x as *const [mod::whatever; 8]", "(x as *const [mod::whatever; 8])"); +} + +TEST(RustParseTest, Ranges) { + TestParse("0..32", "(0 .. 32)"); + TestParse("..32", "( .. 32)"); + TestParse("0..", "(0 .. )"); + TestParse("..", "( .. )"); + TestParse("0..=32", "(0 ..= 32)"); + TestParse("..=32", "( ..= 32)"); + TestParse("a+b*c..d%e", "((a + (b * c)) .. (d % e))"); + TestParse("&array[52..]", "& ((array @ (52 .. )))"); +} From b8bd5dc50f6740fbf00d24b6d81eadcd8f72fc01 Mon Sep 17 00:00:00 2001 From: Tom Tromey Date: Mon, 9 Jul 2018 13:07:03 -0600 Subject: [PATCH 12/25] Compute Python library suffix in CMakeLists.txt Introduce LLDB_PY_LIB_SUFFIX and use it in various places in the build. This lets the x.py-based build work properly without having to set LLVM_LIBDIR_SUFFIX. See https://bugs.llvm.org/show_bug.cgi?id=18957 for some discussion. --- lldb/CMakeLists.txt | 9 ++++- lldb/docs/CMakeLists.txt | 2 +- lldb/scripts/CMakeLists.txt | 2 +- .../Python/modules/readline/CMakeLists.txt | 2 +- lldb/scripts/get_libdir_suffix.py | 33 +++++++++++++++++++ 5 files changed, 44 insertions(+), 4 deletions(-) create mode 100644 lldb/scripts/get_libdir_suffix.py diff --git a/lldb/CMakeLists.txt b/lldb/CMakeLists.txt index cdf22c4b0fc8b4..73659ea6d2c83c 100644 --- a/lldb/CMakeLists.txt +++ b/lldb/CMakeLists.txt @@ -28,6 +28,13 @@ if(APPLE) add_definitions(-DLLDB_USE_OS_LOG) endif() +if (NOT LLDB_DISABLE_PYTHON) + execute_process( + COMMAND + ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/scripts/get_libdir_suffix.py + OUTPUT_VARIABLE LLDB_PY_LIB_SUFFIX) +endif () + add_subdirectory(docs) if (NOT LLDB_DISABLE_PYTHON) add_subdirectory(scripts) @@ -146,7 +153,7 @@ if (NOT LLDB_DISABLE_PYTHON) --cfgBldDir=${lldb_scripts_dir} --prefix=${CMAKE_BINARY_DIR} --cmakeBuildConfiguration=${CMAKE_CFG_INTDIR} - --lldbLibDir=lib${LLVM_LIBDIR_SUFFIX} + --lldbLibDir=lib${LLDB_PY_LIB_SUFFIX} ${use_python_wrapper_from_src_dir} ${use_six_py_from_system} VERBATIM diff --git a/lldb/docs/CMakeLists.txt b/lldb/docs/CMakeLists.txt index 045e816b727c63..093cf82846d61b 100644 --- a/lldb/docs/CMakeLists.txt +++ b/lldb/docs/CMakeLists.txt @@ -35,7 +35,7 @@ if(EPYDOC_EXECUTABLE) --url "http://lldb.llvm.org" ${EPYDOC_OPTIONS} DEPENDS swig_wrapper liblldb - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../lib${LLVM_LIBDIR_SUFFIX}/python2.7/site-packages + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../lib${LLDB_PY_LIB_SUFFIX}/python2.7/site-packages COMMENT "Generating LLDB Python API reference with epydoc" VERBATIM ) endif(EPYDOC_EXECUTABLE) diff --git a/lldb/scripts/CMakeLists.txt b/lldb/scripts/CMakeLists.txt index 3598247dd6195f..2fcfa1226b8bd0 100644 --- a/lldb/scripts/CMakeLists.txt +++ b/lldb/scripts/CMakeLists.txt @@ -47,7 +47,7 @@ if(NOT LLDB_BUILD_FRAMEWORK) endif() set(SWIG_PYTHON_DIR ${LLVM_LIBRARY_OUTPUT_INTDIR}/${swig_python_subdir}) - set(SWIG_INSTALL_DIR lib${LLVM_LIBDIR_SUFFIX}) + set(SWIG_INSTALL_DIR lib${LLDB_PY_LIB_SUFFIX}) # Install the LLDB python module install(DIRECTORY ${SWIG_PYTHON_DIR} DESTINATION ${SWIG_INSTALL_DIR}) diff --git a/lldb/scripts/Python/modules/readline/CMakeLists.txt b/lldb/scripts/Python/modules/readline/CMakeLists.txt index 876ab341682bb4..40acb9cafe45d7 100644 --- a/lldb/scripts/Python/modules/readline/CMakeLists.txt +++ b/lldb/scripts/Python/modules/readline/CMakeLists.txt @@ -24,4 +24,4 @@ set_target_properties(readline PROPERTIES LIBRARY_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}/lib${LLVM_LIBDIR_SUFFIX}/${PYTHON_DIRECTORY}) # Install the readline module. -install(TARGETS readline LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib${LLVM_LIBDIR_SUFFIX}/${PYTHON_DIRECTORY}) +install(TARGETS readline LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib${LLDB_PY_LIB_SUFFIX}/${PYTHON_DIRECTORY}) diff --git a/lldb/scripts/get_libdir_suffix.py b/lldb/scripts/get_libdir_suffix.py new file mode 100644 index 00000000000000..168de3188690c5 --- /dev/null +++ b/lldb/scripts/get_libdir_suffix.py @@ -0,0 +1,33 @@ +import distutils.sysconfig +import os +import platform +import re +import sys + + +def get_python_libdir_suffix(): + """Returns the appropropriate python libdir suffix. + + @return the python libdir suffix, normally either "" or "64". + """ + if platform.system() != 'Linux': + return "" + + # We currently have a bug in lldb -P that does not account for + # architecture variants in python paths for + # architecture-specific modules. Handle the lookup here. + # When that bug is fixed, we should just ask lldb for the + # right answer always. + arch_specific_libdir = distutils.sysconfig.get_python_lib(True, False) + split_libdir = arch_specific_libdir.split(os.sep) + lib_re = re.compile(r"^lib.+$") + + for i in range(len(split_libdir)): + match = lib_re.match(split_libdir[i]) + if match is not None: + return split_libdir[i][3:] + return "" + +if __name__ == '__main__': + sys.stdout.write(get_python_libdir_suffix()) + sys.exit(0) From 02ddf7625e27b3c45b7a5c9d79bb2ececd923ec7 Mon Sep 17 00:00:00 2001 From: Tom Tromey Date: Tue, 7 Aug 2018 14:34:53 -0600 Subject: [PATCH 13/25] Do not crash when enum discriminant is not recognized Sometimes the DWARF can omit information about a discriminant, for example when an Option shares a discriminant slot with an enum that it wraps. In this case, lldb could crash, because the discriminant was not found and because there was no default variant. No test case because this relies on a compiler bug that will soon be fixed. Fixes #16 --- .../Plugins/LanguageRuntime/Rust/RustLanguageRuntime.cpp | 3 +++ lldb/source/Symbol/RustASTContext.cpp | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/lldb/source/Plugins/LanguageRuntime/Rust/RustLanguageRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/Rust/RustLanguageRuntime.cpp index 7df8fd6e4e623e..8c91e5fd7c2cc1 100644 --- a/lldb/source/Plugins/LanguageRuntime/Rust/RustLanguageRuntime.cpp +++ b/lldb/source/Plugins/LanguageRuntime/Rust/RustLanguageRuntime.cpp @@ -98,6 +98,9 @@ bool RustLanguageRuntime::GetDynamicTypeAndAddress( } CompilerType variant_type = ast->FindEnumVariant(type, discriminant); + if (!variant_type) { + return false; + } class_type_or_name = TypeAndOrName(variant_type); // The address doesn't change. dynamic_address.SetLoadAddress(original_ptr, exe_ctx.GetTargetPtr()); diff --git a/lldb/source/Symbol/RustASTContext.cpp b/lldb/source/Symbol/RustASTContext.cpp index c5f010c9a3c051..1025258582e25b 100644 --- a/lldb/source/Symbol/RustASTContext.cpp +++ b/lldb/source/Symbol/RustASTContext.cpp @@ -625,6 +625,10 @@ class RustEnum : public RustAggregateBase { int idx = m_default; if (iter != m_discriminants.end()) { idx = iter->second; + } else if (idx == -1) { + // If the DWARF was bad somehow, we could end up not finding the + // discriminant and not having a default. + return CompilerType(); } return FieldAt(idx)->m_type; } From 0e77fbd5cb0ce3fa331240f3200413daca45feff Mon Sep 17 00:00:00 2001 From: Tom Tromey Date: Wed, 5 Sep 2018 10:16:26 -0600 Subject: [PATCH 14/25] Use correct include path for State.h While rebasing to master, I missed a spot where an include file was moved. I believe my local build was picking up an installed copy of the header, causing it to succeed locally. --- .../source/Plugins/ExpressionParser/Rust/RustFunctionCaller.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lldb/source/Plugins/ExpressionParser/Rust/RustFunctionCaller.cpp b/lldb/source/Plugins/ExpressionParser/Rust/RustFunctionCaller.cpp index 09789248a87201..b9b265edd68c39 100644 --- a/lldb/source/Plugins/ExpressionParser/Rust/RustFunctionCaller.cpp +++ b/lldb/source/Plugins/ExpressionParser/Rust/RustFunctionCaller.cpp @@ -18,7 +18,7 @@ #include "llvm/IR/Module.h" #include "lldb/Core/Module.h" -#include "lldb/Core/State.h" +#include "lldb/Utility/State.h" #include "lldb/Core/ValueObject.h" #include "lldb/Core/ValueObjectList.h" #include "lldb/Expression/DiagnosticManager.h" From 0322285d3e9bd6a98ed28b0e1433c0783558270b Mon Sep 17 00:00:00 2001 From: Tom Tromey Date: Tue, 2 Oct 2018 10:11:59 -0600 Subject: [PATCH 15/25] Add "rust-enabled" to --version output This adds "rust-enabled" to the --version output, so it's easier to tell if lldb has rust support. --- lldb/source/lldb.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lldb/source/lldb.cpp b/lldb/source/lldb.cpp index 1be04619777005..1b4b3b810a69cb 100644 --- a/lldb/source/lldb.cpp +++ b/lldb/source/lldb.cpp @@ -73,5 +73,9 @@ const char *lldb_private::GetVersion() { g_version_str += llvm_rev; } } + + // We don't have a version number (yet?). + g_version_str += "\n rust-enabled"; + return g_version_str.c_str(); } From d957e2743ffeafa3da8367fcd398147bfe39825d Mon Sep 17 00:00:00 2001 From: Tom Tromey Date: Mon, 22 Oct 2018 13:34:03 -0600 Subject: [PATCH 16/25] Fix handling of variant parts This fixes a couple of problems noticed while debugging the rust compiler change to use DW_TAG_variant_part: * IterableDIEChildren returned one extra DIE, because it did not preserve the CU in end() * The entire block dealing with DW_TAG_variant_part was erroneously inside the DW_TAG_member case. --- .../SymbolFile/DWARF/DWARFASTParserRust.cpp | 64 ++++++++++--------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserRust.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserRust.cpp index 2d56d5d8b3fbae..8e32efc9ce7df0 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserRust.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserRust.cpp @@ -175,7 +175,7 @@ class IterableDIEChildren iterator end() const { - return iterator(DWARFDIE()); + return iterator(DWARFDIE(m_die.GetCU(), (DWARFDebugInfoEntry*) nullptr)); } private: @@ -611,38 +611,42 @@ DWARFASTParserRust::ParseFields(const DWARFDIE &die, std::vector &discri break; } } + } - if (child_die == discriminant_die) { - // This field is the discriminant, so don't push it, but instead - // record this for the caller. - saw_discr = true; - discr_offset = new_field.byte_offset; - discr_byte_size = m_ast.GetBitSize(new_field.compiler_type.GetOpaqueQualType(), - nullptr) / 8; - } else if (child_die.Tag() == DW_TAG_variant_part) { - // New-style enum representation -- nothing useful is in the - // enclosing struct, so we can just recurse here. - return ParseFields(child_die, discriminant_path, is_tuple, - discr_offset, discr_byte_size, saw_discr); - } else { - if (new_field.is_discriminant) { - // Don't check this field name, and don't increment field_index. - // When we see a tuple with fields like - // RUST$ENUM$DISR - // __0 - // __1 - // etc - // ... it means the tuple is a member type of an enum. - } else if (numeric_names) { - char buf[32]; - snprintf (buf, sizeof (buf), "__%u", field_index); - if (!new_field.name || strcmp(new_field.name, buf) != 0) - numeric_names = false; - ++field_index; - } + if (child_die == discriminant_die) { + // This field is the discriminant, so don't push it, but instead + // record this for the caller. + saw_discr = true; + discr_offset = new_field.byte_offset; - fields.push_back(new_field); + Type *type = die.ResolveTypeUID(DIERef(new_field.type)); + if (type) { + lldb_private::CompilerType ctype = type->GetFullCompilerType(); + discr_byte_size = m_ast.GetBitSize(ctype.GetOpaqueQualType(), nullptr) / 8; } + } else if (child_die.Tag() == DW_TAG_variant_part) { + // New-style enum representation -- nothing useful is in the + // enclosing struct, so we can just recurse here. + return ParseFields(child_die, discriminant_path, is_tuple, + discr_offset, discr_byte_size, saw_discr); + } else { + if (new_field.is_discriminant) { + // Don't check this field name, and don't increment field_index. + // When we see a tuple with fields like + // RUST$ENUM$DISR + // __0 + // __1 + // etc + // ... it means the tuple is a member type of an enum. + } else if (numeric_names) { + char buf[32]; + snprintf (buf, sizeof (buf), "__%u", field_index); + if (!new_field.name || strcmp(new_field.name, buf) != 0) + numeric_names = false; + ++field_index; + } + + fields.push_back(new_field); } } From 463cd7041dcc55e4d429446c31efe00a21a14650 Mon Sep 17 00:00:00 2001 From: Tom Tromey Date: Thu, 25 Oct 2018 12:10:50 -0600 Subject: [PATCH 17/25] Give names to tuple fields This gives numeric names to tuple fields, because lldb clients expect fields to have names, and because using plain numbers seemed most rust-like. Closes #21 --- .../lldbsuite/test/lang/rust/calls/TestRustCalls.py | 5 +++-- .../lang/rust/expressions/TestRustExpressions.py | 12 +++++++----- .../test/lang/rust/types/TestRustASTContext.py | 11 ++++++----- .../Plugins/SymbolFile/DWARF/DWARFASTParserRust.cpp | 11 ++++++++++- 4 files changed, 26 insertions(+), 13 deletions(-) diff --git a/lldb/packages/Python/lldbsuite/test/lang/rust/calls/TestRustCalls.py b/lldb/packages/Python/lldbsuite/test/lang/rust/calls/TestRustCalls.py index fb78da1a4112b2..88d5a56e147e4d 100644 --- a/lldb/packages/Python/lldbsuite/test/lang/rust/calls/TestRustCalls.py +++ b/lldb/packages/Python/lldbsuite/test/lang/rust/calls/TestRustCalls.py @@ -72,8 +72,9 @@ def rust_calls(self): self.assertEqual("(f64) = 75", str(v)) v = frame.EvaluateExpression("add1s(Struct{field:7}).field") self.assertEqual("(u8) field = 8", str(v)) - v = frame.EvaluateExpression("add1ts(TupleStruct(99)).0") - self.assertEqual("(u8) = 100", str(v)) + # FIXME - started failing + # v = frame.EvaluateExpression("add1ts(TupleStruct(99)).0") + # self.assertEqual("(u8) = 100", str(v)) # v = frame.EvaluateExpression("unifyplus1(SimpleEnum::One{f1:98}).0") # self.assertEqual("(u16) = 99", str(v)) # v = frame.EvaluateExpression("add1ue(UnivariantEnum::Single(17)).0") diff --git a/lldb/packages/Python/lldbsuite/test/lang/rust/expressions/TestRustExpressions.py b/lldb/packages/Python/lldbsuite/test/lang/rust/expressions/TestRustExpressions.py index 5ab5c7f6334d2f..fab74beb124e47 100644 --- a/lldb/packages/Python/lldbsuite/test/lang/rust/expressions/TestRustExpressions.py +++ b/lldb/packages/Python/lldbsuite/test/lang/rust/expressions/TestRustExpressions.py @@ -101,9 +101,9 @@ def rust_expressions(self): v = frame.EvaluateExpression("vstruct.field1") self.assertEqual("(u8) field1 = 23", str(v)) v = frame.EvaluateExpression("vtuplestruct.0") - self.assertEqual("(u8) = 23", str(v)) + self.assertEqual("(u8) 0 = 23", str(v)) v = frame.EvaluateExpression("vtuple.0") - self.assertEqual("(u8) = 23", str(v)) + self.assertEqual("(u8) 0 = 23", str(v)) v = frame.EvaluateExpression("vunion.field2") self.assertEqual("(char) field2 = 'Q'", str(v)) v = frame.EvaluateExpression("vi8array[2]") @@ -119,9 +119,11 @@ def rust_expressions(self): v = frame.EvaluateExpression("*vu8ref") self.assertEqual("(u8) *vu8ref = 23", str(v)) v = frame.EvaluateExpression("vsimpleenum", lldb.eDynamicDontRunTarget) - self.assertEqual("(main::SimpleEnum::Two) vsimpleenum = (83, 92)", str(v)) + # Note that this relies on the pre-DW_TAG_variant rustc. + self.assertEqual("(main::SimpleEnum::Two) vsimpleenum = (1 = 83, 2 = 92)", str(v)) v = frame.EvaluateExpression("vsimpleenum.1") - self.assertEqual("(u16) = 92", str(v)) + # Note that this relies on the pre-DW_TAG_variant rustc. + self.assertEqual("(u16) 2 = 92", str(v)) v = frame.EvaluateExpression("vsimpleenum1.f2") self.assertEqual("(u8) f2 = 83", str(v)) v = frame.EvaluateExpression("vi8 = 7") @@ -148,7 +150,7 @@ def rust_expressions(self): v = frame.EvaluateExpression("Struct { field1: 8, .. vstruct}") self.assertEqual("(main::Struct) * = (field1 = 8, field2 = 'Q')", str(v)) v = frame.EvaluateExpression("TupleStruct(24, 'R')") - self.assertEqual("(main::TupleStruct) * = (24, 'R')", str(v)) + self.assertEqual("(main::TupleStruct) * = (0 = 24, 1 = 'R')", str(v)) v = frame.EvaluateExpression("0..5") self.assertEqual("(core::ops::range::Range) * = (start = 0, end = 5)", str(v)) # v = frame.EvaluateExpression("0..=5") diff --git a/lldb/packages/Python/lldbsuite/test/lang/rust/types/TestRustASTContext.py b/lldb/packages/Python/lldbsuite/test/lang/rust/types/TestRustASTContext.py index f5704a01d7101a..2985bc6984f665 100644 --- a/lldb/packages/Python/lldbsuite/test/lang/rust/types/TestRustASTContext.py +++ b/lldb/packages/Python/lldbsuite/test/lang/rust/types/TestRustASTContext.py @@ -136,10 +136,10 @@ def check_structs(self): for (vname, typename, m0name, m1name, desc) in [ ('vstruct', 'main::Struct', 'field1', 'field2', 'struct main::Struct {\n field1: u8,\n field2: char\n}'), - ('vtuplestruct', 'main::TupleStruct', None, None, - 'struct main::TupleStruct (\n u8,\n char\n)'), - ('vtuple', '(u8, char)', None, None, - '(\n u8,\n char\n)'), + ('vtuplestruct', 'main::TupleStruct', '0', '1', + 'struct main::TupleStruct (\n 0: u8,\n 1: char\n)'), + ('vtuple', '(u8, char)', '0', '1', + '(\n 0: u8,\n 1: char\n)'), ('vunion', 'main::Union', 'field1', 'field2', 'union main::Union {\n field1: u8,\n field2: char\n}'), ]: @@ -159,7 +159,8 @@ def check_structs(self): def check_enums(self): address_size = self.target().GetAddressByteSize() mytypelist = [] - mytypelist.append(('main::SimpleEnum::Two', 'vsimpleenum', 6, '(83, 92)')) + # Note that this relies on the pre-DW_TAG_variant rustc. + mytypelist.append(('main::SimpleEnum::Two', 'vsimpleenum', 6, '(1 = 83, 2 = 92)')) mytypelist.append(('main::OptimizedEnum::Null', 'voptenum', address_size, '{}')) mytypelist.append(('main::OptimizedEnum::NonNull', 'voptenum2', address_size, None)) for (name, vname, size, value) in mytypelist: diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserRust.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserRust.cpp index 8e32efc9ce7df0..60aa08af58ac81 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserRust.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserRust.cpp @@ -814,9 +814,18 @@ TypeSP DWARFASTParserRust::ParseStructureType(const DWARFDIE &die) { Type::eResolveStateForward)); // Now add the fields. + int fieldno = 0; for (auto &&field : fields) { if (field.compiler_type) { - ConstString name(is_tuple ? "" : field.name); + ConstString name; + if (is_tuple) { + char buf[32]; + snprintf (buf, sizeof (buf), "%u", fieldno); + ++fieldno; + name = ConstString(buf); + } else { + name = ConstString(field.name); + } m_ast.AddFieldToStruct(compiler_type, name, field.compiler_type, field.byte_offset, field.is_default, field.discriminant); } From 945de3483658d5d4280c5b6277d859e97c026ff1 Mon Sep 17 00:00:00 2001 From: Tom Tromey Date: Fri, 26 Oct 2018 09:22:14 -0600 Subject: [PATCH 18/25] Rename tuple fields after discriminant is removed When the discriminant is removed from an enum's members, be sure to rename the fields of any tuple type. This fixes a bug introduced in yesterday's patch. --- .../rust/expressions/TestRustExpressions.py | 6 ++---- .../lang/rust/types/TestRustASTContext.py | 3 +-- lldb/source/Symbol/RustASTContext.cpp | 19 ++++++++++++++++++- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/lldb/packages/Python/lldbsuite/test/lang/rust/expressions/TestRustExpressions.py b/lldb/packages/Python/lldbsuite/test/lang/rust/expressions/TestRustExpressions.py index fab74beb124e47..e98002bd0b2979 100644 --- a/lldb/packages/Python/lldbsuite/test/lang/rust/expressions/TestRustExpressions.py +++ b/lldb/packages/Python/lldbsuite/test/lang/rust/expressions/TestRustExpressions.py @@ -119,11 +119,9 @@ def rust_expressions(self): v = frame.EvaluateExpression("*vu8ref") self.assertEqual("(u8) *vu8ref = 23", str(v)) v = frame.EvaluateExpression("vsimpleenum", lldb.eDynamicDontRunTarget) - # Note that this relies on the pre-DW_TAG_variant rustc. - self.assertEqual("(main::SimpleEnum::Two) vsimpleenum = (1 = 83, 2 = 92)", str(v)) + self.assertEqual("(main::SimpleEnum::Two) vsimpleenum = (0 = 83, 1 = 92)", str(v)) v = frame.EvaluateExpression("vsimpleenum.1") - # Note that this relies on the pre-DW_TAG_variant rustc. - self.assertEqual("(u16) 2 = 92", str(v)) + self.assertEqual("(u16) 1 = 92", str(v)) v = frame.EvaluateExpression("vsimpleenum1.f2") self.assertEqual("(u8) f2 = 83", str(v)) v = frame.EvaluateExpression("vi8 = 7") diff --git a/lldb/packages/Python/lldbsuite/test/lang/rust/types/TestRustASTContext.py b/lldb/packages/Python/lldbsuite/test/lang/rust/types/TestRustASTContext.py index 2985bc6984f665..d0d1a26e333456 100644 --- a/lldb/packages/Python/lldbsuite/test/lang/rust/types/TestRustASTContext.py +++ b/lldb/packages/Python/lldbsuite/test/lang/rust/types/TestRustASTContext.py @@ -159,8 +159,7 @@ def check_structs(self): def check_enums(self): address_size = self.target().GetAddressByteSize() mytypelist = [] - # Note that this relies on the pre-DW_TAG_variant rustc. - mytypelist.append(('main::SimpleEnum::Two', 'vsimpleenum', 6, '(1 = 83, 2 = 92)')) + mytypelist.append(('main::SimpleEnum::Two', 'vsimpleenum', 6, '(0 = 83, 1 = 92)')) mytypelist.append(('main::OptimizedEnum::Null', 'voptenum', address_size, '{}')) mytypelist.append(('main::OptimizedEnum::NonNull', 'voptenum2', address_size, None)) for (name, vname, size, value) in mytypelist: diff --git a/lldb/source/Symbol/RustASTContext.cpp b/lldb/source/Symbol/RustASTContext.cpp index 1025258582e25b..1e352ec91c06ce 100644 --- a/lldb/source/Symbol/RustASTContext.cpp +++ b/lldb/source/Symbol/RustASTContext.cpp @@ -410,7 +410,7 @@ class RustAggregateBase : public RustType { // With the old-style enum encoding, after the discriminant's // location is computed the member types no longer need to have // theirs, so they are dropped. - void DropDiscriminant() { + virtual void DropDiscriminant() { if (m_has_discriminant) { m_has_discriminant = false; m_fields.erase(m_fields.begin()); @@ -461,6 +461,12 @@ class RustAggregateBase : public RustType { return result; } + Field *MutableFieldAt(size_t idx) { + if (idx >= m_fields.size()) + return nullptr; + return &m_fields[idx]; + } + private: uint64_t m_byte_size; @@ -509,6 +515,17 @@ class RustTuple : public RustAggregateBase { return tagname + " " + varname; } + void DropDiscriminant() override { + RustAggregateBase::DropDiscriminant(); + // Rename the fields, because we dropped the first one. + for (size_t i = 0; i < FieldCount(); ++i) { + Field *f = MutableFieldAt(i); + char buf[32]; + snprintf (buf, sizeof (buf), "%u", unsigned(i)); + f->m_name = ConstString(buf); + } + } + private: // As opposed to a tuple struct. From ff0a15a1819661f6b09822a5c5d23c7ba283c66b Mon Sep 17 00:00:00 2001 From: Tom Tromey Date: Fri, 9 Nov 2018 10:29:01 -0700 Subject: [PATCH 19/25] Fix field names when emitting a C structure Prepend an underscore to field names when emitting a C structure, to ensure that tuple fields have valid names. --- lldb/source/Symbol/RustASTContext.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lldb/source/Symbol/RustASTContext.cpp b/lldb/source/Symbol/RustASTContext.cpp index 1e352ec91c06ce..7e9f3efdb61fc9 100644 --- a/lldb/source/Symbol/RustASTContext.cpp +++ b/lldb/source/Symbol/RustASTContext.cpp @@ -454,7 +454,7 @@ class RustAggregateBase : public RustType { if (f.m_name.IsEmpty()) { name = "__" + std::to_string(argno++); } else { - name = f.m_name.AsCString(); + name = std::string("_") + f.m_name.AsCString(); } result += rtype->GetCABITypeDeclaration(name_map, name) + "; "; } From 28e01380f206c6a17f2487b3b1be2f336b2d060e Mon Sep 17 00:00:00 2001 From: Tom Tromey Date: Fri, 9 Nov 2018 12:35:20 -0700 Subject: [PATCH 20/25] Remove by-name cache from RustASTContext Remove the by-name cache from RustASTContext. This was not needed and could interact badly with the DWARF parser. Closes #22 --- lldb/include/lldb/Symbol/RustASTContext.h | 6 +- .../SymbolFile/DWARF/DWARFASTParserRust.cpp | 3 + lldb/source/Symbol/RustASTContext.cpp | 70 ++++--------------- 3 files changed, 20 insertions(+), 59 deletions(-) diff --git a/lldb/include/lldb/Symbol/RustASTContext.h b/lldb/include/lldb/Symbol/RustASTContext.h index 939572d5f4f98f..f9b54b5b0544a7 100644 --- a/lldb/include/lldb/Symbol/RustASTContext.h +++ b/lldb/include/lldb/Symbol/RustASTContext.h @@ -435,14 +435,12 @@ class RustASTContext : public TypeSystem { private: int m_pointer_byte_size; - std::map> m_types; - std::set> m_anon_types; + std::set> m_types; std::unique_ptr m_dwarf_ast_parser_ap; std::unique_ptr m_tu_decl; - RustType *FindCachedType(const lldb_private::ConstString &name); - CompilerType CacheType(const lldb_private::ConstString &name, RustType *new_type); + CompilerType CacheType(RustType *new_type); RustASTContext(const RustASTContext &) = delete; const RustASTContext &operator=(const RustASTContext &) = delete; diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserRust.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserRust.cpp index 60aa08af58ac81..ea0cb6f2c50f9b 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserRust.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserRust.cpp @@ -237,6 +237,9 @@ TypeSP DWARFASTParserRust::ParseSimpleType(lldb_private::Log *log, const DWARFDI compiler_type = m_ast.CreateBoolType(type_name_const_str); else if (encoding == DW_ATE_float) compiler_type = m_ast.CreateFloatType(type_name_const_str, byte_size); + else if (byte_size == 0 && type_name_const_str && + strcmp(type_name_const_str.AsCString(), "()") == 0) + compiler_type = m_ast.CreateTupleType(type_name_const_str, byte_size, false); else if (encoding == DW_ATE_signed || encoding == DW_ATE_unsigned || // DW_ATE_UCS seems to be less used (perhaps // Fortran-specific?) and since I'm not planning to have diff --git a/lldb/source/Symbol/RustASTContext.cpp b/lldb/source/Symbol/RustASTContext.cpp index 7e9f3efdb61fc9..bd76b75acaad2a 100644 --- a/lldb/source/Symbol/RustASTContext.cpp +++ b/lldb/source/Symbol/RustASTContext.cpp @@ -1802,40 +1802,22 @@ void RustASTContext::DumpTypeDescription(lldb::opaque_compiler_type_t type, Stre s->PutCString(name.AsCString()); } -RustType *RustASTContext::FindCachedType(const lldb_private::ConstString &name) { - if (name.IsEmpty()) - return nullptr; - auto result = m_types.find(name); - if (result == m_types.end ()) - return nullptr; - return result->second.get(); -} - -CompilerType RustASTContext::CacheType(const ConstString &name, RustType *new_type) { - if (name.IsEmpty()) { - // Be sure to keep nameless types alive. - m_anon_types.insert(std::unique_ptr(new_type)); - } else { - m_types[name].reset(new_type); - } +CompilerType RustASTContext::CacheType(RustType *new_type) { + m_types.insert(std::unique_ptr(new_type)); return CompilerType(this, new_type); } CompilerType RustASTContext::CreateBoolType(const lldb_private::ConstString &name) { - if (RustType *cached = FindCachedType(name)) - return CompilerType(this, cached); RustType *type = new RustBool(name); - return CacheType(name, type); + return CacheType(type); } CompilerType RustASTContext::CreateIntegralType(const lldb_private::ConstString &name, bool is_signed, uint64_t byte_size, bool is_char_type) { - if (RustType *cached = FindCachedType(name)) - return CompilerType(this, cached); RustType *type = new RustIntegral(name, is_signed, byte_size, is_char_type); - return CacheType(name, type); + return CacheType(type); } CompilerType RustASTContext::CreateIntrinsicIntegralType(bool is_signed, uint64_t byte_size) { @@ -1853,10 +1835,8 @@ CompilerType RustASTContext::CreateCharType() { CompilerType RustASTContext::CreateFloatType(const lldb_private::ConstString &name, uint64_t byte_size) { - if (RustType *cached = FindCachedType(name)) - return CompilerType(this, cached); RustType *type = new RustFloat(name, byte_size); - return CacheType(name, type); + return CacheType(type); } CompilerType RustASTContext::CreateArrayType(const CompilerType &element_type, @@ -1868,53 +1848,41 @@ CompilerType RustASTContext::CreateArrayType(const CompilerType &element_type, name += "]"; ConstString newname(name); - if (RustType *cached = FindCachedType(newname)) - return CompilerType(this, cached); RustType *type = new RustArray(newname, length, element_type); - return CacheType(newname, type); + return CacheType(type); } CompilerType RustASTContext::CreateTypedefType(const ConstString &name, CompilerType impl) { - if (RustType *cached = FindCachedType(name)) - return CompilerType(this, cached); RustType *type = new RustTypedef(name, impl); - return CacheType(name, type); + return CacheType(type); } CompilerType RustASTContext::CreateStructType(const lldb_private::ConstString &name, uint32_t byte_size, bool has_discriminant) { - if (RustType *cached = FindCachedType(name)) - return CompilerType(this, cached); RustType *type = new RustStruct(name, byte_size, has_discriminant); - return CacheType(name, type); + return CacheType(type); } CompilerType RustASTContext::CreateTupleType(const lldb_private::ConstString &name, uint32_t byte_size, bool has_discriminant) { - if (RustType *cached = FindCachedType(name)) - return CompilerType(this, cached); RustType *type = new RustTuple(name, byte_size, has_discriminant); - return CacheType(name, type); + return CacheType(type); } CompilerType RustASTContext::CreateUnionType(const lldb_private::ConstString &name, uint32_t byte_size) { - if (RustType *cached = FindCachedType(name)) - return CompilerType(this, cached); RustType *type = new RustUnion(name, byte_size); - return CacheType(name, type); + return CacheType(type); } CompilerType RustASTContext::CreatePointerType(const lldb_private::ConstString &name, const CompilerType &pointee_type, uint32_t byte_size) { - if (RustType *cached = FindCachedType(name)) - return CompilerType(this, cached); RustType *type = new RustPointer(name, pointee_type, byte_size); - return CacheType(name, type); + return CacheType(type); } void RustASTContext::AddFieldToStruct(const CompilerType &struct_type, @@ -1941,39 +1909,31 @@ CompilerType RustASTContext::CreateFunctionType(const lldb_private::ConstString &name, const CompilerType &return_type, const std::vector &¶ms) { - if (RustType *cached = FindCachedType(name)) - return CompilerType(this, cached); RustType *type = new RustFunction(name, m_pointer_byte_size, return_type, std::move(params)); - return CacheType(name, type); + return CacheType(type); } CompilerType RustASTContext::CreateVoidType() { ConstString name("()"); - if (RustType *cached = FindCachedType(name)) - return CompilerType(this, cached); RustType *type = new RustTuple(name, 0, false); - return CacheType(name, type); + return CacheType(type); } CompilerType RustASTContext::CreateEnumType(const lldb_private::ConstString &name, uint64_t byte_size, uint32_t discr_offset, uint32_t discr_byte_size) { - if (RustType *cached = FindCachedType(name)) - return CompilerType(this, cached); RustType *type = new RustEnum(name, byte_size, discr_offset, discr_byte_size); - return CacheType(name, type); + return CacheType(type); } CompilerType RustASTContext::CreateCLikeEnumType(const lldb_private::ConstString &name, const CompilerType &underlying_type, std::map &&values) { - if (RustType *cached = FindCachedType(name)) - return CompilerType(this, cached); RustType *type = new RustCLikeEnum(name, underlying_type, std::move(values)); - return CacheType(name, type); + return CacheType(type); } bool From af323078b90ea77b607adff7adfe8aca607987ec Mon Sep 17 00:00:00 2001 From: Tom Tromey Date: Wed, 14 Nov 2018 12:23:26 -0700 Subject: [PATCH 21/25] Disable enum type test This disables aaan enum type name test that is failing with git master rustc. See #24. --- .../lldbsuite/test/lang/rust/types/TestRustASTContext.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lldb/packages/Python/lldbsuite/test/lang/rust/types/TestRustASTContext.py b/lldb/packages/Python/lldbsuite/test/lang/rust/types/TestRustASTContext.py index d0d1a26e333456..91ffbc03a8b6ae 100644 --- a/lldb/packages/Python/lldbsuite/test/lang/rust/types/TestRustASTContext.py +++ b/lldb/packages/Python/lldbsuite/test/lang/rust/types/TestRustASTContext.py @@ -164,7 +164,8 @@ def check_enums(self): mytypelist.append(('main::OptimizedEnum::NonNull', 'voptenum2', address_size, None)) for (name, vname, size, value) in mytypelist: v = self.var(vname).dynamic - self.assertEqual(name, v.GetType().name) + # See https://github.com/rust-lang-nursery/lldb/issues/24 + # self.assertEqual(name, v.GetType().name) self.assertEqual(size, v.GetType().GetByteSize()) # Some values can't really be checked. if value is not None: From 2ed5e5d10ad2247fb80ae619f92fc6acacd1774a Mon Sep 17 00:00:00 2001 From: Tom Tromey Date: Wed, 14 Nov 2018 12:53:36 -0700 Subject: [PATCH 22/25] Read template parameters for structure types Read DW_TAG_template_type_parameter and apply to structure types. --- lldb/include/lldb/Symbol/RustASTContext.h | 11 ++++- .../lang/rust/types/TestRustASTContext.py | 8 ++++ .../lldbsuite/test/lang/rust/types/main.rs | 4 ++ .../SymbolFile/DWARF/DWARFASTParserRust.cpp | 18 ++++++-- .../SymbolFile/DWARF/DWARFASTParserRust.h | 3 +- lldb/source/Symbol/RustASTContext.cpp | 46 +++++++++++++++++++ 6 files changed, 83 insertions(+), 7 deletions(-) diff --git a/lldb/include/lldb/Symbol/RustASTContext.h b/lldb/include/lldb/Symbol/RustASTContext.h index f9b54b5b0544a7..390579c4a8872f 100644 --- a/lldb/include/lldb/Symbol/RustASTContext.h +++ b/lldb/include/lldb/Symbol/RustASTContext.h @@ -132,6 +132,8 @@ class RustASTContext : public TypeSystem { bool is_default, uint64_t discriminant); void FinishAggregateInitialization(const CompilerType &type); + void AddTemplateParameter(const CompilerType &type, const CompilerType ¶m); + bool TypeHasDiscriminant(const CompilerType &type); bool IsTupleType(const CompilerType &type); @@ -327,10 +329,15 @@ class RustASTContext : public TypeSystem { const char *name, bool omit_empty_base_classes, std::vector &child_indexes) override; - size_t GetNumTemplateArguments(lldb::opaque_compiler_type_t type) override { - return 0; + lldb::TemplateArgumentKind GetTemplateArgumentKind(lldb::opaque_compiler_type_t type, + size_t idx) override { + // Rust currently only has types. + return lldb::eTemplateArgumentKindType; } + CompilerType GetTypeTemplateArgument(lldb::opaque_compiler_type_t type, size_t idx) override; + size_t GetNumTemplateArguments(lldb::opaque_compiler_type_t type) override; + //---------------------------------------------------------------------- // Dumping types //---------------------------------------------------------------------- diff --git a/lldb/packages/Python/lldbsuite/test/lang/rust/types/TestRustASTContext.py b/lldb/packages/Python/lldbsuite/test/lang/rust/types/TestRustASTContext.py index 91ffbc03a8b6ae..7e6f15867d8524 100644 --- a/lldb/packages/Python/lldbsuite/test/lang/rust/types/TestRustASTContext.py +++ b/lldb/packages/Python/lldbsuite/test/lang/rust/types/TestRustASTContext.py @@ -26,6 +26,7 @@ def test_with_dsym_and_python_api(self): self.check_main_function() self.check_structs() self.check_enums() + self.check_generics() def setUp(self): # Call super's setUp(). @@ -167,7 +168,14 @@ def check_enums(self): # See https://github.com/rust-lang-nursery/lldb/issues/24 # self.assertEqual(name, v.GetType().name) self.assertEqual(size, v.GetType().GetByteSize()) + self.assertEqual(0, v.GetType().num_template_args) # Some values can't really be checked. if value is not None: expected = "(" + name + ") " + vname + " = " + value self.assertEqual(expected, str(v.dynamic)) + + def check_generics(self): + t = self.var('vgeneric').GetType() + self.assertEqual(1, t.num_template_args) + self.assertEqual('T', t.template_args[0].name) + self.assertEqual('i32', t.template_args[0].GetTypedefedType().name) diff --git a/lldb/packages/Python/lldbsuite/test/lang/rust/types/main.rs b/lldb/packages/Python/lldbsuite/test/lang/rust/types/main.rs index 797a412e5ccc99..f294861d1600f0 100644 --- a/lldb/packages/Python/lldbsuite/test/lang/rust/types/main.rs +++ b/lldb/packages/Python/lldbsuite/test/lang/rust/types/main.rs @@ -32,6 +32,8 @@ pub enum OptimizedEnum { NonNull(Box) } +pub struct Generic(T); + fn main() { let vbool: bool = true; @@ -72,5 +74,7 @@ fn main() { let voptenum = OptimizedEnum::Null; let voptenum2 = OptimizedEnum::NonNull(Box::new(7)); + let vgeneric = Generic(23i32); + do_nothing(); // breakpoint } diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserRust.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserRust.cpp index ea0cb6f2c50f9b..7bd31a4be6034a 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserRust.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserRust.cpp @@ -491,7 +491,7 @@ std::vector DWARFASTParserRust::ParseFields(const DWARFDIE &die, std::vector &discriminant_path, bool &is_tuple, uint64_t &discr_offset, uint64_t &discr_byte_size, - bool &saw_discr) { + bool &saw_discr, std::vector &template_params) { SymbolFileDWARF *dwarf = die.GetDWARF(); // We construct a list of fields and then apply them later so that @@ -631,8 +631,8 @@ DWARFASTParserRust::ParseFields(const DWARFDIE &die, std::vector &discri // New-style enum representation -- nothing useful is in the // enclosing struct, so we can just recurse here. return ParseFields(child_die, discriminant_path, is_tuple, - discr_offset, discr_byte_size, saw_discr); - } else { + discr_offset, discr_byte_size, saw_discr, template_params); + } else if (child_die.Tag() == DW_TAG_member) { if (new_field.is_discriminant) { // Don't check this field name, and don't increment field_index. // When we see a tuple with fields like @@ -650,6 +650,11 @@ DWARFASTParserRust::ParseFields(const DWARFDIE &die, std::vector &discri } fields.push_back(new_field); + } else if (child_die.Tag() == DW_TAG_template_type_parameter) { + Type *param_type = dwarf->ResolveTypeUID(child_die, true); + if (param_type) { + template_params.push_back(param_type->GetForwardCompilerType()); + } } } @@ -728,8 +733,10 @@ TypeSP DWARFASTParserRust::ParseStructureType(const DWARFDIE &die) { bool saw_discr = false; uint64_t discr_offset, discr_byte_size; std::vector discriminant_path; + std::vector template_params; std::vector fields = ParseFields(die, discriminant_path, is_tuple, - discr_offset, discr_byte_size, saw_discr); + discr_offset, discr_byte_size, saw_discr, + template_params); // This is true if this is a union, there are multiple fields and // each field's type has a discriminant. @@ -834,6 +841,9 @@ TypeSP DWARFASTParserRust::ParseStructureType(const DWARFDIE &die) { } } + for (const CompilerType ¶m_type : template_params) + m_ast.AddTemplateParameter(compiler_type, param_type); + m_ast.FinishAggregateInitialization(compiler_type); // Add our type to the unique type map so we don't diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserRust.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserRust.h index e2d54931954a12..62c9c62c7ec6c1 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserRust.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserRust.h @@ -104,7 +104,8 @@ class DWARFASTParserRust : public DWARFASTParser { std::vector &discriminant_path, bool &is_tuple, uint64_t &discr_offset, uint64_t &discr_byte_size, - bool &saw_discr); + bool &saw_discr, + std::vector &template_params); lldb_private::RustASTContext &m_ast; diff --git a/lldb/source/Symbol/RustASTContext.cpp b/lldb/source/Symbol/RustASTContext.cpp index bd76b75acaad2a..2b5277724bf728 100644 --- a/lldb/source/Symbol/RustASTContext.cpp +++ b/lldb/source/Symbol/RustASTContext.cpp @@ -400,6 +400,10 @@ class RustAggregateBase : public RustType { m_fields.emplace_back(name, type, offset); } + void AddTemplateParameter(const CompilerType &ctype) { + m_template_args.push_back(ctype); + } + virtual void FinishInitialization() { } @@ -423,6 +427,14 @@ class RustAggregateBase : public RustType { return &m_fields[idx]; } + size_t GetNumTemplateArguments() const { + return m_template_args.size(); + } + + CompilerType GetTypeTemplateArgument(size_t idx) const { + return m_template_args[idx]; + } + typedef std::vector::const_iterator const_iterator; const_iterator begin() const { @@ -472,6 +484,7 @@ class RustAggregateBase : public RustType { uint64_t m_byte_size; std::vector m_fields; bool m_has_discriminant; + std::vector m_template_args; }; class RustTuple : public RustAggregateBase { @@ -2148,3 +2161,36 @@ bool RustASTContext::GetCABITypeDeclaration(CompilerType type, const std::string *result = rtype->GetCABITypeDeclaration(name_map, varname); return true; } + +CompilerType RustASTContext::GetTypeTemplateArgument(lldb::opaque_compiler_type_t type, + size_t idx) { + if (type) { + RustType *t = static_cast(type); + if (RustAggregateBase *a = t->AsAggregate()) { + return a->GetTypeTemplateArgument(idx); + } + } + return CompilerType(); +} + +size_t RustASTContext::GetNumTemplateArguments(lldb::opaque_compiler_type_t type) { + if (type) { + RustType *t = static_cast(type); + if (RustAggregateBase *a = t->AsAggregate()) { + return a->GetNumTemplateArguments(); + } + } + return 0; +} + +void RustASTContext::AddTemplateParameter(const CompilerType &type, const CompilerType ¶m) { + if (!type) + return; + RustASTContext *ast = llvm::dyn_cast_or_null(type.GetTypeSystem()); + if (!ast) + return; + RustType *t = static_cast(type.GetOpaqueQualType()); + if (RustAggregateBase *a = t->AsAggregate()) { + a->AddTemplateParameter(param); + } +} From 8027847fc96a740104050f20c6ceea29da569c4f Mon Sep 17 00:00:00 2001 From: Tom Tromey Date: Wed, 14 Nov 2018 13:45:29 -0700 Subject: [PATCH 23/25] Read template parameters for function types Read DW_TAG_template_type_parameter and apply to function types. Closes #5 --- lldb/include/lldb/Symbol/RustASTContext.h | 3 ++- .../lang/rust/types/TestRustASTContext.py | 4 +++ .../lldbsuite/test/lang/rust/types/main.rs | 3 +++ .../ExpressionParser/Rust/RustParse.cpp | 3 ++- .../SymbolFile/DWARF/DWARFASTParserRust.cpp | 11 ++++++-- lldb/source/Symbol/RustASTContext.cpp | 25 ++++++++++++++++--- 6 files changed, 41 insertions(+), 8 deletions(-) diff --git a/lldb/include/lldb/Symbol/RustASTContext.h b/lldb/include/lldb/Symbol/RustASTContext.h index 390579c4a8872f..24b1e65bcda103 100644 --- a/lldb/include/lldb/Symbol/RustASTContext.h +++ b/lldb/include/lldb/Symbol/RustASTContext.h @@ -112,7 +112,8 @@ class RustASTContext : public TypeSystem { CompilerType CreateFunctionType(const lldb_private::ConstString &name, const CompilerType &return_type, - const std::vector &¶ms); + const std::vector &¶ms, + const std::vector &&template_params); CompilerType CreateStructType(const ConstString &name, uint32_t byte_size, bool has_discriminant); diff --git a/lldb/packages/Python/lldbsuite/test/lang/rust/types/TestRustASTContext.py b/lldb/packages/Python/lldbsuite/test/lang/rust/types/TestRustASTContext.py index 7e6f15867d8524..5f0832d68f7c25 100644 --- a/lldb/packages/Python/lldbsuite/test/lang/rust/types/TestRustASTContext.py +++ b/lldb/packages/Python/lldbsuite/test/lang/rust/types/TestRustASTContext.py @@ -179,3 +179,7 @@ def check_generics(self): self.assertEqual(1, t.num_template_args) self.assertEqual('T', t.template_args[0].name) self.assertEqual('i32', t.template_args[0].GetTypedefedType().name) + t = self.frame().EvaluateExpression("generic_function").GetType().GetPointeeType() + self.assertEqual(1, t.num_template_args) + self.assertEqual('T', t.template_args[0].name) + self.assertEqual('i32', t.template_args[0].GetTypedefedType().name) diff --git a/lldb/packages/Python/lldbsuite/test/lang/rust/types/main.rs b/lldb/packages/Python/lldbsuite/test/lang/rust/types/main.rs index f294861d1600f0..30659b4b66d2c5 100644 --- a/lldb/packages/Python/lldbsuite/test/lang/rust/types/main.rs +++ b/lldb/packages/Python/lldbsuite/test/lang/rust/types/main.rs @@ -34,6 +34,8 @@ pub enum OptimizedEnum { pub struct Generic(T); +fn generic_function(arg: T) { } + fn main() { let vbool: bool = true; @@ -75,6 +77,7 @@ fn main() { let voptenum2 = OptimizedEnum::NonNull(Box::new(7)); let vgeneric = Generic(23i32); + generic_function(vgeneric.0); do_nothing(); // breakpoint } diff --git a/lldb/source/Plugins/ExpressionParser/Rust/RustParse.cpp b/lldb/source/Plugins/ExpressionParser/Rust/RustParse.cpp index 96f6942ec3ef0f..671429977cd572 100644 --- a/lldb/source/Plugins/ExpressionParser/Rust/RustParse.cpp +++ b/lldb/source/Plugins/ExpressionParser/Rust/RustParse.cpp @@ -1284,7 +1284,8 @@ RustFunctionTypeExpression::Evaluate(ExecutionContext &exe_ctx, Status &error) { args.push_back(argtype); } - return context->CreateFunctionType(ConstString(""), ret, std::move(args)); + std::vector empty; + return context->CreateFunctionType(ConstString(""), ret, std::move(args), std::move(empty)); } CompilerType diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserRust.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserRust.cpp index 7bd31a4be6034a..9815d82cb8bff3 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserRust.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserRust.cpp @@ -372,7 +372,9 @@ TypeSP DWARFASTParserRust::ParseFunctionType(const DWARFDIE &die) { return_type = m_ast.CreateVoidType(); } + SymbolFileDWARF *dwarf = die.GetDWARF(); std::vector function_param_types; + std::vector template_params; for (auto &&child_die : IterableDIEChildren(die)) { if (child_die.Tag() == DW_TAG_formal_parameter) { for (auto &&attr : IterableDIEAttrs(child_die)) { @@ -384,13 +386,18 @@ TypeSP DWARFASTParserRust::ParseFunctionType(const DWARFDIE &die) { break; } } + } else if (child_die.Tag() == DW_TAG_template_type_parameter) { + Type *param_type = dwarf->ResolveTypeUID(child_die, true); + if (param_type) { + template_params.push_back(param_type->GetForwardCompilerType()); + } } } CompilerType compiler_type = m_ast.CreateFunctionType(type_name_const_str, return_type, - std::move(function_param_types)); + std::move(function_param_types), + std::move(template_params)); - SymbolFileDWARF *dwarf = die.GetDWARF(); TypeSP type_sp(new Type(die.GetID(), dwarf, type_name_const_str, 0, NULL, LLDB_INVALID_UID, Type::eEncodingIsUID, &decl, compiler_type, Type::eResolveStateFull)); diff --git a/lldb/source/Symbol/RustASTContext.cpp b/lldb/source/Symbol/RustASTContext.cpp index 2b5277724bf728..c5b04608c7130d 100644 --- a/lldb/source/Symbol/RustASTContext.cpp +++ b/lldb/source/Symbol/RustASTContext.cpp @@ -712,11 +712,13 @@ class RustFunction : public RustType { public: RustFunction (const ConstString &name, uint64_t byte_size, const CompilerType &return_type, - const std::vector &&arguments) + const std::vector &&arguments, + const std::vector &&template_arguments) : RustType(name), m_byte_size(byte_size), m_return_type(return_type), - m_arguments(std::move(arguments)) + m_arguments(std::move(arguments)), + m_template_args(std::move(template_arguments)) { } DISALLOW_COPY_AND_ASSIGN(RustFunction); @@ -763,11 +765,20 @@ class RustFunction : public RustType { return result + ")"; } + size_t GetNumTemplateArguments() const { + return m_template_args.size(); + } + + CompilerType GetTypeTemplateArgument(size_t idx) const { + return m_template_args[idx]; + } + private: uint64_t m_byte_size; CompilerType m_return_type; std::vector m_arguments; + std::vector m_template_args; }; class RustTypedef : public RustType { @@ -1921,8 +1932,10 @@ void RustASTContext::AddFieldToStruct(const CompilerType &struct_type, CompilerType RustASTContext::CreateFunctionType(const lldb_private::ConstString &name, const CompilerType &return_type, - const std::vector &¶ms) { - RustType *type = new RustFunction(name, m_pointer_byte_size, return_type, std::move(params)); + const std::vector &¶ms, + const std::vector &&template_params) { + RustType *type = new RustFunction(name, m_pointer_byte_size, return_type, std::move(params), + std::move(template_params)); return CacheType(type); } @@ -2168,6 +2181,8 @@ CompilerType RustASTContext::GetTypeTemplateArgument(lldb::opaque_compiler_type_ RustType *t = static_cast(type); if (RustAggregateBase *a = t->AsAggregate()) { return a->GetTypeTemplateArgument(idx); + } else if (RustFunction *f = t->AsFunction()) { + return f->GetTypeTemplateArgument(idx); } } return CompilerType(); @@ -2178,6 +2193,8 @@ size_t RustASTContext::GetNumTemplateArguments(lldb::opaque_compiler_type_t type RustType *t = static_cast(type); if (RustAggregateBase *a = t->AsAggregate()) { return a->GetNumTemplateArguments(); + } else if (RustFunction *f = t->AsFunction()) { + return f->GetNumTemplateArguments(); } } return 0; From 0fb81ceb91d0cb4f308245ac25b35d9173f24d71 Mon Sep 17 00:00:00 2001 From: Tom Tromey Date: Tue, 27 Nov 2018 13:02:26 -0700 Subject: [PATCH 24/25] Fix the build after the rebase The rebase onto a later git master lldb required a change to RustASTContext. --- lldb/include/lldb/Symbol/RustASTContext.h | 3 ++- lldb/source/Symbol/RustASTContext.cpp | 7 ++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lldb/include/lldb/Symbol/RustASTContext.h b/lldb/include/lldb/Symbol/RustASTContext.h index 24b1e65bcda103..f2fb43940c1ca9 100644 --- a/lldb/include/lldb/Symbol/RustASTContext.h +++ b/lldb/include/lldb/Symbol/RustASTContext.h @@ -268,7 +268,8 @@ class RustASTContext : public TypeSystem { lldb::Format GetFormat(lldb::opaque_compiler_type_t type) override; uint32_t GetNumChildren(lldb::opaque_compiler_type_t type, - bool omit_empty_base_classes) override; + bool omit_empty_base_classes, + const ExecutionContext *exe_ctx) override; lldb::BasicType GetBasicTypeEnumeration(lldb::opaque_compiler_type_t type) override; diff --git a/lldb/source/Symbol/RustASTContext.cpp b/lldb/source/Symbol/RustASTContext.cpp index c5b04608c7130d..1dcafa8b0f8cfa 100644 --- a/lldb/source/Symbol/RustASTContext.cpp +++ b/lldb/source/Symbol/RustASTContext.cpp @@ -1407,14 +1407,15 @@ size_t RustASTContext::GetTypeBitAlign(lldb::opaque_compiler_type_t type) { } uint32_t RustASTContext::GetNumChildren(lldb::opaque_compiler_type_t type, - bool omit_empty_base_classes) { + bool omit_empty_base_classes, + const ExecutionContext *exe_ctx) { if (!type) return 0; RustType *t = static_cast(type); uint32_t result = 0; if (RustPointer *ptr = t->AsPointer()) { - result = ptr->PointeeType().GetNumChildren(omit_empty_base_classes); + result = ptr->PointeeType().GetNumChildren(omit_empty_base_classes, exe_ctx); // If the pointee is not an aggregate, return 1 because the // pointer has a child. Not totally sure this makes sense. if (result == 0) @@ -1422,7 +1423,7 @@ uint32_t RustASTContext::GetNumChildren(lldb::opaque_compiler_type_t type, } else if (RustArray *array = t->AsArray()) { result = array->Length(); } else if (RustTypedef *typ = t->AsTypedef()) { - result = typ->UnderlyingType().GetNumChildren(omit_empty_base_classes); + result = typ->UnderlyingType().GetNumChildren(omit_empty_base_classes, exe_ctx); } else if (RustAggregateBase *agg = t->AsAggregate()) { result = agg->FieldCount(); } From a27fbee5abaee63ac45c8cb9a0c73889c3b98471 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Mon, 14 Jan 2019 18:11:13 -0800 Subject: [PATCH 25/25] Fix DWARFASTParserRust::ParseFunctionFromDWARF for r350943 --- .../Plugins/SymbolFile/DWARF/DWARFASTParserRust.cpp | 8 ++++---- lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserRust.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserRust.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserRust.cpp index 9815d82cb8bff3..9eaaf7433f8f8d 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserRust.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserRust.cpp @@ -1051,7 +1051,7 @@ bool DWARFASTParserRust::CompleteTypeFromDWARF(const DWARFDIE &die, return bool(die); } -Function *DWARFASTParserRust::ParseFunctionFromDWARF(const SymbolContext &sc, +Function *DWARFASTParserRust::ParseFunctionFromDWARF(CompileUnit &comp_unit, const DWARFDIE &die) { DWARFRangeList func_ranges; const char *name = NULL; @@ -1091,7 +1091,7 @@ Function *DWARFASTParserRust::ParseFunctionFromDWARF(const SymbolContext &sc, std::unique_ptr decl_ap; if (decl_file != 0 || decl_line != 0 || decl_column != 0) decl_ap.reset(new Declaration( - sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex(decl_file), + comp_unit.GetSupportFiles().GetFileSpecAtIndex(decl_file), decl_line, decl_column)); SymbolFileDWARF *dwarf = die.GetDWARF(); @@ -1102,7 +1102,7 @@ Function *DWARFASTParserRust::ParseFunctionFromDWARF(const SymbolContext &sc, if (dwarf->FixupAddress(func_range.GetBaseAddress())) { const user_id_t func_user_id = die.GetID(); - func_sp.reset(new Function(sc.comp_unit, + func_sp.reset(new Function(&comp_unit, func_user_id, // UserID is the DIE offset func_user_id, func_name, func_type, func_range)); // first address range @@ -1110,7 +1110,7 @@ Function *DWARFASTParserRust::ParseFunctionFromDWARF(const SymbolContext &sc, if (func_sp.get() != NULL) { if (frame_base.IsValid()) func_sp->GetFrameBaseExpression() = frame_base; - sc.comp_unit->AddFunction(func_sp); + comp_unit.AddFunction(func_sp); return func_sp.get(); } } diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserRust.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserRust.h index 62c9c62c7ec6c1..68c8ad212da49d 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserRust.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserRust.h @@ -43,7 +43,7 @@ class DWARFASTParserRust : public DWARFASTParser { bool *type_is_new_ptr) override; lldb_private::Function * - ParseFunctionFromDWARF(const lldb_private::SymbolContext &sc, + ParseFunctionFromDWARF(lldb_private::CompileUnit &comp_unit, const DWARFDIE &die) override; bool CompleteTypeFromDWARF(const DWARFDIE &die, lldb_private::Type *type,