Skip to content

[lldb] lldb can't invoke function when param or return value with anonymous namespaces class. #104712

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
zhiqiangzz opened this issue Aug 18, 2024 · 4 comments · Fixed by #104817
Labels

Comments

@zhiqiangzz
Copy link

zhiqiangzz commented Aug 18, 2024

I found that if a function definition with anonymous namespaces class in the parameter or return value.
While debugging with lldb, these functions can't be invoked with errors like

error: error: function 'xxx' is used but not defined in this translation unit, and cannot be defined in any other translation unit because its type does not have linkage

See the following example:

namespace {
class A {
  int a;

public:
  explicit A(int a) : a{a} {}
  int func() { return a + 1; }
};

} // namespace

void *param(A /*a*/) { return nullptr; }
A *returnvalue() { return nullptr; }

int main() {
  A a = A{1};

  // avoid eliminated by compiler optimization
  (void)a.func();
  (void)param(a);
  (void)returnvalue();

  return 0;
}

Debug above program by cli lldb -- bin/lldbAnonymousVariable

(lldb) target create "bin/lldbAnonymousVariable"
Current executable set to '/Users/zhiqiangz/llvm/llvm-demo/bin/lldbAnonymousVariable' (arm64).
(lldb) b 23
Breakpoint 1: where = lldbAnonymousVariable`main + 76 at lldbAnonymousVariable.cpp:23:3, address = 0x0000000100003efc
(lldb) r
Process 33550 launched: '/Users/zhiqiangz/llvm/llvm-demo/bin/lldbAnonymousVariable' (arm64)
Process 33550 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x0000000100003efc lldbAnonymousVariable`main at lldbAnonymousVariable.cpp:23:3
   20  	  (void)param(a);
   21  	  (void)returnvalue();
   22
-> 23  	  return 0;
   24  	}
(lldb) e param(a)
error: error: function 'param' is used but not defined in this translation unit, and cannot be defined in any other translation unit because its type does not have linkage
<user expression 0>:1:1: used here
    1 | param(a)
      | ^

(lldb) e returnvalue()
error: error: function 'returnvalue' is used but not defined in this translation unit, and cannot be defined in any other translation unit because its type does not have linkage
<user expression 1>:1:1: used here
    1 | returnvalue()
      | ^

definitely the exec program contain corresponding symbols, verified by follow command.

nm bin/lldbAnonymousVariable | c++filt | rg "param"
0000000100003f58 t param((anonymous namespace)::A)
  • machine info
uname -a
Darwin zhiqiangz-mlt 23.3.0 Darwin Kernel Version 23.3.0: Wed Dec 20 21:30:59 PST 2023; root:xnu-10002.81.5~7/RELEASE_ARM64_T6030 arm64
  • llvm version
llvm-config --version
19.0.0git
@zhiqiangzz
Copy link
Author

@Michael137 @adrian-prantl Could you please have a look? I found you resolved similar problems recently. Many thanks!

@llvmbot
Copy link
Member

llvmbot commented Aug 18, 2024

@llvm/issue-subscribers-lldb

Author: None (GithubXxz)

I found that if a function definition with anonymous namespaces class in the parameter or return value. While debugging with lldb, these functions can't be invoked with errors like ```shell error: error: function 'xxx' is used but not defined in this translation unit, and cannot be defined in any other translation unit because its type does not have linkage ```

See the following example:

namespace {
class A {
  int a;

public:
  explicit A(int a) : a{a} {}
  int func() { return a + 1; }
};

} // namespace

void *param(A /*a*/) { return nullptr; }
A *returnvalue() { return nullptr; }

int main() {
  A a = A{1};

  // avoid eliminated by compiler optimization
  (void)a.func();
  (void)param(a);
  (void)returnvalue();

  return 0;
}

Debug above program by cli lldb -- bin/lldbAnonymousVariable

(lldb) target create "bin/lldbAnonymousVariable"
Current executable set to '/Users/zhiqiangz/llvm/llvm-demo/bin/lldbAnonymousVariable' (arm64).
(lldb) b 23
Breakpoint 1: where = lldbAnonymousVariable`main + 76 at lldbAnonymousVariable.cpp:23:3, address = 0x0000000100003efc
(lldb) r
Process 33550 launched: '/Users/zhiqiangz/llvm/llvm-demo/bin/lldbAnonymousVariable' (arm64)
Process 33550 stopped
* thread #<!-- -->1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #<!-- -->0: 0x0000000100003efc lldbAnonymousVariable`main at lldbAnonymousVariable.cpp:23:3
   20  	  (void)param(a);
   21  	  (void)returnvalue();
   22
-&gt; 23  	  return 0;
   24  	}
(lldb) e param(a)
error: error: function 'param' is used but not defined in this translation unit, and cannot be defined in any other translation unit because its type does not have linkage
&lt;user expression 0&gt;:1:1: used here
    1 | param(a)
      | ^

(lldb) e returnvalue()
error: error: function 'returnvalue' is used but not defined in this translation unit, and cannot be defined in any other translation unit because its type does not have linkage
&lt;user expression 1&gt;:1:1: used here
    1 | returnvalue()
      | ^

definitely the exec program contain corresponding symbols, verified by follow command.

nm bin/lldbAnonymousVariable | c++filt | rg "param"
0000000100003f58 t param((anonymous namespace)::A)
  • machine info
uname -a
Darwin zhiqiangz-mlt 23.3.0 Darwin Kernel Version 23.3.0: Wed Dec 20 21:30:59 PST 2023; root:xnu-10002.81.5~7/RELEASE_ARM64_T6030 arm64
  • llvm version
llvm-config --version
19.0.0git

@Michael137
Copy link
Member

Michael137 commented Aug 18, 2024

Interesting, the error comes from Sema. It looks like func for such cases is marked with UniqueExternal linkage, meaning mightHaveNonExternalLinkage is true for this, and clang inserts it into the UndefinedButUsed set. Which it then diagnoses and errors out.

The reason it thinks the function is undefined is that it doesn't have a function body at the point we parse it (i.e., FuncionDecl::getBody() == nullptr. Usually we resolve function references through linkage names.

But Sema does give us the opportunity to reconcile the UndefinedButUsed set with ExternalASTSource::ReadUndefinedButUsed API, which LLDB currently doesn't implement. But we can definitely do so which should resolve this issue

@zhiqiangzz
Copy link
Author

zhiqiangzz commented Aug 19, 2024

Thanks for your reply, Michael!
Ok I got it, expecting the implementation for this. I come across this problem while debugging the MemorySSA in llvm project, a container(SmallVector) with anonymous object in it can't be accessed by operator[] and begin() which mean I can't even get any element in the container.

Michael137 added a commit to Michael137/llvm-project that referenced this issue Aug 19, 2024
…inedButUsed

While parsing an expression, Clang tries to diagnose usage of decls
(with possibly non-external linkage) for which it hasn't been provided
with a definition. This is the case, e.g., for functions with parameters
that live in an anonymous namespace (those will have `UniqueExternal`
linkage). Before diagnosing such situations, Clang calls
`ExternalSemaSource::ReadUndefinedButUsed`. The intended use of this
API is to extend the set of "used but not defined" decls with additional
ones that the external source knows about. However, in LLDB's case,
we never provide `FunctionDecl`s with a definition, and instead
rely on the expression parser to resolve those symbols by linkage name.
Thus, to avoid the Clang parser from erroring out in these situations,
this patch implements `ReadUndefinedButUsed` which just removes the
"undefined" non-external `FunctionDecl`s that Clang found.

Fixes llvm#104712
Michael137 added a commit to swiftlang/llvm-project that referenced this issue Aug 21, 2024
…inedButUsed (llvm#104817)

While parsing an expression, Clang tries to diagnose usage of decls
(with possibly non-external linkage) for which it hasn't been provided
with a definition. This is the case, e.g., for functions with parameters
that live in an anonymous namespace (those will have `UniqueExternal`
linkage, this is computed [here in
computeTypeLinkageInfo](https://github.com/llvm/llvm-project/blob/ea8bb4d633683f5cbfd82491620be3056f347a02/clang/lib/AST/Type.cpp#L4647-L4653)).
Before diagnosing such situations, Clang calls
`ExternalSemaSource::ReadUndefinedButUsed`. The intended use of this API
is to extend the set of "used but not defined" decls with additional
ones that the external source knows about. However, in LLDB's case, we
never provide `FunctionDecl`s with a definition, and instead rely on the
expression parser to resolve those symbols by linkage name. Thus, to
avoid the Clang parser from erroring out in these situations, this patch
implements `ReadUndefinedButUsed` which just removes the "undefined"
non-external `FunctionDecl`s that Clang found.

We also had to add an `ExternalSemaSource` to the `clang::Sema` instance
LLDB creates. We previously didn't have any source on `Sema`. Because we
add the `ExternalASTSourceWrapper` here, that means we'd also
technically be adding the `ClangExpressionDeclMap` as an
`ExternalASTSource` to `Sema`, which is fine because `Sema` will only be
calling into the `ExternalSemaSource` APIs (though nothing currently
strictly enforces this, which is a bit worrying).

Note, the decision for whether to put a function into `UndefinedButUsed`
is done in
[Sema::MarkFunctionReferenced](https://github.com/llvm/llvm-project/blob/ea8bb4d633683f5cbfd82491620be3056f347a02/clang/lib/Sema/SemaExpr.cpp#L18083-L18087).
The `UniqueExternal` linkage computation is done in
[getLVForNamespaceScopeDecl](https://github.com/llvm/llvm-project/blob/ea8bb4d633683f5cbfd82491620be3056f347a02/clang/lib/AST/Decl.cpp#L821-L833).

Fixes llvm#104712

(cherry picked from commit 8056d92)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
4 participants