This is an archive of the discontinued LLVM Phabricator instance.

[lldb] Workaround for type-like entities defined in a lexical block
AbandonedPublic

Authored by krisb on Dec 7 2021, 12:33 PM.

Details

Summary

D113741 and D113743 makes types defined in a lexical (bracketed) block
correctly scoped within this block, which reveals a lack of support of
such a case in lldb.

Consider the following example:

  * thread #1, name = 'a.out', stop reason = step over
      frame #0: 0x000000000040111d a.out`foo(a=1) at test_lldb.cpp:5:12
     1          int foo(int a) {
     2   	  {
     3   	    typedef int Int;
     4   	    Int local = a;
  -> 5   	    return local;
     6   	  }
     7   	}
     8
  (lldb) p local
  error: expression failed to parse:
  error: <lldb wrapper prefix>:45:31: no member named 'local' in namespace '$__lldb_local_vars'
      using $__lldb_local_vars::local;
            ~~~~~~~~~~~~~~~~~~~~^
  error: <user expression 0>:1:1: use of undeclared identifier 'local'
  local
^

lldb failed to find 'local' variable typed with a lexical block defined type (typedef Int).
The immediate cause of this issue is that clang failed to import this typedef:

lldb   (x86_64) a.out: DWARFASTParserClang::ParseTypeFromDWARF (die = 0x00000070, decl_ctx = 0x2509cb0 (die 0x00000055)) DW_TAG_typedef name = 'Int')
lldb   (x86_64) a.out: SymbolFileDWARF::ResolveTypeUID (die = 0x00000070) DW_TAG_typedef 'Int'
lldb   (x86_64) a.out: SymbolFileDWARF::ResolveTypeUID (die = 0x00000096) DW_TAG_base_type 'int'
lldb   Compiler diagnostic:

lldb   Couldn't import type: UnsupportedConstruct
lldb   Couldn't copy a variable's type into the parser's AST context

The importing wasn't success because clang wasn't able to import
typedef's context, which is actually a BlockDecl (import for BlockDecl isn't implemented,
but this is still a question to me why DW_TAG_lexical_block is associated with a BlockDecl).

This workaround makes everything behaves as it was before D113741 / D113743.
While looking for a context for a type-like entity scoped within a lexical block,
GetDeclContextDIEContainingDIE() will return a DeclContext of a parent subprogram,
instead of a BlockDecl.

Despite the patch doesn't fix the issue properly, looks like a step
forward to me as lldb at least may now properly display variables of
such types, which may appear in the DWARF generated by other compilers
(gcc as an example).

Diff Detail

Event Timeline

krisb created this revision.Dec 7 2021, 12:33 PM
krisb requested review of this revision.Dec 7 2021, 12:33 PM
Herald added a project: Restricted Project. · View Herald TranscriptDec 7 2021, 12:33 PM

I worry this makes the case where a function has two definitions of the same type in one function a more confusing error than the "can't find type" error you would get without this patch. Is this of sufficient urgency that we can't take the time to fix it correctly?

krisb added a comment.Dec 8 2021, 6:44 AM

I worry this makes the case where a function has two definitions of the same type in one function a more confusing error than the "can't find type" error you would get without this patch. Is this of sufficient urgency that we can't take the time to fix it correctly?

No urgency at all, I just believe it makes things a bit better than we have now.
And I want D113741 gets relanded :)

Before D113741 (which gets reverted because of the aforementioned issue in lldb), there was an almost the same behavior as we would have if both D113741 and this patch applied.
I mean, before D113741, clang always put types to their parent subprogram scope, no matter where they were actually defined. So we got all the types defined within a particular function emitted in the same scope which is the subprogram.
As expected, if we had two types in the same name defined in the same function, lldb could't properly display information about this type. But there were/are no issues with displaying variables types for such a case.
Here is an example:

int main() {
  int a = -1; 
  if (a > 0) {
    typedef int Int;
    Int local = a;
    return a * local;
  } else {
    typedef long Int;
    Int local = a;
    return a * local;
  }
}

Even if in the beginning of main we can access 'Int' and lldb will always display as it 'int', never 'long':

* thread #1, name = 'a.out', stop reason = breakpoint 1.1
    frame #0: 0x000000000040111b a.out`main at test_lldb.cpp:2:7
   1   	int main() {
-> 2   	  int a = -1;
   3   	  if (a > 0) {
   4   	    typedef int Int;
   5   	    Int local = a;
   6   	    return a * local;
   7   	  } else {
(lldb) image lookup -t Int
Best match found in /home/kbessonova/workspace/llvm/llvm-project/build/a.out:
id = {0x0000006c}, name = "Int", byte-size = 4, decl = test_lldb.cpp:4, compiler_type = "typedef Int"
     typedef 'Int': id = {0x000000a0}, name = "int", byte-size = 4, compiler_type = "int"

There are no issues with 'local':

* thread #1, name = 'a.out', stop reason = step over
    frame #0: 0x0000000000401141 a.out`main at test_lldb.cpp:9:17
   6   	    return a * local;
   7   	  } else {
   8   	    typedef long Int;
-> 9   	    Int local = a;
   10  	    return a * local;
   11  	  }
   12  	}
(lldb) p local
(Int) $0 = 4198432 + 0i

And in scripting mode we can see its proper type:

(lldb) script
Python Interactive Interpreter. To exit, type 'quit()', 'exit()' or Ctrl-D.
>>> var = lldb.frame.FindVariable("local")
>>> type = var.GetType()
>>> print(type)
typedef Int
>>> print(type.GetCanonicalType())
long

So 'before D113741' == ' D113741 + D115277' with one difference: lldb would no longer report errors for variables which types are defined within a lexical block, which looks to me more valuable than having a proper type information from 'image lookup -t'.

Herald added a project: Restricted Project. · View Herald TranscriptMay 16 2022, 8:05 AM