Skip to content

Commit c9bcf5e

Browse files
committed
TopoSorter: Fix sorting of container template parameters
For std::vector and std::list, template parameters are not required to be defined before they can be used. Delay sorting them until the end. Also fix a CodeGen bug where we were defining typedefs in the middle of the forward declarations. They only need to be defined when other types are defined.
1 parent 5560624 commit c9bcf5e

File tree

6 files changed

+103
-16
lines changed

6 files changed

+103
-16
lines changed

oi/CodeGen.cpp

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -150,18 +150,12 @@ void genDeclsEnum(const Enum& e, std::string& code) {
150150
code += ";\n";
151151
}
152152

153-
void genDeclsTypedef(const Typedef& td, std::string& code) {
154-
code += "using " + td.name() + " = " + td.underlyingType()->name() + ";\n";
155-
}
156-
157153
void genDecls(const TypeGraph& typeGraph, std::string& code) {
158154
for (const Type& t : typeGraph.finalTypes) {
159155
if (const auto* c = dynamic_cast<const Class*>(&t)) {
160156
genDeclsClass(*c, code);
161157
} else if (const auto* e = dynamic_cast<const Enum*>(&t)) {
162158
genDeclsEnum(*e, code);
163-
} else if (const auto* td = dynamic_cast<const Typedef*>(&t)) {
164-
genDeclsTypedef(*td, code);
165159
}
166160
}
167161
}

oi/type_graph/TopoSorter.cpp

Lines changed: 55 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -55,29 +55,57 @@ void TopoSorter::visit(Type& type) {
5555
}
5656

5757
void TopoSorter::visit(Class& c) {
58-
for (const auto& param : c.templateParams) {
59-
visit(param.type);
60-
}
6158
for (const auto& parent : c.parents) {
6259
visit(*parent.type);
6360
}
6461
for (const auto& mem : c.members) {
6562
visit(*mem.type);
6663
}
64+
for (const auto& param : c.templateParams) {
65+
visit(param.type);
66+
}
6767
sortedTypes_.push_back(c);
6868

69-
// Same as pointers, child do not create a dependency so are delayed until the
70-
// end
69+
// Same as pointers, children do not create a dependency so are delayed until
70+
// the end.
7171
for (const auto& child : c.children) {
72-
typesToSort_.push(child);
72+
visitAfter(child);
73+
}
74+
}
75+
76+
namespace {
77+
/*
78+
* C++17 allows the std::vector, std::list and std::forward_list containers to
79+
* be instantiated with incomplete types.
80+
*
81+
* Other containers are not required to do this, but might still have this
82+
* behaviour.
83+
*/
84+
bool containerAllowsIncompleteParams(const Container& c) {
85+
switch (c.containerInfo_.ctype) {
86+
case SEQ_TYPE:
87+
case LIST_TYPE:
88+
// Also std::forward_list, if we ever support that
89+
// Would be good to have this as an option in the TOML files
90+
return true;
91+
default:
92+
return false;
7393
}
7494
}
95+
} // namespace
7596

7697
void TopoSorter::visit(Container& c) {
77-
for (const auto& param : c.templateParams) {
78-
visit(param.type);
98+
if (!containerAllowsIncompleteParams(c)) {
99+
for (const auto& param : c.templateParams) {
100+
visit(param.type);
101+
}
79102
}
80103
sortedTypes_.push_back(c);
104+
if (containerAllowsIncompleteParams(c)) {
105+
for (const auto& param : c.templateParams) {
106+
visitAfter(param.type);
107+
}
108+
}
81109
}
82110

83111
void TopoSorter::visit(Enum& e) {
@@ -92,7 +120,25 @@ void TopoSorter::visit(Typedef& td) {
92120
void TopoSorter::visit(Pointer& p) {
93121
// Pointers do not create a dependency, but we do still care about the types
94122
// they point to, so delay them until the end.
95-
typesToSort_.push(*p.pointeeType());
123+
visitAfter(*p.pointeeType());
124+
}
125+
126+
/*
127+
* A type graph may contain cycles, so we need to slightly tweak the standard
128+
* topological sorting algorithm. Cycles can only be introduced by certain
129+
* edges, e.g. a pointer's underlying type. However, these edges do not
130+
* introduce dependencies as these types can be forward declared in a C++
131+
* program. This means we can delay processing them until after all of the true
132+
* dependencies have been sorted.
133+
*/
134+
void TopoSorter::visitAfter(Type& type) {
135+
typesToSort_.push(type);
136+
}
137+
138+
void TopoSorter::visitAfter(Type* type) {
139+
if (type) {
140+
visitAfter(*type);
141+
}
96142
}
97143

98144
} // namespace type_graph

oi/type_graph/TopoSorter.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ class TopoSorter : public RecursiveVisitor {
5151
std::unordered_set<Type*> visited_;
5252
std::vector<std::reference_wrapper<Type>> sortedTypes_;
5353
std::queue<std::reference_wrapper<Type>> typesToSort_;
54+
55+
void visitAfter(Type& type);
56+
void visitAfter(Type* type);
5457
};
5558

5659
} // namespace type_graph

test/test_topo_sorter.cpp

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,11 +153,41 @@ MyChild
153153
}
154154

155155
TEST(TopoSorterTest, Containers) {
156+
auto myparam1 = Class{Class::Kind::Struct, "MyParam1", 13};
157+
auto myparam2 = Class{Class::Kind::Struct, "MyParam2", 13};
158+
auto mycontainer = getMap();
159+
mycontainer.templateParams.push_back((&myparam1));
160+
mycontainer.templateParams.push_back((&myparam2));
161+
162+
test({mycontainer}, R"(
163+
MyParam1
164+
MyParam2
165+
std::map
166+
)");
167+
}
168+
169+
TEST(TopoSorterTest, ContainersVector) {
170+
// std::vector allows forward declared template parameters
156171
auto myparam = Class{Class::Kind::Struct, "MyParam", 13};
157172
auto mycontainer = getVector();
158173
mycontainer.templateParams.push_back((&myparam));
159174

160-
test({mycontainer}, "MyParam\nstd::vector");
175+
test({mycontainer}, R"(
176+
std::vector
177+
MyParam
178+
)");
179+
}
180+
181+
TEST(TopoSorterTest, ContainersList) {
182+
// std::list allows forward declared template parameters
183+
auto myparam = Class{Class::Kind::Struct, "MyParam", 13};
184+
auto mycontainer = getList();
185+
mycontainer.templateParams.push_back((&myparam));
186+
187+
test({mycontainer}, R"(
188+
std::list
189+
MyParam
190+
)");
161191
}
162192

163193
TEST(TopoSorterTest, Arrays) {

test/type_graph_utils.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,15 @@ Container getVector() {
5959
info.stubTemplateParams = {1};
6060
return Container{info, 24};
6161
}
62+
63+
Container getMap() {
64+
static ContainerInfo info{"std::map", STD_MAP_TYPE, "map"};
65+
info.stubTemplateParams = {2, 3};
66+
return Container{info, 48};
67+
}
68+
69+
Container getList() {
70+
static ContainerInfo info{"std::list", LIST_TYPE, "list"};
71+
info.stubTemplateParams = {1};
72+
return Container{info, 24};
73+
}

test/type_graph_utils.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,5 @@ void test(type_graph::Pass pass,
2424
std::string_view expectedAfter);
2525

2626
type_graph::Container getVector();
27+
type_graph::Container getMap();
28+
type_graph::Container getList();

0 commit comments

Comments
 (0)