Skip to content

Commit

Permalink
Statically bind invocations in presence of dynamic arguments only for…
Browse files Browse the repository at this point in the history
… local functions
  • Loading branch information
AlekseyTs committed May 2, 2024
1 parent aaed123 commit 8c74aca
Show file tree
Hide file tree
Showing 23 changed files with 963 additions and 1,282 deletions.
74 changes: 5 additions & 69 deletions src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1118,23 +1118,20 @@ static bool bindMethodGroupInvocation(
addMethods = [];
result = false;
}
else if (finalApplicableCandidates.Length == 1 &&
tryEarlyBindSingleCandidateInvocationWithDynamicArgument(addMethodBinder, syntax, expression, methodGroup, diagnostics, resolution, finalApplicableCandidates[0], out var addMethod) is bool earlyBoundResult)
{
addMethods = addMethod is null ? [] : [addMethod];
result = earlyBoundResult;
}
else
{
Debug.Assert(finalApplicableCandidates.Length > 0);

addMethods = filterOutBadGenericMethods(addMethodBinder, syntax, methodGroup, analyzedArguments, resolution, finalApplicableCandidates, ref useSiteInfo);
result = !addMethods.IsEmpty;

if (!result)
{
diagnostics.Add(ErrorCode.ERR_CollectionExpressionMissingAdd, syntax, methodGroup.ReceiverOpt.Type);
}
else if (addMethods.Length == 1)
{
addMethodBinder.ReportDiagnosticsIfObsolete(diagnostics, addMethods[0], syntax, hasBaseReceiver: false);
ReportDiagnosticsIfUnmanagedCallersOnly(diagnostics, addMethods[0], syntax, isDelegateConversion: false);
}
}
}
else
Expand Down Expand Up @@ -1259,67 +1256,6 @@ static ImmutableArray<MethodSymbol> filterOutBadGenericMethods(
return resultBuilder.ToImmutableAndFree();
}

// This is what CanEarlyBindSingleCandidateInvocationWithDynamicArgument is doing in terms of reporting diagnostics and detecting a failure
static bool canEarlyBindSingleCandidateInvocationWithDynamicArgument(
Binder addMethodBinder,
SyntaxNode syntax,
BoundMethodGroup boundMethodGroup,
BindingDiagnosticBag diagnostics,
MethodGroupResolution resolution,
MemberResolutionResult<MethodSymbol> methodResolutionResult,
MethodSymbol singleCandidate)
{
Debug.Assert(boundMethodGroup.TypeArgumentsOpt.IsDefaultOrEmpty);

if (singleCandidate.IsGenericMethod)
{
return false;
}

if (addMethodBinder.IsAmbiguousDynamicParamsArgument(resolution.AnalyzedArguments.Arguments, methodResolutionResult, out SyntaxNode argumentSyntax))
{
return false;
}

return true;
}

// This is what TryEarlyBindSingleCandidateInvocationWithDynamicArgument is doing in terms of reporting diagnostics and detecting a failure
static bool? tryEarlyBindSingleCandidateInvocationWithDynamicArgument(
Binder addMethodBinder,
SyntaxNode syntax,
SyntaxNode expression,
BoundMethodGroup boundMethodGroup,
BindingDiagnosticBag diagnostics,
MethodGroupResolution resolution,
MemberResolutionResult<MethodSymbol> methodResolutionResult,
out MethodSymbol? addMethod)
{
MethodSymbol singleCandidate = methodResolutionResult.LeastOverriddenMember;
if (!canEarlyBindSingleCandidateInvocationWithDynamicArgument(addMethodBinder, syntax, boundMethodGroup, diagnostics, resolution, methodResolutionResult, singleCandidate))
{
addMethod = null;
return null;
}

var resultWithSingleCandidate = OverloadResolutionResult<MethodSymbol>.GetInstance();
resultWithSingleCandidate.ResultsBuilder.Add(methodResolutionResult);

bool result = bindInvocationExpressionContinued(
addMethodBinder,
node: syntax,
expression: expression,
result: resultWithSingleCandidate,
analyzedArguments: resolution.AnalyzedArguments,
methodGroup: resolution.MethodGroup,
diagnostics: diagnostics,
out addMethod);

resultWithSingleCandidate.Free();

return result;
}

// This is what BindInvocationExpressionContinued is doing in terms of reporting diagnostics and detecting a failure
static bool bindInvocationExpressionContinued(
Binder addMethodBinder,
Expand Down
108 changes: 26 additions & 82 deletions src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6560,70 +6560,38 @@ protected BoundExpression BindClassCreationExpression(
if (finalApplicableCandidates.Length == 1)
{
Debug.Assert(finalApplicableCandidates[0].IsApplicable);

if (IsAmbiguousDynamicParamsArgument(analyzedArguments.Arguments, finalApplicableCandidates[0], out SyntaxNode argumentSyntax))
{
MethodSymbol singleCandidate = finalApplicableCandidates[0].Member;

// We're only in trouble if a dynamic argument is passed to the
// params parameter and is ambiguous at compile time between normal
// and expanded form i.e., there is exactly one dynamic argument to
// a params parameter, and we know that runtime binder might not be
// able to handle the disambiguation
if (!singleCandidate.Parameters.Last().Type.IsSZArray())
{
Error(diagnostics,
ErrorCode.ERR_ParamsCollectionAmbiguousDynamicArgument,
argumentSyntax, singleCandidate);
}
}
else
{
if (!type.IsAbstract)
{
result = BindClassCreationExpressionContinued(node, typeNode, type, analyzedArguments, initializerSyntaxOpt,
initializerTypeOpt, wasTargetTyped, finalApplicableCandidates[0], accessibleConstructors, in useSiteInfo, diagnostics);
}
else
{
result = CreateBadClassCreationExpression(node, typeNode, type, analyzedArguments,
initializerSyntaxOpt, initializerTypeOpt, finalApplicableCandidates[0], accessibleConstructors, in useSiteInfo, diagnostics);
}
}
ReportMemberNotSupportedByDynamicDispatch(node, finalApplicableCandidates[0], analyzedArguments.Arguments, diagnostics);
}

if (result == null)
if (finalApplicableCandidates.Length != 1 &&
Compilation.LanguageVersion > LanguageVersion.CSharp12 && // The following check (while correct) is redundant otherwise
HasApplicableMemberWithPossiblyExpandedNonArrayParamsCollection(analyzedArguments.Arguments, finalApplicableCandidates))
{
if (finalApplicableCandidates.Length != 1 &&
Compilation.LanguageVersion > LanguageVersion.CSharp12 && // The following check (while correct) is redundant otherwise
HasApplicableMemberWithPossiblyExpandedNonArrayParamsCollection(analyzedArguments.Arguments, finalApplicableCandidates))
{
Error(diagnostics,
ErrorCode.WRN_DynamicDispatchToParamsCollectionConstructor,
node);
}
Error(diagnostics,
ErrorCode.WRN_DynamicDispatchToParamsCollectionConstructor,
node);
}

var argArray = BuildArgumentsForDynamicInvocation(analyzedArguments, diagnostics);
var refKindsArray = analyzedArguments.RefKinds.ToImmutableOrNull();
var argArray = BuildArgumentsForDynamicInvocation(analyzedArguments, diagnostics);
var refKindsArray = analyzedArguments.RefKinds.ToImmutableOrNull();

hasErrors &= ReportBadDynamicArguments(node, receiver: null, argArray, refKindsArray, diagnostics, queryClause: null);
hasErrors &= ReportBadDynamicArguments(node, receiver: null, argArray, refKindsArray, diagnostics, queryClause: null);

BoundObjectInitializerExpressionBase boundInitializerOpt;
boundInitializerOpt = MakeBoundInitializerOpt(typeNode, type, initializerSyntaxOpt, initializerTypeOpt, diagnostics);
result = new BoundDynamicObjectCreationExpression(
node,
typeName,
argArray,
analyzedArguments.GetNames(),
refKindsArray,
boundInitializerOpt,
overloadResolutionResult.GetAllApplicableMembers(),
wasTargetTyped,
type,
hasErrors);
BoundObjectInitializerExpressionBase boundInitializerOpt;
boundInitializerOpt = MakeBoundInitializerOpt(typeNode, type, initializerSyntaxOpt, initializerTypeOpt, diagnostics);
result = new BoundDynamicObjectCreationExpression(
node,
typeName,
argArray,
analyzedArguments.GetNames(),
refKindsArray,
boundInitializerOpt,
overloadResolutionResult.GetAllApplicableMembers(),
wasTargetTyped,
type,
hasErrors);

diagnostics.Add(node, useSiteInfo);
}
diagnostics.Add(node, useSiteInfo);
}

overloadResolutionResult.Free();
Expand Down Expand Up @@ -9703,31 +9671,7 @@ private BoundExpression BindIndexerOrIndexedPropertyAccess(
if (finalApplicableCandidates.Length == 1)
{
Debug.Assert(finalApplicableCandidates[0].IsApplicable);

if (IsAmbiguousDynamicParamsArgument(analyzedArguments.Arguments, finalApplicableCandidates[0], out SyntaxNode argumentSyntax))
{
PropertySymbol singleCandidate = finalApplicableCandidates[0].LeastOverriddenMember;

// We're only in trouble if a dynamic argument is passed to the
// params parameter and is ambiguous at compile time between normal
// and expanded form i.e., there is exactly one dynamic argument to
// a params parameter, and we know that runtime binder might not be
// able to handle the disambiguation
if (!singleCandidate.Parameters.Last().Type.IsSZArray())
{
Error(diagnostics,
ErrorCode.ERR_ParamsCollectionAmbiguousDynamicArgument,
argumentSyntax, singleCandidate);
}
}
else
{
var resultWithSingleCandidate = OverloadResolutionResult<PropertySymbol>.GetInstance();
resultWithSingleCandidate.ResultsBuilder.Add(finalApplicableCandidates[0]);
overloadResolutionResult.Free();

return BindIndexerOrIndexedPropertyAccessContinued(syntax, receiver, propertyGroup, analyzedArguments, resultWithSingleCandidate, diagnostics);
}
ReportMemberNotSupportedByDynamicDispatch(syntax, finalApplicableCandidates[0], analyzedArguments.Arguments, diagnostics);
}

if (finalApplicableCandidates.Length != 1 &&
Expand Down
Loading

0 comments on commit 8c74aca

Please sign in to comment.