dotnet / dotnet/csharpstandard

11.7, 12.X Conversions from extension methods to delegate types

Open
#92 9 comments 0 reactions 0 assignees View on GitHub
type: bug
Dominant language
C#
Stars
815
Forks
99
Avg merge
1d 14h
Merged PRs (30d)
16

Description

This issue was reported by Neal Gafter .

Consider this code:

``` C#
using System;

static class S
{
static void Main()
{
string s = "";
Action a = s.Ext;
}

static void Ext(this string s) { }
}
```

It compiles both on Roslyn and Mono, and creates a delegate to the extension method `Ext` encapsulating the empty string as a value to be provided as the first argument to the extension method. Unfortunately, the spec does not explain why it should work this way.

Relevant parts of the spec are:
- **§12.7 Method group conversions**
- **§13.2.1 Expression classifications — General**
- **§13.5.1 Member lookup — General**
- **§13.7.5.1 Member access — General**
- **§13.7.6.3 Extension method invocations**

> **§12.7 Method group conversions**
> An implicit conversion (§12.2) exists from a method group (§13.2) to a compatible delegate type. Given a delegate type `D` and an expression `E` that is classified as a method group, an implicit conversion exists from `E` to `D` if `E` contains at least one method that is applicable in its normal form (§13.6.4.2) to an argument list constructed by use of the parameter types and modifiers of `D`

Note that a prerequisite for applying rules from this section is to have an expression that is already classified as a method group. **§13.2.1 Expression classifications — General** gives the following definition:

> An expression is classified as one of the following:
> <...>
> • A method group, which is a set of overloaded methods resulting from a member lookup (§13.5).

Do we know that `s.Ext` is a method group? Because `s.Ext` is syntactically a _member-access_, we need rules from **§13.7.5.1 Member access — General** to determine its meaning.

> The _member-access_ is evaluated and classified as follows:
> • If `K` is zero and `E` is a namespace... (**NO**)
> • Otherwise, if `E` is a namespace... (**NO**)
> • If `E` is a _predefined-type_ or a _primary-expression_ classified as a type... (**NO**)
> • If `E` is a property access, indexer access, variable (**YES**), or value, the type of which is `T`, and a member lookup (§13.5) of `I` in `T` with `K` type arguments produces a match... (**NO**)
> • Otherwise, an attempt is made to process `E.I` as an extension method invocation (§13.7.6.3). If this fails, `E.I` is an invalid member reference, and a binding-time error occurs.

First of all, how an expression of the form `E.I` could be a method invocation? Where is its argument list? OK, suppose it meant to say

> • Otherwise, if `E.I` occurs as part of an invocation expression `E.I(...)`, an attempt is made to process that expression as an extension method invocation (§13.7.6.3).

But even in this form this bullet would not apply in our case — we do not have an invocation. §13.7.6.3 requires an argument list to start searching for extension methods, but we do not have an argument list. Moreover, §13.7.6.3 does not mention any member lookup, but we know from §13.2.1 that a method group can only result from a member lookup. So, we have to conclude that `s.Ext` is not a method group and its processing according to §13.7.5.1 must result in a compile-time error.

We could try to save the situation by saying that §12.7 describes processing of an implicit conversion to a delegate type by constructing an imaginary method invocation with an argument list built based on the delegate signature, and this argument list can be used as an argument list required in §13.7.6.3. But as I said initially, the current wording in the spec requires that we already must have a method group as a prerequisite for the processing descibed in §12.7.

We need to fix the spec in several places if we want to break this loop.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.