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

C# Pattern Matching type expressions don't obey generic variance #16688

Closed
BillWagner opened this issue Jan 23, 2017 · 5 comments
Closed

C# Pattern Matching type expressions don't obey generic variance #16688

BillWagner opened this issue Jan 23, 2017 · 5 comments
Assignees
Labels
4 - In Review A fix for the issue is submitted for review. Area-Compilers Bug Feature - Pattern Matching Pattern Matching
Milestone

Comments

@BillWagner
Copy link
Member

Version Used:
Microsoft Visual Studio Enterprise 2017 RC
Version 15.0.26118.1 D15REL
Visual C# 2017 RC 00369-50000-00000-AA640
Microsoft Visual C# 2017 RC

Steps to Reproduce:

Compile a class with this method in it:

        private static void Pattern<T, TDerived>(T thing) where T : class where TDerived : T
        {
            switch (thing)
            {
                case TDerived derivedThing:
                    break;
                case T tThing:
                    break;
                case IEnumerable<object> s:
                    break;
                case IEnumerable<T> sequence:
                    break;
                case IEnumerable<TDerived> derivedSequence:
                    break;
            }
        }

Expected Behavior:

Both the case for IEnumerable<T> and IEnumerable<TDerived> should be considered unreachable because there is an implicit conversion from either IEnumerable<T> or from IEnumerable<TDerived> to IEnumerable<object>

Actual Behavior:

Only the case label for IEnumerable<T> generates CS8120.

/cc @gafter
Also, note a variation of this type of behavior in issue #16671

@gafter
Copy link
Member

gafter commented Jan 23, 2017

Actually, I believe all of the IEnumerable cases should be considered unreachable, as T, the input type, has already been handled.

@gafter gafter added this to the 2.0 (RTM) milestone Jan 23, 2017
@gafter gafter self-assigned this Jan 23, 2017
@gafter
Copy link
Member

gafter commented Jan 23, 2017

This may be related to a "missing conversion" in the language #16195 (comment). For example, the following fails to compile, demonstrating that variance doesn't interact well with type parameters.

using System.Collections.Generic;
public class Program
{
    private static void Pattern<T, TDerived>(object thing) where T : class where TDerived : T
    {
        IEnumerable<T> t1 = null;
        IEnumerable<TDerived> t2 = null;
        t1 = t2; // error: no conversion
    }
}

Having said that, once a value of type T (the input type) has been handled, any other type cannot slip through. Investigating why that doesn't work.

@gafter
Copy link
Member

gafter commented Jan 23, 2017

See also #16696, which is one of the root causes of this issue.

@gafter
Copy link
Member

gafter commented Jan 23, 2017

I am narrowing the scope of this issue to the following test case. Other issues are under #16696 and can't be addressed in this milestone.

        [Fact, WorkItem(16688, "https://github.com/dotnet/roslyn/issues/16688")]
        public void TypeParameterSubsumption03()
        {
            var program = @"
using System.Collections.Generic;
public class Program
{
    private static void Pattern<T, TDerived>(T thing) where T : class where TDerived : T
    {
        switch (thing)
        {
            case T tThing:
                break;
            case IEnumerable<object> s:
                break;
        }
    }
}
";
            var compilation = CreateCompilationWithMscorlib45(program).VerifyDiagnostics(
                // (11,18): error CS8120: The switch case has already been handled by a previous case.
                //             case IEnumerable<object> s:
                Diagnostic(ErrorCode.ERR_PatternIsSubsumed, "IEnumerable<object> s").WithLocation(11, 18),
                // (12,17): warning CS0162: Unreachable code detected
                //                 break;
                Diagnostic(ErrorCode.WRN_UnreachableCode, "break").WithLocation(12, 17)
                );
        }

gafter added a commit to gafter/roslyn that referenced this issue Jan 23, 2017
@gafter gafter added 4 - In Review A fix for the issue is submitted for review. and removed 3 - Working labels Jan 23, 2017
@gafter gafter closed this as completed in c069b89 Jan 26, 2017
@gafter
Copy link
Member

gafter commented Jan 29, 2017

It turns out this was not a bug. TDerived is not known to be a reference type, so there is no conversion from IEnumerable<object> to IEnumerable<TDerived>.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
4 - In Review A fix for the issue is submitted for review. Area-Compilers Bug Feature - Pattern Matching Pattern Matching
Projects
None yet
Development

No branches or pull requests

3 participants