-
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
Forbid unsafe types in records #76588
base: main
Are you sure you want to change the base?
Conversation
[The specification](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-9.0/records.md#members-of-a-record-type) for records forbids the use of unsafe types as an instance field of the type. However, we didn't check that in all cases; we only checked when generating the `Equals` method for the record type. This meant that if the user provided their own definition of `Equals`, they could bypass this restriction. We also did not check for nested unsafe types, so `int*[]` was permitted in any scenario. We have 2 options for fixing this: 1. Treat it as a specification bug, and update the spec to follow the current behavior. We'd therefore fix dotnet#66312 by moving the diagnostic to SourceNamedTypeSymbol.AfterMembersChecks, and only error when the user didn't provide their own implementation of `Equals`. 2. Treat it as a compiler bug, and forbid use of unsafe types in all scenarios. I've opted for path 2 here, but we can discuss whether this is too aggressive and if we should opt for path 1 instead. Fixes dotnet#66312.
We also need to decide what to do with manually-implemented properties that have unsafe types. As pointed out by @hamarb123, even with this change we'll still generate invalid IL for |
|
||
***Introduced in Visual Studio 2022 version 17.14*** | ||
|
||
The specification for `record class` and `record struct` types indicated that any unsafe types are disallowed. However, this was not |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
enforced correctly in 2 scenarios: | ||
|
||
1. When the `record class` or `record struct` type defined its own `Equals` implementation. | ||
2. When the field type only used the unsafe type in a nested context, such as `int*[]`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
diagnostics.Add(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, TypeSyntax?.Location ?? this.GetFirstLocation(), type); | ||
diagnostics.Add(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, getTypeErrorLocation(), type); | ||
} | ||
else if (!this.IsStatic && type.ContainsPointerOrFunctionPointer() && (ContainingType.IsRecord || ContainingType.IsRecordStruct)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
{ | ||
diagnostics.Add(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, TypeLocation, type); | ||
if (!this.IsStatic && type.ContainsPointerOrFunctionPointer() && (ContainingType.IsRecord || ContainingType.IsRecordStruct)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
comp.VerifyEmitDiagnostics( | ||
// (12,22): error CS0723: Cannot declare a variable of static type 'C2' | ||
var comp = CreateCompilation(new[] { src, IsExternalInitTypeDefinition }, options: TestOptions.UnsafeDebugDll, targetFramework: TargetFramework.Mscorlib461); | ||
DiagnosticDescription[] expected = [// (12,22): error CS0723: Cannot declare a variable of static type 'C2' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like more test base-lines need an adjustment |
Done with review pass (commit 1) |
The specification for records forbids the use of unsafe types as an instance field of the type. However, we didn't check that in all cases; we only checked when generating the
Equals
method for the record type. This meant that if the user provided their own definition ofEquals
, they could bypass this restriction. We also did not check for nested unsafe types, soint*[]
was permitted in any scenario. We have 2 options for fixing this:Equals
.I've opted for path 2 here, but we can discuss whether this is too aggressive and if we should opt for path 1 instead. Fixes #66312.