Skip to content

Commit

Permalink
Make sure that there is only one applicable implicit conversion when …
Browse files Browse the repository at this point in the history
…dealing with switch, otherwise use an explicit cast.
  • Loading branch information
siegfriedpammer committed Jan 5, 2025
1 parent e1e2f73 commit 7c6f7fe
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 15 deletions.
83 changes: 69 additions & 14 deletions ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3900,38 +3900,93 @@ protected internal override TranslatedExpression VisitIfInstruction(IfInstructio
}
}

internal (TranslatedExpression, IType, StringToInt) TranslateSwitchValue(SwitchInstruction inst, bool allowImplicitConversion)
internal (TranslatedExpression, IType, StringToInt) TranslateSwitchValue(SwitchInstruction inst, bool isExpressionContext)
{
TranslatedExpression value;
IType type;
// prepare expression and expected type
if (inst.Value is StringToInt strToInt)
{
value = Translate(strToInt.Argument)
.ConvertTo(
strToInt.ExpectedType,
this,
allowImplicitConversion: allowImplicitConversion
);
type = compilation.FindType(KnownTypeCode.String);
value = Translate(strToInt.Argument);
type = strToInt.ExpectedType ?? compilation.FindType(KnownTypeCode.String);
}
else
{
strToInt = null;
value = Translate(inst.Value);
type = value.Type;
if (inst.Type != null)
type = inst.Type ?? value.Type;
}

// find and unwrap the input type
IType inputType = value.Type;
if (value.Expression is CastExpression && value.ResolveResult is ConversionResolveResult crr)
{
inputType = crr.Input.Type;
}
inputType = NullableType.GetUnderlyingType(inputType).GetEnumUnderlyingType();

// check input/underlying type for compatibility
bool allowImplicitConversion;
if (IsCompatibleWithSwitch(inputType) || (strToInt != null && inputType.Equals(type)))
{
allowImplicitConversion = !isExpressionContext;
}
else
{
var applicableImplicitConversionOperators = inputType.GetMethods(IsCompatibleImplicitConversionOperator).ToArray();
switch (applicableImplicitConversionOperators.Length)
{
value = value.ConvertTo(inst.Type, this, allowImplicitConversion: allowImplicitConversion);
type = inst.Type;
case 0:
allowImplicitConversion = !isExpressionContext;
break;
case 1:
allowImplicitConversion = !isExpressionContext;
// TODO validate
break;
default:
allowImplicitConversion = false;
break;
}
}
return (value, type, strToInt);

value = value.ConvertTo(type, this, allowImplicitConversion: allowImplicitConversion);

var caseType = strToInt != null
? compilation.FindType(KnownTypeCode.String)
: type;

return (value, caseType, strToInt);

static bool IsCompatibleWithSwitch(IType type)
{
return type.IsKnownType(KnownTypeCode.SByte)
|| type.IsKnownType(KnownTypeCode.Byte)
|| type.IsKnownType(KnownTypeCode.Int16)
|| type.IsKnownType(KnownTypeCode.UInt16)
|| type.IsKnownType(KnownTypeCode.Int32)
|| type.IsKnownType(KnownTypeCode.UInt32)
|| type.IsKnownType(KnownTypeCode.Int64)
|| type.IsKnownType(KnownTypeCode.UInt64)
|| type.IsKnownType(KnownTypeCode.Char)
|| type.IsKnownType(KnownTypeCode.String);
}

bool IsCompatibleImplicitConversionOperator(IMethod operatorMethod)
{
if (!operatorMethod.IsOperator)
return false;
if (operatorMethod.Name != "op_Implicit")
return false;
if (operatorMethod.Parameters.Count != 1)
return false;
return IsCompatibleWithSwitch(operatorMethod.ReturnType);
}
}

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

IL.SwitchSection defaultSection = inst.GetDefaultSection();
SwitchExpression switchExpr = new SwitchExpression();
Expand Down
2 changes: 1 addition & 1 deletion ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ SwitchStatement TranslateSwitch(BlockContainer switchContainer, SwitchInstructio
var oldCaseLabelMapping = caseLabelMapping;
caseLabelMapping = new Dictionary<Block, ConstantResolveResult>();

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

IL.SwitchSection defaultSection = inst.GetDefaultSection();

Expand Down

0 comments on commit 7c6f7fe

Please sign in to comment.