roslyn: is not SomeType fails to compile if there is a member with the same name

Version Used: .NET 5.0 C# 9 Visual Studio 2019 version 16.8.4

Add the following type:

public class SomeType
{
	public int SomeProperty { get; init; }
}

You can use both is SomeType and is not SomeType in a class without a member called SomeType.

// Compiles
public class Version1
{
	public void Execute(object data)
	{
		if (data is SomeType)
			Console.WriteLine("data is SomeType");
		else
			Console.WriteLine("data is not SomeType");
	}
}
// Compiles
public class Version2
{
	public void Execute(object data)
	{
		if (data is not SomeType)
			Console.WriteLine("data is not SomeType");
		else
			Console.WriteLine("data is SomeType");
	}
}

However, in a class containing a member called SomeType, is SomeType compiles, but is not SomeType does not compile.

/// Compiles
public class Version3
{
	private string SomeType => "some type";

	public void Execute(object data)
	{
		if (data is SomeType)
			Console.WriteLine("data is SomeType");
		else
			Console.WriteLine("data is not SomeType");
	}
}
// Fails to compile with error CS0428.
public class Version4
{
	private string SomeType => "some type";

	public void Execute(object data)
	{
		if (data is not SomeType)
			Console.WriteLine("data is not SomeType");
		else
			Console.WriteLine("data is SomeType");
	}
}

Currently, version 4 fails to compile with error CS0428. The issue occurs when there is a static or non-static field, property, or method called SomeType. Workarounds are to change the member SomeType to any other name or to fully qualifying the type as in is not SomeNamespace.SomeType.

Given that version 3 compiles, I would also expect version 4 to compile without these workarounds.

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 25 (16 by maintainers)

Most upvoted comments

Does seem like a bug. The behavior is not limited to not but also extends to and and or.

Need to discuss in LDM / compat council before we consider fixing this. Too late in 16.9 to take a change like this so punting out to 16.10.

Do we introduce a “mode” for binding patterns in is-pattern expressions which prefers types when things are ambiguous while preserving the behavior to prefer consts in switches?

Generally this is not done because it is not a sustainable pattern. Consider that every such mode must be tested in combination with each other. That gets unsustainable very quickly.

I error’d by mentioning compat council in my first comment here. This feature RTM’d with 16.9 which means we’re not even in the first update yet. Compat decisions don’t begin to take hold firmly for close to a year after RTM. We just need to confirm with LDM that this is the intended behavior and if so clarify the spec plus fix the bug.

Can you explain what the Color-Color situation is?

IIRC, Color-Color is pretty similar to the situation in the issue description.

class Color { ... }

class Widget
{
    Color Color;
}

i.e. inside the class Widget, when do you pick the type Color or the member Color.

This is not a bug. is Type is the is-type construct, not pattern-matching. is not Type is an is-pattern operation. Pattern-matching and is-type necessarily have different binding rules. For compatibility, the former must bind to a type if one exists, even if it would be shadowed by a constant, which means it skips other members during lookup. The latter can match to other members, so its name lookup rules do not skip members and do not prioritize a type. We could not use the same rules for pattern-matching because that would (among other things) break the Color-Color situation. Similarly for and and or. This is one of the areas where we could not fully hide the seam between compatibility with existing constructs and extending the language to use the “same” syntax for extended functionality.