diff --git a/lldb/docs/dil-expr-lang.ebnf b/lldb/docs/dil-expr-lang.ebnf index 70eda3bf40650..660a69ea51e7f 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,10 +47,28 @@ 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 + | builtin_typename ; + +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; +builtin_typename = identifier_seq; + class_name = identifier ; enum_name = identifier ; @@ -56,6 +77,7 @@ typedef_name = identifier ; namespace_name = identifier ; - +identifier_seq = identifier + | identifier identifier_seq; diff --git a/lldb/include/lldb/ValueObject/DILAST.h b/lldb/include/lldb/ValueObject/DILAST.h index 0f05d753f1b56..919294f33508b 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, + eCastNode, eErrorNode, eFloatLiteralNode, eIdentifierNode, @@ -35,6 +36,14 @@ enum class UnaryOpKind { Deref, // "*" }; +/// The type casts allowed by DIL. +enum class CastKind { + eEnumeration, /// Casting from a scalar to an enumeration type + eNullptr, /// Casting to a nullptr type + eReference, /// Casting to a reference type + eNone, /// Type promotion casting +}; + /// Forward declaration, for use in DIL AST nodes. Definition is at the very /// end of this file. class Visitor; @@ -244,6 +253,29 @@ class BooleanLiteralNode : public ASTNode { bool m_value; }; +class CastNode : public ASTNode { +public: + CastNode(uint32_t location, CompilerType type, ASTNodeUP operand, + CastKind kind) + : ASTNode(location, NodeKind::eCastNode), 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(); } + CastKind GetCastKind() const { return m_cast_kind; } + + static bool classof(const ASTNode *node) { + return node->GetKind() == NodeKind::eCastNode; + } + +private: + CompilerType m_type; + ASTNodeUP m_operand; + CastKind 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 +299,8 @@ class Visitor { Visit(const FloatLiteralNode *node) = 0; virtual llvm::Expected Visit(const BooleanLiteralNode *node) = 0; + virtual llvm::Expected + Visit(const CastNode *node) = 0; }; } // namespace lldb_private::dil diff --git a/lldb/include/lldb/ValueObject/DILEval.h b/lldb/include/lldb/ValueObject/DILEval.h index eab3218ff828f..d61430c8b10fd 100644 --- a/lldb/include/lldb/ValueObject/DILEval.h +++ b/lldb/include/lldb/ValueObject/DILEval.h @@ -60,6 +60,8 @@ class Interpreter : Visitor { Visit(const FloatLiteralNode *node) override; llvm::Expected Visit(const BooleanLiteralNode *node) override; + llvm::Expected + Visit(const CastNode *node) override; llvm::Expected PickIntegerType(lldb::TypeSystemSP type_system, diff --git a/lldb/include/lldb/ValueObject/DILParser.h b/lldb/include/lldb/ValueObject/DILParser.h index d17ed66d9b3ee..2db23749168d4 100644 --- a/lldb/include/lldb/ValueObject/DILParser.h +++ b/lldb/include/lldb/ValueObject/DILParser.h @@ -101,6 +101,12 @@ class DILParser { ASTNodeUP ParseFloatingPointLiteral(); ASTNodeUP ParseBooleanLiteral(); + ASTNodeUP ParseCastExpression(); + std::optional ParseBuiltinType(); + std::optional ParseTypeId(); + 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..0b9e1f4d48ac8 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 CastNode::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..bfaef0c892e02 100644 --- a/lldb/source/ValueObject/DILEval.cpp +++ b/lldb/source/ValueObject/DILEval.cpp @@ -608,4 +608,16 @@ Interpreter::Visit(const BooleanLiteralNode *node) { return ValueObject::CreateValueObjectFromBool(m_target, value, "result"); } +llvm::Expected +Interpreter::Visit(const CastNode *node) { + auto operand_or_err = Evaluate(node->GetOperand()); + if (!operand_or_err) + return operand_or_err; + + lldb::ValueObjectSP operand = *operand_or_err; + // Don't actually do the cast for now -- that code will be added later. + // For now just return the original operand, unchanged. + return operand; +} + } // namespace lldb_private::dil diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp index 566bcaf81094a..7ac9c06846dc1 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" @@ -80,15 +82,63 @@ 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() { + if (!CurToken().Is(Token::l_paren)) + return ParseUnaryExpression(); + + // This could be a type cast, try parsing the contents as a type declaration. + 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), CastKind::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 +149,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 +324,81 @@ std::string DILParser::ParseNestedNameSpecifier() { } } +// Parse a type_id. +// +// type_id: +// type_specifier_seq [abstract_declarator] +// +// type_specifier_seq: +// type_specifier [type_specifier] +// +// type_specifier: +// ["::"] [nested_name_specifier] type_name // not handled for now! +// builtin_typename +// +std::optional DILParser::ParseTypeId() { + CompilerType type; + // For now only allow builtin types -- will expand add to this later. + auto maybe_builtin_type = ParseBuiltinType(); + if (maybe_builtin_type) { + type = *maybe_builtin_type; + } else + 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 built-in type +// +// builtin_typename: +// identifer_seq +// +// identifier_seq +// identifer [identifier_seq] +// +// 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 {}; +} + // Parse an id_expression. // // id_expression: @@ -339,6 +464,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: