Browse Source

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

pull/2301/head
Daniel Grunwald 5 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
Issue1281(); Issue1281();
Issue1747(); Issue1747();
CallAmbiguousOutParam(); CallAmbiguousOutParam();
CallWithInParam();
} }
#region ConstructorTest #region ConstructorTest
@ -270,6 +271,30 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
AmbiguousOutParam(out b); AmbiguousOutParam(out b);
} }
#endregion #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 class IndexerTests

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

@ -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
argument = argument.ConvertTo(method.Parameters[0].Type, expressionBuilder); argument = argument.ConvertTo(method.Parameters[0].Type, expressionBuilder);
conv = conversions.ImplicitConversion(argument.Type, targetType); 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) return new CastExpression(expressionBuilder.ConvertType(targetType), argument.Expression)
.WithRR(new ConversionResolveResult(targetType, argument.ResolveResult, conv)); .WithRR(new ConversionResolveResult(targetType, argument.ResolveResult, conv));
} }

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

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

Loading…
Cancel
Save