Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 30 additions & 23 deletions src/etc/lldb_providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,6 @@ def num_children(self) -> int:
return self.valobj.GetNumChildren()

def get_child_index(self, name: str) -> int:
if self.is_ptr and name == "$$dereference$$":
return self.valobj.Dereference().GetSyntheticValue()
return self.valobj.GetIndexOfChildWithName(name)

def get_child_at_index(self, index: int) -> Optional[SBValue]:
Expand All @@ -133,13 +131,17 @@ def num_children(self) -> int:
return 1

def get_child_index(self, name: str) -> int:
if self.is_ptr and name == "$$dereference$$":
if name == "$$dereference$$":
Copy link
Copy Markdown
Member

@jieyouxu jieyouxu Apr 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question (non-blocking): Trying to better understand the synthethic provider relationships. Do we know why dereferences are handled I guess "directly" by DefaultSyntheticProvider, and not another *SyntheticProvider? Is it just because dereferences are very common?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DefaultSyntheticProvider shouldnt ever encounter pointer types at all anymore, and its pointer logic was vestigial. Due to this check, all indirection should go through IndirectionSyntheticProvider.

Having an indirection provider fixes a number of issues that boil down to "none of our providers expect pointers to be passed in". Usually it's fine because LLDB propogates pointee children upwards as pointer children, but when we're doing things like inspecting the type's layout directly (e.g. enum variants) or a type's generic params, looking at the pointer type causes the logic to fail.

There's an alternative, iirc it's a flag on synthetics/summaries called lldb.eTypeOptionFrontendWantsDereference. I think it does what we want (forces only dereferenced/non-pointer entities passed to providers), but i need to test it and dig through the logic in LLDB to be sure.

$$dereference$$ is a special child name and LLDB uses it to allow operator * dereferencing and operator -> field access in the repl.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the detailed reply

return 0
return -1

def get_child_at_index(self, index: int) -> Optional[SBValue]:
if index == 0:
return self.valobj.Dereference().GetSyntheticValue()
value = self.valobj.Dereference()
if (synth := value.GetSyntheticValue()).IsValid():
return synth
else:
return value
return None

def update(self):
Expand Down Expand Up @@ -569,7 +571,9 @@ def update(self):
self.variant = all_variants.GetChildAtIndex(index)
self.value = self.variant.GetChildMemberWithName(
ClangEncodedEnumProvider.VALUE_MEMBER_NAME
).GetSyntheticValue()
)
if (synth := self.value.GetSyntheticValue()).IsValid():
self.value = synth

def _getCurrentVariantIndex(self, all_variants: SBValue) -> int:
default_index = 0
Expand Down Expand Up @@ -651,9 +655,10 @@ def update(self):
).GetValueAsUnsigned()
if tag == discr:
self.variant = child
self.value = child.GetChildMemberWithName(
"value"
).GetSyntheticValue()
self.value = child.GetChildMemberWithName("value")
if (synth := self.value.GetSyntheticValue()).IsValid():
self.value = synth

return
else: # if invalid, DISCR must be a range
begin: int = (
Expand All @@ -671,16 +676,18 @@ def update(self):
if begin < end:
if begin <= tag <= end:
self.variant = child
self.value = child.GetChildMemberWithName(
"value"
).GetSyntheticValue()
self.value = child.GetChildMemberWithName("value")
if (synth := self.value.GetSyntheticValue()).IsValid():
self.value = synth

return
else:
if tag >= begin or tag <= end:
self.variant = child
self.value = child.GetChildMemberWithName(
"value"
).GetSyntheticValue()
self.value = child.GetChildMemberWithName("value")
if (synth := self.value.GetSyntheticValue()).IsValid():
self.value = synth

return
else: # if invalid, tag is a 128 bit value
tag_lo: int = self.valobj.GetChildMemberWithName(
Expand Down Expand Up @@ -714,9 +721,9 @@ def update(self):
discr: int = (exact_hi << 64) | exact_lo
if tag == discr:
self.variant = child
self.value = child.GetChildMemberWithName(
"value"
).GetSyntheticValue()
self.value = child.GetChildMemberWithName("value")
if (synth := self.value.GetSyntheticValue()).IsValid():
self.value = synth
return
else: # if invalid, DISCR must be a range
begin_lo: int = (
Expand Down Expand Up @@ -748,16 +755,16 @@ def update(self):
if begin < end:
if begin <= tag <= end:
self.variant = child
self.value = child.GetChildMemberWithName(
"value"
).GetSyntheticValue()
self.value = child.GetChildMemberWithName("value")
if (synth := self.value.GetSyntheticValue()).IsValid():
self.value = synth
return
else:
if tag >= begin or tag <= end:
self.variant = child
self.value = child.GetChildMemberWithName(
"value"
).GetSyntheticValue()
self.value = child.GetChildMemberWithName("value")
if (synth := self.value.GetSyntheticValue()).IsValid():
self.value = synth
return

def num_children(self) -> int:
Expand Down
Loading