Skip to content

Commit 00732b3

Browse files
committed
Update ConstraintSolver_Rules.cs
1 parent 83c160b commit 00732b3

File tree

1 file changed

+81
-57
lines changed

1 file changed

+81
-57
lines changed

src/Draco.Compiler/Internal/Solver/ConstraintSolver_Rules.cs

Lines changed: 81 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,24 @@ internal sealed partial class ConstraintSolver
2323
/// <returns>True, if a change was made, false otherwise.</returns>
2424
private bool ApplyRulesOnce(DiagnosticBag diagnostics)
2525
{
26+
if (Same()) return true;
27+
if (Assignable()) return true;
28+
if (CommonAncestor()) return true;
29+
if (Member()) return true;
30+
if (Indexer()) return true;
31+
if (Callable()) return true;
32+
if (OverloadRefine()) return true;
33+
if (Overload()) return true;
34+
if (MergeAssignables()) return true;
35+
if (SingleAssignable()) return true;
36+
if (CommonAncestorSingleNonTypeVar()) return true;
37+
if (CommonAncestorIsGround()) return true;
38+
return false;
39+
2640
// Trivial same-type constraint, unify all
27-
if (this.constraintStore.TryRemove<Same>(out var same))
41+
bool Same()
2842
{
43+
if (!this.constraintStore.TryRemove<Same>(out var same)) return false;
2944
for (var i = 1; i < same.Types.Length; ++i)
3045
{
3146
if (Unify(same.Types[0], same.Types[i])) continue;
@@ -38,9 +53,13 @@ private bool ApplyRulesOnce(DiagnosticBag diagnostics)
3853
}
3954

4055
// Assignable can be resolved directly, if both types are ground-types
41-
if (this.constraintStore.TryRemove<Assignable>(out var assignable, assignable => assignable.TargetType.IsGroundType
42-
&& assignable.AssignedType.IsGroundType))
56+
bool Assignable()
4357
{
58+
if (!this.constraintStore.TryRemove<Assignable>(out var assignable, assignable => assignable.TargetType.IsGroundType
59+
&& assignable.AssignedType.IsGroundType))
60+
{
61+
return false;
62+
}
4463
var targetType = assignable.TargetType;
4564
var assignedType = assignable.AssignedType;
4665
if (!SymbolEqualityComparer.Default.IsBaseOf(targetType, assignedType))
@@ -53,34 +72,35 @@ private bool ApplyRulesOnce(DiagnosticBag diagnostics)
5372
}
5473

5574
// If all types are ground-types, common-type constraints are trivial
56-
if (this.constraintStore.TryRemove<CommonAncestor>(out var common, common => common.AlternativeTypes.All(t => t.IsGroundType)))
75+
bool CommonAncestor()
5776
{
77+
if (!this.constraintStore.TryRemove<CommonAncestor>(out var common, common => common.AlternativeTypes.All(t => t.IsGroundType))) return false;
5878
foreach (var type in common.AlternativeTypes)
5979
{
6080
if (!common.AlternativeTypes.All(t => SymbolEqualityComparer.Default.IsBaseOf(type, t))) continue;
6181
// Found a good common type
6282
this.Assignable(common.CommonType, type, ConstraintLocator.Constraint(common));
63-
goto common_end;
83+
return true;
6484
}
6585
// No common type found
6686
common.ReportDiagnostic(diagnostics, builder => builder
6787
.WithFormatArgs(string.Join(", ", common.AlternativeTypes)));
6888
// Stop cascading uninferred type
6989
UnifyWithError(common.CommonType);
70-
common_end:
7190
return true;
7291
}
7392

74-
// Member constraints are trivial, if the receiver is not a type-varriable
75-
if (this.constraintStore.TryRemove<Member>(out var member, member => !member.Receiver.Substitution.IsTypeVariable))
93+
// Member constraints are trivial, if the receiver is not a type-variable
94+
bool Member()
7695
{
96+
if (!this.constraintStore.TryRemove<Member>(out var member, member => !member.Receiver.Substitution.IsTypeVariable)) return false;
7797
var accessed = member.Receiver.Substitution;
7898
// Don't propagate type errors
7999
if (accessed.IsError)
80100
{
81101
UnifyWithError(member.MemberType);
82102
member.CompletionSource.SetResult(ErrorMemberSymbol.Instance);
83-
goto member_end;
103+
return true;
84104
}
85105

86106
// Not a type variable, we can look into members
@@ -101,7 +121,7 @@ private bool ApplyRulesOnce(DiagnosticBag diagnostics)
101121
// We still provide a single error symbol
102122
UnifyWithError(member.MemberType);
103123
member.CompletionSource.SetResult(ErrorMemberSymbol.Instance);
104-
goto member_end;
124+
return true;
105125
}
106126
if (membersWithName.Length == 1)
107127
{
@@ -114,7 +134,7 @@ private bool ApplyRulesOnce(DiagnosticBag diagnostics)
114134
// And just provide the member type as is
115135
UnifyAsserted(member.MemberType, memberType);
116136
member.CompletionSource.SetResult(membersWithName[0]);
117-
goto member_end;
137+
return true;
118138
}
119139
// More than one, the member constraint is fine with multiple members but we don't know the member type
120140
{
@@ -125,14 +145,14 @@ private bool ApplyRulesOnce(DiagnosticBag diagnostics)
125145
UnifyWithError(member.MemberType);
126146
var overloadSym = new FunctionGroupSymbol(membersWithName.Cast<FunctionSymbol>().ToImmutableArray());
127147
member.CompletionSource.SetResult(overloadSym);
148+
return true;
128149
}
129-
member_end:
130-
return true;
131150
}
132151

133152
// Indexer constraints are trivial, if the receiver is not a type-variable
134-
if (this.constraintStore.TryRemove<Indexer>(out var indexer, indexer => !indexer.Receiver.Substitution.IsTypeVariable))
153+
bool Indexer()
135154
{
155+
if (!this.constraintStore.TryRemove<Indexer>(out var indexer, indexer => !indexer.Receiver.Substitution.IsTypeVariable)) return false;
136156
var accessed = indexer.Receiver.Substitution;
137157
// Don't propagate type errors
138158
if (accessed.IsError)
@@ -143,7 +163,7 @@ private bool ApplyRulesOnce(DiagnosticBag diagnostics)
143163
? ErrorPropertySymbol.CreateIndexerGet(indexer.Indices.Length)
144164
: ErrorPropertySymbol.CreateIndexerSet(indexer.Indices.Length);
145165
indexer.CompletionSource.SetResult(errorSymbol);
146-
goto indexer_end;
166+
return true;
147167
}
148168

149169
// Not a type variable, we can look into members
@@ -167,7 +187,7 @@ private bool ApplyRulesOnce(DiagnosticBag diagnostics)
167187
? ErrorPropertySymbol.CreateIndexerGet(indexer.Indices.Length)
168188
: ErrorPropertySymbol.CreateIndexerSet(indexer.Indices.Length);
169189
indexer.CompletionSource.SetResult(errorSymbol);
170-
goto indexer_end;
190+
return true;
171191
}
172192

173193
// If there is a single indexer, we check visibility
@@ -208,19 +228,19 @@ private bool ApplyRulesOnce(DiagnosticBag diagnostics)
208228
CompletionSource = indexer.CompletionSource,
209229
});
210230
}
211-
indexer_end:
212231
return true;
213232
}
214233

215234
// A callable can be resolved directly, if the called type is not a type-variable
216-
if (this.constraintStore.TryRemove<Callable>(out var callable, callable => !callable.CalledType.Substitution.IsTypeVariable))
235+
bool Callable()
217236
{
237+
if (!this.constraintStore.TryRemove<Callable>(out var callable, callable => !callable.CalledType.Substitution.IsTypeVariable)) return false;
218238
var called = callable.CalledType.Substitution;
219239
if (called.IsError)
220240
{
221241
// Don't propagate errors
222242
UnifyWithError(callable.ReturnType);
223-
goto callable_end;
243+
return true;
224244
}
225245

226246
// We can now check if it's a function
@@ -234,7 +254,7 @@ private bool ApplyRulesOnce(DiagnosticBag diagnostics)
234254
callable.ReportDiagnostic(diagnostics, diag => diag
235255
.WithTemplate(TypeCheckingErrors.CallNonFunction)
236256
.WithFormatArgs(called));
237-
goto callable_end;
257+
return true;
238258
}
239259

240260
// It's a function
@@ -253,7 +273,7 @@ private bool ApplyRulesOnce(DiagnosticBag diagnostics)
253273
.WithFormatArgs(
254274
functionType,
255275
this.MakeMismatchedFunctionType(callable.Arguments, functionType.ReturnType)));
256-
goto callable_end;
276+
return true;
257277
}
258278

259279
// Start scoring args
@@ -268,27 +288,25 @@ private bool ApplyRulesOnce(DiagnosticBag diagnostics)
268288
.WithFormatArgs(
269289
functionType,
270290
this.MakeMismatchedFunctionType(callable.Arguments, functionType.ReturnType)));
271-
goto callable_end;
291+
return true;
272292
}
273293

274294
// We are done
275295
foreach (var (param, arg) in functionType.Parameters.Zip(callable.Arguments))
276296
{
277297
this.AssignParameterToArgument(param.Type, arg);
278298
}
279-
callable_end:
280299
return true;
281300
}
282301

283302
// If an overload constraint can be advanced, do that
284-
if (this.constraintStore.TryGet<Overload>(out _, overload => !overload.Candidates.IsWellDefined && overload.Candidates.Refine()))
285-
{
286-
return true;
287-
}
303+
bool OverloadRefine() =>
304+
this.constraintStore.TryGet<Overload>(out _, overload => !overload.Candidates.IsWellDefined && overload.Candidates.Refine());
288305

289306
// If overload constraints are unambiguous, we can resolve them directly
290-
if (this.constraintStore.TryRemove<Overload>(out var overload, overload => overload.Candidates.IsWellDefined))
307+
bool Overload()
291308
{
309+
if (!this.constraintStore.TryRemove<Overload>(out var overload, overload => overload.Candidates.IsWellDefined)) return false;
292310
// Call for safety
293311
overload.Candidates.Refine();
294312

@@ -304,7 +322,7 @@ private bool ApplyRulesOnce(DiagnosticBag diagnostics)
304322
.WithTemplate(TypeCheckingErrors.NoMatchingOverload)
305323
.WithFormatArgs(overload.FunctionName));
306324
}
307-
goto overload_end;
325+
return true;
308326
}
309327

310328
if (candidates.Length > 1)
@@ -318,7 +336,7 @@ private bool ApplyRulesOnce(DiagnosticBag diagnostics)
318336
.WithTemplate(TypeCheckingErrors.AmbiguousOverloadedCall)
319337
.WithFormatArgs(overload.FunctionName, string.Join(", ", overload.Candidates)));
320338
}
321-
goto overload_end;
339+
return true;
322340
}
323341

324342
// Resolved fine, choose the symbol, which might generic-instantiate it
@@ -359,7 +377,6 @@ private bool ApplyRulesOnce(DiagnosticBag diagnostics)
359377
UnifyAsserted(overload.ReturnType, chosen.ReturnType);
360378
// Resolve promise
361379
overload.CompletionSource.SetResult(chosen);
362-
overload_end:
363380
return true;
364381
}
365382

@@ -370,18 +387,16 @@ private bool ApplyRulesOnce(DiagnosticBag diagnostics)
370387
// x = Base();
371388
//
372389
// In this case we try to search for the common type of Derived and Base, then assign that
390+
bool MergeAssignables()
373391
{
374392
// First probe, if there are at least 2 such assignables
375393
var assignablesWithSharedTarget = this.constraintStore
376394
.Query<Assignable>()
377395
.GroupBy(a => a.TargetType, SymbolEqualityComparer.AllowTypeVariables)
378396
.Where(g => g.Count() > 1)
379397
.FirstOrDefault();
380-
if (assignablesWithSharedTarget is null)
381-
{
382-
// No such assignables, we are done
383-
goto after_assignable_merge;
384-
}
398+
// No such assignables, we are done
399+
if (assignablesWithSharedTarget is null) return false;
385400
// We have at least 2 assignables with the same target, merge them all
386401
var targetType = assignablesWithSharedTarget.Key;
387402
var commonType = this.AllocateTypeVariable();
@@ -398,46 +413,55 @@ private bool ApplyRulesOnce(DiagnosticBag diagnostics)
398413
assignedType: commonType));
399414
return true;
400415
}
401-
after_assignable_merge:
402416

403417
// As a last-last effort, we assume that a singular assignment means exact matching types
404-
if (this.constraintStore.TryRemove<Assignable>(out assignable, assignable => CanAssign(assignable.TargetType, assignable.AssignedType)))
418+
bool SingleAssignable()
405419
{
420+
if (!this.constraintStore.TryRemove<Assignable>(out var assignable, assignable => CanAssign(assignable.TargetType, assignable.AssignedType)))
421+
{
422+
return false;
423+
}
406424
AssignAsserted(assignable.TargetType, assignable.AssignedType);
407425
return true;
408426
}
409427

410428
// As a last-effort, if we see a common ancestor constraint with a single non-type-var, we
411429
// assume that the common type is the non-type-var
412430
// We also substitute all the type-vars with the common type
413-
if (this.constraintStore.TryRemove<CommonAncestor>(out common, common =>
414-
{
415-
if (common.AlternativeTypes.Count(t => !t.Substitution.IsTypeVariable) != 1) return false;
416-
if (common.AlternativeTypes.Count(t => t.Substitution.IsTypeVariable) != common.AlternativeTypes.Length - 1) return false;
417-
if (common.AlternativeTypes.Any(alt => !CanUnify(alt, common.CommonType))) return false;
418-
var nonTypeVar = common.AlternativeTypes.First(t => !t.Substitution.IsTypeVariable);
419-
var typeVars = common.AlternativeTypes.Where(t => t.Substitution.IsTypeVariable);
420-
if (!CanUnify(common.CommonType, nonTypeVar)) return false;
421-
return typeVars.All(t => CanUnify(t, nonTypeVar));
422-
}))
431+
bool CommonAncestorSingleNonTypeVar()
423432
{
424-
var nonTypeVar = common.AlternativeTypes.First(t => !t.Substitution.IsTypeVariable);
425-
var typeVars = common.AlternativeTypes.Where(t => t.Substitution.IsTypeVariable);
426-
foreach (var typeVar in typeVars) UnifyAsserted(typeVar, nonTypeVar);
427-
UnifyAsserted(common.CommonType, nonTypeVar);
428-
return true;
433+
if (this.constraintStore.TryRemove<CommonAncestor>(out var common, common =>
434+
{
435+
if (common.AlternativeTypes.Count(t => !t.Substitution.IsTypeVariable) != 1) return false;
436+
if (common.AlternativeTypes.Count(t => t.Substitution.IsTypeVariable) != common.AlternativeTypes.Length - 1) return false;
437+
if (common.AlternativeTypes.Any(alt => !CanUnify(alt, common.CommonType))) return false;
438+
var nonTypeVar = common.AlternativeTypes.First(t => !t.Substitution.IsTypeVariable);
439+
var typeVars = common.AlternativeTypes.Where(t => t.Substitution.IsTypeVariable);
440+
if (!CanUnify(common.CommonType, nonTypeVar)) return false;
441+
return typeVars.All(t => CanUnify(t, nonTypeVar));
442+
}))
443+
{
444+
var nonTypeVar = common.AlternativeTypes.First(t => !t.Substitution.IsTypeVariable);
445+
var typeVars = common.AlternativeTypes.Where(t => t.Substitution.IsTypeVariable);
446+
foreach (var typeVar in typeVars) UnifyAsserted(typeVar, nonTypeVar);
447+
UnifyAsserted(common.CommonType, nonTypeVar);
448+
return true;
449+
}
450+
return false;
429451
}
430452

431453
// If the target type of common ancestor is a concrete type, we can try to unify all non-concrete types
432-
if (this.constraintStore.TryRemove<CommonAncestor>(out common, common => common.CommonType.Substitution.IsGroundType
433-
&& common.AlternativeTypes.All(alt => CanUnify(alt, common.CommonType))))
454+
bool CommonAncestorIsGround()
434455
{
456+
if (!this.constraintStore.TryRemove<CommonAncestor>(out var common, common => common.CommonType.Substitution.IsGroundType
457+
&& common.AlternativeTypes.All(alt => CanUnify(alt, common.CommonType))))
458+
{
459+
return false;
460+
}
435461
var concreteType = common.CommonType.Substitution;
436462
foreach (var type in common.AlternativeTypes) UnifyAsserted(type, concreteType);
437463
return true;
438464
}
439-
440-
return false;
441465
}
442466

443467
/// <summary>

0 commit comments

Comments
 (0)