From 04ac4efa4cfe8a965e914521bb93a50a8bed9e26 Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Tue, 25 Jun 2024 13:52:48 +0200 Subject: [PATCH 1/4] Update first-class Span speclet with LDM decisions --- proposals/first-class-span-types.md | 56 +++++++++++++---------------- 1 file changed, 25 insertions(+), 31 deletions(-) diff --git a/proposals/first-class-span-types.md b/proposals/first-class-span-types.md index de1832774f..e4f143185f 100644 --- a/proposals/first-class-span-types.md +++ b/proposals/first-class-span-types.md @@ -37,7 +37,7 @@ The changes in this proposal will be tied to `LangVersion >= 13`. ### Implicit Span Conversions We add a new type of implicit conversion to the list in [§10.2.1](https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/conversions.md#1021-general), an -_implicit span conversion_. This conversion is a conversion from expression and is defined as follows: +_implicit span conversion_. This conversion is a conversion from type and is defined as follows: ------ @@ -50,6 +50,9 @@ An implicit span conversion permits `array_types`, `System.Span`, `System.Rea ------ +Any Span/ReadOnlySpan types are considered applicable for the conversion if they match by their fully-qualified name. +If they do not match the types that are selected by the compiler's well-known type resolution logic, a compile-time error is reported. + We also add _implicit span conversion_ to the list of standard implicit conversions ([§10.4.2](https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/conversions.md#1042-standard-implicit-conversions)). This allows overload resolution to consider them when performing argument resolution, as in the previously-linked API proposal. @@ -61,11 +64,21 @@ The explicit span conversions are the following: There is no standard explicit span conversion unlike other *standard explicit conversions* ([§10.4.3][standard-explicit-conversions]) which always exist given the opposite standard implicit conversion. +#### User defined conversions + User-defined conversions are not considered when converting between - any single-dimensional `array_type` and `System.Span`/`System.ReadOnlySpan`, - any combination of `System.Span`/`System.ReadOnlySpan`, - `string` and `System.ReadOnlySpan`. +The implicit span conversions are exempted from the rule +that it is not possible to define a user-defined operator between types for which a non-user-defined conversion exists +([§10.5.2 Permitted user-defined conversions][permitted-udcs]). +This is needed so BCL can keep defining the existing Span conversion operators even when they switch to C# 13 +(to avoid binary breaking changes and also because we use these operators in codegen of the new standard span conversion). + +#### Extension receiver + We also add _implicit span conversion_ to the list of acceptable implicit conversions on the first parameter of an extension method when determining applicability ([12.8.9.3](https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/expressions.md#12893-extension-method-invocations)) (change in bold): @@ -226,33 +239,6 @@ static class C > [the new `OverloadResolutionPriorityAttribute`][overload-resolution-priority]. > See also [an open question](#unrestricted-betterness-rule) below. -#### Better conversion target - -*Better conversion target* ([§12.6.4.7][better-conversion-target]) is updated to consider implicit span conversions. - -> Given two types `T₁` and `T₂`, `T₁` is a *better conversion target* than `T₂` if one of the following holds: -> -> - An implicit conversion from `T₁` to `T₂` exists and no implicit conversion from `T₂` to `T₁` exists -> **(the implicit span conversion is considered in this bullet point, even though it's a conversion from expression)** -> - [...] - -This change is needed to avoid ambiguities in existing code that would arise -because we are now ignoring user-defined Span conversions which were considered in the "better conversion target" rule, -whereas "implicit Span conversion" would not be considered as it is a "conversion from expression", not a "conversion from type". - -```cs -using System; -C.M(null); // used to print 1, would be ambiguous without this rule -static class C -{ - public static void M(object[] x) => Console.Write(1); - public static void M(ReadOnlySpan x) => Console.Write(2); -} -``` - -This rule is not needed if the span conversion is a conversion from type. -See [an open question][conversion-from-type] below. - ### Type inference We update the type inferences section of the specification as follows (changes in **bold**). @@ -457,6 +443,7 @@ However, that would mean users could get different behavior after updating the t Should we break existing code like the following (real code found in runtime)? LDM recently allowed breaks related to new Span overloads (https://github.com/dotnet/csharplang/blob/main/meetings/2024/LDM-2024-06-17.md#params-span-breaks). +Currently, this speclet has a mitigation for this break in [the extension receiver section](#extension-receiver). ```cs using System; @@ -468,14 +455,13 @@ var toRemove = new int[] { 2, 3 }; list.RemoveAll(toRemove.Contains); // error CS1113: Extension method 'MemoryExtensions.Contains(Span, int)' defined on value type 'Span' cannot be used to create delegates ``` -### Conversion from type vs. from expression -[conversion-from-type]: #conversion-from-type-vs-from-expression +### Conversion from type vs. from expression (answered) We now think it would be better if the span conversion would be a conversion from type: - Nothing about span conversions cares what form the expression takes; it can be a local variable, a `new[]`, a collection expression, a field, a `stackalloc`, etc. - We have to go through everywhere in the spec that doesn't accept conversions from expression and check if they need updating to accept span conversions. - Like [better conversion target](#better-conversion-target) above. + Like *better conversion target* ([§12.6.4.7][better-conversion-target]). Note that it is not possible to define a user-defined operator between types for which a non-user-defined conversion exists ([§10.5.2 Permitted user-defined conversions][permitted-udcs]). Hence, we would need to make an exception, so BCL can keep defining the existing Span conversion operators even when they switch to C# 13 @@ -490,6 +476,14 @@ We see a couple of ways of solving this concern: On the other hand, that could affect partners abilities to take them up. 2. We couple type conversions to a Compilation or look at other ways of providing the well-known type to it. This will take a bit of investigation. +#### Answer + +Go with a third option: match the Span types by their full name. +Then later check that the type matches the well-known type and report a compile-time error if it does not. +Although this type selection logic is inconsistent with the rest of the compiler, +it should not affect the vast majority of users, +does not break downlevel scenarios, and is straightforward to implement. + ## Alternatives Keep things as they are. From 022e399e05767880841ee4ede8388982797a8c24 Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Wed, 26 Jun 2024 11:56:04 +0200 Subject: [PATCH 2/4] Improve wording Co-authored-by: Charles Stoner <10732005+cston@users.noreply.github.com> --- proposals/first-class-span-types.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/first-class-span-types.md b/proposals/first-class-span-types.md index e4f143185f..f215357330 100644 --- a/proposals/first-class-span-types.md +++ b/proposals/first-class-span-types.md @@ -75,7 +75,7 @@ The implicit span conversions are exempted from the rule that it is not possible to define a user-defined operator between types for which a non-user-defined conversion exists ([§10.5.2 Permitted user-defined conversions][permitted-udcs]). This is needed so BCL can keep defining the existing Span conversion operators even when they switch to C# 13 -(to avoid binary breaking changes and also because we use these operators in codegen of the new standard span conversion). +(to avoid binary breaking changes and also because these operators are used in codegen of the new standard span conversion). #### Extension receiver From e27ead730292a51dd2d780f6e6bda88555b96df5 Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Wed, 26 Jun 2024 11:57:17 +0200 Subject: [PATCH 3/4] Remove answered open question --- proposals/first-class-span-types.md | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/proposals/first-class-span-types.md b/proposals/first-class-span-types.md index f215357330..6f22bd8fa6 100644 --- a/proposals/first-class-span-types.md +++ b/proposals/first-class-span-types.md @@ -455,35 +455,6 @@ var toRemove = new int[] { 2, 3 }; list.RemoveAll(toRemove.Contains); // error CS1113: Extension method 'MemoryExtensions.Contains(Span, int)' defined on value type 'Span' cannot be used to create delegates ``` -### Conversion from type vs. from expression (answered) - -We now think it would be better if the span conversion would be a conversion from type: - -- Nothing about span conversions cares what form the expression takes; it can be a local variable, a `new[]`, a collection expression, a field, a `stackalloc`, etc. -- We have to go through everywhere in the spec that doesn't accept conversions from expression and check if they need updating to accept span conversions. - Like *better conversion target* ([§12.6.4.7][better-conversion-target]). - -Note that it is not possible to define a user-defined operator between types for which a non-user-defined conversion exists ([§10.5.2 Permitted user-defined conversions][permitted-udcs]). -Hence, we would need to make an exception, so BCL can keep defining the existing Span conversion operators even when they switch to C# 13 -(to avoid binary breaking changes and also because we use these operators in codegen of the new standard span conversion). - -In Roslyn, type conversions do not have access to Compilation which is needed to access well-known type Span. -We see a couple of ways of solving this concern: - -1. We make the new conversions only applicable to the case where Span comes from the corelib, which would significantly simplify this space for us. - This would mean the rules don't exist downlevel; on the one hand, they already partly have issues downlevel - (covariance won't exist downlevel because the helper API doesn't exist). - On the other hand, that could affect partners abilities to take them up. -2. We couple type conversions to a Compilation or look at other ways of providing the well-known type to it. This will take a bit of investigation. - -#### Answer - -Go with a third option: match the Span types by their full name. -Then later check that the type matches the well-known type and report a compile-time error if it does not. -Although this type selection logic is inconsistent with the rest of the compiler, -it should not affect the vast majority of users, -does not break downlevel scenarios, and is straightforward to implement. - ## Alternatives Keep things as they are. From 699e0b881f31ac78d242f9a26dd42abd6df1acd8 Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Thu, 27 Jun 2024 15:13:39 +0200 Subject: [PATCH 4/4] Remove well known type restriction --- proposals/first-class-span-types.md | 1 - 1 file changed, 1 deletion(-) diff --git a/proposals/first-class-span-types.md b/proposals/first-class-span-types.md index 6f22bd8fa6..9841ae26b7 100644 --- a/proposals/first-class-span-types.md +++ b/proposals/first-class-span-types.md @@ -51,7 +51,6 @@ An implicit span conversion permits `array_types`, `System.Span`, `System.Rea ------ Any Span/ReadOnlySpan types are considered applicable for the conversion if they match by their fully-qualified name. -If they do not match the types that are selected by the compiler's well-known type resolution logic, a compile-time error is reported. We also add _implicit span conversion_ to the list of standard implicit conversions ([§10.4.2](https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/conversions.md#1042-standard-implicit-conversions)). This allows overload resolution to consider