From b1ef9c1da056faf3175008f742daf5830b44cd37 Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Wed, 10 Sep 2025 08:35:02 -0700 Subject: [PATCH] WIP: Create BoundBadExpression instead of BoundCall for erroneous invocations There are 52 known test failures in C# compiler tests: ``` Test Class: OutVarTests Failed Stale (3) Project: Microsoft.CodeAnalysis.CSharp.Emit3.UnitTests (net9.0) Failed Stale (10) Namespace: Microsoft.CodeAnalysis.CSharp.UnitTests Failed Stale (5) Class: DiagnosticAnalyzerTests Failed Stale (1) Microsoft.CodeAnalysis.CSharp.UnitTests.DiagnosticAnalyzerTests.TestOperationConstructorBlockCallbackOnInvalidBaseCall Failed Stale Class: FlowDiagnosticTests Failed Stale (1) Microsoft.CodeAnalysis.CSharp.UnitTests.FlowDiagnosticTests.RegressionTest949324 Failed Stale Microsoft.CodeAnalysis.CSharp.UnitTests.OutVarTests.DiagnosticsDifferenceBetweenLanguageVersions_01 Failed Stale Microsoft.CodeAnalysis.CSharp.UnitTests.OutVarTests.OutVarDiscardInCtor_02 Failed Stale Microsoft.CodeAnalysis.CSharp.UnitTests.OutVarTests.VarIsNotVar_02 Failed Stale Namespace: Microsoft.CodeAnalysis.CSharp.UnitTests.Semantics Failed Stale (5) Class: ExtensionTests Failed Stale (4) Microsoft.CodeAnalysis.CSharp.UnitTests.Semantics.ExtensionTests.ExtensionMemberLookup_InterpolationHandler_AppendFormattedExtensionMethod Failed Stale Microsoft.CodeAnalysis.CSharp.UnitTests.Semantics.ExtensionTests.ExtensionMemberLookup_InterpolationHandler_AppendFormattedExtensionTypeMethod Failed Stale Microsoft.CodeAnalysis.CSharp.UnitTests.Semantics.ExtensionTests.ExtensionMemberLookup_InterpolationHandler_AppendLiteralExtensionDeclarationMethod Failed Stale Microsoft.CodeAnalysis.CSharp.UnitTests.Semantics.ExtensionTests.ExtensionMemberLookup_InterpolationHandler_AppendLiteralExtensionMethod Failed Stale Class: RefReadonlyParameterTests Failed Stale (1) Microsoft.CodeAnalysis.CSharp.UnitTests.Semantics.RefReadonlyParameterTests.RefReadonlyParameter_Ctor_OutArgument Failed Stale Project: Microsoft.CodeAnalysis.CSharp.IOperation.UnitTests (net9.0) Failed Stale (21) Namespace: Microsoft.CodeAnalysis.CSharp.UnitTests Failed Stale (21) Class: IOperationTests Failed Stale (1) Microsoft.CodeAnalysis.CSharp.UnitTests.IOperationTests.NullInPlaceOfParamArray Failed Stale Class: IOperationTests_IArgument Failed Stale (3) Microsoft.CodeAnalysis.CSharp.UnitTests.IOperationTests_IArgument.ExtraArgument Failed Stale Microsoft.CodeAnalysis.CSharp.UnitTests.IOperationTests_IArgument.TestOmittedArgument Failed Stale Microsoft.CodeAnalysis.CSharp.UnitTests.IOperationTests_IArgument.WrongArgumentType Failed Stale Class: IOperationTests_IConstructorBodyOperation Failed Stale (1) Microsoft.CodeAnalysis.CSharp.UnitTests.IOperationTests_IConstructorBodyOperation.ConstructorBody_19 Failed Stale Class: IOperationTests_IDeclarationExpression Failed Stale (1) Microsoft.CodeAnalysis.CSharp.UnitTests.IOperationTests_IDeclarationExpression.DeclarationFlow_01 Failed Stale Class: IOperationTests_IDefaultValueOperation Failed Stale (1) Microsoft.CodeAnalysis.CSharp.UnitTests.IOperationTests_IDefaultValueOperation.DefaultValueFlow_03 Failed Stale Class: IOperationTests_IInterpolatedStringExpression Failed Stale (4) Microsoft.CodeAnalysis.CSharp.UnitTests.IOperationTests_IInterpolatedStringExpression.InterpolatedStringExpression_EmptyInterpolationPart(hasDefaultHandler: True) Failed Stale Microsoft.CodeAnalysis.CSharp.UnitTests.IOperationTests_IInterpolatedStringExpression.InterpolatedStringExpression_InvalidExpressionInInterpolation(hasDefaultHandler: True) Failed Stale Microsoft.CodeAnalysis.CSharp.UnitTests.IOperationTests_IInterpolatedStringExpression.InterpolatedStringHandlerConversion_05 Failed Stale Microsoft.CodeAnalysis.CSharp.UnitTests.IOperationTests_IInterpolatedStringExpression.InterpolatedStringHandlerConversion_07 Failed Stale Class: IOperationTests_InvalidExpression Failed Stale (4) Microsoft.CodeAnalysis.CSharp.UnitTests.IOperationTests_InvalidExpression.BuildsArgumentsOperationsForDuplicateExplicitArguments_CorrectArgumentsOrder_Methods Failed Stale Microsoft.CodeAnalysis.CSharp.UnitTests.IOperationTests_InvalidExpression.BuildsArgumentsOperationsForDuplicateExplicitArguments_IncorrectArgumentsOrder_Methods Failed Stale Microsoft.CodeAnalysis.CSharp.UnitTests.IOperationTests_InvalidExpression.InvalidInvocationExpression_OverloadResolutionFailureBadArgument Failed Stale Microsoft.CodeAnalysis.CSharp.UnitTests.IOperationTests_InvalidExpression.InvalidInvocationExpression_OverloadResolutionFailureExtraArgument Failed Stale Class: IOperationTests_IObjectCreationExpression Failed Stale (4) Microsoft.CodeAnalysis.CSharp.UnitTests.IOperationTests_IObjectCreationExpression.ImplicitObjectCreationCollectionInitializerStaticAddMethod Failed Stale Microsoft.CodeAnalysis.CSharp.UnitTests.IOperationTests_IObjectCreationExpression.ImplicitObjectCreationCollectionInitializerWithRefAddMethod Failed Stale Microsoft.CodeAnalysis.CSharp.UnitTests.IOperationTests_IObjectCreationExpression.ObjectCreationCollectionInitializerStaticAddMethod Failed Stale Microsoft.CodeAnalysis.CSharp.UnitTests.IOperationTests_IObjectCreationExpression.ObjectCreationCollectionInitializerWithRefAddMethod Failed Stale Class: IOperationTests_IThrowOperation Failed Stale (1) Microsoft.CodeAnalysis.CSharp.UnitTests.IOperationTests_IThrowOperation.ThrowFlow_13 Failed Stale Class: IOperationTests_StackAllocArrayCreationAndInitializer Failed Stale (1) Microsoft.CodeAnalysis.CSharp.UnitTests.IOperationTests_StackAllocArrayCreationAndInitializer.StackAllocArrayCreationErrorCase_InvocationExpressionAsDimension Failed Stale Project: Microsoft.CodeAnalysis.CSharp.Semantic.UnitTests (net472) Failed Stale (12) Namespace: Microsoft.CodeAnalysis.CSharp.UnitTests Failed Stale (5) Class: DelegateTypeTests Failed Stale (1) Microsoft.CodeAnalysis.CSharp.UnitTests.DelegateTypeTests.TypeInference_03 Failed Stale Class: FunctionPointerTests Failed Stale (1) Microsoft.CodeAnalysis.CSharp.UnitTests.FunctionPointerTests.FunctionPointerInferenceInReturn Failed Stale Class: LocalFunctionTests Failed Stale (2) Microsoft.CodeAnalysis.CSharp.UnitTests.LocalFunctionTests.ConstraintBinding2 Failed Stale Microsoft.CodeAnalysis.CSharp.UnitTests.LocalFunctionTests.ParameterScope_NotInTypeConstraint Failed Stale Class: UnsafeTests Failed Stale (1) Microsoft.CodeAnalysis.CSharp.UnitTests.UnsafeTests.FixingVariables_TypeParameters2 Failed Stale Namespace: Microsoft.CodeAnalysis.CSharp.UnitTests.Semantics Failed Stale (7) Class: BindingTests Failed Stale (2) Microsoft.CodeAnalysis.CSharp.UnitTests.Semantics.BindingTests.NonMethodsWithArgs Failed Stale Microsoft.CodeAnalysis.CSharp.UnitTests.Semantics.BindingTests.NonViableDelegates Failed Stale Class: InterpolationTests Failed Stale (1) Microsoft.CodeAnalysis.CSharp.UnitTests.Semantics.InterpolationTests.MissingAppendMethods Failed Stale Class: NullableReferenceTypesTests Failed Stale (4) Microsoft.CodeAnalysis.CSharp.UnitTests.Semantics.NullableReferenceTypesTests.Lambda_21 Failed Stale Microsoft.CodeAnalysis.CSharp.UnitTests.Semantics.NullableReferenceTypesTests.MemberNotNull_NonStaticLocalFunction_MissingArgument Failed Stale Microsoft.CodeAnalysis.CSharp.UnitTests.Semantics.NullableReferenceTypesTests.NullableBaseMembers Failed Stale Microsoft.CodeAnalysis.CSharp.UnitTests.Semantics.NullableReferenceTypesTests.TypeInference_LowerBounds_NestedNullability_Pointers Failed Stale Project: Microsoft.CodeAnalysis.CSharp.Symbol.UnitTests (net472) Failed Stale (9) Namespace: Microsoft.CodeAnalysis.CSharp.UnitTests Failed Stale (3) Class: NullablePublicAPITests Failed Stale (1) Microsoft.CodeAnalysis.CSharp.UnitTests.NullablePublicAPITests.LambdaInBadExpression Failed Stale Class: SemanticModelGetSemanticInfoTests Failed Stale (1) Microsoft.CodeAnalysis.CSharp.UnitTests.SemanticModelGetSemanticInfoTests.GenericExtensionMethodCall Failed Stale Class: SemanticTests Failed Stale (1) Microsoft.CodeAnalysis.CSharp.UnitTests.SemanticTests.UnmanagedConstraintOnExtensionMethod Failed Stale Namespace: Microsoft.CodeAnalysis.CSharp.UnitTests.Symbols Failed Stale (6) Class: ExtensionMethodTests Failed Stale (1) Microsoft.CodeAnalysis.CSharp.UnitTests.Symbols.ExtensionMethodTests.InaccessibleInstanceMember Failed Stale Class: RequiredMembersTests Failed Stale (5) Test Group: Microsoft.CodeAnalysis.CSharp.UnitTests.Symbols.RequiredMembersTests.ForbidRequiredAsNew_Inheritance Failed Stale (2) Microsoft.CodeAnalysis.CSharp.UnitTests.Symbols.RequiredMembersTests.ForbidRequiredAsNew_Inheritance(useMetadataReference: False) Failed Stale Microsoft.CodeAnalysis.CSharp.UnitTests.Symbols.RequiredMembersTests.ForbidRequiredAsNew_Inheritance(useMetadataReference: True) Failed Stale Microsoft.CodeAnalysis.CSharp.UnitTests.Symbols.RequiredMembersTests.ForbidRequiredAsNew_MalformedMembersList Failed Stale Test Group: Microsoft.CodeAnalysis.CSharp.UnitTests.Symbols.RequiredMembersTests.ForbidRequiredAsNew_NoInheritance Failed Stale (2) Microsoft.CodeAnalysis.CSharp.UnitTests.Symbols.RequiredMembersTests.ForbidRequiredAsNew_NoInheritance(typeKind: "class") Failed Stale Microsoft.CodeAnalysis.CSharp.UnitTests.Symbols.RequiredMembersTests.ForbidRequiredAsNew_NoInheritance(typeKind: "struct") Failed Stale ``` --- .../Portable/Binder/Binder_Conversions.cs | 5 + .../Portable/Binder/Binder_Deconstruct.cs | 6 + .../Portable/Binder/Binder_Expressions.cs | 4 +- .../Portable/Binder/Binder_Invocation.cs | 89 +++++++++-- .../CSharp/Portable/Binder/Binder_Query.cs | 148 ++++++++++++------ .../Portable/Binder/Binder_Statements.cs | 8 +- .../BoundTree/BoundImplicitIndexerAccess.cs | 2 +- .../CSharp/Portable/BoundTree/BoundNodes.xml | 2 + .../CSharp/Portable/BoundTree/Constructors.cs | 14 +- .../Compilation/CSharpSemanticModel.cs | 5 + .../Compilation/MemberSemanticModel.cs | 32 ++++ .../Portable/FlowAnalysis/AbstractFlowPass.cs | 66 ++++---- .../FlowAnalysis/AlwaysAssignedWalker.cs | 4 +- .../FlowAnalysis/DefiniteAssignment.cs | 17 +- .../Generated/BoundNodes.xml.Generated.cs | 17 +- .../Operations/CSharpOperationFactory.cs | 2 +- .../Portable/Symbols/TypeSymbolExtensions.cs | 30 ++++ 17 files changed, 337 insertions(+), 114 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs index c5948108b5049..953b2c6dd71f3 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs @@ -1652,6 +1652,11 @@ internal static BoundExpression GetUnderlyingCollectionExpressionElement(BoundCo Debug.Assert(call.Method.Name == "Add"); return call.Arguments[call.InvokedAsExtensionMethod ? 1 : 0]; case BoundBadExpression badExpression: + if (badExpression.ChildBoundNodes is [BoundObjectOrCollectionValuePlaceholder, var child]) + { + return child; + } + Debug.Assert(false); // Add test if we hit this assert. return badExpression; default: diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Deconstruct.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Deconstruct.cs index 36f16a496685c..c2bcdf8659de6 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Deconstruct.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Deconstruct.cs @@ -679,6 +679,12 @@ private BoundExpression MakeDeconstructInvocationExpression( return MissingDeconstruct(receiver, rightSyntax, numCheckedVariables, diagnostics, out outPlaceholders, result); } + if (result.Kind == BoundKind.BadExpression) + { + outPlaceholders = default; + return result; + } + // Verify all the parameters (except "this" for extension methods) are out parameters. // This prevents, for example, an unused params parameter after the out parameters. var deconstructMethod = ((BoundCall)result).Method; diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index cc20ba28415c6..0ca917ffcc663 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -6852,7 +6852,7 @@ private BoundObjectCreationExpression BindClassCreationExpressionContinued( return creation; } - private BoundExpression CreateBadClassCreationExpression( + private BoundBadExpression CreateBadClassCreationExpression( SyntaxNode node, SyntaxNode typeNode, NamedTypeSymbol type, @@ -10263,7 +10263,7 @@ private bool TryBindIndexOrRangeImplicitIndexer( } Debug.Assert(lengthOrCountAccess is BoundPropertyAccess); - Debug.Assert(indexerOrSliceAccess is BoundIndexerAccess or BoundCall); + Debug.Assert(indexerOrSliceAccess is BoundIndexerAccess or BoundCall or BoundBadExpression); Debug.Assert(indexerOrSliceAccess.Type is not null); implicitIndexerAccess = new BoundImplicitIndexerAccess( diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs index edc5a1bdea0da..513a99b0adc7b 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs @@ -1098,6 +1098,7 @@ private void CheckRestrictedTypeReceiver(BoundExpression expression, CSharpCompi } break; case BoundKind.FunctionPointerInvocation: + case BoundKind.BadExpression: break; default: throw ExceptionUtilities.UnexpectedValue(expression.Kind); @@ -1118,7 +1119,7 @@ private void CheckRestrictedTypeReceiver(BoundExpression expression, CSharpCompi /// Diagnostics. /// The syntax for the query clause generating this invocation expression, if any. /// BoundCall or error expression representing the invocation. - private BoundCall BindInvocationExpressionContinued( + private BoundExpression BindInvocationExpressionContinued( SyntaxNode node, SyntaxNode expression, string methodName, @@ -1972,7 +1973,7 @@ private static NamedTypeSymbol GetDelegateType(BoundExpression expr) return null; } - private BoundCall CreateBadCall( + private BoundExpression CreateBadCall( SyntaxNode node, string name, BoundExpression receiver, @@ -1983,7 +1984,6 @@ private BoundCall CreateBadCall( bool invokedAsExtensionMethod, bool isDelegate) { - MethodSymbol method; ImmutableArray args; if (!typeArgumentsWithAnnotations.IsDefaultOrEmpty) { @@ -2010,24 +2010,83 @@ private BoundCall CreateBadCall( methods = constructedMethods.ToImmutableAndFree(); } + TypeSymbol resultType; + if (methods.Length == 1 && !IsUnboundGeneric(methods[0])) { - method = methods[0]; + resultType = methods[0].ReturnType; } else { - var returnType = GetCommonTypeOrReturnType(methods) ?? new ExtendedErrorTypeSymbol(this.Compilation, string.Empty, arity: 0, errorInfo: null); - var methodContainer = (object)receiver != null && (object)receiver.Type != null - ? receiver.Type - : this.ContainingType; - method = new ErrorMethodSymbol(methodContainer, returnType, name); + resultType = GetCommonTypeOrReturnType(methods) ?? new ExtendedErrorTypeSymbol(this.Compilation, string.Empty, arity: 0, errorInfo: null); } args = BuildArgumentsForErrorRecovery(analyzedArguments, methods); - var argNames = analyzedArguments.GetNames(); var argRefKinds = analyzedArguments.RefKinds.ToImmutableOrNull(); receiver = BindToTypeForErrorRecovery(receiver); - return BoundCall.ErrorCall(node, receiver, method, args, argNames, argRefKinds, isDelegate, invokedAsExtensionMethod: invokedAsExtensionMethod, originalMethods: methods, resultKind: resultKind, binder: this); + return ErrorCall(node, receiver, args, argRefKinds, methods, resultKind, resultType, binder: this); + } + + private static BoundBadExpression ErrorCall( + SyntaxNode node, + BoundExpression receiverOpt, + ImmutableArray arguments, + ImmutableArray argumentRefKindsOpt, + ImmutableArray originalMethods, + LookupResultKind resultKind, + TypeSymbol resultType, + Binder binder) + { + if (!originalMethods.IsEmpty) + resultKind = resultKind.WorseResultKind(LookupResultKind.OverloadResolutionFailure); + + var childBoundNodes = arguments.SelectAsArray((e, binder) => binder.BindToTypeForErrorRecovery(e), binder); + + if (receiverOpt is not null) + { + childBoundNodes = childBoundNodes.Insert(0, binder.BindToTypeForErrorRecovery(receiverOpt)); + + RefKind receiverRefKind = RefKind.None; + + if (originalMethods is [MethodSymbol singleCandidate]) + { + if (singleCandidate.TryGetThisParameter(out var thisParameter) + && thisParameter is object + && !thisParameter.Type.TypeIsImmutable()) + { + receiverRefKind = thisParameter.RefKind; + //if (thisRefKind.IsWritableReference()) + //{ + // WriteArgument(receiverOpt, thisRefKind, method); + //} + } + + } + else if (!originalMethods.Any(m => m.MethodKind == MethodKind.Constructor && !m.ContainingType.IsStructType())) + { + receiverRefKind = RefKind.Ref; + } + + if (!argumentRefKindsOpt.IsDefault) + { + argumentRefKindsOpt = argumentRefKindsOpt.Insert(0, receiverRefKind); + } + else if (receiverRefKind != RefKind.None) + { + var builder = ArrayBuilder.GetInstance(childBoundNodes.Length, RefKind.None); + builder[0] = receiverRefKind; + argumentRefKindsOpt = builder.ToImmutableAndFree(); + } + } + + return new BoundBadExpression( + syntax: node, + resultKind: resultKind, + symbols: originalMethods.Cast(), + childBoundNodes: childBoundNodes, + argumentRefKindsOpt: argumentRefKindsOpt, + type: resultType, + hasErrors: true); } private static bool IsUnboundGeneric(MethodSymbol method) @@ -2251,22 +2310,18 @@ private ImmutableArray BuildArgumentsForErrorRecovery(AnalyzedA return BuildArgumentsForErrorRecovery(analyzedArguments, Enumerable.Empty>()); } - private BoundCall CreateBadCall( + private BoundExpression CreateBadCall( SyntaxNode node, BoundExpression expr, LookupResultKind resultKind, AnalyzedArguments analyzedArguments) { TypeSymbol returnType = new ExtendedErrorTypeSymbol(this.Compilation, string.Empty, arity: 0, errorInfo: null); - var methodContainer = expr.Type ?? this.ContainingType; - MethodSymbol method = new ErrorMethodSymbol(methodContainer, returnType, string.Empty); - var args = BuildArgumentsForErrorRecovery(analyzedArguments); - var argNames = analyzedArguments.GetNames(); var argRefKinds = analyzedArguments.RefKinds.ToImmutableOrNull(); var originalMethods = (expr.Kind == BoundKind.MethodGroup) ? ((BoundMethodGroup)expr).Methods : ImmutableArray.Empty; - return BoundCall.ErrorCall(node, expr, method, args, argNames, argRefKinds, isDelegateCall: false, invokedAsExtensionMethod: false, originalMethods: originalMethods, resultKind: resultKind, binder: this); + return ErrorCall(node, expr, args, argRefKinds, originalMethods, resultKind: resultKind, returnType, binder: this); } private static TypeSymbol GetCommonTypeOrReturnType(ImmutableArray members) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs index a5259fae6226f..12a3e619472a1 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs @@ -254,7 +254,7 @@ private BoundExpression FinalTranslation(QueryTranslationState state, BindingDia var v = groupClause.GroupExpression; var k = groupClause.ByExpression; var vId = v as IdentifierNameSyntax; - BoundCall result; + BoundExpression result; var lambdaLeft = MakeQueryUnboundLambda(state.RangeVariableMap(), x, k, diagnostics.AccumulatesDependencies); // this is the unoptimized form (when v is not the identifier x) @@ -305,29 +305,42 @@ private BoundExpression FinalTranslation(QueryTranslationState state, BindingDia } } - private static BoundCall ReverseLastTwoParameterOrder(BoundCall result) + private static BoundExpression ReverseLastTwoParameterOrder(BoundExpression possibleCall) { // The input call has its arguments in the appropriate order for the invocation, but its last // two argument expressions appear in the reverse order from which they appeared in source. // Since we want region analysis to see them in source order, we rewrite the call so that these // two arguments are evaluated in source order. - int n = result.Arguments.Length; - var arguments = ArrayBuilder.GetInstance(); - arguments.AddRange(result.Arguments); - var lastArgument = arguments[n - 1]; - arguments[n - 1] = arguments[n - 2]; - arguments[n - 2] = lastArgument; - var argsToParams = ArrayBuilder.GetInstance(); - argsToParams.AddRange(Enumerable.Range(0, n)); - argsToParams[n - 1] = n - 2; - argsToParams[n - 2] = n - 1; - var defaultArguments = result.DefaultArguments.Clone(); - (defaultArguments[n - 1], defaultArguments[n - 2]) = (defaultArguments[n - 2], defaultArguments[n - 1]); - - return result.Update( - result.ReceiverOpt, result.InitialBindingReceiverIsSubjectToCloning, result.Method, arguments.ToImmutableAndFree(), argumentNamesOpt: default, - argumentRefKindsOpt: default, result.IsDelegateCall, result.Expanded, result.InvokedAsExtensionMethod, - argsToParams.ToImmutableAndFree(), defaultArguments, result.ResultKind, result.OriginalMethodsOpt, result.Type); + if (possibleCall is BoundCall result) + { + int n = result.Arguments.Length; + var argsToParams = ArrayBuilder.GetInstance(); + argsToParams.AddRange(Enumerable.Range(0, n)); + argsToParams[n - 1] = n - 2; + argsToParams[n - 2] = n - 1; + var defaultArguments = result.DefaultArguments.Clone(); + (defaultArguments[n - 1], defaultArguments[n - 2]) = (defaultArguments[n - 2], defaultArguments[n - 1]); + + return result.Update( + result.ReceiverOpt, result.InitialBindingReceiverIsSubjectToCloning, result.Method, adjustArguments(result.Arguments), argumentNamesOpt: default, + argumentRefKindsOpt: default, result.IsDelegateCall, result.Expanded, result.InvokedAsExtensionMethod, + argsToParams.ToImmutableAndFree(), defaultArguments, result.ResultKind, result.OriginalMethodsOpt, result.Type); + } + + var badResult = (BoundBadExpression)possibleCall; + + return badResult.Update(badResult.ResultKind, badResult.Symbols, adjustArguments(badResult.ChildBoundNodes), argumentRefKindsOpt: default, badResult.Type); + + static ImmutableArray adjustArguments(ImmutableArray originalArguments) + { + int n = originalArguments.Length; + var arguments = ArrayBuilder.GetInstance(); + arguments.AddRange(originalArguments); + var lastArgument = arguments[n - 1]; + arguments[n - 1] = arguments[n - 2]; + arguments[n - 2] = lastArgument; + return arguments.ToImmutableAndFree(); + } } private void ReduceQuery(QueryTranslationState state, BindingDiagnosticBag diagnostics) @@ -412,7 +425,7 @@ private void ReduceJoin(JoinClauseSyntax join, QueryTranslationState state, Bind if (state.clauses.IsEmpty() && state.selectOrGroup.Kind() == SyntaxKind.SelectClause) { var select = (SelectClauseSyntax)state.selectOrGroup; - BoundCall invocation; + BoundExpression invocation; if (join.Into == null) { // A query expression with a join clause without an into followed by a select clause @@ -468,10 +481,17 @@ private void ReduceJoin(JoinClauseSyntax join, QueryTranslationState state, Bind #endif // record the into clause in the bound tree - var arguments = invocation.Arguments; - arguments = arguments.SetItem(arguments.Length - 1, MakeQueryClause(join.Into, arguments[arguments.Length - 1], g)); - - invocation = invocation.Update(invocation.ReceiverOpt, invocation.InitialBindingReceiverIsSubjectToCloning, invocation.Method, arguments); + if (invocation is BoundCall invocationCall) + { + invocation = invocationCall.Update(invocationCall.ReceiverOpt, invocationCall.InitialBindingReceiverIsSubjectToCloning, invocationCall.Method, + adjustLastArgument(join.Into, g, invocationCall.Arguments)); + } + else + { + var badInvocation = (BoundBadExpression)invocation; + invocation = badInvocation.Update(badInvocation.ResultKind, badInvocation.Symbols, + adjustLastArgument(join.Into, g, badInvocation.ChildBoundNodes), badInvocation.Type); + } } state.Clear(); // this completes the whole query @@ -480,7 +500,7 @@ private void ReduceJoin(JoinClauseSyntax join, QueryTranslationState state, Bind } else { - BoundCall invocation; + BoundExpression invocation; if (join.Into == null) { // A query expression with a join clause without an into followed by something other than a select clause @@ -539,14 +559,27 @@ private void ReduceJoin(JoinClauseSyntax join, QueryTranslationState state, Bind state.nextInvokedMethodName = null; #endif - var arguments = invocation.Arguments; - arguments = arguments.SetItem(arguments.Length - 1, MakeQueryClause(join.Into, arguments[arguments.Length - 1], g)); - - invocation = invocation.Update(invocation.ReceiverOpt, invocation.InitialBindingReceiverIsSubjectToCloning, invocation.Method, arguments); + if (invocation is BoundCall invocationCall) + { + invocation = invocationCall.Update(invocationCall.ReceiverOpt, invocationCall.InitialBindingReceiverIsSubjectToCloning, invocationCall.Method, + adjustLastArgument(join.Into, g, invocationCall.Arguments)); + } + else + { + var badInvocation = (BoundBadExpression)invocation; + invocation = badInvocation.Update(badInvocation.ResultKind, badInvocation.Symbols, + adjustLastArgument(join.Into, g, badInvocation.ChildBoundNodes), badInvocation.Type); + } } state.fromExpression = MakeQueryClause(join, invocation, x2, invocation, castInvocation); } + + ImmutableArray adjustLastArgument(JoinIntoClauseSyntax joinInto, RangeVariableSymbol g, ImmutableArray arguments) + { + arguments = arguments.SetItem(arguments.Length - 1, MakeQueryClause(joinInto, arguments[arguments.Length - 1], g)); + return arguments; + } } private void ReduceOrderBy(OrderByClauseSyntax orderby, QueryTranslationState state, BindingDiagnosticBag diagnostics) @@ -630,12 +663,23 @@ private void ReduceFrom(FromClauseSyntax from, QueryTranslationState state, Bind // Adjust the second-to-last parameter to be a query clause (if it was an extension method, an extra parameter was added) BoundExpression? castInvocation = (from.Type != null) ? ExtractCastInvocation(invocation) : null; - var arguments = invocation.Arguments; - invocation = invocation.Update( - invocation.ReceiverOpt, - invocation.InitialBindingReceiverIsSubjectToCloning, - invocation.Method, - arguments.SetItem(arguments.Length - 2, MakeQueryClause(from, arguments[arguments.Length - 2], x2, invocation, castInvocation))); + if (invocation is BoundCall invocationCall) + { + invocation = invocationCall.Update( + invocationCall.ReceiverOpt, + invocationCall.InitialBindingReceiverIsSubjectToCloning, + invocationCall.Method, + adjustSecondToLastArgument(from, x2, invocation, castInvocation, invocationCall.Arguments)); + } + else + { + var badInvocation = (BoundBadExpression)invocation; + invocation = badInvocation.Update( + badInvocation.ResultKind, + badInvocation.Symbols, + adjustSecondToLastArgument(from, x2, invocation, castInvocation, badInvocation.ChildBoundNodes), + badInvocation.Type); + } state.Clear(); state.fromExpression = MakeQueryClause(from, invocation, definedSymbol: x2, queryInvocation: invocation); @@ -678,16 +722,26 @@ private void ReduceFrom(FromClauseSyntax from, QueryTranslationState state, Bind BoundExpression? castInvocation = (from.Type != null) ? ExtractCastInvocation(invocation) : null; state.fromExpression = MakeQueryClause(from, invocation, x2, invocation, castInvocation); } + + ImmutableArray adjustSecondToLastArgument(FromClauseSyntax from, RangeVariableSymbol x2, BoundExpression invocation, BoundExpression? castInvocation, ImmutableArray arguments) + { + return arguments.SetItem(arguments.Length - 2, MakeQueryClause(from, arguments[arguments.Length - 2], x2, invocation, castInvocation)); + } } - private static BoundExpression? ExtractCastInvocation(BoundCall invocation) + private static BoundExpression? ExtractCastInvocation(BoundExpression possibleCall) { - int index = invocation.InvokedAsExtensionMethod ? 1 : 0; - var c1 = invocation.Arguments[index] as BoundConversion; - var l1 = c1 != null ? c1.Operand as BoundLambda : null; - var r1 = l1 != null ? l1.Body.Statements[0] as BoundReturnStatement : null; - var i1 = r1 != null ? r1.ExpressionOpt as BoundCall : null; - return i1; + if (possibleCall is BoundCall invocation) + { + int index = invocation.InvokedAsExtensionMethod ? 1 : 0; + var c1 = invocation.Arguments[index] as BoundConversion; + var l1 = c1 != null ? c1.Operand as BoundLambda : null; + var r1 = l1 != null ? l1.Body.Statements[0] as BoundReturnStatement : null; + var i1 = r1 != null ? r1.ExpressionOpt as BoundCall : null; + return i1; + } + + return null; } private UnboundLambda MakePairLambda(CSharpSyntaxNode node, QueryTranslationState state, RangeVariableSymbol x1, RangeVariableSymbol x2, bool withDependencies) @@ -877,7 +931,7 @@ private static UnboundLambda MakeQueryUnboundLambda(CSharpSyntaxNode node, Query return lambda; } - protected BoundCall MakeQueryInvocation(CSharpSyntaxNode node, BoundExpression receiver, bool receiverIsCheckedForRValue, string methodName, BoundExpression arg, BindingDiagnosticBag diagnostics + protected BoundExpression MakeQueryInvocation(CSharpSyntaxNode node, BoundExpression receiver, bool receiverIsCheckedForRValue, string methodName, BoundExpression arg, BindingDiagnosticBag diagnostics #if DEBUG , string? expectedMethodName #endif @@ -890,7 +944,7 @@ protected BoundCall MakeQueryInvocation(CSharpSyntaxNode node, BoundExpression r ); } - protected BoundCall MakeQueryInvocation(CSharpSyntaxNode node, BoundExpression receiver, bool receiverIsCheckedForRValue, string methodName, ImmutableArray args, BindingDiagnosticBag diagnostics + protected BoundExpression MakeQueryInvocation(CSharpSyntaxNode node, BoundExpression receiver, bool receiverIsCheckedForRValue, string methodName, ImmutableArray args, BindingDiagnosticBag diagnostics #if DEBUG , string? expectedMethodName #endif @@ -903,7 +957,7 @@ protected BoundCall MakeQueryInvocation(CSharpSyntaxNode node, BoundExpression r ); } - protected BoundCall MakeQueryInvocation(CSharpSyntaxNode node, BoundExpression receiver, bool receiverIsCheckedForRValue, string methodName, TypeSyntax typeArgSyntax, TypeWithAnnotations typeArg, BindingDiagnosticBag diagnostics + protected BoundExpression MakeQueryInvocation(CSharpSyntaxNode node, BoundExpression receiver, bool receiverIsCheckedForRValue, string methodName, TypeSyntax typeArgSyntax, TypeWithAnnotations typeArg, BindingDiagnosticBag diagnostics #if DEBUG , string? expectedMethodName #endif @@ -916,7 +970,7 @@ protected BoundCall MakeQueryInvocation(CSharpSyntaxNode node, BoundExpression r ); } - protected BoundCall MakeQueryInvocation(CSharpSyntaxNode node, BoundExpression receiver, bool receiverIsCheckedForRValue, string methodName, SeparatedSyntaxList typeArgsSyntax, ImmutableArray typeArgs, ImmutableArray args, BindingDiagnosticBag diagnostics + protected BoundExpression MakeQueryInvocation(CSharpSyntaxNode node, BoundExpression receiver, bool receiverIsCheckedForRValue, string methodName, SeparatedSyntaxList typeArgsSyntax, ImmutableArray typeArgs, ImmutableArray args, BindingDiagnosticBag diagnostics #if DEBUG , string? expectedMethodName #endif @@ -1002,7 +1056,7 @@ protected BoundCall MakeQueryInvocation(CSharpSyntaxNode node, BoundExpression r } } - return (BoundCall)MakeInvocationExpression( + return MakeInvocationExpression( node, receiver, methodName, diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index 2becf428155e4..8695ed76d4364 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -1746,7 +1746,7 @@ internal static PropertySymbol GetPropertySymbol(BoundExpression expr, out Bound // this[int] BoundImplicitIndexerAccess { IndexerOrSliceAccess: BoundIndexerAccess indexerAccess } => indexerAccess.Indexer, // array[Index] - BoundImplicitIndexerAccess { IndexerOrSliceAccess: BoundArrayAccess } => null, + BoundImplicitIndexerAccess { IndexerOrSliceAccess: BoundArrayAccess or BoundBadExpression } => null, // array[int or Range] BoundArrayAccess => null, BoundDynamicIndexerAccess => null, @@ -4182,6 +4182,12 @@ internal PatternLookupResult PerformPatternMethodLookup(BoundExpression receiver if (patternMethodCall.Kind != BoundKind.Call) { + if (patternMethodCall is BoundBadExpression { ResultKind: not LookupResultKind.Empty }) + { + diagnostics.AddRange(bindingDiagnostics); + return PatternLookupResult.ResultHasErrors; + } + return PatternLookupResult.NotCallable; } diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundImplicitIndexerAccess.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundImplicitIndexerAccess.cs index ae4f2da2ff75e..e43b2f1166107 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundImplicitIndexerAccess.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundImplicitIndexerAccess.cs @@ -18,7 +18,7 @@ internal BoundImplicitIndexerAccess WithLengthOrCountAccess(BoundExpression leng private partial void Validate() { Debug.Assert(LengthOrCountAccess is BoundPropertyAccess or BoundArrayLength or BoundLocal or BoundBadExpression); - Debug.Assert(IndexerOrSliceAccess is BoundIndexerAccess or BoundCall or BoundArrayAccess); + Debug.Assert(IndexerOrSliceAccess is BoundIndexerAccess or BoundCall or BoundArrayAccess or BoundBadExpression); } } } diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml index 6900c90d9ce5f..9378ff8e4aa91 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml @@ -219,6 +219,8 @@ + + diff --git a/src/Compilers/CSharp/Portable/BoundTree/Constructors.cs b/src/Compilers/CSharp/Portable/BoundTree/Constructors.cs index 58b62bd2e5213..c68c6b52b6f5a 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/Constructors.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/Constructors.cs @@ -117,7 +117,7 @@ public BoundCall Update(BoundExpression? receiverOpt, LookupResultKind resultKind, TypeSymbol type) => Update(receiverOpt, initialBindingReceiverIsSubjectToCloning, method, arguments, argumentNamesOpt, argumentRefKindsOpt, isDelegateCall, expanded, invokedAsExtensionMethod, argsToParamsOpt, defaultArguments, resultKind, this.OriginalMethodsOpt, type); - +/* public static BoundCall ErrorCall( SyntaxNode node, BoundExpression receiverOpt, @@ -154,7 +154,7 @@ public static BoundCall ErrorCall( type: method.ReturnType, hasErrors: true); } - +*/ public BoundCall Update(ImmutableArray arguments) { return this.Update(ReceiverOpt, InitialBindingReceiverIsSubjectToCloning, Method, arguments, ArgumentNamesOpt, ArgumentRefKindsOpt, IsDelegateCall, Expanded, InvokedAsExtensionMethod, ArgsToParamsOpt, DefaultArguments, ResultKind, OriginalMethodsOpt, Type); @@ -530,6 +530,16 @@ public BoundBadExpression(SyntaxNode syntax, LookupResultKind resultKind, Immuta { Debug.Assert((object)type != null); } + + public BoundBadExpression(SyntaxNode syntax, LookupResultKind resultKind, ImmutableArray symbols, ImmutableArray childBoundNodes, TypeSymbol? type, bool hasErrors = false) + : this(syntax, resultKind, symbols, childBoundNodes, argumentRefKindsOpt: default, type, hasErrors) + { + } + + public BoundBadExpression Update(LookupResultKind resultKind, ImmutableArray symbols, ImmutableArray childBoundNodes, TypeSymbol? type) + { + return Update(resultKind, symbols, childBoundNodes, ArgumentRefKindsOpt, type); + } } internal partial class BoundStatementList diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs index 757547a9ba86b..23fb23ec758c0 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs @@ -4344,6 +4344,11 @@ private OneOrMany GetMethodGroupSemanticSymbols( ImmutableArray myMethodGroup = memberGroup; symbols = OneOrMany.Create(((BoundBadExpression)boundNodeForSyntacticParent).Symbols.WhereAsArray((sym, myMethodGroup) => myMethodGroup.Contains(sym), myMethodGroup)); + if (!symbols.Any()) + { + symbols = OneOrMany.Create(myMethodGroup.WhereAsArray(static (g, symbols) => symbols.Any(static (sym, g) => sym.Equals((g as MethodSymbol)?.ReducedFrom), g), ((BoundBadExpression)boundNodeForSyntacticParent).Symbols)); + } + if (symbols.Any()) { resultKind = ((BoundBadExpression)boundNodeForSyntacticParent).ResultKind; diff --git a/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs index d20133b92585e..fa5b9e0d2c870 100644 --- a/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs @@ -1805,6 +1805,38 @@ private static Binder GetQueryEnclosingBinder(int position, CSharpSyntaxNode sta // TODO: should we look for the "nearest" argument as a fallback? node = call.Arguments.LastOrDefault(); continue; + + case BoundKind.BadExpression: + var badExpression = (BoundBadExpression)node; + node = GetContainingArgument(badExpression.ChildBoundNodes, position); + if (node != null) + { + continue; + } + + if (badExpression.ChildBoundNodes is [BoundExpression badReceiver, ..]) + { + // In some error scenarios, we end-up with a method group as the receiver, + // let's get to real receiver. + while (badReceiver?.Kind == BoundKind.MethodGroup) + { + receiver = ((BoundMethodGroup)badReceiver).ReceiverOpt; + } + + if (badReceiver != null) + { + node = GetContainingExprOrQueryClause(badReceiver, position); + if (node != null) + { + continue; + } + } + } + + // TODO: should we look for the "nearest" argument as a fallback? + node = badExpression.ChildBoundNodes.LastOrDefault(); + continue; + case BoundKind.Conversion: node = ((BoundConversion)node).Operand; continue; diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs index b3a06862247d4..80037ed4706ba 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs @@ -9,6 +9,7 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Text; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -1437,7 +1438,7 @@ private void VisitReceiverAfterCall(BoundExpression receiverOpt, MethodSymbol me } else if (method.TryGetThisParameter(out var thisParameter) && thisParameter is object - && !TypeIsImmutable(thisParameter.Type)) + && !thisParameter.Type.TypeIsImmutable()) { var thisRefKind = thisParameter.RefKind; if (thisRefKind.IsWritableReference()) @@ -1447,36 +1448,6 @@ private void VisitReceiverAfterCall(BoundExpression receiverOpt, MethodSymbol me } } - /// - /// Certain (struct) types are known by the compiler to be immutable. In these cases calling a method on - /// the type is known (by flow analysis) not to write the receiver. - /// - /// - /// - private static bool TypeIsImmutable(TypeSymbol t) - { - switch (t.SpecialType) - { - case SpecialType.System_Boolean: - case SpecialType.System_Char: - case SpecialType.System_SByte: - case SpecialType.System_Byte: - case SpecialType.System_Int16: - case SpecialType.System_UInt16: - case SpecialType.System_Int32: - case SpecialType.System_UInt32: - case SpecialType.System_Int64: - case SpecialType.System_UInt64: - case SpecialType.System_Decimal: - case SpecialType.System_Single: - case SpecialType.System_Double: - case SpecialType.System_DateTime: - return true; - default: - return t.IsNullableType(); - } - } - public override BoundNode VisitIndexerAccess(BoundIndexerAccess node) { var method = GetReadMethod(node.Indexer); @@ -1581,9 +1552,38 @@ protected virtual void WriteArgument(BoundExpression arg, RefKind refKind, Metho public override BoundNode VisitBadExpression(BoundBadExpression node) { - foreach (var child in node.ChildBoundNodes) + for (int i = 0; i < node.ChildBoundNodes.Length; i++) { - VisitRvalue(child as BoundExpression); + if (i == 0 && node.Symbols.Length != 0 && node.Symbols.All(s => s is MethodSymbol { MethodKind: MethodKind.Constructor, ContainingType.TypeKind: not TypeKind.Delegate })) + { + continue; + } + + var child = node.ChildBoundNodes[i] as BoundExpression; + RefKind refKind = GetRefKind(node.ArgumentRefKindsOpt, i); + if (refKind != RefKind.Out) + { + VisitRvalue(child, isKnownToBeAnLvalue: refKind != RefKind.None); + } + else + { + VisitLvalue(child); + } + } + + if (!node.ArgumentRefKindsOpt.IsDefault) + { + for (int i = 0; i < node.ChildBoundNodes.Length; i++) + { + RefKind refKind = GetRefKind(node.ArgumentRefKindsOpt, i); + switch (refKind) + { + case RefKind.Ref: + case RefKind.Out: + WriteArgument(node.ChildBoundNodes[i] as BoundExpression, refKind, node.Symbols is [MethodSymbol method] ? method : null); + break; + } + } } return null; diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/AlwaysAssignedWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/AlwaysAssignedWalker.cs index c562970d33c20..54ec38c2b07c4 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/AlwaysAssignedWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/AlwaysAssignedWalker.cs @@ -70,7 +70,8 @@ private List Analyze(ref bool badRegion) return result; } - protected override void WriteArgument(BoundExpression arg, RefKind refKind, MethodSymbol method) +#nullable enable + protected override void WriteArgument(BoundExpression arg, RefKind refKind, MethodSymbol? method) { // ref parameter does not "always" assign. if (refKind == RefKind.Out) @@ -78,6 +79,7 @@ protected override void WriteArgument(BoundExpression arg, RefKind refKind, Meth Assign(arg, value: null); } } +#nullable disable protected override void ResolveBranch(PendingBranch pending, LabelSymbol label, BoundStatement target, ref bool labelStateChanged) { diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.cs index e6e11d2093ab0..0db260ebf02b9 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.cs @@ -2424,6 +2424,19 @@ public override BoundNode VisitMethodGroup(BoundMethodGroup node) return base.VisitMethodGroup(node); } + public override BoundNode VisitBadExpression(BoundBadExpression node) + { + foreach (var symbol in node.Symbols) + { + if (symbol is LocalFunctionSymbol localFunction) + { + _usedLocalFunctions.Add(localFunction); + } + } + + return base.VisitBadExpression(node); + } + public override BoundNode VisitLambda(BoundLambda node) { var oldSymbol = this.CurrentSymbol; @@ -2569,7 +2582,7 @@ public override BoundNode VisitAddressOfOperator(BoundAddressOfOperator node) } #nullable enable - protected override void WriteArgument(BoundExpression arg, RefKind refKind, MethodSymbol method) + protected override void WriteArgument(BoundExpression arg, RefKind refKind, MethodSymbol? method) { if (refKind == RefKind.Ref) { @@ -2585,7 +2598,7 @@ protected override void WriteArgument(BoundExpression arg, RefKind refKind, Meth // we assume that external method may write and/or read all of its fields (recursively). // Strangely, the native compiler requires the "ref", even for reference types, to exhibit // this behavior. - if (refKind != RefKind.None && ((object)method == null || method.IsExtern) && arg.Type is TypeSymbol type) + if (refKind != RefKind.None && ((object?)method == null || method.IsExtern) && arg.Type is TypeSymbol type) { MarkFieldsUsed(type); } diff --git a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs index 9281b156bf687..7f5cfb1e311f8 100644 --- a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs @@ -958,7 +958,7 @@ public BoundPassByCopy Update(BoundExpression expression, TypeSymbol? type) internal sealed partial class BoundBadExpression : BoundExpression { - public BoundBadExpression(SyntaxNode syntax, LookupResultKind resultKind, ImmutableArray symbols, ImmutableArray childBoundNodes, TypeSymbol? type, bool hasErrors = false) + public BoundBadExpression(SyntaxNode syntax, LookupResultKind resultKind, ImmutableArray symbols, ImmutableArray childBoundNodes, ImmutableArray argumentRefKindsOpt, TypeSymbol? type, bool hasErrors = false) : base(BoundKind.BadExpression, syntax, type, hasErrors || childBoundNodes.HasErrors()) { @@ -968,20 +968,22 @@ public BoundBadExpression(SyntaxNode syntax, LookupResultKind resultKind, Immuta this.ResultKind = resultKind; this.Symbols = symbols; this.ChildBoundNodes = childBoundNodes; + this.ArgumentRefKindsOpt = argumentRefKindsOpt; } public override LookupResultKind ResultKind { get; } public ImmutableArray Symbols { get; } public ImmutableArray ChildBoundNodes { get; } + public ImmutableArray ArgumentRefKindsOpt { get; } [DebuggerStepThrough] public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitBadExpression(this); - public BoundBadExpression Update(LookupResultKind resultKind, ImmutableArray symbols, ImmutableArray childBoundNodes, TypeSymbol? type) + public BoundBadExpression Update(LookupResultKind resultKind, ImmutableArray symbols, ImmutableArray childBoundNodes, ImmutableArray argumentRefKindsOpt, TypeSymbol? type) { - if (resultKind != this.ResultKind || symbols != this.Symbols || childBoundNodes != this.ChildBoundNodes || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) + if (resultKind != this.ResultKind || symbols != this.Symbols || childBoundNodes != this.ChildBoundNodes || argumentRefKindsOpt != this.ArgumentRefKindsOpt || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) { - var result = new BoundBadExpression(this.Syntax, resultKind, symbols, childBoundNodes, type, this.HasErrors); + var result = new BoundBadExpression(this.Syntax, resultKind, symbols, childBoundNodes, argumentRefKindsOpt, type, this.HasErrors); result.CopyAttributes(this); return result; } @@ -10943,7 +10945,7 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor ImmutableArray symbols = this.VisitSymbols(node.Symbols); ImmutableArray childBoundNodes = this.VisitList(node.ChildBoundNodes); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(node.ResultKind, symbols, childBoundNodes, type); + return node.Update(node.ResultKind, symbols, childBoundNodes, node.ArgumentRefKindsOpt, type); } public override BoundNode? VisitBadStatement(BoundBadStatement node) { @@ -12641,12 +12643,12 @@ public NullabilityRewriter(ImmutableDictionary + /// Certain (struct) types are known by the compiler to be immutable. In these cases calling a method on + /// the type is known (by flow analysis) not to write the receiver. + /// + /// + /// + internal static bool TypeIsImmutable(this TypeSymbol t) + { + switch (t.SpecialType) + { + case SpecialType.System_Boolean: + case SpecialType.System_Char: + case SpecialType.System_SByte: + case SpecialType.System_Byte: + case SpecialType.System_Int16: + case SpecialType.System_UInt16: + case SpecialType.System_Int32: + case SpecialType.System_UInt32: + case SpecialType.System_Int64: + case SpecialType.System_UInt64: + case SpecialType.System_Decimal: + case SpecialType.System_Single: + case SpecialType.System_Double: + case SpecialType.System_DateTime: + return true; + default: + return t.IsNullableType(); + } + } } }