Skip to content

Commit 4a80818

Browse files
authored
Merge pull request #656 from glu-lang/sema-splitting
[Sema] Feature: CS Splitting
2 parents a459f45 + fd021cc commit 4a80818

6 files changed

Lines changed: 191 additions & 24 deletions

File tree

include/Sema/Constraint.hpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,15 @@ class Constraint {
455455
/// @return True if the constraint is disabled, false otherwise.
456456
bool isDisabled() const { return _isDisabled; }
457457

458+
/// @brief Disable this constraint.
459+
void disable() { _isDisabled = 1; }
460+
/// @brief Enable this constraint.
461+
void enable() { _isDisabled = 0; }
462+
463+
/// @brief Sets the enabled state of this constraint.
464+
/// @param enabled True to enable the constraint, false to disable it.
465+
void setEnabled(bool enabled) { _isDisabled = !enabled; }
466+
458467
/// @brief Print this constraint to the output stream.
459468
void print() const;
460469
};

include/Sema/ConstraintSystem.hpp

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "ScopeTable.hpp"
77

88
#include <llvm/ADT/DenseMap.h>
9+
#include <llvm/ADT/DenseSet.h>
910
#include <llvm/ADT/ilist.h>
1011

1112
namespace glu::sema {
@@ -58,6 +59,10 @@ struct SystemState {
5859
/// @return A deep copy of the current state.
5960
SystemState clone() const { return *this; }
6061

62+
/// @brief Merges this state into another, combining bindings and choices.
63+
/// @param other The target state to merge into.
64+
void mergeInto(SystemState &other) const;
65+
6166
/// @brief Calculates the score of the current state based on implicit
6267
/// conversions.
6368
/// @return The score representing the number of implicit conversions.
@@ -214,8 +219,16 @@ class ConstraintSystem {
214219
/// extracted.
215220
void mapImplicitConversions(Solution *solution);
216221

217-
/// @brief Solves all constraints and applies type mappings to the specified
218-
/// expressions.
222+
/// @brief Solves all enabled constraints within this local constraint
223+
/// system and returns the solution result through the provided parameter.
224+
/// This method is called within solveConstraints, after simplification and
225+
/// splitting of the constraint system, and before mapping types back to the
226+
/// AST.
227+
/// @param result The solution result to populate with found solutions.
228+
/// @return True if a solution was found, false otherwise.
229+
bool solveLocalConstraints(SolutionResult &result);
230+
231+
/// @brief Solves all constraints and applies mappings.
219232
///
220233
/// This method combines constraint solving with type mapping for
221234
/// expressions. For module expressions (part of the AST tree), type
@@ -424,6 +437,11 @@ void printConstraints(
424437
ConstraintSystem &system, llvm::raw_ostream &os = llvm::outs()
425438
);
426439

440+
void collectTypeVariables(
441+
Constraint *constraint,
442+
llvm::DenseSet<glu::types::TypeVariableTy *> &typeVars
443+
);
444+
427445
} // namespace glu::sema
428446

429447
#endif // GLU_SEMA_CONSTRAINT_SYSTEM_HPP

lib/Sema/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ target_sources(Sema
2525
ConstraintSystem/OccursCheckVisitor.cpp
2626
ConstraintSystem/Solver.cpp
2727
ConstraintSystem/SubstitutionMapper.cpp
28+
ConstraintSystem/TypeVariableCollector.cpp
2829
ConstraintSystem/TypeVariableTyMapper.cpp
2930
ConstraintSystem/UnificationVisitor.cpp
3031
ModuleWalker.cpp

lib/Sema/ConstraintSystem/ConstraintSystem.cpp

Lines changed: 70 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -145,20 +145,16 @@ void ConstraintSystem::mapImplicitConversions(Solution *solution)
145145
}
146146
}
147147

148-
bool ConstraintSystem::solveConstraints()
148+
bool ConstraintSystem::solveLocalConstraints(SolutionResult &result)
149149
{
150150
/// The initial system state used to begin constraint solving.
151151
std::vector<SystemState> worklist;
152152
worklist.emplace_back(_context); // Start from an empty state
153153

154-
SolutionResult result; // Local solution result
155-
156154
while (!worklist.empty()) {
157155
SystemState current = std::move(worklist.back());
158156
worklist.pop_back();
159157

160-
bool failed = false;
161-
162158
/// Apply non-defaultable constraints first
163159
for (Constraint *constraint : _constraints) {
164160
// Skip disabled constraints
@@ -176,15 +172,11 @@ bool ConstraintSystem::solveConstraints()
176172
ConstraintResult result = apply(constraint, current, worklist);
177173
markConstraint(result, constraint);
178174
if (result == ConstraintResult::Failed) {
179-
failed = true;
180-
break;
175+
goto failed;
181176
}
182177
// Continue if Satisfied or Applied
183178
}
184179

185-
if (failed)
186-
continue;
187-
188180
for (Constraint *constraint : _constraints) {
189181
if (constraint->isDisabled())
190182
continue;
@@ -194,8 +186,7 @@ bool ConstraintSystem::solveConstraints()
194186
ConstraintResult result = apply(constraint, current, worklist);
195187
markConstraint(result, constraint);
196188
if (result == ConstraintResult::Failed) {
197-
failed = true;
198-
break;
189+
goto failed;
199190
}
200191
}
201192

@@ -214,8 +205,7 @@ bool ConstraintSystem::solveConstraints()
214205
ConstraintResult result = apply(constraint, current, worklist);
215206
markConstraint(result, constraint);
216207
if (result == ConstraintResult::Failed) {
217-
failed = true;
218-
break;
208+
goto failed;
219209
}
220210
// Continue if Satisfied or Applied
221211
}
@@ -235,17 +225,16 @@ bool ConstraintSystem::solveConstraints()
235225
ConstraintResult result = apply(constraint, current, worklist);
236226
markConstraint(result, constraint);
237227
if (result == ConstraintResult::Failed) {
238-
failed = true;
239-
break;
228+
goto failed;
240229
}
241230
// Continue if Satisfied or Applied
242231
}
243232

244-
if (failed)
245-
continue;
246-
247233
/// All constraints are satisfied -- record the solution.
248234
result.tryAddSolution(current);
235+
236+
failed:
237+
continue;
249238
}
250239

251240
if (result.isAmbiguous()) {
@@ -259,9 +248,68 @@ bool ConstraintSystem::solveConstraints()
259248
reportNoSolutionError();
260249
return false;
261250
}
262-
mapTypeVariables(solution);
263-
mapOverloadChoices(solution);
264-
mapImplicitConversions(solution);
251+
return true;
252+
}
253+
254+
bool ConstraintSystem::solveConstraints()
255+
{
256+
// color the constraints based on which type variables they contain
257+
// map of color => set of used type variables
258+
std::vector<llvm::DenseSet<glu::types::TypeVariableTy *>> colors;
259+
// map of color => set of constraints
260+
std::vector<llvm::DenseSet<Constraint *>> colorConstraints;
261+
for (auto *constraint : _constraints) {
262+
llvm::DenseSet<glu::types::TypeVariableTy *> typeVars;
263+
collectTypeVariables(constraint, typeVars);
264+
colors.push_back(std::move(typeVars));
265+
colorConstraints.push_back({ constraint });
266+
}
267+
// merge colors that share type variables
268+
bool changed;
269+
do {
270+
changed = false;
271+
for (std::size_t i = 0; i < colors.size(); ++i) {
272+
for (std::size_t j = i + 1; j < colors.size(); ++j) {
273+
llvm::DenseSet<glu::types::TypeVariableTy *> intersection;
274+
for (auto *typeVar : colors[j]) {
275+
if (colors[i].count(typeVar)) {
276+
intersection.insert(typeVar);
277+
}
278+
}
279+
if (!intersection.empty()) {
280+
// merge j into i
281+
for (auto *typeVar : colors[j]) {
282+
colors[i].insert(typeVar);
283+
}
284+
for (auto *constraint : colorConstraints[j]) {
285+
colorConstraints[i].insert(constraint);
286+
}
287+
changed = true;
288+
// Erase the merged color
289+
colors.erase(colors.begin() + j);
290+
colorConstraints.erase(colorConstraints.begin() + j);
291+
--j;
292+
}
293+
}
294+
}
295+
} while (changed);
296+
// solve each color separately
297+
SystemState finalSolution(_context);
298+
for (std::size_t i = 0; i < colors.size(); ++i) {
299+
// Disable all constraints not in this color
300+
for (auto *constraint : _constraints) {
301+
constraint->setEnabled(colorConstraints[i].count(constraint));
302+
}
303+
SolutionResult result;
304+
if (!solveLocalConstraints(result)) {
305+
return false;
306+
}
307+
result.getBestSolution()->mergeInto(finalSolution);
308+
}
309+
310+
mapTypeVariables(&finalSolution);
311+
mapOverloadChoices(&finalSolution);
312+
mapImplicitConversions(&finalSolution);
265313
return true;
266314
}
267315

lib/Sema/ConstraintSystem/Solver.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,4 +148,22 @@ void SolutionResult::tryAddSolution(SystemState const &s)
148148
}
149149
}
150150

151+
void SystemState::mergeInto(SystemState &other) const
152+
{
153+
// Merge type bindings
154+
for (auto const &[var, type] : typeBindings) {
155+
other.typeBindings[var] = type;
156+
}
157+
158+
// Merge overload choices
159+
for (auto const &[expr, decl] : overloadChoices) {
160+
other.overloadChoices[expr] = decl;
161+
}
162+
163+
// Merge implicit conversions
164+
for (auto const &[expr, targetType] : implicitConversions) {
165+
other.implicitConversions[expr] = targetType;
166+
}
167+
}
168+
151169
} // namespace glu::sema
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
#include "AST/Types.hpp"
2+
#include "Constraint.hpp"
3+
4+
#include <llvm/ADT/DenseSet.h>
5+
6+
namespace glu::sema {
7+
8+
class TypeVariableCollector
9+
: public glu::types::TypeVisitor<TypeVariableCollector, void> {
10+
protected:
11+
llvm::DenseSet<glu::types::TypeVariableTy *> &_typeVariables;
12+
13+
public:
14+
TypeVariableCollector(
15+
llvm::DenseSet<glu::types::TypeVariableTy *> &typeVariables
16+
)
17+
: _typeVariables(typeVariables)
18+
{
19+
}
20+
21+
void visitTypeBase([[maybe_unused]] glu::types::TypeBase *type) { }
22+
23+
void visitFunctionTy(glu::types::FunctionTy *type)
24+
{
25+
visit(type->getReturnType());
26+
for (glu::types::TypeBase *paramType : type->getParameters())
27+
visit(paramType);
28+
}
29+
30+
void visitPointerTy(types::PointerTy *type) { visit(type->getPointee()); }
31+
32+
void visitTypeAliasTy(types::TypeAliasTy *type)
33+
{
34+
visit(type->getWrappedType());
35+
}
36+
37+
void visitStaticArrayTy(types::StaticArrayTy *type)
38+
{
39+
visit(type->getDataType());
40+
}
41+
42+
void visitDynamicArrayTy(types::DynamicArrayTy *type)
43+
{
44+
visit(type->getDataType());
45+
}
46+
47+
void visitTypeVariableTy(glu::types::TypeVariableTy *type)
48+
{
49+
_typeVariables.insert(type);
50+
}
51+
};
52+
53+
void collectTypeVariables(
54+
Constraint *constraint,
55+
llvm::DenseSet<glu::types::TypeVariableTy *> &typeVars
56+
)
57+
{
58+
if (constraint->getKind() == ConstraintKind::Disjunction
59+
|| constraint->getKind() == ConstraintKind::Conjunction) {
60+
for (Constraint *nested : constraint->getNestedConstraints()) {
61+
collectTypeVariables(nested, typeVars);
62+
}
63+
return;
64+
}
65+
TypeVariableCollector collector(typeVars);
66+
collector.visit(constraint->getFirstType());
67+
if (!constraint->isTypePropertyConstraint()
68+
&& constraint->getKind() != ConstraintKind::BindOverload
69+
&& constraint->getKind() != ConstraintKind::StructInitialiser)
70+
collector.visit(constraint->getSecondType());
71+
}
72+
73+
} // namespace glu::sema

0 commit comments

Comments
 (0)