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

Dictionary expressions: support concrete Dictionary<TKey, TValue> target type #76502

Draft
wants to merge 1 commit into
base: features/dictionary-expressions
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -946,7 +946,9 @@ private BoundCollectionExpression ConvertCollectionExpression(
{
GenerateImplicitConversionError(diagnostics, Compilation, syntax, dictionaryConversion, dictionaryType, targetType);
}

}
if (collectionTypeKind is CollectionExpressionTypeKind.Dictionary or CollectionExpressionTypeKind.DictionaryInterface)
{
_ = GetWellKnownTypeMember(WellKnownMember.System_Collections_Generic_Dictionary_KV__ctor, diagnostics, syntax: syntax);
_ = GetWellKnownTypeMember(WellKnownMember.System_Collections_Generic_Dictionary_KV__set_Item, diagnostics, syntax: syntax);
_ = GetWellKnownTypeMember(WellKnownMember.System_Collections_Generic_KeyValuePair_KV__get_Key, diagnostics, syntax: syntax);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ internal enum CollectionExpressionTypeKind
CollectionBuilder,
ImplementsIEnumerable,
ArrayInterface,
Dictionary,
DictionaryInterface,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1678,6 +1678,20 @@ internal static CollectionExpressionTypeKind GetCollectionExpressionTypeKind(CSh
elementType = default;
return CollectionExpressionTypeKind.CollectionBuilder;
}
else if (destination.IsArrayInterface(out elementType))
{
return CollectionExpressionTypeKind.ArrayInterface;
}
else if (isDictionaryType(compilation, destination, WellKnownType.System_Collections_Generic_IDictionary_KV, out elementType) ||
isDictionaryType(compilation, destination, WellKnownType.System_Collections_Generic_IReadOnlyDictionary_KV, out elementType))
{
return CollectionExpressionTypeKind.DictionaryInterface;
}
else if (isDictionaryType(compilation, destination, WellKnownType.System_Collections_Generic_Dictionary_KV, out elementType) &&
compilation.IsFeatureEnabled(MessageID.IDS_FeatureDictionaryExpressions)) // fallback to "implements IEnumerable" for earlier language versions
{
return CollectionExpressionTypeKind.Dictionary;
}
else if (implementsSpecialInterface(compilation, destination, SpecialType.System_Collections_IEnumerable))
{
// ^ This implementation differs from Binder.CollectionInitializerTypeImplementsIEnumerable().
Expand All @@ -1689,14 +1703,6 @@ internal static CollectionExpressionTypeKind GetCollectionExpressionTypeKind(CSh
elementType = default;
return CollectionExpressionTypeKind.ImplementsIEnumerable;
}
else if (destination.IsArrayInterface(out elementType))
{
return CollectionExpressionTypeKind.ArrayInterface;
}
else if (isDictionaryInterface(compilation, destination, out elementType))
{
return CollectionExpressionTypeKind.DictionaryInterface;
}

elementType = default;
return CollectionExpressionTypeKind.None;
Expand All @@ -1708,11 +1714,10 @@ static bool implementsSpecialInterface(CSharpCompilation compilation, TypeSymbol
return allInterfaces.Any(static (a, b) => ReferenceEquals(a.OriginalDefinition, b), specialType);
}

static bool isDictionaryInterface(CSharpCompilation compilation, TypeSymbol targetType, out TypeWithAnnotations elementType)
static bool isDictionaryType(CSharpCompilation compilation, TypeSymbol targetType, WellKnownType dictionaryType, out TypeWithAnnotations elementType)
{
if (targetType is NamedTypeSymbol { Arity: 2 } namedType &&
(ReferenceEquals(namedType.OriginalDefinition, compilation.GetWellKnownType(WellKnownType.System_Collections_Generic_IDictionary_KV)) ||
ReferenceEquals(namedType.OriginalDefinition, compilation.GetWellKnownType(WellKnownType.System_Collections_Generic_IReadOnlyDictionary_KV))))
ReferenceEquals(namedType.OriginalDefinition, compilation.GetWellKnownType(dictionaryType)))
{
elementType = TypeWithAnnotations.Create(compilation.GetWellKnownType(WellKnownType.System_Collections_Generic_KeyValuePair_KV).
Construct(namedType.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics));
Expand Down Expand Up @@ -1753,7 +1758,7 @@ internal static bool IsKeyValuePairType(CSharpCompilation compilation, TypeSymbo
internal static bool CollectionUsesKeyValuePairs(CSharpCompilation compilation, CollectionExpressionTypeKind collectionTypeKind, TypeSymbol elementType, out TypeWithAnnotations keyType, out TypeWithAnnotations valueType)
{
// PROTOTYPE: Should apply to any collection of KeyValuePair<,>, not just dictionaries.
if (collectionTypeKind == CollectionExpressionTypeKind.DictionaryInterface)
if (collectionTypeKind is CollectionExpressionTypeKind.Dictionary or CollectionExpressionTypeKind.DictionaryInterface)
{
bool usesKeyValuePairs = IsKeyValuePairType(compilation, elementType, WellKnownType.System_Collections_Generic_KeyValuePair_KV, out keyType, out valueType);
Debug.Assert(usesKeyValuePairs);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ private BoundExpression RewriteCollectionExpressionConversion(Conversion convers
return VisitCollectionBuilderCollectionExpression(node);
case CollectionExpressionTypeKind.ArrayInterface:
return VisitListInterfaceCollectionExpression(node);
case CollectionExpressionTypeKind.Dictionary:
return CreateAndPopulateDictionary(node, ((NamedTypeSymbol)node.Type).TypeArgumentsWithAnnotationsNoUseSiteDiagnostics, node.Elements);
case CollectionExpressionTypeKind.DictionaryInterface:
return VisitDictionaryInterfaceCollectionExpression(node);
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1249,6 +1249,9 @@ private ICollectionExpressionOperation CreateBoundCollectionExpression(BoundColl
return null;
case CollectionExpressionTypeKind.ImplementsIEnumerable:
return (expr.CollectionCreation as BoundObjectCreationExpression)?.Constructor;
case CollectionExpressionTypeKind.Dictionary:
Debug.Assert(expr.CollectionCreation is null);
return ((MethodSymbol?)compilation.CommonGetWellKnownTypeMember(WellKnownMember.System_Collections_Generic_Dictionary_KV__ctor))?.AsMember((NamedTypeSymbol)expr.Type);
case CollectionExpressionTypeKind.CollectionBuilder:
return expr.CollectionBuilderMethod;
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9381,25 +9381,15 @@ static void Main()
{
Dictionary<int, int> d;
d = [default];
d.Report();
d = [new KeyValuePair<int, int>(1, 2)];
d.Report();
d = [3:4];
d.Report();
}
}
""";
var comp = CreateCompilation(source);
comp.VerifyEmitDiagnostics(
// (7,13): error CS9215: Collection expression type 'Dictionary<int, int>' must have an instance or extension method 'Add' that can be called with a single argument.
// d = [default];
Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[default]").WithArguments("System.Collections.Generic.Dictionary<int, int>").WithLocation(7, 13),
// (8,13): error CS9215: Collection expression type 'Dictionary<int, int>' must have an instance or extension method 'Add' that can be called with a single argument.
// d = [new KeyValuePair<int, int>(1, 2)];
Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[new KeyValuePair<int, int>(1, 2)]").WithArguments("System.Collections.Generic.Dictionary<int, int>").WithLocation(8, 13),
// (9,13): error CS9215: Collection expression type 'Dictionary<int, int>' must have an instance or extension method 'Add' that can be called with a single argument.
// d = [3:4];
Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[3:4]").WithArguments("System.Collections.Generic.Dictionary<int, int>").WithLocation(9, 13),
// (9,14): error CS9268: Collection expression type 'Dictionary<int, int>' does not support key-value pair elements.
// d = [3:4];
Diagnostic(ErrorCode.ERR_CollectionExpressionKeyValuePairNotSupported, "3:4").WithArguments("System.Collections.Generic.Dictionary<int, int>").WithLocation(9, 14));
CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[[0, 0]], [[1, 2]], [[3, 4]], ");
}

[Fact]
Expand Down Expand Up @@ -27301,7 +27291,7 @@ .. GetConfig(),
}
""";

var comp = CreateCompilation(source);
var comp = CreateCompilation(source, parseOptions: TestOptions.Regular13);
comp.VerifyEmitDiagnostics(
// (4,52): error CS9215: Collection expression type 'Dictionary<string, object>' must have an instance or extension method 'Add' that can be called with a single argument.
// Dictionary<string, object> Config => /*<bind>*/[
Expand Down
Loading