Browse Source

Fix #1944: Add support for implicit conversions with `in` parameters.

pull/2301/head
Daniel Grunwald 4 years ago
parent
commit
514cf9b03b
  1. 25
      ICSharpCode.Decompiler.Tests/TestCases/Correctness/OverloadResolution.cs
  2. 47
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/UserDefinedConversions.cs
  3. 6
      ICSharpCode.Decompiler/CSharp/CallBuilder.cs
  4. 17
      ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs

25
ICSharpCode.Decompiler.Tests/TestCases/Correctness/OverloadResolution.cs

@ -35,6 +35,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -35,6 +35,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
Issue1281();
Issue1747();
CallAmbiguousOutParam();
CallWithInParam();
}
#region ConstructorTest
@ -270,6 +271,30 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -270,6 +271,30 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
AmbiguousOutParam(out b);
}
#endregion
#region In Parameter
static void CallWithInParam()
{
Console.WriteLine("OverloadSetWithInParam:");
#if CS72
OverloadSetWithInParam(1);
OverloadSetWithInParam(2L);
int i = 3;
OverloadSetWithInParam(in i);
#endif
}
#if CS72
static void OverloadSetWithInParam(in int i)
{
Console.WriteLine("in int " + i);
}
static void OverloadSetWithInParam(long l)
{
Console.WriteLine("long " + l);
}
#endif
#endregion
}
class IndexerTests

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

@ -131,4 +131,51 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -131,4 +131,51 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
}
}
#if CS72
internal class T03ConversionWithInArgument
{
private struct T
{
private byte dummy;
public static implicit operator T(in int val)
{
return default(T);
}
public static explicit operator T(in long val)
{
return default(T);
}
}
private struct U
{
private byte dummy;
public static implicit operator T(in U u)
{
return default(T);
}
public static explicit operator int(in U u)
{
return 0;
}
}
private void UseT(T t)
{
}
private int Test(int i, long l, U u)
{
UseT(i);
UseT((T)l);
UseT(u);
return (int)u;
}
}
#endif
}

6
ICSharpCode.Decompiler/CSharp/CallBuilder.cs

@ -1154,6 +1154,12 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1154,6 +1154,12 @@ namespace ICSharpCode.Decompiler.CSharp
argument = argument.ConvertTo(method.Parameters[0].Type, expressionBuilder);
conv = conversions.ImplicitConversion(argument.Type, targetType);
}
if (argument.Expression is DirectionExpression { FieldDirection: FieldDirection.In, Expression: var lvalueExpr })
{
// `(TargetType)(in arg)` is invalid syntax.
// Also, `f(in arg)` is invalid when there's an implicit conversion involved.
argument = argument.UnwrapChild(lvalueExpr);
}
return new CastExpression(expressionBuilder.ConvertType(targetType), argument.Expression)
.WithRR(new ConversionResolveResult(targetType, argument.ResolveResult, conv));
}

17
ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs

@ -1103,6 +1103,15 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -1103,6 +1103,15 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
}
}
static IType UnderlyingTypeForConversion(IType type)
{
if (type.Kind == TypeKind.ByReference)
{
type = ((ByReferenceType)type).ElementType;
}
return NullableType.GetUnderlyingType(type);
}
List<OperatorInfo> GetApplicableConversionOperators(ResolveResult fromResult, IType fromType, IType toType, bool isExplicit)
{
// Find the candidate operators:
@ -1112,13 +1121,17 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -1112,13 +1121,17 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
else
opFilter = m => m.IsStatic && m.IsOperator && m.Name == "op_Implicit" && m.Parameters.Count == 1;
var operators = NullableType.GetUnderlyingType(fromType).GetMethods(opFilter)
.Concat(NullableType.GetUnderlyingType(toType).GetMethods(opFilter)).Distinct();
var operators = UnderlyingTypeForConversion(fromType).GetMethods(opFilter)
.Concat(UnderlyingTypeForConversion(toType).GetMethods(opFilter)).Distinct();
// Determine whether one of them is applicable:
List<OperatorInfo> result = new List<OperatorInfo>();
foreach (IMethod op in operators)
{
IType sourceType = op.Parameters[0].Type;
if (sourceType.Kind == TypeKind.ByReference && op.Parameters[0].IsIn && fromType.Kind != TypeKind.ByReference)
{
sourceType = ((ByReferenceType)sourceType).ElementType;
}
IType targetType = op.ReturnType;
// Try if the operator is applicable:
bool isApplicable;

Loading…
Cancel
Save