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

Emit ParamArrayAttribute for synthesized delegates #65533

Merged
merged 15 commits into from
Nov 29, 2022
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
19 changes: 15 additions & 4 deletions src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6781,7 +6781,8 @@ private BoundExpression BindInstanceMemberAccess(
lookupResult.Symbols.All(s => s.Kind == SymbolKind.Method) ? lookupResult.Symbols.SelectAsArray(s_toMethodSymbolFunc) : ImmutableArray<MethodSymbol>.Empty,
lookupResult,
flags,
this);
this,
diagnostics);
jjonescz marked this conversation as resolved.
Show resolved Hide resolved

if (!boundMethodGroup.HasErrors && typeArgumentsSyntax.Any(SyntaxKind.OmittedTypeArgument))
{
Expand Down Expand Up @@ -8945,15 +8946,15 @@ private MethodGroupResolution ResolveDefaultMethodGroup(
}

#nullable enable
internal NamedTypeSymbol? GetMethodGroupDelegateType(BoundMethodGroup node)
internal NamedTypeSymbol? GetMethodGroupDelegateType(BoundMethodGroup node, BindingDiagnosticBag diagnostics)
{
var method = GetUniqueSignatureFromMethodGroup(node);
if (method is null)
{
return null;
}

return GetMethodGroupOrLambdaDelegateType(node.Syntax, method);
return GetMethodGroupOrLambdaDelegateType(node.Syntax, method, diagnostics);
}

/// <summary>
Expand Down Expand Up @@ -9037,6 +9038,7 @@ static bool isCandidateUnique(ref MethodSymbol? method, MethodSymbol candidate)
internal NamedTypeSymbol? GetMethodGroupOrLambdaDelegateType(
SyntaxNode syntax,
MethodSymbol methodSymbol,
BindingDiagnosticBag diagnostics,
ImmutableArray<DeclarationScope>? parameterScopesOverride = null,
RefKind? returnRefKindOverride = null,
TypeWithAnnotations? returnTypeOverride = null)
Expand Down Expand Up @@ -9108,6 +9110,7 @@ static bool isCandidateUnique(ref MethodSymbol? method, MethodSymbol candidate)
var location = syntax.Location;
for (int i = 0; i < parameterTypes.Length; i++)
{
var isParams = hasParamsArray && i == parameterTypes.Length - 1;
fieldsBuilder.Add(
new AnonymousTypeField(
name: "",
Expand All @@ -9116,8 +9119,16 @@ static bool isCandidateUnique(ref MethodSymbol? method, MethodSymbol candidate)
parameterRefKinds.IsDefault ? RefKind.None : parameterRefKinds[i],
parameterScopes.IsDefault ? DeclarationScope.Unscoped : parameterScopes[i],
parameterDefaultValues.IsDefault ? null : parameterDefaultValues[i],
isParams: hasParamsArray && i == parameterTypes.Length - 1)
isParams: isParams)
);

if (isParams)
{
ReportUseSiteDiagnosticForSynthesizedAttribute(Compilation,
WellKnownMember.System_ParamArrayAttribute__ctor,
diagnostics,
parameters[i].Locations is [var loc, ..] ? loc : syntax.GetLocation());
}
}
fieldsBuilder.Add(new AnonymousTypeField(name: "", location, returnType, returnRefKind, DeclarationScope.Unscoped));

Expand Down
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ private UnboundLambda AnalyzeAnonymousFunction(

namesBuilder.Free();

return UnboundLambda.Create(syntax, this, diagnostics.AccumulatesDependencies, returnRefKind, returnType, parameterAttributes, refKinds, scopes, types, names, discardsOpt, parameterSyntaxList, defaultValues, isAsync: isAsync, isStatic: isStatic, hasParamsArray: hasParamsArray);
return UnboundLambda.Create(syntax, this, diagnostics, returnRefKind, returnType, parameterAttributes, refKinds, scopes, types, names, discardsOpt, parameterSyntaxList, defaultValues, isAsync: isAsync, isStatic: isStatic, hasParamsArray: hasParamsArray);

static ImmutableArray<bool> computeDiscards(SeparatedSyntaxList<ParameterSyntax> parameters, int underscoresCount)
{
Expand Down
1 change: 1 addition & 0 deletions src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1359,6 +1359,7 @@ private BoundMethodOrPropertyGroup ConstructBoundMemberGroupAndReportOmittedType
lookupResult,
methodGroupFlags,
this,
diagnostics,
hasErrors);

case SymbolKind.Property:
Expand Down
7 changes: 4 additions & 3 deletions src/Compilers/CSharp/Portable/BoundTree/BoundMethodGroup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,16 @@ public BoundMethodGroup(
LookupResult lookupResult,
BoundMethodGroupFlags flags,
Binder binder,
BindingDiagnosticBag diagnostics,
bool hasErrors = false)
: this(syntax, typeArgumentsOpt, name, methods, lookupResult.SingleSymbolOrDefault, lookupResult.Error, flags, functionType: GetFunctionType(binder, syntax), receiverOpt, lookupResult.Kind, hasErrors)
: this(syntax, typeArgumentsOpt, name, methods, lookupResult.SingleSymbolOrDefault, lookupResult.Error, flags, functionType: GetFunctionType(binder, syntax, diagnostics), receiverOpt, lookupResult.Kind, hasErrors)
{
FunctionType?.SetExpression(this);
}

private static FunctionTypeSymbol? GetFunctionType(Binder binder, SyntaxNode syntax)
private static FunctionTypeSymbol? GetFunctionType(Binder binder, SyntaxNode syntax, BindingDiagnosticBag diagnostics)
{
return FunctionTypeSymbol.CreateIfFeatureEnabled(syntax, binder, static (binder, expr) => binder.GetMethodGroupDelegateType((BoundMethodGroup)expr));
return FunctionTypeSymbol.CreateIfFeatureEnabled(syntax, binder, diagnostics, static (binder, expr, diagnostics) => binder.GetMethodGroupDelegateType((BoundMethodGroup)expr, diagnostics));
}

public MemberAccessExpressionSyntax? MemberAccessExpressionSyntax
Expand Down
9 changes: 5 additions & 4 deletions src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,7 @@ internal partial class UnboundLambda
public static UnboundLambda Create(
CSharpSyntaxNode syntax,
Binder binder,
bool withDependencies,
BindingDiagnosticBag diagnostics,
RefKind returnRefKind,
TypeWithAnnotations returnType,
ImmutableArray<SyntaxList<AttributeListSyntax>> parameterAttributes,
Expand All @@ -395,9 +395,9 @@ public static UnboundLambda Create(
Debug.Assert(syntax.IsAnonymousFunction());
bool hasErrors = !types.IsDefault && types.Any(static t => t.Type?.Kind == SymbolKind.ErrorType);

var functionType = FunctionTypeSymbol.CreateIfFeatureEnabled(syntax, binder, static (binder, expr) => ((UnboundLambda)expr).Data.InferDelegateType());
var functionType = FunctionTypeSymbol.CreateIfFeatureEnabled(syntax, binder, diagnostics, static (binder, expr, diagnostics) => ((UnboundLambda)expr).Data.InferDelegateType(diagnostics));
var data = new PlainUnboundLambdaState(binder, returnRefKind, returnType, parameterAttributes, names, discardsOpt, types, refKinds, declaredScopes, defaultValues, syntaxList, isAsync: isAsync, isStatic: isStatic, hasParamsArray: hasParamsArray, includeCache: true);
var lambda = new UnboundLambda(syntax, data, functionType, withDependencies, hasErrors: hasErrors);
var lambda = new UnboundLambda(syntax, data, functionType, withDependencies: diagnostics.AccumulatesDependencies, hasErrors: hasErrors);
data.SetUnboundLambda(lambda);
functionType?.SetExpression(lambda.WithNoCache());

Expand Down Expand Up @@ -642,7 +642,7 @@ private static TypeWithAnnotations DelegateReturnTypeWithAnnotations(MethodSymbo
return (parameterRefKinds, parameterScopesBuilder, parameterTypes, getEffectiveScopeFromSymbol);
}

internal NamedTypeSymbol? InferDelegateType()
internal NamedTypeSymbol? InferDelegateType(BindingDiagnosticBag diagnostics)
{
Debug.Assert(Binder.ContainingMemberOrLambda is { });

Expand Down Expand Up @@ -710,6 +710,7 @@ private static TypeWithAnnotations DelegateReturnTypeWithAnnotations(MethodSymbo
return Binder.GetMethodGroupOrLambdaDelegateType(
_unboundLambda.Syntax,
lambdaSymbol,
diagnostics,
parameterScopesBuilder.ToImmutableAndFree(),
returnRefKind,
returnType);
Expand Down
13 changes: 8 additions & 5 deletions src/Compilers/CSharp/Portable/Symbols/FunctionTypeSymbol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,21 +37,23 @@ internal sealed class FunctionTypeSymbol : TypeSymbol
private static readonly NamedTypeSymbol Uninitialized = new UnsupportedMetadataTypeSymbol();

private readonly Binder? _binder;
private readonly Func<Binder, BoundExpression, NamedTypeSymbol?>? _calculateDelegate;
private readonly BindingDiagnosticBag? _diagnostics;
private readonly Func<Binder, BoundExpression, BindingDiagnosticBag, NamedTypeSymbol?>? _calculateDelegate;

private BoundExpression? _expression;
private NamedTypeSymbol? _lazyDelegateType;

internal static FunctionTypeSymbol? CreateIfFeatureEnabled(SyntaxNode syntax, Binder binder, Func<Binder, BoundExpression, NamedTypeSymbol?> calculateDelegate)
internal static FunctionTypeSymbol? CreateIfFeatureEnabled(SyntaxNode syntax, Binder binder, BindingDiagnosticBag diagnostics, Func<Binder, BoundExpression, BindingDiagnosticBag, NamedTypeSymbol?> calculateDelegate)
{
return syntax.IsFeatureEnabled(MessageID.IDS_FeatureInferredDelegateType) ?
new FunctionTypeSymbol(binder, calculateDelegate) :
new FunctionTypeSymbol(binder, diagnostics, calculateDelegate) :
null;
}

private FunctionTypeSymbol(Binder binder, Func<Binder, BoundExpression, NamedTypeSymbol?> calculateDelegate)
private FunctionTypeSymbol(Binder binder, BindingDiagnosticBag diagnostics, Func<Binder, BoundExpression, BindingDiagnosticBag, NamedTypeSymbol?> calculateDelegate)
{
_binder = binder;
_diagnostics = diagnostics;
_calculateDelegate = calculateDelegate;
_lazyDelegateType = Uninitialized;
}
Expand Down Expand Up @@ -79,10 +81,11 @@ internal void SetExpression(BoundExpression expression)
if ((object?)_lazyDelegateType == Uninitialized)
{
Debug.Assert(_binder is { });
Debug.Assert(_diagnostics is { });
Debug.Assert(_calculateDelegate is { });
Debug.Assert(_expression is { });

var delegateType = _calculateDelegate(_binder, _expression);
var delegateType = _calculateDelegate(_binder, _expression, _diagnostics);
var result = Interlocked.CompareExchange(ref _lazyDelegateType, delegateType, Uninitialized);

if (_binder.Compilation.TestOnlyCompilationData is InferredDelegateTypeData data &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,12 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r
{
AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeIsReadOnlyAttribute(this));
}

if (this.IsParams)
jjonescz marked this conversation as resolved.
Show resolved Hide resolved
{
Debug.Assert(this.ContainingSymbol is SynthesizedDelegateInvokeMethod);
AddSynthesizedAttribute(ref attributes, compilation.TrySynthesizeAttribute(WellKnownMember.System_ParamArrayAttribute__ctor));
jjonescz marked this conversation as resolved.
Show resolved Hide resolved
}
}

internal override ImmutableArray<int> InterpolatedStringHandlerArgumentIndexes => ImmutableArray<int>.Empty;
Expand Down Expand Up @@ -304,6 +310,7 @@ public SynthesizedComplexParameterSymbol(
Debug.Assert(!refCustomModifiers.IsDefault);
Debug.Assert(isParams || !refCustomModifiers.IsEmpty || baseParameterForAttributes is object || defaultValue is not null);
Debug.Assert(baseParameterForAttributes is null || baseParameterForAttributes.ExplicitDefaultConstantValue == defaultValue);
Debug.Assert(!isParams || container is SynthesizedDelegateInvokeMethod, "Synthesized params array parameter is only supported on delegates");

_refCustomModifiers = refCustomModifiers;
_baseParameterForAttributes = baseParameterForAttributes;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13927,6 +13927,20 @@ public abstract class MulticastDelegate : Delegate { }
Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "2m").WithArguments("System.Decimal").WithLocation(2, 24));
}

[Fact]
public void ParamsArray_MissingParamArrayAttribute()
{
var source = """
var lam = (params int[] xs) => xs.Length;
jjonescz marked this conversation as resolved.
Show resolved Hide resolved
""";
var comp = CreateCompilation(source);
comp.MakeTypeMissing(WellKnownType.System_ParamArrayAttribute);
comp.VerifyDiagnostics(
// (1,25): error CS0656: Missing compiler required member 'System.ParamArrayAttribute..ctor'
jjonescz marked this conversation as resolved.
Show resolved Hide resolved
// var lam = (params int[] xs) => xs.Length;
Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "xs").WithArguments("System.ParamArrayAttribute", ".ctor").WithLocation(1, 25));
}

[Fact]
public void ParamsArray_SynthesizedTypesMatch()
{
Expand Down Expand Up @@ -14011,6 +14025,10 @@ instance void Invoke (
int32[] arg2
) runtime managed
{
.param [2]
.custom instance void [{{s_libPrefix}}]System.ParamArrayAttribute::.ctor() = (
jjonescz marked this conversation as resolved.
Show resolved Hide resolved
01 00 00 00
)
} // end of method '<>f__AnonymousDelegate0'::Invoke
} // end of class <>f__AnonymousDelegate0
""");
Expand Down