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

Use ParamCollectionAttribute to mark params collections in metadata #71747

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
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ public override bool HasExplicitReturnType(out RefKind refKind, out TypeWithAnno
public override int ParameterCount { get { return _parameters.Length; } }
public override bool IsAsync { get { return false; } }
public override bool IsStatic => false;
public override bool HasParamsArray => false;
public override RefKind RefKind(int index) { return Microsoft.CodeAnalysis.RefKind.None; }
public override ScopedKind DeclaredScope(int index) => ScopedKind.None;
public override MessageID MessageID { get { return MessageID.IDS_FeatureQueryExpression; } } // TODO: what is the correct ID here?
Expand Down
6 changes: 4 additions & 2 deletions src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4795,7 +4795,8 @@ private SignatureOnlyMethodSymbol GetInlineArrayAccessEquivalentSignatureMethod(
ImmutableArray.Create<ParameterSymbol>(new SignatureOnlyParameterSymbol(
TypeWithAnnotations.Create(elementAccess.Expression.Type),
ImmutableArray<CustomModifier>.Empty,
isParams: false,
isParamArray: false,
isParamCollection: false,
parameterRefKind
)),
resultRefKind,
Expand Down Expand Up @@ -4834,7 +4835,8 @@ private SignatureOnlyMethodSymbol GetInlineArrayConversionEquivalentSignatureMet
ImmutableArray.Create<ParameterSymbol>(new SignatureOnlyParameterSymbol(
TypeWithAnnotations.Create(inlineArray.Type),
ImmutableArray<CustomModifier>.Empty,
isParams: false,
isParamArray: false,
isParamCollection: false,
parameterRefKind
)),
RefKind.None,
Expand Down
14 changes: 8 additions & 6 deletions src/Compilers/CSharp/Portable/Binder/Binder_Await.cs
Original file line number Diff line number Diff line change
Expand Up @@ -344,9 +344,10 @@ private bool GetGetAwaiterMethod(BoundExpression expression, SyntaxNode node, Bi
return false;
}

var getAwaiterMethod = ((BoundCall)getAwaiterCall).Method;
var call = (BoundCall)getAwaiterCall;
var getAwaiterMethod = call.Method;
if (getAwaiterMethod is ErrorMethodSymbol ||
HasOptionalOrVariableParameters(getAwaiterMethod) || // We might have been able to resolve a GetAwaiter overload with optional parameters, so check for that here
call.Expanded || HasOptionalParameters(getAwaiterMethod) || // We might have been able to resolve a GetAwaiter overload with optional parameters, so check for that here
getAwaiterMethod.ReturnsVoid) // If GetAwaiter returns void, don't bother checking that it returns an Awaiter.
{
Error(diagnostics, ErrorCode.ERR_BadAwaitArg, node, expression.Type);
Expand Down Expand Up @@ -451,7 +452,8 @@ private bool GetGetResultMethod(BoundExpression awaiterExpression, SyntaxNode no
return false;
}

getResultMethod = ((BoundCall)getAwaiterGetResultCall).Method;
var call = (BoundCall)getAwaiterGetResultCall;
getResultMethod = call.Method;
if (getResultMethod.IsExtensionMethod)
{
Error(diagnostics, ErrorCode.ERR_NoSuchMember, node, awaiterType, WellKnownMemberNames.GetResult);
Expand All @@ -460,7 +462,7 @@ private bool GetGetResultMethod(BoundExpression awaiterExpression, SyntaxNode no
return false;
}

if (HasOptionalOrVariableParameters(getResultMethod) || getResultMethod.IsConditional)
if (call.Expanded || HasOptionalParameters(getResultMethod) || getResultMethod.IsConditional)
{
Error(diagnostics, ErrorCode.ERR_BadAwaiterPattern, node, awaiterType, awaitedExpressionType);
getResultMethod = null;
Expand All @@ -472,14 +474,14 @@ private bool GetGetResultMethod(BoundExpression awaiterExpression, SyntaxNode no
return true;
}

private static bool HasOptionalOrVariableParameters(MethodSymbol method)
private static bool HasOptionalParameters(MethodSymbol method)
{
RoslynDebug.Assert(method != null);

if (method.ParameterCount != 0)
{
var parameter = method.Parameters[method.ParameterCount - 1];
return parameter.IsOptional || parameter.IsParams;
return parameter.IsOptional;
}

return false;
Expand Down
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/Binder/Binder_Crefs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -942,7 +942,7 @@ private ImmutableArray<ParameterSymbol> BindCrefParameters(BaseCrefParameterList
Debug.Assert(parameterListSyntax.Parent is object);
TypeSymbol type = BindCrefParameterOrReturnType(parameter.Type, (MemberCrefSyntax)parameterListSyntax.Parent, diagnostics);

parameterBuilder.Add(new SignatureOnlyParameterSymbol(TypeWithAnnotations.Create(type), ImmutableArray<CustomModifier>.Empty, isParams: false, refKind: refKind));
parameterBuilder.Add(new SignatureOnlyParameterSymbol(TypeWithAnnotations.Create(type), ImmutableArray<CustomModifier>.Empty, isParamArray: false, isParamCollection: false, refKind: refKind));
}

return parameterBuilder.ToImmutableAndFree();
Expand Down
7 changes: 2 additions & 5 deletions src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ private UnboundLambda AnalyzeAnonymousFunction(

bool isAsync = false;
bool isStatic = false;
var hasParamsArray = false;

foreach (var modifier in syntax.Modifiers)
{
Expand Down Expand Up @@ -183,10 +182,8 @@ private UnboundLambda AnalyzeAnonymousFunction(
refKind = ParameterHelpers.GetModifiers(p.Modifiers, out _, out var paramsKeyword, out _, out scope);

var isLastParameter = parameterCount == parameterSyntaxList.Value.Count;
if (isLastParameter && paramsKeyword.Kind() != SyntaxKind.None)
if (isLastParameter && paramsKeyword.Kind() != SyntaxKind.None && type.IsSZArray())
{
hasParamsArray = true;

ReportUseSiteDiagnosticForSynthesizedAttribute(Compilation,
WellKnownMember.System_ParamArrayAttribute__ctor,
diagnostics,
Expand Down Expand Up @@ -243,7 +240,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.AccumulatesDependencies, returnRefKind, returnType, parameterAttributes, refKinds, scopes, types, names, discardsOpt, parameterSyntaxList, defaultValues, isAsync: isAsync, isStatic: isStatic);

static ImmutableArray<bool> computeDiscards(SeparatedSyntaxList<ParameterSyntax> parameters, int underscoresCount)
{
Expand Down
13 changes: 8 additions & 5 deletions src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs
Original file line number Diff line number Diff line change
Expand Up @@ -748,7 +748,7 @@ private BoundStatement BindDeclarationStatementParts(LocalDeclarationStatementSy
/// <param name="syntaxNode">The syntax node to perform lookup on</param>
/// <param name="diagnostics">Populated with invocation errors, and warnings of near misses</param>
/// <returns>The <see cref="MethodSymbol"/> of the Dispose method if one is found, otherwise null.</returns>
internal MethodSymbol TryFindDisposePatternMethod(BoundExpression expr, SyntaxNode syntaxNode, bool hasAwait, BindingDiagnosticBag diagnostics)
internal MethodSymbol TryFindDisposePatternMethod(BoundExpression expr, SyntaxNode syntaxNode, bool hasAwait, BindingDiagnosticBag diagnostics, out bool isExpanded)
{
Debug.Assert(expr is object);
Debug.Assert(expr.Type is object);
Expand All @@ -758,7 +758,8 @@ internal MethodSymbol TryFindDisposePatternMethod(BoundExpression expr, SyntaxNo
hasAwait ? WellKnownMemberNames.DisposeAsyncMethodName : WellKnownMemberNames.DisposeMethodName,
syntaxNode,
diagnostics,
out var disposeMethod);
out var disposeMethod,
out isExpanded);

if (disposeMethod?.IsExtensionMethod == true)
{
Expand Down Expand Up @@ -1335,14 +1336,14 @@ private MethodSymbol GetFixedPatternMethodOpt(BoundExpression initializer, Bindi

const string methodName = "GetPinnableReference";

var result = PerformPatternMethodLookup(initializer, methodName, initializer.Syntax, additionalDiagnostics, out var patternMethodSymbol);
var result = PerformPatternMethodLookup(initializer, methodName, initializer.Syntax, additionalDiagnostics, out var patternMethodSymbol, out bool isExpanded);

if (patternMethodSymbol is null)
{
return null;
}

if (HasOptionalOrVariableParameters(patternMethodSymbol) ||
if (isExpanded || HasOptionalParameters(patternMethodSymbol) ||
patternMethodSymbol.ReturnsVoid ||
!patternMethodSymbol.RefKind.IsManagedReference() ||
!(patternMethodSymbol.ParameterCount == 0 || patternMethodSymbol.IsStatic && patternMethodSymbol.ParameterCount == 1))
Expand Down Expand Up @@ -4009,13 +4010,14 @@ internal virtual ImmutableArray<AliasAndUsingDirective> UsingAliases
/// <param name="result">The method symbol that was looked up, or null</param>
/// <returns>A <see cref="PatternLookupResult"/> value with the outcome of the lookup</returns>
internal PatternLookupResult PerformPatternMethodLookup(BoundExpression receiver, string methodName,
SyntaxNode syntaxNode, BindingDiagnosticBag diagnostics, out MethodSymbol result)
SyntaxNode syntaxNode, BindingDiagnosticBag diagnostics, out MethodSymbol result, out bool isExpanded)
{
var bindingDiagnostics = BindingDiagnosticBag.GetInstance(diagnostics);

try
{
result = null;
isExpanded = false;

var boundAccess = BindInstanceMemberAccess(
syntaxNode,
Expand Down Expand Up @@ -4079,6 +4081,7 @@ internal PatternLookupResult PerformPatternMethodLookup(BoundExpression receiver

// Success!
result = patternMethodSymbol;
isExpanded = call.Expanded;
return PatternLookupResult.Success;
}
finally
Expand Down
5 changes: 1 addition & 4 deletions src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1199,7 +1199,7 @@ private void GetDisposalInfoForEnumerator(SyntaxNode syntax, ref ForEachEnumerat
{
// we throw away any binding diagnostics, and assume it's not disposable if we encounter errors
var receiver = new BoundDisposableValuePlaceholder(syntax, enumeratorType);
patternDisposeMethod = TryFindDisposePatternMethod(receiver, syntax, isAsync, BindingDiagnosticBag.Discarded);
patternDisposeMethod = TryFindDisposePatternMethod(receiver, syntax, isAsync, BindingDiagnosticBag.Discarded, out bool expanded);
if (patternDisposeMethod is object)
{
Debug.Assert(!patternDisposeMethod.IsExtensionMethod);
Expand All @@ -1208,9 +1208,6 @@ private void GetDisposalInfoForEnumerator(SyntaxNode syntax, ref ForEachEnumerat
var argsBuilder = ArrayBuilder<BoundExpression>.GetInstance(patternDisposeMethod.ParameterCount);
var argsToParams = default(ImmutableArray<int>);

// PROTOTYPE(ParamsCollections): Test this code path
bool expanded = patternDisposeMethod.HasParamsParameter();

BindDefaultArgumentsAndParamsCollection(
syntax,
patternDisposeMethod.Parameters,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public override Conversion GetMethodGroupDelegateConversion(BoundMethodGroup sou
if (methodSymbol.OriginalDefinition is SynthesizedDelegateInvokeMethod invoke)
{
// If synthesizing a delegate with `params` array, check that `ParamArrayAttribute` is available.
if (invoke.IsParams()) // PROTOTYPE(ParamsCollections): Test this code path
if (invoke.Parameters is [.., { IsParamArray: true }])
{
Binder.AddUseSiteDiagnosticForSynthesizedAttribute(
Compilation,
Expand Down Expand Up @@ -447,7 +447,8 @@ public static void GetDelegateOrFunctionPointerArguments(SyntaxNode syntax, Anal
// If we don't have System.Object, then we'll get an error type, which will cause overload resolution to fail,
// which will cause some error to be reported. That's sufficient (i.e. no need to specifically report its absence here).
parameter = new SignatureOnlyParameterSymbol(
TypeWithAnnotations.Create(compilation.GetSpecialType(SpecialType.System_Object), customModifiers: parameter.TypeWithAnnotations.CustomModifiers), parameter.RefCustomModifiers, parameter.IsParams, parameter.RefKind);
TypeWithAnnotations.Create(compilation.GetSpecialType(SpecialType.System_Object), customModifiers: parameter.TypeWithAnnotations.CustomModifiers), parameter.RefCustomModifiers,
isParamArray: parameter.IsParamArray, isParamCollection: parameter.IsParamCollection, parameter.RefKind);
}

analyzedArguments.Arguments.Add(new BoundParameter(syntax, parameter) { WasCompilerGenerated = true });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1167,7 +1167,7 @@ public static bool IsValidParams(Binder binder, Symbol member)
}

ParameterSymbol final = member.GetParameters().Last();
if (final.IsParams)
if ((final.IsParamArray && final.Type.IsSZArray()) || (final.IsParamCollection && !final.Type.IsSZArray()))
{
return TryInferParamsCollectionIterationType(binder, final.OriginalDefinition.Type, out _);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1300,7 +1300,8 @@ private static void ReportBadArgumentError(
SignatureOnlyParameterSymbol displayArg = new SignatureOnlyParameterSymbol(
TypeWithAnnotations.Create(argType),
ImmutableArray<CustomModifier>.Empty,
isParams: false,
isParamArray: false,
isParamCollection: false,
refKind: refArg);

SymbolDistinguisher distinguisher = new SymbolDistinguisher(binder.Compilation, displayArg, unwrapIfParamsCollection(badArg, parameter, isLastParameter));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,17 +217,14 @@ bool bindDisposable(bool fromExpression, out MethodArgumentInfo? patternDisposeI
BindingDiagnosticBag patternDiagnostics = originalBinder.Compilation.IsFeatureEnabled(MessageID.IDS_FeatureDisposalPattern)
? diagnostics
: BindingDiagnosticBag.Discarded;
MethodSymbol disposeMethod = originalBinder.TryFindDisposePatternMethod(receiver, syntax, hasAwait, patternDiagnostics);
MethodSymbol disposeMethod = originalBinder.TryFindDisposePatternMethod(receiver, syntax, hasAwait, patternDiagnostics, out bool expanded);
if (disposeMethod is object)
{
MessageID.IDS_FeatureDisposalPattern.CheckFeatureAvailability(diagnostics, originalBinder.Compilation, syntax.Location);

var argumentsBuilder = ArrayBuilder<BoundExpression>.GetInstance(disposeMethod.ParameterCount);
ImmutableArray<int> argsToParams = default;

// PROTOTYPE(ParamsCollections): Test this code path
bool expanded = disposeMethod.HasParamsParameter();

originalBinder.BindDefaultArgumentsAndParamsCollection(
// If this is a using statement, then we want to use the whole `using (expr) { }` as the argument location. These arguments
// will be represented in the IOperation tree and the "correct" node for them, given that they are an implicit invocation
Expand Down
Loading