Skip to content

Commit

Permalink
Adjust lowering of a string interpolation in an expression lambda to …
Browse files Browse the repository at this point in the history
…not use expanded non-array `params` collection in Format/Create calls.

Fixes dotnet#74163.
  • Loading branch information
AlekseyTs committed Jul 5, 2024
1 parent 16eecff commit a81eab0
Show file tree
Hide file tree
Showing 7 changed files with 602 additions and 44 deletions.
4 changes: 3 additions & 1 deletion src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8248,6 +8248,7 @@ protected MethodGroupResolution BindExtensionMethod(
OverloadResolution.Options.IsFunctionPointerResolution |
OverloadResolution.Options.InferWithDynamic |
OverloadResolution.Options.IgnoreNormalFormIfHasValidParamsParameter |
OverloadResolution.Options.DisallowExpandedNonArrayParams |
OverloadResolution.Options.DynamicResolution |
OverloadResolution.Options.DynamicConvertsToAnything)) == 0);

Expand Down Expand Up @@ -10282,6 +10283,7 @@ private MethodGroupResolution ResolveDefaultMethodGroup(
OverloadResolution.Options.IsFunctionPointerResolution |
OverloadResolution.Options.InferWithDynamic |
OverloadResolution.Options.IgnoreNormalFormIfHasValidParamsParameter |
OverloadResolution.Options.DisallowExpandedNonArrayParams |
OverloadResolution.Options.DynamicResolution |
OverloadResolution.Options.DynamicConvertsToAnything)) == 0);

Expand Down Expand Up @@ -10607,7 +10609,7 @@ bool satisfiesConstraintChecks(MethodSymbol method)
parameters.SelectAsArray(p => p.ExplicitDefaultConstantValue) :
default;

var hasParams = OverloadResolution.IsValidParams(this, methodSymbol, out _);
var hasParams = OverloadResolution.IsValidParams(this, methodSymbol, disallowExpandedNonArrayParams: false, out _);

Debug.Assert(ContainingMemberOrLambda is { });
Debug.Assert(parameterRefKinds.IsDefault || parameterRefKinds.Length == parameterTypes.Length);
Expand Down
20 changes: 14 additions & 6 deletions src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ internal BoundExpression MakeInvocationExpression(
CSharpSyntaxNode? queryClause = null,
bool allowFieldsAndProperties = false,
bool ignoreNormalFormIfHasValidParamsParameter = false,
bool searchExtensionMethodsIfNecessary = true)
bool searchExtensionMethodsIfNecessary = true,
bool disallowExpandedNonArrayParams = false)
{
//
// !!! ATTENTION !!!
Expand Down Expand Up @@ -137,7 +138,8 @@ internal BoundExpression MakeInvocationExpression(

BoundExpression result = BindInvocationExpression(
node, node, methodName, boundExpression, analyzedArguments, diagnostics, queryClause,
ignoreNormalFormIfHasValidParamsParameter: ignoreNormalFormIfHasValidParamsParameter);
ignoreNormalFormIfHasValidParamsParameter: ignoreNormalFormIfHasValidParamsParameter,
disallowExpandedNonArrayParams: disallowExpandedNonArrayParams);

// Query operator can't be called dynamically.
if (queryClause != null && result.Kind == BoundKind.DynamicInvocation)
Expand Down Expand Up @@ -323,7 +325,8 @@ private BoundExpression BindInvocationExpression(
AnalyzedArguments analyzedArguments,
BindingDiagnosticBag diagnostics,
CSharpSyntaxNode queryClause = null,
bool ignoreNormalFormIfHasValidParamsParameter = false)
bool ignoreNormalFormIfHasValidParamsParameter = false,
bool disallowExpandedNonArrayParams = false)
{
//
// !!! ATTENTION !!!
Expand All @@ -349,7 +352,10 @@ private BoundExpression BindInvocationExpression(
ReportSuppressionIfNeeded(boundExpression, diagnostics);
result = BindMethodGroupInvocation(
node, expression, methodName, (BoundMethodGroup)boundExpression, analyzedArguments,
diagnostics, queryClause, ignoreNormalFormIfHasValidParamsParameter: ignoreNormalFormIfHasValidParamsParameter, anyApplicableCandidates: out _);
diagnostics, queryClause,
ignoreNormalFormIfHasValidParamsParameter: ignoreNormalFormIfHasValidParamsParameter,
disallowExpandedNonArrayParams: disallowExpandedNonArrayParams,
anyApplicableCandidates: out _);
}
else if ((object)(delegateType = GetDelegateType(boundExpression)) != null)
{
Expand Down Expand Up @@ -689,7 +695,8 @@ private BoundExpression BindMethodGroupInvocation(
BindingDiagnosticBag diagnostics,
CSharpSyntaxNode queryClause,
bool ignoreNormalFormIfHasValidParamsParameter,
out bool anyApplicableCandidates)
out bool anyApplicableCandidates,
bool disallowExpandedNonArrayParams = false)
{
//
// !!! ATTENTION !!!
Expand All @@ -705,6 +712,7 @@ private BoundExpression BindMethodGroupInvocation(
methodGroup, expression, methodName, analyzedArguments,
useSiteInfo: ref useSiteInfo,
options: (ignoreNormalFormIfHasValidParamsParameter ? OverloadResolution.Options.IgnoreNormalFormIfHasValidParamsParameter : OverloadResolution.Options.None) |
(disallowExpandedNonArrayParams ? OverloadResolution.Options.DisallowExpandedNonArrayParams : OverloadResolution.Options.None) |
(analyzedArguments.HasDynamicArgument ? OverloadResolution.Options.DynamicResolution : OverloadResolution.Options.None));
diagnostics.Add(expression, useSiteInfo);
anyApplicableCandidates = resolution.ResultKind == LookupResultKind.Viable && resolution.OverloadResolutionResult.HasAnyApplicableMember;
Expand Down Expand Up @@ -848,7 +856,7 @@ private void ReportDynamicInvocationWarnings(SyntaxNode syntax, BoundMethodGroup
private bool IsAmbiguousDynamicParamsArgument<TMethodOrPropertySymbol>(ArrayBuilder<BoundExpression> arguments, MemberResolutionResult<TMethodOrPropertySymbol> candidate, out SyntaxNode argumentSyntax)
where TMethodOrPropertySymbol : Symbol
{
if (OverloadResolution.IsValidParams(this, candidate.LeastOverriddenMember, out _) &&
if (OverloadResolution.IsValidParams(this, candidate.LeastOverriddenMember, disallowExpandedNonArrayParams: false, out _) &&
candidate.Result.Kind == MemberResolutionKind.ApplicableInNormalForm)
{
var parameters = candidate.Member.GetParameters();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,17 +116,18 @@ public void ObjectCreationOverloadResolution(ImmutableArray<MethodSymbol> constr
}

[Flags]
public enum Options : byte
public enum Options : ushort
{
None = 0,
IsMethodGroupConversion = 0b_00000001,
AllowRefOmittedArguments = 0b_00000010,
InferWithDynamic = 0b_00000100,
IgnoreNormalFormIfHasValidParamsParameter = 0b_00001000,
IsFunctionPointerResolution = 0b_00010000,
IsExtensionMethodResolution = 0b_00100000,
DynamicResolution = 0b_01000000,
DynamicConvertsToAnything = 0b_10000000,
IsMethodGroupConversion = 1 << 0,
AllowRefOmittedArguments = 1 << 1,
InferWithDynamic = 1 << 2,
IgnoreNormalFormIfHasValidParamsParameter = 1 << 3,
IsFunctionPointerResolution = 1 << 4,
IsExtensionMethodResolution = 1 << 5,
DynamicResolution = 1 << 6,
DynamicConvertsToAnything = 1 << 7,
DisallowExpandedNonArrayParams = 1 << 8,
}

// Perform overload resolution on the given method group, with the given arguments and
Expand Down Expand Up @@ -817,7 +818,7 @@ private void AddConstructorToCandidateSet(MethodSymbol constructor, ArrayBuilder
var result = normalResult;
if (!normalResult.IsValid)
{
if (IsValidParams(_binder, constructor, out TypeWithAnnotations definitionElementType))
if (IsValidParams(_binder, constructor, disallowExpandedNonArrayParams: false, out TypeWithAnnotations definitionElementType))
{
var expandedResult = IsConstructorApplicableInExpandedForm(constructor, arguments, definitionElementType, completeResults, ref useSiteInfo);
if (expandedResult.IsValid || completeResults)
Expand Down Expand Up @@ -1040,8 +1041,8 @@ private void AddMemberToCandidateSet<TMember>(
Debug.Assert(typeArguments.Count == 0 || typeArguments.Count == member.GetMemberArity());

// Second, we need to determine if the method is applicable in its normal form or its expanded form.

var normalResult = ((options & Options.IgnoreNormalFormIfHasValidParamsParameter) != 0 && IsValidParams(_binder, leastOverriddenMember, out _))
bool disallowExpandedNonArrayParams = (options & Options.DisallowExpandedNonArrayParams) != 0;
var normalResult = ((options & Options.IgnoreNormalFormIfHasValidParamsParameter) != 0 && IsValidParams(_binder, leastOverriddenMember, disallowExpandedNonArrayParams, out _))
? default(MemberResolutionResult<TMember>)
: IsMemberApplicableInNormalForm(
member,
Expand All @@ -1060,7 +1061,7 @@ private void AddMemberToCandidateSet<TMember>(
// tricks you can pull to make overriding methods [indexers] inconsistent with overridden
// methods [indexers] (or implementing methods [indexers] inconsistent with interfaces).

if ((options & Options.IsMethodGroupConversion) == 0 && IsValidParams(_binder, leastOverriddenMember, out TypeWithAnnotations definitionElementType))
if ((options & Options.IsMethodGroupConversion) == 0 && IsValidParams(_binder, leastOverriddenMember, disallowExpandedNonArrayParams, out TypeWithAnnotations definitionElementType))
{
var expandedResult = IsMemberApplicableInExpandedForm(
member,
Expand Down Expand Up @@ -1160,7 +1161,7 @@ static bool haveBadArgumentForLastParameter(MemberResolutionResult<TMember> resu
// We need to know if this is a valid formal parameter list with a parameter array
// as the final formal parameter. We might be in an error recovery scenario
// where the params array is not an array type.
public static bool IsValidParams(Binder binder, Symbol member, out TypeWithAnnotations definitionElementType)
public static bool IsValidParams(Binder binder, Symbol member, bool disallowExpandedNonArrayParams, out TypeWithAnnotations definitionElementType)
{
// A varargs method is never a valid params method.
if (member.GetIsVararg())
Expand All @@ -1178,7 +1179,7 @@ public static bool IsValidParams(Binder binder, Symbol member, out TypeWithAnnot

ParameterSymbol final = member.GetParameters().Last();
if ((final.IsParamsArray && final.Type.IsSZArray()) ||
(final.IsParamsCollection && !final.Type.IsSZArray() &&
(final.IsParamsCollection && !final.Type.IsSZArray() && !disallowExpandedNonArrayParams &&
(binder.Compilation.LanguageVersion > LanguageVersion.CSharp12 || member.ContainingModule == binder.Compilation.SourceModule)))
{
return TryInferParamsCollectionIterationType(binder, final.OriginalDefinition.Type, out definitionElementType);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1094,12 +1094,12 @@ private BoundExpression VisitUnaryOperator(BoundUnaryOperator node)

private BoundExpression ExprFactory(string name, params BoundExpression[] arguments)
{
return _bound.StaticCall(ExpressionType, name, arguments);
return _bound.StaticCall(ExpressionType, name, disallowExpandedNonArrayParams: true, arguments);
}

private BoundExpression ExprFactory(string name, ImmutableArray<TypeSymbol> typeArgs, params BoundExpression[] arguments)
{
return _bound.StaticCall(_ignoreAccessibility ? BinderFlags.IgnoreAccessibility : BinderFlags.None, ExpressionType, name, typeArgs, arguments);
return _bound.StaticCall(_ignoreAccessibility ? BinderFlags.IgnoreAccessibility : BinderFlags.None, ExpressionType, name, disallowExpandedNonArrayParams: true, typeArgs, arguments);
}

private BoundExpression Constant(BoundExpression node)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ private BoundExpression RewriteInterpolatedStringConversion(BoundConversion conv
// the arguments first in this situation because we do not know what conversions will be
// produced for the arguments until after we've done overload resolution. So we produce the invocation
// and then lower it along with its arguments.
var result = _factory.StaticCall(stringFactory, "Create", expressions.ToImmutableAndFree(),
var result = _factory.StaticCall(stringFactory, "Create", disallowExpandedNonArrayParams: _inExpressionLambda, expressions.ToImmutableAndFree(),
ignoreNormalFormIfHasValidParamsParameter: true // if an interpolation expression is the null literal, it should not match a params parameter.
);
if (!result.HasAnyErrors)
Expand Down Expand Up @@ -351,7 +351,7 @@ public override BoundNode VisitInterpolatedString(BoundInterpolatedString node)
// and then lower it along with its arguments.
expressions.Insert(0, format);
var stringType = node.Type;
result = _factory.StaticCall(stringType, "Format", expressions.ToImmutableAndFree(),
result = _factory.StaticCall(stringType, "Format", disallowExpandedNonArrayParams: _inExpressionLambda, expressions.ToImmutableAndFree(),
ignoreNormalFormIfHasValidParamsParameter: true // if an interpolation expression is the null literal, it should not match a params parameter.
);
}
Expand Down
29 changes: 11 additions & 18 deletions src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,12 @@ private set
/// </summary>
private Binder? _binder;

internal BoundExpression MakeInvocationExpression(
private BoundExpression MakeInvocationExpression(
BinderFlags flags,
SyntaxNode node,
BoundExpression receiver,
string methodName,
bool disallowExpandedNonArrayParams,
ImmutableArray<BoundExpression> args,
BindingDiagnosticBag diagnostics,
ImmutableArray<TypeSymbol> typeArgs = default(ImmutableArray<TypeSymbol>),
Expand All @@ -119,7 +120,8 @@ internal BoundExpression MakeInvocationExpression(
diagnostics,
typeArgs: typeArgs.IsDefault ? default(ImmutableArray<TypeWithAnnotations>) : typeArgs.SelectAsArray(t => TypeWithAnnotations.Create(t)),
allowFieldsAndProperties: false,
ignoreNormalFormIfHasValidParamsParameter: ignoreNormalFormIfHasValidParamsParameter);
ignoreNormalFormIfHasValidParamsParameter: ignoreNormalFormIfHasValidParamsParameter,
disallowExpandedNonArrayParams: disallowExpandedNonArrayParams);
}

/// <summary>
Expand Down Expand Up @@ -831,29 +833,20 @@ public BoundExpression MakeIsNotANumberTest(BoundExpression input)
}
}

public BoundExpression InstanceCall(BoundExpression receiver, string name, BoundExpression arg)
public BoundExpression StaticCall(TypeSymbol receiver, string name, bool disallowExpandedNonArrayParams, params BoundExpression[] args)
{
return MakeInvocationExpression(BinderFlags.None, this.Syntax, receiver, name, ImmutableArray.Create(arg), this.Diagnostics);
return MakeInvocationExpression(BinderFlags.None, this.Syntax, this.Type(receiver), name, disallowExpandedNonArrayParams, args.ToImmutableArray(), this.Diagnostics);
}

public BoundExpression InstanceCall(BoundExpression receiver, string name)
public BoundExpression StaticCall(TypeSymbol receiver, string name, bool disallowExpandedNonArrayParams, ImmutableArray<BoundExpression> args, bool ignoreNormalFormIfHasValidParamsParameter)
{
return MakeInvocationExpression(BinderFlags.None, this.Syntax, receiver, name, ImmutableArray<BoundExpression>.Empty, this.Diagnostics);
return MakeInvocationExpression(
BinderFlags.None, this.Syntax, this.Type(receiver), name, disallowExpandedNonArrayParams, args, this.Diagnostics, ignoreNormalFormIfHasValidParamsParameter: ignoreNormalFormIfHasValidParamsParameter);
}

public BoundExpression StaticCall(TypeSymbol receiver, string name, params BoundExpression[] args)
public BoundExpression StaticCall(BinderFlags flags, TypeSymbol receiver, string name, bool disallowExpandedNonArrayParams, ImmutableArray<TypeSymbol> typeArgs, params BoundExpression[] args)
{
return MakeInvocationExpression(BinderFlags.None, this.Syntax, this.Type(receiver), name, args.ToImmutableArray(), this.Diagnostics);
}

public BoundExpression StaticCall(TypeSymbol receiver, string name, ImmutableArray<BoundExpression> args, bool ignoreNormalFormIfHasValidParamsParameter)
{
return MakeInvocationExpression(BinderFlags.None, this.Syntax, this.Type(receiver), name, args, this.Diagnostics, ignoreNormalFormIfHasValidParamsParameter: ignoreNormalFormIfHasValidParamsParameter);
}

public BoundExpression StaticCall(BinderFlags flags, TypeSymbol receiver, string name, ImmutableArray<TypeSymbol> typeArgs, params BoundExpression[] args)
{
return MakeInvocationExpression(flags, this.Syntax, this.Type(receiver), name, args.ToImmutableArray(), this.Diagnostics, typeArgs);
return MakeInvocationExpression(flags, this.Syntax, this.Type(receiver), name, disallowExpandedNonArrayParams, args.ToImmutableArray(), this.Diagnostics, typeArgs);
}

public BoundExpression StaticCall(TypeSymbol receiver, MethodSymbol method, params BoundExpression[] args)
Expand Down
Loading

0 comments on commit a81eab0

Please sign in to comment.