https://github.com/Michael137 created https://github.com/llvm/llvm-project/pull/177926
Depends on: * https://github.com/llvm/llvm-project/pull/177920 * https://github.com/llvm/llvm-project/pull/177922 In https://github.com/llvm/llvm-project/pull/177922 we make expressions run in C++ member functions honor the function qualifiers of the current stop context. E.g., this means we can no longer run non-const member functions when stopped in a const-member function. To ensure users can still do this if they really need/want to, we provide an option to not honor the qualifiers at all, leaving the `__lldb_expr` as the least qualified, allowing it to call any function/mutate any members. We may want to come up with a better name because this doesn't only apply to `const-`qualified functions but also `volatile` member functions. Perhaps `--ignore-context-qualifiers`/`-K`? >From d6d51a930e9ee916ee938d54f439683d26c475dd Mon Sep 17 00:00:00 2001 From: Michael Buch <[email protected]> Date: Wed, 21 Jan 2026 16:12:03 +0000 Subject: [PATCH 1/3] [lldb][Expression] Make __lldb_expr function qualifiers match source context This was originally removed in `8bdcd522510f923185cdfaec66c4a78d0a0d38c0` under the assumption this wasn't required (i.e., LLDB should just always pretend it's in a mutable context). But since function qualifiers affect overloading in C++, this assumption can lead to unexpected expression evaluator behaviour. Instead, in this patch we try to add function qualifiers to `$__lldb_class::$__lldb_expr` that resemble the qualifiers in the method that we're stopped in. --- .../Clang/ClangExpressionDeclMap.cpp | 8 +- .../Clang/ClangExpressionSourceCode.cpp | 43 +++++-- .../TestExprInsideLambdas.py | 4 +- .../API/lang/cpp/const_this/TestConstThis.py | 10 +- .../const_method/Makefile | 3 + .../const_method/TestExprInConstMethod.py | 106 ++++++++++++++++++ .../const_method/main.cpp | 49 ++++++++ .../const_volatile_method/Makefile | 3 + .../TestExprInConstVolatileMethod.py | 60 ++++++++++ .../const_volatile_method/main.cpp | 43 +++++++ .../cv_qualified_objects/Makefile | 3 + .../TestExprOnCVQualifiedObjects.py | 21 ++++ .../cv_qualified_objects/main.cpp | 25 +++++ .../fixit/Makefile | 3 + .../fixit/TestExprInConstMethodWithFixit.py | 42 +++++++ .../fixit/main.cpp | 22 ++++ .../non_const_method/Makefile | 3 + .../TestExprInNonConstMethod.py | 85 ++++++++++++++ .../non_const_method/main.cpp | 49 ++++++++ .../template_const_method/Makefile | 3 + .../TestExprInTemplateConstMethod.py | 106 ++++++++++++++++++ .../template_const_method/main.cpp | 51 +++++++++ .../template_non_const_method/Makefile | 3 + .../TestExprInTemplateNonConstMethod.py | 85 ++++++++++++++ .../template_non_const_method/main.cpp | 49 ++++++++ lldb/test/API/lang/cpp/this/TestCPPThis.py | 12 +- 26 files changed, 870 insertions(+), 21 deletions(-) create mode 100644 lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/Makefile create mode 100644 lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/TestExprInConstMethod.py create mode 100644 lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/main.cpp create mode 100644 lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/Makefile create mode 100644 lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/TestExprInConstVolatileMethod.py create mode 100644 lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/main.cpp create mode 100644 lldb/test/API/lang/cpp/expression-context-qualifiers/cv_qualified_objects/Makefile create mode 100644 lldb/test/API/lang/cpp/expression-context-qualifiers/cv_qualified_objects/TestExprOnCVQualifiedObjects.py create mode 100644 lldb/test/API/lang/cpp/expression-context-qualifiers/cv_qualified_objects/main.cpp create mode 100644 lldb/test/API/lang/cpp/expression-context-qualifiers/fixit/Makefile create mode 100644 lldb/test/API/lang/cpp/expression-context-qualifiers/fixit/TestExprInConstMethodWithFixit.py create mode 100644 lldb/test/API/lang/cpp/expression-context-qualifiers/fixit/main.cpp create mode 100644 lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/Makefile create mode 100644 lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/TestExprInNonConstMethod.py create mode 100644 lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/main.cpp create mode 100644 lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/Makefile create mode 100644 lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/TestExprInTemplateConstMethod.py create mode 100644 lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/main.cpp create mode 100644 lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/Makefile create mode 100644 lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/TestExprInTemplateNonConstMethod.py create mode 100644 lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/main.cpp diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp index 24a5dc5362964..41d2d2cb23146 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp @@ -844,6 +844,12 @@ void ClangExpressionDeclMap::LookUpLldbClass(NameSearchContext &context) { QualType class_qual_type = m_ast_context->getCanonicalTagType(class_decl); + // The synthesized __lldb_expr will adopt the qualifiers from this class + // type. Make sure we use the qualifiers of the method that we're currently + // stopped in. + class_qual_type.addFastQualifiers( + method_decl->getMethodQualifiers().getFastQualifiers()); + TypeFromUser class_user_type( class_qual_type.getAsOpaquePtr(), function_decl_ctx.GetTypeSystem()->weak_from_this()); @@ -1991,7 +1997,7 @@ void ClangExpressionDeclMap::AddContextClassType(NameSearchContext &context, std::array<CompilerType, 1> args{void_clang_type.GetPointerType()}; CompilerType method_type = m_clang_ast_context->CreateFunctionType( - void_clang_type, args, false, 0); + void_clang_type, args, false, ut.GetTypeQualifiers()); const bool is_virtual = false; const bool is_static = false; diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp index cfe187ffc4114..ca4199faa3841 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp @@ -10,6 +10,7 @@ #include "ClangExpressionUtil.h" +#include "clang/AST/TypeBase.h" #include "clang/Basic/CharInfo.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/SourceManager.h" @@ -189,6 +190,28 @@ static void AddMacros(const DebugMacros *dm, CompileUnit *comp_unit, } } +/// Return qualifers of the current C++ method. +static clang::Qualifiers GetFrameCVQualifiers(StackFrame *frame) { + if (!frame) + return {}; + + auto this_sp = frame->FindVariable(ConstString("this")); + if (!this_sp) + return {}; + + // Lambdas that capture 'this' have a member variable called 'this'. The class + // context of __lldb_expr for a lambda is the class type of the 'this' capture + // (not the anonymous lambda structure). So use the qualifiers of the captured + // 'this'. + if (auto this_this_sp = this_sp->GetChildMemberWithName("this")) + return clang::Qualifiers::fromCVRMask( + this_this_sp->GetCompilerType().GetPointeeType().GetTypeQualifiers()); + + // Not in a lambda. Return 'this' qualifiers. + return clang::Qualifiers::fromCVRMask( + this_sp->GetCompilerType().GetPointeeType().GetTypeQualifiers()); +} + lldb_private::ClangExpressionSourceCode::ClangExpressionSourceCode( llvm::StringRef filename, llvm::StringRef name, llvm::StringRef prefix, llvm::StringRef body, Wrapping wrap, WrapKind wrap_kind) @@ -463,15 +486,17 @@ bool ClangExpressionSourceCode::GetText( lldb_local_var_decls.GetData(), tagged_body.c_str()); break; case WrapKind::CppMemberFunction: - wrap_stream.Printf("%s" - "void \n" - "$__lldb_class::%s(void *$__lldb_arg) \n" - "{ \n" - " %s; \n" - "%s" - "} \n", - module_imports.c_str(), m_name.c_str(), - lldb_local_var_decls.GetData(), tagged_body.c_str()); + wrap_stream.Printf( + "%s" + "void \n" + "$__lldb_class::%s(void *$__lldb_arg) %s \n" + "{ \n" + " %s; \n" + "%s" + "} \n", + module_imports.c_str(), m_name.c_str(), + GetFrameCVQualifiers(exe_ctx.GetFramePtr()).getAsString().c_str(), + lldb_local_var_decls.GetData(), tagged_body.c_str()); break; case WrapKind::ObjCInstanceMethod: wrap_stream.Printf( diff --git a/lldb/test/API/commands/expression/expr_inside_lambda/TestExprInsideLambdas.py b/lldb/test/API/commands/expression/expr_inside_lambda/TestExprInsideLambdas.py index e35cfa6a289a7..0a7683b310f43 100644 --- a/lldb/test/API/commands/expression/expr_inside_lambda/TestExprInsideLambdas.py +++ b/lldb/test/API/commands/expression/expr_inside_lambda/TestExprInsideLambdas.py @@ -127,8 +127,8 @@ def test_expr_inside_lambda(self): # Inside non_capturing_method lldbutil.continue_to_breakpoint(process, bkpt) - self.expect_expr("local", result_type="int", result_value="5") - self.expect_expr("local2", result_type="int", result_value="10") + self.expect_expr("local", result_type="const int", result_value="5") + self.expect_expr("local2", result_type="const int", result_value="10") self.expect_expr("local2 * local", result_type="int", result_value="50") self.expectExprError( diff --git a/lldb/test/API/lang/cpp/const_this/TestConstThis.py b/lldb/test/API/lang/cpp/const_this/TestConstThis.py index 4b7d3aadb62ab..c2df61fde2b58 100644 --- a/lldb/test/API/lang/cpp/const_this/TestConstThis.py +++ b/lldb/test/API/lang/cpp/const_this/TestConstThis.py @@ -11,10 +11,9 @@ def run_class_tests(self): # Expression not referencing context class. self.expect_expr("1 + 1", result_type="int", result_value="2") # Referencing context class. - # FIXME: This and the expression below should return const types. - self.expect_expr("member", result_type="int", result_value="3") + self.expect_expr("member", result_type="const int", result_value="3") # Check the type of context class. - self.expect_expr("this", result_type="ContextClass *") + self.expect_expr("this", result_type="const ContextClass *") def test_member_func(self): self.build() @@ -36,10 +35,9 @@ def run_template_class_tests(self): # Expression not referencing context class. self.expect_expr("1 + 1", result_type="int", result_value="2") # Referencing context class. - # FIXME: This and the expression below should return const types. - self.expect_expr("member", result_type="int", result_value="4") + self.expect_expr("member", result_type="const int", result_value="4") # Check the type of context class. - self.expect_expr("this", result_type="TemplatedContextClass<int> *") + self.expect_expr("this", result_type="const TemplatedContextClass<int> *") def test_template_member_func(self): self.build() diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/Makefile b/lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/Makefile new file mode 100644 index 0000000000000..99998b20bcb05 --- /dev/null +++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/TestExprInConstMethod.py b/lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/TestExprInConstMethod.py new file mode 100644 index 0000000000000..0ba6c8a837db4 --- /dev/null +++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/TestExprInConstMethod.py @@ -0,0 +1,106 @@ +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class TestCase(TestBase): + def test(self): + self.build() + (_, process, _, _) = lldbutil.run_to_source_breakpoint( + self, "Break: const_method begin", lldb.SBFileSpec("main.cpp") + ) + + self.expect_expr("bar()", result_value="2", result_type="int") + self.expect( + "expression m_const_mem = 2.0", + error=True, + substrs=[ + "cannot assign to non-static data member", + "with const-qualified type", + ], + ) + self.expect( + "expression m_mem = 2.0", + error=True, + substrs=[ + "cannot assign to non-static data member within const member function" + ], + ) + self.expect_expr("((Foo*)this)->bar()", result_type="double", result_value="5") + + lldbutil.continue_to_source_breakpoint( + self, + process, + "Break: const_method no-this lambda", + lldb.SBFileSpec("main.cpp"), + ) + + self.expect( + "expression x = 7.0", + error=True, + substrs=[ + "cannot assign to non-static data member within const member function" + ], + ) + + lldbutil.continue_to_source_breakpoint( + self, + process, + "Break: const_method mutable no-this lambda", + lldb.SBFileSpec("main.cpp"), + ) + + self.expect_expr("x = 7.0") + self.expect_expr("x", result_value="7") + + lldbutil.continue_to_source_breakpoint( + self, process, "Break: const_method lambda", lldb.SBFileSpec("main.cpp") + ) + + # FIXME: mutating this capture should be disallowed in a non-mutable lambda. + self.expect_expr("y = 8.0") + self.expect_expr("bar()", result_value="2", result_type="int") + self.expect( + "expression m_const_mem = 2.0", + error=True, + substrs=[ + "cannot assign to non-static data member", + "with const-qualified type", + ], + ) + self.expect( + "expression m_mem = 2.0", + error=True, + substrs=[ + "cannot assign to non-static data member within const member function" + ], + ) + self.expect_expr("m_mem", result_value="-2") + self.expect_expr("((Foo*)this)->bar()", result_type="double", result_value="5") + + lldbutil.continue_to_source_breakpoint( + self, + process, + "Break: const_method mutable lambda", + lldb.SBFileSpec("main.cpp"), + ) + + self.expect_expr("y = 9.0") + self.expect_expr("bar()", result_value="2", result_type="int") + self.expect( + "expression m_const_mem = 2.0", + error=True, + substrs=[ + "cannot assign to non-static data member", + "with const-qualified type", + ], + ) + self.expect( + "expression m_mem = 2.0", + error=True, + substrs=[ + "cannot assign to non-static data member within const member function" + ], + ) + self.expect_expr("m_mem", result_value="-2") diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/main.cpp b/lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/main.cpp new file mode 100644 index 0000000000000..7cb74767b458a --- /dev/null +++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/main.cpp @@ -0,0 +1,49 @@ +#include <cassert> +#include <cstdio> + +struct Foo { + double bar() { return 5.0; } + + int bar() const { return 2; } + + int const_method() const { + auto x = bar(); + assert(x == 2); + std::puts("Break: const_method begin"); + + [x] { + std::puts("Keep on multiple lines..."); + std::puts("Break: const_method no-this lambda"); + }(); + + [x]() mutable { + std::puts("Keep on multiple lines..."); + std::puts("Break: const_method mutable no-this lambda"); + }(); + + [this, y = x] { + auto x = bar() + y; + std::puts("Break: const_method lambda"); + }(); + + [this, y = x]() mutable { + auto x = bar() + y; + std::puts("Break: const_method mutable lambda"); + }(); + + return 120; + } + + float m_mem = -2.0; + const float m_const_mem = -3.0; +}; + +int main() { + const Foo f; + f.bar(); + + Foo f2; + f2.bar(); + + return Foo{}.const_method(); +} diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/Makefile b/lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/Makefile new file mode 100644 index 0000000000000..99998b20bcb05 --- /dev/null +++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/TestExprInConstVolatileMethod.py b/lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/TestExprInConstVolatileMethod.py new file mode 100644 index 0000000000000..f20b9d8164841 --- /dev/null +++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/TestExprInConstVolatileMethod.py @@ -0,0 +1,60 @@ +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class TestCase(TestBase): + def test(self): + self.build() + (_, process, _, _) = lldbutil.run_to_source_breakpoint( + self, "Break here: const", lldb.SBFileSpec("main.cpp") + ) + + self.expect_expr("bar()", result_type="double", result_value="5") + self.expect_expr("const_volatile_method()") + self.expect_expr("const_method()") + self.expect( + "expression volatile_method()", + error=True, + substrs=["has type 'const Foo'", "but function is not marked const"], + ) + + lldbutil.continue_to_source_breakpoint( + self, process, "Break here: volatile", lldb.SBFileSpec("main.cpp") + ) + + self.expect_expr( + "bar()", result_type="const char *", result_summary='"volatile_bar"' + ) + self.expect_expr("const_volatile_method()") + self.expect( + "expression const_method()", + error=True, + substrs=["has type 'volatile Foo'", "but function is not marked volatile"], + ) + self.expect_expr("volatile_method()") + + lldbutil.continue_to_source_breakpoint( + self, process, "Break here: const volatile", lldb.SBFileSpec("main.cpp") + ) + + self.expect_expr("bar()", result_type="int", result_value="2") + self.expect_expr("other_cv_method()") + + self.expect( + "expression const_method()", + error=True, + substrs=[ + "has type 'const volatile Foo'", + "but function is not marked const or volatile", + ], + ) + self.expect( + "expression volatile_method()", + error=True, + substrs=[ + "has type 'const volatile Foo'", + "but function is not marked const or volatile", + ], + ) diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/main.cpp b/lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/main.cpp new file mode 100644 index 0000000000000..da0f7e7d1be95 --- /dev/null +++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/main.cpp @@ -0,0 +1,43 @@ +#include <cassert> +#include <cstdio> + +struct Foo { + double bar() const { return 5.0; } + const char *bar() volatile { return "volatile_bar"; } + int bar() volatile const { return 2; } + + int volatile_method() volatile { + std::puts("Break here: volatile"); + return 0; + } + int const_method() const { + std::puts("Break here: const"); + return 0; + } + int other_cv_method() const volatile { return 20; } + + int const_volatile_method() const volatile { + auto x = bar(); + assert(x == 2); + other_cv_method(); + + std::puts("Break here: const volatile"); + + return 120; + } +}; + +int main() { + const Foo f; + f.bar(); + f.const_method(); + + volatile Foo f2; + f2.bar(); + f2.volatile_method(); + + const volatile Foo f3; + f3.bar(); + + return Foo{}.const_volatile_method(); +} diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/cv_qualified_objects/Makefile b/lldb/test/API/lang/cpp/expression-context-qualifiers/cv_qualified_objects/Makefile new file mode 100644 index 0000000000000..99998b20bcb05 --- /dev/null +++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/cv_qualified_objects/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/cv_qualified_objects/TestExprOnCVQualifiedObjects.py b/lldb/test/API/lang/cpp/expression-context-qualifiers/cv_qualified_objects/TestExprOnCVQualifiedObjects.py new file mode 100644 index 0000000000000..0f661471df749 --- /dev/null +++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/cv_qualified_objects/TestExprOnCVQualifiedObjects.py @@ -0,0 +1,21 @@ +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class TestCase(TestBase): + def test(self): + self.build() + lldbutil.run_to_source_breakpoint( + self, + "Break here", + lldb.SBFileSpec("main.cpp"), + ) + + self.expect_expr("f.bar()", result_type="double", result_value="5") + self.expect_expr("cf.bar()", result_type="int", result_value="2") + self.expect_expr("vf.bar()", result_type="short", result_value="8") + self.expect_expr( + "cvf.bar()", result_type="const char *", result_summary='"volatile"' + ) diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/cv_qualified_objects/main.cpp b/lldb/test/API/lang/cpp/expression-context-qualifiers/cv_qualified_objects/main.cpp new file mode 100644 index 0000000000000..eb7f8b82f2695 --- /dev/null +++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/cv_qualified_objects/main.cpp @@ -0,0 +1,25 @@ +#include <cstdio> + +struct Foo { + double bar() { return 5.0; } + int bar() const { return 2; } + short bar() volatile { return 8; } + char const *bar() const volatile { return "volatile"; } + + float m_mem = -2.0; + const float m_const_mem = -3.0; +}; + +int main() { + Foo f; + const Foo cf; + volatile Foo vf; + const volatile Foo cvf; + + f.bar(); + cf.bar(); + vf.bar(); + cvf.bar(); + + std::puts("Break here"); +} diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/fixit/Makefile b/lldb/test/API/lang/cpp/expression-context-qualifiers/fixit/Makefile new file mode 100644 index 0000000000000..99998b20bcb05 --- /dev/null +++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/fixit/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/fixit/TestExprInConstMethodWithFixit.py b/lldb/test/API/lang/cpp/expression-context-qualifiers/fixit/TestExprInConstMethodWithFixit.py new file mode 100644 index 0000000000000..3c239c89d0ca7 --- /dev/null +++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/fixit/TestExprInConstMethodWithFixit.py @@ -0,0 +1,42 @@ +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class TestCase(TestBase): + def test(self): + self.build() + (_, process, _, _) = lldbutil.run_to_source_breakpoint( + self, "Break here", lldb.SBFileSpec("main.cpp") + ) + + self.expect( + "expression m_bar->method()", + error=True, + substrs=[ + "member reference type 'const Bar' is not a pointer", + "but function is not marked const", + ], + ) + + # Two fix-its + self.expect( + "expression -- m_bar->method() + m_bar->method()", + error=True, + substrs=[ + "member reference type 'const Bar' is not a pointer", + "but function is not marked const", + "member reference type 'const Bar' is not a pointer", + "but function is not marked const", + ], + ) + + self.expect( + "expression m_bar->method() + blah", + error=True, + substrs=[ + "member reference type 'const Bar' is not a pointer", + "but function is not marked const", + ], + ) diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/fixit/main.cpp b/lldb/test/API/lang/cpp/expression-context-qualifiers/fixit/main.cpp new file mode 100644 index 0000000000000..1dab96e2986da --- /dev/null +++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/fixit/main.cpp @@ -0,0 +1,22 @@ +#include <cassert> +#include <cstdio> + +struct Bar { + void method() {} +}; + +struct Foo { + int const_method() const { + std::puts("Break here"); + + return 120; + } + + Bar m_bar; +}; + +int main() { + Foo{}.m_bar.method(); + + return Foo{}.const_method(); +} diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/Makefile b/lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/Makefile new file mode 100644 index 0000000000000..99998b20bcb05 --- /dev/null +++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/TestExprInNonConstMethod.py b/lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/TestExprInNonConstMethod.py new file mode 100644 index 0000000000000..8ff88e8014b4c --- /dev/null +++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/TestExprInNonConstMethod.py @@ -0,0 +1,85 @@ +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class TestCase(TestBase): + def test(self): + self.build() + (_, process, _, _) = lldbutil.run_to_source_breakpoint( + self, "Break: non_const_method begin", lldb.SBFileSpec("main.cpp") + ) + + self.expect_expr("bar()", result_value="5", result_type="double") + self.expect( + "expression m_const_mem = 2.0", + error=True, + substrs=[ + "cannot assign to non-static data member", + "with const-qualified type", + ], + ) + + lldbutil.continue_to_source_breakpoint( + self, + process, + "Break: non_const_method no-this lambda", + lldb.SBFileSpec("main.cpp"), + ) + + self.expect( + "expression x = 7.0", + error=True, + substrs=[ + "cannot assign to non-static data member within const member function" + ], + ) + + lldbutil.continue_to_source_breakpoint( + self, + process, + "Break: non_const_method mutable no-this lambda", + lldb.SBFileSpec("main.cpp"), + ) + + self.expect_expr("x = 7.0") + self.expect_expr("x", result_value="7") + + lldbutil.continue_to_source_breakpoint( + self, process, "Break: non_const_method lambda", lldb.SBFileSpec("main.cpp") + ) + + # FIXME: mutating this capture should be disallowed in a non-mutable lambda. + self.expect_expr("y = 8.0") + self.expect_expr("bar()", result_value="5", result_type="double") + self.expect( + "expression m_const_mem = 2.0", + error=True, + substrs=[ + "cannot assign to non-static data member", + "with const-qualified type", + ], + ) + self.expect_expr("m_mem = 2.0") + self.expect_expr("m_mem", result_value="2") + + lldbutil.continue_to_source_breakpoint( + self, + process, + "Break: non_const_method mutable lambda", + lldb.SBFileSpec("main.cpp"), + ) + + self.expect_expr("y = 9.0") + self.expect_expr("bar()", result_value="5", result_type="double") + self.expect( + "expression m_const_mem = 2.0", + error=True, + substrs=[ + "cannot assign to non-static data member", + "with const-qualified type", + ], + ) + self.expect_expr("m_mem = 4.0") + self.expect_expr("m_mem", result_value="4") diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/main.cpp b/lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/main.cpp new file mode 100644 index 0000000000000..141f63b6f2f24 --- /dev/null +++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/main.cpp @@ -0,0 +1,49 @@ +#include <cassert> +#include <cstdio> + +struct Foo { + double bar() { return 5.0; } + + int bar() const { return 2; } + + int non_const_method() { + auto x = bar(); + assert(x == 5.0); + std::puts("Break: non_const_method begin"); + + [x] { + std::puts("Keep on multiple lines..."); + std::puts("Break: non_const_method no-this lambda"); + }(); + + [x]() mutable { + std::puts("Keep on multiple lines..."); + std::puts("Break: non_const_method mutable no-this lambda"); + }(); + + [this, y = x] { + auto x = bar() + y; + std::puts("Break: non_const_method lambda"); + }(); + + [this, y = x]() mutable { + auto x = bar() + y; + std::puts("Break: non_const_method mutable lambda"); + }(); + + return 120; + } + + float m_mem = -2.0; + const float m_const_mem = -3.0; +}; + +int main() { + const Foo f; + f.bar(); + + Foo f2; + f2.bar(); + + return Foo{}.non_const_method(); +} diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/Makefile b/lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/Makefile new file mode 100644 index 0000000000000..99998b20bcb05 --- /dev/null +++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/TestExprInTemplateConstMethod.py b/lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/TestExprInTemplateConstMethod.py new file mode 100644 index 0000000000000..0ba6c8a837db4 --- /dev/null +++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/TestExprInTemplateConstMethod.py @@ -0,0 +1,106 @@ +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class TestCase(TestBase): + def test(self): + self.build() + (_, process, _, _) = lldbutil.run_to_source_breakpoint( + self, "Break: const_method begin", lldb.SBFileSpec("main.cpp") + ) + + self.expect_expr("bar()", result_value="2", result_type="int") + self.expect( + "expression m_const_mem = 2.0", + error=True, + substrs=[ + "cannot assign to non-static data member", + "with const-qualified type", + ], + ) + self.expect( + "expression m_mem = 2.0", + error=True, + substrs=[ + "cannot assign to non-static data member within const member function" + ], + ) + self.expect_expr("((Foo*)this)->bar()", result_type="double", result_value="5") + + lldbutil.continue_to_source_breakpoint( + self, + process, + "Break: const_method no-this lambda", + lldb.SBFileSpec("main.cpp"), + ) + + self.expect( + "expression x = 7.0", + error=True, + substrs=[ + "cannot assign to non-static data member within const member function" + ], + ) + + lldbutil.continue_to_source_breakpoint( + self, + process, + "Break: const_method mutable no-this lambda", + lldb.SBFileSpec("main.cpp"), + ) + + self.expect_expr("x = 7.0") + self.expect_expr("x", result_value="7") + + lldbutil.continue_to_source_breakpoint( + self, process, "Break: const_method lambda", lldb.SBFileSpec("main.cpp") + ) + + # FIXME: mutating this capture should be disallowed in a non-mutable lambda. + self.expect_expr("y = 8.0") + self.expect_expr("bar()", result_value="2", result_type="int") + self.expect( + "expression m_const_mem = 2.0", + error=True, + substrs=[ + "cannot assign to non-static data member", + "with const-qualified type", + ], + ) + self.expect( + "expression m_mem = 2.0", + error=True, + substrs=[ + "cannot assign to non-static data member within const member function" + ], + ) + self.expect_expr("m_mem", result_value="-2") + self.expect_expr("((Foo*)this)->bar()", result_type="double", result_value="5") + + lldbutil.continue_to_source_breakpoint( + self, + process, + "Break: const_method mutable lambda", + lldb.SBFileSpec("main.cpp"), + ) + + self.expect_expr("y = 9.0") + self.expect_expr("bar()", result_value="2", result_type="int") + self.expect( + "expression m_const_mem = 2.0", + error=True, + substrs=[ + "cannot assign to non-static data member", + "with const-qualified type", + ], + ) + self.expect( + "expression m_mem = 2.0", + error=True, + substrs=[ + "cannot assign to non-static data member within const member function" + ], + ) + self.expect_expr("m_mem", result_value="-2") diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/main.cpp b/lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/main.cpp new file mode 100644 index 0000000000000..8238f2a52f5aa --- /dev/null +++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/main.cpp @@ -0,0 +1,51 @@ +#include <cassert> +#include <cstdio> + +struct Foo { + double bar() { return 5.0; } + + int bar() const { return 2; } + + void non_const_method() {} + + int template_const_method() const { + auto x = bar(); + assert(x == 2); + std::puts("Break: const_method begin"); + + [x] { + std::puts("Keep on multiple lines..."); + std::puts("Break: const_method no-this lambda"); + }(); + + [x]() mutable { + std::puts("Keep on multiple lines..."); + std::puts("Break: const_method mutable no-this lambda"); + }(); + + [this, y = x] { + auto x = bar() + y; + std::puts("Break: const_method lambda"); + }(); + + [this, y = x]() mutable { + auto x = bar() + y; + std::puts("Break: const_method mutable lambda"); + }(); + + return 120; + } + + float m_mem = -2.0; + const float m_const_mem = -3.0; +}; + +int main() { + const Foo f; + f.bar(); + + Foo f2; + f2.bar(); + + return Foo{}.template_const_method(); +} diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/Makefile b/lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/Makefile new file mode 100644 index 0000000000000..99998b20bcb05 --- /dev/null +++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/TestExprInTemplateNonConstMethod.py b/lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/TestExprInTemplateNonConstMethod.py new file mode 100644 index 0000000000000..8ff88e8014b4c --- /dev/null +++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/TestExprInTemplateNonConstMethod.py @@ -0,0 +1,85 @@ +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class TestCase(TestBase): + def test(self): + self.build() + (_, process, _, _) = lldbutil.run_to_source_breakpoint( + self, "Break: non_const_method begin", lldb.SBFileSpec("main.cpp") + ) + + self.expect_expr("bar()", result_value="5", result_type="double") + self.expect( + "expression m_const_mem = 2.0", + error=True, + substrs=[ + "cannot assign to non-static data member", + "with const-qualified type", + ], + ) + + lldbutil.continue_to_source_breakpoint( + self, + process, + "Break: non_const_method no-this lambda", + lldb.SBFileSpec("main.cpp"), + ) + + self.expect( + "expression x = 7.0", + error=True, + substrs=[ + "cannot assign to non-static data member within const member function" + ], + ) + + lldbutil.continue_to_source_breakpoint( + self, + process, + "Break: non_const_method mutable no-this lambda", + lldb.SBFileSpec("main.cpp"), + ) + + self.expect_expr("x = 7.0") + self.expect_expr("x", result_value="7") + + lldbutil.continue_to_source_breakpoint( + self, process, "Break: non_const_method lambda", lldb.SBFileSpec("main.cpp") + ) + + # FIXME: mutating this capture should be disallowed in a non-mutable lambda. + self.expect_expr("y = 8.0") + self.expect_expr("bar()", result_value="5", result_type="double") + self.expect( + "expression m_const_mem = 2.0", + error=True, + substrs=[ + "cannot assign to non-static data member", + "with const-qualified type", + ], + ) + self.expect_expr("m_mem = 2.0") + self.expect_expr("m_mem", result_value="2") + + lldbutil.continue_to_source_breakpoint( + self, + process, + "Break: non_const_method mutable lambda", + lldb.SBFileSpec("main.cpp"), + ) + + self.expect_expr("y = 9.0") + self.expect_expr("bar()", result_value="5", result_type="double") + self.expect( + "expression m_const_mem = 2.0", + error=True, + substrs=[ + "cannot assign to non-static data member", + "with const-qualified type", + ], + ) + self.expect_expr("m_mem = 4.0") + self.expect_expr("m_mem", result_value="4") diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/main.cpp b/lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/main.cpp new file mode 100644 index 0000000000000..1632555c69d01 --- /dev/null +++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/main.cpp @@ -0,0 +1,49 @@ +#include <cassert> +#include <cstdio> + +struct Foo { + double bar() { return 5.0; } + + int bar() const { return 2; } + + int template_non_const_method() { + auto x = bar(); + assert(x == 5.0); + std::puts("Break: non_const_method begin"); + + [x] { + std::puts("Keep on multiple lines..."); + std::puts("Break: non_const_method no-this lambda"); + }(); + + [x]() mutable { + std::puts("Keep on multiple lines..."); + std::puts("Break: non_const_method mutable no-this lambda"); + }(); + + [this, y = x] { + auto x = bar() + y; + std::puts("Break: non_const_method lambda"); + }(); + + [this, y = x]() mutable { + auto x = bar() + y; + std::puts("Break: non_const_method mutable lambda"); + }(); + + return 120; + } + + float m_mem = -2.0; + const float m_const_mem = -3.0; +}; + +int main() { + const Foo f; + f.bar(); + + Foo f2; + f2.bar(); + + return Foo{}.template_non_const_method(); +} diff --git a/lldb/test/API/lang/cpp/this/TestCPPThis.py b/lldb/test/API/lang/cpp/this/TestCPPThis.py index c9dcf30b53443..2fbbd16204609 100644 --- a/lldb/test/API/lang/cpp/this/TestCPPThis.py +++ b/lldb/test/API/lang/cpp/this/TestCPPThis.py @@ -1,6 +1,7 @@ """ Tests that C++ member and static variables are available where they should be. """ + import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * @@ -37,10 +38,15 @@ def test_with_run_command(self): self.runCmd("process continue") - # This would be disallowed if we enforced const. But we don't. - self.expect("expression -- m_a = 2", startstr="(int) $1 = 2") + self.expect( + "expression -- m_a = 2", + error=True, + substrs=[ + "cannot assign to non-static data member within const member function" + ], + ) - self.expect("expression -- (int)getpid(); m_a", startstr="(int) $2 = 2") + self.expect("expression -- (int)getpid(); m_a", startstr="(const int) $1 = 2") self.runCmd("process continue") >From 6c5d280f2e2299009cddd3b8100927d8ef66cc74 Mon Sep 17 00:00:00 2001 From: Michael Buch <[email protected]> Date: Mon, 26 Jan 2026 08:45:56 +0000 Subject: [PATCH 2/3] [lldb][test] Add SBExpressionOptions parameter to expect_expr (cherry picked from commit aaf3b8d90de29d548a718bb4b5415bcdb55ba164) --- lldb/packages/Python/lldbsuite/test/lldbtest.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/lldb/packages/Python/lldbsuite/test/lldbtest.py b/lldb/packages/Python/lldbsuite/test/lldbtest.py index 6bb4516948da5..6034eca3b93f2 100644 --- a/lldb/packages/Python/lldbsuite/test/lldbtest.py +++ b/lldb/packages/Python/lldbsuite/test/lldbtest.py @@ -2575,6 +2575,7 @@ def expect_expr( result_value=None, result_type=None, result_children=None, + options=None, ): """ Evaluates the given expression and verifies the result. @@ -2584,6 +2585,7 @@ def expect_expr( :param result_type: The type that the expression result should have. None if the type should not be checked. :param result_children: The expected children of the expression result as a list of ValueChecks. None if the children shouldn't be checked. + :param options: Expression evaluation options. None if a default set of options should be used. """ self.assertTrue( expr.strip() == expr, @@ -2591,13 +2593,15 @@ def expect_expr( ) frame = self.frame() - options = lldb.SBExpressionOptions() - # Disable fix-its that tests don't pass by accident. - options.SetAutoApplyFixIts(False) + if not options: + options = lldb.SBExpressionOptions() - # Set the usual default options for normal expressions. - options.SetIgnoreBreakpoints(True) + # Disable fix-its that tests don't pass by accident. + options.SetAutoApplyFixIts(False) + + # Set the usual default options for normal expressions. + options.SetIgnoreBreakpoints(True) if self.frame().IsValid(): options.SetLanguage(frame.GuessLanguage()) >From 2edd1f41c1ad4a6fbce771b4fae31d124963964b Mon Sep 17 00:00:00 2001 From: Michael Buch <[email protected]> Date: Fri, 23 Jan 2026 14:49:56 +0000 Subject: [PATCH 3/3] [lldb][Expression] Add --ignore-const-context expression evaluation option Depends on: * https://github.com/llvm/llvm-project/pull/177920 * https://github.com/llvm/llvm-project/pull/177922 In https://github.com/llvm/llvm-project/pull/177922 we make expressions run in C++ member functions honor the function qualifiers of the current stop context. E.g., this means we can no longer run non-const member functions when stopped in a const-member function. To ensure users can still do this if they really need/want to, we provide an option to not honor the qualifiers at all, leaving the `__lldb_expr` as the least qualified, allowing it to call any function/mutate any members. --- lldb/include/lldb/API/SBExpressionOptions.h | 4 ++ lldb/include/lldb/Target/Target.h | 6 +++ lldb/source/API/SBExpressionOptions.cpp | 12 ++++++ .../Commands/CommandObjectExpression.cpp | 6 +++ .../source/Commands/CommandObjectExpression.h | 1 + lldb/source/Commands/Options.td | 3 ++ .../Clang/ClangExpressionDeclMap.cpp | 9 ++-- .../Clang/ClangExpressionDeclMap.h | 5 ++- .../Clang/ClangExpressionSourceCode.cpp | 34 ++++++++------- .../Clang/ClangExpressionSourceCode.h | 4 +- .../Clang/ClangUserExpression.cpp | 9 ++-- .../Clang/ClangUserExpression.h | 8 ++-- .../Clang/ClangUtilityFunction.cpp | 2 +- .../const_method/TestExprInConstMethod.py | 42 +++++++++++++++++++ .../TestExprInConstVolatileMethod.py | 25 +++++++++++ .../TestExprInNonConstMethod.py | 4 ++ .../TestExprInTemplateConstMethod.py | 8 +++- .../TestExprInTemplateNonConstMethod.py | 4 ++ lldb/test/API/lang/cpp/this/TestCPPThis.py | 3 ++ .../Expression/ClangExpressionDeclMapTest.cpp | 2 +- 20 files changed, 160 insertions(+), 31 deletions(-) diff --git a/lldb/include/lldb/API/SBExpressionOptions.h b/lldb/include/lldb/API/SBExpressionOptions.h index a9e929a4c0bd9..2b922b97f75b9 100644 --- a/lldb/include/lldb/API/SBExpressionOptions.h +++ b/lldb/include/lldb/API/SBExpressionOptions.h @@ -107,6 +107,10 @@ class LLDB_API SBExpressionOptions { // Sets whether we will JIT an expression if it cannot be interpreted void SetAllowJIT(bool allow); + bool GetIgnoreConstContext(); + + void SetIgnoreConstContext(bool b = true); + protected: lldb_private::EvaluateExpressionOptions *get() const; diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h index 812a638910b3b..77caccdbcc74d 100644 --- a/lldb/include/lldb/Target/Target.h +++ b/lldb/include/lldb/Target/Target.h @@ -481,6 +481,10 @@ class EvaluateExpressionOptions { void SetIsForUtilityExpr(bool b) { m_running_utility_expression = b; } + bool GetIgnoreConstContext() const { return m_ignore_const_contexts; } + + void SetIgnoreConstContext(bool val) { m_ignore_const_contexts = val; } + private: ExecutionPolicy m_execution_policy = default_execution_policy; SourceLanguage m_language; @@ -518,6 +522,8 @@ class EvaluateExpressionOptions { /// used for symbol/function lookup before any other context (except for /// the module corresponding to the current frame). SymbolContextList m_preferred_lookup_contexts; + + bool m_ignore_const_contexts = false; }; // Target diff --git a/lldb/source/API/SBExpressionOptions.cpp b/lldb/source/API/SBExpressionOptions.cpp index 15ed403eaaea1..84f23df47b0cb 100644 --- a/lldb/source/API/SBExpressionOptions.cpp +++ b/lldb/source/API/SBExpressionOptions.cpp @@ -256,6 +256,18 @@ void SBExpressionOptions::SetAllowJIT(bool allow) { : eExecutionPolicyNever); } +bool SBExpressionOptions::GetIgnoreConstContext() { + LLDB_INSTRUMENT_VA(this); + + return m_opaque_up->GetIgnoreConstContext(); +} + +void SBExpressionOptions::SetIgnoreConstContext(bool b) { + LLDB_INSTRUMENT_VA(this, b); + + m_opaque_up->SetIgnoreConstContext(b); +} + EvaluateExpressionOptions *SBExpressionOptions::get() const { return m_opaque_up.get(); } diff --git a/lldb/source/Commands/CommandObjectExpression.cpp b/lldb/source/Commands/CommandObjectExpression.cpp index 4919bd3639d3e..14dea55164c6e 100644 --- a/lldb/source/Commands/CommandObjectExpression.cpp +++ b/lldb/source/Commands/CommandObjectExpression.cpp @@ -25,6 +25,7 @@ #include "lldb/lldb-enumerations.h" #include "lldb/lldb-forward.h" #include "lldb/lldb-private-enumerations.h" +#include <tuple> using namespace lldb; using namespace lldb_private; @@ -44,6 +45,9 @@ Status CommandObjectExpression::CommandOptions::SetOptionValue( const int short_option = GetDefinitions()[option_idx].short_option; switch (short_option) { + case 'K': + ignore_const_context = true; + break; case 'l': language = Language::GetLanguageTypeFromString(option_arg); if (language == eLanguageTypeUnknown) { @@ -191,6 +195,7 @@ void CommandObjectExpression::CommandOptions::OptionParsingStarting( top_level = false; allow_jit = true; suppress_persistent_result = eLazyBoolCalculate; + ignore_const_context = false; } llvm::ArrayRef<OptionDefinition> @@ -213,6 +218,7 @@ CommandObjectExpression::CommandOptions::GetEvaluateExpressionOptions( options.SetExecutionPolicy( allow_jit ? EvaluateExpressionOptions::default_execution_policy : lldb_private::eExecutionPolicyNever); + options.SetIgnoreConstContext(ignore_const_context); bool auto_apply_fixits; if (this->auto_apply_fixits == eLazyBoolCalculate) diff --git a/lldb/source/Commands/CommandObjectExpression.h b/lldb/source/Commands/CommandObjectExpression.h index 6fccf10e5dbc1..6bbe02f584b21 100644 --- a/lldb/source/Commands/CommandObjectExpression.h +++ b/lldb/source/Commands/CommandObjectExpression.h @@ -57,6 +57,7 @@ class CommandObjectExpression : public CommandObjectRaw, LanguageRuntimeDescriptionDisplayVerbosity m_verbosity; LazyBool auto_apply_fixits; LazyBool suppress_persistent_result; + bool ignore_const_context; }; CommandObjectExpression(CommandInterpreter &interpreter); diff --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td index d96354a39b8b8..c82a029ae95ce 100644 --- a/lldb/source/Commands/Options.td +++ b/lldb/source/Commands/Options.td @@ -778,6 +778,9 @@ let Command = "expression" in { Desc<"Persist expression result in a variable for subsequent use. " "Expression results will be labeled with $-prefixed variables, " "e.g. $0, $1, etc.">; + def ignore_const_context : Option<"ignore-const-context", "K">, + Groups<[1, 2]>, + Desc<"TODO">; } let Command = "frame diag" in { diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp index 41d2d2cb23146..52a77fbbac66a 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp @@ -88,10 +88,12 @@ ClangExpressionDeclMap::ClangExpressionDeclMap( bool keep_result_in_memory, Materializer::PersistentVariableDelegate *result_delegate, const lldb::TargetSP &target, - const std::shared_ptr<ClangASTImporter> &importer, ValueObject *ctx_obj) + const std::shared_ptr<ClangASTImporter> &importer, ValueObject *ctx_obj, + bool ignore_const_context) : ClangASTSource(target, importer), m_found_entities(), m_struct_members(), m_keep_result_in_memory(keep_result_in_memory), - m_result_delegate(result_delegate), m_ctx_obj(ctx_obj), m_parser_vars(), + m_result_delegate(result_delegate), m_ctx_obj(ctx_obj), + m_ignore_const_context(ignore_const_context), m_parser_vars(), m_struct_vars() { EnableStructVars(); } @@ -1997,7 +1999,8 @@ void ClangExpressionDeclMap::AddContextClassType(NameSearchContext &context, std::array<CompilerType, 1> args{void_clang_type.GetPointerType()}; CompilerType method_type = m_clang_ast_context->CreateFunctionType( - void_clang_type, args, false, ut.GetTypeQualifiers()); + void_clang_type, args, false, + m_ignore_const_context ? 0 : ut.GetTypeQualifiers()); const bool is_virtual = false; const bool is_static = false; diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h index dddc5a06c9051..3a5f702f6c674 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h @@ -82,7 +82,8 @@ class ClangExpressionDeclMap : public ClangASTSource { bool keep_result_in_memory, Materializer::PersistentVariableDelegate *result_delegate, const lldb::TargetSP &target, - const std::shared_ptr<ClangASTImporter> &importer, ValueObject *ctx_obj); + const std::shared_ptr<ClangASTImporter> &importer, ValueObject *ctx_obj, + bool ignore_const_context); /// Destructor ~ClangExpressionDeclMap() override; @@ -306,6 +307,8 @@ class ClangExpressionDeclMap : public ClangASTSource { ///For details see the comment to ///`UserExpression::Evaluate`. + bool m_ignore_const_context = false; + /// The following values should not live beyond parsing class ParserVars { public: diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp index ca4199faa3841..2c764d250d465 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp @@ -363,9 +363,12 @@ void ClangExpressionSourceCode::AddLocalVariableDecls(StreamString &stream, } } -bool ClangExpressionSourceCode::GetText( - std::string &text, ExecutionContext &exe_ctx, bool add_locals, - bool force_add_all_locals, llvm::ArrayRef<std::string> modules) const { +bool ClangExpressionSourceCode::GetText(std::string &text, + ExecutionContext &exe_ctx, + bool add_locals, + bool force_add_all_locals, + llvm::ArrayRef<std::string> modules, + bool ignore_const_context) const { const char *target_specific_defines = "typedef signed char BOOL;\n"; std::string module_macros; llvm::raw_string_ostream module_macros_stream(module_macros); @@ -486,17 +489,20 @@ bool ClangExpressionSourceCode::GetText( lldb_local_var_decls.GetData(), tagged_body.c_str()); break; case WrapKind::CppMemberFunction: - wrap_stream.Printf( - "%s" - "void \n" - "$__lldb_class::%s(void *$__lldb_arg) %s \n" - "{ \n" - " %s; \n" - "%s" - "} \n", - module_imports.c_str(), m_name.c_str(), - GetFrameCVQualifiers(exe_ctx.GetFramePtr()).getAsString().c_str(), - lldb_local_var_decls.GetData(), tagged_body.c_str()); + wrap_stream.Printf("%s" + "void \n" + "$__lldb_class::%s(void *$__lldb_arg) %s \n" + "{ \n" + " %s; \n" + "%s" + "} \n", + module_imports.c_str(), m_name.c_str(), + ignore_const_context + ? "" + : GetFrameCVQualifiers(exe_ctx.GetFramePtr()) + .getAsString() + .c_str(), + lldb_local_var_decls.GetData(), tagged_body.c_str()); break; case WrapKind::ObjCInstanceMethod: wrap_stream.Printf( diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.h b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.h index f721bb2f319e1..02090b1aa9b12 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.h +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.h @@ -63,8 +63,8 @@ class ClangExpressionSourceCode : public ExpressionSourceCode { /// /// \return true iff the source code was successfully generated. bool GetText(std::string &text, ExecutionContext &exe_ctx, bool add_locals, - bool force_add_all_locals, - llvm::ArrayRef<std::string> modules) const; + bool force_add_all_locals, llvm::ArrayRef<std::string> modules, + bool ignore_const_context) const; // Given a string returned by GetText, find the beginning and end of the body // passed to CreateWrapped. Return true if the bounds could be found. This diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp index 2cbbae11bd18a..3b49eb4716a63 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp @@ -416,7 +416,8 @@ void ClangUserExpression::CreateSourceCode( m_filename, prefix, m_expr_text, GetWrapKind())); if (!m_source_code->GetText(m_transformed_text, exe_ctx, !m_ctx_obj, - for_completion, modules_to_import)) { + for_completion, modules_to_import, + m_options.GetIgnoreConstContext())) { diagnostic_manager.PutString(lldb::eSeverityError, "couldn't construct expression body"); return; @@ -950,8 +951,8 @@ char ClangUserExpression::ClangUserExpressionHelper::ID; void ClangUserExpression::ClangUserExpressionHelper::ResetDeclMap( ExecutionContext &exe_ctx, Materializer::PersistentVariableDelegate &delegate, - bool keep_result_in_memory, - ValueObject *ctx_obj) { + bool keep_result_in_memory, ValueObject *ctx_obj, + bool ignore_const_context) { std::shared_ptr<ClangASTImporter> ast_importer; auto *state = exe_ctx.GetTargetSP()->GetPersistentExpressionStateForLanguage( lldb::eLanguageTypeC); @@ -961,7 +962,7 @@ void ClangUserExpression::ClangUserExpressionHelper::ResetDeclMap( } m_expr_decl_map_up = std::make_unique<ClangExpressionDeclMap>( keep_result_in_memory, &delegate, exe_ctx.GetTargetSP(), ast_importer, - ctx_obj); + ctx_obj, ignore_const_context); } clang::ASTConsumer * diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h b/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h index 7c0c6a0147e2a..2ac50b8152f33 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h @@ -71,8 +71,8 @@ class ClangUserExpression : public LLVMUserExpression { void ResetDeclMap(ExecutionContext &exe_ctx, Materializer::PersistentVariableDelegate &result_delegate, - bool keep_result_in_memory, - ValueObject *ctx_obj); + bool keep_result_in_memory, ValueObject *ctx_obj, + bool ignore_const_context); /// Return the object that the parser should allow to access ASTs. May be /// NULL if the ASTs do not need to be transformed. @@ -167,8 +167,8 @@ class ClangUserExpression : public LLVMUserExpression { Materializer::PersistentVariableDelegate &result_delegate, bool keep_result_in_memory) { m_type_system_helper.ResetDeclMap(exe_ctx, result_delegate, - keep_result_in_memory, - m_ctx_obj); + keep_result_in_memory, m_ctx_obj, + m_options.GetIgnoreConstContext()); } lldb::ExpressionVariableSP diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.cpp index e6983066a12fa..ea9a4d815a14d 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.cpp @@ -187,5 +187,5 @@ void ClangUtilityFunction::ClangUtilityFunctionHelper::ResetDeclMap( } m_expr_decl_map_up = std::make_unique<ClangExpressionDeclMap>( keep_result_in_memory, nullptr, exe_ctx.GetTargetSP(), ast_importer, - nullptr); + nullptr, /*ignore_const_context=*/false); } diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/TestExprInConstMethod.py b/lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/TestExprInConstMethod.py index 0ba6c8a837db4..8100bf414d00b 100644 --- a/lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/TestExprInConstMethod.py +++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/const_method/TestExprInConstMethod.py @@ -27,6 +27,19 @@ def test(self): "cannot assign to non-static data member within const member function" ], ) + self.expect_expr("m_mem", result_value="-2") + + self.expect( + "expression --ignore-const-context -- m_mem = 3.0", + error=False, + ) + self.expect_expr("m_mem", result_value="3") + + options = lldb.SBExpressionOptions() + options.SetIgnoreConstContext() + self.expect_expr("m_mem = -2.0", options=options) + self.expect_expr("m_mem", result_value="-2") + self.expect_expr("((Foo*)this)->bar()", result_type="double", result_value="5") lldbutil.continue_to_source_breakpoint( @@ -43,6 +56,16 @@ def test(self): "cannot assign to non-static data member within const member function" ], ) + self.expect_expr("x", result_value="2") + + self.expect( + "expression --ignore-const-context -- x = -5", + error=False, + ) + self.expect_expr("x", result_value="-5") + + self.expect_expr("x = 2", options=options) + self.expect_expr("x", result_value="2") lldbutil.continue_to_source_breakpoint( self, @@ -77,6 +100,16 @@ def test(self): ], ) self.expect_expr("m_mem", result_value="-2") + + self.expect( + "expression --ignore-const-context -- m_mem = -1", + error=False, + ) + self.expect_expr("m_mem", result_value="-1") + + self.expect_expr("m_mem = -2", options=options) + self.expect_expr("m_mem", result_value="-2") + self.expect_expr("((Foo*)this)->bar()", result_type="double", result_value="5") lldbutil.continue_to_source_breakpoint( @@ -104,3 +137,12 @@ def test(self): ], ) self.expect_expr("m_mem", result_value="-2") + + self.expect( + "expression --ignore-const-context -- m_mem = -1", + error=False, + ) + self.expect_expr("m_mem", result_value="-1") + + self.expect_expr("m_mem = -2", options=options) + self.expect_expr("m_mem", result_value="-2") diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/TestExprInConstVolatileMethod.py b/lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/TestExprInConstVolatileMethod.py index f20b9d8164841..f74d7607df661 100644 --- a/lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/TestExprInConstVolatileMethod.py +++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/const_volatile_method/TestExprInConstVolatileMethod.py @@ -20,6 +20,16 @@ def test(self): substrs=["has type 'const Foo'", "but function is not marked const"], ) + options = lldb.SBExpressionOptions() + options.SetIgnoreConstContext() + options.SetIgnoreBreakpoints(True) + self.expect_expr("volatile_method()", options=options) + self.expect( + "expression --ignore-const-context -- bar()", + error=True, + substrs=["call to member function 'bar' is ambiguous"], + ) + lldbutil.continue_to_source_breakpoint( self, process, "Break here: volatile", lldb.SBFileSpec("main.cpp") ) @@ -35,6 +45,13 @@ def test(self): ) self.expect_expr("volatile_method()") + self.expect_expr("const_method()", options=options) + self.expect( + "expression --ignore-const-context -- bar()", + error=True, + substrs=["call to member function 'bar' is ambiguous"], + ) + lldbutil.continue_to_source_breakpoint( self, process, "Break here: const volatile", lldb.SBFileSpec("main.cpp") ) @@ -58,3 +75,11 @@ def test(self): "but function is not marked const or volatile", ], ) + + self.expect_expr("const_method()", options=options) + self.expect_expr("volatile_method()", options=options) + self.expect( + "expression --ignore-const-context -- bar()", + error=True, + substrs=["call to member function 'bar' is ambiguous"], + ) diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/TestExprInNonConstMethod.py b/lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/TestExprInNonConstMethod.py index 8ff88e8014b4c..a8e30b25e273f 100644 --- a/lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/TestExprInNonConstMethod.py +++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/non_const_method/TestExprInNonConstMethod.py @@ -36,6 +36,10 @@ def test(self): ], ) + options = lldb.SBExpressionOptions() + options.SetIgnoreConstContext() + self.expect_expr("x = 6.0; x", options=options, result_value="6") + lldbutil.continue_to_source_breakpoint( self, process, diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/TestExprInTemplateConstMethod.py b/lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/TestExprInTemplateConstMethod.py index 0ba6c8a837db4..6157e22df163f 100644 --- a/lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/TestExprInTemplateConstMethod.py +++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/template_const_method/TestExprInTemplateConstMethod.py @@ -29,6 +29,10 @@ def test(self): ) self.expect_expr("((Foo*)this)->bar()", result_type="double", result_value="5") + options = lldb.SBExpressionOptions() + options.SetIgnoreConstContext() + self.expect_expr("m_mem = -2.0; m_mem", options=options, result_value="-2") + lldbutil.continue_to_source_breakpoint( self, process, @@ -43,6 +47,7 @@ def test(self): "cannot assign to non-static data member within const member function" ], ) + self.expect_expr("x = -7.0; x", options=options, result_value="-7") lldbutil.continue_to_source_breakpoint( self, @@ -78,6 +83,7 @@ def test(self): ) self.expect_expr("m_mem", result_value="-2") self.expect_expr("((Foo*)this)->bar()", result_type="double", result_value="5") + self.expect_expr("m_mem = -8.0; m_mem", options=options, result_value="-8") lldbutil.continue_to_source_breakpoint( self, @@ -103,4 +109,4 @@ def test(self): "cannot assign to non-static data member within const member function" ], ) - self.expect_expr("m_mem", result_value="-2") + self.expect_expr("m_mem = -11.0; m_mem", options=options, result_value="-11") diff --git a/lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/TestExprInTemplateNonConstMethod.py b/lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/TestExprInTemplateNonConstMethod.py index 8ff88e8014b4c..a8e30b25e273f 100644 --- a/lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/TestExprInTemplateNonConstMethod.py +++ b/lldb/test/API/lang/cpp/expression-context-qualifiers/template_non_const_method/TestExprInTemplateNonConstMethod.py @@ -36,6 +36,10 @@ def test(self): ], ) + options = lldb.SBExpressionOptions() + options.SetIgnoreConstContext() + self.expect_expr("x = 6.0; x", options=options, result_value="6") + lldbutil.continue_to_source_breakpoint( self, process, diff --git a/lldb/test/API/lang/cpp/this/TestCPPThis.py b/lldb/test/API/lang/cpp/this/TestCPPThis.py index 2fbbd16204609..83271581b1c0c 100644 --- a/lldb/test/API/lang/cpp/this/TestCPPThis.py +++ b/lldb/test/API/lang/cpp/this/TestCPPThis.py @@ -47,6 +47,9 @@ def test_with_run_command(self): ) self.expect("expression -- (int)getpid(); m_a", startstr="(const int) $1 = 2") + self.expect( + "expression --ignore-const-context -- m_a = 2", startstr="(int) $2 = 2" + ) self.runCmd("process continue") diff --git a/lldb/unittests/Expression/ClangExpressionDeclMapTest.cpp b/lldb/unittests/Expression/ClangExpressionDeclMapTest.cpp index 1c07119d4497f..16d5553fa7c6b 100644 --- a/lldb/unittests/Expression/ClangExpressionDeclMapTest.cpp +++ b/lldb/unittests/Expression/ClangExpressionDeclMapTest.cpp @@ -23,7 +23,7 @@ namespace { struct FakeClangExpressionDeclMap : public ClangExpressionDeclMap { FakeClangExpressionDeclMap(const std::shared_ptr<ClangASTImporter> &importer) : ClangExpressionDeclMap(false, nullptr, lldb::TargetSP(), importer, - nullptr) { + nullptr, /*ignore_const_context=*/false) { m_holder = std::make_unique<clang_utils::TypeSystemClangHolder>("ast"); m_scratch_context = m_holder->GetAST(); } _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
