Skip to content

Commit

Permalink
Merge pull request #50105 from 333fred/remove-binder
Browse files Browse the repository at this point in the history
  • Loading branch information
333fred committed Dec 31, 2020
2 parents 1ad38b2 + ae52de5 commit 6c5cef5
Show file tree
Hide file tree
Showing 71 changed files with 2,222 additions and 886 deletions.
11 changes: 9 additions & 2 deletions src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -201,8 +201,16 @@ private BoundIndexerAccess BindIndexerDefaultArguments(BoundIndexerAccess indexe
{
parameters = parameters.RemoveAt(parameters.Length - 1);
}

BitVector defaultArguments = default;
Debug.Assert(parameters.Length == indexerAccess.Indexer.Parameters.Length);
BindDefaultArguments(indexerAccess.Syntax, parameters, argumentsBuilder, refKindsBuilderOpt, ref argsToParams, out var defaultArguments, indexerAccess.Expanded, enableCallerInfo: true, diagnostics);

// If OriginalIndexersOpt is set, there was an overload resolution failure, and we don't want to make guesses about the default
// arguments that will end up being reflected in the SemanticModel/IOperation
if (indexerAccess.OriginalIndexersOpt.IsDefault)
{
BindDefaultArguments(indexerAccess.Syntax, parameters, argumentsBuilder, refKindsBuilderOpt, ref argsToParams, out defaultArguments, indexerAccess.Expanded, enableCallerInfo: true, diagnostics);
}

indexerAccess = indexerAccess.Update(
indexerAccess.ReceiverOpt,
Expand All @@ -213,7 +221,6 @@ private BoundIndexerAccess BindIndexerDefaultArguments(BoundIndexerAccess indexe
indexerAccess.Expanded,
argsToParams,
defaultArguments,
indexerAccess.BinderOpt,
indexerAccess.Type);

refKindsBuilderOpt?.Free();
Expand Down
7 changes: 1 addition & 6 deletions src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4067,7 +4067,6 @@ private BoundExpression BindConstructorInitializerCore(
argsToParamsOpt: argsToParamsOpt,
defaultArguments: defaultArguments,
resultKind: LookupResultKind.Viable,
binderOpt: this,
type: constructorReturnType,
hasErrors: hasErrors)
{ WasCompilerGenerated = initializerArgumentListOpt == null };
Expand Down Expand Up @@ -4741,7 +4740,6 @@ private BoundExpression BindObjectInitializerMember(
defaultArguments,
resultKind,
implicitReceiver.Type,
binder: this,
type: boundMember.Type,
hasErrors: hasErrors);
}
Expand Down Expand Up @@ -5091,7 +5089,6 @@ private BoundExpression BindCollectionInitializerElementAddMethod(
boundCall.DefaultArguments,
boundCall.InvokedAsExtensionMethod,
boundCall.ResultKind,
binderOpt: boundCall.BinderOpt,
boundCall.Type,
boundCall.HasAnyErrors)
{ WasCompilerGenerated = true };
Expand Down Expand Up @@ -5296,7 +5293,6 @@ protected BoundExpression BindClassCreationExpression(
constantValueOpt,
boundInitializerOpt,
wasTargetTyped,
this,
type,
hasError);

Expand Down Expand Up @@ -5455,7 +5451,7 @@ private BoundExpression BindComImportCoClassCreationExpression(SyntaxNode node,
var creation = (BoundObjectCreationExpression)classCreation;
return creation.Update(creation.Constructor, creation.ConstructorsGroup, creation.Arguments, creation.ArgumentNamesOpt,
creation.ArgumentRefKindsOpt, creation.Expanded, creation.ArgsToParamsOpt, creation.DefaultArguments, creation.ConstantValueOpt,
creation.InitializerExpressionOpt, creation.BinderOpt, interfaceType);
creation.InitializerExpressionOpt, interfaceType);

case BoundKind.BadExpression:
var bad = (BoundBadExpression)classCreation;
Expand Down Expand Up @@ -7810,7 +7806,6 @@ private BoundExpression BindIndexerOrIndexedPropertyAccess(
isExpanded,
argsToParams,
defaultArguments: default,
this,
property.Type,
gotError);
}
Expand Down
195 changes: 101 additions & 94 deletions src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1142,7 +1142,7 @@ private BoundCall BindInvocationExpressionContinued(

return new BoundCall(node, receiver, method, args, argNames, argRefKinds, isDelegateCall: isDelegateCall,
expanded: expanded, invokedAsExtensionMethod: invokedAsExtensionMethod,
argsToParamsOpt: argsToParams, defaultArguments, resultKind: LookupResultKind.Viable, binderOpt: this, type: returnType, hasErrors: gotError);
argsToParamsOpt: argsToParams, defaultArguments, resultKind: LookupResultKind.Viable, type: returnType, hasErrors: gotError);
}

#nullable enable
Expand All @@ -1162,90 +1162,6 @@ private static SourceLocation GetCallerLocation(SyntaxNode syntax)
return new SourceLocation(token);
}

internal BoundExpression BindDefaultArgument(SyntaxNode syntax, ParameterSymbol parameter, Symbol containingMember, bool enableCallerInfo, DiagnosticBag diagnostics)
{
Debug.Assert(parameter.IsOptional);

TypeSymbol parameterType = parameter.Type;
if (Flags.Includes(BinderFlags.ParameterDefaultValue))
{
// This is only expected to occur in recursive error scenarios, for example: `object F(object param = F()) { }`
// We return a non-error expression here to ensure ERR_DefaultValueMustBeConstant (or another appropriate diagnostics) is produced by the caller.
return new BoundDefaultExpression(syntax, parameterType) { WasCompilerGenerated = true };
}

var defaultConstantValue = parameter.ExplicitDefaultConstantValue switch
{
// Bad default values are implicitly replaced with default(T) at call sites.
{ IsBad: true } => ConstantValue.Null,
var constantValue => constantValue
};
Debug.Assert((object?)defaultConstantValue != ConstantValue.Unset);

var callerSourceLocation = enableCallerInfo ? GetCallerLocation(syntax) : null;
BoundExpression defaultValue;
if (callerSourceLocation is object && parameter.IsCallerLineNumber)
{
int line = callerSourceLocation.SourceTree.GetDisplayLineNumber(callerSourceLocation.SourceSpan);
defaultValue = new BoundLiteral(syntax, ConstantValue.Create(line), Compilation.GetSpecialType(SpecialType.System_Int32)) { WasCompilerGenerated = true };
}
else if (callerSourceLocation is object && parameter.IsCallerFilePath)
{
string path = callerSourceLocation.SourceTree.GetDisplayPath(callerSourceLocation.SourceSpan, Compilation.Options.SourceReferenceResolver);
defaultValue = new BoundLiteral(syntax, ConstantValue.Create(path), Compilation.GetSpecialType(SpecialType.System_String)) { WasCompilerGenerated = true };
}
else if (callerSourceLocation is object && parameter.IsCallerMemberName)
{
var memberName = containingMember.GetMemberCallerName();
defaultValue = new BoundLiteral(syntax, ConstantValue.Create(memberName), Compilation.GetSpecialType(SpecialType.System_String)) { WasCompilerGenerated = true };
}
else if (defaultConstantValue == ConstantValue.NotAvailable)
{
// There is no constant value given for the parameter in source/metadata.
if (parameterType.IsDynamic() || parameterType.SpecialType == SpecialType.System_Object)
{
// We have something like M([Optional] object x). We have special handling for such situations.
defaultValue = GetDefaultParameterSpecialNoConversion(syntax, parameter, diagnostics);
}
else
{
// The argument to M([Optional] int x) becomes default(int)
defaultValue = new BoundDefaultExpression(syntax, parameterType) { WasCompilerGenerated = true };
}
}
else if (defaultConstantValue.IsNull)
{
defaultValue = new BoundDefaultExpression(syntax, parameterType) { WasCompilerGenerated = true };
}
else
{
TypeSymbol constantType = Compilation.GetSpecialType(defaultConstantValue.SpecialType);
defaultValue = new BoundLiteral(syntax, defaultConstantValue, constantType) { WasCompilerGenerated = true };
}

HashSet<DiagnosticInfo>? useSiteDiagnostics = null;
Conversion conversion = Conversions.ClassifyConversionFromExpression(defaultValue, parameterType, ref useSiteDiagnostics);
diagnostics.Add(syntax, useSiteDiagnostics);

if (!conversion.IsValid && defaultConstantValue is { SpecialType: SpecialType.System_Decimal or SpecialType.System_DateTime })
{
// Usually, if a default constant value fails to convert to the parameter type, we want an error at the call site.
// For legacy reasons, decimal and DateTime constants are special. If such a constant fails to convert to the parameter type
// then we want to silently replace it with default(ParameterType).
defaultValue = new BoundDefaultExpression(syntax, parameterType) { WasCompilerGenerated = true };
}
else
{
if (!conversion.IsValid)
{
GenerateImplicitConversionError(diagnostics, syntax, conversion, defaultValue, parameterType);
}
defaultValue = CreateConversion(defaultValue, conversion, parameterType, diagnostics);
}

return defaultValue;
}

private BoundExpression GetDefaultParameterSpecialNoConversion(SyntaxNode syntax, ParameterSymbol parameter, DiagnosticBag diagnostics)
{
var parameterType = parameter.Type;
Expand Down Expand Up @@ -1274,7 +1190,7 @@ private BoundExpression GetDefaultParameterSpecialNoConversion(SyntaxNode syntax
{
// new UnknownWrapper(default(object))
var unknownArgument = new BoundDefaultExpression(syntax, parameterType) { WasCompilerGenerated = true };
defaultValue = new BoundObjectCreationExpression(syntax, methodSymbol, null, unknownArgument) { WasCompilerGenerated = true };
defaultValue = new BoundObjectCreationExpression(syntax, methodSymbol, unknownArgument) { WasCompilerGenerated = true };
}
}
else if (parameter.IsIDispatchConstant)
Expand All @@ -1283,7 +1199,7 @@ private BoundExpression GetDefaultParameterSpecialNoConversion(SyntaxNode syntax
{
// new DispatchWrapper(default(object))
var dispatchArgument = new BoundDefaultExpression(syntax, parameterType) { WasCompilerGenerated = true };
defaultValue = new BoundObjectCreationExpression(syntax, methodSymbol, null, dispatchArgument) { WasCompilerGenerated = true };
defaultValue = new BoundObjectCreationExpression(syntax, methodSymbol, dispatchArgument) { WasCompilerGenerated = true };
}
}
else
Expand Down Expand Up @@ -1349,7 +1265,8 @@ internal void BindDefaultArguments(
out BitVector defaultArguments,
bool expanded,
bool enableCallerInfo,
DiagnosticBag diagnostics)
DiagnosticBag diagnostics,
bool assertMissingParametersAreOptional = true)
{

var visitedParameters = BitVector.Create(parameters.Length);
Expand All @@ -1362,8 +1279,8 @@ internal void BindDefaultArguments(
}
}

// only proceed with binding default arguments if we know there is some optional parameter that has not been matched by an explicit argument
if (!parameters.Any(static (param, visitedParameters) => !visitedParameters[param.Ordinal] && param.IsOptional, visitedParameters))
// only proceed with binding default arguments if we know there is some parameter that has not been matched by an explicit argument
if (parameters.All(static (param, visitedParameters) => visitedParameters[param.Ordinal], visitedParameters))
{
defaultArguments = default;
return;
Expand All @@ -1385,14 +1302,21 @@ internal void BindDefaultArguments(
argsToParamsBuilder.AddRange(argsToParamsOpt);
}

// Params methods can be invoked in normal form, so the strongest assertion we can make is that, if
// we're in an expanded context, the last param must be params. The inverse is not necessarily true.
Debug.Assert(!expanded || parameters[^1].IsParams);
// Params array is filled in the local rewriter
var lastIndex = expanded ? ^1 : ^0;

// Go over missing parameters, inserting default values for optional parameters
for (int i = 0; i < parameters.Length; i++)
foreach (var parameter in parameters.AsSpan()[..lastIndex])
{
var parameter = parameters[i];
if (!visitedParameters[parameter.Ordinal] && parameter.IsOptional)
if (!visitedParameters[parameter.Ordinal])
{
Debug.Assert(parameter.IsOptional || !assertMissingParametersAreOptional);

defaultArguments[argumentsBuilder.Count] = true;
argumentsBuilder.Add(BindDefaultArgument(node, parameter, containingMember, enableCallerInfo, diagnostics));
argumentsBuilder.Add(bindDefaultArgument(node, parameter, containingMember, enableCallerInfo, diagnostics));

if (argumentRefKindsBuilder is { Count: > 0 })
{
Expand All @@ -1410,6 +1334,89 @@ internal void BindDefaultArguments(
argsToParamsOpt = argsToParamsBuilder.ToImmutableOrNull();
argsToParamsBuilder.Free();
}

BoundExpression bindDefaultArgument(SyntaxNode syntax, ParameterSymbol parameter, Symbol containingMember, bool enableCallerInfo, DiagnosticBag diagnostics)
{
TypeSymbol parameterType = parameter.Type;
if (Flags.Includes(BinderFlags.ParameterDefaultValue))
{
// This is only expected to occur in recursive error scenarios, for example: `object F(object param = F()) { }`
// We return a non-error expression here to ensure ERR_DefaultValueMustBeConstant (or another appropriate diagnostics) is produced by the caller.
return new BoundDefaultExpression(syntax, parameterType) { WasCompilerGenerated = true };
}

var defaultConstantValue = parameter.ExplicitDefaultConstantValue switch
{
// Bad default values are implicitly replaced with default(T) at call sites.
{ IsBad: true } => ConstantValue.Null,
var constantValue => constantValue
};
Debug.Assert((object?)defaultConstantValue != ConstantValue.Unset);

var callerSourceLocation = enableCallerInfo ? GetCallerLocation(syntax) : null;
BoundExpression defaultValue;
if (callerSourceLocation is object && parameter.IsCallerLineNumber)
{
int line = callerSourceLocation.SourceTree.GetDisplayLineNumber(callerSourceLocation.SourceSpan);
defaultValue = new BoundLiteral(syntax, ConstantValue.Create(line), Compilation.GetSpecialType(SpecialType.System_Int32)) { WasCompilerGenerated = true };
}
else if (callerSourceLocation is object && parameter.IsCallerFilePath)
{
string path = callerSourceLocation.SourceTree.GetDisplayPath(callerSourceLocation.SourceSpan, Compilation.Options.SourceReferenceResolver);
defaultValue = new BoundLiteral(syntax, ConstantValue.Create(path), Compilation.GetSpecialType(SpecialType.System_String)) { WasCompilerGenerated = true };
}
else if (callerSourceLocation is object && parameter.IsCallerMemberName)
{
var memberName = containingMember.GetMemberCallerName();
defaultValue = new BoundLiteral(syntax, ConstantValue.Create(memberName), Compilation.GetSpecialType(SpecialType.System_String)) { WasCompilerGenerated = true };
}
else if (defaultConstantValue == ConstantValue.NotAvailable)
{
// There is no constant value given for the parameter in source/metadata.
if (parameterType.IsDynamic() || parameterType.SpecialType == SpecialType.System_Object)
{
// We have something like M([Optional] object x). We have special handling for such situations.
defaultValue = GetDefaultParameterSpecialNoConversion(syntax, parameter, diagnostics);
}
else
{
// The argument to M([Optional] int x) becomes default(int)
defaultValue = new BoundDefaultExpression(syntax, parameterType) { WasCompilerGenerated = true };
}
}
else if (defaultConstantValue.IsNull)
{
defaultValue = new BoundDefaultExpression(syntax, parameterType) { WasCompilerGenerated = true };
}
else
{
TypeSymbol constantType = Compilation.GetSpecialType(defaultConstantValue.SpecialType);
defaultValue = new BoundLiteral(syntax, defaultConstantValue, constantType) { WasCompilerGenerated = true };
}

HashSet<DiagnosticInfo>? useSiteDiagnostics = null;
Conversion conversion = Conversions.ClassifyConversionFromExpression(defaultValue, parameterType, ref useSiteDiagnostics);
diagnostics.Add(syntax, useSiteDiagnostics);

if (!conversion.IsValid && defaultConstantValue is { SpecialType: SpecialType.System_Decimal or SpecialType.System_DateTime })
{
// Usually, if a default constant value fails to convert to the parameter type, we want an error at the call site.
// For legacy reasons, decimal and DateTime constants are special. If such a constant fails to convert to the parameter type
// then we want to silently replace it with default(ParameterType).
defaultValue = new BoundDefaultExpression(syntax, parameterType) { WasCompilerGenerated = true };
}
else
{
if (!conversion.IsValid)
{
GenerateImplicitConversionError(diagnostics, syntax, conversion, defaultValue, parameterType);
}
defaultValue = CreateConversion(defaultValue, conversion, parameterType, diagnostics);
}

return defaultValue;
}

}

#nullable disable
Expand Down
Loading

0 comments on commit 6c5cef5

Please sign in to comment.