@@ -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