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

Eliminate Warning CS1957 (runtime ambiguous override) by generating correct code #44067

Closed
gafter opened this issue May 8, 2020 · 22 comments
Closed
Assignees
Labels
Area-Compilers Bug Feature - Covariant Returns Permit an override to return a more specific reference type
Milestone

Comments

@gafter
Copy link
Member

gafter commented May 8, 2020

The compiler sometimes reports CS1957:

class Base<TInt>
{
    //these signatures differ only in ref/out
    public virtual void Method(int @in, ref int @ref) { }
    public virtual void Method(TInt @in, out TInt @out) { @out = @in; }
}

class Derived : Base<int>
{
    public override void Method(int @in, ref int @ref) { } // HERE
}

warning CS1957: Member 'Derived.Method(int, ref int)' overrides 'Base<int>.Method(int, ref int)'. There are multiple override candidates at run-time. It is implementation dependent which method will be called.

The compiler is capable of emitting a methodimpl record which will disambiguate the situation for the runtime. If we do that, there will never be any need to produce this warning. Perhaps more importantly, the compile-time behavior and the runtime behavior will agree.

@gafter gafter added Bug Area-Compilers Feature - Covariant Returns Permit an override to return a more specific reference type labels May 8, 2020
@gafter gafter added this to the 16.7 milestone May 8, 2020
@gafter gafter self-assigned this May 8, 2020
@gafter
Copy link
Member Author

gafter commented May 8, 2020

The simplest fix is to modify (and rename) GetFirstRuntimeOverriddenMethodIgnoringNewSlot to return a method only if it is the only runtime overridden method (otherwise return null).

gafter pushed a commit to gafter/roslyn that referenced this issue May 13, 2020
@gafter gafter added the 4 - In Review A fix for the issue is submitted for review. label May 13, 2020
@AlekseyTs
Copy link
Contributor

At the moment the removal of the warning doesn't look like the right thing to do due to dotnet/runtime#38119.

@gafter
Copy link
Member Author

gafter commented Jun 19, 2020

Not sure if we intend to have the C# compiler produce warnings for runtime-version-specific bugs. If so, the warning should be reworded to identify the bug and suggest upgrading the runtime.

@AlekseyTs
Copy link
Contributor

The wording of the warning is correct no matter what version of the runtime is used: "Member '{1}' overrides '{0}'. There are multiple override candidates at run-time. It is implementation dependent which method will be called." What method is called is indeed implementation dependent, even when the "right" one is called.

@gafter
Copy link
Member Author

gafter commented Jun 19, 2020

The CLI specification is not ambiguous about which method will be called. There is only one candidate at runtime. The only reason it is implementation-dependent is that some implementations are incorrect.

@AlekseyTs
Copy link
Contributor

The only reason it is implementation-dependent is that some implementations are incorrect.

The actual reason makes absolutely no difference in my opinion. We are not trying to state it in the warning.

@gafter
Copy link
Member Author

gafter commented Jun 19, 2020

Sure, if we have a policy of producing warnings when we generate code that might be incorrect on some implementation because of runtime bugs.

@AlekseyTs
Copy link
Contributor

We could emit explicit override long time ago, we already did in some scenarios. Why do you think we didn't do that and instead kept reporting the warning?

@gafter
Copy link
Member Author

gafter commented Jun 19, 2020

Why do you think we didn't do that and instead kept reporting the warning?

I think we didn't do that because that portion of the Roslyn compiler was transcribed from a similar part of the native compiler, which only added the use of a class .override for a very specific scenario not related to methods that are declared override, not realizing it could be used for other scenarios as well. We have comments in the code incorrectly indicating that in IL there is no way to express the override precisely. It is only recently that the compiler team has become aware of this application of .override in metadata.

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Jun 19, 2020

It is only recently that the compiler team has become aware of this application of .override in metadata.

That is definitely not the case. We, even in the native compiler time, knew about that and even used it in some scenarios in classes. But not tried to use it for these scenarios. My guess, we new it wouldn't help.

@gafter
Copy link
Member Author

gafter commented Jun 19, 2020

@AlekseyTs I'm working from evidence rather than guessing.

The native and Roslyn compilers never used methodimpl entries for methods declared override. The comments in the compiler indicate that there is no way to express the override relationship precisely in metadata. That comment is, of course, incorrect and shows that the author of the relevant compiler code misunderstood the expressiveness of the metadata. The comment did not say that there is a runtime bug preventing it from working. The runtime team was unaware of the bug until this week. It seems unlikely that the compiler team knew about the bug but never bothered to report it to the runtime team.

In any case, we do not have a policy of reporting warnings on source that might encounter bugs specific to some version of the runtime.

@gafter
Copy link
Member Author

gafter commented Jun 19, 2020

The actual reason makes absolutely no difference in my opinion. We are not trying to state it in the warning.

The warning does state a reason. It says that there are multiple candidates at runtime. Since there is one candidate at runtime, I prefer to reword of remove the diagnostic.

@AlekseyTs
Copy link
Contributor

The native and Roslyn compilers never used methodimpl entries for methods declared override.

That is definitely not the case. Please look at the implementation and usage of

        internal static bool RequiresExplicitOverride(this MethodSymbol method)
        {
            if (method.IsOverride)
            {
                MethodSymbol csharpOverriddenMethod = method.OverriddenMethod;
                if ((object)csharpOverriddenMethod == null)
                {
                    return false;
                }
                // We can ignore newslot, since we checked IsOverride.
                // We can ignore interface implementation changes since the method is already metadata virtual (since override).
                // TODO: do we want to add more sophisticated handling for the case where there are multiple runtime-overridden methods?
                MethodSymbol runtimeOverriddenMethod = method.GetFirstRuntimeOverriddenMethodIgnoringNewSlot(ignoreInterfaceImplementationChanges: true);
                return csharpOverriddenMethod != runtimeOverriddenMethod &&
                    method.IsAccessor() != runtimeOverriddenMethod.IsAccessor();
            }

            return false;
        }

Similar method exists in VB compiler and it uses "methodimpl entries for methods declared override" even in more scenarios.

@gafter
Copy link
Member Author

gafter commented Jun 19, 2020

I stand corrected. More precisely, the compilers did not produce a methodimpl in order to disambiguate multiple candidate overridden methods.

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Jun 19, 2020

So, do you still think we we didn't know that methidimpl can be used with overrides in classes?

@gafter
Copy link
Member Author

gafter commented Jun 20, 2020

Yes, I still think the compiler team documented that there was no mechanism to disambiguate runtime ambiguous overrides (unlike the situation with the code quoted in the compiler that uses methodimpl to correct runtime unambiguous but incorrect overrides).

@AlekseyTs
Copy link
Contributor

You are answering the question that wasn't asked and do not answer the question that was asked. I wonder why.

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Jun 20, 2020

compiler team documented that there was no mechanism to disambiguate runtime ambiguous overrides

This, by the way, is still correct. There is no mechanism to disambiguate runtime ambiguous overrides
today. Your tests confirm that.

@gafter
Copy link
Member Author

gafter commented Jun 20, 2020

You are answering the question that wasn't asked and do not answer the question that was asked. I wonder why.

Perhaps because I assumed you had read my previous comment which answered your question and clarified what I think.

There is no mechanism to disambiguate runtime ambiguous overrides today.

That wasn't what I claimed. I claimed that the compiler team thought (incorrectly, as it turns out) that there was no mechanism to communicate a disambiguation to the runtime.

@gafter
Copy link
Member Author

gafter commented Jun 20, 2020

The belief by the compiler team that there was no such mechanism to express it was incorrect. The mechanism is documented in the ECMA specification, though some runtimes do not implement it correctly yet. I am aware of no evidence anybody was aware of that bug before this week. We expect this to be implemented correctly in .net 5.

@gafter gafter modified the milestones: 16.7, Compiler.Net5 Jun 20, 2020
@AlekseyTs
Copy link
Contributor

AlekseyTs commented Jun 20, 2020

I am aware of no evidence anybody was aware of that bug before this week.

I am that evidence. I was aware of the way runtime behaves since the native compiler times. I new that emitting explicit override doesn't help, at least in some scenarios. That is why I made the comment on the PR that tried to eliminate the warning and asked you to properly test your assumptions and see for yourself.

We expect this to be implemented correctly in .net 5.

I definitely support fixing this long standing bug in runtime. And will be happy to return to discussion about the warning once that happened. Until then, I think, doing anything is premature.

@jaredpar jaredpar modified the milestones: Compiler.Net5, 16.8 Jun 23, 2020
gafter pushed a commit that referenced this issue Jul 7, 2020
* Implement and test retargeting symbols for covariant returns.
* A PE symbol with multiple methodimpl entries is ignored as an explicit override for language covariant returns.
* Test binary compatibility scenarios with overrides inserted into the hierarchy.
* Test a scenario with duplicate requirements for a methodimpl entry
* A methodimpl may be required anytime the runtime and language disagree about the overridden method.
  (As a side-effect, this fixes a number of previously-believed-unfixable bugs regarding the mismatch between C# and CIL)
* Emit a call to the least derived override in the hierarchy with the correct return type.
* Test covariant returns in expression trees.
* Test capturing a covariant method in a delegate creation.
* Test consumption and override of covariantly overridden methods, properties, and indexers from VB.
* Require that the target runtime supports covariant returns when the feature is used.
* Adjust tests for new special member.
* Test compile-time behavior of nullable variance in covariant returns.
* Produce RequireMethodImplToRemainInEffectAttribute on covariant overrides.
* Eliminate CS1957 by generating correct code.
  Fixes #44067
* Cache `PEMethodSymbol.ExplicitlyOverriddenClassMethod`
  Closes #44068
* Move some PROTOTYPE comments to issues.
  Relates to #44206, #44207, #44208, #44209
* Remove virtual `ExplicitlyOverriddenClassMethod` from MethodSymbol.
  Instead provide (and test) leaf APIs to be used directly by `OverriddenOrHiddenMembersHelpers`.
* Enhance the covariant return tests per PR review.
* Rename `RequireMethodImplToRemainInEffectAttribute` to `PreserveBaseOverridesAttribute` per API review
* Document breaking change
* Changes per review comments
  - Revert behavior of PEMethodSymbol.IsOverride (true even if we cannot report a unique overridden method)
  - Add runtime test for behavior of methodimpl in ambiguous scenarios (reported 
    dotnet/runtime#38119)
* Modify how overrides are computed for PE symbols as requested in code review.
* Condition WRN_MultipleRuntimeOverrideMatches on whether runtime has working methodimpl
  Relates to dotnet/runtime#38119
  Fixes #45453
* Test `SourceMethodSymbol.RequiresExplicitOverride` in ambiguous scenarios.
* Preserve historical test for methodimpl generation as a fallback.
@gafter
Copy link
Member Author

gafter commented Jul 7, 2020

Fixed in #44025

@gafter gafter closed this as completed Jul 7, 2020
@gafter gafter removed the 4 - In Review A fix for the issue is submitted for review. label Jul 7, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Compilers Bug Feature - Covariant Returns Permit an override to return a more specific reference type
Projects
None yet
Development

No branches or pull requests

3 participants