PageRenderTime 43ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 1ms

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

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