Skip to content

Commit 7c6f7fe

Browse files
Make sure that there is only one applicable implicit conversion when dealing with switch, otherwise use an explicit cast.
1 parent e1e2f73 commit 7c6f7fe

2 files changed

Lines changed: 70 additions & 15 deletions

File tree

ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

Lines changed: 69 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3900,38 +3900,93 @@ protected internal override TranslatedExpression VisitIfInstruction(IfInstructio
39003900
}
39013901
}
39023902

3903-
internal (TranslatedExpression, IType, StringToInt) TranslateSwitchValue(SwitchInstruction inst, bool allowImplicitConversion)
3903+
internal (TranslatedExpression, IType, StringToInt) TranslateSwitchValue(SwitchInstruction inst, bool isExpressionContext)
39043904
{
39053905
TranslatedExpression value;
39063906
IType type;
3907+
// prepare expression and expected type
39073908
if (inst.Value is StringToInt strToInt)
39083909
{
3909-
value = Translate(strToInt.Argument)
3910-
.ConvertTo(
3911-
strToInt.ExpectedType,
3912-
this,
3913-
allowImplicitConversion: allowImplicitConversion
3914-
);
3915-
type = compilation.FindType(KnownTypeCode.String);
3910+
value = Translate(strToInt.Argument);
3911+
type = strToInt.ExpectedType ?? compilation.FindType(KnownTypeCode.String);
39163912
}
39173913
else
39183914
{
39193915
strToInt = null;
39203916
value = Translate(inst.Value);
3921-
type = value.Type;
3922-
if (inst.Type != null)
3917+
type = inst.Type ?? value.Type;
3918+
}
3919+
3920+
// find and unwrap the input type
3921+
IType inputType = value.Type;
3922+
if (value.Expression is CastExpression && value.ResolveResult is ConversionResolveResult crr)
3923+
{
3924+
inputType = crr.Input.Type;
3925+
}
3926+
inputType = NullableType.GetUnderlyingType(inputType).GetEnumUnderlyingType();
3927+
3928+
// check input/underlying type for compatibility
3929+
bool allowImplicitConversion;
3930+
if (IsCompatibleWithSwitch(inputType) || (strToInt != null && inputType.Equals(type)))
3931+
{
3932+
allowImplicitConversion = !isExpressionContext;
3933+
}
3934+
else
3935+
{
3936+
var applicableImplicitConversionOperators = inputType.GetMethods(IsCompatibleImplicitConversionOperator).ToArray();
3937+
switch (applicableImplicitConversionOperators.Length)
39233938
{
3924-
value = value.ConvertTo(inst.Type, this, allowImplicitConversion: allowImplicitConversion);
3925-
type = inst.Type;
3939+
case 0:
3940+
allowImplicitConversion = !isExpressionContext;
3941+
break;
3942+
case 1:
3943+
allowImplicitConversion = !isExpressionContext;
3944+
// TODO validate
3945+
break;
3946+
default:
3947+
allowImplicitConversion = false;
3948+
break;
39263949
}
39273950
}
3928-
return (value, type, strToInt);
3951+
3952+
value = value.ConvertTo(type, this, allowImplicitConversion: allowImplicitConversion);
3953+
3954+
var caseType = strToInt != null
3955+
? compilation.FindType(KnownTypeCode.String)
3956+
: type;
3957+
3958+
return (value, caseType, strToInt);
3959+
3960+
static bool IsCompatibleWithSwitch(IType type)
3961+
{
3962+
return type.IsKnownType(KnownTypeCode.SByte)
3963+
|| type.IsKnownType(KnownTypeCode.Byte)
3964+
|| type.IsKnownType(KnownTypeCode.Int16)
3965+
|| type.IsKnownType(KnownTypeCode.UInt16)
3966+
|| type.IsKnownType(KnownTypeCode.Int32)
3967+
|| type.IsKnownType(KnownTypeCode.UInt32)
3968+
|| type.IsKnownType(KnownTypeCode.Int64)
3969+
|| type.IsKnownType(KnownTypeCode.UInt64)
3970+
|| type.IsKnownType(KnownTypeCode.Char)
3971+
|| type.IsKnownType(KnownTypeCode.String);
3972+
}
3973+
3974+
bool IsCompatibleImplicitConversionOperator(IMethod operatorMethod)
3975+
{
3976+
if (!operatorMethod.IsOperator)
3977+
return false;
3978+
if (operatorMethod.Name != "op_Implicit")
3979+
return false;
3980+
if (operatorMethod.Parameters.Count != 1)
3981+
return false;
3982+
return IsCompatibleWithSwitch(operatorMethod.ReturnType);
3983+
}
39293984
}
39303985

39313986
protected internal override TranslatedExpression VisitSwitchInstruction(SwitchInstruction inst, TranslationContext context)
39323987
{
39333988
// switch-expression does not support implicit conversions
3934-
var (value, type, strToInt) = TranslateSwitchValue(inst, allowImplicitConversion: false);
3989+
var (value, type, strToInt) = TranslateSwitchValue(inst, true);
39353990

39363991
IL.SwitchSection defaultSection = inst.GetDefaultSection();
39373992
SwitchExpression switchExpr = new SwitchExpression();

ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ SwitchStatement TranslateSwitch(BlockContainer switchContainer, SwitchInstructio
210210
var oldCaseLabelMapping = caseLabelMapping;
211211
caseLabelMapping = new Dictionary<Block, ConstantResolveResult>();
212212

213-
var (value, type, strToInt) = exprBuilder.TranslateSwitchValue(inst, allowImplicitConversion: true);
213+
var (value, type, strToInt) = exprBuilder.TranslateSwitchValue(inst, false);
214214

215215
IL.SwitchSection defaultSection = inst.GetDefaultSection();
216216

0 commit comments

Comments
 (0)