2 // Copyright (c) 2003, 2004, 2005 Rodrigo B. de Oliveira (rbo@acm.org)
3 // All rights reserved.
5 // Redistribution and use in source and binary forms, with or without modification,
6 // are permitted provided that the following conditions are met:
8 // * Redistributions of source code must retain the above copyright notice,
9 // this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright notice,
11 // this list of conditions and the following disclaimer in the documentation
12 // and/or other materials provided with the distribution.
13 // * Neither the name of Rodrigo B. de Oliveira nor the names of its
14 // contributors may be used to endorse or promote products derived from this
15 // software without specific prior written permission.
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
21 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 using Boo
.Lang
.Compiler
.Ast
;
33 namespace Boo
.Lang
.Compiler
.TypeSystem
36 /// Overload resolution service.
38 public class CallableResolutionService
: AbstractCompilerComponent
40 private const int CallableExactMatchScore
= 10;
41 private const int CallableUpCastScore
= 9;
42 private const int CallableImplicitConversionScore
= 8;
43 private const int ExactMatchScore
= 8;
44 private const int UpCastScore
= 7;
45 private const int WideningPromotion
= 6;
46 private const int ImplicitConversionScore
= 5;
47 private const int NarrowingPromotion
= 4;
48 private const int DowncastScore
= 3;
50 private List _candidates
= new List();
51 private ExpressionCollection _arguments
;
53 private Expression
GetArgument(int index
)
55 return _arguments
[index
];
58 public List ValidCandidates
60 get { return _candidates; }
63 public override void Dispose()
69 public class Candidate
71 public IMethod Method
;
72 private CallableResolutionService _crs
;
74 bool _expanded
= false;
76 public Candidate(CallableResolutionService crs
, IMethod entity
)
80 _scores
= new int[crs
._arguments
.Count
];
83 public IParameter
[] Parameters
85 get { return Method.GetParameters(); }
88 public int[] ArgumentScores
98 get { return _expanded; }
99 set { _expanded = value; }
102 public int Score(int argumentIndex
)
104 _scores
[argumentIndex
] = _crs
.CalculateArgumentScore(
105 Parameters
[argumentIndex
],
106 Parameters
[argumentIndex
].Type
,
107 _crs
.GetArgument(argumentIndex
));
109 return _scores
[argumentIndex
];
112 public int ScoreVarArgs(int argumentIndex
)
114 IParameter parameter
= Parameters
[Parameters
.Length
-1];
115 _scores
[argumentIndex
] = _crs
.CalculateArgumentScore(
117 parameter
.Type
.GetElementType(),
118 _crs
.GetArgument(argumentIndex
));
120 return _scores
[argumentIndex
];
123 override public int GetHashCode()
125 return Method
.GetHashCode();
128 override public bool Equals(object other
)
130 Candidate score
= other
as Candidate
;
133 : Method
== score
.Method
;
136 override public string ToString()
138 return Method
.ToString();
142 public int GetLogicalTypeDepth(IType type
)
144 int depth
= type
.GetTypeDepth();
145 if (type
.IsValueType
) return depth
- 1;
149 IType
GetExpressionTypeOrEntityType(Node node
)
151 Expression e
= node
as Expression
;
153 ? TypeSystemServices
.GetExpressionType(e
)
154 : TypeSystem
.TypeSystemServices
.GetType(node
);
157 public bool IsValidByRefArg(IParameter param
, IType parameterType
, IType argType
, Node arg
)
159 if ((parameterType
.IsByRef
&&
160 argType
== parameterType
.GetElementType()))
162 return CanLoadAddress(arg
);
164 else if (param
.IsByRef
&&
165 argType
== parameterType
)
167 return CanLoadAddress(arg
);
172 static bool CanLoadAddress(Node node
)
174 IEntity entity
= node
.Entity
;
176 if (null == entity
) return true;
178 switch (entity
.EntityType
)
180 case EntityType
.Local
:
182 return !((InternalLocal
)entity
).IsPrivateScope
;
185 case EntityType
.Parameter
:
190 case EntityType
.Field
:
192 return !TypeSystemServices
.IsReadOnlyField((IField
)entity
);
198 public IEntity
ResolveCallableReference(ExpressionCollection args
, IEntity
[] candidates
)
201 FindApplicableCandidates(candidates
);
202 if (ValidCandidates
.Count
== 0) return null;
203 if (ValidCandidates
.Count
== 1) return ((Candidate
)ValidCandidates
[0]).Method
;
205 List dataPreserving
= ValidCandidates
.Collect(DoesNotRequireConversions
);
206 if (dataPreserving
.Count
> 0)
208 if (dataPreserving
.Count
== 1) return ((Candidate
)dataPreserving
[0]).Method
;
209 IEntity found
= BestMethod(dataPreserving
);
210 if (null != found
) return found
;
212 return BestCandidate();
215 private static bool DoesNotRequireConversions(object candidate
)
217 return !Array
.Exists(((Candidate
) candidate
).ArgumentScores
, RequiresConversion
);
220 private static bool RequiresConversion(int score
)
222 return score
< WideningPromotion
;
225 private IEntity
BestCandidate()
227 return BestMethod(_candidates
);
230 private IEntity
BestMethod(List candidates
)
232 candidates
.Sort(new Comparer(BetterCandidate
));
234 if (BetterCandidate(candidates
[-1], candidates
[-2]) == 0)
236 object pivot
= candidates
[-2];
238 candidates
.RemoveAll(delegate(object item
)
240 return 0 != BetterCandidate(item
, pivot
);
246 // SUCCESS: _candidates[-1] is the winner
247 return ((Candidate
)candidates
[-1]).Method
;
250 private int BetterCandidate(object lhs
, object rhs
)
252 return BetterCandidate((Candidate
) lhs
, (Candidate
) rhs
);
255 private bool ApplicableCandidate(Candidate candidate
)
257 // Figure out whether method should be varargs-expanded
259 candidate
.Method
.AcceptVarArgs
&&
260 (_arguments
.Count
== 0 || (_arguments
.Count
> 0 &&
261 !AstUtil
.IsExplodeExpression(_arguments
[-1])));
263 // Determine number of fixed (non-varargs) parameters
265 (expand
? candidate
.Parameters
.Length
- 1 : candidate
.Parameters
.Length
);
267 // Validate number of parameters against number of arguments
268 if (_arguments
.Count
< fixedParams
) return false;
269 if (_arguments
.Count
> fixedParams
&& !expand
) return false;
271 // Score each argument against a fixed parameter
272 for (int i
= 0; i
< fixedParams
; i
++)
274 if (candidate
.Score(i
) < 0)
280 // If method should be expanded, match remaining arguments against
284 candidate
.Expanded
= true;
285 for (int i
= fixedParams
; i
< _arguments
.Count
; i
++)
287 if (candidate
.ScoreVarArgs(i
) < 0)
297 private int TotalScore(Candidate c1
)
300 foreach (int score
in c1
.ArgumentScores
)
307 private int BetterCandidate(Candidate c1
, Candidate c2
)
309 int result
= Math
.Sign(TotalScore(c1
) - TotalScore(c2
));
314 for (int i = 0; i < _arguments.Count; i++)
316 // Compare methods based on their score for the current argument
317 int better = Math.Sign(c1.ArgumentScores[i] - c2.ArgumentScores[i]);
318 if (better == 0) continue;
320 // If neither method has been selecteed yet, select one
321 // based on this argument
326 // If this argument comparison is in conflict with previous selection,
327 // neither method is better
328 else if (result != better)
341 result
= c1
.Method
.DeclaringType
.GetTypeDepth() - c2
.Method
.DeclaringType
.GetTypeDepth();
347 // --- Tie breaking mode! ---
349 // Non-generic methods are better than generic ones
351 // Commented out since current syntax distinguishes between invoking
352 // a generic method and a non generic one
354 IGenericMethodInfo generic1 = c1.Method.GenericMethodInfo;
355 IGenericMethodInfo generic2 = c2.Method.GenericMethodInfo;
356 if (generic1 == null && generic2 != null)
360 else if (generic1 != null && generic2 == null)
366 // Non-expanded methods are better than expanded ones
367 if (!c1
.Expanded
&& c2
.Expanded
)
371 else if (c1
.Expanded
&& !c2
.Expanded
)
376 // An expanded method with more fixed parameters is better
377 result
= c1
.Parameters
.Length
- c2
.Parameters
.Length
;
378 if (result
!= 0) return result
;
380 // As a last means of breaking this desparate tie, we select the
381 // "more specific" candidate, if one exists
382 return MoreSpecific(c1
, c2
);
385 private int MoreSpecific(Candidate c1
, Candidate c2
)
388 for (int i
= 0; i
< _arguments
.Count
&& i
< c1
.Parameters
.Length
; ++i
)
390 if (c1
.ArgumentScores
[i
] <= DowncastScore
) continue;
392 // Select the most specific of the parameters' types,
393 // taking into account generic mapped parameters
394 int better
= MoreSpecific(
395 GetParameterTypeTemplate(c1
, i
),
396 GetParameterTypeTemplate(c2
, i
));
398 // Skip parameters that are the same for both candidates
404 // Keep the first result that is not a tie
409 // If a further result contradicts the initial result, neither candidate is more specific
410 else if (result
!= better
)
418 private IType
GetParameterTypeTemplate(Candidate candidate
, int position
)
420 // Get the method this candidate represents, or its generic template
421 IMethod method
= candidate
.Method
;
422 if (candidate
.Method
.DeclaringType
.ConstructedInfo
!= null)
424 method
= candidate
.Method
.DeclaringType
.ConstructedInfo
.GetMethodTemplate(method
);
427 // If the parameter is the varargs parameter, use its element type
428 IParameter
[] parameters
= method
.GetParameters();
429 if (candidate
.Expanded
&& position
>= parameters
.Length
)
431 return parameters
[parameters
.Length
- 1].Type
.GetElementType();
434 // Otherwise use the parameter's original type
435 return parameters
[position
].Type
;
438 private int MoreSpecific(IType t1
, IType t2
)
440 // Dive into array types and ref types
441 if (t1
.IsArray
&& t2
.IsArray
|| t1
.IsByRef
&& t2
.IsByRef
)
443 return MoreSpecific(t1
.GetElementType(), t2
.GetElementType());
446 // The less-generic type is more specific
447 int result
= GenericsServices
.GetTypeGenerity(t2
) - GenericsServices
.GetTypeGenerity(t1
);
448 if (result
!= 0) return result
;
450 // If both types have the same genrity, the deeper-nested type is more specific
451 return GetLogicalTypeDepth(t1
) - GetLogicalTypeDepth(t2
);
454 private void FindApplicableCandidates(IEntity
[] candidates
)
456 foreach (IEntity entity
in candidates
)
458 IMethod method
= entity
as IMethod
;
459 if (null == method
) continue;
461 Candidate candidate
= new Candidate(this, method
);
463 if (!ApplicableCandidate(candidate
)) continue;
465 _candidates
.Add(candidate
);
469 private void Reset(ExpressionCollection arguments
)
471 _arguments
= arguments
;
475 public bool IsValidVargsInvocation(IParameter
[] parameters
, ExpressionCollection args
)
477 int lastIndex
= parameters
.Length
- 1;
478 if (args
.Count
< lastIndex
) return false;
480 IType lastParameterType
= parameters
[lastIndex
].Type
;
481 if (!lastParameterType
.IsArray
) return false;
483 if (!IsValidInvocation(parameters
, args
, lastIndex
)) return false;
487 Node lastArg
= args
[-1];
488 if (AstUtil
.IsExplodeExpression(lastArg
))
490 return CalculateArgumentScore(parameters
[lastIndex
], lastParameterType
, lastArg
) > 0;
494 IType varArgType
= lastParameterType
.GetElementType();
495 for (int i
= lastIndex
; i
< args
.Count
; ++i
)
497 int argumentScore
= CalculateArgumentScore(parameters
[lastIndex
], varArgType
, args
[i
]);
498 if (argumentScore
< 0) return false;
505 private bool IsValidInvocation(IParameter
[] parameters
, ExpressionCollection args
, int count
)
507 for (int i
= 0; i
< count
; ++i
)
509 IParameter parameter
= parameters
[i
];
510 IType parameterType
= parameter
.Type
;
511 int argumentScore
= CalculateArgumentScore(parameter
, parameterType
, args
[i
]);
512 if (argumentScore
< 0) return false;
518 private int CalculateArgumentScore(IParameter param
, IType parameterType
, Node arg
)
520 IType argumentType
= GetExpressionTypeOrEntityType(arg
);
523 if (IsValidByRefArg(param
, parameterType
, argumentType
, arg
))
525 return ExactMatchScore
;
529 else if (parameterType
== argumentType
530 || (TypeSystemServices
.IsSystemObject(argumentType
) &&
531 TypeSystemServices
.IsSystemObject(parameterType
)))
533 return parameterType
is ICallableType
534 ? CallableExactMatchScore
537 else if (parameterType
.IsAssignableFrom(argumentType
))
539 ICallableType callableType
= parameterType
as ICallableType
;
540 ICallableType callableArg
= argumentType
as ICallableType
;
541 if (callableType
!= null && callableArg
!= null)
543 return CalculateCallableScore(callableType
, callableArg
);
547 else if (TypeSystemServices
.FindImplicitConversionOperator(argumentType
, parameterType
) != null)
549 return ImplicitConversionScore
;
551 else if (TypeSystemServices
.CanBeReachedByPromotion(parameterType
, argumentType
))
553 if (IsWideningPromotion(parameterType
, argumentType
)) return WideningPromotion
;
554 return NarrowingPromotion
;
556 else if (TypeSystemServices
.CanBeReachedByDowncast(parameterType
, argumentType
))
558 return DowncastScore
;
563 private bool IsWideningPromotion(IType paramType
, IType argumentType
)
565 ExternalType expected
= paramType
as ExternalType
;
566 if (null == expected
) return false;
567 ExternalType actual
= argumentType
as ExternalType
;
568 if (null == actual
) return false;
569 return Boo
.Lang
.Runtime
.NumericTypes
.IsWideningPromotion(expected
.ActualType
, actual
.ActualType
);
572 private static int CalculateCallableScore(ICallableType parameterType
, ICallableType argType
)
575 // parameterType == ICallableType, "ThreadStart"
576 // argumentType == ICallableType, "Anonymous Closure"
578 // Number of arguments for argumentType && parameterType == same
579 // Either: all arguments "IsAssignableFrom"
581 // all arguments == exactly (best case scenario)
582 // ExactMatch -- (best case)
583 // UpCast -- "not exact match, but very close" (this is OK)
584 // ImplicitConversion -- "assignable, but wrong number of parameters / whatever" (boo does the normal thing)
586 CallableSignature siggyType
= parameterType
.GetSignature();
587 CallableSignature siggyArg
= argType
.GetSignature();
588 // Ensuring that these callables have same number of arguments.
589 // def foo(a, b,c) == { a, b, c| print foobar }
590 if (siggyType
.Parameters
.Length
!= siggyArg
.Parameters
.Length
)
592 return CallableUpCastScore
;
594 for (int i
= 0; i
< siggyType
.Parameters
.Length
; i
++)
596 if (siggyType
.Parameters
[i
].Type
!= siggyArg
.Parameters
[i
].Type
)
598 return CallableImplicitConversionScore
;
601 return siggyType
.ReturnType
== siggyArg
.ReturnType
602 ? CallableExactMatchScore
: CallableUpCastScore
;