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

Add handling of params collections for natural delegate types #71334

Merged
Merged
Show file tree
Hide file tree
Changes from 3 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
4 changes: 4 additions & 0 deletions src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1329,6 +1329,10 @@ private BoundExpression CreateMethodGroupConversion(SyntaxNode syntax, BoundExpr
{
hasErrors = true;
}
else if (destination is AnonymousTypeManager.AnonymousDelegatePublicSymbol { CheckParamsCollectionsFeatureAvailability: true })
{
MessageID.IDS_ParamsCollections.CheckFeatureAvailability(diagnostics, syntax);
}

Debug.Assert(conversion.UnderlyingConversions.IsDefault);
conversion.MarkUnderlyingConversionsChecked();
Expand Down
13 changes: 7 additions & 6 deletions src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10051,14 +10051,14 @@ bool satisfiesConstraintChecks(MethodSymbol method)
var parameterDefaultValues = parameters.Any(p => p.HasExplicitDefaultValue) ?
parameters.SelectAsArray(p => p.ExplicitDefaultConstantValue) :
default;
// PROTOTYPE(ParamsCollections): Adjust
var hasParamsArray = parameters is [.., { IsParams: true } p] && p.Type.IsSZArray();

var hasParams = OverloadResolution.IsValidParams(this, methodSymbol);

Debug.Assert(ContainingMemberOrLambda is { });
Debug.Assert(parameterRefKinds.IsDefault || parameterRefKinds.Length == parameterTypes.Length);
Debug.Assert(parameterDefaultValues.IsDefault || parameterDefaultValues.Length == parameterTypes.Length);
Debug.Assert(returnType.Type is { }); // Expecting System.Void rather than null return type.
Debug.Assert(!hasParamsArray || parameterTypes.Length != 0);
Debug.Assert(!hasParams || parameterTypes.Length != 0);

bool returnsVoid = returnType.Type.IsVoidType();
var typeArguments = returnsVoid ? parameterTypes : parameterTypes.Add(returnType);
Expand All @@ -10076,7 +10076,7 @@ bool satisfiesConstraintChecks(MethodSymbol method)
}

// Use System.Action<...> or System.Func<...> if possible.
if (!hasParamsArray &&
if (!hasParams &&
returnRefKind == RefKind.None &&
parameterDefaultValues.IsDefault &&
(parameterRefKinds.IsDefault || parameterRefKinds.All(refKind => refKind == RefKind.None)) &&
Expand Down Expand Up @@ -10116,13 +10116,14 @@ bool satisfiesConstraintChecks(MethodSymbol method)
parameterRefKinds.IsDefault ? RefKind.None : parameterRefKinds[i],
parameterScopes.IsDefault ? ScopedKind.None : parameterScopes[i],
parameterDefaultValues.IsDefault ? null : parameterDefaultValues[i],
isParams: hasParamsArray && i == parameterTypes.Length - 1,
isParams: hasParams && i == parameterTypes.Length - 1,
hasUnscopedRefAttribute: parameterHasUnscopedRefAttributes.IsDefault ? false : parameterHasUnscopedRefAttributes[i]));
}
fieldsBuilder.Add(new AnonymousTypeField(name: "", location, returnType, returnRefKind, ScopedKind.None));

var typeDescr = new AnonymousTypeDescriptor(fieldsBuilder.ToImmutableAndFree(), location);
return Compilation.AnonymousTypeManager.ConstructAnonymousDelegateSymbol(typeDescr);
return Compilation.AnonymousTypeManager.ConstructAnonymousDelegateSymbol(typeDescr,
checkParamsCollectionsFeatureAvailability: hasParams && !parameters[^1].Type.IsSZArray() && Compilation.SourceModule != methodSymbol.ContainingModule);

static bool checkConstraints(CSharpCompilation compilation, ConversionsBase conversions, NamedTypeSymbol delegateType, ImmutableArray<TypeWithAnnotations> typeArguments)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public AnonymousTypeField(
bool isParams = false,
bool hasUnscopedRefAttribute = false)
{
Debug.Assert(!isParams || !typeWithAnnotations.Type.IsTypeParameter());
this.Name = name;
this.Location = location;
this.TypeWithAnnotations = typeWithAnnotations;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,9 @@ private NamedTypeSymbol ConstructAnonymousDelegateImplementationSymbol(Anonymous
// Replace `T` with `T[]` for params array.
if (fields is [.., { IsParams: true } lastParam, _])
{
var index = nTypeArguments - 1;
Debug.Assert(lastParam.Type.IsSZArray());

var index = fields.Length - 2;
// T minus `NullabilityAnnotation.Ignored`
var original = TypeWithAnnotations.Create(genericFieldTypes[index].Type);
// T[]
Expand Down Expand Up @@ -328,7 +330,8 @@ static bool isValidTypeArgument(bool useUpdatedEscapeRules, AnonymousTypeField f
return hasDefaultScope(useUpdatedEscapeRules, field) &&
field.Type is { } type &&
!type.IsPointerOrFunctionPointer() &&
!type.IsRestrictedType();
!type.IsRestrictedType() &&
(!field.IsParams || field.Type.IsSZArray()); // [params T collection] is not recognized as a valid params parameter definition
}

static SynthesizedDelegateKey getTemplateKey(AnonymousTypeDescriptor typeDescr, ImmutableArray<TypeParameterSymbol> typeParameters)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ public NamedTypeSymbol ConstructAnonymousTypeSymbol(AnonymousTypeDescriptor type
return new AnonymousTypePublicSymbol(this, typeDescr);
}

public NamedTypeSymbol ConstructAnonymousDelegateSymbol(AnonymousTypeDescriptor typeDescr)
public NamedTypeSymbol ConstructAnonymousDelegateSymbol(AnonymousTypeDescriptor typeDescr, bool checkParamsCollectionsFeatureAvailability)
{
return new AnonymousDelegatePublicSymbol(this, typeDescr);
return new AnonymousDelegatePublicSymbol(this, typeDescr, checkParamsCollectionsFeatureAvailability);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,20 @@ internal sealed class AnonymousDelegatePublicSymbol : AnonymousTypeOrDelegatePub
{
private ImmutableArray<Symbol> _lazyMembers;

internal AnonymousDelegatePublicSymbol(AnonymousTypeManager manager, AnonymousTypeDescriptor typeDescr) :
/// <summary>
/// This member does not participate in equality because it is not reflecting any semantic aspect of the symbol.
/// It is only used to determine if we need to check for <see cref="MessageID.IDS_ParamsCollections"/>
/// feature availability, which happens if this field is set to 'true'.
/// If in the process of merging equivalent types, the one with 'false' wins over the one with 'true'.
/// That is fine, because that means that the feature availability check is performed on a
AlekseyTs marked this conversation as resolved.
Show resolved Hide resolved
/// method declared in this compilation.
/// </summary>
internal readonly bool CheckParamsCollectionsFeatureAvailability;

internal AnonymousDelegatePublicSymbol(AnonymousTypeManager manager, AnonymousTypeDescriptor typeDescr, bool checkParamsCollectionsFeatureAvailability) :
base(manager, typeDescr)
{
CheckParamsCollectionsFeatureAvailability = checkParamsCollectionsFeatureAvailability;
}

internal override NamedTypeSymbol MapToImplementationSymbol()
Expand All @@ -30,7 +41,7 @@ internal override AnonymousTypeOrDelegatePublicSymbol SubstituteTypes(AbstractTy
{
var typeDescr = TypeDescriptor.SubstituteTypes(map, out bool changed);
return changed ?
new AnonymousDelegatePublicSymbol(Manager, typeDescr) :
new AnonymousDelegatePublicSymbol(Manager, typeDescr, CheckParamsCollectionsFeatureAvailability) :
this;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ static SynthesizedDelegateInvokeMethod createInvokeMethod(
// Replace `T` with `T[]` for params array.
if (field.IsParams)
{
Debug.Assert(field.Type.IsSZArray());
Debug.Assert(i == parameterCount - 1);
type = TypeWithAnnotations.Create(ArrayTypeSymbol.CreateSZArray(containingType.ContainingAssembly, type));
}

Expand Down
Loading