Skip to content

Commit

Permalink
Merge pull request #72796 from cston/merge-CollectionAdd
Browse files Browse the repository at this point in the history
Collection expressions: require Add method callable with a single argument for class and struct types that do not use [CollectionBuilder]
  • Loading branch information
cston authored Mar 29, 2024
2 parents fd65b1a + 46163a6 commit 51523ba
Show file tree
Hide file tree
Showing 35 changed files with 5,924 additions and 1,823 deletions.
20 changes: 1 addition & 19 deletions docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public class C
*Conversion* of a collection expression to a `struct` or `class` that implements `System.Collections.IEnumerable` and *does not* have a `CollectionBuilderAttribute`
requires the target type to have an accessible constructor that can be called with no arguments and,
if the collection expression is not empty, the target type must have an accessible `Add` method
that can be called with a single argument of [*iteration type*](https://github.com/dotnet/csharpstandard/blob/standard-v7/standard/statements.md#1395-the-foreach-statement) of the target type.
that can be called with a single argument.

Previously, the constructor and `Add` methods were required for *construction* of the collection instance but not for *conversion*.
That meant the following call was ambiguous since both `char[]` and `string` were valid target types for the collection expression.
Expand All @@ -47,24 +47,6 @@ static void Print(char[] arg) { }
static void Print(string arg) { }
```

Previously, the collection expression in `y = [1, 2, 3]` was allowed since construction only requires an applicable `Add` method for each element expression.
The collection expression is now an error because of the conversion requirement for an `Add` method than be called with an argument of the iteration type `object`.
```csharp
// ok: Add is not required for empty collection
MyCollection x = [];

// error CS9215: Collection expression type must have an applicable instance or extension method 'Add'
// that can be called with an argument of iteration type 'object'.
// The best overloaded method is 'MyCollection.Add(int)'.
MyCollection y = [1, 2, 3];

class MyCollection : IEnumerable
{
public void Add(int i) { ... }
IEnumerator IEnumerable.GetEnumerator() { ... }
}
```

## `ref` arguments can be passed to `in` parameters

***Introduced in Visual Studio 2022 version 17.8p2***
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5270,16 +5270,6 @@ public void Add(string s) { }
{
OutputKind = OutputKind.DynamicallyLinkedLibrary,
},
FixedState =
{
ExpectedDiagnostics =
{
// /0/Test0.cs(6,26): error CS1503: Argument 1: cannot convert from 'object' to 'string'
DiagnosticResult.CompilerError("CS1503").WithSpan(6, 26, 6, 36).WithArguments("1", "object", "string"),
// /0/Test0.cs(6,26): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'object'. The best overloaded method is 'MyCollection.Add(string)'.
DiagnosticResult.CompilerError("CS9215").WithSpan(6, 26, 6, 36).WithArguments("object", "MyCollection.Add(string)"),
}
}
}.RunAsync();
}

Expand Down
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ private BoundIndexerAccess BindIndexerDefaultArgumentsAndParamsCollection(BoundI
}
}

BindDefaultArgumentsAndParamsCollection(indexerAccess.Syntax, parameters, argumentsBuilder, refKindsBuilderOpt, namesBuilder, ref argsToParams, out defaultArguments, indexerAccess.Expanded, enableCallerInfo: true, diagnostics);
BindDefaultArguments(indexerAccess.Syntax, parameters, argumentsBuilder, refKindsBuilderOpt, namesBuilder, ref argsToParams, out defaultArguments, indexerAccess.Expanded, enableCallerInfo: true, diagnostics);

if (namesBuilder is object)
{
Expand Down
12 changes: 8 additions & 4 deletions src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ private BoundAttribute BindAttributeCore(AttributeSyntax node, NamedTypeSymbol a
Binder attributeArgumentBinder = this.WithAdditionalFlags(BinderFlags.AttributeArgument);
AnalyzedAttributeArguments analyzedArguments = attributeArgumentBinder.BindAttributeArguments(argumentListOpt, attributeTypeForBinding, diagnostics);

ImmutableArray<int> argsToParamsOpt = default;
ImmutableArray<int> argsToParamsOpt;
bool expanded = false;
BitVector defaultArguments = default;
MethodSymbol? attributeConstructor = null;
Expand All @@ -191,6 +191,7 @@ private BoundAttribute BindAttributeCore(AttributeSyntax node, NamedTypeSymbol a
boundConstructorArguments = analyzedArguments.ConstructorArguments.Arguments.SelectAsArray(
static (arg, attributeArgumentBinder) => attributeArgumentBinder.BindToTypeForErrorRecovery(arg),
attributeArgumentBinder);
argsToParamsOpt = default;
}
else
{
Expand All @@ -210,12 +211,15 @@ private BoundAttribute BindAttributeCore(AttributeSyntax node, NamedTypeSymbol a

if (memberResolutionResult.IsNotNull)
{
this.CheckAndCoerceArguments<MethodSymbol>(memberResolutionResult, analyzedArguments.ConstructorArguments, diagnostics, receiver: null, invokedAsExtensionMethod: false);
this.CheckAndCoerceArguments<MethodSymbol>(node, memberResolutionResult, analyzedArguments.ConstructorArguments, diagnostics, receiver: null, invokedAsExtensionMethod: false, out argsToParamsOpt);
}
else
{
argsToParamsOpt = memberResolutionResult.Result.ArgsToParamsOpt;
}

attributeConstructor = memberResolutionResult.Member;
expanded = memberResolutionResult.Resolution == MemberResolutionKind.ApplicableInExpandedForm;
argsToParamsOpt = memberResolutionResult.Result.ArgsToParamsOpt;

if (!found)
{
Expand All @@ -229,7 +233,7 @@ private BoundAttribute BindAttributeCore(AttributeSyntax node, NamedTypeSymbol a
}
else
{
attributeArgumentBinder.BindDefaultArgumentsAndParamsCollection(
attributeArgumentBinder.BindDefaultArguments(
node,
attributeConstructor.Parameters,
analyzedArguments.ConstructorArguments.Arguments,
Expand Down
Loading

0 comments on commit 51523ba

Please sign in to comment.