diff --git a/lld/CMakeLists.txt b/lld/CMakeLists.txt index e2fbdbfbbb47f..ccf33f8fd2881 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) diff --git a/lldb/CMakeLists.txt b/lldb/CMakeLists.txt index cdf22c4b0fc8b..73659ea6d2c83 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 045e816b727c6..093cf82846d61 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/include/lldb/Expression/ExpressionTypeSystemHelper.h b/lldb/include/lldb/Expression/ExpressionTypeSystemHelper.h index ffcad54fb9f9b..04f0b1c038d62 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 01e9c416e7c01..de99d9ed690dc 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 0000000000000..f2fb43940c1ca --- /dev/null +++ b/lldb/include/lldb/Symbol/RustASTContext.h @@ -0,0 +1,471 @@ +//===-- 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, + const std::vector &&template_params); + + 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); + + void AddTemplateParameter(const CompilerType &type, const CompilerType ¶m); + + 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, + const ExecutionContext *exe_ctx) 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; + + 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 + //---------------------------------------------------------------------- + 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::set> m_types; + std::unique_ptr m_dwarf_ast_parser_ap; + + std::unique_ptr m_tu_decl; + + CompilerType CacheType(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 6afbd188a2342..bacb09b938195 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 92aa5bb611c3c..3a55a491fcda2 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/decorators.py b/lldb/packages/Python/lldbsuite/test/decorators.py index 2458a6f8745ee..806d5cd190f16 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/lang/rust/calls/TestRustCalls.py b/lldb/packages/Python/lldbsuite/test/lang/rust/calls/TestRustCalls.py new file mode 100644 index 0000000000000..88d5a56e147e4 --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/lang/rust/calls/TestRustCalls.py @@ -0,0 +1,83 @@ +"""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)) + # 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") + # 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 0000000000000..6f088b46e78c2 --- /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 0000000000000..e98002bd0b297 --- /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) 0 = 23", str(v)) + v = frame.EvaluateExpression("vtuple.0") + self.assertEqual("(u8) 0 = 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 = (0 = 83, 1 = 92)", str(v)) + v = frame.EvaluateExpression("vsimpleenum.1") + self.assertEqual("(u16) 1 = 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) * = (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") + # 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 0000000000000..e656066e38bf2 --- /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 0000000000000..c7dacdca5e21c --- /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 0000000000000..6556d3304c306 --- /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 0000000000000..5f0832d68f7c2 --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/lang/rust/types/TestRustASTContext.py @@ -0,0 +1,185 @@ +"""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() + self.check_generics() + + 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', '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}'), + ]: + 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, '(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: + v = self.var(vname).dynamic + # 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) + 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 new file mode 100644 index 0000000000000..30659b4b66d2c --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/lang/rust/types/main.rs @@ -0,0 +1,83 @@ +#![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) +} + +pub struct Generic(T); + +fn generic_function(arg: T) { } + +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)); + + let vgeneric = Generic(23i32); + generic_function(vgeneric.0); + + do_nothing(); // breakpoint +} diff --git a/lldb/packages/Python/lldbsuite/test/lldbtest.py b/lldb/packages/Python/lldbsuite/test/lldbtest.py index 161e8c61349d1..c97afd7a484e4 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" % ( diff --git a/lldb/scripts/CMakeLists.txt b/lldb/scripts/CMakeLists.txt index 3598247dd6195..2fcfa1226b8bd 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 876ab341682bb..40acb9cafe45d 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 0000000000000..168de3188690c --- /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) diff --git a/lldb/source/API/SystemInitializerFull.cpp b/lldb/source/API/SystemInitializerFull.cpp index 42dea6a01abad..fa10d3fdf6a60 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 15f2dcf6ed999..1c23ba7445f41 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/Core/Mangled.cpp b/lldb/source/Core/Mangled.cpp index 943df008edadd..20fea1db2f152 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: diff --git a/lldb/source/Plugins/ExpressionParser/CMakeLists.txt b/lldb/source/Plugins/ExpressionParser/CMakeLists.txt index 17c40aee44cc2..78db827bae5a4 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 0000000000000..bddec8cf20767 --- /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 0000000000000..e3a0b5ddc6b9d --- /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 0000000000000..b9b265edd68c3 --- /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/Utility/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 0000000000000..1a5c54e46f54c --- /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 0000000000000..99a6cb7b55865 --- /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 0000000000000..84bbbd984c6f8 --- /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 0000000000000..671429977cd57 --- /dev/null +++ b/lldb/source/Plugins/ExpressionParser/Rust/RustParse.cpp @@ -0,0 +1,2285 @@ +//===-- 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); + } + + std::vector empty; + return context->CreateFunctionType(ConstString(""), ret, std::move(args), std::move(empty)); +} + +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 0000000000000..e9b6eb488e229 --- /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 0000000000000..cdea659bd5fb0 --- /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 0000000000000..13cc8ad4fe585 --- /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 7869074566d1e..c58d89618f6ed 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 0000000000000..4ad4166235e7b --- /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 0000000000000..b3d5b11bfdfe1 --- /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 0000000000000..bd073d8f7f8ad --- /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 c62791445a9a2..9b7036e71fbd7 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 0000000000000..17d4c558ae81e --- /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 0000000000000..8c91e5fd7c2cc --- /dev/null +++ b/lldb/source/Plugins/LanguageRuntime/Rust/RustLanguageRuntime.cpp @@ -0,0 +1,119 @@ +//===-- 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); + 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()); + 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 0000000000000..c8ff5a1952995 --- /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 0e47ee34fe51b..0a376e23fdaff 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/DIERef.h b/lldb/source/Plugins/SymbolFile/DWARF/DIERef.h index cb28c890c25a1..df17e1465adef 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; }; diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserRust.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserRust.cpp new file mode 100644 index 0000000000000..9eaaf7433f8f8 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserRust.cpp @@ -0,0 +1,1210 @@ +//===-- 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(m_die.GetCU(), (DWARFDebugInfoEntry*) nullptr)); + } + +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 (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 + // 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(); + } + + 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)) { + if (attr.first == DW_AT_type) { + Type *type = die.ResolveTypeUID(DIERef(attr.second)); + if (type) { + function_param_types.push_back(type->GetForwardCompilerType()); + } + 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(template_params)); + + 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, std::vector &template_params) { + 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; + + 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, 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 + // 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); + } 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()); + } + } + } + + 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 template_params; + std::vector fields = ParseFields(die, discriminant_path, is_tuple, + 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. + 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. + int fieldno = 0; + for (auto &&field : fields) { + if (field.compiler_type) { + 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); + } + } + + 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 + // 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(CompileUnit &comp_unit, + 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( + 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(&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; + 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 0000000000000..68c8ad212da49 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserRust.h @@ -0,0 +1,135 @@ +//===-- 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(lldb_private::CompileUnit &comp_unit, + 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, + std::vector &template_params); + + 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 d351289f8b51b..bfe9744367145 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 56562d3224858..d1b4390aa5def 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 85436b630b7d7..868daa1d484bf 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 0000000000000..1dcafa8b0f8cf --- /dev/null +++ b/lldb/source/Symbol/RustASTContext.cpp @@ -0,0 +1,2214 @@ +//===-- 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); + } + + void AddTemplateParameter(const CompilerType &ctype) { + m_template_args.push_back(ctype); + } + + 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. + virtual 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]; + } + + 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 { + 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 = std::string("_") + f.m_name.AsCString(); + } + result += rtype->GetCABITypeDeclaration(name_map, name) + "; "; + } + return result; + } + + Field *MutableFieldAt(size_t idx) { + if (idx >= m_fields.size()) + return nullptr; + return &m_fields[idx]; + } + +private: + + uint64_t m_byte_size; + std::vector m_fields; + bool m_has_discriminant; + std::vector m_template_args; +}; + +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; + } + + 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. + 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; + } 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; + } + + 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, + const std::vector &&template_arguments) + : RustType(name), + m_byte_size(byte_size), + m_return_type(return_type), + m_arguments(std::move(arguments)), + m_template_args(std::move(template_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 + ")"; + } + + 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 { +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, + 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, 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) + 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, exe_ctx); + } 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()); +} + +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) { + RustType *type = new RustBool(name); + return CacheType(type); +} + +CompilerType RustASTContext::CreateIntegralType(const lldb_private::ConstString &name, + bool is_signed, + uint64_t byte_size, + bool is_char_type) { + RustType *type = new RustIntegral(name, is_signed, byte_size, is_char_type); + return CacheType(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) { + RustType *type = new RustFloat(name, byte_size); + return CacheType(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); + + RustType *type = new RustArray(newname, length, element_type); + return CacheType(type); +} + +CompilerType RustASTContext::CreateTypedefType(const ConstString &name, CompilerType impl) { + RustType *type = new RustTypedef(name, impl); + return CacheType(type); +} + +CompilerType +RustASTContext::CreateStructType(const lldb_private::ConstString &name, uint32_t byte_size, + bool has_discriminant) { + RustType *type = new RustStruct(name, byte_size, has_discriminant); + return CacheType(type); +} + +CompilerType +RustASTContext::CreateTupleType(const lldb_private::ConstString &name, uint32_t byte_size, + bool has_discriminant) { + RustType *type = new RustTuple(name, byte_size, has_discriminant); + return CacheType(type); +} + +CompilerType +RustASTContext::CreateUnionType(const lldb_private::ConstString &name, uint32_t byte_size) { + RustType *type = new RustUnion(name, byte_size); + return CacheType(type); +} + +CompilerType +RustASTContext::CreatePointerType(const lldb_private::ConstString &name, + const CompilerType &pointee_type, + uint32_t byte_size) { + RustType *type = new RustPointer(name, pointee_type, byte_size); + return CacheType(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, + 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); +} + +CompilerType +RustASTContext::CreateVoidType() { + ConstString name("()"); + RustType *type = new RustTuple(name, 0, false); + return CacheType(type); +} + +CompilerType +RustASTContext::CreateEnumType(const lldb_private::ConstString &name, + uint64_t byte_size, uint32_t discr_offset, + uint32_t discr_byte_size) { + RustType *type = new RustEnum(name, byte_size, discr_offset, discr_byte_size); + return CacheType(type); +} + +CompilerType +RustASTContext::CreateCLikeEnumType(const lldb_private::ConstString &name, + const CompilerType &underlying_type, + std::map &&values) { + RustType *type = new RustCLikeEnum(name, underlying_type, std::move(values)); + return CacheType(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; +} + +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); + } else if (RustFunction *f = t->AsFunction()) { + return f->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(); + } else if (RustFunction *f = t->AsFunction()) { + return f->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); + } +} diff --git a/lldb/source/Symbol/Type.cpp b/lldb/source/Symbol/Type.cpp index e966c269408a4..15589b83249b0 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; diff --git a/lldb/source/lldb.cpp b/lldb/source/lldb.cpp index 1be0461977700..1b4b3b810a69c 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(); } diff --git a/lldb/unittests/CMakeLists.txt b/lldb/unittests/CMakeLists.txt index 1ffae834dfb2f..90041b45b0375 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 0000000000000..7bf33d9031cba --- /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 0000000000000..e2e4cc7f5c6a4 --- /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 0000000000000..af9c6d95c7f40 --- /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 .. )))"); +} diff --git a/llvm/cmake/modules/CheckAtomic.cmake b/llvm/cmake/modules/CheckAtomic.cmake index 9a4cdf12a6223..de8e54153a741 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 diff --git a/llvm/include/llvm/Analysis/TargetLibraryInfo.def b/llvm/include/llvm/Analysis/TargetLibraryInfo.def index 518a85ee1a016..c67d6cae9ddd7 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/include/llvm/MC/MCSubtargetInfo.h b/llvm/include/llvm/MC/MCSubtargetInfo.h index b3ce523d9c0ca..91a459f0a9b58 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 diff --git a/llvm/lib/Analysis/MemoryBuiltins.cpp b/llvm/lib/Analysis/MemoryBuiltins.cpp index 686ad294378c9..a694be8965aab 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 4643f75da42d1..5c6ed816c581e 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; } diff --git a/llvm/lib/ExecutionEngine/RuntimeDyld/RTDyldMemoryManager.cpp b/llvm/lib/ExecutionEngine/RuntimeDyld/RTDyldMemoryManager.cpp index 75d4c2b5134e1..b5d5f1b977273 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; diff --git a/llvm/lib/Support/Unix/Program.inc b/llvm/lib/Support/Unix/Program.inc index d0abc3763e821..24962ba52533f 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;