Champion: Name lookup for simple name when target type known #8641
Replies: 61 comments
-
So with this, would I be able to do something akin to |
Beta Was this translation helpful? Give feedback.
-
Quite useful, but what about the compiler overhead of overload resolution? enum E1 { EA, EB, EC }
enum E2 { EA, EB }
void M(E1 e, int x);
void M(E2 e, string x);
M(EA, someParameter); Does this meet the similar overload resolution algorithm when using number literals? |
Beta Was this translation helpful? Give feedback.
-
Greed is the color of money. |
Beta Was this translation helpful? Give feedback.
-
As per current proposal, I think you would need to qualify at least one operand, GetMethod("Name", BindingFlags.Public | Instance) In order to omit type for all of them, the operator |
Beta Was this translation helpful? Give feedback.
-
That's exactly correct, and we actually brought up the |
Beta Was this translation helpful? Give feedback.
-
I've encountered with an issue with using static, case IBinaryOperation { OperatorKind: Equals } op:
For target-typed lookup, would it make sense to alter the spec as follow?
The lookup itself would have no information about the context and we'll need to add a new bottleneck to check the validity which is defined as being a constant of a particular type in this case. |
Beta Was this translation helpful? Give feedback.
-
@alrz We would need to define a concept of validity for every context in which an expression could appear. I don't think it is worth it. |
Beta Was this translation helpful? Give feedback.
-
To avoid that, as a simpler rule, we could say if an identifier is already found but the conversion fails, we treat as target-typed. In other words, it's defined as the fallback conversion iif the source expression is a simple identifier. Then it wouldn't depend on the context. In the example above, method group fails to convert to enum, so it will be treated as target-typed. edit: I realized the above might be a breaking change in some scenarios involving overload resolution, candidates that were not applicable becomes applicable and could change program's behaviour. |
Beta Was this translation helpful? Give feedback.
-
That would be a breaking change. In an expression such as |
Beta Was this translation helpful? Give feedback.
-
In a design note there was a consideration to separate out overload resolution in target-typing scenarios to be able to tweak conversions without breaking change. I think this one would be another case where that could help. https://github.com/dotnet/csharplang/blob/master/meetings/2019/LDM-2019-10-28.md I think the situation is almost the same as |
Beta Was this translation helpful? Give feedback.
-
No, this is give a meaning to code that was definitely an error before. There is no breaking change in that case. |
Beta Was this translation helpful? Give feedback.
-
I was responding to:
Target-typing |
Beta Was this translation helpful? Give feedback.
-
If |
Beta Was this translation helpful? Give feedback.
-
Yes, i mentioned that below:
I'm not saying it's exactly this form. I'm saying i'm willing to champion a form that has no syntactic ambiguity but has some syntactic difference beyond just a simple name. Thanks! |
Beta Was this translation helpful? Give feedback.
-
I have to say, that I don't really get why syntactic ambiguity is so much of a problem here. So meticulously as this topic is approached here, one could argue that properties or fields should also always have a dot in front, when used, to show that they are properties/fields and not class or variable names. But for that kind of ambiguity there is the Or am I totally missing a point here? 🤔 |
Beta Was this translation helpful? Give feedback.
-
@cytoph I'm afraid that would be a breaking change, since code that already has an enum member identified exactly as some other accessible identifier at the same context doesn't issue a warning today, but will issue with the new compiler. |
Beta Was this translation helpful? Give feedback.
-
@Logerfo Ah, I think I get what you mean. In the following case (which is perfectly fine and clear with the current compiler, but with the changes I proposed) the compiler wouldn't be able to tell exactly what I want to assign to the variable
|
Beta Was this translation helpful? Give feedback.
-
Surely if we want this to be a non-breaking change then it's a no-brainer to pick the non-breaking interpretation, that this new interpretation only applies if the name isn't found any other way. Also the new interpretation should only apply if the enum value name is the entire expression; |
Beta Was this translation helpful? Give feedback.
-
@sab39 That would also fit the way as it works with properties/fields and variables: If both are named exactly the same, it's always assumed the variable is used, unless of course you specifically use the |
Beta Was this translation helpful? Give feedback.
-
I think you folks are missing a crucial point. This is not a semantic ambiguity, where we know how to parse the code and just need to do name lookup to decide between one interpretation or another. This is a syntactic ambiguity while trying to parse the code (figure out which punctuation means what). Parsing occurs long before name lookup, and cannot depend on name lookup. |
Beta Was this translation helpful? Give feedback.
-
Hi. Would this work for cases when the outer type is used as a return type? Consider public abstract record Result<TR, TE>
{
public sealed record Ok(TR Value) : Result<TR, TE>;
public sealed record Error(TE Value) : Result<TR, TE>;
} Will this proposal allow me to write some thing like that? Result<int, string> Validate(int value) => value switch
{
> 100 => Ok(value),
_ => Error("Some error text")
};
var result = Validate(101);
int val = result switch
{
Ok(var value) => value,
Error(var value) => throw new InvalidOperationException(value)
}; |
Beta Was this translation helpful? Give feedback.
-
@tuscen I would assume so, since the feature is likely to be built upon the existing target typing rules and return type is considered there. For example, this compiles fine: public class C
{
public object M()
{
return new();
}
} |
Beta Was this translation helpful? Give feedback.
-
Today "using static" solves this problem. If you have to many "using static" - may be it is a time to split code to different classes? :-D Also in this thread I see also problem "simplification of generic names when taget type known". This is other than initial problem! |
Beta Was this translation helpful? Give feedback.
-
No, it doesn't. Consider you initialize GUI tree in code and have HorizontalAlignment and VerticalAlignment enums both having Center entry. |
Beta Was this translation helpful? Give feedback.
-
Interpretation-wise, do you care if it's a local variable or a class member (assuming there is no this. prefix)? If not, then i don't see why you would care if it's an enum value. Hope you elaborate. |
Beta Was this translation helpful? Give feedback.
-
If we end up making |
Beta Was this translation helpful? Give feedback.
-
I would very much prefer explicitness in this case — i.e. use a leading dot (a la Swift) to denote "target-typed" for enums. Makes the rules more straightforward. It also makes things less ambiguous and makes code easier to scan in PRs, etc. |
Beta Was this translation helpful? Give feedback.
-
See #2926 (comment) for a discussion explaining why that proposal leads to a syntactic ambiguity. |
Beta Was this translation helpful? Give feedback.
-
There are three contexts in which lookup of a simple (not qualified) name would benefit from the fallback of looking up the identifier in a target type.
Simple ID expression
When an expression is an unqualified identifier, and lookup fails to find any accessible binding for that identifier, it is treated as target-typed. When the target type becomes available, the identifier is looked up in the context of the target type. If the identifier in that type designates an accessible constant, static field, or static property whose type is that target type, the identifier binds to that member.
Example:
Object creation expression
When an object creation expression's type is given as an unqualified identifier (possibly with type arguments), and lookup fails to find any accessible binding for that identifier, it is treated as target-typed. When the target type becomes available, the identifier is looked up in the context of the target type (and the rest of the creation expression is bound, like the target-typed new expression). If the identifier in that type designates an accessible type that has an implicit reference conversion to that target type, the identifier binds to that type.
Example:
Type in a pattern
When the type appearing in a pattern is given as an unqualified identifier (possibly with type arguments), and lookup fails to find any accessible binding for that identifier, the identifier is looked up in the context of the pattern's input type. If the identifier in that type designates an accessible type, the identifier binds to that accessible type.
Example:
Design Meetings
https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-09-26.md#discriminated-unions
Beta Was this translation helpful? Give feedback.
All reactions