Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CallBuilder produces strange cast on call target, in case two very similar overloads are present #2741

Closed
CreateAndInject opened this issue Jul 11, 2022 · 5 comments
Labels
Bug Decompiler The decompiler engine itself

Comments

@CreateAndInject
Copy link
Contributor

StrangeCast.zip

See XpoTspecInsulInfo2021:

public int XpoTspecInsulInfo2021Id
{
	get
	{
		return _fXpoTspecInsulInfo2021Id;
	}
	set
	{
		((PersistentBase)this).SetPropertyValue<int>("XpoTspecInsulInfo2021Id", ref _fXpoTspecInsulInfo2021Id, value);
	}
}

There's ((PersistentBase)this), but there's no such issue in the other properties defined in the same type

@CreateAndInject CreateAndInject added Bug Decompiler The decompiler engine itself labels Jul 11, 2022
@siegfriedpammer
Copy link
Member

The problem here is that there are two very similar methods defined in the base class PersistentBase.

protected bool SetPropertyValue<T>(string propertyName, ref T propertyValueHolder, T newValue);
protected bool SetPropertyValue(string propertyName, ref int propertyValueHolder, int newValue);

The IL tells us that the generic overload should be used:

IL_000d: call instance bool PersistentBase::SetPropertyValue<int32>(string, !!0&, !!0) /* 2B000081 */

And while it would be sufficient to specify generic type arguments to disambiguate in this case, our algorithm is not clever enough to try that combination:

// TODO : implement some more intelligent algorithm that decides which of these fixes (cast args, add target, cast target, add type args)
// is best in this case. Additionally we should not cast all arguments at once, but step-by-step try to add only a minimal number of casts.
if (argumentList.FirstOptionalArgumentIndex >= 0)
{
argumentList.FirstOptionalArgumentIndex = -1;
}
else if (!argumentsCasted)
{
// If we added type arguments beforehand, but that didn't make the code any better,
// undo that decision and add casts first.
if (appliedRequireTypeArgumentsShortcut)
{
requireTypeArguments = false;
typeArguments = Empty<IType>.Array;
appliedRequireTypeArgumentsShortcut = false;
}
argumentsCasted = true;
argumentList.UseImplicitlyTypedOut = false;
CastArguments(argumentList.Arguments, argumentList.ExpectedParameters);
}
else if ((allowedTransforms & CallTransformation.RequireTarget) != 0 && !requireTarget)
{
requireTarget = true;
targetResolveResult = target.ResolveResult;
}
else if ((allowedTransforms & CallTransformation.RequireTarget) != 0 && !targetCasted)
{
targetCasted = true;
target = target.ConvertTo(method.DeclaringType, expressionBuilder);
targetResolveResult = target.ResolveResult;
}
else if ((allowedTransforms & CallTransformation.RequireTypeArguments) != 0 && !requireTypeArguments)
{
requireTypeArguments = true;
typeArguments = method.TypeArguments.ToArray();
}
else
{
break;
}
continue;

We try the following things (in that order):

  1. cast all arguments to the expected types
  2. add call target, if it was optional
  3. cast the target to the exact type
  4. add type arguments explicitly

If steps 2/3 and 4 are swapped, then the produced code should just be:

SetPropertyValue<int>("XpoTspecInsulInfo2021Id", ref _fXpoTspecInsulInfo2021Id, value);

However, I think there are many cases where this change would make the code look a lot worse... especially if the method has more than one type argument, or the type argument is a generic type with multiple levels of nesting.
Also note that there is already a TODO note on that issue on line 991.

@siegfriedpammer siegfriedpammer changed the title Strange cast CallBuilder produces strange cast on call target, in case two very similar overloads are present Jul 13, 2022
@CreateAndInject
Copy link
Contributor Author

@siegfriedpammer Seems no need to swap steps, since SetPropertyValue is protected, so ((PersistentBase)this).SetPropertyValue is invalid

@siegfriedpammer
Copy link
Member

Accessing protected members from derived classes is perfectly fine. What do you mean?

@CreateAndInject
Copy link
Contributor Author

@siegfriedpammer
image

@siegfriedpammer
Copy link
Member

Ah, yes, sorry, you are right. That is definitely something that we should fix. Thanks for reporting.

clin1234 pushed a commit to clin1234/ILSpy that referenced this issue Jul 14, 2022
…when disambiguating calls to protected methods.
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Oct 13, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Bug Decompiler The decompiler engine itself
Projects
None yet
Development

No branches or pull requests

2 participants