diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index e8151a8c999a5..0690f4d67dd11 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -3220,7 +3220,7 @@ private BoundExpression BindArrayDimension(ExpressionSyntax dimension, BindingDi var size = BindValue(dimension, diagnostics, BindValueKind.RValue); if (!size.HasAnyErrors) { - size = ConvertToArrayIndex(size, diagnostics, allowIndexAndRange: false); + size = ConvertToArrayIndex(size, diagnostics, allowIndexAndRange: false, indexOrRangeWellknownType: out _); if (IsNegativeConstantForArraySize(size)) { Error(diagnostics, ErrorCode.ERR_NegativeArraySize, dimension); @@ -7517,7 +7517,7 @@ private BoundExpression BindArrayAccess(SyntaxNode node, BoundExpression expr, A Error(diagnostics, ErrorCode.ERR_NamedArgumentForArray, node); } - bool hasErrors = ReportRefOrOutArgument(arguments, diagnostics); + ReportRefOrOutArgument(arguments, diagnostics); var arrayType = (ArrayTypeSymbol)expr.Type; // Note that the spec says to determine which of {int, uint, long, ulong} *each* index @@ -7535,11 +7535,12 @@ private BoundExpression BindArrayAccess(SyntaxNode node, BoundExpression expr, A // Convert all the arguments to the array index type. BoundExpression[] convertedArguments = new BoundExpression[arguments.Arguments.Count]; + WellKnownType indexOrRangeWellknownType = WellKnownType.Unknown; for (int i = 0; i < arguments.Arguments.Count; ++i) { BoundExpression argument = arguments.Arguments[i]; - BoundExpression index = ConvertToArrayIndex(argument, diagnostics, allowIndexAndRange: rank == 1); + BoundExpression index = ConvertToArrayIndex(argument, diagnostics, allowIndexAndRange: rank == 1, out indexOrRangeWellknownType); convertedArguments[i] = index; // NOTE: Dev10 only warns if rank == 1 @@ -7555,23 +7556,37 @@ private BoundExpression BindArrayAccess(SyntaxNode node, BoundExpression expr, A } } - TypeSymbol resultType = rank == 1 && - TypeSymbol.Equals( - convertedArguments[0].Type, - Compilation.GetWellKnownType(WellKnownType.System_Range), - TypeCompareKind.ConsiderEverything) + TypeSymbol resultType = indexOrRangeWellknownType == WellKnownType.System_Range ? arrayType : arrayType.ElementType; - return hasErrors - ? new BoundArrayAccess(node, BindToTypeForErrorRecovery(expr), convertedArguments.Select(e => BindToTypeForErrorRecovery(e)).AsImmutableOrNull(), resultType, hasErrors: true) - : new BoundArrayAccess(node, expr, convertedArguments.AsImmutableOrNull(), resultType, hasErrors: false); + if (indexOrRangeWellknownType == WellKnownType.System_Index) + { + Debug.Assert(convertedArguments.Length == 1); + + var int32 = GetSpecialType(SpecialType.System_Int32, diagnostics, node); + var receiverPlaceholder = new BoundImplicitIndexerReceiverPlaceholder(expr.Syntax, GetValEscape(expr, LocalScopeDepth), expr.Type) { WasCompilerGenerated = true }; + var argumentPlaceholders = ImmutableArray.Create(new BoundImplicitIndexerValuePlaceholder(convertedArguments[0].Syntax, int32) { WasCompilerGenerated = true }); + + return new BoundImplicitIndexerAccess( + node, + argument: convertedArguments[0], + lengthOrCountAccess: new BoundArrayLength(node, receiverPlaceholder, int32) { WasCompilerGenerated = true }, + receiverPlaceholder, + indexerOrSliceAccess: new BoundArrayAccess(node, expr, ImmutableArray.CastUp(argumentPlaceholders), resultType), + argumentPlaceholders, + resultType); + } + + return new BoundArrayAccess(node, expr, convertedArguments.AsImmutableOrNull(), resultType); } - private BoundExpression ConvertToArrayIndex(BoundExpression index, BindingDiagnosticBag diagnostics, bool allowIndexAndRange) + private BoundExpression ConvertToArrayIndex(BoundExpression index, BindingDiagnosticBag diagnostics, bool allowIndexAndRange, out WellKnownType indexOrRangeWellknownType) { Debug.Assert(index != null); + indexOrRangeWellknownType = WellKnownType.Unknown; + if (index.Kind == BoundKind.OutVariablePendingInference) { return ((OutVariablePendingInference)index).FailInference(this, diagnostics); @@ -7597,6 +7612,7 @@ private BoundExpression ConvertToArrayIndex(BoundExpression index, BindingDiagno result = TryImplicitConversionToArrayIndex(index, WellKnownType.System_Range, node, diagnostics); if (result is object) { + indexOrRangeWellknownType = WellKnownType.System_Range; // This member is needed for lowering and should produce an error if not present _ = GetWellKnownTypeMember( WellKnownMember.System_Runtime_CompilerServices_RuntimeHelpers__GetSubArray_T, @@ -7606,6 +7622,8 @@ private BoundExpression ConvertToArrayIndex(BoundExpression index, BindingDiagno } else { + indexOrRangeWellknownType = WellKnownType.System_Index; + // This member is needed for lowering and should produce an error if not present _ = GetWellKnownTypeMember( WellKnownMember.System_Index__GetOffset, @@ -7733,7 +7751,7 @@ private BoundExpression BindPointerElementAccess(SyntaxNode node, BoundExpressio BoundExpression index = arguments[0]; - index = ConvertToArrayIndex(index, diagnostics, allowIndexAndRange: false); + index = ConvertToArrayIndex(index, diagnostics, allowIndexAndRange: false, indexOrRangeWellknownType: out _); return new BoundPointerElementAccess(node, expr, index, CheckOverflowAtRuntime, refersToLocation: false, pointedAtType, hasErrors); } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index fa52da51cd7b7..b7cbdca662298 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -1610,7 +1610,9 @@ internal static PropertySymbol GetPropertySymbol(BoundExpression expr, out Bound BoundImplicitIndexerAccess { IndexerOrSliceAccess: BoundCall call } => call.Method, // this[int] BoundImplicitIndexerAccess { IndexerOrSliceAccess: BoundIndexerAccess indexerAccess } => indexerAccess.Indexer, - // array[int] + // array[Index] + BoundImplicitIndexerAccess { IndexerOrSliceAccess: BoundArrayAccess } => null, + // array[int or Range] BoundArrayAccess => null, BoundDynamicIndexerAccess => null, BoundBadExpression => null, diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundArrayAccess.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundArrayAccess.cs new file mode 100644 index 0000000000000..8f3678ab17596 --- /dev/null +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundArrayAccess.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.CodeAnalysis.CSharp +{ + internal partial class BoundArrayAccess + { + internal BoundArrayAccess WithReceiver(BoundExpression receiver) + { + return this.Update(receiver, this.Indices, this.Type); + } + } +} diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundDagEvaluation.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundDagEvaluation.cs index a20ecdb9d9722..9f668fee486bc 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundDagEvaluation.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundDagEvaluation.cs @@ -39,14 +39,29 @@ private Symbol? Symbol BoundDagTypeEvaluation e => e.Type, BoundDagDeconstructEvaluation e => e.DeconstructMethod, BoundDagIndexEvaluation e => e.Property, - BoundDagSliceEvaluation e => e.IndexerAccess is BoundArrayAccess arrayAccess ? arrayAccess.Expression.Type : Binder.GetIndexerOrImplicitIndexerSymbol(e.IndexerAccess), - BoundDagIndexerEvaluation e => e.IndexerAccess is BoundArrayAccess arrayAccess ? arrayAccess.Expression.Type : Binder.GetIndexerOrImplicitIndexerSymbol(e.IndexerAccess), + BoundDagSliceEvaluation e => getSymbolFromIndexerAccess(e.IndexerAccess), + BoundDagIndexerEvaluation e => getSymbolFromIndexerAccess(e.IndexerAccess), BoundDagAssignmentEvaluation => null, _ => throw ExceptionUtilities.UnexpectedValue(this.Kind) }; Debug.Assert(result is not null || this is BoundDagAssignmentEvaluation); return result; + + static Symbol? getSymbolFromIndexerAccess(BoundExpression indexerAccess) + { + switch (indexerAccess) + { + case BoundArrayAccess arrayAccess: + return arrayAccess.Expression.Type; + + case BoundImplicitIndexerAccess { IndexerOrSliceAccess: BoundArrayAccess arrayAccess }: + return arrayAccess.Expression.Type; + + default: + return Binder.GetIndexerOrImplicitIndexerSymbol(indexerAccess); + } + } } } diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundIndexOrRangePatternIndexerAccess.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundIndexOrRangePatternIndexerAccess.cs index 8c962909e29af..834e4084594b8 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundIndexOrRangePatternIndexerAccess.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundIndexOrRangePatternIndexerAccess.cs @@ -23,6 +23,7 @@ internal BoundExpression GetReceiver() { BoundIndexerAccess { ReceiverOpt: var r } => r, BoundCall { ReceiverOpt: var r } => r, + BoundArrayAccess { Expression: var r } => r, _ => throw ExceptionUtilities.UnexpectedValue(this.IndexerOrSliceAccess.Kind) }; diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml index 3fa57b3b32270..1b22ecb5f2fdd 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml @@ -2078,7 +2078,7 @@