Skip to content

Commit fced0ea

Browse files
committed
issue #10717 Issue documenting multiple mixin classes with virtual inheritance
1 parent 9b513db commit fced0ea

File tree

5 files changed

+92
-51
lines changed

5 files changed

+92
-51
lines changed

src/classdef.cpp

Lines changed: 74 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,7 @@ class ClassDefImpl : public DefinitionMixin<ClassDefMutable>
395395
void addTypeConstraint(const QCString &typeConstraint,const QCString &type);
396396
void writeTemplateSpec(OutputList &ol,const Definition *d,
397397
const QCString &type,SrcLangExt lang) const;
398+
void mergeMembersFromBaseClasses(bool mergeVirtualBaseClass);
398399

399400
// PIMPL idiom
400401
class IMPL;
@@ -1201,7 +1202,7 @@ void ClassDefImpl::internalInsertMember(MemberDef *md,
12011202
//printf("=======> adding member %s to class %s\n",qPrint(md->name()),qPrint(name()));
12021203

12031204
MemberNameInfo *mni = m_impl->allMemberNameInfoLinkedMap.add(md->name());
1204-
mni->push_back(std::make_unique<MemberInfo>(md,prot,md->virtualness(),FALSE));
1205+
mni->push_back(std::make_unique<MemberInfo>(md,prot,md->virtualness(),false,false));
12051206
}
12061207
}
12071208

@@ -3518,47 +3519,34 @@ static bool isStandardFunc(const MemberDef *md)
35183519
md->isDestructor(); // destructor
35193520
}
35203521

3521-
/*!
3522-
* recursively merges the 'all members' lists of a class base
3523-
* with that of this class. Must only be called for classes without
3524-
* subclasses!
3525-
*/
3526-
void ClassDefImpl::mergeMembers()
3522+
void ClassDefImpl::mergeMembersFromBaseClasses(bool mergeVirtualBaseClass)
35273523
{
3528-
if (m_impl->membersMerged) return;
3529-
3530-
//bool optimizeOutputForJava = Config_getBool(OPTIMIZE_OUTPUT_JAVA);
3531-
//bool vhdlOpt = Config_getBool(OPTIMIZE_OUTPUT_VHDL);
35323524
SrcLangExt lang = getLanguage();
35333525
QCString sep=getLanguageSpecificSeparator(lang,TRUE);
35343526
size_t sepLen = sep.length();
3535-
3536-
m_impl->membersMerged=TRUE;
3537-
//printf(" mergeMembers for %s\n",qPrint(name()));
35383527
bool inlineInheritedMembers = Config_getBool(INLINE_INHERITED_MEMB);
35393528
bool extractPrivate = Config_getBool(EXTRACT_PRIVATE);
3529+
3530+
//printf(" mergeMembers for %s mergeVirtualBaseClass=%d\n",qPrint(name()),mergeVirtualBaseClass);
3531+
// the merge the base members with this class' members
35403532
for (const auto &bcd : baseClasses())
35413533
{
35423534
ClassDefMutable *bClass=toClassDefMutable(bcd.classDef);
35433535
if (bClass)
35443536
{
3545-
// merge the members in the base class of this inheritance branch first
3546-
bClass->mergeMembers();
3547-
if (bClass->getLanguage()==SrcLangExt::Python) continue; // python does not have member overloading, see issue 8480
3548-
35493537
const MemberNameInfoLinkedMap &srcMnd = bClass->memberNameInfoLinkedMap();
35503538
MemberNameInfoLinkedMap &dstMnd = m_impl->allMemberNameInfoLinkedMap;
35513539

35523540
for (auto &srcMni : srcMnd)
35533541
{
3554-
//printf(" Base member name %s\n",srcMni->memberName());
35553542
MemberNameInfo *dstMni;
35563543
if ((dstMni=dstMnd.find(srcMni->memberName())))
35573544
// a member with that name is already in the class.
35583545
// the member may hide or reimplement the one in the sub class
35593546
// or there may be another path to the base class that is already
35603547
// visited via another branch in the class hierarchy.
35613548
{
3549+
//printf(" %s hides member name %s\n",qPrint(bClass->name()),qPrint(srcMni->memberName()));
35623550
for (auto &srcMi : *srcMni)
35633551
{
35643552
MemberDef *srcMd = srcMi->memberDef();
@@ -3583,19 +3571,19 @@ void ClassDefImpl::mergeMembers()
35833571
dstMd->getOuterScope(),dstMd->getFileDef(),&dstAl,
35843572
TRUE,getLanguage()
35853573
);
3586-
//printf(" Yes, matching (%s<->%s): %d\n",
3574+
//printf(" Yes, matching (%s<->%s): %d\n",
35873575
// qPrint(argListToString(srcMd->argumentList())),
35883576
// qPrint(argListToString(dstMd->argumentList())),
35893577
// found);
35903578
hidden = hidden || !found;
35913579
}
35923580
else // member is in a non base class => multiple inheritance
3593-
// using the same base class.
3581+
// using the same base class.
35943582
{
3595-
//printf("$$ Existing member %s %s add scope %s\n",
3596-
// qPrint(dstMi->ambiguityResolutionScope),
3583+
//printf(" $$ Existing member %s %s add scope %s\n",
3584+
// qPrint(dstMi->ambiguityResolutionScope()),
35973585
// qPrint(dstMd->name()),
3598-
// qPrint(dstMi->scopePath.left(dstMi->scopePath.find("::")+2));
3586+
// qPrint(dstMi->scopePath().left(dstMi->scopePath().find("::")+2)));
35993587

36003588
QCString scope=dstMi->scopePath().left(dstMi->scopePath().find(sep)+sepLen);
36013589
if (scope!=dstMi->ambiguityResolutionScope().left(scope.length()))
@@ -3610,7 +3598,7 @@ void ClassDefImpl::mergeMembers()
36103598
// do not add if base class is virtual or
36113599
// if scope paths are equal or
36123600
// if base class is an interface (and thus implicitly virtual).
3613-
//printf("same member found srcMi->virt=%d dstMi->virt=%d\n",srcMi->virt,dstMi->virt);
3601+
//printf(" same member found srcMi->virt=%d dstMi->virt=%d\n",srcMi->virt(),dstMi->virt());
36143602
if ((srcMi->virt()!=Specifier::Normal && dstMi->virt()!=Specifier::Normal) ||
36153603
bClass->name()+sep+srcMi->scopePath() == dstMi->scopePath() ||
36163604
dstMd->getClassDef()->compoundType()==Interface
@@ -3619,12 +3607,12 @@ void ClassDefImpl::mergeMembers()
36193607
found=TRUE;
36203608
}
36213609
else // member can be reached via multiple paths in the
3622-
// inheritance tree
3610+
// inheritance tree
36233611
{
3624-
//printf("$$ Existing member %s %s add scope %s\n",
3625-
// qPrint(dstMi->ambiguityResolutionScope),
3612+
//printf(" $$ Existing member %s %s add scope %s\n",
3613+
// qPrint(dstMi->ambiguityResolutionScope()),
36263614
// qPrint(dstMd->name()),
3627-
// qPrint(dstMi->scopePath.left(dstMi->scopePath.find("::")+2));
3615+
// qPrint(dstMi->scopePath().left(dstMi->scopePath().find("::")+2)));
36283616

36293617
QCString scope=dstMi->scopePath().left(dstMi->scopePath().find(sep)+sepLen);
36303618
if (scope!=dstMi->ambiguityResolutionScope().left(scope.length()))
@@ -3636,8 +3624,9 @@ void ClassDefImpl::mergeMembers()
36363624
}
36373625
if (found) break;
36383626
}
3639-
//printf("member %s::%s hidden %d ambiguous %d srcMi->ambigClass=%p\n",
3640-
// qPrint(srcCd->name()),qPrint(srcMd->name()),hidden,ambiguous,srcMi->ambigClass);
3627+
//printf(" member %s::%s hidden %d ambiguous %d srcMi->ambigClass=%p found=%d\n",
3628+
// qPrint(srcCd->name()),qPrint(srcMd->name()),hidden,ambiguous,
3629+
// (void*)srcMi->ambigClass(),found);
36413630

36423631
// TODO: fix the case where a member is hidden by inheritance
36433632
// of a member with the same name but with another prototype,
@@ -3646,7 +3635,7 @@ void ClassDefImpl::mergeMembers()
36463635
// it seems that the member is not reachable by prefixing a
36473636
// scope name either (according to my compiler). Currently,
36483637
// this case is shown anyway.
3649-
if (!found && srcMd->protection()!=Protection::Private && !srcMd->isFriend())
3638+
if (!found && srcMd->protection()!=Protection::Private && !srcMd->isFriend() && srcMi->virtualBaseClass()==mergeVirtualBaseClass)
36503639
{
36513640
Protection prot = srcMd->protection();
36523641
if (bcd.prot==Protection::Protected && prot==Protection::Public)
@@ -3662,15 +3651,16 @@ void ClassDefImpl::mergeMembers()
36623651
{
36633652
if (!isStandardFunc(srcMd))
36643653
{
3665-
//printf(" insertMember '%s'\n",qPrint(srcMd->name()));
3654+
//printf(" %s::insertMember(%s)\n",qPrint(name()),qPrint(srcMd->name()));
36663655
internalInsertMember(srcMd,prot,FALSE);
36673656
}
36683657
}
36693658

36703659
Specifier virt=srcMi->virt();
36713660
if (virt==Specifier::Normal && bcd.virt!=Specifier::Normal) virt=bcd.virt;
3661+
bool virtualBaseClass = bcd.virt!=Specifier::Normal;
36723662

3673-
std::unique_ptr<MemberInfo> newMi = std::make_unique<MemberInfo>(srcMd,prot,virt,TRUE);
3663+
std::unique_ptr<MemberInfo> newMi = std::make_unique<MemberInfo>(srcMd,prot,virt,TRUE,virtualBaseClass);
36743664
newMi->setScopePath(bClass->name()+sep+srcMi->scopePath());
36753665
if (ambiguous)
36763666
{
@@ -3704,14 +3694,15 @@ void ClassDefImpl::mergeMembers()
37043694
}
37053695
else // base class has a member that is not in the sub class => copy
37063696
{
3697+
//printf(" %s adds member name %s\n",qPrint(bClass->name()),qPrint(srcMni->memberName()));
37073698
// create a deep copy of the list (only the MemberInfo's will be
37083699
// copied, not the actual MemberDef's)
37093700
MemberNameInfo *newMni = dstMnd.add(srcMni->memberName());
37103701

37113702
// copy the member(s) from the base to the sub class
37123703
for (auto &mi : *srcMni)
37133704
{
3714-
if (!mi->memberDef()->isFriend()) // don't inherit friends
3705+
if (mi->virtualBaseClass()==mergeVirtualBaseClass && !mi->memberDef()->isFriend()) // don't inherit friends
37153706
{
37163707
Protection prot = mi->prot();
37173708
if (bcd.prot==Protection::Protected)
@@ -3722,25 +3713,29 @@ void ClassDefImpl::mergeMembers()
37223713
{
37233714
prot=Protection::Private;
37243715
}
3725-
//printf("%s::%s: prot=%d bcd.prot=%d result=%d\n",
3726-
// qPrint(name()),qPrint(mi->memberDef->name()),mi->prot,
3727-
// bcd.prot,prot);
3716+
Specifier virt=mi->virt();
3717+
bool virtualBaseClass = bcd.virt!=Specifier::Normal || mi->virtualBaseClass();
3718+
if (virt==Specifier::Normal && bcd.virt!=Specifier::Normal) virt=bcd.virt;
3719+
//printf(" %s::%s: [mi.prot=%d, bcd.prot=%d => prot=%d], [mi.virt=%d, bcd.virt=%d => virt=%d] virtualBase=%d\n",
3720+
// qPrint(name()),qPrint(mi->memberDef()->name()),
3721+
// mi->prot(),bcd.prot,prot,
3722+
// mi->virt(),bcd.virt,virt,
3723+
// virtualBaseClass
3724+
// );
37283725

37293726
if (prot!=Protection::Private || extractPrivate)
37303727
{
3731-
Specifier virt=mi->virt();
3732-
if (virt==Specifier::Normal && bcd.virt!=Specifier::Normal) virt=bcd.virt;
37333728

37343729
if (inlineInheritedMembers)
37353730
{
37363731
if (!isStandardFunc(mi->memberDef()))
37373732
{
3738-
//printf(" insertMember '%s'\n",qPrint(mi->memberDef->name()));
3733+
//printf(" %s::insertMember '%s'\n",qPrint(name()),qPrint(mi->memberDef()->name()));
37393734
internalInsertMember(mi->memberDef(),prot,FALSE);
37403735
}
37413736
}
37423737
//printf("Adding!\n");
3743-
std::unique_ptr<MemberInfo> newMi = std::make_unique<MemberInfo>(mi->memberDef(),prot,virt,TRUE);
3738+
std::unique_ptr<MemberInfo> newMi = std::make_unique<MemberInfo>(mi->memberDef(),prot,virt,TRUE,virtualBaseClass);
37443739
newMi->setScopePath(bClass->name()+sep+mi->scopePath());
37453740
newMi->setAmbigClass(mi->ambigClass());
37463741
newMi->setAmbiguityResolutionScope(mi->ambiguityResolutionScope());
@@ -3752,7 +3747,41 @@ void ClassDefImpl::mergeMembers()
37523747
}
37533748
}
37543749
}
3755-
//printf(" end mergeMembers\n");
3750+
}
3751+
3752+
/*!
3753+
* recursively merges the 'all members' lists of a class base
3754+
* with that of this class. Must only be called for classes without
3755+
* subclasses!
3756+
*/
3757+
void ClassDefImpl::mergeMembers()
3758+
{
3759+
if (m_impl->membersMerged) return;
3760+
if (getLanguage()==SrcLangExt::Python) return; // python does not have member overloading, see issue 8480
3761+
3762+
//printf("> %s::mergeMembers()\n",qPrint(name()));
3763+
3764+
m_impl->membersMerged=TRUE;
3765+
3766+
// first merge the members of the base class recursively
3767+
for (const auto &bcd : baseClasses())
3768+
{
3769+
ClassDefMutable *bClass=toClassDefMutable(bcd.classDef);
3770+
if (bClass)
3771+
{
3772+
// merge the members in the base class of this inheritance branch first
3773+
bClass->mergeMembers();
3774+
}
3775+
}
3776+
3777+
// first merge the member that are not inherited via a virtual base class
3778+
// (as this can end up reimplemented via multiple paths, see #10717 for examples)
3779+
mergeMembersFromBaseClasses(false);
3780+
// then process the member that are inherited via a virtual base class to add the
3781+
// ones that are not reimplemented via any path
3782+
mergeMembersFromBaseClasses(true);
3783+
3784+
//printf("< %s::mergeMembers()\n",qPrint(name()));
37563785
}
37573786

37583787
//----------------------------------------------------------------------------
@@ -3839,7 +3868,7 @@ void ClassDefImpl::mergeCategory(ClassDef *cat)
38393868
//printf("Copying member %s\n",qPrint(mi->memberDef->name()));
38403869
mmd->moveTo(this);
38413870

3842-
auto newMi=std::make_unique<MemberInfo>(newMd.get(),prot,mi->virt(),mi->inherited());
3871+
auto newMi=std::make_unique<MemberInfo>(newMd.get(),prot,mi->virt(),mi->inherited(),mi->virtualBaseClass());
38433872
newMi->setScopePath(mi->scopePath());
38443873
newMi->setAmbigClass(mi->ambigClass());
38453874
newMi->setAmbiguityResolutionScope(mi->ambiguityResolutionScope());
@@ -4042,7 +4071,7 @@ ClassDef *ClassDefImpl::insertTemplateInstance(const QCString &fileName,
40424071
}
40434072
if (templateClass==nullptr)
40444073
{
4045-
QCString tcname = removeRedundantWhiteSpace(localName()+templSpec);
4074+
QCString tcname = removeRedundantWhiteSpace(name()+templSpec);
40464075
AUTO_TRACE("New template instance class name='{}' templSpec='{}' inside '{}' hidden={}",
40474076
name(),templSpec,name(),isHidden());
40484077

src/groupdef.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -395,7 +395,7 @@ bool GroupDefImpl::insertMember(MemberDef *md,bool docOnly)
395395
return FALSE; // member is the same as one that is already added
396396
}
397397
}
398-
mni->push_back(std::make_unique<MemberInfo>(md,md->protection(),md->virtualness(),FALSE));
398+
mni->push_back(std::make_unique<MemberInfo>(md,md->protection(),md->virtualness(),false,false));
399399
//printf("Added member!\n");
400400
m_allMemberList.push_back(md);
401401
switch(md->memberType())

src/membername.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ class MemberNameLinkedMap : public LinkedMap<MemberName>
6666
class MemberInfo
6767
{
6868
public:
69-
MemberInfo(MemberDef *md,Protection p,Specifier v,bool inh) :
70-
m_memberDef(md), m_prot(p), m_virt(v), m_inherited(inh) {}
69+
MemberInfo(MemberDef *md,Protection p,Specifier v,bool inh,bool vbc) :
70+
m_memberDef(md), m_prot(p), m_virt(v), m_inherited(inh), m_virtBaseClass(vbc) {}
7171
~MemberInfo() = default;
7272

7373
// getters
@@ -79,6 +79,7 @@ class MemberInfo
7979
QCString scopePath() const { return m_scopePath; }
8080
QCString ambiguityResolutionScope() const { return m_ambiguityResolutionScope; }
8181
const ClassDef *ambigClass() const { return m_ambigClass; }
82+
bool virtualBaseClass() const { return m_virtBaseClass; }
8283

8384
// setters
8485
void setAmbiguityResolutionScope(const QCString &s) { m_ambiguityResolutionScope = s; }
@@ -93,6 +94,7 @@ class MemberInfo
9394
QCString m_scopePath;
9495
QCString m_ambiguityResolutionScope;
9596
const ClassDef *m_ambigClass = nullptr;
97+
bool m_virtBaseClass;
9698
};
9799

98100
class MemberNameInfo

src/scanner.l

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6379,7 +6379,7 @@ NONLopt [^\n]*
63796379
yyextra->current->args += yytext;
63806380
}
63816381
}
6382-
<Bases>("::")?{BN}*({ID}{BN}*"::"{BN}*)*{ID} {
6382+
<Bases>("::")?{BN}*({ID}{BN}*"::"{BN}*)*{ID}("...")? {
63836383
lineCount(yyscanner);
63846384
QCString baseScope = yytext;
63856385
if (yyextra->insideCS && baseScope.stripWhiteSpace()=="where")

src/util.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4676,7 +4676,7 @@ QCString substituteTemplateArgumentsInString(
46764676
if (formalArgs.empty()) return nm;
46774677
QCString result;
46784678

4679-
static const reg::Ex re(R"(\a[\w:]*)");
4679+
static const reg::Ex re(R"(\a[\w:.]*)");
46804680
std::string name = nm.str();
46814681
reg::Iterator it(name,re);
46824682
reg::Iterator end;
@@ -4714,11 +4714,21 @@ QCString substituteTemplateArgumentsInString(
47144714
formArg.name = formArg.type.mid(6);
47154715
formArg.type = "class";
47164716
}
4717-
if (formArg.type.startsWith("typename ") && formArg.name.isEmpty())
4717+
else if (formArg.type.startsWith("typename ") && formArg.name.isEmpty())
47184718
{
47194719
formArg.name = formArg.type.mid(9);
47204720
formArg.type = "typename";
47214721
}
4722+
else if (formArg.type.startsWith("class...")) // match 'class... name' to 'name...'
4723+
{
4724+
formArg.name += "...";
4725+
formArg.type = formArg.type.left(5)+formArg.type.mid(8);
4726+
}
4727+
else if (formArg.type.startsWith("typename...")) // match 'typename... name' to 'name...'
4728+
{
4729+
formArg.name += "...";
4730+
formArg.type = formArg.type.left(8)+formArg.type.mid(11);
4731+
}
47224732
//printf("n=%s formArg->type='%s' formArg->name='%s' formArg->defval='%s' actArg->type='%s' actArg->name='%s' \n",
47234733
// qPrint(n),qPrint(formArg.type),qPrint(formArg.name),qPrint(formArg.defval),qPrint(actArg.type),qPrint(actArg.name));
47244734
if (formArg.type=="class" || formArg.type=="typename" || formArg.type.startsWith("template"))

0 commit comments

Comments
 (0)