You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

639 lines
18 KiB

  1. // Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team
  2. //
  3. // Permission is hereby granted, free of charge, to any person obtaining a copy of this
  4. // software and associated documentation files (the "Software"), to deal in the Software
  5. // without restriction, including without limitation the rights to use, copy, modify, merge,
  6. // publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
  7. // to whom the Software is furnished to do so, subject to the following conditions:
  8. //
  9. // The above copyright notice and this permission notice shall be included in all copies or
  10. // substantial portions of the Software.
  11. //
  12. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
  13. // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  14. // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
  15. // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  16. // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  17. // DEALINGS IN THE SOFTWARE.
  18. using System;
  19. using System.Collections.Immutable;
  20. using System.Linq;
  21. using ICSharpCode.Decompiler.TypeSystem;
  22. namespace ICSharpCode.Decompiler.Semantics
  23. {
  24. /// <summary>
  25. /// Holds information about a conversion between two types.
  26. /// </summary>
  27. public abstract class Conversion : IEquatable<Conversion>
  28. {
  29. #region Conversion factory methods
  30. /// <summary>
  31. /// Not a valid conversion.
  32. /// </summary>
  33. public static readonly Conversion None = new InvalidConversion();
  34. /// <summary>
  35. /// Identity conversion.
  36. /// </summary>
  37. public static readonly Conversion IdentityConversion = new BuiltinConversion(true, 0);
  38. public static readonly Conversion ImplicitNumericConversion = new NumericOrEnumerationConversion(true, false);
  39. public static readonly Conversion ExplicitNumericConversion = new NumericOrEnumerationConversion(false, false);
  40. public static readonly Conversion ImplicitLiftedNumericConversion = new NumericOrEnumerationConversion(true, true);
  41. public static readonly Conversion ExplicitLiftedNumericConversion = new NumericOrEnumerationConversion(false, true);
  42. public static Conversion EnumerationConversion(bool isImplicit, bool isLifted)
  43. {
  44. return new NumericOrEnumerationConversion(isImplicit, isLifted, true);
  45. }
  46. public static readonly Conversion NullLiteralConversion = new BuiltinConversion(true, 1);
  47. /// <summary>
  48. /// The numeric conversion of a constant expression.
  49. /// </summary>
  50. public static readonly Conversion ImplicitConstantExpressionConversion = new BuiltinConversion(true, 2);
  51. public static readonly Conversion ImplicitReferenceConversion = new BuiltinConversion(true, 3);
  52. public static readonly Conversion ExplicitReferenceConversion = new BuiltinConversion(false, 3);
  53. public static readonly Conversion ImplicitDynamicConversion = new BuiltinConversion(true, 4);
  54. public static readonly Conversion ExplicitDynamicConversion = new BuiltinConversion(false, 4);
  55. public static readonly Conversion ImplicitNullableConversion = new BuiltinConversion(true, 5);
  56. public static readonly Conversion ExplicitNullableConversion = new BuiltinConversion(false, 5);
  57. public static readonly Conversion ImplicitPointerConversion = new BuiltinConversion(true, 6);
  58. public static readonly Conversion ExplicitPointerConversion = new BuiltinConversion(false, 6);
  59. public static readonly Conversion BoxingConversion = new BuiltinConversion(true, 7);
  60. public static readonly Conversion UnboxingConversion = new BuiltinConversion(false, 8);
  61. /// <summary>
  62. /// C# 'as' cast.
  63. /// </summary>
  64. public static readonly Conversion TryCast = new BuiltinConversion(false, 9);
  65. /// <summary>
  66. /// C# 6 string interpolation expression implicitly being converted to <see cref="System.IFormattable"/> or <see cref="System.FormattableString"/>.
  67. /// </summary>
  68. public static readonly Conversion ImplicitInterpolatedStringConversion = new BuiltinConversion(true, 10);
  69. /// <summary>
  70. /// C# 7 throw expression being converted to an arbitrary type.
  71. /// </summary>
  72. public static readonly Conversion ThrowExpressionConversion = new BuiltinConversion(true, 11);
  73. public static Conversion UserDefinedConversion(IMethod operatorMethod, bool isImplicit, Conversion conversionBeforeUserDefinedOperator, Conversion conversionAfterUserDefinedOperator, bool isLifted = false, bool isAmbiguous = false)
  74. {
  75. if (operatorMethod == null)
  76. throw new ArgumentNullException(nameof(operatorMethod));
  77. return new UserDefinedConv(isImplicit, operatorMethod, conversionBeforeUserDefinedOperator, conversionAfterUserDefinedOperator, isLifted, isAmbiguous);
  78. }
  79. public static Conversion MethodGroupConversion(IMethod chosenMethod, bool isVirtualMethodLookup, bool delegateCapturesFirstArgument)
  80. {
  81. if (chosenMethod == null)
  82. throw new ArgumentNullException(nameof(chosenMethod));
  83. return new MethodGroupConv(chosenMethod, isVirtualMethodLookup, delegateCapturesFirstArgument, isValid: true);
  84. }
  85. public static Conversion InvalidMethodGroupConversion(IMethod chosenMethod, bool isVirtualMethodLookup, bool delegateCapturesFirstArgument)
  86. {
  87. if (chosenMethod == null)
  88. throw new ArgumentNullException(nameof(chosenMethod));
  89. return new MethodGroupConv(chosenMethod, isVirtualMethodLookup, delegateCapturesFirstArgument, isValid: false);
  90. }
  91. public static Conversion TupleConversion(ImmutableArray<Conversion> conversions)
  92. {
  93. return new TupleConv(conversions);
  94. }
  95. #endregion
  96. #region Inner classes
  97. sealed class InvalidConversion : Conversion
  98. {
  99. public override bool IsValid {
  100. get { return false; }
  101. }
  102. public override string ToString()
  103. {
  104. return "None";
  105. }
  106. }
  107. sealed class NumericOrEnumerationConversion : Conversion
  108. {
  109. readonly bool isImplicit;
  110. readonly bool isLifted;
  111. readonly bool isEnumeration;
  112. public NumericOrEnumerationConversion(bool isImplicit, bool isLifted, bool isEnumeration = false)
  113. {
  114. this.isImplicit = isImplicit;
  115. this.isLifted = isLifted;
  116. this.isEnumeration = isEnumeration;
  117. }
  118. public override bool IsImplicit {
  119. get { return isImplicit; }
  120. }
  121. public override bool IsExplicit {
  122. get { return !isImplicit; }
  123. }
  124. public override bool IsNumericConversion {
  125. get { return !isEnumeration; }
  126. }
  127. public override bool IsEnumerationConversion {
  128. get { return isEnumeration; }
  129. }
  130. public override bool IsLifted {
  131. get { return isLifted; }
  132. }
  133. public override string ToString()
  134. {
  135. return (isImplicit ? "implicit" : "explicit")
  136. + (isLifted ? " lifted" : "")
  137. + (isEnumeration ? " enumeration" : " numeric")
  138. + " conversion";
  139. }
  140. public override bool Equals(Conversion other)
  141. {
  142. NumericOrEnumerationConversion o = other as NumericOrEnumerationConversion;
  143. return o != null && isImplicit == o.isImplicit && isLifted == o.isLifted && isEnumeration == o.isEnumeration;
  144. }
  145. public override int GetHashCode()
  146. {
  147. return (isImplicit ? 1 : 0) + (isLifted ? 2 : 0) + (isEnumeration ? 4 : 0);
  148. }
  149. }
  150. sealed class BuiltinConversion : Conversion
  151. {
  152. readonly bool isImplicit;
  153. readonly byte type;
  154. public BuiltinConversion(bool isImplicit, byte type)
  155. {
  156. this.isImplicit = isImplicit;
  157. this.type = type;
  158. }
  159. public override bool IsImplicit {
  160. get { return isImplicit; }
  161. }
  162. public override bool IsExplicit {
  163. get { return !isImplicit; }
  164. }
  165. public override bool IsIdentityConversion {
  166. get { return type == 0; }
  167. }
  168. public override bool IsNullLiteralConversion {
  169. get { return type == 1; }
  170. }
  171. public override bool IsConstantExpressionConversion {
  172. get { return type == 2; }
  173. }
  174. public override bool IsReferenceConversion {
  175. get { return type == 3; }
  176. }
  177. public override bool IsDynamicConversion {
  178. get { return type == 4; }
  179. }
  180. public override bool IsNullableConversion {
  181. get { return type == 5; }
  182. }
  183. public override bool IsPointerConversion {
  184. get { return type == 6; }
  185. }
  186. public override bool IsBoxingConversion {
  187. get { return type == 7; }
  188. }
  189. public override bool IsUnboxingConversion {
  190. get { return type == 8; }
  191. }
  192. public override bool IsTryCast {
  193. get { return type == 9; }
  194. }
  195. public override bool IsInterpolatedStringConversion => type == 10;
  196. public override bool IsThrowExpressionConversion {
  197. get { return type == 11; }
  198. }
  199. public override string ToString()
  200. {
  201. string name = null;
  202. switch (type)
  203. {
  204. case 0:
  205. return "identity conversion";
  206. case 1:
  207. return "null-literal conversion";
  208. case 2:
  209. name = "constant-expression";
  210. break;
  211. case 3:
  212. name = "reference";
  213. break;
  214. case 4:
  215. name = "dynamic";
  216. break;
  217. case 5:
  218. name = "nullable";
  219. break;
  220. case 6:
  221. name = "pointer";
  222. break;
  223. case 7:
  224. return "boxing conversion";
  225. case 8:
  226. return "unboxing conversion";
  227. case 9:
  228. return "try cast";
  229. case 10:
  230. return "interpolated string";
  231. case 11:
  232. return "throw-expression conversion";
  233. }
  234. return (isImplicit ? "implicit " : "explicit ") + name + " conversion";
  235. }
  236. }
  237. sealed class UserDefinedConv : Conversion
  238. {
  239. readonly IMethod method;
  240. readonly bool isLifted;
  241. readonly Conversion conversionBeforeUserDefinedOperator;
  242. readonly Conversion conversionAfterUserDefinedOperator;
  243. readonly bool isImplicit;
  244. readonly bool isValid;
  245. public UserDefinedConv(bool isImplicit, IMethod method, Conversion conversionBeforeUserDefinedOperator, Conversion conversionAfterUserDefinedOperator, bool isLifted, bool isAmbiguous)
  246. {
  247. this.method = method;
  248. this.isLifted = isLifted;
  249. this.conversionBeforeUserDefinedOperator = conversionBeforeUserDefinedOperator;
  250. this.conversionAfterUserDefinedOperator = conversionAfterUserDefinedOperator;
  251. this.isImplicit = isImplicit;
  252. this.isValid = !isAmbiguous;
  253. }
  254. public override bool IsValid {
  255. get { return isValid; }
  256. }
  257. public override bool IsImplicit {
  258. get { return isImplicit; }
  259. }
  260. public override bool IsExplicit {
  261. get { return !isImplicit; }
  262. }
  263. public override bool IsLifted {
  264. get { return isLifted; }
  265. }
  266. public override bool IsUserDefined {
  267. get { return true; }
  268. }
  269. public override Conversion ConversionBeforeUserDefinedOperator {
  270. get { return conversionBeforeUserDefinedOperator; }
  271. }
  272. public override Conversion ConversionAfterUserDefinedOperator {
  273. get { return conversionAfterUserDefinedOperator; }
  274. }
  275. public override IMethod Method {
  276. get { return method; }
  277. }
  278. public override bool Equals(Conversion other)
  279. {
  280. UserDefinedConv o = other as UserDefinedConv;
  281. return o != null && isLifted == o.isLifted && isImplicit == o.isImplicit && isValid == o.isValid && method.Equals(o.method);
  282. }
  283. public override int GetHashCode()
  284. {
  285. return unchecked(method.GetHashCode() + (isLifted ? 31 : 27) + (isImplicit ? 71 : 61) + (isValid ? 107 : 109));
  286. }
  287. public override string ToString()
  288. {
  289. return (isImplicit ? "implicit" : "explicit")
  290. + (isLifted ? " lifted" : "")
  291. + (isValid ? "" : " ambiguous")
  292. + "user-defined conversion (" + method + ")";
  293. }
  294. }
  295. sealed class MethodGroupConv : Conversion
  296. {
  297. readonly IMethod method;
  298. readonly bool isVirtualMethodLookup;
  299. readonly bool delegateCapturesFirstArgument;
  300. readonly bool isValid;
  301. public MethodGroupConv(IMethod method, bool isVirtualMethodLookup, bool delegateCapturesFirstArgument, bool isValid)
  302. {
  303. this.method = method;
  304. this.isVirtualMethodLookup = isVirtualMethodLookup;
  305. this.delegateCapturesFirstArgument = delegateCapturesFirstArgument;
  306. this.isValid = isValid;
  307. }
  308. public override bool IsValid {
  309. get { return isValid; }
  310. }
  311. public override bool IsImplicit {
  312. get { return true; }
  313. }
  314. public override bool IsMethodGroupConversion {
  315. get { return true; }
  316. }
  317. public override bool IsVirtualMethodLookup {
  318. get { return isVirtualMethodLookup; }
  319. }
  320. public override bool DelegateCapturesFirstArgument {
  321. get { return delegateCapturesFirstArgument; }
  322. }
  323. public override IMethod Method {
  324. get { return method; }
  325. }
  326. public override bool Equals(Conversion other)
  327. {
  328. MethodGroupConv o = other as MethodGroupConv;
  329. return o != null && method.Equals(o.method);
  330. }
  331. public override int GetHashCode()
  332. {
  333. return method.GetHashCode();
  334. }
  335. }
  336. sealed class TupleConv : Conversion
  337. {
  338. public override bool IsImplicit { get; }
  339. public override bool IsExplicit => !IsImplicit;
  340. public override ImmutableArray<Conversion> ElementConversions { get; }
  341. public override bool IsTupleConversion => true;
  342. public TupleConv(ImmutableArray<Conversion> elementConversions)
  343. {
  344. this.ElementConversions = elementConversions;
  345. this.IsImplicit = elementConversions.All(c => c.IsImplicit);
  346. }
  347. public override bool Equals(Conversion other)
  348. {
  349. return other is TupleConv o
  350. && ElementConversions.SequenceEqual(o.ElementConversions);
  351. }
  352. public override int GetHashCode()
  353. {
  354. unchecked
  355. {
  356. int hash = 0;
  357. foreach (var conv in ElementConversions)
  358. {
  359. hash *= 31;
  360. hash += conv.GetHashCode();
  361. }
  362. return hash;
  363. }
  364. }
  365. public override string ToString()
  366. {
  367. return (IsImplicit ? "implicit " : "explicit ") + " tuple conversion";
  368. }
  369. }
  370. #endregion
  371. /// <summary>
  372. /// Gets whether the conversion is valid.
  373. /// </summary>
  374. public virtual bool IsValid {
  375. get { return true; }
  376. }
  377. public virtual bool IsImplicit {
  378. get { return false; }
  379. }
  380. public virtual bool IsExplicit {
  381. get { return false; }
  382. }
  383. /// <summary>
  384. /// Gets whether the conversion is an '<c>as</c>' cast.
  385. /// </summary>
  386. public virtual bool IsTryCast {
  387. get { return false; }
  388. }
  389. public virtual bool IsThrowExpressionConversion {
  390. get { return false; }
  391. }
  392. public virtual bool IsIdentityConversion {
  393. get { return false; }
  394. }
  395. public virtual bool IsNullLiteralConversion {
  396. get { return false; }
  397. }
  398. public virtual bool IsConstantExpressionConversion {
  399. get { return false; }
  400. }
  401. public virtual bool IsNumericConversion {
  402. get { return false; }
  403. }
  404. /// <summary>
  405. /// Gets whether this conversion is a lifted version of another conversion.
  406. /// </summary>
  407. public virtual bool IsLifted {
  408. get { return false; }
  409. }
  410. /// <summary>
  411. /// Gets whether the conversion is dynamic.
  412. /// </summary>
  413. public virtual bool IsDynamicConversion {
  414. get { return false; }
  415. }
  416. /// <summary>
  417. /// Gets whether the conversion is a reference conversion.
  418. /// </summary>
  419. public virtual bool IsReferenceConversion {
  420. get { return false; }
  421. }
  422. /// <summary>
  423. /// Gets whether the conversion is an enumeration conversion.
  424. /// </summary>
  425. public virtual bool IsEnumerationConversion {
  426. get { return false; }
  427. }
  428. /// <summary>
  429. /// Gets whether the conversion is a nullable conversion
  430. /// (conversion between a nullable type and the regular type).
  431. /// </summary>
  432. public virtual bool IsNullableConversion {
  433. get { return false; }
  434. }
  435. /// <summary>
  436. /// Gets whether this conversion is user-defined (op_Implicit or op_Explicit).
  437. /// </summary>
  438. public virtual bool IsUserDefined {
  439. get { return false; }
  440. }
  441. /// <summary>
  442. /// The conversion that is applied to the input before the user-defined conversion operator is invoked.
  443. /// </summary>
  444. public virtual Conversion ConversionBeforeUserDefinedOperator {
  445. get { return null; }
  446. }
  447. /// <summary>
  448. /// The conversion that is applied to the result of the user-defined conversion operator.
  449. /// </summary>
  450. public virtual Conversion ConversionAfterUserDefinedOperator {
  451. get { return null; }
  452. }
  453. /// <summary>
  454. /// Gets whether this conversion is a boxing conversion.
  455. /// </summary>
  456. public virtual bool IsBoxingConversion {
  457. get { return false; }
  458. }
  459. /// <summary>
  460. /// Gets whether this conversion is an unboxing conversion.
  461. /// </summary>
  462. public virtual bool IsUnboxingConversion {
  463. get { return false; }
  464. }
  465. /// <summary>
  466. /// Gets whether this conversion is a pointer conversion.
  467. /// </summary>
  468. public virtual bool IsPointerConversion {
  469. get { return false; }
  470. }
  471. /// <summary>
  472. /// Gets whether this conversion is a method group conversion.
  473. /// </summary>
  474. public virtual bool IsMethodGroupConversion {
  475. get { return false; }
  476. }
  477. /// <summary>
  478. /// For method-group conversions, gets whether to perform a virtual method lookup at runtime.
  479. /// </summary>
  480. public virtual bool IsVirtualMethodLookup {
  481. get { return false; }
  482. }
  483. /// <summary>
  484. /// For method-group conversions, gets whether the conversion captures the first argument.
  485. ///
  486. /// For instance methods, this property always returns true for C# method-group conversions.
  487. /// For static methods, this property returns true for method-group conversions of an extension method performed on an instance (eg. <c>Func&lt;int&gt; f = myEnumerable.Single</c>).
  488. /// </summary>
  489. public virtual bool DelegateCapturesFirstArgument {
  490. get { return false; }
  491. }
  492. /// <summary>
  493. /// Gets whether this conversion is an anonymous function conversion.
  494. /// </summary>
  495. public virtual bool IsAnonymousFunctionConversion {
  496. get { return false; }
  497. }
  498. /// <summary>
  499. /// Gets the method associated with this conversion.
  500. /// For user-defined conversions, this is the method being called.
  501. /// For method-group conversions, this is the method that was chosen from the group.
  502. /// </summary>
  503. public virtual IMethod Method {
  504. get { return null; }
  505. }
  506. /// <summary>
  507. /// Gets whether this conversion is a tuple conversion.
  508. /// </summary>
  509. public virtual bool IsTupleConversion => false;
  510. /// <summary>
  511. /// Gets whether this is an interpolated string conversion to <see cref="IFormattable" /> or <see cref="FormattableString"/>.
  512. /// </summary>
  513. public virtual bool IsInterpolatedStringConversion => false;
  514. /// <summary>
  515. /// For a tuple conversion, gets the individual tuple element conversions.
  516. /// </summary>
  517. public virtual ImmutableArray<Conversion> ElementConversions => default(ImmutableArray<Conversion>);
  518. public override sealed bool Equals(object obj)
  519. {
  520. return Equals(obj as Conversion);
  521. }
  522. public override int GetHashCode()
  523. {
  524. return base.GetHashCode();
  525. }
  526. public virtual bool Equals(Conversion other)
  527. {
  528. return this == other;
  529. }
  530. }
  531. }