Skip to content

fix(cpp): a receiver declared as a class template's member type, like SkipList<Key, Comparator>::Iterator, is that template's member - #2573

Merged
colbymchenry merged 1 commit into
mainfrom
claude/cpp-template-member-types
Oct 15, 2026
Merged

colbymchenry merged 1 commit into
mainfrom
claude/cpp-template-member-types

Conversation

@colbymchenry

Copy link
Copy Markdown
Owner

Summary

This is the follow-up that the #2569 A/B surfaced.

The C++ declarator regex read a declared type as a name plus one template argument list. So SkipList<Key, Comparator>::Iterator iter(&list); couldn't be read whole: the match started at Iterator, and C++ lookup from the calling function found a different class.

  • In google/leveldb's skip-list tests, iter.Valid(), Seek() and key() linked to leveldb::Iterator.
  • In facebook/rocksdb's skip-list tests, the same calls linked to IteratorBase.
  • rocksdb's SkipListRep::Iterator and LookaheadIterator hold an InlineSkipList<const MemTableRep::KeyComparator&>::Iterator iter_; (and prev_). Their iter_.Valid() calls linked to the enclosing iterator's own methods, which the code doesn't call.

What changed:

  • Reading the type: CPP_DECLARATOR_TYPE now reads Outer<Args>::Member as one type, so C++ lookup (cppClassWritten) finds the member in the class template. Declarations written with typename, over two lines, through a pointer, and as data members are all covered.

  • Library templates: isCppLibraryType counts a member of a library template's specialization as a library type only when it is an iterator. std::vector<int>::iterator is the library's, never a project class named iterator, which is what main linked . calls to. Other members name another type that may be the project's, so their calls go on as before:

    • std::vector<Slice>::value_type;
    • gmock's typename std::tuple_element<N - 1, MatcherTuple>::type matcher.

    The existing test "a std trait or member type names another type, maybe the project's" relies on this rule.

Validation

  • New test __tests__/cpp-template-member-types.test.ts (5 tests): 3 reproductions fail on main and pass here. 2 guards pass on both: a member alias BaseHyperClockCache<Table>::Shard*, and std::vector<Slice>::value_type.
  • Mutations: switching off each part fails its tests:
    • the member read: the 3 reproductions;
    • the iterator rule: the std::vector<int>::iterator test;
    • the library-member rule: the value_type test and the existing cpp-library-type-receiver test.
  • A/B (full index, main vs fix) on google/leveldb, fmtlib/fmt, redis (C control), facebook/rocksdb and protocolbuffers/protobuf. Every changed site was triaged.
    • leveldb: 33 calls in db/skiplist_test.cc moved from leveldb::Iterator::* to leveldb::SkipList::Iterator::*. All are correct.
    • rocksdb: 106 calls moved, all correct:
      • 38 in memtable/skiplist_test.cc moved from IteratorBase to SkipList::Iterator;
      • 38 in memtable/inlineskiplist_test.cc moved from IteratorBase to InlineSkipList::Iterator;
      • 30 in memtable/skiplistrep.cc moved from SkipListRep::Iterator to InlineSkipList::Iterator.
    • fmt, redis, protobuf: byte-identical. A first version without the library-member rule dropped one gmock call in fmt's vendored gtest (the tuple_element<…>::type matcher above). The rule restores it.
    • No edges were added or removed.
  • Full suite, including every C/C++ test file: 7,135 passed, 0 failed.

🤖 Generated with Claude Code

… SkipList<Key, Comparator>::Iterator, is that template's member

The declarator regex read a type as a name plus one template argument
list, so `SkipList<Key, Comparator>::Iterator iter(&list);` was read from
its last name alone and looked up from the calling function: leveldb's
skip-list tests linked `iter.Valid()` to `leveldb::Iterator::Valid`,
rocksdb's to `IteratorBase::Valid`, and rocksdb's SkipListRep iterators
linked their `iter_` member's calls (`InlineSkipList<…>::Iterator iter_;`)
to their own methods. The type is now read whole, `Outer<Args>::Member`,
and C++ lookup finds the member in the class template.

A library template's member read this way is a library type only as an
iterator (`std::vector<int>::iterator`, never a project class named
`iterator`). Others name another type that may be the project's
(`std::vector<Slice>::value_type`, gmock's `typename
std::tuple_element<N - 1, MatcherTuple>::type matcher`), so their calls go
on as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant