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

Bug in spec for better conversion from expression #283

Closed
gafter opened this issue Mar 27, 2017 · 3 comments · Fixed by #484
Closed

Bug in spec for better conversion from expression #283

gafter opened this issue Mar 27, 2017 · 3 comments · Fixed by #484
Assignees
Milestone

Comments

@gafter
Copy link
Member

gafter commented Mar 27, 2017

@gafter commented on Tue Sep 27 2016

The spec for better conversion from expression doesn't match the implementation. The spec says

7.5.3.3 Better conversion from expression
Given an implicit conversion C1 that converts from an expression E to a type T1, and an implicit conversion C2 that converts from an expression E to a type T2, C1 is a better conversion than C2 if E does not exactly match T2 and one of the following holds:
• E exactly matches T1 (§7.5.3.4)
• T1 is a better conversion target than T2 (§7.5.3.5)

but the implementation does

            bool t1MatchesExactly = ExpressionMatchExactly(node, t1, ref useSiteDiagnostics);
            bool t2MatchesExactly = ExpressionMatchExactly(node, t2, ref useSiteDiagnostics);

            if (t1MatchesExactly)
            {
                if (!t2MatchesExactly)
                {
                    // - E exactly matches T1
                    return BetterResult.Left;
                } // NOTE NO ELSE HERE
            }
            else if (t2MatchesExactly)
            {
                // - E exactly matches T2
                return BetterResult.Right;
            }

            // - T1 is a better conversion target than T2
            return BetterConversionTarget(node, t1, conv1, t2, conv2, ref useSiteDiagnostics, out okToDowngradeToNeither);

Specifically, if both match exactly, then we test for a better conversion target. This is what is implemented:

7.5.3.3 Better conversion from expression
Given an implicit conversion C1 that converts from an expression E to a type T1, and an implicit conversion C2 that converts from an expression E to a type T2, C1 is a better conversion than C2 if one of the following holds:
• E does not exactly match T2 and E exactly matches T1 (§7.5.3.4)
• E either exactly matches both T1 and T2, or exactly matches neither T1 and T2, and also T1 is a better conversion target than T2 (§7.5.3.5)


@gafter commented on Tue Sep 27 2016

This issue is preventing the proposed spec approach for out var overload resolution:

an inferred “out var” argument exactly matches (see 7.5.3.4 Exactly matching Expression) every type.

from having the effect of causing out var arguments to ignore the corresponding parameter position.


@gafter commented on Tue Sep 27 2016

This appears to be mooted in the ECMA version. But the ECMA version has no concept of exactly matching expression.


@AlekseyTs commented on Tue Sep 27 2016

It looks like the implementation is intentional, we would want the following scenario compiling without errors and using the second overload:

using System;
namespace ConsoleApplication2
{
    public static class Foo
    {
        public static U IfNotNull<T, U>(this T value, Func<T, U> selector)
        {
            System.Console.WriteLine("IfNotNull<T, U>(this T value, Func<T, U> selector");
            return value != null ? selector(value) : default(U);
        }
        public static U IfNotNull<T, U>(this T? source, Func<T, U> selector) where T : struct
        {
            System.Console.WriteLine("IfNotNull<T, U>(this T? source, Func<T, U> selector)");
            return source.HasValue ? selector(source.Value) : default(U);
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            double? val = null;
            var d1 = val.IfNotNull(v => v / 100);
            var d2 = Foo.IfNotNull(val, v => v / 100);
        }
    }
}

@gafter commented on Tue Sep 27 2016

Indeed. Moreover, we want to implement the ECMA spec instead of the old C# 4 spec, which is quoted above in the OP.


@gafter commented on Tue Sep 27 2016

Closing, as we are intending to use the ECMA spec as our base, and it does not have this problem.


@MadsTorgersen commented on Tue Sep 27 2016

We cannot use the ECMA spec as our base, because it does not reflect the changes for "better betterness" that were done in C# 6.0. Instead, the ECMA spec needs to embrace the Microsoft spec when it gets updated for C# 6.0.

I agree that there is a discrepancy between how "better betterness" was spec'ed and how it's implemented. The implementation is already in use, so unless there's a strong reason not to (I don't see any), we should go with what's implemented.

That means adjusting the spec a bit, so that better conversion target is checked both when neither matches precisely, and when both do. Something like this:

7.5.3.3 Better conversion from expression
Given an implicit conversion C1 that converts from an expression E to a type T1, and an implicit conversion C2 that converts from an expression E to a type T2, C1 is a better conversion than C2 if E does not exactly match T2 and one of the following holds:
• E exactly matches T1 and E does not exactly match T2 (§7.5.3.4)
• E exactly matches both or neither of T1 and T2, and T1 is a better conversion target than T2 (§7.5.3.5)


@gafter commented on Tue Sep 27 2016

@MadsTorgersen
I think you intended to delete

E does not exactly match T2 and

from the prefix to the bulleted list.


@gafter commented on Tue Sep 27 2016

@MadsTorgersen That reopen the question if how you would like to handle out var in the spec.

@BillWagner
Copy link
Member

This is for §12.6.4.4 in the standard.

This is part of the "better betterness" for C# 6.0. Moving to dotnet/csharpstandard.

@BillWagner BillWagner transferred this issue from dotnet/csharplang Apr 30, 2021
@BillWagner BillWagner added this to the C# 6 milestone Apr 30, 2021
@jskeet
Copy link
Contributor

jskeet commented Oct 20, 2021

@jskeet
Copy link
Contributor

jskeet commented Nov 17, 2021

For the record: Neal asked this to be assigned to him (against his better judgement).

@BillWagner BillWagner self-assigned this Mar 9, 2022
BillWagner added a commit to BillWagner/csharpstandard that referenced this issue Mar 9, 2022
Issue dotnet#283 contains notes for how the implementation differed from the proposed better betterness text.
BillWagner added a commit that referenced this issue Mar 24, 2022
* Incorporate Better Betterness

This commit adds the notes from https://github.com/dotnet/roslyn/blob/main/docs/specs/CSharp%206/Better%20Betterness.md

* Incorporate changes from #283

Issue #283 contains notes for how the implementation differed from the proposed better betterness text.

* compat for delegate conversion

This commit brings in the method group conversion fix that caused dotnet/roslyn#6750

It needs wordsmithing for a number of reasons:
- "corresponding argument" isn't defined.
- the section references itself.
- I think there may have been requirements from the prior commit regarding delegates and expressions that are still needed.

* initial feedback.

* section reference

* a bit of editing

* I think this gets close

This gets close to what's implemented in C# 6.0.

I think this fails as a description in the case of #499

For 7.3, it would be better to describe that change in the section on Method Group conversion.

* review from the meeting.

* fix awkward grammar

* limit bullet to method group conversion.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants