Skip to content

Commit 74bf1a3

Browse files
committed
fix: inherit base members from dependent template specializations
When the base is a dependent template specialization (e.g., `base<T>` in `template<T> class derived : public base<T>`), `BaseMembersFinalizer` unconditionally read `SpecializationName::specializationID`, which is only set for concrete `ClassTemplateSpecializationDecl`s. For dependent bases, it stays invalid, so the `MRDOCS_CHECK_OR_CONTINUE(baseID)` guard silently skipped the base and no members were inherited. The fix falls back to the primary template's ID (`Name::id`) when `specializationID` is invalid, so a class template derived from a dependent specialization now picks up the primary's members. Concrete cases like `derived : public base<int>` still inherit from the implicit specialization as before. Closes issue #1176.
1 parent 9b193a5 commit 74bf1a3

6 files changed

Lines changed: 1007 additions & 1 deletion

File tree

src/lib/Metadata/Finalizers/BaseMembersFinalizer.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,12 +242,25 @@ operator()(RecordSymbol& I)
242242
auto& baseNameType = baseI.Type->asNamed();
243243
MRDOCS_ASSERT(!baseNameType.Name.valueless_after_move());
244244
auto& baseName = baseNameType.Name->asName();
245+
// `baseName.id` is the primary template's ID. When the base
246+
// names a concrete specialization (e.g. `base<int>`) and
247+
// `extract-implicit-specializations` is on, we prefer the
248+
// implicit specialization's ID so inherited members carry
249+
// the substituted types. For a dependent base such as
250+
// `base<T>` in `template<T> class derived : public base<T>`,
251+
// `specializationID` stays invalid (no `ClassTemplate-
252+
// SpecializationDecl` exists in the AST), so we fall back
253+
// to the primary's ID and inherit its members with the
254+
// primary's template parameter intact.
245255
SymbolID baseID = baseName.id;
246256
if (corpus_.config->extractImplicitSpecializations &&
247257
baseName.isSpecialization())
248258
{
249259
auto& baseSpec = baseName.asSpecialization();
250-
baseID = baseSpec.specializationID;
260+
if (baseSpec.specializationID)
261+
{
262+
baseID = baseSpec.specializationID;
263+
}
251264
}
252265
MRDOCS_CHECK_OR_CONTINUE(baseID);
253266
auto basePtr = corpus_.find(baseID);
Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
= Reference
2+
:mrdocs:
3+
4+
[#index]
5+
== Global namespace
6+
7+
=== Types
8+
9+
[cols="1,4"]
10+
|===
11+
| Name| Description
12+
| link:#base-06[`base`]
13+
| A class template base&period;
14+
| link:#concrete_derived[`concrete&lowbar;derived`]
15+
| A non&hyphen;template that inherits from a concrete specialization&period;
16+
| link:#derived[`derived`]
17+
| A class template that inherits from `base&lt;T&gt;`&period;
18+
|===
19+
20+
[#base-06]
21+
== base
22+
23+
A class template base&period;
24+
25+
=== Synopsis
26+
27+
Declared in `&lt;template&hyphen;base&period;cpp&gt;`
28+
29+
[source,cpp,subs="verbatim,replacements,macros,-callouts"]
30+
----
31+
template&lt;typename T&gt;
32+
class base;
33+
----
34+
35+
=== Member Functions
36+
37+
[cols="1,4"]
38+
|===
39+
| Name| Description
40+
| link:#base-06-method[`method`]
41+
| A regular member function that should also be inherited&period;
42+
| link:#base-06-operator_subs[`operator&lsqb;&rsqb;`]
43+
| Indexing operator that should be inherited&period;
44+
|===
45+
46+
=== Derived Classes
47+
48+
[cols="1,4"]
49+
|===
50+
|Name|Description
51+
52+
| link:#concrete_derived[`concrete&lowbar;derived`]
53+
| A non&hyphen;template that inherits from a concrete specialization&period;
54+
| link:#derived[`derived`]
55+
| A class template that inherits from `base&lt;T&gt;`&period;
56+
|===
57+
58+
[#base-06-method]
59+
== link:#base-06[base]::method
60+
61+
A regular member function that should also be inherited&period;
62+
63+
=== Synopsis
64+
65+
Declared in `&lt;template&hyphen;base&period;cpp&gt;`
66+
67+
[source,cpp,subs="verbatim,replacements,macros,-callouts"]
68+
----
69+
T&
70+
method();
71+
----
72+
73+
[#base-06-operator_subs]
74+
== link:#base-06[base]::operator&lsqb;&rsqb;
75+
76+
Indexing operator that should be inherited&period;
77+
78+
=== Synopsis
79+
80+
Declared in `&lt;template&hyphen;base&period;cpp&gt;`
81+
82+
[source,cpp,subs="verbatim,replacements,macros,-callouts"]
83+
----
84+
T&
85+
operator&lsqb;&rsqb;(int n);
86+
----
87+
88+
=== Parameters
89+
90+
[cols="1,4"]
91+
|===
92+
|Name|Description
93+
94+
| *n*
95+
| The right operand
96+
|===
97+
98+
[#concrete_derived]
99+
== concrete&lowbar;derived
100+
101+
A non&hyphen;template that inherits from a concrete specialization&period;
102+
103+
=== Synopsis
104+
105+
Declared in `&lt;template&hyphen;base&period;cpp&gt;`
106+
107+
[source,cpp,subs="verbatim,replacements,macros,-callouts"]
108+
----
109+
class concrete&lowbar;derived
110+
: public link:#base-06[base&lt;int&gt;]
111+
----
112+
113+
=== Base Classes
114+
115+
[cols="1,4"]
116+
|===
117+
|Name|Description
118+
119+
| `link:#base-06[base&lt;int&gt;]`
120+
| A class template base&period;
121+
|===
122+
123+
=== Member Functions
124+
125+
[cols=1]
126+
|===
127+
| Name
128+
| link:#concrete_derived-method[`method`]
129+
| link:#concrete_derived-operator_subs[`operator&lsqb;&rsqb;`]
130+
|===
131+
132+
[#concrete_derived-method]
133+
== link:#concrete_derived[concrete&lowbar;derived]::method
134+
135+
=== Synopsis
136+
137+
Declared in `&lt;template&hyphen;base&period;cpp&gt;`
138+
139+
[source,cpp,subs="verbatim,replacements,macros,-callouts"]
140+
----
141+
int&
142+
method();
143+
----
144+
145+
[#concrete_derived-operator_subs]
146+
== link:#concrete_derived[concrete&lowbar;derived]::operator&lsqb;&rsqb;
147+
148+
=== Synopsis
149+
150+
Declared in `&lt;template&hyphen;base&period;cpp&gt;`
151+
152+
[source,cpp,subs="verbatim,replacements,macros,-callouts"]
153+
----
154+
int&
155+
operator&lsqb;&rsqb;(int n);
156+
----
157+
158+
[#derived]
159+
== derived
160+
161+
A class template that inherits from `base&lt;T&gt;`&period;
162+
163+
=== Synopsis
164+
165+
Declared in `&lt;template&hyphen;base&period;cpp&gt;`
166+
167+
[source,cpp,subs="verbatim,replacements,macros,-callouts"]
168+
----
169+
template&lt;typename T&gt;
170+
class derived
171+
: public link:#base-06[base&lt;T&gt;]
172+
----
173+
174+
=== Base Classes
175+
176+
[cols="1,4"]
177+
|===
178+
|Name|Description
179+
180+
| `link:#base-06[base&lt;T&gt;]`
181+
| A class template base&period;
182+
|===
183+
184+
=== Member Functions
185+
186+
[cols="1,4"]
187+
|===
188+
| Name| Description
189+
| link:#derived-method[`method`]
190+
| A regular member function that should also be inherited&period;
191+
| link:#derived-operator_subs[`operator&lsqb;&rsqb;`]
192+
| Indexing operator that should be inherited&period;
193+
|===
194+
195+
[#derived-method]
196+
== link:#derived[derived]::method
197+
198+
A regular member function that should also be inherited&period;
199+
200+
=== Synopsis
201+
202+
Declared in `&lt;template&hyphen;base&period;cpp&gt;`
203+
204+
[source,cpp,subs="verbatim,replacements,macros,-callouts"]
205+
----
206+
T&
207+
method();
208+
----
209+
210+
[#derived-operator_subs]
211+
== link:#derived[derived]::operator&lsqb;&rsqb;
212+
213+
Indexing operator that should be inherited&period;
214+
215+
=== Synopsis
216+
217+
Declared in `&lt;template&hyphen;base&period;cpp&gt;`
218+
219+
[source,cpp,subs="verbatim,replacements,macros,-callouts"]
220+
----
221+
T&
222+
operator&lsqb;&rsqb;(int n);
223+
----
224+
225+
=== Parameters
226+
227+
[cols="1,4"]
228+
|===
229+
|Name|Description
230+
231+
| *n*
232+
| The right operand
233+
|===
234+
235+
236+
[.small]#Created with https://www.mrdocs.com[MrDocs]#
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Reproduces issue #1176: members defined in a class template base do not
2+
// appear in derived classes that inherit from a specialization of that base.
3+
//
4+
// Boost.Multi's `array_ref` derives from `subarray`, and `subarray`'s
5+
// `operator[]` did not show up on `array_ref`'s documentation page,
6+
// even with `inherit-base-members: copy-all`.
7+
8+
/// A class template base.
9+
template <typename T>
10+
class base
11+
{
12+
public:
13+
/// Indexing operator that should be inherited.
14+
T& operator[](int n);
15+
16+
/// A regular member function that should also be inherited.
17+
T& method();
18+
};
19+
20+
/// A class template that inherits from `base<T>`.
21+
template <typename T>
22+
class derived
23+
: public base<T>
24+
{};
25+
26+
/// A non-template that inherits from a concrete specialization.
27+
class concrete_derived
28+
: public base<int>
29+
{};

0 commit comments

Comments
 (0)