PageRenderTime 50ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/Dependencies/boo/src/Boo.Lang.Compiler/TypeSystem/CallableResolutionService.cs

https://github.com/w4x/boolangstudio
C# | 597 lines | 425 code | 80 blank | 92 comment | 111 complexity | 41dd6411f222e91f94920343eece697f MD5 | raw file
Possible License(s): GPL-2.0
  1. #region license
  2. // Copyright (c) 2003, 2004, 2005 Rodrigo B. de Oliveira (rbo@acm.org)
  3. // All rights reserved.
  4. //
  5. // Redistribution and use in source and binary forms, with or without modification,
  6. // are permitted provided that the following conditions are met:
  7. //
  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.
  16. //
  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.
  27. #endregion
  28. using System;
  29. using Boo.Lang.Compiler.Ast;
  30. namespace Boo.Lang.Compiler.TypeSystem
  31. {
  32. /// <summary>
  33. /// Overload resolution service.
  34. /// </summary>
  35. public class CallableResolutionService : AbstractCompilerComponent
  36. {
  37. private const int CallableExactMatchScore = 10;
  38. private const int CallableUpCastScore = 9;
  39. private const int CallableImplicitConversionScore = 8;
  40. private const int ExactMatchScore = 8;
  41. private const int UpCastScore = 7;
  42. private const int WideningPromotion = 6;
  43. private const int ImplicitConversionScore = 5;
  44. private const int NarrowingPromotion = 4;
  45. private const int DowncastScore = 3;
  46. private List _candidates = new List();
  47. private ExpressionCollection _arguments;
  48. private Expression GetArgument(int index)
  49. {
  50. return _arguments[index];
  51. }
  52. public List ValidCandidates
  53. {
  54. get { return _candidates; }
  55. }
  56. public override void Dispose()
  57. {
  58. _candidates.Clear();
  59. base.Dispose();
  60. }
  61. public class Candidate
  62. {
  63. public IMethod Method;
  64. private CallableResolutionService _crs;
  65. int[] _scores = null;
  66. bool _expanded = false;
  67. public Candidate(CallableResolutionService crs, IMethod entity)
  68. {
  69. _crs = crs;
  70. Method = entity;
  71. _scores = new int[crs._arguments.Count];
  72. }
  73. public IParameter[] Parameters
  74. {
  75. get { return Method.GetParameters(); }
  76. }
  77. public int[] ArgumentScores
  78. {
  79. get
  80. {
  81. return _scores;
  82. }
  83. }
  84. public bool Expanded
  85. {
  86. get { return _expanded; }
  87. set { _expanded = value; }
  88. }
  89. public int Score(int argumentIndex)
  90. {
  91. _scores[argumentIndex] = _crs.CalculateArgumentScore(
  92. Parameters[argumentIndex],
  93. Parameters[argumentIndex].Type,
  94. _crs.GetArgument(argumentIndex));
  95. return _scores[argumentIndex];
  96. }
  97. public int ScoreVarArgs(int argumentIndex)
  98. {
  99. IParameter parameter = Parameters[Parameters.Length-1];
  100. _scores[argumentIndex] = _crs.CalculateArgumentScore(
  101. parameter,
  102. parameter.Type.GetElementType(),
  103. _crs.GetArgument(argumentIndex));
  104. return _scores[argumentIndex];
  105. }
  106. override public int GetHashCode()
  107. {
  108. return Method.GetHashCode();
  109. }
  110. override public bool Equals(object other)
  111. {
  112. Candidate score = other as Candidate;
  113. return null == score
  114. ? false
  115. : Method == score.Method;
  116. }
  117. override public string ToString()
  118. {
  119. return Method.ToString();
  120. }
  121. }
  122. public int GetLogicalTypeDepth(IType type)
  123. {
  124. int depth = type.GetTypeDepth();
  125. if (type.IsValueType) return depth - 1;
  126. return depth;
  127. }
  128. IType GetExpressionTypeOrEntityType(Node node)
  129. {
  130. Expression e = node as Expression;
  131. return null != e
  132. ? TypeSystemServices.GetExpressionType(e)
  133. : TypeSystem.TypeSystemServices.GetType(node);
  134. }
  135. public bool IsValidByRefArg(IParameter param, IType parameterType, IType argType, Node arg)
  136. {
  137. if ((parameterType.IsByRef &&
  138. argType == parameterType.GetElementType()))
  139. {
  140. return CanLoadAddress(arg);
  141. }
  142. else if (param.IsByRef &&
  143. argType == parameterType)
  144. {
  145. return CanLoadAddress(arg);
  146. }
  147. return false;
  148. }
  149. static bool CanLoadAddress(Node node)
  150. {
  151. IEntity entity = node.Entity;
  152. if (null == entity) return true;
  153. switch (entity.EntityType)
  154. {
  155. case EntityType.Local:
  156. {
  157. return !((InternalLocal)entity).IsPrivateScope;
  158. }
  159. case EntityType.Parameter:
  160. {
  161. return true;
  162. }
  163. case EntityType.Field:
  164. {
  165. return !TypeSystemServices.IsReadOnlyField((IField)entity);
  166. }
  167. }
  168. return false;
  169. }
  170. public IEntity ResolveCallableReference(ExpressionCollection args, IEntity[] candidates)
  171. {
  172. Reset(args);
  173. FindApplicableCandidates(candidates);
  174. if (ValidCandidates.Count == 0) return null;
  175. if (ValidCandidates.Count == 1) return ((Candidate)ValidCandidates[0]).Method;
  176. List dataPreserving = ValidCandidates.Collect(DoesNotRequireConversions);
  177. if (dataPreserving.Count > 0)
  178. {
  179. if (dataPreserving.Count == 1) return ((Candidate)dataPreserving[0]).Method;
  180. IEntity found = BestMethod(dataPreserving);
  181. if (null != found) return found;
  182. }
  183. return BestCandidate();
  184. }
  185. private static bool DoesNotRequireConversions(object candidate)
  186. {
  187. return !Array.Exists(((Candidate) candidate).ArgumentScores, RequiresConversion);
  188. }
  189. private static bool RequiresConversion(int score)
  190. {
  191. return score < WideningPromotion;
  192. }
  193. private IEntity BestCandidate()
  194. {
  195. return BestMethod(_candidates);
  196. }
  197. private IEntity BestMethod(List candidates)
  198. {
  199. candidates.Sort(new Comparer(BetterCandidate));
  200. if (BetterCandidate(candidates[-1], candidates[-2]) == 0)
  201. {
  202. object pivot = candidates[-2];
  203. candidates.RemoveAll(delegate(object item)
  204. {
  205. return 0 != BetterCandidate(item, pivot);
  206. });
  207. // Ambiguous match
  208. return null;
  209. }
  210. // SUCCESS: _candidates[-1] is the winner
  211. return ((Candidate)candidates[-1]).Method;
  212. }
  213. private int BetterCandidate(object lhs, object rhs)
  214. {
  215. return BetterCandidate((Candidate) lhs, (Candidate) rhs);
  216. }
  217. private bool ApplicableCandidate(Candidate candidate)
  218. {
  219. // Figure out whether method should be varargs-expanded
  220. bool expand =
  221. candidate.Method.AcceptVarArgs &&
  222. (_arguments.Count == 0 || (_arguments.Count > 0 &&
  223. !AstUtil.IsExplodeExpression(_arguments[-1])));
  224. // Determine number of fixed (non-varargs) parameters
  225. int fixedParams =
  226. (expand ? candidate.Parameters.Length - 1 : candidate.Parameters.Length);
  227. // Validate number of parameters against number of arguments
  228. if (_arguments.Count < fixedParams) return false;
  229. if (_arguments.Count > fixedParams && !expand) return false;
  230. // Score each argument against a fixed parameter
  231. for (int i = 0; i < fixedParams; i++)
  232. {
  233. if (candidate.Score(i) < 0)
  234. {
  235. return false;
  236. }
  237. }
  238. // If method should be expanded, match remaining arguments against
  239. // last parameter
  240. if (expand)
  241. {
  242. candidate.Expanded = true;
  243. for (int i = fixedParams; i < _arguments.Count; i++)
  244. {
  245. if (candidate.ScoreVarArgs(i) < 0)
  246. {
  247. return false;
  248. }
  249. }
  250. }
  251. return true;
  252. }
  253. private int TotalScore(Candidate c1)
  254. {
  255. int total = 0;
  256. foreach (int score in c1.ArgumentScores)
  257. {
  258. total += score;
  259. }
  260. return total;
  261. }
  262. private int BetterCandidate(Candidate c1, Candidate c2)
  263. {
  264. int result = Math.Sign(TotalScore(c1) - TotalScore(c2));
  265. // int result = 0;
  266. /*
  267. if (false)
  268. {
  269. for (int i = 0; i < _arguments.Count; i++)
  270. {
  271. // Compare methods based on their score for the current argument
  272. int better = Math.Sign(c1.ArgumentScores[i] - c2.ArgumentScores[i]);
  273. if (better == 0) continue;
  274. // If neither method has been selecteed yet, select one
  275. // based on this argument
  276. if (result == 0)
  277. {
  278. result = better;
  279. }
  280. // If this argument comparison is in conflict with previous selection,
  281. // neither method is better
  282. else if (result != better)
  283. {
  284. return 0;
  285. }
  286. }
  287. }
  288. */
  289. if (result != 0)
  290. {
  291. return result;
  292. }
  293. // Prefer methods declared on deeper types
  294. result =
  295. c1.Method.DeclaringType.GetTypeDepth() -
  296. c2.Method.DeclaringType.GetTypeDepth();
  297. if (result != 0) return result;
  298. // Prefer methods with less generic parameters
  299. result =
  300. GenericsServices.GetMethodGenerity(c2.Method) -
  301. GenericsServices.GetMethodGenerity(c1.Method);
  302. if (result != 0) return result;
  303. // --- Tie breaking mode! ---
  304. // Non-expanded methods are better than expanded ones
  305. if (!c1.Expanded && c2.Expanded)
  306. {
  307. return 1;
  308. }
  309. else if (c1.Expanded && !c2.Expanded)
  310. {
  311. return -1;
  312. }
  313. // An expanded method with more fixed parameters is better
  314. result = c1.Parameters.Length - c2.Parameters.Length;
  315. if (result != 0) return result;
  316. // As a last means of breaking this desparate tie, we select the
  317. // "more specific" candidate, if one exists
  318. return MoreSpecific(c1, c2);
  319. }
  320. private int MoreSpecific(Candidate c1, Candidate c2)
  321. {
  322. int result = 0;
  323. for (int i = 0; i < _arguments.Count && i < c1.Parameters.Length; ++i)
  324. {
  325. if (c1.ArgumentScores[i] <= DowncastScore) continue;
  326. // Select the most specific of the parameters' types,
  327. // taking into account generic mapped parameters
  328. int better = MoreSpecific(
  329. GetParameterTypeTemplate(c1, i),
  330. GetParameterTypeTemplate(c2, i));
  331. // Skip parameters that are the same for both candidates
  332. if (better == 0)
  333. {
  334. continue;
  335. }
  336. // Keep the first result that is not a tie
  337. if (result == 0)
  338. {
  339. result = better;
  340. }
  341. // If a further result contradicts the initial result, neither candidate is more specific
  342. else if (result != better)
  343. {
  344. return 0;
  345. }
  346. }
  347. return result;
  348. }
  349. private IType GetParameterTypeTemplate(Candidate candidate, int position)
  350. {
  351. // Get the method this candidate represents, or its generic template
  352. IMethod method = candidate.Method;
  353. if (candidate.Method.DeclaringType.ConstructedInfo != null)
  354. {
  355. method = candidate.Method.DeclaringType.ConstructedInfo.GetMethodTemplate(method);
  356. }
  357. // If the parameter is the varargs parameter, use its element type
  358. IParameter[] parameters = method.GetParameters();
  359. if (candidate.Expanded && position >= parameters.Length)
  360. {
  361. return parameters[parameters.Length - 1].Type.GetElementType();
  362. }
  363. // Otherwise use the parameter's original type
  364. return parameters[position].Type;
  365. }
  366. private int MoreSpecific(IType t1, IType t2)
  367. {
  368. // Dive into array types and ref types
  369. if (t1.IsArray && t2.IsArray || t1.IsByRef && t2.IsByRef)
  370. {
  371. return MoreSpecific(t1.GetElementType(), t2.GetElementType());
  372. }
  373. // The less-generic type is more specific
  374. int result = GenericsServices.GetTypeGenerity(t2) - GenericsServices.GetTypeGenerity(t1);
  375. if (result != 0) return result;
  376. // If both types have the same genrity, the deeper-nested type is more specific
  377. return GetLogicalTypeDepth(t1) - GetLogicalTypeDepth(t2);
  378. }
  379. private void FindApplicableCandidates(IEntity[] candidates)
  380. {
  381. foreach (IEntity entity in candidates)
  382. {
  383. IMethod method = entity as IMethod;
  384. if (null == method) continue;
  385. Candidate candidate = new Candidate(this, method);
  386. if (!ApplicableCandidate(candidate)) continue;
  387. _candidates.Add(candidate);
  388. }
  389. }
  390. private void Reset(ExpressionCollection arguments)
  391. {
  392. _arguments = arguments;
  393. _candidates.Clear();
  394. }
  395. public bool IsValidVargsInvocation(IParameter[] parameters, ExpressionCollection args)
  396. {
  397. int lastIndex = parameters.Length - 1;
  398. if (args.Count < lastIndex) return false;
  399. IType lastParameterType = parameters[lastIndex].Type;
  400. if (!lastParameterType.IsArray) return false;
  401. if (!IsValidInvocation(parameters, args, lastIndex)) return false;
  402. if (args.Count > 0)
  403. {
  404. Node lastArg = args[-1];
  405. if (AstUtil.IsExplodeExpression(lastArg))
  406. {
  407. return CalculateArgumentScore(parameters[lastIndex], lastParameterType, lastArg) > 0;
  408. }
  409. else
  410. {
  411. IType varArgType = lastParameterType.GetElementType();
  412. for (int i = lastIndex; i < args.Count; ++i)
  413. {
  414. int argumentScore = CalculateArgumentScore(parameters[lastIndex], varArgType, args[i]);
  415. if (argumentScore < 0) return false;
  416. }
  417. }
  418. }
  419. return true;
  420. }
  421. private bool IsValidInvocation(IParameter[] parameters, ExpressionCollection args, int count)
  422. {
  423. for (int i = 0; i < count; ++i)
  424. {
  425. IParameter parameter = parameters[i];
  426. IType parameterType = parameter.Type;
  427. int argumentScore = CalculateArgumentScore(parameter, parameterType, args[i]);
  428. if (argumentScore < 0) return false;
  429. }
  430. return true;
  431. }
  432. private int CalculateArgumentScore(IParameter param, IType parameterType, Node arg)
  433. {
  434. IType argumentType = GetExpressionTypeOrEntityType(arg);
  435. if (param.IsByRef)
  436. {
  437. if (IsValidByRefArg(param, parameterType, argumentType, arg))
  438. {
  439. return ExactMatchScore;
  440. }
  441. return -1;
  442. }
  443. else if (parameterType == argumentType
  444. || (TypeSystemServices.IsSystemObject(argumentType) &&
  445. TypeSystemServices.IsSystemObject(parameterType)))
  446. {
  447. return parameterType is ICallableType
  448. ? CallableExactMatchScore
  449. : ExactMatchScore;
  450. }
  451. else if (parameterType.IsAssignableFrom(argumentType))
  452. {
  453. ICallableType callableType = parameterType as ICallableType;
  454. ICallableType callableArg = argumentType as ICallableType;
  455. if (callableType != null && callableArg != null)
  456. {
  457. return CalculateCallableScore(callableType, callableArg);
  458. }
  459. return UpCastScore;
  460. }
  461. else if (TypeSystemServices.FindImplicitConversionOperator(argumentType, parameterType) != null)
  462. {
  463. return ImplicitConversionScore;
  464. }
  465. else if (TypeSystemServices.CanBeReachedByPromotion(parameterType, argumentType))
  466. {
  467. if (IsWideningPromotion(parameterType, argumentType)) return WideningPromotion;
  468. return NarrowingPromotion;
  469. }
  470. else if (TypeSystemServices.CanBeReachedByDowncast(parameterType, argumentType))
  471. {
  472. return DowncastScore;
  473. }
  474. return -1;
  475. }
  476. private bool IsWideningPromotion(IType paramType, IType argumentType)
  477. {
  478. ExternalType expected = paramType as ExternalType;
  479. if (null == expected) return false;
  480. ExternalType actual = argumentType as ExternalType;
  481. if (null == actual) return false;
  482. return Boo.Lang.Runtime.NumericTypes.IsWideningPromotion(expected.ActualType, actual.ActualType);
  483. }
  484. private static int CalculateCallableScore(ICallableType parameterType, ICallableType argType)
  485. {
  486. // upcast
  487. // parameterType == ICallableType, "ThreadStart"
  488. // argumentType == ICallableType, "Anonymous Closure"
  489. // RULES:
  490. // Number of arguments for argumentType && parameterType == same
  491. // Either: all arguments "IsAssignableFrom"
  492. // OR
  493. // all arguments == exactly (best case scenario)
  494. // ExactMatch -- (best case)
  495. // UpCast -- "not exact match, but very close" (this is OK)
  496. // ImplicitConversion -- "assignable, but wrong number of parameters / whatever" (boo does the normal thing)
  497. CallableSignature siggyType = parameterType.GetSignature();
  498. CallableSignature siggyArg = argType.GetSignature();
  499. // Ensuring that these callables have same number of arguments.
  500. // def foo(a, b,c) == { a, b, c| print foobar }
  501. if (siggyType.Parameters.Length != siggyArg.Parameters.Length)
  502. {
  503. return CallableUpCastScore;
  504. }
  505. for (int i = 0; i < siggyType.Parameters.Length; i++)
  506. {
  507. if (siggyType.Parameters[i].Type != siggyArg.Parameters[i].Type)
  508. {
  509. return CallableImplicitConversionScore;
  510. }
  511. }
  512. return siggyType.ReturnType == siggyArg.ReturnType
  513. ? CallableExactMatchScore : CallableUpCastScore;
  514. }
  515. }
  516. }