-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
Warnings missing on tuple name rearrangement #14217
Comments
@jcouv @VSadov @jaredpar I want to call this to your attention, as it appears to be a significant work item not yet started. It appears we have special-cased certain contexts (e.g. overriding), but not implemented the general case (identity conversion). This is specified at the top of #10429 (The April 6 design notes).
There is a similar work item for VB: see #11370, which says
|
@VSadov pointed out it would be silly to have this warning for identity conversions but not for implicit tuple conversions, such as from |
Tuple name rearrangement warningsTuple name rearrangement warnings were called out by the LDM; see the top of #10429 (The April 6 design notes):
We may have a warning on tuple name rearrangement when the following kinds of implicit conversions are used Implicit Tuple Literal ConversionThis one may not need a separate warning, as there is already a warning for this situation due to the dropped name. However, warning about rearragement instead in this case is probably more valuable to the user than the existing warning about dropped names. Identity ConversionThis is the one called out by the original LDM requirement in the context of a variable initialization. (string first, string last) GetNames() { ... }
(string last, string first) names = GetNames(); // Oops! Presumably it should apply to an assignment expression as well. (string first, string last) GetNames() { ... }
(string last, string first) names;
names = GetNames(); // Oops! Implicit Tuple ConversionThe implicit tuple (elementwise) conversion was added subsequently, and we agree that if we have a warning for the identity conversion case, the warning should also be applied to the implicit tuple conversion case. (string first, string last) GetNames() { ... }
(object last, object first) names = GetNames(); // Oops! Contexts in which a tuple name rearrangement warning might be dueIt seems unlikely that the LDM intended the requirement for the rearrangement warning to be produced only on the variable initialization and not, for example, on the assignment expression. There are many other contexts in which conversions are used to define the semantics of the language, and for which diagnostics would be helpful. Local variable initializationThis is where, by example, the LDM explicitly acknowledged the utility of the warning. We imagine that field and property initialization would follow. Assignment (including ref)Analogous to variable initialization. Object initializerIn a context such as new C() { X = Y } where the position of names in X and Y differ. It should similarly apply to other forms of object initializers. Overriding, Implementing, and Hiding (OHI already requires a strict match)In the places where types must match for OHI purposes, we already have custom rules. Generally, names must match exactly. We need to verify the required behavior for explicit hiding ( Argument conversion (including ref, out)For example (string first, string last) GetNames() { ... }
void UseNames((string last, string first) names) { ... }
UseNames(GetNames()); Proposed: yes, the warning applies to this context. This applies to operator invocation, delegate invocation, indexer invocation, etc. It applied even if the argument is passed by Extension method receiverFor example (string first, string last) GetNames() { ... }
static void UseNames(this (string last, string first) names) { ... }
GetNames().UseNames(); Proposed: yes. ForeachFor example List<(string first, string last)> list = ...;
foreach ((string last, string first) item in list) ... // context 1
foreach ((string last, string first) in list) ... // context 2
foreach (var (last, first) in list) ... // context 3 This question can be combined with class C
{
public void Deconstruct(out string first, out string last) { ... }
}
List<C> list = ...;
foreach ((string last, string first) in list) ... // context 4
foreach (var (last, first) in list) ... // context 5 Since the foreach loop is currently specified to use an explicit conversion, one would imagine that no warning would be required. However,
Based on this approach, contexts 1 could be given a diagnostic. Contexts 2, 3, 4, and 5 are deconstruction contexts, and if we want a warning for these situations (LDM should chime in here) it would have to be specified separately. I propose that yes, we want such a warning for deconstruction declarations as well. DeconstructionAs described in the discussion of (string first, string last) GetNames() { ... }
(string last, string first) = GetNames(); // context 1
var (last, first) = GetNames(); // context 2 A similar situation arises with class C
{
public void Deconstruct(out string first, out string last) { ... }
}
C GetNames() { ... }
(string last, string first) = GetNames(); // context 3
var (last, first) = GetNames(); // context 4 A similar question would arise for deconstruction into existing (rather than newly declared) variables. We need to decide for each of these contexts whether or not a warning is intended. Return, Yield Return(string first, string last) GetNames()
{
(string last, string first) v = ...;
return v; // warning?
} Under the principle that the use of an identity or implicit tuple conversion should be capable of producing a name rearrangement warning, this context should. This applies to block-bodied lambdas, methods, local functions, operator declarations, etc. It should apply appropriately to async methods. Conditional, Coalesce (best common type)(string first, string last) x1 = ...;
(string last, string first) x2 = ...;
bool b = ...;
var x3 = b ? x1 : x2; In this case, due to newly introduced type inference rules, the type of x3 is (string first, string last)? x1 = ...;
(string last, string first) x2 = ...;
var x3 = x1 ?? x2; We infer the type Lambda Conversion e.g. from ((string first, string last) x)=>{} to Action<(string last, string first)> Action<(string last, string first)> a = ((string first, string last) x) => {}; Propose yes, warning for this case. On the other hand, I do not believe that arguments being declared to be of tuple type are likely to occur much in practice. Type Arguments in Identity Conversionclass C<T> {}
C<(string first, string last)> x = ...;
C<(string last, string first)> y = x; // warning? Proposed: yes Type Arguments in Reference Conversioninterface I<T> {}
class C<T> : I<T> {}
C<(string first, string last)> x = ...;
I<(string last, string first)> y = x; // warning? Proposed: yes Variance Conversion e.g. from Action(I<(f, l)>) to Action(C<l, f)>) where C:Iinterface I<T> {}
class C<T> : I<T> {}
interface A<in T> {}
A<I<(string first, string last)>> x = ...;
A<C<(string last, string first)>> y = x; // warning? Propose yes, warning for this case. from (string first, string last) in e select e.firstThe code var r = from (string first, string last) xin e select x.first; is, by specification, translated into var r = e.Cast<(string first, string last)>().Select(x => x.first); where
In other words, the language does not provide a reasonable place for this user error to be caught, just as this code is accepted today: string[] e = null;
var r = from int x in e select x + 1; So propose no diagnostic for this situation. CastWhat about an explicit cast? (string first, string last) x = ...;
(string last, string first) y = ((string last, string first))x; // warning? On the one hand, this cast does make use of the problematic conversion, so one could reasonably argue that the warning should be required. On the other hand, the user may have intended to circumvent the warning by the use of the cast. Propose that the warning be required here. To suppress the warning, cast to Array intitializerThe elements of an array initializer must be implicitly convertible to the type of the array. The warning on name rearrangement should apply in this case. Array type inference (best common type)(string first, string last) x = ...;
(string last, string first) y = ...;
var a = new[] { x, y }; // what is the type of a? Presumably we'd use the same solution as for conditional and coalesce, and infer nameless types; the type of a is Identity of typesConsider class C<T>
{
public static T Value;
}
C<(string first, string last)>.Value.first = "first";
Console.WriteLine(C<(string last, string first)>.Value.last); // prints "first" I don't think there is anything we can do about this. Type parameter constraintsConsider T M<T, U>(U u) where T : U
{
return u;
}
(string first, string last) x = ...;
(string last, string first) y = M<(string last, string first), (string first, string last)>(x); We can detect this problem when verifying that the (inferred or explicit) type arguments satisfy the type parameter's constraints. I propose that we do so. Dynamic type testThe dynamic type test Partial MethodsConsider partial void M((string first, string last) x) {}
partial void M((string last, string first) y); Proposed: warning required. Constraints on types of operatorsThe spec says
(Emphasis mine) As this requirement for the type to be the same could only make a difference inside the declaration of ArraysA conversion from an array of type Similarly, a conversion from an array of type Generic type inference(string first, string last) x = ...;
(string last, string first) y = ...;
T M<T>(T x, T y) => ...;
var z = M(x, y); In this case the type of Expression lambda return value(string first, string last) x = ...;
Func<(string last, string first)> y = () => x Proposed: yes, warning required. Compound Assignment
My emphasis. If we use this rule in analyzing an operator, we should produce a warning if appropriate. Default argumentvoid M((string first, string last) name = default((string last, string first))) ... We should produce a warning here. Method Group Conversionvoid M((string first, string last) x) {}
Action<(string last, string first)> a = M; We should produce a warning here. |
@jcouv @VSadov @AlekseyTs We stood up today and enumerated a few of the contexts in which the tuple name rearrangement warning might arise in the language. I made a quick pass through the language spec to fill in a few more that I think we missed, and recorded them above. This list also includes places where I do not recommend we add a warning, but I put the places on the list for completeness. |
Per today's LDM, we won't be able to fit this change into C# 7. We'll consider for a warning wave. |
Warning waves are described in #1580. |
I thought of an analogous situation with out vars: void M(out bool first, out bool second);
M(out var second, out var first); |
The following program is supposed to give a warning about shuffled tuple names, but we report nothing.
The text was updated successfully, but these errors were encountered: