-
Notifications
You must be signed in to change notification settings - Fork 4k
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
Align params
type validation with the the spec
#71918
Align params
type validation with the the spec
#71918
Conversation
@RikkiGibson, @333fred, @dotnet/roslyn-compiler Please review. |
@RikkiGibson, @333fred, @dotnet/roslyn-compiler Please review. |
2 similar comments
@RikkiGibson, @333fred, @dotnet/roslyn-compiler Please review. |
@RikkiGibson, @333fred, @dotnet/roslyn-compiler Please review. |
implicitReceiver, | ||
out addMethods); | ||
|
||
// This is what BindCollectionInitializerElementAddMethod is doing in terms of reporting diagnostics and detecting a failure |
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.
This seems like a lot of code that needs to stay in sync with BindCollectionInitializerElementAddMethod
. Is the reason for duplicating the code to handle cycles and return the add methods, and is there an easy way to use the existing Binder
methods? For instance, if the reason is to handle cycles, could we use throw an exception at the point HasParamsCollectionTypeInProgress(namedType)
is determined and have the outer most caller that created the ParamsCollectionTypeInProgressBinder
catch that case? #Resolved
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.
There are several reasons. Main reasons are to reduce amount of binding and have a better control what is causing a failure. And cycles, of course. For example, in general we are not supposed to make semantic decisions based on BoundNode.HasErrors
flag, but we did. We are trying to not set the flag too liberally, but that guideline is not always followed (especially in a very old code), because we are not supposed to use its value for semantic decisions. Also, I did find examples when some errors that are not supposed to fail conversion classification failed it, etc. I prefer to have a little code duplication, but have a better control over what we are doing. I do not expect the methods to change much in the future. In the process, in the new code I was able to drop a lot of original code that is not relevant to our scenarios and cause. Therefore, no need to think about "protecting" those code paths. The chosen approach gives me greater confidence that we are doing the right thing from behavior point of view.
if (!paramsType.IsSZArray()) | ||
{ | ||
var discardedUseSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded; | ||
emptyCollection ??= new BoundUnconvertedCollectionExpression(node, ImmutableArray<BoundNode>.CastUp(ImmutableArray<BoundExpression>.Empty)) { WasCompilerGenerated = true, IsParamsCollection = true }; |
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.
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.
No, we need this array of BoundExpression
underneath, so that we could do a cast down without allocation/copy, if needed. Perhaps for an empty array, there isn't much to allocate and copy, but I prefer to be consistent
case CollectionExpressionTypeKind.ImplementsIEnumerable: | ||
case CollectionExpressionTypeKind.CollectionBuilder: | ||
{ | ||
binder.TryGetCollectionIterationType(CSharpSyntaxTree.Dummy.GetRoot(), type, out elementType); |
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.
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.
Consider extracting a local.
Done
} | ||
} | ||
|
||
void reportInvalidParams(BindingDiagnosticBag diagnostics) |
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.
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.
Consider declaring this local function and the two above at the same scope.
Done
@@ -3707,30 +4937,12 @@ static void Main() | |||
|
|||
// Inline collection expression results in an ambiguity. |
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.
Is this comment still accurate? I thought collection-expressions also made a change to make this conversion not applicable when the expected APIs are not present on the collection type. #Resolved
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.
Is this comment still accurate?
Will remove. Thanks.
var x = (params int a) => a; | ||
local (0); | ||
|
||
int local(params int a) => a; |
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's possible I missed it, but I did not see a test case which demonstrates the lack of accessibility checks on the constructor and Add method when the params parameter is in a local function or lambda. Please add such a test if one does not already exist, for example:
using System.Collections;
using System.Collections.Generic;
public class MyCollection : IEnumerable<string>
{
internal MyCollection() { }
internal void Add(string s) { }
IEnumerator<string> IEnumerable<string>.GetEnumerator() => throw null!;
IEnumerator IEnumerable.GetEnumerator() => throw null!;
}
public class Program
{
public void Test1()
{
local();
local("a");
// expect no diagnostic
void local(params MyCollection collection) { }
// expect no diagnostic
var x = (params MyCollection collection) => { };
}
// expect a declaration diagnostic for insufficient accessibility of MyCollection..ctor/Add()
public void Test2(params MyCollection collection)
{
}
}
``` #Resolved
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's possible I missed it, but I did not see a test case which demonstrates the lack of accessibility checks on the constructor and Add method when the params parameter is in a local function or lambda. Please add such a test if one does not already exist
Added ImplementsIEnumerableT_20_LessAccessibleConstructorAndAdd_NoError_In_LambdaOrLocalFunction
binder.ReportDiagnosticsIfObsolete(diagnostics, method, node, hasBaseReceiver: false); | ||
// NOTE: Use-site diagnostics were reported during overload resolution. | ||
|
||
CheckRequiredMembersInObjectInitializer(method, initializers: default, node, diagnostics); |
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.
Perhaps we should have a test for when the collection type has a required property. I would expect an error when using 'params' with such a type or when converting to the type from collection expression. But that the conversion would still exist. #Resolved
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.
Perhaps we should have a test for when the collection type has a required property. I would expect an error when using 'params' with such a type or when converting to the type from collection expression. But that the conversion would still exist.
Added tests, they behave as you expect, but I am not sure whether I am happy about that fact.
type: GetSpecialType(SpecialType.System_Void, diagnostics, elementInitializer), | ||
hasErrors: hasErrors); | ||
var d = BindingDiagnosticBag.GetInstance(); | ||
bool toCheck = collectionInitializerAddMethodBinder.HasCollectionExpressionApplicableAddMethod(elementInitializer, implicitReceiver.Type, boundElementInitializerExpressions[0].Type, addMethods: out _, d); |
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.
Please add a comment to explain what this assert is checking in future PR #Resolved
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.
Please add a comment to explain what this assert is checking in future PR
Added a comment.
@333fred, @dotnet/roslyn-compiler For the second review |
1 similar comment
@333fred, @dotnet/roslyn-compiler For the second review |
Consider including: Test([]);
Test([4]);
``` #Closed
---
Refers to: src/Compilers/CSharp/Test/Emit2/Semantics/ParamsCollectionTests.cs:10458 in 9562c7c. [](commit_id = 9562c7c9baa1bc082c7e969a8cfd8eb04d26d4ee, deletion_comment = False) |
Looks like these cases are covered in In reply to: 1932705423 Refers to: src/Compilers/CSharp/Test/Emit2/Semantics/ParamsCollectionTests.cs:10458 in 9562c7c. [](commit_id = 9562c7c, deletion_comment = False) |
Looks like a dupe indeed. In reply to: 1932682227 Refers to: src/Compilers/CSharp/Test/Emit2/Semantics/ParamsCollectionTests.cs:1019 in 9562c7c. [](commit_id = 9562c7c, deletion_comment = False) |
The runtime behavior isn't something that I am interested in. We can "bind" creation and that is good enough for the purpose of the feature. In reply to: 1932688118 Refers to: src/Compilers/CSharp/Test/Emit2/Semantics/ParamsCollectionTests.cs:1885 in 9562c7c. [](commit_id = 9562c7c, deletion_comment = False) |
The runtime behavior isn't something that I am interested in. We can "bind" creation and that is good enough for the purpose of the feature. In reply to: 1932688311 Refers to: src/Compilers/CSharp/Test/Emit2/Semantics/ParamsCollectionTests.cs:1925 in 9562c7c. [](commit_id = 9562c7c, deletion_comment = False) |
To clarify, the process of creating the collection and adding elements to it is handled by Collection Expressions code In reply to: 1932813913 Refers to: src/Compilers/CSharp/Test/Emit2/Semantics/ParamsCollectionTests.cs:1925 in 9562c7c. [](commit_id = 9562c7c, deletion_comment = False) |
Since I am not planning to make other changes in this PR. I am going to merge as is and get rid of the dupe in the next PR In reply to: 1932710683 Refers to: src/Compilers/CSharp/Test/Emit2/Semantics/ParamsCollectionTests.cs:1019 in 9562c7c. [](commit_id = 9562c7c, deletion_comment = False) |
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.
Resolution of the PR comments looks good to me, thanks.
Removed the dupe in #72000 In reply to: 1932839926 Refers to: src/Compilers/CSharp/Test/Emit2/Semantics/ParamsCollectionTests.cs:1019 in 9562c7c. [](commit_id = 9562c7c, deletion_comment = False) |
https://github.com/dotnet/csharplang/blob/main/proposals/params-collections.md#method-parameters