Browse Source

Fix #1574: When re-introducing an explicit cast for an implicit user-defined conversion; ensure we use a direct cast and don't go through the code path for builtin conversions.

pull/1596/head
Daniel Grunwald 6 years ago
parent
commit
e6489d543e
  1. 1
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  2. 6
      ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
  3. 134
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/UserDefinedConversions.cs
  4. 14
      ICSharpCode.Decompiler/CSharp/Annotations.cs
  5. 23
      ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs

1
ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj

@ -89,6 +89,7 @@
<Compile Include="TestCases\ILPretty\Issue1323.cs" /> <Compile Include="TestCases\ILPretty\Issue1323.cs" />
<Compile Include="TestCases\Pretty\CustomAttributes2.cs" /> <Compile Include="TestCases\Pretty\CustomAttributes2.cs" />
<Compile Include="TestCases\Pretty\EnumTests.cs" /> <Compile Include="TestCases\Pretty\EnumTests.cs" />
<Compile Include="TestCases\Pretty\UserDefinedConversions.cs" />
<None Include="TestCases\ILPretty\Unsafe.il" /> <None Include="TestCases\ILPretty\Unsafe.il" />
<None Include="TestCases\Pretty\NullableRefTypes.cs" /> <None Include="TestCases\Pretty\NullableRefTypes.cs" />
<Compile Include="TestCases\Pretty\TypeMemberTests.cs" /> <Compile Include="TestCases\Pretty\TypeMemberTests.cs" />

6
ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs

@ -425,6 +425,12 @@ namespace ICSharpCode.Decompiler.Tests
RunForLibrary(cscOptions: cscOptions); RunForLibrary(cscOptions: cscOptions);
} }
[Test]
public void UserDefinedConversions([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}
[Test] [Test]
public void Discards([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions) public void Discards([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions)
{ {

134
ICSharpCode.Decompiler.Tests/TestCases/Pretty/UserDefinedConversions.cs

@ -0,0 +1,134 @@
// Copyright (c) 2019 Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
internal class T01Issue1574
{
private struct A
{
private bool val;
public static implicit operator bool(A a)
{
return a.val;
}
}
private struct C
{
private int val;
public static implicit operator C(bool b)
{
return default(C);
}
}
private C ChainedConversion()
{
return (bool)default(A);
}
public void Call_Overloaded()
{
Overloaded((bool)default(A));
}
private void Overloaded(A a)
{
}
private void Overloaded(bool a)
{
}
}
internal class T02BothDirectAndChainedConversionPossible
{
private struct A
{
private bool val;
public static implicit operator bool(A a)
{
return a.val;
}
}
private struct C
{
private int val;
public static implicit operator C(bool b)
{
return default(C);
}
public static implicit operator C(A a)
{
return default(C);
}
public static bool operator ==(C a, C b)
{
return true;
}
public static bool operator !=(C a, C b)
{
return false;
}
}
private C DirectConvert(A a)
{
return a;
}
private C IndirectConvert(A a)
{
return (bool)a;
}
private C? LiftedDirectConvert(A? a)
{
return a;
}
private C? LiftedIndirectConvert(A? a)
{
return (bool?)a;
}
private bool Compare(A a, C c)
{
return a == c;
}
private void LiftedCompare(A? a, C? c)
{
UseBool(a == c);
UseBool(a == default(C));
UseBool(c == default(A));
}
private void UseBool(bool b)
{
}
}
}

14
ICSharpCode.Decompiler/CSharp/Annotations.cs

@ -240,4 +240,18 @@ namespace ICSharpCode.Decompiler.CSharp
this.Leave = leave; this.Leave = leave;
} }
} }
/// <summary>
/// Annotates an expression when an implicit user-defined conversion was omitted.
/// </summary>
public class ImplicitConversionAnnotation
{
public readonly ConversionResolveResult ConversionResolveResult;
public IType TargetType => ConversionResolveResult.Type;
public ImplicitConversionAnnotation(ConversionResolveResult conversionResolveResult)
{
this.ConversionResolveResult = conversionResolveResult;
}
}
} }

23
ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs

@ -191,7 +191,11 @@ namespace ICSharpCode.Decompiler.CSharp
conversion.Input.Type, conversion.Input.Type,
type, targetType type, targetType
)) { )) {
return this.UnwrapChild(cast.Expression); var result = this.UnwrapChild(cast.Expression);
if (conversion.Conversion.IsUserDefined) {
result.Expression.AddAnnotation(new ImplicitConversionAnnotation(conversion));
}
return result;
} else if (Expression is ObjectCreateExpression oce && conversion.Conversion.IsMethodGroupConversion } else if (Expression is ObjectCreateExpression oce && conversion.Conversion.IsMethodGroupConversion
&& oce.Arguments.Count == 1 && expressionBuilder.settings.UseImplicitMethodGroupConversion) { && oce.Arguments.Count == 1 && expressionBuilder.settings.UseImplicitMethodGroupConversion) {
return this.UnwrapChild(oce.Arguments.Single()); return this.UnwrapChild(oce.Arguments.Single());
@ -211,6 +215,18 @@ namespace ICSharpCode.Decompiler.CSharp
if (targetType.Kind == TypeKind.Unknown || targetType.Kind == TypeKind.Void || targetType.Kind == TypeKind.None) { if (targetType.Kind == TypeKind.Unknown || targetType.Kind == TypeKind.Void || targetType.Kind == TypeKind.None) {
return this; // don't attempt to insert cast to '?' or 'void' as these are not valid. return this; // don't attempt to insert cast to '?' or 'void' as these are not valid.
} }
var convAnnotation = this.Expression.Annotation<ImplicitConversionAnnotation>();
if (convAnnotation != null) {
// If an implicit user-defined conversion was stripped from this expression;
// it needs to be re-introduced before we can apply other casts to this expression.
// This happens when the CallBuilder discovers that the conversion is necessary in
// order to choose the correct overload.
this.Expression.RemoveAnnotations<ImplicitConversionAnnotation>();
return new CastExpression(expressionBuilder.ConvertType(convAnnotation.TargetType), Expression)
.WithoutILInstruction()
.WithRR(convAnnotation.ConversionResolveResult)
.ConvertTo(targetType, expressionBuilder, checkForOverflow, allowImplicitConversion);
}
if (Expression is TupleExpression tupleExpr && targetType is TupleType targetTupleType if (Expression is TupleExpression tupleExpr && targetType is TupleType targetTupleType
&& tupleExpr.Elements.Count == targetTupleType.ElementTypes.Length) && tupleExpr.Elements.Count == targetTupleType.ElementTypes.Length)
{ {
@ -231,8 +247,9 @@ namespace ICSharpCode.Decompiler.CSharp
} }
var compilation = expressionBuilder.compilation; var compilation = expressionBuilder.compilation;
var conversions = Resolver.CSharpConversions.Get(compilation); var conversions = Resolver.CSharpConversions.Get(compilation);
if (ResolveResult is ConversionResolveResult conv && Expression is CastExpression cast2 && if (ResolveResult is ConversionResolveResult conv && Expression is CastExpression cast2
CastCanBeMadeImplicit(conversions, conv.Conversion, conv.Input.Type, type, targetType)) && !conv.Conversion.IsUserDefined
&& CastCanBeMadeImplicit(conversions, conv.Conversion, conv.Input.Type, type, targetType))
{ {
var unwrapped = this.UnwrapChild(cast2.Expression); var unwrapped = this.UnwrapChild(cast2.Expression);
if (allowImplicitConversion) if (allowImplicitConversion)

Loading…
Cancel
Save