diff --git a/lldb/docs/dil-expr-lang.ebnf b/lldb/docs/dil-expr-lang.ebnf index 783432dabd6db..da1796d936c6a 100644 --- a/lldb/docs/dil-expr-lang.ebnf +++ b/lldb/docs/dil-expr-lang.ebnf @@ -31,6 +31,9 @@ identifier = ? C99 Identifier ? ; integer_literal = ? Integer constant: hexademical, decimal, octal, binary ? ; +numeric_literal = ? Integer constant: hexademical, decimal, octal, binary ? + | ? Floating constant ? ; + register = "$" ? Register name ? ; nested_name_specifier = type_name "::" diff --git a/lldb/include/lldb/ValueObject/DILAST.h b/lldb/include/lldb/ValueObject/DILAST.h index 709f0639135f1..1d10755c46e39 100644 --- a/lldb/include/lldb/ValueObject/DILAST.h +++ b/lldb/include/lldb/ValueObject/DILAST.h @@ -21,7 +21,9 @@ enum class NodeKind { eArraySubscriptNode, eBitExtractionNode, eErrorNode, + eFloatLiteralNode, eIdentifierNode, + eIntegerLiteralNode, eMemberOfNode, eUnaryOpNode, }; @@ -178,6 +180,52 @@ class BitFieldExtractionNode : public ASTNode { int64_t m_last_index; }; +enum class IntegerTypeSuffix { None, Long, LongLong }; + +class IntegerLiteralNode : public ASTNode { +public: + IntegerLiteralNode(uint32_t location, llvm::APInt value, uint32_t radix, + bool is_unsigned, IntegerTypeSuffix type) + : ASTNode(location, NodeKind::eIntegerLiteralNode), + m_value(std::move(value)), m_radix(radix), m_is_unsigned(is_unsigned), + m_type(type) {} + + llvm::Expected Accept(Visitor *v) const override; + + const llvm::APInt &GetValue() const { return m_value; } + uint32_t GetRadix() const { return m_radix; } + bool IsUnsigned() const { return m_is_unsigned; } + IntegerTypeSuffix GetTypeSuffix() const { return m_type; } + + static bool classof(const ASTNode *node) { + return node->GetKind() == NodeKind::eIntegerLiteralNode; + } + +private: + llvm::APInt m_value; + uint32_t m_radix; + bool m_is_unsigned; + IntegerTypeSuffix m_type; +}; + +class FloatLiteralNode : public ASTNode { +public: + FloatLiteralNode(uint32_t location, llvm::APFloat value) + : ASTNode(location, NodeKind::eFloatLiteralNode), + m_value(std::move(value)) {} + + llvm::Expected Accept(Visitor *v) const override; + + const llvm::APFloat &GetValue() const { return m_value; } + + static bool classof(const ASTNode *node) { + return node->GetKind() == NodeKind::eFloatLiteralNode; + } + +private: + llvm::APFloat m_value; +}; + /// 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 @@ -195,6 +243,10 @@ class Visitor { Visit(const ArraySubscriptNode *node) = 0; virtual llvm::Expected Visit(const BitFieldExtractionNode *node) = 0; + virtual llvm::Expected + Visit(const IntegerLiteralNode *node) = 0; + virtual llvm::Expected + Visit(const FloatLiteralNode *node) = 0; }; } // namespace lldb_private::dil diff --git a/lldb/include/lldb/ValueObject/DILEval.h b/lldb/include/lldb/ValueObject/DILEval.h index 45e29b3ddcd7b..5a48c2c989f4d 100644 --- a/lldb/include/lldb/ValueObject/DILEval.h +++ b/lldb/include/lldb/ValueObject/DILEval.h @@ -54,6 +54,15 @@ class Interpreter : Visitor { Visit(const ArraySubscriptNode *node) override; llvm::Expected Visit(const BitFieldExtractionNode *node) override; + llvm::Expected + Visit(const IntegerLiteralNode *node) override; + llvm::Expected + Visit(const FloatLiteralNode *node) override; + + llvm::Expected + PickIntegerType(lldb::TypeSystemSP type_system, + std::shared_ptr ctx, + const IntegerLiteralNode *literal); // Used by the interpreter to create objects, perform casts, etc. lldb::TargetSP m_target; diff --git a/lldb/include/lldb/ValueObject/DILLexer.h b/lldb/include/lldb/ValueObject/DILLexer.h index 9c1ba97680253..cbffb6375778c 100644 --- a/lldb/include/lldb/ValueObject/DILLexer.h +++ b/lldb/include/lldb/ValueObject/DILLexer.h @@ -28,12 +28,14 @@ class Token { arrow, coloncolon, eof, + floating_constant, identifier, + integer_constant, l_paren, l_square, minus, - numeric_constant, period, + plus, r_paren, r_square, star, diff --git a/lldb/include/lldb/ValueObject/DILParser.h b/lldb/include/lldb/ValueObject/DILParser.h index 9eda7bac4a364..90df109337dcf 100644 --- a/lldb/include/lldb/ValueObject/DILParser.h +++ b/lldb/include/lldb/ValueObject/DILParser.h @@ -96,6 +96,9 @@ class DILParser { std::string ParseIdExpression(); std::string ParseUnqualifiedId(); std::optional ParseIntegerConstant(); + ASTNodeUP ParseNumericLiteral(); + ASTNodeUP ParseIntegerLiteral(); + ASTNodeUP ParseFloatingPointLiteral(); void BailOut(const std::string &error, uint32_t loc, uint16_t err_len); diff --git a/lldb/source/ValueObject/DILAST.cpp b/lldb/source/ValueObject/DILAST.cpp index b1cd824c2299e..70564663a62cd 100644 --- a/lldb/source/ValueObject/DILAST.cpp +++ b/lldb/source/ValueObject/DILAST.cpp @@ -37,4 +37,13 @@ BitFieldExtractionNode::Accept(Visitor *v) const { return v->Visit(this); } +llvm::Expected +IntegerLiteralNode::Accept(Visitor *v) const { + return v->Visit(this); +} + +llvm::Expected FloatLiteralNode::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 6f28434c646cd..edcd67dc88e51 100644 --- a/lldb/source/ValueObject/DILEval.cpp +++ b/lldb/source/ValueObject/DILEval.cpp @@ -7,7 +7,9 @@ //===----------------------------------------------------------------------===// #include "lldb/ValueObject/DILEval.h" +#include "lldb/Core/Module.h" #include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/TypeSystem.h" #include "lldb/Symbol/VariableList.h" #include "lldb/Target/RegisterContext.h" #include "lldb/ValueObject/DILAST.h" @@ -402,4 +404,125 @@ 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) + if (auto compiler_type = type_system.get()->GetBasicTypeFromAST(basic_type)) + return compiler_type; + + CompilerType empty_type; + return empty_type; +} + +llvm::Expected +Interpreter::PickIntegerType(lldb::TypeSystemSP type_system, + std::shared_ptr ctx, + const IntegerLiteralNode *literal) { + // Binary, Octal, Hexadecimal and literals with a U suffix are allowed to be + // an unsigned integer. + bool unsigned_is_allowed = literal->IsUnsigned() || literal->GetRadix() != 10; + llvm::APInt apint = literal->GetValue(); + + // Try int/unsigned int. + llvm::Expected int_size = + GetBasicType(type_system, lldb::eBasicTypeInt).GetBitSize(ctx.get()); + if (!int_size) + return int_size.takeError(); + if (literal->GetTypeSuffix() == IntegerTypeSuffix::None && + apint.isIntN(*int_size)) { + if (!literal->IsUnsigned() && apint.isIntN(*int_size - 1)) + return GetBasicType(type_system, lldb::eBasicTypeInt); + if (unsigned_is_allowed) + return GetBasicType(type_system, lldb::eBasicTypeUnsignedInt); + } + // Try long/unsigned long. + llvm::Expected long_size = + GetBasicType(type_system, lldb::eBasicTypeLong).GetBitSize(ctx.get()); + if (!long_size) + return long_size.takeError(); + if (literal->GetTypeSuffix() != IntegerTypeSuffix::LongLong && + apint.isIntN(*long_size)) { + if (!literal->IsUnsigned() && apint.isIntN(*long_size - 1)) + return GetBasicType(type_system, lldb::eBasicTypeLong); + if (unsigned_is_allowed) + return GetBasicType(type_system, lldb::eBasicTypeUnsignedLong); + } + // Try long long/unsigned long long. + llvm::Expected long_long_size = + GetBasicType(type_system, lldb::eBasicTypeLongLong).GetBitSize(ctx.get()); + if (!long_long_size) + return long_long_size.takeError(); + if (apint.isIntN(*long_long_size)) { + if (!literal->IsUnsigned() && apint.isIntN(*long_long_size - 1)) + return GetBasicType(type_system, lldb::eBasicTypeLongLong); + // If we still couldn't decide a type, we probably have something that + // does not fit in a signed long long, but has no U suffix. Also known as: + // + // warning: integer literal is too large to be represented in a signed + // integer type, interpreting as unsigned [-Wimplicitly-unsigned-literal] + // + return GetBasicType(type_system, lldb::eBasicTypeUnsignedLongLong); + } + return llvm::make_error( + m_expr, + "integer literal is too large to be represented in any integer type", + literal->GetLocation()); +} + +llvm::Expected +Interpreter::Visit(const IntegerLiteralNode *node) { + llvm::Expected type_system = + GetTypeSystemFromCU(m_exe_ctx_scope); + if (!type_system) + return type_system.takeError(); + + llvm::Expected type = + PickIntegerType(*type_system, m_exe_ctx_scope, node); + if (!type) + return type.takeError(); + + Scalar scalar = node->GetValue(); + // APInt from StringRef::getAsInteger comes with just enough bitwidth to + // hold the value. This adjusts APInt bitwidth to match the compiler type. + llvm::Expected type_bitsize = + type->GetBitSize(m_exe_ctx_scope.get()); + if (!type_bitsize) + return type_bitsize.takeError(); + scalar.TruncOrExtendTo(*type_bitsize, false); + return ValueObject::CreateValueObjectFromScalar(m_target, scalar, *type, + "result"); +} + +llvm::Expected +Interpreter::Visit(const FloatLiteralNode *node) { + llvm::Expected type_system = + GetTypeSystemFromCU(m_exe_ctx_scope); + if (!type_system) + return type_system.takeError(); + + bool isFloat = + &node->GetValue().getSemantics() == &llvm::APFloat::IEEEsingle(); + lldb::BasicType basic_type = + isFloat ? lldb::eBasicTypeFloat : lldb::eBasicTypeDouble; + CompilerType type = GetBasicType(*type_system, basic_type); + + if (!type) + return llvm::make_error( + m_expr, "unable to create a const literal", node->GetLocation()); + + Scalar scalar = node->GetValue(); + return ValueObject::CreateValueObjectFromScalar(m_target, scalar, type, + "result"); +} + } // namespace lldb_private::dil diff --git a/lldb/source/ValueObject/DILLexer.cpp b/lldb/source/ValueObject/DILLexer.cpp index eaefaf484bc18..fee93b7a2a50c 100644 --- a/lldb/source/ValueObject/DILLexer.cpp +++ b/lldb/source/ValueObject/DILLexer.cpp @@ -28,18 +28,23 @@ llvm::StringRef Token::GetTokenName(Kind kind) { return "coloncolon"; case Kind::eof: return "eof"; + case Kind::floating_constant: + return "floating_constant"; case Kind::identifier: return "identifier"; + case Kind::integer_constant: + return "integer_constant"; case Kind::l_paren: return "l_paren"; case Kind::l_square: return "l_square"; case Kind::minus: return "minus"; - case Kind::numeric_constant: - return "numeric_constant"; case Kind::period: return "period"; + return "l_square"; + case Kind::plus: + return "plus"; case Kind::r_paren: return "r_paren"; case Kind::r_square: @@ -70,13 +75,34 @@ static std::optional IsWord(llvm::StringRef expr, return candidate; } -static bool IsNumberBodyChar(char ch) { return IsDigit(ch) || IsLetter(ch); } +static bool IsNumberBodyChar(char ch) { + return IsDigit(ch) || IsLetter(ch) || ch == '.'; +} -static std::optional IsNumber(llvm::StringRef expr, - llvm::StringRef &remainder) { - if (IsDigit(remainder[0])) { - llvm::StringRef number = remainder.take_while(IsNumberBodyChar); - remainder = remainder.drop_front(number.size()); +static std::optional IsNumber(llvm::StringRef &remainder, + bool &isFloat) { + llvm::StringRef tail = remainder; + llvm::StringRef body = tail.take_while(IsNumberBodyChar); + size_t dots = body.count('.'); + if (dots > 1 || dots == body.size()) + return std::nullopt; + if (IsDigit(body.front()) || (body[0] == '.' && IsDigit(body[1]))) { + isFloat = dots == 1; + char last = body.back(); + tail = tail.drop_front(body.size()); + if (last == 'e' || last == 'E' || last == 'p' || last == 'P') { + if (tail.consume_front("+") || tail.consume_front("-")) { + tail = tail.drop_while(IsNumberBodyChar); + isFloat = true; + } + } + size_t number_length = remainder.size() - tail.size(); + llvm::StringRef number = remainder.take_front(number_length); + if (!isFloat) { + isFloat = number.contains('p') || // 0x1p1 = 2.0 + (!number.contains('x') && number.contains('e')); // 1e1 = 10.0 + } + remainder = remainder.drop_front(number_length); return number; } return std::nullopt; @@ -106,18 +132,21 @@ llvm::Expected DILLexer::Lex(llvm::StringRef expr, return Token(Token::eof, "", (uint32_t)expr.size()); uint32_t position = cur_pos - expr.begin(); - std::optional maybe_number = IsNumber(expr, remainder); - if (maybe_number) - return Token(Token::numeric_constant, maybe_number->str(), position); + bool isFloat = false; + std::optional maybe_number = IsNumber(remainder, isFloat); + if (maybe_number) { + auto kind = isFloat ? Token::floating_constant : Token::integer_constant; + return Token(kind, maybe_number->str(), position); + } std::optional maybe_word = IsWord(expr, remainder); if (maybe_word) return Token(Token::identifier, maybe_word->str(), position); constexpr std::pair operators[] = { - {Token::amp, "&"}, {Token::arrow, "->"}, {Token::coloncolon, "::"}, - {Token::l_paren, "("}, {Token::l_square, "["}, {Token::minus, "-"}, - {Token::period, "."}, {Token::r_paren, ")"}, {Token::r_square, "]"}, - {Token::star, "*"}, + {Token::amp, "&"}, {Token::arrow, "->"}, {Token::coloncolon, "::"}, + {Token::l_paren, "("}, {Token::l_square, "["}, {Token::minus, "-"}, + {Token::period, "."}, {Token::plus, "+"}, {Token::r_paren, ")"}, + {Token::r_square, "]"}, {Token::star, "*"}, }; for (auto [kind, str] : operators) { if (remainder.consume_front(str)) diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp index eac41fab90763..50e5d5cd00d14 100644 --- a/lldb/source/ValueObject/DILParser.cpp +++ b/lldb/source/ValueObject/DILParser.cpp @@ -179,10 +179,13 @@ ASTNodeUP DILParser::ParsePostfixExpression() { // Parse a primary_expression. // // primary_expression: +// numeric_literal // id_expression // "(" expression ")" // ASTNodeUP DILParser::ParsePrimaryExpression() { + if (CurToken().IsOneOf({Token::integer_constant, Token::floating_constant})) + return ParseNumericLiteral(); if (CurToken().IsOneOf( {Token::coloncolon, Token::identifier, Token::l_paren})) { // Save the source location for the diagnostics message. @@ -346,6 +349,7 @@ void DILParser::BailOut(const std::string &error, uint32_t loc, m_dil_lexer.ResetTokenIdx(m_dil_lexer.NumLexedTokens() - 1); } +// FIXME: Remove this once subscript operator uses ScalarLiteralNode. // Parse a integer_literal. // // integer_literal: @@ -370,6 +374,69 @@ std::optional DILParser::ParseIntegerConstant() { return std::nullopt; } +// Parse a numeric_literal. +// +// numeric_literal: +// ? Token::integer_constant ? +// ? Token::floating_constant ? +// +ASTNodeUP DILParser::ParseNumericLiteral() { + ASTNodeUP numeric_constant; + if (CurToken().Is(Token::integer_constant)) + numeric_constant = ParseIntegerLiteral(); + else + numeric_constant = ParseFloatingPointLiteral(); + if (!numeric_constant) { + BailOut(llvm::formatv("Failed to parse token as numeric-constant: {0}", + CurToken()), + CurToken().GetLocation(), CurToken().GetSpelling().length()); + return std::make_unique(); + } + m_dil_lexer.Advance(); + return numeric_constant; +} + +ASTNodeUP DILParser::ParseIntegerLiteral() { + Token token = CurToken(); + auto spelling = token.GetSpelling(); + llvm::StringRef spelling_ref = spelling; + + auto radix = llvm::getAutoSenseRadix(spelling_ref); + IntegerTypeSuffix type = IntegerTypeSuffix::None; + bool is_unsigned = false; + if (spelling_ref.consume_back_insensitive("u")) + is_unsigned = true; + if (spelling_ref.consume_back_insensitive("ll")) + type = IntegerTypeSuffix::LongLong; + else if (spelling_ref.consume_back_insensitive("l")) + type = IntegerTypeSuffix::Long; + // Suffix 'u' can be only specified only once, before or after 'l' + if (!is_unsigned && spelling_ref.consume_back_insensitive("u")) + is_unsigned = true; + + llvm::APInt raw_value; + if (!spelling_ref.getAsInteger(radix, raw_value)) + return std::make_unique(token.GetLocation(), raw_value, + radix, is_unsigned, type); + return nullptr; +} + +ASTNodeUP DILParser::ParseFloatingPointLiteral() { + Token token = CurToken(); + auto spelling = token.GetSpelling(); + llvm::StringRef spelling_ref = spelling; + + llvm::APFloat raw_float(llvm::APFloat::IEEEdouble()); + if (spelling_ref.consume_back_insensitive("f")) + raw_float = llvm::APFloat(llvm::APFloat::IEEEsingle()); + + auto StatusOrErr = raw_float.convertFromString( + spelling_ref, llvm::APFloat::rmNearestTiesToEven); + if (!errorToBool(StatusOrErr.takeError())) + return std::make_unique(token.GetLocation(), raw_float); + return nullptr; +} + void DILParser::Expect(Token::Kind kind) { if (CurToken().IsNot(kind)) { BailOut(llvm::formatv("expected {0}, got: {1}", kind, CurToken()), diff --git a/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py index 0f56057189395..1d0340160f5e4 100644 --- a/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py +++ b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py @@ -66,7 +66,7 @@ def test_subscript(self): self.expect( "frame var 'int_arr[1.0]'", error=True, - substrs=["expected 'r_square', got: <'.'"], + substrs=["failed to parse integer constant: <'1.0' (floating_constant)>"], ) # Base should be a "pointer to T" and index should be of an integral type. @@ -88,7 +88,7 @@ def test_subscript(self): self.expect( "frame var '1[2]'", error=True, - substrs=["Unexpected token"], + substrs=["subscripted value is not an array or pointer"], ) # Base should not be a pointer to void diff --git a/lldb/test/API/commands/frame/var-dil/basics/Indirection/TestFrameVarDILIndirection.py b/lldb/test/API/commands/frame/var-dil/basics/Indirection/TestFrameVarDILIndirection.py index 38c72131d797c..28eba4f1a70bc 100644 --- a/lldb/test/API/commands/frame/var-dil/basics/Indirection/TestFrameVarDILIndirection.py +++ b/lldb/test/API/commands/frame/var-dil/basics/Indirection/TestFrameVarDILIndirection.py @@ -35,7 +35,7 @@ def test_frame_var(self): self.expect( "frame variable '*1'", error=True, - substrs=["Unexpected token: <'1' (numeric_constant)>"], + substrs=["dereference failed: not a pointer, reference or array type"], ) self.expect( "frame variable '*val'", diff --git a/lldb/test/API/commands/frame/var-dil/expr/Literals/Makefile b/lldb/test/API/commands/frame/var-dil/expr/Literals/Makefile new file mode 100644 index 0000000000000..99998b20bcb05 --- /dev/null +++ b/lldb/test/API/commands/frame/var-dil/expr/Literals/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/commands/frame/var-dil/expr/Literals/TestFrameVarDILLiterals.py b/lldb/test/API/commands/frame/var-dil/expr/Literals/TestFrameVarDILLiterals.py new file mode 100644 index 0000000000000..7093ae1097947 --- /dev/null +++ b/lldb/test/API/commands/frame/var-dil/expr/Literals/TestFrameVarDILLiterals.py @@ -0,0 +1,92 @@ +""" +Test DIL literals. +""" + +import lldb +from lldbsuite.test.lldbtest import * +from lldbsuite.test.decorators import * +from lldbsuite.test import lldbutil + + +class TestFrameVarDILLiterals(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + def test_literals(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") + + # Check number literals parsing + self.expect_var_path("1.0", value="1", type="double") + self.expect_var_path("1.0f", value="1", type="float") + self.expect_var_path("0x1.2p+3f", value="9", type="float") + self.expect_var_path("1", value="1", type="int") + self.expect_var_path("1u", value="1", type="unsigned int") + self.expect_var_path("0b1l", value="1", type="long") + self.expect_var_path("01ul", value="1", type="unsigned long") + self.expect_var_path("01lu", value="1", type="unsigned long") + self.expect_var_path("0o1ll", value="1", type="long long") + self.expect_var_path("0x1ULL", value="1", type="unsigned long long") + self.expect_var_path("0x1llu", value="1", type="unsigned long long") + self.expect( + "frame var '1LLL'", + error=True, + substrs=["Failed to parse token as numeric-constant"], + ) + self.expect( + "frame var '1ullu'", + error=True, + substrs=["Failed to parse token as numeric-constant"], + ) + + # Check integer literal type edge cases (dil::Interpreter::PickIntegerType) + frame = thread.GetFrameAtIndex(0) + v = frame.GetValueForVariablePath("argc") + # Creating an SBType from a BasicType still requires any value from the frame + int_size = v.GetType().GetBasicType(lldb.eBasicTypeInt).GetByteSize() + long_size = v.GetType().GetBasicType(lldb.eBasicTypeLong).GetByteSize() + longlong_size = v.GetType().GetBasicType(lldb.eBasicTypeLongLong).GetByteSize() + + longlong_str = "0x" + "F" * longlong_size * 2 + longlong_str = str(int(longlong_str, 16)) + self.assert_literal_type(frame, longlong_str, lldb.eBasicTypeUnsignedLongLong) + toolong_str = "0x" + "F" * longlong_size * 2 + "F" + self.expect( + f"frame var '{toolong_str}'", + error=True, + substrs=[ + "integer literal is too large to be represented in any integer type" + ], + ) + + # These check only apply if adjacent types have different sizes + if int_size < long_size: + # For exmaple, 0xFFFFFFFF and 4294967295 will have different types + # even though the numeric value is the same + hex_str = "0x" + "F" * int_size * 2 + dec_str = str(int(hex_str, 16)) + self.assert_literal_type(frame, hex_str, lldb.eBasicTypeUnsignedInt) + self.assert_literal_type(frame, dec_str, lldb.eBasicTypeLong) + long_str = "0x" + "F" * int_size * 2 + "F" + ulong_str = long_str + "u" + self.assert_literal_type(frame, long_str, lldb.eBasicTypeLong) + self.assert_literal_type(frame, ulong_str, lldb.eBasicTypeUnsignedLong) + if long_size < longlong_size: + hex_str = "0x" + "F" * long_size * 2 + dec_str = str(int(hex_str, 16)) + self.assert_literal_type(frame, hex_str, lldb.eBasicTypeUnsignedLong) + self.assert_literal_type(frame, dec_str, lldb.eBasicTypeLongLong) + longlong_str = "0x" + "F" * long_size * 2 + "F" + ulonglong_str = longlong_str + "u" + self.assert_literal_type(frame, longlong_str, lldb.eBasicTypeLongLong) + self.assert_literal_type( + frame, ulonglong_str, lldb.eBasicTypeUnsignedLongLong + ) + + def assert_literal_type(self, frame, literal, expected_type): + value = frame.GetValueForVariablePath(literal) + basic_type = value.GetType().GetBasicType() + self.assertEqual(basic_type, expected_type) diff --git a/lldb/test/API/commands/frame/var-dil/expr/Literals/main.cpp b/lldb/test/API/commands/frame/var-dil/expr/Literals/main.cpp new file mode 100644 index 0000000000000..c9bd8afb0d71d --- /dev/null +++ b/lldb/test/API/commands/frame/var-dil/expr/Literals/main.cpp @@ -0,0 +1,3 @@ +int main(int argc, char **argv) { + return 0; // Set a breakpoint here +} diff --git a/lldb/unittests/ValueObject/DILLexerTests.cpp b/lldb/unittests/ValueObject/DILLexerTests.cpp index f65034c1dbea3..66413c3aa9032 100644 --- a/lldb/unittests/ValueObject/DILLexerTests.cpp +++ b/lldb/unittests/ValueObject/DILLexerTests.cpp @@ -151,22 +151,32 @@ TEST(DILLexerTests, IdentifiersTest) { Token token = lexer.GetCurrentToken(); EXPECT_TRUE(token.IsNot(Token::identifier)); EXPECT_TRUE(token.IsOneOf({Token::eof, Token::coloncolon, Token::l_paren, - Token::r_paren, Token::numeric_constant})); + Token::r_paren, Token::integer_constant})); } } TEST(DILLexerTests, NumbersTest) { // These strings should lex into number tokens. - std::vector valid_numbers = {"123", "0x123", "0123", "0b101"}; + std::vector valid_integers = {"123", "0x123", "0123", "0b101"}; + std::vector valid_floats = { + "1.2", ".2", "2.f", "0x1.2", "0x.2", ".2e1f", + "2.e+1f", "0x1.f", "0x1.2p1", "0x1.p-1f", "0x1.2p+3f", "1e1", + "1e+1", "0x1p1", "0x1p+1", "0xf.fp1f"}; // The lexer can lex these strings, but they should not be numbers. - std::vector invalid_numbers = {"", "x123", "b123"}; + std::vector invalid_numbers = {"", "x123", "b123", "a.b"}; - for (auto &str : valid_numbers) { + for (auto &str : valid_integers) { SCOPED_TRACE(str); EXPECT_THAT_EXPECTED(ExtractTokenData(str), llvm::HasValue(testing::ElementsAre( - testing::Pair(Token::numeric_constant, str)))); + testing::Pair(Token::integer_constant, str)))); + } + for (auto &str : valid_floats) { + SCOPED_TRACE(str); + EXPECT_THAT_EXPECTED(ExtractTokenData(str), + llvm::HasValue(testing::ElementsAre( + testing::Pair(Token::floating_constant, str)))); } // Verify that none of the invalid numbers come out as numeric tokens. for (auto &str : invalid_numbers) { @@ -175,7 +185,27 @@ TEST(DILLexerTests, NumbersTest) { EXPECT_THAT_EXPECTED(maybe_lexer, llvm::Succeeded()); DILLexer lexer(*maybe_lexer); Token token = lexer.GetCurrentToken(); - EXPECT_TRUE(token.IsNot(Token::numeric_constant)); + EXPECT_TRUE(token.IsNot(Token::integer_constant)); EXPECT_TRUE(token.IsOneOf({Token::eof, Token::identifier})); } + + // Verify that '-' and '+' are not lexed if they're not part of a number + std::vector expressions = {"1+e", "0x1+p", "1.1+e", + "1.1e1+e", "0x1.1p-1-p", "1e-1+e", + "1e1+e", "0x1p-1-p"}; + for (auto &str : expressions) { + SCOPED_TRACE(str); + llvm::Expected maybe_lexer = DILLexer::Create(str); + EXPECT_THAT_EXPECTED(maybe_lexer, llvm::Succeeded()); + DILLexer lexer(*maybe_lexer); + Token token = lexer.GetCurrentToken(); + EXPECT_TRUE( + token.IsOneOf({Token::integer_constant, Token::floating_constant})); + lexer.Advance(); + token = lexer.GetCurrentToken(); + EXPECT_TRUE(token.IsOneOf({Token::plus, Token::minus})); + lexer.Advance(); + token = lexer.GetCurrentToken(); + EXPECT_TRUE(token.Is(Token::identifier)); + } }