Skip to content
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

Record structs: Bind type declaration #51494

Merged
merged 4 commits into from
Mar 6, 2021

Conversation

jcouv
Copy link
Member

@jcouv jcouv commented Feb 25, 2021

Test plan: #51199
Spec: https://github.com/dotnet/csharplang/blob/master/proposals/record-structs.md

This PR implements the following sections, but the parts related to the primary constructor parameters will be handled in the follow-up PR (which synthesizes positional members and primary constructor).

Record struct types are value types, like other struct types. They implicitly inherit from the class System.ValueType.
The modifiers and members of a record struct are subject to the same restrictions as those of structs
(accessibility on type, modifiers on members, base(...) instance constructor initializers,
definite assignment for this in constructor, destructors, ...).
Record structs will also follow the same rules as structs for parameterless instance constructors and field initializers,
but this document assumes that we will lift those restrictions for structs generally.

See https://github.com/dotnet/csharplang/blob/master/spec/structs.md

Record structs cannot use ref modifier.

At most one partial type declaration of a partial record struct may provide a parameter_list.
The parameter_list may not be empty.

Record struct parameters cannot use ref, out or this modifiers (but in and params are allowed).

and

Allow record class

The existing syntax for record types allows record class with the same meaning as record:

record_declaration
   : attributes? class_modifier* 'partial'? 'record' 'class'? identifier type_parameter_list?
     parameter_list? record_base? type_parameter_constraints_clause* record_body
    ;

internal abstract bool IsRecord { get; }

internal abstract bool IsRecordStruct { get; }
Copy link
Member Author

@jcouv jcouv Mar 3, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Alternatively, we could re-use and extend IsRecord. Some places that previously checked IsRecord would need to be updated to also check IsReferenceType. This probably should be discussed more in context of public API we want to expose (tracked in test plan). #Resolved

Copy link
Contributor

@RikkiGibson RikkiGibson Mar 5, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it is convenient to have IsRecordStruct, it might make sense to have the following set of properties

// new property
internal abstract bool IsRecord { get; }

// current IsRecord property renamed (with usages audited to determine what property they should be changed to)
internal bool IsRecordClass => IsRecord && TypeKind == TypeKind.Class;

internal bool IsRecordStruct => IsRecord && TypeKind == TypeKind.Struct;
``` #Resolved

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, that's what I was considering. I have a follow-up in test plan and also PROTOTYPE above to track.


In reply to: 588778787 [](ancestors = 588778787)

@@ -1593,185 +1784,6 @@ record R()
);
}

[Fact(Skip = "record struct")]
Copy link
Member Author

@jcouv jcouv Mar 3, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 All the skipped tests were moved to new file (still skipped for now) #Resolved

public void EmptyRecord_01_RecordClass()
{
var src = @"
record class C();
Copy link
Member Author

@jcouv jcouv Mar 3, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 The class keyword is ignored by the compiler. So I just picked a couple of tests and duplicated them to give record class some coverage. #Resolved

@jcouv jcouv marked this pull request as ready for review March 3, 2021 15:43
@jcouv jcouv requested a review from a team as a code owner March 3, 2021 15:43
}

[Fact]
public void TypeDeclaration_AllowedModifiers()
Copy link
Contributor

@AlekseyTs AlekseyTs Mar 5, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TypeDeclaration_AllowedModifiers [](start = 20, length = 32)

Are we testing an unsafe modifier? #Closed

}

[Fact]
public void TypeDeclaration_DifferentPartials()
Copy link
Contributor

@AlekseyTs AlekseyTs Mar 5, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TypeDeclaration_DifferentPartials [](start = 20, length = 33)

It feels like there are more permutations that we can test. Perhaps we should cover record struct declaration paired with all other kinds of declarations, the same for record class. #Closed

Copy link
Contributor

@AlekseyTs AlekseyTs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM (commit 1), with test suggestions.

@RikkiGibson RikkiGibson self-assigned this Mar 5, 2021
@@ -182,6 +182,7 @@ Global
src\Analyzers\VisualBasic\Analyzers\VisualBasicAnalyzers.projitems*{2531a8c4-97dd-47bc-a79c-b7846051e137}*SharedItemsImports = 5
src\Workspaces\SharedUtilitiesAndExtensions\Compiler\VisualBasic\VisualBasicCompilerExtensions.projitems*{2531a8c4-97dd-47bc-a79c-b7846051e137}*SharedItemsImports = 5
src\Analyzers\Core\Analyzers\Analyzers.projitems*{275812ee-dedb-4232-9439-91c9757d2ae4}*SharedItemsImports = 5
src\Dependencies\Collections\Microsoft.CodeAnalysis.Collections.projitems*{275812ee-dedb-4232-9439-91c9757d2ae4}*SharedItemsImports = 5
Copy link
Contributor

@AlekseyTs AlekseyTs Mar 5, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

src\Dependencies\Collections\Microsoft.CodeAnalysis.Collections.projitems*{275812ee-dedb-4232-9439-91c9757d2ae4}*SharedItemsImports = 5 [](start = 2, length = 135)

Is this an intentional change? #Closed

Copy link
Contributor

@RikkiGibson RikkiGibson Mar 5, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IDE has tended to add this implicitly when using certain refactorings in recent weeks. IIRC we saw this change come in another PR and decided to just merge it. Merging latest main into the feature branch will probably cause this diff to go away. #Resolved

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Reverted


In reply to: 588362066 [](ancestors = 588362066)


var comp = CreateCompilation(src);
comp.VerifyDiagnostics(
// (16,22): error CS0227: Unsafe code may only appear if compiling with /unsafe
Copy link
Contributor

@AlekseyTs AlekseyTs Mar 5, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// (16,22): error CS0227: Unsafe code may only appear if compiling with /unsafe [](start = 16, length = 79)

It looks like this error is covered in the test below. I was looking for a success case. #Closed

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah. Changed to use "unsafe" compilation option


In reply to: 588363595 [](ancestors = 588363595)

@jcouv jcouv requested a review from RikkiGibson March 5, 2021 18:54

var comp = CreateCompilation(src1, parseOptions: TestOptions.Regular9, options: TestOptions.ReleaseDll);
comp.VerifyDiagnostics(
// (2,13): error CS1514: { expected
Copy link
Contributor

@RikkiGibson RikkiGibson Mar 5, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm starting to wonder whether such type declarations should instead be treated as records with a missing 'record' keyword. Not asking you to do it in this PR but I'm curious what you think. #Resolved

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not obvious. If someone is making the mistake of thinking that primary constructors are generally a thing, then they really don't want to add "record" to fix this. I'll add a note for follow-up discussion.


In reply to: 588779583 [](ancestors = 588779583)

var src3 = @"
struct E
{
record struct Point(int x, int y);
Copy link
Contributor

@RikkiGibson RikkiGibson Mar 5, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For completeness consider also nesting this in a namespace #Resolved

@jcouv jcouv merged commit a137cbd into dotnet:features/record-structs Mar 6, 2021
@jcouv jcouv deleted the rs-symbol1 branch March 6, 2021 20:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants