diff --git a/lldb/docs/dil-expr-lang.ebnf b/lldb/docs/dil-expr-lang.ebnf index 70eda3bf40650..d68548f59659a 100644 --- a/lldb/docs/dil-expr-lang.ebnf +++ b/lldb/docs/dil-expr-lang.ebnf @@ -3,10 +3,13 @@ (* This is currently a subset of the final DIL Language, matching the current DIL implementation. *) -expression = unary_expression ; +expression = cast_expression; + +cast_expression = unary_expression + | "(" type_id ")" cast_expression; unary_expression = postfix_expression - | unary_operator expression ; + | unary_operator cast_expression ; unary_operator = "*" | "&" ; @@ -44,6 +47,31 @@ nested_name_specifier = type_name "::" | namespace_name '::' | nested_name_specifier identifier "::" ; +type_id = type_specifier_seq [abstract_declarator] ; + +type_specifier_seq = type_specifier [type_specifier]; + +type_specifier = ["::"] [nested_name_specifier] type_name; + | "char" + | "bool" + | "short" + | "int" + | "long" + | "signed" + | "unsigned" + | "float" + | "double" + | "void" ; + +nested_name_specifier = type_name "::" + | namespace_name "::" + | nested_name_specifier identifier "::" ; + +abstract_declarator = ptr_operator [abstract_declarator] ; + +ptr_operator = "*" + | "&"; + type_name = class_name | enum_name | typedef_name; diff --git a/lldb/include/lldb/ValueObject/DILAST.h b/lldb/include/lldb/ValueObject/DILAST.h index 0f05d753f1b56..ebd9a962ddef3 100644 --- a/lldb/include/lldb/ValueObject/DILAST.h +++ b/lldb/include/lldb/ValueObject/DILAST.h @@ -21,6 +21,7 @@ enum class NodeKind { eArraySubscriptNode, eBitExtractionNode, eBooleanLiteralNode, + eCStyleCastNode, eErrorNode, eFloatLiteralNode, eIdentifierNode, @@ -29,6 +30,21 @@ enum class NodeKind { eUnaryOpNode, }; +/// The C-Style casts allowed by DIL. +enum class CStyleCastKind { + eEnumeration, + eNullptr, + eReference, + eNone, +}; + +/// Promotions for C-Style casts in DIL. +enum class CastPromoKind { + eArithmetic, + ePointer, + eNone, +}; + /// The Unary operators recognized by DIL. enum class UnaryOpKind { AddrOf, // "&" @@ -244,6 +260,29 @@ class BooleanLiteralNode : public ASTNode { bool m_value; }; +class CStyleCastNode : public ASTNode { +public: + CStyleCastNode(uint32_t location, CompilerType type, ASTNodeUP operand, + CStyleCastKind kind) + : ASTNode(location, NodeKind::eCStyleCastNode), m_type(type), + m_operand(std::move(operand)), m_cast_kind(kind) {} + + llvm::Expected Accept(Visitor *v) const override; + + CompilerType GetType() const { return m_type; } + ASTNode *GetOperand() const { return m_operand.get(); } + CStyleCastKind GetCastKind() const { return m_cast_kind; } + + static bool classof(const ASTNode *node) { + return node->GetKind() == NodeKind::eCStyleCastNode; + } + +private: + CompilerType m_type; + ASTNodeUP m_operand; + CStyleCastKind m_cast_kind; +}; + /// This class contains one Visit method for each specialized type of /// DIL AST node. The Visit methods are used to dispatch a DIL AST node to /// the correct function in the DIL expression evaluator for evaluating that @@ -267,6 +306,8 @@ class Visitor { Visit(const FloatLiteralNode *node) = 0; virtual llvm::Expected Visit(const BooleanLiteralNode *node) = 0; + virtual llvm::Expected + Visit(const CStyleCastNode *node) = 0; }; } // namespace lldb_private::dil diff --git a/lldb/include/lldb/ValueObject/DILEval.h b/lldb/include/lldb/ValueObject/DILEval.h index eab3218ff828f..0563436b93aa3 100644 --- a/lldb/include/lldb/ValueObject/DILEval.h +++ b/lldb/include/lldb/ValueObject/DILEval.h @@ -60,12 +60,19 @@ class Interpreter : Visitor { Visit(const FloatLiteralNode *node) override; llvm::Expected Visit(const BooleanLiteralNode *node) override; + llvm::Expected + Visit(const CStyleCastNode *node) override; llvm::Expected PickIntegerType(lldb::TypeSystemSP type_system, std::shared_ptr ctx, const IntegerLiteralNode *literal); + llvm::Expected + VerifyCStyleCastType(lldb::ValueObjectSP &operand, CompilerType &op_type, + CompilerType target_type, CastPromoKind &promo_kind, + CStyleCastKind &cast_kind, int location); + // Used by the interpreter to create objects, perform casts, etc. lldb::TargetSP m_target; llvm::StringRef m_expr; diff --git a/lldb/include/lldb/ValueObject/DILParser.h b/lldb/include/lldb/ValueObject/DILParser.h index d17ed66d9b3ee..0a3ea58cb3d04 100644 --- a/lldb/include/lldb/ValueObject/DILParser.h +++ b/lldb/include/lldb/ValueObject/DILParser.h @@ -10,6 +10,7 @@ #define LLDB_VALUEOBJECT_DILPARSER_H #include "lldb/Target/ExecutionContextScope.h" +#include "lldb/Target/StackFrame.h" #include "lldb/Utility/DiagnosticsRendering.h" #include "lldb/Utility/Status.h" #include "lldb/ValueObject/DILAST.h" @@ -31,6 +32,9 @@ enum class ErrorCode : unsigned char { kUnknown, }; +llvm::Expected +GetTypeSystemFromCU(std::shared_ptr ctx); + // The following is modeled on class OptionParseError. class DILDiagnosticError : public llvm::ErrorInfo { @@ -101,6 +105,15 @@ class DILParser { ASTNodeUP ParseFloatingPointLiteral(); ASTNodeUP ParseBooleanLiteral(); + ASTNodeUP ParseCastExpression(); + std::optional ParseBuiltinType(); + std::optional ParseTypeId(); + void ParseTypeSpecifierSeq(std::string &type_name); + bool ParseTypeSpecifier(std::string &user_type_name); + std::string ParseTypeName(); + CompilerType ResolveTypeDeclarators(CompilerType type, + const std::vector &ptr_operators); + void BailOut(const std::string &error, uint32_t loc, uint16_t err_len); void Expect(Token::Kind kind); diff --git a/lldb/source/ValueObject/DILAST.cpp b/lldb/source/ValueObject/DILAST.cpp index 7ed34db6e20df..5922999baaa52 100644 --- a/lldb/source/ValueObject/DILAST.cpp +++ b/lldb/source/ValueObject/DILAST.cpp @@ -51,4 +51,8 @@ BooleanLiteralNode::Accept(Visitor *v) const { return v->Visit(this); } +llvm::Expected CStyleCastNode::Accept(Visitor *v) const { + return v->Visit(this); +} + } // namespace lldb_private::dil diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp index a9dbfad298d05..37fa3ca52300c 100644 --- a/lldb/source/ValueObject/DILEval.cpp +++ b/lldb/source/ValueObject/DILEval.cpp @@ -13,6 +13,7 @@ #include "lldb/Symbol/VariableList.h" #include "lldb/Target/RegisterContext.h" #include "lldb/ValueObject/DILAST.h" +#include "lldb/ValueObject/DILParser.h" #include "lldb/ValueObject/ValueObject.h" #include "lldb/ValueObject/ValueObjectRegister.h" #include "lldb/ValueObject/ValueObjectVariable.h" @@ -21,6 +22,42 @@ namespace lldb_private::dil { +lldb::ValueObjectSP +GetDynamicOrSyntheticValue(lldb::ValueObjectSP in_valobj_sp, + lldb::DynamicValueType use_dynamic, + bool use_synthetic) { + Status error; + if (!in_valobj_sp) { + error = Status("invalid value object"); + return in_valobj_sp; + } + lldb::ValueObjectSP value_sp = in_valobj_sp; + Target *target = value_sp->GetTargetSP().get(); + // If this ValueObject holds an error, then it is valuable for that. + if (value_sp->GetError().Fail()) + return value_sp; + + if (!target) + return lldb::ValueObjectSP(); + + if (use_dynamic != lldb::eNoDynamicValues) { + lldb::ValueObjectSP dynamic_sp = value_sp->GetDynamicValue(use_dynamic); + if (dynamic_sp) + value_sp = dynamic_sp; + } + + if (use_synthetic) { + lldb::ValueObjectSP synthetic_sp = value_sp->GetSyntheticValue(); + if (synthetic_sp) + value_sp = synthetic_sp; + } + + if (!value_sp) + error = Status("invalid value object"); + + return value_sp; +} + static lldb::VariableSP DILFindVariable(ConstString name, VariableList &variable_list) { lldb::VariableSP exact_match; @@ -499,16 +536,6 @@ Interpreter::Visit(const BitFieldExtractionNode *node) { return child_valobj_sp; } -static llvm::Expected -GetTypeSystemFromCU(std::shared_ptr ctx) { - SymbolContext symbol_context = - ctx->GetSymbolContext(lldb::eSymbolContextCompUnit); - lldb::LanguageType language = symbol_context.comp_unit->GetLanguage(); - - symbol_context = ctx->GetSymbolContext(lldb::eSymbolContextModule); - return symbol_context.module_sp->GetTypeSystemForLanguage(language); -} - static CompilerType GetBasicType(lldb::TypeSystemSP type_system, lldb::BasicType basic_type) { if (type_system) @@ -608,4 +635,205 @@ Interpreter::Visit(const BooleanLiteralNode *node) { return ValueObject::CreateValueObjectFromBool(m_target, value, "result"); } +llvm::Expected Interpreter::VerifyCStyleCastType( + lldb::ValueObjectSP &operand, CompilerType &op_type, + CompilerType target_type, CastPromoKind &promo_kind, + CStyleCastKind &cast_kind, int location) { + + promo_kind = CastPromoKind::eNone; + if (op_type.IsReferenceType()) + op_type = op_type.GetNonReferenceType(); + if (target_type.IsScalarType()) { + if (op_type.IsArrayType()) { + // Do array-to-pointer conversion. + CompilerType deref_type = + op_type.IsReferenceType() ? op_type.GetNonReferenceType() : op_type; + CompilerType result_type = + deref_type.GetArrayElementType(nullptr).GetPointerType(); + uint64_t addr = operand->GetLoadAddress(); + llvm::StringRef name = operand->GetName().GetStringRef(); + operand = ValueObject::CreateValueObjectFromAddress( + name, addr, m_exe_ctx_scope, result_type, /*do_deref=*/false); + op_type = result_type; + } + + if (op_type.IsPointerType() || op_type.IsNullPtrType()) { + // C-style cast from pointer to float/double is not allowed. + if (target_type.IsFloat()) { + std::string errMsg = llvm::formatv( + "C-style cast from {0} to {1} is not allowed", + op_type.TypeDescription(), target_type.TypeDescription()); + return llvm::make_error( + m_expr, std::move(errMsg), location, + op_type.TypeDescription().length()); + } + // Casting pointer to bool is valid. Otherwise check if the result type + // is at least as big as the pointer size. + uint64_t type_byte_size = 0; + uint64_t rhs_type_byte_size = 0; + if (auto temp = target_type.GetByteSize(m_exe_ctx_scope.get())) + type_byte_size = *temp; + if (auto temp = op_type.GetByteSize(m_exe_ctx_scope.get())) + rhs_type_byte_size = *temp; + if (!target_type.IsBoolean() && type_byte_size < rhs_type_byte_size) { + std::string errMsg = llvm::formatv( + "cast from pointer to smaller type {0} loses information", + target_type.TypeDescription()); + return llvm::make_error( + m_expr, std::move(errMsg), location, + op_type.TypeDescription().length()); + } + } else if (!op_type.IsScalarType() && !op_type.IsEnumerationType()) { + // Otherwise accept only arithmetic types and enums. + std::string errMsg = llvm::formatv( + "cannot convert {0} to {1} without a conversion operator", + op_type.TypeDescription(), target_type.TypeDescription()); + + return llvm::make_error( + m_expr, std::move(errMsg), location, + op_type.TypeDescription().length()); + } + promo_kind = CastPromoKind::eArithmetic; + } else if (target_type.IsEnumerationType()) { + // Cast to enum type. + if (!op_type.IsScalarType() && !op_type.IsEnumerationType()) { + std::string errMsg = llvm::formatv( + "C-style cast from {0} to {1} is not allowed", + op_type.TypeDescription(), target_type.TypeDescription()); + + return llvm::make_error( + m_expr, std::move(errMsg), location, + op_type.TypeDescription().length()); + } + cast_kind = CStyleCastKind::eEnumeration; + + } else if (target_type.IsPointerType()) { + if (!op_type.IsInteger() && !op_type.IsEnumerationType() && + !op_type.IsArrayType() && !op_type.IsPointerType() && + !op_type.IsNullPtrType()) { + std::string errMsg = llvm::formatv( + "cannot cast from type {0} to pointer type {1}", + op_type.TypeDescription(), target_type.TypeDescription()); + + return llvm::make_error( + m_expr, std::move(errMsg), location, + op_type.TypeDescription().length()); + } + promo_kind = CastPromoKind::ePointer; + + } else if (target_type.IsNullPtrType()) { + // Cast to nullptr type. + bool is_signed; + if (!target_type.IsNullPtrType() && + (!operand->IsIntegerType(is_signed) || + (is_signed && operand->GetValueAsSigned(0) != 0) || + (!is_signed && operand->GetValueAsUnsigned(0) != 0))) { + std::string errMsg = llvm::formatv( + "C-style cast from {0} to {1} is not allowed", + op_type.TypeDescription(), target_type.TypeDescription()); + + return llvm::make_error( + m_expr, std::move(errMsg), location, + op_type.TypeDescription().length()); + } + cast_kind = CStyleCastKind::eNullptr; + + } else if (target_type.IsReferenceType()) { + // Cast to a reference type. + cast_kind = CStyleCastKind::eReference; + } else { + // Unsupported cast. + std::string errMsg = + llvm::formatv("casting of {0} to {1} is not implemented yet", + op_type.TypeDescription(), target_type.TypeDescription()); + return llvm::make_error( + m_expr, std::move(errMsg), location, + op_type.TypeDescription().length()); + } + + return target_type; +} + +llvm::Expected +Interpreter::Visit(const CStyleCastNode *node) { + auto operand_or_err = Evaluate(node->GetOperand()); + if (!operand_or_err) + return operand_or_err; + + lldb::ValueObjectSP operand = *operand_or_err; + CompilerType op_type = operand->GetCompilerType(); + CStyleCastKind cast_kind = CStyleCastKind::eNone; + CastPromoKind promo_kind = CastPromoKind::eNone; + + auto type_or_err = + VerifyCStyleCastType(operand, op_type, node->GetType(), promo_kind, + cast_kind, node->GetLocation()); + if (!type_or_err) + return type_or_err.takeError(); + + CompilerType target_type = *type_or_err; + if (op_type.IsReferenceType()) { + Status error; + operand = operand->Dereference(error); + if (error.Fail()) + return llvm::make_error(m_expr, error.AsCString(), + node->GetLocation()); + } + + switch (cast_kind) { + case CStyleCastKind::eEnumeration: { + if (op_type.IsFloat() || op_type.IsInteger() || op_type.IsEnumerationType()) + return operand->CastToEnumType(target_type); + + std::string errMsg = + "invalid ast: operand is not convertible to enumeration type"; + return llvm::make_error(m_expr, std::move(errMsg), + node->GetLocation()); + } + case CStyleCastKind::eNullptr: { + return ValueObject::CreateValueObjectFromNullptr(m_target, target_type, + "result"); + } + case CStyleCastKind::eReference: { + lldb::ValueObjectSP operand_sp( + GetDynamicOrSyntheticValue(operand, m_use_dynamic, m_use_synthetic)); + return lldb::ValueObjectSP( + operand_sp->Cast(target_type.GetNonReferenceType())); + } + case CStyleCastKind::eNone: { + switch (promo_kind) { + case CastPromoKind::eArithmetic: { + if (op_type.IsPointerType() || op_type.IsNullPtrType() || + op_type.IsScalarType() || op_type.IsEnumerationType()) { + return operand->CastToBasicType(target_type); + } + + std::string errMsg = + "invalid ast: operand is not convertible to arithmetic type"; + return llvm::make_error(m_expr, std::move(errMsg), + node->GetLocation()); + } + case CastPromoKind::ePointer: { + uint64_t addr = + op_type.IsArrayType() + ? operand->GetLoadAddress() + : (op_type.IsSigned() ? operand->GetValueAsSigned(0) + : operand->GetValueAsUnsigned(0)); + llvm::StringRef name = "result"; + ExecutionContext exe_ctx(m_target.get(), false); + return ValueObject::CreateValueObjectFromAddress(name, addr, exe_ctx, + target_type, + /* do_deref */ false); + } + case CastPromoKind::eNone: { + return lldb::ValueObjectSP(); + } + } // switch promo_kind + } // case CStyleCastKind::eNone + } // switch cast_kind + std::string errMsg = "invalid ast: unexpected c-style cast kind"; + return llvm::make_error(m_expr, std::move(errMsg), + node->GetLocation()); +} + } // namespace lldb_private::dil diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp index 566bcaf81094a..61af022d337d1 100644 --- a/lldb/source/ValueObject/DILParser.cpp +++ b/lldb/source/ValueObject/DILParser.cpp @@ -12,7 +12,9 @@ //===----------------------------------------------------------------------===// #include "lldb/ValueObject/DILParser.h" +#include "lldb/Symbol/CompileUnit.h" #include "lldb/Target/ExecutionContextScope.h" +#include "lldb/Target/LanguageRuntime.h" #include "lldb/Utility/DiagnosticsRendering.h" #include "lldb/ValueObject/DILAST.h" #include "lldb/ValueObject/DILEval.h" @@ -42,6 +44,72 @@ DILDiagnosticError::DILDiagnosticError(llvm::StringRef expr, m_detail.rendered = std::move(rendered_msg); } +llvm::Expected +GetTypeSystemFromCU(std::shared_ptr ctx) { + SymbolContext symbol_context = + ctx->GetSymbolContext(lldb::eSymbolContextCompUnit); + lldb::LanguageType language = symbol_context.comp_unit->GetLanguage(); + + symbol_context = ctx->GetSymbolContext(lldb::eSymbolContextModule); + return symbol_context.module_sp->GetTypeSystemForLanguage(language); +} + +CompilerType +ResolveTypeByName(const std::string &name, + std::shared_ptr ctx_scope) { + // Internally types don't have global scope qualifier in their names and + // LLDB doesn't support queries with it too. + llvm::StringRef name_ref(name); + + if (name_ref.starts_with("::")) + name_ref = name_ref.drop_front(2); + + std::vector result_type_list; + lldb::TargetSP target_sp = ctx_scope->CalculateTarget(); + const char *type_name = name_ref.data(); + if (type_name && type_name[0] && target_sp) { + ModuleList &images = target_sp->GetImages(); + ConstString const_type_name(type_name); + TypeQuery query(type_name); + TypeResults results; + images.FindTypes(nullptr, query, results); + for (const lldb::TypeSP &type_sp : results.GetTypeMap().Types()) + if (type_sp) + result_type_list.push_back(type_sp->GetFullCompilerType()); + + if (auto process_sp = target_sp->GetProcessSP()) { + for (auto *runtime : process_sp->GetLanguageRuntimes()) { + if (auto *vendor = runtime->GetDeclVendor()) { + auto types = vendor->FindTypes(const_type_name, UINT32_MAX); + for (auto type : types) + result_type_list.push_back(type); + } + } + } + } + + // We've found multiple types, try finding the "correct" one. + CompilerType full_match; + std::vector partial_matches; + + for (uint32_t i = 0; i < result_type_list.size(); ++i) { + CompilerType type = result_type_list[i]; + llvm::StringRef type_name_ref = type.GetTypeName().GetStringRef(); + + if (type_name_ref == name_ref && type.IsValid()) + return type; + + if (type_name_ref.ends_with(name_ref)) + partial_matches.push_back(type); + } + + // If we have partial matches, pick a "random" one. + if (partial_matches.size() > 0) + return partial_matches.back(); + + return {}; +} + llvm::Expected DILParser::Parse(llvm::StringRef dil_input_expr, DILLexer lexer, std::shared_ptr frame_sp, @@ -80,15 +148,62 @@ ASTNodeUP DILParser::Run() { // Parse an expression. // // expression: -// unary_expression +// cast_expression // -ASTNodeUP DILParser::ParseExpression() { return ParseUnaryExpression(); } +ASTNodeUP DILParser::ParseExpression() { return ParseCastExpression(); } + +// Parse a cast_expression. +// +// cast_expression: +// unary_expression +// "(" type_id ")" cast_expression + +ASTNodeUP DILParser::ParseCastExpression() { + // This can be a C-style cast, try parsing the contents as a type declaration. + if (CurToken().Is(Token::l_paren)) { + Token token = CurToken(); + uint32_t loc = token.GetLocation(); + + // Enable lexer backtracking, so that we can rollback in case it's not + // actually a type declaration. + + // Start tentative parsing (save token location/idx, for possible rollback). + uint32_t save_token_idx = m_dil_lexer.GetCurrentTokenIdx(); + + // Consume the token only after enabling the backtracking. + m_dil_lexer.Advance(); + + // Try parsing the type declaration. If the returned value is not valid, + // then we should rollback and try parsing the expression. + auto type_id = ParseTypeId(); + if (type_id) { + // Successfully parsed the type declaration. Commit the backtracked + // tokens and parse the cast_expression. + + if (!type_id.value().IsValid()) + return std::make_unique(); + + Expect(Token::r_paren); + m_dil_lexer.Advance(); + auto rhs = ParseCastExpression(); + + return std::make_unique( + loc, type_id.value(), std::move(rhs), CStyleCastKind::eNone); + } + + // Failed to parse the contents of the parentheses as a type declaration. + // Rollback the lexer and try parsing it as unary_expression. + TentativeParsingRollback(save_token_idx); + } + + return ParseUnaryExpression(); +} // Parse an unary_expression. // // unary_expression: // postfix_expression -// unary_operator expression +// unary_operator cast_expression // // unary_operator: // "&" @@ -99,7 +214,7 @@ ASTNodeUP DILParser::ParseUnaryExpression() { Token token = CurToken(); uint32_t loc = token.GetLocation(); m_dil_lexer.Advance(); - auto rhs = ParseExpression(); + auto rhs = ParseCastExpression(); switch (token.GetKind()) { case Token::star: return std::make_unique(loc, UnaryOpKind::Deref, @@ -274,6 +389,167 @@ std::string DILParser::ParseNestedNameSpecifier() { } } +// Parse a built-in type +// +// A built-in type can be a single identifier or a space-separated +// list of identifiers (e.g. "short" or "long long"). +std::optional DILParser::ParseBuiltinType() { + std::string type_name = ""; + uint32_t save_token_idx = m_dil_lexer.GetCurrentTokenIdx(); + bool first_word = true; + while (CurToken().GetKind() == Token::identifier) { + if (CurToken().GetSpelling() == "const" || + CurToken().GetSpelling() == "volatile") + continue; + if (!first_word) + type_name.push_back(' '); + else + first_word = false; + type_name.append(CurToken().GetSpelling()); + m_dil_lexer.Advance(); + } + + if (type_name.size() > 0) { + lldb::TargetSP target_sp = m_ctx_scope->CalculateTarget(); + ConstString const_type_name(type_name.c_str()); + for (auto type_system_sp : target_sp->GetScratchTypeSystems()) + if (auto compiler_type = + type_system_sp->GetBuiltinTypeByName(const_type_name)) + return compiler_type; + } + + TentativeParsingRollback(save_token_idx); + return {}; +} + +std::optional DILParser::ParseTypeId() { + CompilerType type; + auto maybe_builtin_type = ParseBuiltinType(); + if (maybe_builtin_type) { + type = *maybe_builtin_type; + } else { + // Check to see if we have a user-defined type here. + // First build up the user-defined type name. + std::string type_name; + ParseTypeSpecifierSeq(type_name); + + if (type_name.size() == 0) + return {}; + type = ResolveTypeByName(type_name, m_ctx_scope); + if (!type.IsValid()) + return {}; + + // Same-name identifiers should be preferred over typenames. + if (LookupIdentifier(type_name, m_ctx_scope, m_use_dynamic)) + // TODO: Make type accessible with 'class', 'struct' and 'union' keywords. + return {}; + + // Same-name identifiers should be preferred over typenames. + if (LookupGlobalIdentifier(type_name, m_ctx_scope, + m_ctx_scope->CalculateTarget(), m_use_dynamic)) + // TODO: Make type accessible with 'class', 'struct' and 'union' keywords + return {}; + } + + // + // abstract_declarator: + // ptr_operator [abstract_declarator] + // + std::vector ptr_operators; + while (CurToken().IsOneOf({Token::star, Token::amp})) { + Token tok = CurToken(); + ptr_operators.push_back(std::move(tok)); + m_dil_lexer.Advance(); + } + type = ResolveTypeDeclarators(type, ptr_operators); + + return type; +} + +// Parse a type_specifier_seq. +// +// type_specifier_seq: +// type_specifier [type_specifier_seq] +// +void DILParser::ParseTypeSpecifierSeq(std::string &type_name) { + while (true) { + bool type_specifier = ParseTypeSpecifier(type_name); + if (!type_specifier) { + break; + } + } +} + +// Parse a type_specifier. +// +// type_specifier: +// ["::"] [nested_name_specifier] type_name +// +// Returns TRUE if a type_specifier was successfully parsed at this location. +// +bool DILParser::ParseTypeSpecifier(std::string &user_type_name) { + // The type_specifier must be a user-defined type. Try parsing a + // simple_type_specifier. + { + // Try parsing optional global scope operator. + bool global_scope = false; + if (CurToken().Is(Token::coloncolon)) { + global_scope = true; + m_dil_lexer.Advance(); + } + + // uint32_t loc = CurToken().GetLocation(); + + // Try parsing optional nested_name_specifier. + auto nested_name_specifier = ParseNestedNameSpecifier(); + + // Try parsing required type_name. + auto type_name = ParseTypeName(); + + // If there is a type_name, then this is indeed a simple_type_specifier. + // Global and qualified (namespace/class) scopes can be empty, since they're + // optional. In this case type_name is type we're looking for. + if (!type_name.empty()) { + // User-defined typenames can't be combined with builtin keywords. + user_type_name = llvm::formatv("{0}{1}{2}", global_scope ? "::" : "", + nested_name_specifier, type_name); + return true; + } + } + + // No type_specifier was found here. + return false; +} + +// Parse a type_name. +// +// type_name: +// class_name +// enum_name +// typedef_name +// +// class_name +// identifier +// +// enum_name +// identifier +// +// typedef_name +// identifier +// +std::string DILParser::ParseTypeName() { + // Typename always starts with an identifier. + if (CurToken().IsNot(Token::identifier)) { + return ""; + } + + // Otherwise look for a class_name, enum_name or a typedef_name. + std::string identifier = CurToken().GetSpelling(); + m_dil_lexer.Advance(); + + return identifier; +} + // Parse an id_expression. // // id_expression: @@ -339,6 +615,40 @@ std::string DILParser::ParseUnqualifiedId() { return identifier; } +CompilerType +DILParser::ResolveTypeDeclarators(CompilerType type, + const std::vector &ptr_operators) { + CompilerType bad_type; + // Resolve pointers/references. + for (Token tk : ptr_operators) { + uint32_t loc = tk.GetLocation(); + if (tk.GetKind() == Token::star) { + // Pointers to reference types are forbidden. + if (type.IsReferenceType()) { + BailOut(llvm::formatv("'type name' declared as a pointer to a " + "reference of type {0}", + type.TypeDescription()), + loc, CurToken().GetSpelling().length()); + return bad_type; + } + // Get pointer type for the base type: e.g. int* -> int**. + type = type.GetPointerType(); + + } else if (tk.GetKind() == Token::amp) { + // References to references are forbidden. + if (type.IsReferenceType()) { + BailOut("type name declared as a reference to a reference", loc, + CurToken().GetSpelling().length()); + return bad_type; + } + // Get reference type for the base type: e.g. int -> int&. + type = type.GetLValueReferenceType(); + } + } + + return type; +} + // Parse an boolean_literal. // // boolean_literal: diff --git a/lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py b/lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py index 0f6618fe47984..85899caaa7433 100644 --- a/lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py +++ b/lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py @@ -28,4 +28,5 @@ def test_frame_var(self): self.expect_var_path("a", value="1") self.expect_var_path("b", value="2") self.expect_var_path("c", value="'\\xfd'") + self.expect_var_path("(int)c", value="-3") self.expect_var_path("s", value="4") diff --git a/lldb/test/API/commands/frame/var-dil/expr/CStyleCast/Makefile b/lldb/test/API/commands/frame/var-dil/expr/CStyleCast/Makefile new file mode 100644 index 0000000000000..0165eb73f3073 --- /dev/null +++ b/lldb/test/API/commands/frame/var-dil/expr/CStyleCast/Makefile @@ -0,0 +1,6 @@ +CXX_SOURCES := main.cpp +#CXXFLAGS_EXTRAS := -std=c++14 + +USE_LIBSTDCPP := 1 + +include Makefile.rules diff --git a/lldb/test/API/commands/frame/var-dil/expr/CStyleCast/TestFrameVarDILCStyleCast.py b/lldb/test/API/commands/frame/var-dil/expr/CStyleCast/TestFrameVarDILCStyleCast.py new file mode 100644 index 0000000000000..84b4ecff508a4 --- /dev/null +++ b/lldb/test/API/commands/frame/var-dil/expr/CStyleCast/TestFrameVarDILCStyleCast.py @@ -0,0 +1,246 @@ +""" +Make sure 'frame var' using DIL parser/evaultor works for C-Style casts. +""" + +import lldb +from lldbsuite.test.lldbtest import * +from lldbsuite.test.decorators import * +from lldbsuite.test import lldbutil + +import os +import shutil +import time + + +class TestFrameVarDILCStyleCast(TestBase): + def test_type_cast(self): + self.build() + (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( + self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp") + ) + + self.runCmd("settings set target.experimental.use-DIL true") + + # TestCStyleCastBUiltins + + self.expect_var_path("(int)1", value="1", type="int") + self.expect_var_path("(long long)1", value="1", type="long long") + self.expect_var_path("(unsigned long)1", value="1", type="unsigned long") + self.expect_var_path("(char*)1", value="0x0000000000000001", type="char *") + self.expect_var_path( + "(long long**)1", value="0x0000000000000001", type="long long **" + ) + + self.expect( + "frame variable '(long&*)1'", + error=True, + substrs=[ + "'type name' declared as a pointer to a reference of type 'long &'" + ], + ) + + self.expect( + "frame variable '(long& &)1'", + error=True, + substrs=["type name declared as a reference to a reference"], + ) + + self.expect( + "frame variable '(long 1)1'", + error=True, + substrs=["expected 'r_paren', got: <'1' (integer_constant)>"], + ) + + # TestCStyleCastBasicType + + # Test with integer literals. + self.expect_var_path("(char)1", type="char", value="'\\x01'") + self.expect_var_path("(long long)1", type="long long", value="1") + self.expect_var_path("(short)65534", type="short", value="-2") + self.expect_var_path( + "(unsigned short)100000", type="unsigned short", value="34464" + ) + self.expect_var_path("(int)false", type="int", value="0") + self.expect_var_path("(int)true", type="int", value="1") + self.expect_var_path("(float)1", type="float", value="1") + self.expect_var_path("(float)1.1", type="float", value="1.10000002") + self.expect_var_path("(float)1.1f", type="float", value="1.10000002") + self.expect_var_path("(float)false", type="float", value="0") + self.expect_var_path("(float)true", type="float", value="1") + self.expect_var_path("(double)1", type="double", value="1") + self.expect_var_path("(double)1.1", type="double", value="1.1000000000000001") + self.expect_var_path("(double)1.1f", type="double", value="1.1000000238418579") + self.expect_var_path("(double)false", type="double", value="0") + self.expect_var_path("(double)true", type="double", value="1") + self.expect_var_path("(int)1.1", type="int", value="1") + self.expect_var_path("(int)1.1f", type="int", value="1") + self.expect_var_path("(long)1.1", type="long", value="1") + self.expect_var_path("(bool)0", type="bool", value="false") + self.expect_var_path("(bool)0.0", type="bool", value="false") + self.expect_var_path("(bool)0.0f", type="bool", value="false") + self.expect_var_path("(bool)3", type="bool", value="true") + + self.expect( + "frame variable '&(int)1'", + error=True, + substrs=["'result' doesn't have a valid address"], + ) + + # Test with variables. + self.expect_var_path("(char)a", type="char", value="'\\x01'") + self.expect_var_path("(unsigned char)na", type="unsigned char", value="'\\xff'") + self.expect_var_path("(short)na", type="short", value="-1") + self.expect_var_path("(long long)a", type="long long", value="1") + self.expect_var_path("(float)a", type="float", value="1") + self.expect_var_path("(float)f", type="float", value="1.10000002") + self.expect_var_path("(double)f", type="double", value="1.1000000238418579") + self.expect_var_path("(int)f", type="int", value="1") + self.expect_var_path("(long)f", type="long", value="1") + self.expect_var_path("(bool)finf", type="bool", value="true") + self.expect_var_path("(bool)fnan", type="bool", value="true") + self.expect_var_path("(bool)fsnan", type="bool", value="true") + self.expect_var_path("(bool)fmax", type="bool", value="true") + self.expect_var_path("(bool)fdenorm", type="bool", value="true") + self.expect( + "frame variable '(int)ns_foo_'", + error=True, + substrs=["cannot convert 'ns::Foo' to 'int' without a conversion operator"], + ) + + # Test with typedefs and namespaces. + self.expect_var_path("(myint)1", type="myint", value="1") + self.expect_var_path("(myint)1LL", type="myint", value="1") + self.expect_var_path("(ns::myint)1", type="ns::myint", value="1") + self.expect_var_path("(::ns::myint)1", type="ns::myint", value="1") + self.expect_var_path("(::ns::myint)myint_", type="ns::myint", value="1") + + self.expect_var_path("(int)myint_", type="int", value="1") + self.expect_var_path("(int)ns_myint_", type="int", value="2") + self.expect_var_path("(long long)myint_", type="long long", value="1") + self.expect_var_path("(long long)ns_myint_", type="long long", value="2") + self.expect_var_path("(::ns::myint)myint_", type="ns::myint", value="1") + + self.expect_var_path( + "(ns::inner::mydouble)1", type="ns::inner::mydouble", value="1" + ) + self.expect_var_path( + "(::ns::inner::mydouble)1.2", type="ns::inner::mydouble", value="1.2" + ) + self.expect_var_path( + "(ns::inner::mydouble)myint_", type="ns::inner::mydouble", value="1" + ) + self.expect_var_path( + "(::ns::inner::mydouble)ns_inner_mydouble_", + type="ns::inner::mydouble", + value="1.2", + ) + self.expect_var_path("(myint)ns_inner_mydouble_", type="myint", value="1") + + # Test with pointers and arrays. + self.expect_var_path("(long long)ap", type="long long") + self.expect_var_path("(unsigned long long)vp", type="unsigned long long") + self.expect_var_path("(long long)arr", type="long long") + self.expect_var_path("(bool)ap", type="bool", value="true") + self.expect_var_path("(bool)(int*)0x00000000", type="bool", value="false") + self.expect_var_path("(bool)arr", type="bool", value="true") + self.expect( + "frame variable '(char)ap'", + error=True, + substrs=["cast from pointer to smaller type 'char' loses information"], + ) + Is32Bit = False + if self.target().GetAddressByteSize() == 4: + Is32Bit = True + + if Is32Bit: + self.expect("frame variable '(int)arr'", type="int") + else: + self.expect( + "frame variable '(int)arr'", + error=True, + substrs=["cast from pointer to smaller type 'int' loses information"], + ) + + self.expect( + "frame variable '(float)ap'", + error=True, + substrs=["C-style cast from 'int *' to 'float' is not allowed"], + ) + self.expect( + "frame variable '(float)arr'", + error=True, + substrs=["C-style cast from 'int *' to 'float' is not allowed"], + ) + + # TestCStyleCastPointer + self.expect_var_path("(void*)&a", type="void *") + self.expect_var_path("(void*)ap", type="void *") + self.expect_var_path("(long long*)vp", type="long long *") + self.expect_var_path("(short int*)vp", type="short *") + self.expect_var_path("(unsigned long long*)vp", type="unsigned long long *") + self.expect_var_path("(unsigned short int*)vp", type="unsigned short *") + + if Is32Bit: + self.expect_var_path("(void*)0", type="void *", value="0x00000000") + self.expect_var_path("(void*)1", type="void *", value="0x00000001") + self.expect_var_path("(void*)a", type="void *", value="0x00000001") + self.expect_var_path("(void*)na", type="void *", value="0xffffffff") + else: + self.expect_var_path("(void*)0", type="void *", value="0x0000000000000000") + self.expect_var_path("(void*)1", type="void *", value="0x0000000000000001") + self.expect_var_path("(void*)a", type="void *", value="0x0000000000000001") + self.expect_var_path("(void*)na", type="void *", value="0xffffffffffffffff") + + self.expect_var_path("(int*&)ap", type="int *") + + self.expect( + "frame variable '(char*) 1.0'", + error=True, + substrs=["cannot cast from type 'double' to pointer type 'char *'"], + ) + + self.expect_var_path("*(int*)(void*)ap", type="int", value="1") + + self.expect_var_path("(ns::Foo*)ns_inner_foo_ptr_", type="ns::Foo *") + self.expect_var_path("(ns::inner::Foo*)ns_foo_ptr_", type="ns::inner::Foo *") + + self.expect( + "frame variable '(int& &)ap'", + error=True, + substrs=["type name declared as a reference to a reference"], + ) + self.expect( + "frame variable '(int&*)ap'", + error=True, + substrs=[ + "'type name' declared as a pointer to a reference of type 'int &'" + ], + ) + + if Is32Bit: + self.expect_var_path("(void *)0", type="void *", value="0x00000000") + else: + self.expect_var_path("(void *)0", type="void *", value="0x0000000000000000") + + # TestCStyleCastArray + + self.expect_var_path("(int*)arr_1d", type="int *") + self.expect_var_path("(char*)arr_1d", type="char *") + self.expect_var_path("((char*)arr_1d)[0]", type="char", value="'\\x01'") + self.expect_var_path("((char*)arr_1d)[1]", type="char", value="'\\0'") + + # 2D arrays. + self.expect_var_path("(int*)arr_2d", type="int *") + self.expect_var_path("((int*)arr_2d)[1]", type="int", value="2") + self.expect_var_path("((int*)arr_2d)[2]", type="int", value="3") + self.expect_var_path("((int*)arr_2d[1])[1]", type="int", value="5") + + # TestCStyleCastReference + + self.expect_var_path("((InnerFoo&)arr_1d[1]).a", type="int", value="2") + self.expect_var_path("((InnerFoo&)arr_1d[1]).b", type="int", value="3") + + self.expect_var_path("(int&)arr_1d[0]", type="int", value="1") + self.expect_var_path("(int&)arr_1d[1]", type="int", value="2") + + self.expect_var_path("&(int&)arr_1d", type="int *") diff --git a/lldb/test/API/commands/frame/var-dil/expr/CStyleCast/main.cpp b/lldb/test/API/commands/frame/var-dil/expr/CStyleCast/main.cpp new file mode 100644 index 0000000000000..6b865badc76e5 --- /dev/null +++ b/lldb/test/API/commands/frame/var-dil/expr/CStyleCast/main.cpp @@ -0,0 +1,72 @@ +// CStyleCast, main.cpp + +#include +#include +#include +#include +#include +#include +#include + +namespace ns { + +typedef int myint; + +class Foo {}; + +namespace inner { + +using mydouble = double; + +class Foo {}; + +} // namespace inner + +} // namespace ns + +int main(int argc, char **argv) { + int a = 1; + int *ap = &a; + void *vp = &a; + int arr[2] = {1, 2}; + + int na = -1; + float f = 1.1; + + typedef int myint; + std::nullptr_t std_nullptr_t = nullptr; + bool found_it = false; + if (std_nullptr_t) { + found_it = true; + } else { + found_it = (bool)0; + } + + myint myint_ = 1; + ns::myint ns_myint_ = 2; + ns::Foo ns_foo_; + ns::Foo *ns_foo_ptr_ = &ns_foo_; + + ns::inner::mydouble ns_inner_mydouble_ = 1.2; + ns::inner::Foo ns_inner_foo_; + ns::inner::Foo *ns_inner_foo_ptr_ = &ns_inner_foo_; + + float finf = std::numeric_limits::infinity(); + float fnan = std::numeric_limits::quiet_NaN(); + float fsnan = std::numeric_limits::signaling_NaN(); + float fmax = std::numeric_limits::max(); + float fdenorm = std::numeric_limits::denorm_min(); + + struct InnerFoo { + int a; + int b; + }; + + InnerFoo ifoo; + (void)ifoo; + + int arr_1d[] = {1, 2, 3, 4}; + int arr_2d[2][3] = {{1, 2, 3}, {4, 5, 6}}; + + return 0; // Set a breakpoint here +}