I’m writing formatters for Clang, and run into a systematic issue that LLDB doesn’t seem to offer a good solution for. Consider the following simplified example of real Clang code:
struct Type {
enum TypeClass { Enum };
TypeClass TC;
int TypeData;
};
struct EnumType : Type {
int EnumData;
};
struct QualType {
llvm::PointerIntPair<Type *, unsigned, 3> Value;
};
void foo(QualType QT, EnumType *ET) {
// QT stores a pointer to EnumType
// breakpoint here
}
If you write some straightforward[1] formatters that doesn’t perform any downcasts, v -T
inside foo
would print something along the lines of:
(QualType) QT = {
(llvm::PointerIntPair<Type *, unsigned, 3>) Value = {
(Type *) SynthesizedPointer = {
(TypeClass) TC = Enum
(int) TypeData = 0
}
(unsigned) SynthesizedInt = 0
}
}
(EnumType *) ET = {
(Type) Type = {
(TypeClass) TC = Enum
(int) TypeData = 0
}
(int) EnumData = 0
}
I find it unfortunate that less data is extracted from QualType
(EnumData
is missing). Since the code itself makes perfect sense, I need to address it on the formatter side:
Option 1: add SynthesizedDerived
child to Type
formatter. This immediately leads to recursion: Type
has an EnumType
child, which has an implicit base class child Type
.
Option 2: option 1 + leveraging SyntheticChildrenGenerated
flag in SBValue
. The issues that while this flag is not set for a local Type
variable, it is set (at least should be) for SBValue
s created out of thin air by PointerIntPair
formatter, including Type
pointer. So no way to write a robust formatter on top of this.
Option 3: make PointerIntPair
produce a downcasted pointer instead of pointer to base as written in the source. This is doable, but requires special-casing every base. This means (a) coupling between PointerIntPair
formatter and formatters for the types passed as the first argument that is not present in the source; (b) one global type search by name (FindFirstType
) for so that we have an SBType
to cast to, which is in my experience a 1 or 2 orders of magnitude more expensive operation that the usual stuff formatters do. None of this sounds good from responsiveness and maintainability perspective.
Option 4: add a new downcast(SBValue) -> SBValue
function to formatters that LLDB calls before settling on the type of the value and picking the formatter based on that. A no-op by default, this would be the place for “let’s take a look at TC
and downcast to a proper type if possible” logic. This option is free from concerns raised for option 3.
I don’t see a good solution that is available in LLDB today, and don’t see any obvious flaws in option 4. It would be nice to get feedback on the problem presented, option 4, or other options that haven’t occurred to me. If there is a way skip over implementation of option 4 without compromising maintainability, responsiveness, or other important aspects, I’m all ears.
CC @jingham
[1] PointerIntPair
is a bit-packed pointer, so calling a formatter for it that unpacks pointer and integer straightforward is a stretch.