6.0 KiB
C# Language Design Meeting for June 6, 2022
Agenda
Discussion
Open issues for ref
fields
https://github.com/dotnet/csharplang/issues/6149
We discussed the questions out of order because of the way we perceived them to influence each other.
Question 5: scoped
differences in overrides and interface implementations
When overrides or implementations differ from their base in terms of scoped
what should we do? We seem to have the following options:
- Do nothing: no restrictions for 'scoped' in override/implementation (unsafe)
- Disallow use of 'scoped' in overrides/interfaces
- Disallow differences in 'scoped' in override/implementation
- Allow adding 'scoped', disallow dropping 'scoped' in override/implementation
Option 1 and 2 do not seem helpful. Option 3 is simple, allows expressing reasonable things and is safe.Option 4 is a bit more involved, but is more expressive too, and seems more consistent with nullability etc.
Conclusion
We lean to 4, but do not have important scenarios. We're fine if we only make it to 3 now and potentially do 4 later, which will be compatible. We'd need to spend the time to assure ourselves that 4 is safe.
Question 6. scoped
differences in delegate conversions
Similar question and similar options, but for conversion of lambdas and method groups to delegate types.
Conclusion
This should do the same as in the previous question. Even for explicit conversion we should not break scoped
safety. There are unsafe APIs for breaking the rules if people really want to.
Inferred delegate types will preserve scoped
from their lambda or method group.
Question 4. Constructor that does not set ref field
Should we report a diagnostic on a constructor that doesn't initialize a ref field? It will lead to a null ref exception later.
On the one hand we have a set of rules for non-nullable reference types that we could probably adapt to ref fields. On the other hand this is an advanced feature set and this may not be worth it. Also we can't do anything about default values of the ref struct, which are likely to be common.
The runtime is planning to do work so that null ref dereference behavior is well defined, as no matter what, safe code can now create ref fields with nulls in them. The runtime will ensure that all locals with references will be zeroed even if SkipLocalsInit
is specified.
Conclusion
No diagnostics - it's not worth it, wouldn't save every case anyway, and this is an expert feature.
Question 3. Ref assignment in object initializers
Constructors allow ref parameters to be assigned to ref fields. Should object initializers also allow ref assignment?
Conclusion
Yes. There's no reason not to, it's just a little bit of syntax and it's obvious what it does.
Open issues for static virtual members
Adjusting the type argument rule
Should we allow interfaces to be type arguments if they have virtual but not abstract static members - i.e. if all virtual static members have a default implementation?
If we don't allow this, adding a static virtual with a default implementation to an existing interface that has no static virtuals will break anyone currently passing that interface as a type argument. This breaks the principle that new interface members with default implementations don't break existing code.
Conclusion
Let's loosen the rule to preserve the value of default implementations, assuming that we can loosen the corresponding rule in the runtime.
Virtual equality and conversion operators
Should virtual equality and conversion operators be allowed to have default implementations, and to provide implementations in overrides? If so are there any restrictions?
Currently interfaces are not allowed to declare non-virtual equality and conversion operators. The reason is that such operators too easily lead to confusing or conflicting behavior on the interfaces. However, virtual operators do not apply to the interfaces themselves but only to type parameters constrained by them, so they do not seem to have these same problems.
Nevertheless, an interface IThing
with a declaration of the form
public interface IThing
{
static virtual bool operator ==(IThing i1, IThing i2) => ...
...
}
would be quite confusing, especially since it would not apply to instances of the interface itself:
var result = i1 == i2; // Reference equality
On the other hand, a declaration that has "self-type" operands would probably be a lot harder to misunderstand in that way:
public interface IThing<TSelf> where TSelf : IThing<TSelf>
{
static virtual bool operator ==(TSelf i1, TSelf i2) => ...
...
}
Conversions are already not allowed to have interface operands directly.
Conclusion
Both types of operand declarations should be allowed to have default implementations.
We will limit equality operators so that one of the operands cannot be the interface itself and has to be a self-constrained type parameter. The other can be anything (including an interface).
Concatenation of Utf8 literals
https://github.com/dotnet/roslyn/issues/60896#issuecomment-1129362977
The motivating scenario is line breaking source code containing very large Utf8 string literals.
Should we wait for a +
operator for Utf8 strings (ReadOnlySpan<byte>
) in general? Is such a thing even desirable? After all it would be expensive at runtime, and concatenation might not generally be the obvious semantics for +
on spans - even on spans of bytes. However, on literals its meaning is obvious.
Conclusion
Let's do it. Let's make sure people get good error messages when they try to +
non-literals.