meetings/2025/LDM-2025-03-10.md
Champion issue: https://github.com/dotnet/csharplang/issues/8697
Specification: https://github.com/dotnet/csharplang/blob/82157ff229f6c1bd2954a96ec2acb47064a48bad/proposals/extensions.md
We looked at a few questions in extensions today.
The first question we looked at was whether to allow extension property underlying methods to be called directly on an instance. For example:
_ = new object().Prop; // Allowed
_ = C.get_Prop(new object()); // Previously said this is allowed
_ = new object().get_Prop(); // Should this be allowed?
public static class C
{
extension (object o)
{
public int Prop { get => 1; }
}
}
We're not sure what the use case for this form is. We allow the get_Prop syntax on C itself to serve as a disambiguation syntax, but
we don't know why get_Prop should be visible as a method on object itself. We don't do this for regular properties, so we don't have a
reason to allow it for extension properties either. Given this, we will disallow it.
get_Prop form is disallowed in extension method form.
Next, we looked at a couple of questions around scoping and shadowing. First, should extension parameter names be in scope in static members, even if they'd be an error to reference, and even if they shadow a field? And second, should instance methods be able to shadow the extension parameter in their own parameter lists? As an example:
public static class E
{
static string s;
extension(string s)
{
public int M(int i)
{
return s.Length + i;
}
public static string P => s; // Does this error that the extension parameter s can't be used, or bind to E.s?
public void M2(string s) { ... } // Should we allow this to shadow the extension parameter?
public static void M3(string s) { ... } // Should we allow this to shadow the extension parameter, since there's no implicit s?
public static void M4() { s.ToString(); } // Does this bind to the extension receiver parameter and error, or to E.s?
}
}
There are a couple of sets of rules we can look to for inspiration here: existing parameter rules, and primary constructor rules. After
some thought, we think existing parameter rules are the right inspiration; as mentioned in the previous topic, we keep leaning further
towards the extension block being just another parameter. This means it should follow the same rules for other parameters, and when we
consider instance extension methods, M2 isn't shadowing anything, it's literally declaring 2 parameters with the same name, which is just
not permitted by C#'s rules. On the other hand, local functions or lambdas inside M2 would be allowed to shadow the parameter s, just
like they can with other parameters today.
More interesting is the static question: should we allow the parameter list in a static member to shadow the extension parameter?
The LDM is fairly split here between allowing or disallowing it. We therefore think we'll start most restrictive, disallowing the parameter
list to shadow, and wait for feedback. There is a workaround for users who want to do this, as they can just declare a new parameterless
extension block and declare the method in that block, so this isn't a hard blocker for code, but we'd rather start as more restrictive and
loosen when we have real examples, rather than starting with a loose rule that we may regret later.
Extension block parameters will be lexically in scope in all members declared within the block, and will be treated as if they were declared in the parameter list of each extension member for the purposes of naming collisions and shadowing.
Question: https://github.com/dotnet/csharplang/blob/82157ff229f6c1bd2954a96ec2acb47064a48bad/proposals/extensions.md#extension-declaration-validation (first bullet)
We next looked at whether we should loosen the requirement that any type parameters in the extension block declaration had to be inferrable
from the extension parameter. We don't think that we have the examples that would be necessary to lift this restriction today: we can think
of a couple of hypotheticals, such as an out-of-order TResult, TSource, or a set of type parameters where TSource needed to be
constrained based on TResult, but we don't have any concrete examples of this. Given that, we think we should start most restrictive, and
wait for users to bring examples to us.
We will not loosen the restriction, all type parameters in the extension block declaration must be inferrable from the extension parameter.
Finally today, we took a brief look at accessibility to get an initial gut feeling. The expressed sentiment in the room leaned towards
private being with respect to the entire static class, not towards an individual extension block, but we did not have time to deeply
dive into this topic, so no conclusions were made.