From 025f3981bae6a2fe54ecd6fd80dbfc48893c2b6f Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Tue, 19 Oct 2021 18:25:19 -0700 Subject: [PATCH 01/29] Address some PROTOTYPE markers --- .../Portable/Binder/Binder.ValueChecks.cs | 38 +- .../Portable/Binder/Binder_Expressions.cs | 95 ++- .../CSharp/Portable/Binder/Binder_Patterns.cs | 74 +- .../Portable/Binder/Binder_Statements.cs | 4 +- .../CSharp/Portable/BoundTree/BoundNodes.xml | 6 +- .../CSharp/Portable/BoundTree/Expression.cs | 2 +- .../Compilation/CSharpSemanticModel.cs | 6 +- .../CSharp/Portable/Errors/ErrorCode.cs | 11 +- .../CSharp/Portable/Errors/MessageID.cs | 12 +- .../Portable/FlowAnalysis/AbstractFlowPass.cs | 10 +- .../Portable/FlowAnalysis/NullableWalker.cs | 2 +- .../FlowAnalysis/NullableWalker_Patterns.cs | 4 +- .../Generated/BoundNodes.xml.Generated.cs | 68 +- .../DiagnosticsPass_ExpressionTrees.cs | 4 +- .../Lowering/LocalRewriter/LocalRewriter.cs | 6 +- .../LocalRewriter_AssignmentOperator.cs | 6 +- ...ocalRewriter_CompoundAssignmentOperator.cs | 16 +- .../LocalRewriter_IndexerAccess.cs | 16 +- .../Operations/CSharpOperationFactory.cs | 2 +- .../Parser/LanguageParser_Patterns.cs | 3 +- .../CSharp/Portable/Syntax/SyntaxKind.cs | 2 +- .../AttributeTests_WellKnownAttributes.cs | 7 +- .../Test/Emit/CodeGen/IndexAndRangeTests.cs | 37 + .../PatternMatchingTests_ListPatterns.cs | 740 ++++++++++++++++-- .../PatternParsingTests_ListPatterns.cs | 117 +-- 25 files changed, 976 insertions(+), 312 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index 3bfd6cddddca..d9cf7a1a4f78 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -398,16 +398,16 @@ internal bool CheckValueKind(SyntaxNode node, BoundExpression expr, BindValueKin case BoundKind.IndexerAccess: return CheckPropertyValueKind(node, expr, valueKind, checkingReceiver, diagnostics); - case BoundKind.IndexOrRangePatternIndexerAccess: - var patternIndexer = ((BoundIndexOrRangePatternIndexerAccess)expr); - if (patternIndexer.PatternSymbol.Kind == SymbolKind.Property) + case BoundKind.IndexOrRangeIndexerFallbackAccess: + var fallbackIndexer = (BoundIndexOrRangeIndexerFallbackAccess)expr; + if (fallbackIndexer.PatternSymbol.Kind == SymbolKind.Property) { // If this is an Index indexer, PatternSymbol should be a property, pointing to the - // pattern indexer. If it's a Range access, it will be a method, pointing to a Slice method + // fallback indexer. If it's a Range access, it will be a method, pointing to a Slice method // and it's handled below as part of invocations. return CheckPropertyValueKind(node, expr, valueKind, checkingReceiver, diagnostics); } - Debug.Assert(patternIndexer.PatternSymbol.Kind == SymbolKind.Method); + Debug.Assert(fallbackIndexer.PatternSymbol.Kind == SymbolKind.Method); break; case BoundKind.EventAccess: @@ -589,9 +589,9 @@ internal bool CheckValueKind(SyntaxNode node, BoundExpression expr, BindValueKin checkingReceiver, diagnostics); - case BoundKind.IndexOrRangePatternIndexerAccess: - var patternIndexer = (BoundIndexOrRangePatternIndexerAccess)expr; - // If we got here this should be a pattern indexer taking a Range, + case BoundKind.IndexOrRangeIndexerFallbackAccess: + var patternIndexer = (BoundIndexOrRangeIndexerFallbackAccess)expr; + // If we got here this should be a fallback indexer taking a Range, // meaning that the pattern symbol must be a method (either Slice or Substring) return CheckMethodReturnValueKind( (MethodSymbol)patternIndexer.PatternSymbol, @@ -2347,12 +2347,12 @@ internal static bool CheckRefEscape(SyntaxNode node, BoundExpression expr, uint diagnostics, isRefEscape: true); - case BoundKind.IndexOrRangePatternIndexerAccess: - var patternIndexer = (BoundIndexOrRangePatternIndexerAccess)expr; + case BoundKind.IndexOrRangeIndexerFallbackAccess: + var fallbackIndexer = (BoundIndexOrRangeIndexerFallbackAccess)expr; RefKind refKind; ImmutableArray parameters; - switch (patternIndexer.PatternSymbol) + switch (fallbackIndexer.PatternSymbol) { case PropertySymbol p: refKind = p.RefKind; @@ -2372,11 +2372,11 @@ internal static bool CheckRefEscape(SyntaxNode node, BoundExpression expr, uint } return CheckInvocationEscape( - patternIndexer.Syntax, - patternIndexer.PatternSymbol, - patternIndexer.Receiver, + fallbackIndexer.Syntax, + fallbackIndexer.PatternSymbol, + fallbackIndexer.Receiver, parameters, - ImmutableArray.Create(patternIndexer.Argument), + ImmutableArray.Create(fallbackIndexer.Argument), default, default, checkingReceiver, @@ -2618,8 +2618,8 @@ internal static uint GetValEscape(BoundExpression expr, uint scopeOfTheContainin scopeOfTheContainingExpression, isRefEscape: false); - case BoundKind.IndexOrRangePatternIndexerAccess: - var patternIndexer = (BoundIndexOrRangePatternIndexerAccess)expr; + case BoundKind.IndexOrRangeIndexerFallbackAccess: + var patternIndexer = (BoundIndexOrRangeIndexerFallbackAccess)expr; var parameters = patternIndexer.PatternSymbol switch { PropertySymbol p => p.Parameters, @@ -3040,8 +3040,8 @@ internal static bool CheckValEscape(SyntaxNode node, BoundExpression expr, uint diagnostics, isRefEscape: false); - case BoundKind.IndexOrRangePatternIndexerAccess: - var patternIndexer = (BoundIndexOrRangePatternIndexerAccess)expr; + case BoundKind.IndexOrRangeIndexerFallbackAccess: + var patternIndexer = (BoundIndexOrRangeIndexerFallbackAccess)expr; var patternSymbol = patternIndexer.PatternSymbol; var parameters = patternSymbol switch { diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index 68aaf368eedb..f1d31cd0c344 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -7736,29 +7736,30 @@ private BoundExpression BindIndexerAccess(ExpressionSyntax node, BoundExpression LookupOptions lookupOptions = expr.Kind == BoundKind.BaseReference ? LookupOptions.UseBaseReferenceAccessibility : LookupOptions.Default; CompoundUseSiteInfo useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); this.LookupMembersWithFallback(lookupResult, expr.Type, WellKnownMemberNames.Indexer, arity: 0, useSiteInfo: ref useSiteInfo, options: lookupOptions); - diagnostics.Add(node, useSiteInfo); // Store, rather than return, so that we can release resources. BoundExpression indexerAccessExpression; if (!lookupResult.IsMultiViable) { - if (TryBindIndexOrRangeIndexer( + if (TryBindIndexOrRangeIndexerFallback( node, expr, analyzedArguments, diagnostics, - out var patternIndexerAccess)) + out var indexerFallbackAccess)) { - indexerAccessExpression = patternIndexerAccess; + indexerAccessExpression = indexerFallbackAccess; } else { + diagnostics.Add(node, useSiteInfo); indexerAccessExpression = BadIndexerExpression(node, expr, analyzedArguments, lookupResult.Error, diagnostics); } } else { + diagnostics.Add(node, useSiteInfo); ArrayBuilder indexerGroup = ArrayBuilder.GetInstance(); foreach (Symbol symbol in lookupResult.Symbols) { @@ -7889,7 +7890,6 @@ private BoundExpression BindIndexerOrIndexedPropertyAccess( bool allowRefOmittedArguments = receiverOpt.IsExpressionOfComImportType(); CompoundUseSiteInfo useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); this.OverloadResolution.PropertyOverloadResolution(propertyGroup, receiverOpt, analyzedArguments, overloadResolutionResult, allowRefOmittedArguments, ref useSiteInfo); - diagnostics.Add(syntax, useSiteInfo); BoundExpression propertyAccess; if (analyzedArguments.HasDynamicArgument && overloadResolutionResult.HasAnyApplicableMember) @@ -7914,14 +7914,14 @@ private BoundExpression BindIndexerOrIndexedPropertyAccess( if (!analyzedArguments.HasErrors) { - if (TryBindIndexOrRangeIndexer( + if (TryBindIndexOrRangeIndexerFallback( syntax, receiverOpt, analyzedArguments, diagnostics, - out var patternIndexerAccess)) + out var indexerFallbackAccess)) { - return patternIndexerAccess; + return indexerFallbackAccess; } else { @@ -7941,6 +7941,8 @@ private BoundExpression BindIndexerOrIndexedPropertyAccess( memberGroup: candidates, typeContainingConstructor: null, delegateTypeBeingInvoked: null); + + diagnostics.Add(syntax, useSiteInfo); } } @@ -7961,6 +7963,8 @@ private BoundExpression BindIndexerOrIndexedPropertyAccess( } else { + diagnostics.Add(syntax, useSiteInfo); + MemberResolutionResult resolutionResult = overloadResolutionResult.ValidResult; PropertySymbol property = resolutionResult.Member; RefKind? receiverRefKind = receiverOpt?.GetRefKind(); @@ -8020,14 +8024,14 @@ private BoundExpression BindIndexerOrIndexedPropertyAccess( } #nullable enable - private bool TryBindIndexOrRangeIndexer( + private bool TryBindIndexOrRangeIndexerFallback( SyntaxNode syntax, BoundExpression? receiverOpt, AnalyzedArguments arguments, BindingDiagnosticBag diagnostics, - [NotNullWhen(true)] out BoundIndexOrRangePatternIndexerAccess? patternIndexerAccess) + [NotNullWhen(true)] out BoundIndexOrRangeIndexerFallbackAccess? indexerFallbackAccess) { - patternIndexerAccess = null; + indexerFallbackAccess = null; // Verify a few things up-front, namely that we have a single argument // to this indexer that has an Index or Range type and that there is @@ -8053,14 +8057,13 @@ private bool TryBindIndexOrRangeIndexer( } bool argIsIndex = argIsIndexNotRange.Value(); - var useSiteInfo = CompoundUseSiteInfo.Discarded; - if (!TryFindIndexOrRangeIndexerPattern(receiverOpt, receiverType, argIsIndex: argIsIndex, - out PropertySymbol? lengthOrCountProperty, out Symbol? patternSymbol, diagnostics, ref useSiteInfo)) + if (!TryFindIndexOrRangeIndexerFallback(syntax, receiverOpt, receiverType, argIsIndex: argIsIndex, + out PropertySymbol? lengthOrCountProperty, out Symbol? patternSymbol, diagnostics)) { return false; } - patternIndexerAccess = new BoundIndexOrRangePatternIndexerAccess( + indexerFallbackAccess = new BoundIndexOrRangeIndexerFallbackAccess( syntax, receiverOpt, lengthOrCountProperty, @@ -8068,9 +8071,6 @@ private bool TryBindIndexOrRangeIndexer( BindToNaturalType(argument, diagnostics), patternSymbol.GetTypeOrReturnType().Type); - ReportDiagnosticsIfObsolete(diagnostics, patternSymbol, syntax, hasBaseReceiver: false); - ReportDiagnosticsIfObsolete(diagnostics, lengthOrCountProperty, syntax, hasBaseReceiver: false); - if (!argIsIndex) { checkWellKnown(WellKnownMember.System_Range__get_Start); @@ -8098,15 +8098,17 @@ void checkWellKnown(WellKnownMember member) } } - private bool TryFindIndexOrRangeIndexerPattern( + /// + /// Finds pattern-based fallback indexer and Length/Count property. + /// + private bool TryFindIndexOrRangeIndexerFallback( + SyntaxNode syntax, BoundExpression? receiverOpt, TypeSymbol receiverType, bool argIsIndex, [NotNullWhen(true)] out PropertySymbol? lengthOrCountProperty, [NotNullWhen(true)] out Symbol? patternSymbol, - // PROTOTYPE(list-patterns) We should take either of these and adjust the caller. - BindingDiagnosticBag diagnostics, - ref CompoundUseSiteInfo useSiteInfo) + BindingDiagnosticBag diagnostics) { // SPEC: @@ -8120,10 +8122,12 @@ private bool TryFindIndexOrRangeIndexerPattern( var lookupResult = LookupResult.GetInstance(); - if (TryLookupLengthOrCount(receiverType, lookupResult, out lengthOrCountProperty, ref useSiteInfo) && - TryFindIndexOrRangeIndexerPattern(lookupResult, receiverOpt, receiverType, argIsIndex, out patternSymbol, diagnostics, ref useSiteInfo)) + if (TryLookupLengthOrCount(syntax, receiverType, lookupResult, out lengthOrCountProperty, diagnostics) && + TryFindIndexOrRangeIndexerFallback(syntax, lookupResult, receiverOpt, receiverType, argIsIndex, out patternSymbol, diagnostics)) { - CheckImplicitThisCopyInReadOnlyMember(receiverOpt, lengthOrCountProperty.GetMethod, diagnostics); + var lengthAccess = new BoundPropertyAccess(syntax, receiverOpt, lengthOrCountProperty, LookupResultKind.Viable, lengthOrCountProperty.Type); + CheckPropertyValueKind(syntax, lengthAccess, BindValueKind.RValue, checkingReceiver: false, diagnostics); + lookupResult.Free(); return true; } @@ -8133,16 +8137,22 @@ private bool TryFindIndexOrRangeIndexerPattern( return false; } - private bool TryFindIndexOrRangeIndexerPattern( + /// + /// Finds pattern-based fallback indexer: + /// - for Index indexer, this will find `this[int]`. + /// - for Range indexer, this will find `Slice(int, int)` or `string.Substring(int, int)`. + /// + private bool TryFindIndexOrRangeIndexerFallback( + SyntaxNode syntax, LookupResult lookupResult, BoundExpression? receiverOpt, TypeSymbol receiverType, bool argIsIndex, [NotNullWhen(true)] out Symbol? patternSymbol, - // PROTOTYPE(list-patterns) We should take either of these and adjust the caller. - BindingDiagnosticBag diagnostics, - ref CompoundUseSiteInfo useSiteInfo) + BindingDiagnosticBag diagnostics) { + var useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); + Debug.Assert(lookupResult.IsClear); if (argIsIndex) { @@ -8171,6 +8181,9 @@ candidate is PropertySymbol property && { // note: implicit copy check on the indexer accessor happens in CheckPropertyValueKind patternSymbol = property; + property.AddUseSiteInfo(ref useSiteInfo); + diagnostics.Add(syntax, useSiteInfo); + ReportDiagnosticsIfObsolete(diagnostics, property, syntax, hasBaseReceiver: false); return true; } } @@ -8181,7 +8194,6 @@ candidate is PropertySymbol property && Debug.Assert(!argIsIndex); // Look for Substring var substring = (MethodSymbol)Compilation.GetSpecialTypeMember(SpecialMember.System_String__Substring); - // PROTOTYPE(list-patterns) Consider reporting a missing member for string.Substring if (substring is object) { patternSymbol = substring; @@ -8218,6 +8230,9 @@ method.OriginalDefinition is var original && original.Parameters[1] is { Type: { SpecialType: SpecialType.System_Int32 }, RefKind: RefKind.None }) { patternSymbol = method; + method.AddUseSiteInfo(ref useSiteInfo); + diagnostics.Add(syntax, useSiteInfo); + ReportDiagnosticsIfObsolete(diagnostics, method, syntax, hasBaseReceiver: false); CheckImplicitThisCopyInReadOnlyMember(receiverOpt, method, diagnostics); return true; } @@ -8230,18 +8245,25 @@ method.OriginalDefinition is var original && } private bool TryLookupLengthOrCount( + SyntaxNode syntax, TypeSymbol receiverType, LookupResult lookupResult, [NotNullWhen(true)] out PropertySymbol? lengthOrCountProperty, - ref CompoundUseSiteInfo useSiteInfo) + BindingDiagnosticBag diagnostics) { Debug.Assert(lookupResult.IsClear); - // PROTOTYPE(list-patterns) Assert about whether diagnostics and dependencies are being accumulated. - return tryLookupLengthOrCount(WellKnownMemberNames.LengthPropertyName, out lengthOrCountProperty, ref useSiteInfo) || - tryLookupLengthOrCount(WellKnownMemberNames.CountPropertyName, out lengthOrCountProperty, ref useSiteInfo); + if (tryLookupLengthOrCount(WellKnownMemberNames.LengthPropertyName, out lengthOrCountProperty, diagnostics) || + tryLookupLengthOrCount(WellKnownMemberNames.CountPropertyName, out lengthOrCountProperty, diagnostics)) + { + ReportDiagnosticsIfObsolete(diagnostics, lengthOrCountProperty, syntax, hasBaseReceiver: false); + return true; + } - bool tryLookupLengthOrCount(string propertyName, out PropertySymbol? valid, ref CompoundUseSiteInfo useSiteInfo) + return false; + + bool tryLookupLengthOrCount(string propertyName, [NotNullWhen(true)] out PropertySymbol? valid, BindingDiagnosticBag diagnostics) { + var useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); LookupMembersInType( lookupResult, receiverType, @@ -8263,8 +8285,11 @@ lookupResult.Symbols[0] is PropertySymbol property && { lookupResult.Clear(); valid = property; + property.AddUseSiteInfo(ref useSiteInfo); + diagnostics.Add(syntax, useSiteInfo); return true; } + lookupResult.Clear(); valid = null; return false; diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs index cd669247a70e..94ddeab5f62b 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs @@ -214,7 +214,7 @@ private BoundPattern BindSlicePattern( { sliceType = inputType; } - else if (TryPerformPatternIndexerLookup(node, inputType, argIsIndex: false, out indexerAccess, out Symbol? patternSymbol, lengthProperty: out _, diagnostics)) + else if (TryBindIndexerForPattern(node, inputType, argIsIndex: false, out indexerAccess, out Symbol? patternSymbol, lengthProperty: out _, diagnostics, ref hasErrors)) { if (patternSymbol is MethodSymbol method) { @@ -280,6 +280,8 @@ private BoundListPattern BindListPattern( bool hasErrors, BindingDiagnosticBag diagnostics) { + CheckFeatureAvailability(node, MessageID.IDS_FeatureListPattern, diagnostics); + TypeSymbol elementType; BoundIndexerAccess? indexerAccess = null; PropertySymbol? indexerSymbol = null; @@ -295,7 +297,7 @@ private BoundListPattern BindListPattern( elementType = ((ArrayTypeSymbol)inputType).ElementType; hasErrors |= !TryGetSpecialTypeMember(Compilation, SpecialMember.System_Array__Length, node, diagnostics, out lengthProperty); } - else if (TryPerformPatternIndexerLookup(node, narrowedType, argIsIndex: true, out indexerAccess, out Symbol? patternSymbol, out lengthProperty, diagnostics)) + else if (TryBindIndexerForPattern(node, narrowedType, argIsIndex: true, out indexerAccess, out Symbol? patternSymbol, out lengthProperty, diagnostics, ref hasErrors)) { if (patternSymbol is PropertySymbol indexer) { @@ -331,20 +333,21 @@ private BoundListPattern BindListPattern( variableAccess: variableAccess, inputType: inputType, narrowedType: narrowedType, hasErrors); } - private bool TryPerformPatternIndexerLookup( + private bool TryBindIndexerForPattern( SyntaxNode syntax, TypeSymbol receiverType, bool argIsIndex, out BoundIndexerAccess? indexerAccess, out Symbol? patternSymbol, [NotNullWhen(true)] out PropertySymbol? lengthProperty, - BindingDiagnosticBag diagnostics) + BindingDiagnosticBag diagnostics, + ref bool hasErrors) { Debug.Assert(!receiverType.IsErrorType()); indexerAccess = null; patternSymbol = null; lengthProperty = null; - bool found; + bool found = false; CompoundUseSiteInfo useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); var bindingDiagnostics = BindingDiagnosticBag.GetInstance(diagnostics); TypeSymbol argType = Compilation.GetWellKnownType(argIsIndex ? WellKnownType.System_Index : WellKnownType.System_Range); @@ -373,31 +376,28 @@ private bool TryPerformPatternIndexerLookup( lookupResult.Clear(); var analyzedArguments = AnalyzedArguments.GetInstance(); - analyzedArguments.Arguments.Add(new BoundIndexOrRangeIndexerPatternValuePlaceholder(syntax, argType)); + analyzedArguments.Arguments.Add(new BoundIndexOrRangeIndexerFallbackValuePlaceholder(syntax, argType)); var receiver = new BoundImplicitReceiver(syntax, receiverType); BoundExpression boundAccess = BindIndexerOrIndexedPropertyAccess(syntax, receiver, indexerGroup, analyzedArguments, bindingDiagnostics); switch (boundAccess) { case BoundIndexerAccess boundIndexerAccess: if (boundIndexerAccess.ResultKind == LookupResultKind.Viable && - boundIndexerAccess.Indexer.GetMethod is { } getMethod && + boundIndexerAccess.Indexer.GetOwnOrInheritedGetMethod() is { } getMethod && IsAccessible(getMethod, ref useSiteInfo) && - TryLookupLengthOrCount(receiverType, lookupResult, out lengthProperty, ref useSiteInfo)) + TryLookupLengthOrCount(syntax, receiverType, lookupResult, out lengthProperty, bindingDiagnostics) && + !getMethod.IsStatic) { - // PROTOTYPE(list-patterns) Can this be ever true? If so, move to if above - Debug.Assert(!boundIndexerAccess.Indexer.IsStatic); - ReportDiagnosticsIfObsolete(bindingDiagnostics, lengthProperty, syntax, hasBaseReceiver: false); GetWellKnownTypeMember(argIsIndex ? WellKnownMember.System_Index__ctor : WellKnownMember.System_Range__ctor, bindingDiagnostics, syntax: syntax); indexerAccess = BindIndexerDefaultArguments(boundIndexerAccess, BindValueKind.RValue, bindingDiagnostics); found = true; break; } - found = false; break; - case BoundIndexOrRangePatternIndexerAccess boundIndexOrRangePatternIndexerAccess: - lengthProperty = boundIndexOrRangePatternIndexerAccess.LengthOrCountProperty; - patternSymbol = boundIndexOrRangePatternIndexerAccess.PatternSymbol; + case BoundIndexOrRangeIndexerFallbackAccess boundIndexOrRangeIndexerFallbackAccess: + lengthProperty = boundIndexOrRangeIndexerFallbackAccess.LengthOrCountProperty; + patternSymbol = boundIndexOrRangeIndexerFallbackAccess.PatternSymbol; found = true; break; @@ -406,26 +406,55 @@ boundIndexerAccess.Indexer.GetMethod is { } getMethod && } analyzedArguments.Free(); indexerGroup.Free(); - goto done; } lookupResult.Clear(); } + else + { + // If the argType is missing, we will fallback to the implicit indexer support. + found = TryLookupLengthOrCount(syntax, receiverType, lookupResult, out lengthProperty, diagnostics) && + TryFindIndexOrRangeIndexerFallback(syntax, lookupResult, receiverOpt: null, receiverType, argIsIndex, out patternSymbol, diagnostics); + } - // If the argType is missing or the indexer lookup has failed, we will fallback to the implicit indexer support. - found = TryLookupLengthOrCount(receiverType, lookupResult, out lengthProperty, ref useSiteInfo) && - TryFindIndexOrRangeIndexerPattern(lookupResult, receiverOpt: null, receiverType, argIsIndex, out patternSymbol, diagnostics, ref useSiteInfo); - -done: if (found) { Debug.Assert(indexerAccess is not null ^ patternSymbol is not null); Debug.Assert(lengthProperty is not null); + + if (!hasErrors) + { + if (patternSymbol is not null) + { + var indexerFallbackAccess = new BoundIndexOrRangeIndexerFallbackAccess(syntax, new BoundImplicitReceiver(syntax, receiverType), + lengthProperty, patternSymbol, argument: new BoundIndexOrRangeIndexerFallbackValuePlaceholder(syntax, argType), patternSymbol.GetTypeOrReturnType().Type); + + if (!CheckValueKind(syntax, indexerFallbackAccess, BindValueKind.RValue, checkingReceiver: false, bindingDiagnostics)) + { + hasErrors = true; + } + } + else + { + var lengthAccess = new BoundPropertyAccess(syntax, new BoundImplicitReceiver(syntax, receiverType), lengthProperty, LookupResultKind.Viable, lengthProperty.Type); + if (!CheckValueKind(syntax, lengthAccess, BindValueKind.RValue, checkingReceiver: false, bindingDiagnostics)) + { + hasErrors = true; + } + + if (indexerAccess is not null && !CheckValueKind(syntax, indexerAccess, BindValueKind.RValue, checkingReceiver: false, bindingDiagnostics)) + { + hasErrors = true; + } + } + } + // At this point we have succeeded to bind a viable indexer, // report additional binding diagnostics that we have seen so far diagnostics.AddRange(bindingDiagnostics); diagnostics.Add(syntax, useSiteInfo); } + bindingDiagnostics.Free(); lookupResult.Free(); return found; } @@ -1456,9 +1485,10 @@ private ImmutableArray BindPropertyPatternClause( TypeSymbol receiverType = member.Receiver?.Type ?? inputType; if (!receiverType.IsErrorType()) { + bool ignoredHasErrors = false; isLengthOrCount = receiverType.IsSZArray() ? ReferenceEquals(memberSymbol, Compilation.GetSpecialTypeMember(SpecialMember.System_Array__Length)) - : TryPerformPatternIndexerLookup(node, receiverType, argIsIndex: true, indexerAccess: out _, patternSymbol: out _, out PropertySymbol? lengthProperty, BindingDiagnosticBag.Discarded) && + : TryBindIndexerForPattern(node, receiverType, argIsIndex: true, indexerAccess: out _, patternSymbol: out _, out PropertySymbol? lengthProperty, BindingDiagnosticBag.Discarded, ref ignoredHasErrors) && memberSymbol.Equals(lengthProperty, TypeCompareKind.ConsiderEverything); // If Length and Count are both present, only the former is assumed to be non-negative. } } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index 7d8c6429aace..16cf50dbc607 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -1540,9 +1540,9 @@ private static PropertySymbol GetPropertySymbol(BoundExpression expr, out BoundE propertySymbol = indexerAccess.Indexer; } break; - case BoundKind.IndexOrRangePatternIndexerAccess: + case BoundKind.IndexOrRangeIndexerFallbackAccess: { - var patternIndexer = (BoundIndexOrRangePatternIndexerAccess)expr; + var patternIndexer = (BoundIndexOrRangeIndexerFallbackAccess)expr; receiver = patternIndexer.Receiver; propertySymbol = (PropertySymbol)patternIndexer.PatternSymbol; } diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml index 2440aa5cd5bc..7220abca3605 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml @@ -129,10 +129,10 @@ - + @@ -1989,7 +1989,7 @@ - + diff --git a/src/Compilers/CSharp/Portable/BoundTree/Expression.cs b/src/Compilers/CSharp/Portable/BoundTree/Expression.cs index ab4a498be4c8..7ac5366965dc 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/Expression.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/Expression.cs @@ -175,7 +175,7 @@ internal partial class BoundPassByCopy protected override ImmutableArray Children => ImmutableArray.Create(this.Expression); } - internal partial class BoundIndexOrRangePatternIndexerAccess + internal partial class BoundIndexOrRangeIndexerFallbackAccess { protected override ImmutableArray Children => ImmutableArray.Create(Receiver, Argument); } diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs index ee0c6656a11e..a44b33015c23 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs @@ -3427,13 +3427,13 @@ private ImmutableArray GetSemanticSymbols( } break; - case BoundKind.IndexOrRangePatternIndexerAccess: + case BoundKind.IndexOrRangeIndexerFallbackAccess: { - var indexerAccess = (BoundIndexOrRangePatternIndexerAccess)boundNode; + var indexerAccess = (BoundIndexOrRangeIndexerFallbackAccess)boundNode; resultKind = indexerAccess.ResultKind; - // The only time a BoundIndexOrRangePatternIndexerAccess is created, overload resolution succeeded + // The only time a BoundIndexOrRangeIndexerFallbackAccess is created, overload resolution succeeded // and returned only 1 result Debug.Assert(indexerAccess.PatternSymbol is object); symbols = ImmutableArray.Create(indexerAccess.PatternSymbol); diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 9dcc58632b1f..de0233405de3 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -2000,10 +2000,13 @@ internal enum ErrorCode WRN_MethGrpToNonDel = 8974, ERR_LambdaExplicitReturnTypeVar = 8975, - // PROTOTYPE(list-patterns) - ERR_UnsupportedTypeForListPattern = 9200, - ERR_UnsupportedTypeForSlicePattern, - ERR_MisplacedSlicePattern, + #endregion + + #region diagnostics introduced for C# 11.0 + + ERR_UnsupportedTypeForListPattern = 9000, + ERR_UnsupportedTypeForSlicePattern = 9001, + ERR_MisplacedSlicePattern = 9002, #endregion diff --git a/src/Compilers/CSharp/Portable/Errors/MessageID.cs b/src/Compilers/CSharp/Portable/Errors/MessageID.cs index f74fb040fff8..283384bc1e0b 100644 --- a/src/Compilers/CSharp/Portable/Errors/MessageID.cs +++ b/src/Compilers/CSharp/Portable/Errors/MessageID.cs @@ -224,9 +224,6 @@ internal enum MessageID IDS_FeatureInferredDelegateType = MessageBase + 12799, IDS_FeatureLambdaAttributes = MessageBase + 12800, - // PROTOTYPE(list-patterns) To reduce conflicts with upstream. Should be moved/indexed eventually. - IDS_FeatureListPattern = MessageBase + 12850, - IDS_FeatureWithOnAnonymousTypes = MessageBase + 12801, IDS_FeatureExtendedPropertyPatterns = MessageBase + 12802, IDS_FeatureStaticAbstractMembersInInterfaces = MessageBase + 12803, @@ -238,7 +235,9 @@ internal enum MessageID IDS_FeatureFileScopedNamespace = MessageBase + 12809, IDS_FeatureParameterlessStructConstructors = MessageBase + 12810, IDS_FeatureStructFieldInitializers = MessageBase + 12811, + IDS_FeatureGenericAttributes = MessageBase + 12812, + IDS_FeatureListPattern = MessageBase + 12813, } // Message IDs may refer to strings that need to be localized. @@ -345,9 +344,12 @@ internal static LanguageVersion RequiredVersion(this MessageID feature) // Checks are in the LanguageParser unless otherwise noted. switch (feature) { + // PREFER reporting diagnostics in binding when diagnostics do not affect the shape of the tree + // C# preview features. case MessageID.IDS_FeatureStaticAbstractMembersInInterfaces: // semantic check case MessageID.IDS_FeatureGenericAttributes: // semantic check + case MessageID.IDS_FeatureListPattern: // semantic check return LanguageVersion.Preview; // C# 10.0 features. @@ -533,10 +535,6 @@ internal static LanguageVersion RequiredVersion(this MessageID feature) default: throw ExceptionUtilities.UnexpectedValue(feature); - - // PROTOTYPE(list-patterns) - case MessageID.IDS_FeatureListPattern: - return LanguageVersion.Preview; } } } diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs index 3f839eec2901..c43b4198a4f2 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs @@ -1433,13 +1433,13 @@ public override BoundNode VisitIndexerAccess(BoundIndexerAccess node) return null; } - public override BoundNode VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node) + public override BoundNode VisitIndexOrRangeIndexerFallbackAccess(BoundIndexOrRangeIndexerFallbackAccess node) { - // Index or Range pattern indexers evaluate the following in order: + // Index or Range fallback indexers evaluate the following in order: // 1. The receiver - // 1. The Count or Length method off the receiver - // 2. The argument to the access - // 3. The pattern method + // 2. The Count or Length method off the receiver + // 3. The argument to the access + // 4. The pattern method VisitRvalue(node.Receiver); var method = GetReadMethod(node.LengthOrCountProperty); VisitReceiverAfterCall(node.Receiver, method); diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index cca9838c5c82..8c245a361217 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -8692,7 +8692,7 @@ private TypeWithAnnotations GetDeclaredParameterResult(ParameterSymbol parameter return null; } - public override BoundNode? VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node) + public override BoundNode? VisitIndexOrRangeIndexerFallbackAccess(BoundIndexOrRangeIndexerFallbackAccess node) { BoundExpression receiver = node.Receiver; var receiverType = VisitRvalueWithState(receiver).Type; diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker_Patterns.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker_Patterns.cs index 5569ee590337..1d66f03dcc04 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker_Patterns.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker_Patterns.cs @@ -155,6 +155,7 @@ private void LearnFromAnyNullPatterns( case BoundITuplePattern _: case BoundRelationalPattern _: case BoundSlicePattern _: + case BoundListPattern lp: break; // nothing to learn case BoundTypePattern tp: if (tp.IsExplicitNotNullTest) @@ -162,9 +163,6 @@ private void LearnFromAnyNullPatterns( LearnFromNullTest(inputSlot, inputType, ref this.State, markDependentSlotsNotNull: false); } break; - case BoundListPattern lp: - // PROTOTYPE(list-patterns) - break; case BoundRecursivePattern rp: { if (rp.IsExplicitNotNullTest) diff --git a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs index e53fe81fccf0..30e1314a2ada 100644 --- a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs @@ -30,7 +30,7 @@ internal enum BoundKind : byte AwaitableValuePlaceholder, DisposableValuePlaceholder, ObjectOrCollectionValuePlaceholder, - IndexOrRangeIndexerPatternValuePlaceholder, + IndexOrRangeIndexerFallbackValuePlaceholder, Dup, PassByCopy, BadExpression, @@ -197,7 +197,7 @@ internal enum BoundKind : byte PropertyAccess, EventAccess, IndexerAccess, - IndexOrRangePatternIndexerAccess, + IndexOrRangeIndexerFallbackAccess, DynamicIndexerAccess, Lambda, UnboundLambda, @@ -640,18 +640,18 @@ public BoundObjectOrCollectionValuePlaceholder Update(bool isNewInstance, TypeSy } } - internal sealed partial class BoundIndexOrRangeIndexerPatternValuePlaceholder : BoundValuePlaceholderBase + internal sealed partial class BoundIndexOrRangeIndexerFallbackValuePlaceholder : BoundValuePlaceholderBase { - public BoundIndexOrRangeIndexerPatternValuePlaceholder(SyntaxNode syntax, TypeSymbol type, bool hasErrors) - : base(BoundKind.IndexOrRangeIndexerPatternValuePlaceholder, syntax, type, hasErrors) + public BoundIndexOrRangeIndexerFallbackValuePlaceholder(SyntaxNode syntax, TypeSymbol type, bool hasErrors) + : base(BoundKind.IndexOrRangeIndexerFallbackValuePlaceholder, syntax, type, hasErrors) { RoslynDebug.Assert(type is object, "Field 'type' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); } - public BoundIndexOrRangeIndexerPatternValuePlaceholder(SyntaxNode syntax, TypeSymbol type) - : base(BoundKind.IndexOrRangeIndexerPatternValuePlaceholder, syntax, type) + public BoundIndexOrRangeIndexerFallbackValuePlaceholder(SyntaxNode syntax, TypeSymbol type) + : base(BoundKind.IndexOrRangeIndexerFallbackValuePlaceholder, syntax, type) { RoslynDebug.Assert(type is object, "Field 'type' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); @@ -661,13 +661,13 @@ public BoundIndexOrRangeIndexerPatternValuePlaceholder(SyntaxNode syntax, TypeSy public new TypeSymbol Type => base.Type!; [DebuggerStepThrough] - public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitIndexOrRangeIndexerPatternValuePlaceholder(this); + public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitIndexOrRangeIndexerFallbackValuePlaceholder(this); - public BoundIndexOrRangeIndexerPatternValuePlaceholder Update(TypeSymbol type) + public BoundIndexOrRangeIndexerFallbackValuePlaceholder Update(TypeSymbol type) { if (!TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) { - var result = new BoundIndexOrRangeIndexerPatternValuePlaceholder(this.Syntax, type, this.HasErrors); + var result = new BoundIndexOrRangeIndexerFallbackValuePlaceholder(this.Syntax, type, this.HasErrors); result.CopyAttributes(this); return result; } @@ -7096,10 +7096,10 @@ public BoundIndexerAccess Update(BoundExpression? receiverOpt, PropertySymbol in } } - internal sealed partial class BoundIndexOrRangePatternIndexerAccess : BoundExpression + internal sealed partial class BoundIndexOrRangeIndexerFallbackAccess : BoundExpression { - public BoundIndexOrRangePatternIndexerAccess(SyntaxNode syntax, BoundExpression receiver, PropertySymbol lengthOrCountProperty, Symbol patternSymbol, BoundExpression argument, TypeSymbol type, bool hasErrors = false) - : base(BoundKind.IndexOrRangePatternIndexerAccess, syntax, type, hasErrors || receiver.HasErrors() || argument.HasErrors()) + public BoundIndexOrRangeIndexerFallbackAccess(SyntaxNode syntax, BoundExpression receiver, PropertySymbol lengthOrCountProperty, Symbol patternSymbol, BoundExpression argument, TypeSymbol type, bool hasErrors = false) + : base(BoundKind.IndexOrRangeIndexerFallbackAccess, syntax, type, hasErrors || receiver.HasErrors() || argument.HasErrors()) { RoslynDebug.Assert(receiver is object, "Field 'receiver' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); @@ -7125,13 +7125,13 @@ public BoundIndexOrRangePatternIndexerAccess(SyntaxNode syntax, BoundExpression public BoundExpression Argument { get; } [DebuggerStepThrough] - public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitIndexOrRangePatternIndexerAccess(this); + public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitIndexOrRangeIndexerFallbackAccess(this); - public BoundIndexOrRangePatternIndexerAccess Update(BoundExpression receiver, PropertySymbol lengthOrCountProperty, Symbol patternSymbol, BoundExpression argument, TypeSymbol type) + public BoundIndexOrRangeIndexerFallbackAccess Update(BoundExpression receiver, PropertySymbol lengthOrCountProperty, Symbol patternSymbol, BoundExpression argument, TypeSymbol type) { if (receiver != this.Receiver || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(lengthOrCountProperty, this.LengthOrCountProperty) || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(patternSymbol, this.PatternSymbol) || argument != this.Argument || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) { - var result = new BoundIndexOrRangePatternIndexerAccess(this.Syntax, receiver, lengthOrCountProperty, patternSymbol, argument, type, this.HasErrors); + var result = new BoundIndexOrRangeIndexerFallbackAccess(this.Syntax, receiver, lengthOrCountProperty, patternSymbol, argument, type, this.HasErrors); result.CopyAttributes(this); return result; } @@ -8483,8 +8483,8 @@ internal R VisitInternal(BoundNode node, A arg) return VisitDisposableValuePlaceholder((BoundDisposableValuePlaceholder)node, arg); case BoundKind.ObjectOrCollectionValuePlaceholder: return VisitObjectOrCollectionValuePlaceholder((BoundObjectOrCollectionValuePlaceholder)node, arg); - case BoundKind.IndexOrRangeIndexerPatternValuePlaceholder: - return VisitIndexOrRangeIndexerPatternValuePlaceholder((BoundIndexOrRangeIndexerPatternValuePlaceholder)node, arg); + case BoundKind.IndexOrRangeIndexerFallbackValuePlaceholder: + return VisitIndexOrRangeIndexerFallbackValuePlaceholder((BoundIndexOrRangeIndexerFallbackValuePlaceholder)node, arg); case BoundKind.Dup: return VisitDup((BoundDup)node, arg); case BoundKind.PassByCopy: @@ -8817,8 +8817,8 @@ internal R VisitInternal(BoundNode node, A arg) return VisitEventAccess((BoundEventAccess)node, arg); case BoundKind.IndexerAccess: return VisitIndexerAccess((BoundIndexerAccess)node, arg); - case BoundKind.IndexOrRangePatternIndexerAccess: - return VisitIndexOrRangePatternIndexerAccess((BoundIndexOrRangePatternIndexerAccess)node, arg); + case BoundKind.IndexOrRangeIndexerFallbackAccess: + return VisitIndexOrRangeIndexerFallbackAccess((BoundIndexOrRangeIndexerFallbackAccess)node, arg); case BoundKind.DynamicIndexerAccess: return VisitDynamicIndexerAccess((BoundDynamicIndexerAccess)node, arg); case BoundKind.Lambda: @@ -8907,7 +8907,7 @@ internal abstract partial class BoundTreeVisitor public virtual R VisitAwaitableValuePlaceholder(BoundAwaitableValuePlaceholder node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitDisposableValuePlaceholder(BoundDisposableValuePlaceholder node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitObjectOrCollectionValuePlaceholder(BoundObjectOrCollectionValuePlaceholder node, A arg) => this.DefaultVisit(node, arg); - public virtual R VisitIndexOrRangeIndexerPatternValuePlaceholder(BoundIndexOrRangeIndexerPatternValuePlaceholder node, A arg) => this.DefaultVisit(node, arg); + public virtual R VisitIndexOrRangeIndexerFallbackValuePlaceholder(BoundIndexOrRangeIndexerFallbackValuePlaceholder node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitDup(BoundDup node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitPassByCopy(BoundPassByCopy node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitBadExpression(BoundBadExpression node, A arg) => this.DefaultVisit(node, arg); @@ -9074,7 +9074,7 @@ internal abstract partial class BoundTreeVisitor public virtual R VisitPropertyAccess(BoundPropertyAccess node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitEventAccess(BoundEventAccess node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitIndexerAccess(BoundIndexerAccess node, A arg) => this.DefaultVisit(node, arg); - public virtual R VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node, A arg) => this.DefaultVisit(node, arg); + public virtual R VisitIndexOrRangeIndexerFallbackAccess(BoundIndexOrRangeIndexerFallbackAccess node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitDynamicIndexerAccess(BoundDynamicIndexerAccess node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitLambda(BoundLambda node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitUnboundLambda(UnboundLambda node, A arg) => this.DefaultVisit(node, arg); @@ -9124,7 +9124,7 @@ internal abstract partial class BoundTreeVisitor public virtual BoundNode? VisitAwaitableValuePlaceholder(BoundAwaitableValuePlaceholder node) => this.DefaultVisit(node); public virtual BoundNode? VisitDisposableValuePlaceholder(BoundDisposableValuePlaceholder node) => this.DefaultVisit(node); public virtual BoundNode? VisitObjectOrCollectionValuePlaceholder(BoundObjectOrCollectionValuePlaceholder node) => this.DefaultVisit(node); - public virtual BoundNode? VisitIndexOrRangeIndexerPatternValuePlaceholder(BoundIndexOrRangeIndexerPatternValuePlaceholder node) => this.DefaultVisit(node); + public virtual BoundNode? VisitIndexOrRangeIndexerFallbackValuePlaceholder(BoundIndexOrRangeIndexerFallbackValuePlaceholder node) => this.DefaultVisit(node); public virtual BoundNode? VisitDup(BoundDup node) => this.DefaultVisit(node); public virtual BoundNode? VisitPassByCopy(BoundPassByCopy node) => this.DefaultVisit(node); public virtual BoundNode? VisitBadExpression(BoundBadExpression node) => this.DefaultVisit(node); @@ -9291,7 +9291,7 @@ internal abstract partial class BoundTreeVisitor public virtual BoundNode? VisitPropertyAccess(BoundPropertyAccess node) => this.DefaultVisit(node); public virtual BoundNode? VisitEventAccess(BoundEventAccess node) => this.DefaultVisit(node); public virtual BoundNode? VisitIndexerAccess(BoundIndexerAccess node) => this.DefaultVisit(node); - public virtual BoundNode? VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node) => this.DefaultVisit(node); + public virtual BoundNode? VisitIndexOrRangeIndexerFallbackAccess(BoundIndexOrRangeIndexerFallbackAccess node) => this.DefaultVisit(node); public virtual BoundNode? VisitDynamicIndexerAccess(BoundDynamicIndexerAccess node) => this.DefaultVisit(node); public virtual BoundNode? VisitLambda(BoundLambda node) => this.DefaultVisit(node); public virtual BoundNode? VisitUnboundLambda(UnboundLambda node) => this.DefaultVisit(node); @@ -9357,7 +9357,7 @@ internal abstract partial class BoundTreeWalker : BoundTreeVisitor public override BoundNode? VisitAwaitableValuePlaceholder(BoundAwaitableValuePlaceholder node) => null; public override BoundNode? VisitDisposableValuePlaceholder(BoundDisposableValuePlaceholder node) => null; public override BoundNode? VisitObjectOrCollectionValuePlaceholder(BoundObjectOrCollectionValuePlaceholder node) => null; - public override BoundNode? VisitIndexOrRangeIndexerPatternValuePlaceholder(BoundIndexOrRangeIndexerPatternValuePlaceholder node) => null; + public override BoundNode? VisitIndexOrRangeIndexerFallbackValuePlaceholder(BoundIndexOrRangeIndexerFallbackValuePlaceholder node) => null; public override BoundNode? VisitDup(BoundDup node) => null; public override BoundNode? VisitPassByCopy(BoundPassByCopy node) { @@ -10124,7 +10124,7 @@ internal abstract partial class BoundTreeWalker : BoundTreeVisitor this.VisitList(node.Arguments); return null; } - public override BoundNode? VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node) + public override BoundNode? VisitIndexOrRangeIndexerFallbackAccess(BoundIndexOrRangeIndexerFallbackAccess node) { this.Visit(node.Receiver); this.Visit(node.Argument); @@ -10352,7 +10352,7 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor TypeSymbol? type = this.VisitType(node.Type); return node.Update(node.IsNewInstance, type); } - public override BoundNode? VisitIndexOrRangeIndexerPatternValuePlaceholder(BoundIndexOrRangeIndexerPatternValuePlaceholder node) + public override BoundNode? VisitIndexOrRangeIndexerFallbackValuePlaceholder(BoundIndexOrRangeIndexerFallbackValuePlaceholder node) { TypeSymbol? type = this.VisitType(node.Type); return node.Update(type); @@ -11343,7 +11343,7 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor TypeSymbol? type = this.VisitType(node.Type); return node.Update(receiverOpt, node.Indexer, arguments, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, node.Expanded, node.ArgsToParamsOpt, node.DefaultArguments, node.OriginalIndexersOpt, type); } - public override BoundNode? VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node) + public override BoundNode? VisitIndexOrRangeIndexerFallbackAccess(BoundIndexOrRangeIndexerFallbackAccess node) { BoundExpression receiver = (BoundExpression)this.Visit(node.Receiver); BoundExpression argument = (BoundExpression)this.Visit(node.Argument); @@ -11693,14 +11693,14 @@ public NullabilityRewriter(ImmutableDictionary new TreeDumperNode("indexOrRangeIndexerPatternValuePlaceholder", null, new TreeDumperNode[] + public override TreeDumperNode VisitIndexOrRangeIndexerFallbackValuePlaceholder(BoundIndexOrRangeIndexerFallbackValuePlaceholder node, object? arg) => new TreeDumperNode("indexOrRangeIndexerFallbackValuePlaceholder", null, new TreeDumperNode[] { new TreeDumperNode("type", node.Type, null), new TreeDumperNode("isSuppressed", node.IsSuppressed, null), @@ -15708,7 +15708,7 @@ private BoundTreeDumperNodeProducer() new TreeDumperNode("hasErrors", node.HasErrors, null) } ); - public override TreeDumperNode VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node, object? arg) => new TreeDumperNode("indexOrRangePatternIndexerAccess", null, new TreeDumperNode[] + public override TreeDumperNode VisitIndexOrRangeIndexerFallbackAccess(BoundIndexOrRangeIndexerFallbackAccess node, object? arg) => new TreeDumperNode("indexOrRangeIndexerFallbackAccess", null, new TreeDumperNode[] { new TreeDumperNode("receiver", null, new TreeDumperNode[] { Visit(node.Receiver, null) }), new TreeDumperNode("lengthOrCountProperty", node.LengthOrCountProperty, null), diff --git a/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs b/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs index 2f4b93cf5d24..82d0568f95b8 100644 --- a/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs +++ b/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs @@ -97,14 +97,14 @@ public override BoundNode VisitArrayAccess(BoundArrayAccess node) return base.VisitArrayAccess(node); } - public override BoundNode VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node) + public override BoundNode VisitIndexOrRangeIndexerFallbackAccess(BoundIndexOrRangeIndexerFallbackAccess node) { if (_inExpressionLambda) { Error(ErrorCode.ERR_ExpressionTreeContainsPatternIndexOrRangeIndexer, node); } - return base.VisitIndexOrRangePatternIndexerAccess(node); + return base.VisitIndexOrRangeIndexerFallbackAccess(node); } public override BoundNode VisitFromEndIndexExpression(BoundFromEndIndexExpression node) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs index 95588b85ab77..b7b8595acc89 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs @@ -953,8 +953,8 @@ internal static bool CanBePassedByReference(BoundExpression expr) case BoundKind.IndexerAccess: return ((BoundIndexerAccess)expr).Indexer.RefKind != RefKind.None; - case BoundKind.IndexOrRangePatternIndexerAccess: - var patternIndexer = (BoundIndexOrRangePatternIndexerAccess)expr; + case BoundKind.IndexOrRangeIndexerFallbackAccess: + var patternIndexer = (BoundIndexOrRangeIndexerFallbackAccess)expr; var refKind = patternIndexer.PatternSymbol switch { PropertySymbol p => p.RefKind, @@ -1048,7 +1048,7 @@ public static void Validate(BoundNode node) return null; } - public override BoundNode? VisitIndexOrRangeIndexerPatternValuePlaceholder(BoundIndexOrRangeIndexerPatternValuePlaceholder node) + public override BoundNode? VisitIndexOrRangeIndexerFallbackValuePlaceholder(BoundIndexOrRangeIndexerFallbackValuePlaceholder node) { Fail(node); return null; diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs index fce8d6336746..8f2c357f1ae7 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs @@ -34,9 +34,9 @@ private BoundExpression VisitAssignmentOperator(BoundAssignmentOperator node, bo loweredLeft = VisitIndexerAccess((BoundIndexerAccess)left, isLeftOfAssignment: true); break; - case BoundKind.IndexOrRangePatternIndexerAccess: - loweredLeft = VisitIndexOrRangePatternIndexerAccess( - (BoundIndexOrRangePatternIndexerAccess)left, + case BoundKind.IndexOrRangeIndexerFallbackAccess: + loweredLeft = VisitIndexOrRangeIndexerFallbackAccess( + (BoundIndexOrRangeIndexerFallbackAccess)left, isLeftOfAssignment: true); break; diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs index bd4b043efb3f..58c7d8225b60 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs @@ -404,19 +404,19 @@ private BoundIndexerAccess TransformIndexerAccess(BoundIndexerAccess indexerAcce indexerAccess.Type); } - private BoundExpression TransformPatternIndexerAccess( - BoundIndexOrRangePatternIndexerAccess indexerAccess, + private BoundExpression TransformIndexerFallbackAccess( + BoundIndexOrRangeIndexerFallbackAccess indexerAccess, ArrayBuilder stores, ArrayBuilder temps, bool isDynamicAssignment) { - // A pattern indexer is fundamentally a sequence which ends in either + // A fallback indexer is fundamentally a sequence which ends in either // a conventional indexer access or a method call. The lowering of a - // pattern indexer already lowers everything we need into temps, so + // fallback indexer already lowers everything we need into temps, so // the only thing we need to do is lift the stores and temps out of // the sequence, and use the final expression as the new argument - var sequence = VisitIndexOrRangePatternIndexerAccess(indexerAccess, isLeftOfAssignment: true); + var sequence = VisitIndexOrRangeIndexerFallbackAccess(indexerAccess, isLeftOfAssignment: true); stores.AddRange(sequence.SideEffects); temps.AddRange(sequence.Locals); return TransformCompoundAssignmentLHS(sequence.Value, stores, temps, isDynamicAssignment); @@ -585,9 +585,9 @@ private BoundExpression TransformCompoundAssignmentLHS(BoundExpression originalL } break; - case BoundKind.IndexOrRangePatternIndexerAccess: + case BoundKind.IndexOrRangeIndexerFallbackAccess: { - var patternIndexerAccess = (BoundIndexOrRangePatternIndexerAccess)originalLHS; + var patternIndexerAccess = (BoundIndexOrRangeIndexerFallbackAccess)originalLHS; RefKind refKind = patternIndexerAccess.PatternSymbol switch { PropertySymbol p => p.RefKind, @@ -596,7 +596,7 @@ private BoundExpression TransformCompoundAssignmentLHS(BoundExpression originalL }; if (refKind == RefKind.None) { - return TransformPatternIndexerAccess(patternIndexerAccess, stores, temps, isDynamicAssignment); + return TransformIndexerFallbackAccess(patternIndexerAccess, stores, temps, isDynamicAssignment); } } break; diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs index 6a91f88b4da0..a39d3de83285 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs @@ -161,20 +161,19 @@ private BoundExpression MakeIndexerAccess( } } } - - public override BoundNode VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node) + public override BoundNode VisitIndexOrRangeIndexerFallbackAccess(BoundIndexOrRangeIndexerFallbackAccess node) { - return VisitIndexOrRangePatternIndexerAccess(node, isLeftOfAssignment: false); + return VisitIndexOrRangeIndexerFallbackAccess(node, isLeftOfAssignment: false); } - private BoundSequence VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node, bool isLeftOfAssignment) + private BoundSequence VisitIndexOrRangeIndexerFallbackAccess(BoundIndexOrRangeIndexerFallbackAccess node, bool isLeftOfAssignment) { if (TypeSymbol.Equals( node.Argument.Type, _compilation.GetWellKnownType(WellKnownType.System_Index), TypeCompareKind.ConsiderEverything)) { - return VisitIndexPatternIndexerAccess( + return VisitIndexIndexerFallbackAccess( node.Syntax, node.Receiver, node.LengthOrCountProperty, @@ -188,7 +187,7 @@ private BoundSequence VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePat node.Argument.Type, _compilation.GetWellKnownType(WellKnownType.System_Range), TypeCompareKind.ConsiderEverything)); - return VisitRangePatternIndexerAccess( + return VisitRangeIndexerFallbackAccess( node.Receiver, node.LengthOrCountProperty, (MethodSymbol)node.PatternSymbol, @@ -196,8 +195,7 @@ private BoundSequence VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePat } } - - private BoundSequence VisitIndexPatternIndexerAccess( + private BoundSequence VisitIndexIndexerFallbackAccess( SyntaxNode syntax, BoundExpression receiver, PropertySymbol lengthOrCountProperty, @@ -286,7 +284,7 @@ private BoundExpression MakePatternIndexOffsetExpression( } } - private BoundSequence VisitRangePatternIndexerAccess( + private BoundSequence VisitRangeIndexerFallbackAccess( BoundExpression receiver, PropertySymbol lengthOrCountProperty, MethodSymbol sliceMethod, diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index aed9268bee5a..9d9feb695b8b 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -300,7 +300,7 @@ public CSharpOperationFactory(SemanticModel semanticModel) case BoundKind.StackAllocArrayCreation: case BoundKind.TypeExpression: case BoundKind.TypeOrValueExpression: - case BoundKind.IndexOrRangePatternIndexerAccess: + case BoundKind.IndexOrRangeIndexerFallbackAccess: ConstantValue? constantValue = (boundNode as BoundExpression)?.ConstantValue; bool isImplicit = boundNode.WasCompilerGenerated; diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser_Patterns.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser_Patterns.cs index 994f0e669dad..117a668f5788 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser_Patterns.cs @@ -707,8 +707,7 @@ private PatternSyntax ParseListPattern(bool whenIsKeyword) SyntaxKind.CloseBracketToken, static p => p.ParsePattern(Precedence.Conditional)); TryParseSimpleDesignation(out VariableDesignationSyntax designation, whenIsKeyword); - var result = _syntaxFactory.ListPattern(openBracket, list, closeBracket, designation); - return CheckFeatureAvailability(result, MessageID.IDS_FeatureListPattern); + return _syntaxFactory.ListPattern(openBracket, list, closeBracket, designation); } } } diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs index 5442ada3c68f..6a7d6469fa54 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs @@ -829,7 +829,7 @@ public enum SyntaxKind : ushort AndPattern = 9032, NotPattern = 9033, - // PROTOTYPE(list-patterns) new patterns added + // new patterns added in C# 11.0 SlicePattern = 9034, ListPattern = 9035, diff --git a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_WellKnownAttributes.cs b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_WellKnownAttributes.cs index 8de0d7582a5a..5124d9ae3b4e 100644 --- a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_WellKnownAttributes.cs +++ b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_WellKnownAttributes.cs @@ -13637,11 +13637,16 @@ public void M() } "; - // PROTOTYPE missing diagnostics (we're checking the Count property but not the accessor) CreateCompilation(code, targetFramework: TargetFramework.NetCoreApp).VerifyDiagnostics( + // (23,13): error CS0619: 'C.Count.get' is obsolete: 'error' + // _ = this[^1]; // 1, 2 + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "this[^1]").WithArguments("C.Count.get", "error").WithLocation(23, 13), // (23,13): error CS0619: 'C.this[int].get' is obsolete: 'error' // _ = this[^1]; // 1, 2 Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "this[^1]").WithArguments("C.this[int].get", "error").WithLocation(23, 13), + // (24,13): error CS0619: 'C.Count.get' is obsolete: 'error' + // _ = this[..]; // 3, 4 + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "this[..]").WithArguments("C.Count.get", "error").WithLocation(24, 13), // (24,13): error CS0619: 'C.Slice(int, int)' is obsolete: 'error' // _ = this[..]; // 3, 4 Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "this[..]").WithArguments("C.Slice(int, int)", "error").WithLocation(24, 13) diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/IndexAndRangeTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/IndexAndRangeTests.cs index 60daa1c37ee1..1824287c21d3 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/IndexAndRangeTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/IndexAndRangeTests.cs @@ -3152,5 +3152,42 @@ Indexer get 3 "); } + + [Fact] + public void ObsoleteRangeType() + { + var source = @" +_ = new C()[..]; + +class C +{ + public int Length => 0; + public int this[int i] => 0; + public int Slice(int i, int j) => 0; +} + +namespace System +{ + [Obsolete] + public readonly struct Range + { + public Index Start { get; } + + public Index End { get; } + + public Range(Index start, Index end) => throw null; + + public static Range StartAt(Index start) => throw null; + + public static Range EndAt(Index end) => throw null; + + public static Range All => throw null; + } +} +"; + // Note: we currently don't report Obsolete diagnostic on either Index or Range + var comp = CreateCompilation(new[] { source, TestSources.Index }); + comp.VerifyDiagnostics(); + } } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs index a2251002616e..8768879b118d 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs @@ -225,6 +225,42 @@ .locals init (char V_0, //first ); } + [Fact] + public void ListPattern_LangVer() + { + var source = @" +_ = new C() is []; +_ = new C() is [.. var x]; +_ = new C() is .. var y; + +class C +{ + public int this[int i] => 1; + public int Length => 0; + public int Slice(int i, int j) => 0; +} +"; + var compilation = CreateCompilationWithIndexAndRange(source, parseOptions: TestOptions.Regular10); + compilation.VerifyDiagnostics( + // (2,16): error CS8652: The feature 'list pattern' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // _ = new C() is []; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "[]").WithArguments("list pattern").WithLocation(2, 16), + // (3,16): error CS8652: The feature 'list pattern' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // _ = new C() is [.. var x]; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "[.. var x]").WithArguments("list pattern").WithLocation(3, 16), + // (4,16): error CS9002: Slice patterns may only be used once and directly inside a list pattern. + // _ = new C() is .. var y; + Diagnostic(ErrorCode.ERR_MisplacedSlicePattern, ".. var y").WithLocation(4, 16) + ); + + compilation = CreateCompilationWithIndex(source, parseOptions: TestOptions.RegularNext); + compilation.VerifyDiagnostics( + // (4,16): error CS9002: Slice patterns may only be used once and directly inside a list pattern. + // _ = new C() is .. var y; + Diagnostic(ErrorCode.ERR_MisplacedSlicePattern, ".. var y").WithLocation(4, 16) + ); + } + [Fact] public void ListPattern_Index() { @@ -683,9 +719,9 @@ void M(object o) "; var expectedDiagnostics = new[] { - // error CS9200: List patterns may not be used for a value of type 'object'. + // error CS9000: List patterns may not be used for a value of type 'object'. subpattern != "" ? Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, listPattern).WithArguments("object") : null, - // error CS9201: Slice patterns may not be used for a value of type 'object'. + // error CS9001: Slice patterns may not be used for a value of type 'object'. subpattern == ".._" ? Diagnostic(ErrorCode.ERR_UnsupportedTypeForSlicePattern, subpattern).WithArguments("object") : null }; var compilation = CreateCompilationWithIndexAndRange(source, parseOptions: TestOptions.RegularWithListPatterns); @@ -775,7 +811,7 @@ public void M(string s) var compilation = CreateCompilationWithIndexAndRange(source, parseOptions: TestOptions.RegularWithListPatterns); compilation.MakeMemberMissing(SpecialMember.System_String__Substring); compilation.VerifyEmitDiagnostics( - // (6,19): error CS9201: Slice patterns may not be used for a value of type 'string'. + // (6,19): error CS9001: Slice patterns may not be used for a value of type 'string'. // _ = s is {.. var slice}; Diagnostic(ErrorCode.ERR_UnsupportedTypeForSlicePattern, ".. var slice").WithArguments("string").WithLocation(6, 19), // (7,15): error CS1503: Argument 1: cannot convert from 'System.Range' to 'int' @@ -855,10 +891,10 @@ public static void Main() var isIndexable = implicitIndex || explicitIndex; var expectedDiagnostics = new[] { - // (13,24): error CS9200: List patterns may not be used for a value of type 'X'. + // (13,24): error CS9000: List patterns may not be used for a value of type 'X'. // _ = new X() is [.._]; !isIndexable || !isCountable ? Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[.._]").WithArguments("X").WithLocation(13, 24) : null, - // (13,25): error CS9201: Slice patterns may not be used for a value of type 'X'. + // (13,25): error CS9001: Slice patterns may not be used for a value of type 'X'. // _ = new X() is [.._]; !isSliceable || !isCountable ? Diagnostic(ErrorCode.ERR_UnsupportedTypeForSlicePattern, ".._").WithArguments("X").WithLocation(13, 25) : null }; @@ -940,6 +976,252 @@ public void M() ); } + [Fact] + public void ListPattern_ObsoleteAccessors() + { + var source = @" +using System; +class Test1 +{ + [Obsolete(""error1"", error: true)] + public int Slice(int i, int j) => 0; + public int this[int i] + { + [Obsolete(""error1"", error: true)] + get => 0; + } + public int Count + { + [Obsolete(""error2"", error: true)] + get => 0; + } +} +class Test2 +{ + public int this[Index i] + { + [Obsolete(""error3"", error: true)] + get => 0; + } + public int this[Range i] + { + [Obsolete(""error4"", error: true)] + get => 0; + } + public int Length + { + [Obsolete(""error5"", error: true)] + get => 0; + } +} +class X +{ + public void M() + { + _ = new Test1() is [0]; + _ = new Test1() is [..0]; + _ = new Test2() is [0]; + _ = new Test2() is [..0]; + } +} +"; + var compilation = CreateCompilationWithIndexAndRange(source); + compilation.VerifyEmitDiagnostics( + // (40,28): error CS0619: 'Test1.Count.get' is obsolete: 'error2' + // _ = new Test1() is [0]; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[0]").WithArguments("Test1.Count.get", "error2").WithLocation(40, 28), + // (40,28): error CS0619: 'Test1.this[int].get' is obsolete: 'error1' + // _ = new Test1() is [0]; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[0]").WithArguments("Test1.this[int].get", "error1").WithLocation(40, 28), + + // (41,28): error CS0619: 'Test1.Count.get' is obsolete: 'error2' + // _ = new Test1() is [..0]; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[..0]").WithArguments("Test1.Count.get", "error2").WithLocation(41, 28), + // (41,28): error CS0619: 'Test1.this[int].get' is obsolete: 'error1' + // _ = new Test1() is [..0]; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[..0]").WithArguments("Test1.this[int].get", "error1").WithLocation(41, 28), + // (41,29): error CS0619: 'Test1.Slice(int, int)' is obsolete: 'error1' + // _ = new Test1() is [..0]; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "..0").WithArguments("Test1.Slice(int, int)", "error1").WithLocation(41, 29), + // (41,29): error CS0619: 'Test1.Count.get' is obsolete: 'error2' + // _ = new Test1() is [..0]; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "..0").WithArguments("Test1.Count.get", "error2").WithLocation(41, 29), + + // (42,28): error CS0619: 'Test2.Length.get' is obsolete: 'error5' + // _ = new Test2() is [0]; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[0]").WithArguments("Test2.Length.get", "error5").WithLocation(42, 28), + // (42,28): error CS0619: 'Test2.this[Index].get' is obsolete: 'error3' + // _ = new Test2() is [0]; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[0]").WithArguments("Test2.this[System.Index].get", "error3").WithLocation(42, 28), + + // (43,28): error CS0619: 'Test2.Length.get' is obsolete: 'error5' + // _ = new Test2() is [..0]; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[..0]").WithArguments("Test2.Length.get", "error5").WithLocation(43, 28), + // (43,28): error CS0619: 'Test2.this[Index].get' is obsolete: 'error3' + // _ = new Test2() is [..0]; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[..0]").WithArguments("Test2.this[System.Index].get", "error3").WithLocation(43, 28), + // (43,29): error CS0619: 'Test2.Length.get' is obsolete: 'error5' + // _ = new Test2() is [..0]; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "..0").WithArguments("Test2.Length.get", "error5").WithLocation(43, 29), + // (43,29): error CS0619: 'Test2.this[Range].get' is obsolete: 'error4' + // _ = new Test2() is [..0]; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "..0").WithArguments("Test2.this[System.Range].get", "error4").WithLocation(43, 29) + ); + } + + [Fact] + public void ListPattern_ObsoleteAccessors_OnBase() + { + var source = @" +using System; +class Base1 +{ + [Obsolete(""error1"", error: true)] + public int Slice(int i, int j) => 0; + + public virtual int this[int i] + { + [Obsolete(""error2"", error: true)] + get => 0; + set { } + } + public virtual int Count + { + [Obsolete(""error3"", error: true)] + get => 0; + set { } + } +} +class Test1 : Base1 +{ + public override int this[int i] + { + set { } + } + public override int Count + { + set { } + } +} +class Base2 +{ + public virtual int this[Index i] + { + [Obsolete(""error4"", error: true)] + get => 0; + set { } + } + public virtual int this[Range i] + { + [Obsolete(""error5"", error: true)] + get => 0; + set { } + } + public virtual int Length + { + [Obsolete(""error6"", error: true)] + get => 0; + set { } + } +} + +class Test2 : Base2 +{ + public override int this[Index i] + { + set { } + } + public override int this[Range i] + { + set { } + } + public override int Length + { + set { } + } +} +class X +{ + public void M() + { + _ = new Test1() is [0]; + _ = new Test1() is [..0]; + _ = new Test2() is [0]; + _ = new Test2() is [..0]; + + _ = new Test1()[^1]; + _ = new Test1()[..0]; + _ = new Test2()[^1]; + _ = new Test2()[..0]; + } +} +"; + var compilation = CreateCompilationWithIndexAndRange(source); + compilation.VerifyEmitDiagnostics( + // (73,28): error CS0619: 'Base1.Count.get' is obsolete: 'error3' + // _ = new Test1() is [0]; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[0]").WithArguments("Base1.Count.get", "error3").WithLocation(73, 28), + // (73,28): error CS0619: 'Base1.this[int].get' is obsolete: 'error2' + // _ = new Test1() is [0]; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[0]").WithArguments("Base1.this[int].get", "error2").WithLocation(73, 28), + + // (74,28): error CS0619: 'Base1.Count.get' is obsolete: 'error3' + // _ = new Test1() is [..0]; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[..0]").WithArguments("Base1.Count.get", "error3").WithLocation(74, 28), + // (74,28): error CS0619: 'Base1.this[int].get' is obsolete: 'error2' + // _ = new Test1() is [..0]; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[..0]").WithArguments("Base1.this[int].get", "error2").WithLocation(74, 28), + // (74,29): error CS0619: 'Base1.Slice(int, int)' is obsolete: 'error1' + // _ = new Test1() is [..0]; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "..0").WithArguments("Base1.Slice(int, int)", "error1").WithLocation(74, 29), + // (74,29): error CS0619: 'Base1.Count.get' is obsolete: 'error3' + // _ = new Test1() is [..0]; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "..0").WithArguments("Base1.Count.get", "error3").WithLocation(74, 29), + + // (75,28): error CS0619: 'Base2.Length.get' is obsolete: 'error6' + // _ = new Test2() is [0]; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[0]").WithArguments("Base2.Length.get", "error6").WithLocation(75, 28), + // (75,28): error CS0619: 'Base2.this[Index].get' is obsolete: 'error4' + // _ = new Test2() is [0]; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[0]").WithArguments("Base2.this[System.Index].get", "error4").WithLocation(75, 28), + + // (76,28): error CS0619: 'Base2.Length.get' is obsolete: 'error6' + // _ = new Test2() is [..0]; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[..0]").WithArguments("Base2.Length.get", "error6").WithLocation(76, 28), + // (76,28): error CS0619: 'Base2.this[Index].get' is obsolete: 'error4' + // _ = new Test2() is [..0]; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[..0]").WithArguments("Base2.this[System.Index].get", "error4").WithLocation(76, 28), + // (76,29): error CS0619: 'Base2.Length.get' is obsolete: 'error6' + // _ = new Test2() is [..0]; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "..0").WithArguments("Base2.Length.get", "error6").WithLocation(76, 29), + // (76,29): error CS0619: 'Base2.this[Range].get' is obsolete: 'error5' + // _ = new Test2() is [..0]; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "..0").WithArguments("Base2.this[System.Range].get", "error5").WithLocation(76, 29), + + // (78,13): error CS0619: 'Base1.Count.get' is obsolete: 'error3' + // _ = new Test1()[^1]; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "new Test1()[^1]").WithArguments("Base1.Count.get", "error3").WithLocation(78, 13), + // (78,13): error CS0619: 'Base1.this[int].get' is obsolete: 'error2' + // _ = new Test1()[^1]; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "new Test1()[^1]").WithArguments("Base1.this[int].get", "error2").WithLocation(78, 13), + + // (79,13): error CS0619: 'Base1.Slice(int, int)' is obsolete: 'error1' + // _ = new Test1()[..0]; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "new Test1()[..0]").WithArguments("Base1.Slice(int, int)", "error1").WithLocation(79, 13), + // (79,13): error CS0619: 'Base1.Count.get' is obsolete: 'error3' + // _ = new Test1()[..0]; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "new Test1()[..0]").WithArguments("Base1.Count.get", "error3").WithLocation(79, 13), + + // (80,13): error CS0619: 'Base2.this[Index].get' is obsolete: 'error4' + // _ = new Test2()[^1]; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "new Test2()[^1]").WithArguments("Base2.this[System.Index].get", "error4").WithLocation(80, 13), + + // (81,13): error CS0619: 'Base2.this[Range].get' is obsolete: 'error5' + // _ = new Test2()[..0]; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "new Test2()[..0]").WithArguments("Base2.this[System.Range].get", "error5").WithLocation(81, 13) + ); + } + [Fact] public void SlicePattern_Misplaced() { @@ -960,22 +1242,22 @@ public void M(int[] a) "; var compilation = CreateCompilationWithIndexAndRange(source, parseOptions: TestOptions.RegularWithListPatterns); compilation.VerifyEmitDiagnostics( - // (6,23): error CS9202: Slice patterns may only be used once and directly inside a list pattern. + // (6,23): error CS9002: Slice patterns may only be used once and directly inside a list pattern. // _ = a is [.., ..]; Diagnostic(ErrorCode.ERR_MisplacedSlicePattern, "..").WithLocation(6, 23), - // (7,29): error CS9202: Slice patterns may only be used once and directly inside a list pattern. + // (7,29): error CS9002: Slice patterns may only be used once and directly inside a list pattern. // _ = a is [1, .., 2, .., 3]; Diagnostic(ErrorCode.ERR_MisplacedSlicePattern, "..").WithLocation(7, 29), - // (8,20): error CS9202: Slice patterns may only be used once and directly inside a list pattern. + // (8,20): error CS9002: Slice patterns may only be used once and directly inside a list pattern. // _ = a is [(..)]; Diagnostic(ErrorCode.ERR_MisplacedSlicePattern, "..").WithLocation(8, 20), - // (9,18): error CS9202: Slice patterns may only be used once and directly inside a list pattern. + // (9,18): error CS9002: Slice patterns may only be used once and directly inside a list pattern. // _ = a is ..; Diagnostic(ErrorCode.ERR_MisplacedSlicePattern, "..").WithLocation(9, 18), - // (11,24): error CS9202: Slice patterns may only be used once and directly inside a list pattern. + // (11,24): error CS9002: Slice patterns may only be used once and directly inside a list pattern. // _ = a switch { .. => 0, _ => 0 }; Diagnostic(ErrorCode.ERR_MisplacedSlicePattern, "..").WithLocation(11, 24), - // (12,27): error CS9202: Slice patterns may only be used once and directly inside a list pattern. + // (12,27): error CS9002: Slice patterns may only be used once and directly inside a list pattern. // switch (a) { case ..: break; } Diagnostic(ErrorCode.ERR_MisplacedSlicePattern, "..").WithLocation(12, 27) ); @@ -1011,7 +1293,7 @@ public static void Main() var csCompilation = CreateCompilation(csSource, parseOptions: TestOptions.RegularWithListPatterns, references: new[] { vbCompilation.EmitToImageReference() }); // PROTOTYPE(list-patterns) Unsupported because the lookup fails not that the indexer is static csCompilation.VerifyEmitDiagnostics( - // (6,28): error CS9200: List patterns may not be used for a value of type 'Test1'. + // (6,28): error CS9000: List patterns may not be used for a value of type 'Test1'. // _ = new Test1() is [0]; Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[0]").WithArguments("Test1").WithLocation(6, 28)); } @@ -1049,7 +1331,7 @@ public static void Main() return; } compilation.VerifyEmitDiagnostics( - // (12,28): error CS9200: List patterns may not be used for a value of type 'Test1'. + // (12,28): error CS9000: List patterns may not be used for a value of type 'Test1'. // _ = new Test1() is [0]; Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[0]").WithArguments("Test1").WithLocation(12, 28)); } @@ -1092,7 +1374,7 @@ public static void Main() return; } compilation.VerifyEmitDiagnostics( - // (14,29): error CS9201: Slice patterns may not be used for a value of type 'Test1'. + // (14,29): error CS9001: Slice patterns may not be used for a value of type 'Test1'. // _ = new Test1() is {..var p}; Diagnostic(ErrorCode.ERR_UnsupportedTypeForSlicePattern, "..var p").WithArguments("Test1").WithLocation(14, 29)); } @@ -1702,7 +1984,7 @@ public static void M(T t) where T : I } [Fact] - public void ListPattern_Nullable() + public void ListPattern_NullableValueType() { var source = @" using System; @@ -1926,12 +2208,23 @@ void M(C c) verify(declarations[0], "item", "Missing?"); verify(declarations[1], "rest", "Missing?"); + var localDeclarations = tree.GetRoot().DescendantNodes().OfType().ToArray(); + verify2(localDeclarations[0], "index", "Missing"); + verify2(localDeclarations[1], "range", "Missing"); + void verify(VarPatternSyntax declaration, string name, string expectedType) { var local = (ILocalSymbol)model.GetDeclaredSymbol(declaration.Designation)!; Assert.Equal(name, local.Name); Assert.Equal(expectedType, local.Type.ToTestDisplayString(includeNonNullable: true)); } + + void verify2(VariableDeclaratorSyntax declaration, string name, string expectedType) + { + var local = (ILocalSymbol)model.GetDeclaredSymbol(declaration)!; + Assert.Equal(name, local.Name); + Assert.Equal(expectedType, local.Type.ToTestDisplayString(includeNonNullable: true)); + } } [Fact] @@ -1949,45 +2242,199 @@ public class Missing using System; public class C { - public int Length => 0; + public int Length => 1; public Missing this[Index i] => throw null; public Missing this[Range r] => throw null; - public int this[int i] => throw null; - public int Slice(int i, int j) => throw null; + public int this[int i] => 42; + public int Slice(int i, int j) => 43; } "; var lib2Ref = CreateCompilation(new[] { lib2_cs, TestSources.Index, TestSources.Range }, references: new[] { missingRef }) .EmitToImageReference(); var source = @" -class D +var c = new C(); + +if (c is [var item] && c is [..var slice]) { - void M(C c) + var item2 = c[^1]; + var slice2 = c[..]; + System.Console.Write((item, slice, item2, slice2)); +} +"; + var compilation = CreateCompilation(source, references: new[] { lib2Ref }); + compilation.VerifyEmitDiagnostics(); + CompileAndVerify(compilation, expectedOutput: "(42, 43, 42, 43)"); + } + + [Fact] + public void ListPattern_RangeTypeWithMissingInterface() { - _ = c is [var item]; - _ = c is [..var rest]; - var index = c[^1]; - var range = c[1..^1]; + var missing_cs = @" +public interface IMissing { } +"; + + var range_cs = @" +namespace System; + +public struct Range : IMissing +{ + public Index Start { get; } + public Index End { get; } + public Range(Index start, Index end) => throw null; + public static Range StartAt(Index start) => throw null; + public static Range EndAt(Index end) => throw null; + public static Range All => throw null; +} +"; + + var lib_cs = @" +public class C +{ + public int Length => 0; + public int this[System.Index i] => 0; + public int this[System.Range r] => 0; +} +"; + + var missingComp = CreateCompilation(missing_cs, assemblyName: "missing"); + missingComp.VerifyDiagnostics(); + + var rangeComp = CreateCompilation(new[] { range_cs, TestSources.Index }, references: new[] { missingComp.EmitToImageReference() }, assemblyName: "range"); + rangeComp.VerifyDiagnostics(); + var rangeRef = rangeComp.EmitToImageReference(); + + var libComp = CreateCompilation(lib_cs, references: new[] { rangeRef }, assemblyName: "lib"); + libComp.VerifyDiagnostics(); + + var sources = new[] + { + "_ = ^1;", + "_ = ..;", + "_ = new C() is [var x];", + "_ = new C() is [..var y];", + "_ = new C()[^1];", + "_ = new C()[..];" + }; + + foreach (var source in sources) + { + var comp = CreateCompilation(source, references: new[] { libComp.EmitToImageReference(), rangeRef }); + comp.VerifyDiagnostics(); + var used = comp.GetUsedAssemblyReferences(); + Assert.True(used.Any(r => r.Display == "range")); + } } + + [Fact] + public void ListPattern_ObsoleteLengthAndIndexerAndSlice() + { + var source = @" +_ = new C() is [var x]; // 1, 2 +_ = new C() is [.. var y]; // 3, 4, 5, 6 +new C().Slice(0, 0); // 7 +_ = new C()[^1]; // 8, 9 +_ = new C()[..]; // 10, 11 + +class C +{ + [System.Obsolete] + public int Length => 0; + + [System.Obsolete] + public int this[int i] => 0; + + [System.Obsolete] + public int Slice(int i, int j) => 0; } "; - var compilation = CreateCompilation(source, references: new[] { lib2Ref }, parseOptions: TestOptions.RegularWithListPatterns); - compilation.VerifyEmitDiagnostics( - // (6,18): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. - // _ = c is [var item]; - Diagnostic(ErrorCode.ERR_NoTypeDef, "[var item]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(6, 18), - // (7,18): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. - // _ = c is [..var rest]; - Diagnostic(ErrorCode.ERR_NoTypeDef, "[..var rest]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(7, 18), - // (7,19): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. - // _ = c is [..var rest]; - Diagnostic(ErrorCode.ERR_NoTypeDef, "..var rest").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(7, 19), - // (8,21): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. - // var index = c[^1]; - Diagnostic(ErrorCode.ERR_NoTypeDef, "c[^1]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(8, 21), - // (9,21): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. - // var range = c[1..^1]; - Diagnostic(ErrorCode.ERR_NoTypeDef, "c[1..^1]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(9, 21) + var comp = CreateCompilation(new[] { source, TestSources.Index, TestSources.Range }); + comp.VerifyDiagnostics( + // (2,16): warning CS0612: 'C.Length' is obsolete + // _ = new C() is [var x]; // 1, 2 + Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "[var x]").WithArguments("C.Length").WithLocation(2, 16), + // (2,16): warning CS0612: 'C.this[int]' is obsolete + // _ = new C() is [var x]; // 1, 2 + Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "[var x]").WithArguments("C.this[int]").WithLocation(2, 16), + // (3,16): warning CS0612: 'C.Length' is obsolete + // _ = new C() is [.. var y]; // 3, 4, 5, 6 + Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "[.. var y]").WithArguments("C.Length").WithLocation(3, 16), + // (3,16): warning CS0612: 'C.this[int]' is obsolete + // _ = new C() is [.. var y]; // 3, 4, 5, 6 + Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "[.. var y]").WithArguments("C.this[int]").WithLocation(3, 16), + // (3,17): warning CS0612: 'C.Length' is obsolete + // _ = new C() is [.. var y]; // 3, 4, 5, 6 + Diagnostic(ErrorCode.WRN_DeprecatedSymbol, ".. var y").WithArguments("C.Length").WithLocation(3, 17), + // (3,17): warning CS0612: 'C.Slice(int, int)' is obsolete + // _ = new C() is [.. var y]; // 3, 4, 5, 6 + Diagnostic(ErrorCode.WRN_DeprecatedSymbol, ".. var y").WithArguments("C.Slice(int, int)").WithLocation(3, 17), + // (4,1): warning CS0612: 'C.Slice(int, int)' is obsolete + // new C().Slice(0, 0); // 7 + Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "new C().Slice(0, 0)").WithArguments("C.Slice(int, int)").WithLocation(4, 1), + // (5,5): warning CS0612: 'C.Length' is obsolete + // _ = new C()[^1]; // 8, 9 + Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "new C()[^1]").WithArguments("C.Length").WithLocation(5, 5), + // (5,5): warning CS0612: 'C.this[int]' is obsolete + // _ = new C()[^1]; // 8, 9 + Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "new C()[^1]").WithArguments("C.this[int]").WithLocation(5, 5), + // (6,5): warning CS0612: 'C.Length' is obsolete + // _ = new C()[..]; // 10, 11 + Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "new C()[..]").WithArguments("C.Length").WithLocation(6, 5), + // (6,5): warning CS0612: 'C.Slice(int, int)' is obsolete + // _ = new C()[..]; // 10, 11 + Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "new C()[..]").WithArguments("C.Slice(int, int)").WithLocation(6, 5) + ); + } + + [Fact] + public void ListPattern_IndexAndSliceReturnMissingTypes() + { + var missing_cs = @" +public class Missing { } +"; + + var lib_cs = @" +public class C +{ + public int Length => throw null; + public Missing this[int i] => throw null; + public Missing Slice(int i, int j) => throw null; +} +"; + + var source = @" +_ = new C() is [var x]; // 1 +_ = new C() is [.. var y]; // 2, 3 +new C().Slice(0, 0); // 4 +_ = new C()[^1]; // 5 +_ = new C()[..]; // 6 +"; + var missingComp = CreateCompilation(missing_cs, assemblyName: "missing"); + missingComp.VerifyDiagnostics(); + + var libComp = CreateCompilation(lib_cs, references: new[] { missingComp.EmitToImageReference() }, assemblyName: "lib"); + libComp.VerifyDiagnostics(); + + var comp = CreateCompilation(new[] { source, TestSources.Index, TestSources.Range }, references: new[] { libComp.EmitToImageReference() }); + comp.VerifyDiagnostics( + // (2,16): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // _ = new C() is [var x]; // 1 + Diagnostic(ErrorCode.ERR_NoTypeDef, "[var x]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(2, 16), + // (3,16): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // _ = new C() is [.. var y]; // 2, 3 + Diagnostic(ErrorCode.ERR_NoTypeDef, "[.. var y]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(3, 16), + // (3,17): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // _ = new C() is [.. var y]; // 2, 3 + Diagnostic(ErrorCode.ERR_NoTypeDef, ".. var y").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(3, 17), + // (4,1): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // new C().Slice(0, 0); // 4 + Diagnostic(ErrorCode.ERR_NoTypeDef, "new C().Slice").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(4, 1), + // (5,5): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // _ = new C()[^1]; // 5 + Diagnostic(ErrorCode.ERR_NoTypeDef, "new C()[^1]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(5, 5), + // (6,5): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // _ = new C()[..]; // 6 + Diagnostic(ErrorCode.ERR_NoTypeDef, "new C()[..]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(6, 5) ); } @@ -2150,7 +2597,7 @@ public void M() // (4,17): error CS0547: 'C.Length': property or indexer cannot have void type // public void Length => throw null; Diagnostic(ErrorCode.ERR_PropertyCantHaveVoidType, "Length").WithArguments("C.Length").WithLocation(4, 17), - // (8,21): error CS9200: List patterns may not be used for a value of type 'C'. + // (8,21): error CS9000: List patterns may not be used for a value of type 'C'. // _ = this is [1]; Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[1]").WithArguments("C").WithLocation(8, 21) ); @@ -2174,7 +2621,7 @@ public void M() "; var compilation = CreateCompilation(source); compilation.VerifyEmitDiagnostics( - // (9,21): error CS9200: List patterns may not be used for a value of type 'C'. + // (9,21): error CS9000: List patterns may not be used for a value of type 'C'. // _ = this is [1]; Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[1]").WithArguments("C").WithLocation(9, 21), // (10,18): error CS0518: Predefined type 'System.Index' is not defined or imported @@ -2210,13 +2657,13 @@ public void M() "; var compilation = CreateCompilation(source); compilation.VerifyEmitDiagnostics( - // (11,22): error CS9201: Slice patterns may not be used for a value of type 'C'. + // (11,22): error CS9001: Slice patterns may not be used for a value of type 'C'. // _ = this is [.._]; Diagnostic(ErrorCode.ERR_UnsupportedTypeForSlicePattern, ".._").WithArguments("C").WithLocation(11, 22), - // (12,22): error CS9201: Slice patterns may not be used for a value of type 'C'. + // (12,22): error CS9001: Slice patterns may not be used for a value of type 'C'. // _ = this is [..var unused]; Diagnostic(ErrorCode.ERR_UnsupportedTypeForSlicePattern, "..var unused").WithArguments("C").WithLocation(12, 22), - // (13,22): error CS9201: Slice patterns may not be used for a value of type 'C'. + // (13,22): error CS9001: Slice patterns may not be used for a value of type 'C'. // if (this is [..var used]) Diagnostic(ErrorCode.ERR_UnsupportedTypeForSlicePattern, "..var used").WithArguments("C").WithLocation(13, 22) ); @@ -2278,13 +2725,13 @@ public void M() "; var compilation = CreateCompilation(new[] { source, TestSources.Index }); compilation.VerifyEmitDiagnostics( - // (10,29): error CS9200: List patterns may not be used for a value of type 'C'. + // (10,29): error CS9000: List patterns may not be used for a value of type 'C'. // if (new C() is [var item]) // 1 Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[var item]").WithArguments("C").WithLocation(10, 29), // (12,26): error CS1503: Argument 1: cannot convert from 'System.Index' to 'int' // _ = new C()[^1]; // 2 Diagnostic(ErrorCode.ERR_BadArgType, "^1").WithArguments("1", "System.Index", "int").WithLocation(12, 26), - // (14,30): error CS9200: List patterns may not be used for a value of type 'C'. + // (14,30): error CS9000: List patterns may not be used for a value of type 'C'. // if (new C() is [var item2]) // 3 Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[var item2]").WithArguments("C").WithLocation(14, 30), // (16,27): error CS1503: Argument 1: cannot convert from 'System.Index' to 'int?' @@ -2882,10 +3329,10 @@ public void M() "; var compilation = CreateCompilationWithIL(source, il); compilation.VerifyEmitDiagnostics( - // (6,24): error CS9200: List patterns may not be used for a value of type 'C'. + // (6,24): error CS9000: List patterns may not be used for a value of type 'C'. // _ = new C() is [var item, ..var rest]; Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[var item, ..var rest]").WithArguments("C").WithLocation(6, 24), - // (6,35): error CS9201: Slice patterns may not be used for a value of type 'C'. + // (6,35): error CS9001: Slice patterns may not be used for a value of type 'C'. // _ = new C() is [var item, ..var rest]; Diagnostic(ErrorCode.ERR_UnsupportedTypeForSlicePattern, "..var rest").WithArguments("C").WithLocation(6, 35) ); @@ -3100,10 +3547,10 @@ public void M() "; var compilation = CreateCompilationWithIL(source, il); compilation.VerifyEmitDiagnostics( - // (6,24): error CS9200: List patterns may not be used for a value of type 'C'. + // (6,24): error CS9000: List patterns may not be used for a value of type 'C'. // _ = new C() is [var item, ..var rest]; Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[var item, ..var rest]").WithArguments("C").WithLocation(6, 24), - // (6,35): error CS9201: Slice patterns may not be used for a value of type 'C'. + // (6,35): error CS9001: Slice patterns may not be used for a value of type 'C'. // _ = new C() is [var item, ..var rest]; Diagnostic(ErrorCode.ERR_UnsupportedTypeForSlicePattern, "..var rest").WithArguments("C").WithLocation(6, 35) ); @@ -3340,7 +3787,7 @@ void M(dynamic d) "; var compilation = CreateCompilation(source); compilation.VerifyEmitDiagnostics( - // (6,18): error CS9200: List patterns may not be used for a value of type 'dynamic'. + // (6,18): error CS9000: List patterns may not be used for a value of type 'dynamic'. // _ = d is [_]; Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[_]").WithArguments("dynamic").WithLocation(6, 18) ); @@ -3383,13 +3830,13 @@ void M(C c) "; var compilation = CreateCompilation(source, references: new[] { lib2Ref }); compilation.VerifyEmitDiagnostics( - // (6,18): error CS9200: List patterns may not be used for a value of type 'C'. + // (6,18): error CS9000: List patterns may not be used for a value of type 'C'. // _ = c is [var item]; Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[var item]").WithArguments("C").WithLocation(6, 18), - // (7,18): error CS9200: List patterns may not be used for a value of type 'C'. + // (7,18): error CS9000: List patterns may not be used for a value of type 'C'. // _ = c is [..var rest]; Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[..var rest]").WithArguments("C").WithLocation(7, 18), - // (7,19): error CS9201: Slice patterns may not be used for a value of type 'C'. + // (7,19): error CS9001: Slice patterns may not be used for a value of type 'C'. // _ = c is [..var rest]; Diagnostic(ErrorCode.ERR_UnsupportedTypeForSlicePattern, "..var rest").WithArguments("C").WithLocation(7, 19), // (8,21): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. @@ -3424,10 +3871,10 @@ void M() // (5,21): error CS0631: ref and out are not valid in this context // public int this[ref int i] => 0; Diagnostic(ErrorCode.ERR_IllegalRefParam, "ref").WithLocation(5, 21), - // (10,21): error CS9200: List patterns may not be used for a value of type 'C'. + // (10,21): error CS9000: List patterns may not be used for a value of type 'C'. // _ = this is [var item, ..var rest]; Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[var item, ..var rest]").WithArguments("C").WithLocation(10, 21), - // (10,32): error CS9201: Slice patterns may not be used for a value of type 'C'. + // (10,32): error CS9001: Slice patterns may not be used for a value of type 'C'. // _ = this is [var item, ..var rest]; Diagnostic(ErrorCode.ERR_UnsupportedTypeForSlicePattern, "..var rest").WithArguments("C").WithLocation(10, 32), // (11,18): error CS0518: Predefined type 'System.Index' is not defined or imported @@ -3481,10 +3928,10 @@ void M() // (7,21): error CS0631: ref and out are not valid in this context // public int this[ref Range r] => 0; Diagnostic(ErrorCode.ERR_IllegalRefParam, "ref").WithLocation(7, 21), - // (11,21): error CS9200: List patterns may not be used for a value of type 'C'. + // (11,21): error CS9000: List patterns may not be used for a value of type 'C'. // _ = this is [var item, ..var rest]; Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[var item, ..var rest]").WithArguments("C").WithLocation(11, 21), - // (11,32): error CS9201: Slice patterns may not be used for a value of type 'C'. + // (11,32): error CS9001: Slice patterns may not be used for a value of type 'C'. // _ = this is [var item, ..var rest]; Diagnostic(ErrorCode.ERR_UnsupportedTypeForSlicePattern, "..var rest").WithArguments("C").WithLocation(11, 32), // (12,18): error CS1620: Argument 1 must be passed with the 'ref' keyword @@ -3516,10 +3963,10 @@ void M() "; var compilation = CreateCompilation(source); compilation.VerifyEmitDiagnostics( - // (10,21): error CS9200: List patterns may not be used for a value of type 'C'. + // (10,21): error CS9000: List patterns may not be used for a value of type 'C'. // _ = this is [var item, ..var rest]; Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[var item, ..var rest]").WithArguments("C").WithLocation(10, 21), - // (10,32): error CS9201: Slice patterns may not be used for a value of type 'C'. + // (10,32): error CS9001: Slice patterns may not be used for a value of type 'C'. // _ = this is [var item, ..var rest]; Diagnostic(ErrorCode.ERR_UnsupportedTypeForSlicePattern, "..var rest").WithArguments("C").WithLocation(10, 32), // (11,18): error CS0518: Predefined type 'System.Index' is not defined or imported @@ -3772,7 +4219,7 @@ class C }"; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( - // (2,17): error CS9201: Slice patterns may not be used for a value of type 'C'. + // (2,17): error CS9001: Slice patterns may not be used for a value of type 'C'. // _ = new C() is [..var y]; Diagnostic(ErrorCode.ERR_UnsupportedTypeForSlicePattern, "..var y").WithArguments("C").WithLocation(2, 17) ); @@ -4069,7 +4516,7 @@ class C }"; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( - // (4,5): error CS9200: List patterns may not be used for a value of type 'C'. + // (4,5): error CS9000: List patterns may not be used for a value of type 'C'. // [..] => 1, Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[..]").WithArguments("C").WithLocation(4, 5), // (7,13): error CS0518: Predefined type 'System.Index' is not defined or imported @@ -4099,7 +4546,7 @@ class C }"; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( - // (4,5): error CS9200: List patterns may not be used for a value of type 'C'. + // (4,5): error CS9000: List patterns may not be used for a value of type 'C'. // [..] => 1, Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[..]").WithArguments("C").WithLocation(4, 5), // (7,13): error CS0518: Predefined type 'System.Index' is not defined or imported @@ -5268,4 +5715,167 @@ .locals init (C V_0, IL_0068: ret }"); } + + [Fact] + public void ListPattern_LengthAndCountAreOrthogonal() + { + var source = @" +_ = new C() switch +{ + [] => 0, + { Length: 1 } => 0, + { Count: > 1 } => 0 +}; + +class C +{ + public int this[System.Index i] => 1; + public int Length => 1; + public int Count => 1; +} +"; + var compilation = CreateCompilationWithIndexAndRange(source); + compilation.VerifyEmitDiagnostics( + // (2,13): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{ Length: 2, Count: 0 }' is not covered. + // _ = new C() switch + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("{ Length: 2, Count: 0 }").WithLocation(2, 13) + ); + } + + [Fact] + public void ListPattern_LengthFieldNotApplicable() + { + var source = @" +_ = new C() is []; +_ = new C()[^1]; + +class C +{ + public int this[int i] => 1; + public int Length = 0; +} +"; + var compilation = CreateCompilationWithIndex(source); + compilation.VerifyEmitDiagnostics( + // (2,16): error CS9000: List patterns may not be used for a value of type 'C'. + // _ = new C() is []; + Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[]").WithArguments("C").WithLocation(2, 16), + // (3,13): error CS1503: Argument 1: cannot convert from 'System.Index' to 'int' + // _ = new C()[^1]; + Diagnostic(ErrorCode.ERR_BadArgType, "^1").WithArguments("1", "System.Index", "int").WithLocation(3, 13) + ); + } + + [Fact] + public void ListPattern_IndexAndRangeNotNecessary() + { + var source = @" +class C +{ + public int this[int i] => 1; + public int Length => 0; + public int Slice(int i, int j) => 0; + + void M() + { + _ = this is []; + _ = this is [.. var x]; + } +} +"; + + var compilation = CreateCompilation(source); + compilation.MakeTypeMissing(WellKnownType.System_Index); + compilation.MakeTypeMissing(WellKnownType.System_Range); + + var verifier = CompileAndVerify(compilation); + verifier.VerifyDiagnostics(); + // Note: no Index or Range involved + verifier.VerifyIL("C.M", @" +{ + // Code size 47 (0x2f) + .maxstack 4 + .locals init (C V_0, + int V_1) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: brfalse.s IL_0010 + IL_0005: ldloc.0 + IL_0006: callvirt ""int C.Length.get"" + IL_000b: ldc.i4.0 + IL_000c: ceq + IL_000e: br.s IL_0011 + IL_0010: ldc.i4.0 + IL_0011: pop + IL_0012: ldarg.0 + IL_0013: stloc.0 + IL_0014: ldloc.0 + IL_0015: brfalse.s IL_002c + IL_0017: ldloc.0 + IL_0018: callvirt ""int C.Length.get"" + IL_001d: stloc.1 + IL_001e: ldloc.0 + IL_001f: ldc.i4.0 + IL_0020: ldloc.1 + IL_0021: ldc.i4.0 + IL_0022: sub + IL_0023: callvirt ""int C.Slice(int, int)"" + IL_0028: pop + IL_0029: ldc.i4.1 + IL_002a: br.s IL_002d + IL_002c: ldc.i4.0 + IL_002d: pop + IL_002e: ret +} +"); + } + + [Fact] + public void ListPattern_StaticIndexers() + { + var source = @" +_ = new C() is [var x, .. var y]; + +class C +{ + public int Length => 0; + public static int this[System.Index i] => 0; + public static int this[System.Range r] => 0; +} +"; + var comp = CreateCompilation(new[] { source, TestSources.Index, TestSources.Range }); + comp.VerifyDiagnostics( + // (7,23): error CS0106: The modifier 'static' is not valid for this item + // public static int this[System.Index i] => 0; + Diagnostic(ErrorCode.ERR_BadMemberFlag, "this").WithArguments("static").WithLocation(7, 23), + // (8,23): error CS0106: The modifier 'static' is not valid for this item + // public static int this[System.Range r] => 0; + Diagnostic(ErrorCode.ERR_BadMemberFlag, "this").WithArguments("static").WithLocation(8, 23) + ); + } + + [Fact] + public void ListPattern_SetOnlyIndexers() + { + var source = @" +_ = new C() is [var x, .. var y]; + +class C +{ + public int Length { set { } } + public int this[System.Index i] { set { } } + public int this[System.Range r] { set { } } +} +"; + var comp = CreateCompilation(new[] { source, TestSources.Index, TestSources.Range }); + comp.VerifyEmitDiagnostics( + // (2,16): error CS9000: List patterns may not be used for a value of type 'C'. + // _ = new C() is [var x, .. var y]; + Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[var x, .. var y]").WithArguments("C").WithLocation(2, 16), + // (2,24): error CS9001: Slice patterns may not be used for a value of type 'C'. + // _ = new C() is [var x, .. var y]; + Diagnostic(ErrorCode.ERR_UnsupportedTypeForSlicePattern, ".. var y").WithArguments("C").WithLocation(2, 24) + ); + } } diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests_ListPatterns.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests_ListPatterns.cs index e75208cc61e6..dd6a6e6ccdf7 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests_ListPatterns.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests_ListPatterns.cs @@ -11,9 +11,6 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests { public class PatternParsingTests_ListPatterns : ParsingTests { - // PROTOTYPE(list-patterns) - private static CSharpParseOptions RegularWithoutListPatterns => TestOptions.Regular9; - private new void UsingExpression(string text, params DiagnosticDescription[] expectedErrors) { UsingExpression(text, options: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Preview), expectedErrors); @@ -27,83 +24,57 @@ public PatternParsingTests_ListPatterns(ITestOutputHelper output) : base(output) public void ListPattern_00() { UsingExpression(@"c is [[]]"); - verify(); - - UsingExpression(@"c is [[]]", RegularWithoutListPatterns, - // (1,6): error CS8652: The feature 'list pattern' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. - // c is [[]] - Diagnostic(ErrorCode.ERR_FeatureInPreview, "[[]]").WithArguments("list pattern").WithLocation(1, 6), - // (1,7): error CS8652: The feature 'list pattern' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. - // c is [[]] - Diagnostic(ErrorCode.ERR_FeatureInPreview, "[]").WithArguments("list pattern").WithLocation(1, 7)); - verify(); - - void verify() + + N(SyntaxKind.IsPatternExpression); { - N(SyntaxKind.IsPatternExpression); + N(SyntaxKind.IdentifierName); { - N(SyntaxKind.IdentifierName); - { - N(SyntaxKind.IdentifierToken, "c"); - } - N(SyntaxKind.IsKeyword); + N(SyntaxKind.IdentifierToken, "c"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.ListPattern); + { + N(SyntaxKind.OpenBracketToken); N(SyntaxKind.ListPattern); { N(SyntaxKind.OpenBracketToken); - N(SyntaxKind.ListPattern); - { - N(SyntaxKind.OpenBracketToken); - N(SyntaxKind.CloseBracketToken); - } N(SyntaxKind.CloseBracketToken); } + N(SyntaxKind.CloseBracketToken); } - EOF(); } + EOF(); } [Fact] public void ListPattern_01() { UsingExpression(@"c is [[],] v"); - verify(); - - UsingExpression(@"c is [[],] v", RegularWithoutListPatterns, - // (1,6): error CS8652: The feature 'list pattern' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. - // c is [[],] - Diagnostic(ErrorCode.ERR_FeatureInPreview, "[[],] v").WithArguments("list pattern").WithLocation(1, 6), - // (1,7): error CS8652: The feature 'list pattern' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. - // c is [[],] - Diagnostic(ErrorCode.ERR_FeatureInPreview, "[]").WithArguments("list pattern").WithLocation(1, 7)); - verify(); - - void verify() + + N(SyntaxKind.IsPatternExpression); { - N(SyntaxKind.IsPatternExpression); + N(SyntaxKind.IdentifierName); { - N(SyntaxKind.IdentifierName); - { - N(SyntaxKind.IdentifierToken, "c"); - } - N(SyntaxKind.IsKeyword); + N(SyntaxKind.IdentifierToken, "c"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.ListPattern); + { + N(SyntaxKind.OpenBracketToken); N(SyntaxKind.ListPattern); { N(SyntaxKind.OpenBracketToken); - N(SyntaxKind.ListPattern); - { - N(SyntaxKind.OpenBracketToken); - N(SyntaxKind.CloseBracketToken); - } - N(SyntaxKind.CommaToken); N(SyntaxKind.CloseBracketToken); - N(SyntaxKind.SingleVariableDesignation); - { - N(SyntaxKind.IdentifierToken, "v"); - } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.CloseBracketToken); + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "v"); } } - EOF(); } + EOF(); } [Fact] @@ -456,35 +427,25 @@ public void NoRegressionOnArrayTypePattern_02() public void SlicePattern_01() { UsingExpression(@"c is [..]"); - verify(); - - UsingExpression(@"c is [..]", RegularWithoutListPatterns, - // (1,6): error CS8652: The feature 'list pattern' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. - // c is [..] - Diagnostic(ErrorCode.ERR_FeatureInPreview, "[..]").WithArguments("list pattern").WithLocation(1, 6)); - verify(); - void verify() + N(SyntaxKind.IsPatternExpression); { - N(SyntaxKind.IsPatternExpression); + N(SyntaxKind.IdentifierName); { - N(SyntaxKind.IdentifierName); - { - N(SyntaxKind.IdentifierToken, "c"); - } - N(SyntaxKind.IsKeyword); - N(SyntaxKind.ListPattern); + N(SyntaxKind.IdentifierToken, "c"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.ListPattern); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.SlicePattern); { - N(SyntaxKind.OpenBracketToken); - N(SyntaxKind.SlicePattern); - { - N(SyntaxKind.DotDotToken); - } - N(SyntaxKind.CloseBracketToken); + N(SyntaxKind.DotDotToken); } + N(SyntaxKind.CloseBracketToken); } - EOF(); } + EOF(); } [Fact] From 00e6ff2d61b3ac1133c9a4bb08e69b92b7798b38 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Tue, 26 Oct 2021 12:04:42 -0700 Subject: [PATCH 02/29] Address feedback --- .../CSharp/Portable/Binder/Binder_Patterns.cs | 12 ++++++------ src/Compilers/CSharp/Portable/Errors/ErrorCode.cs | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs index 94ddeab5f62b..ee088f642fbf 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs @@ -406,15 +406,15 @@ private bool TryBindIndexerForPattern( } analyzedArguments.Free(); indexerGroup.Free(); + goto done; } lookupResult.Clear(); } - else - { - // If the argType is missing, we will fallback to the implicit indexer support. - found = TryLookupLengthOrCount(syntax, receiverType, lookupResult, out lengthProperty, diagnostics) && - TryFindIndexOrRangeIndexerFallback(syntax, lookupResult, receiverOpt: null, receiverType, argIsIndex, out patternSymbol, diagnostics); - } + + // If the argType is missing or the indexer lookup has failed, we will fallback to the implicit indexer support. + found = TryLookupLengthOrCount(syntax, receiverType, lookupResult, out lengthProperty, diagnostics) && + TryFindIndexOrRangeIndexerFallback(syntax, lookupResult, receiverOpt: null, receiverType, argIsIndex, out patternSymbol, diagnostics); +done: if (found) { diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index de0233405de3..e8ef05224752 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1932,6 +1932,7 @@ internal enum ErrorCode #endregion diagnostics introduced for C# 9.0 #region diagnostics introduced for C# 10.0 + ERR_InheritingFromRecordWithSealedToString = 8912, ERR_HiddenPositionalMember = 8913, ERR_GlobalUsingInNamespace = 8914, From 8d16d553597b1eedcab51c6f78030d5e8f7fb005 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Mon, 1 Nov 2021 09:25:42 -0700 Subject: [PATCH 03/29] Rename --- .../Portable/Binder/Binder.ValueChecks.cs | 50 +++++------ .../Portable/Binder/Binder_Expressions.cs | 22 ++--- .../CSharp/Portable/Binder/Binder_Patterns.cs | 14 +-- .../Portable/Binder/Binder_Statements.cs | 6 +- .../CSharp/Portable/BoundTree/BoundNodes.xml | 8 +- .../CSharp/Portable/BoundTree/Expression.cs | 2 +- .../Compilation/CSharpSemanticModel.cs | 10 +-- .../Portable/FlowAnalysis/AbstractFlowPass.cs | 8 +- .../Portable/FlowAnalysis/NullableWalker.cs | 6 +- .../Generated/BoundNodes.xml.Generated.cs | 86 +++++++++---------- .../DiagnosticsPass_ExpressionTrees.cs | 4 +- .../Lowering/LocalRewriter/LocalRewriter.cs | 10 +-- .../LocalRewriter_AssignmentOperator.cs | 6 +- ...ocalRewriter_CompoundAssignmentOperator.cs | 20 ++--- .../LocalRewriter_IndexerAccess.cs | 18 ++-- .../Operations/CSharpOperationFactory.cs | 2 +- .../Semantics/PatternMatchingTests3.cs | 14 +-- 17 files changed, 143 insertions(+), 143 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index d9cf7a1a4f78..3039298668ab 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -398,16 +398,16 @@ internal bool CheckValueKind(SyntaxNode node, BoundExpression expr, BindValueKin case BoundKind.IndexerAccess: return CheckPropertyValueKind(node, expr, valueKind, checkingReceiver, diagnostics); - case BoundKind.IndexOrRangeIndexerFallbackAccess: - var fallbackIndexer = (BoundIndexOrRangeIndexerFallbackAccess)expr; - if (fallbackIndexer.PatternSymbol.Kind == SymbolKind.Property) + case BoundKind.IndexOrRangeImplicitIndexerAccess: + var implicitIndexer = (BoundIndexOrRangeImplicitIndexerAccess)expr; + if (implicitIndexer.UnderlyingIndexerOrSliceSymbol.Kind == SymbolKind.Property) { - // If this is an Index indexer, PatternSymbol should be a property, pointing to the - // fallback indexer. If it's a Range access, it will be a method, pointing to a Slice method + // If this is an Index indexer, UnderlyingIndexerOrSliceSymbol should be a property, pointing to the + // implicit indexer. If it's a Range access, it will be a method, pointing to a Slice method // and it's handled below as part of invocations. return CheckPropertyValueKind(node, expr, valueKind, checkingReceiver, diagnostics); } - Debug.Assert(fallbackIndexer.PatternSymbol.Kind == SymbolKind.Method); + Debug.Assert(implicitIndexer.UnderlyingIndexerOrSliceSymbol.Kind == SymbolKind.Method); break; case BoundKind.EventAccess: @@ -589,12 +589,12 @@ internal bool CheckValueKind(SyntaxNode node, BoundExpression expr, BindValueKin checkingReceiver, diagnostics); - case BoundKind.IndexOrRangeIndexerFallbackAccess: - var patternIndexer = (BoundIndexOrRangeIndexerFallbackAccess)expr; - // If we got here this should be a fallback indexer taking a Range, + case BoundKind.IndexOrRangeImplicitIndexerAccess: + var patternIndexer = (BoundIndexOrRangeImplicitIndexerAccess)expr; + // If we got here this should be an implicit indexer taking a Range, // meaning that the pattern symbol must be a method (either Slice or Substring) return CheckMethodReturnValueKind( - (MethodSymbol)patternIndexer.PatternSymbol, + (MethodSymbol)patternIndexer.UnderlyingIndexerOrSliceSymbol, patternIndexer.Syntax, node, valueKind, @@ -2347,12 +2347,12 @@ internal static bool CheckRefEscape(SyntaxNode node, BoundExpression expr, uint diagnostics, isRefEscape: true); - case BoundKind.IndexOrRangeIndexerFallbackAccess: - var fallbackIndexer = (BoundIndexOrRangeIndexerFallbackAccess)expr; + case BoundKind.IndexOrRangeImplicitIndexerAccess: + var implicitIndexer = (BoundIndexOrRangeImplicitIndexerAccess)expr; RefKind refKind; ImmutableArray parameters; - switch (fallbackIndexer.PatternSymbol) + switch (implicitIndexer.UnderlyingIndexerOrSliceSymbol) { case PropertySymbol p: refKind = p.RefKind; @@ -2372,11 +2372,11 @@ internal static bool CheckRefEscape(SyntaxNode node, BoundExpression expr, uint } return CheckInvocationEscape( - fallbackIndexer.Syntax, - fallbackIndexer.PatternSymbol, - fallbackIndexer.Receiver, + implicitIndexer.Syntax, + implicitIndexer.UnderlyingIndexerOrSliceSymbol, + implicitIndexer.Receiver, parameters, - ImmutableArray.Create(fallbackIndexer.Argument), + ImmutableArray.Create(implicitIndexer.Argument), default, default, checkingReceiver, @@ -2618,17 +2618,17 @@ internal static uint GetValEscape(BoundExpression expr, uint scopeOfTheContainin scopeOfTheContainingExpression, isRefEscape: false); - case BoundKind.IndexOrRangeIndexerFallbackAccess: - var patternIndexer = (BoundIndexOrRangeIndexerFallbackAccess)expr; - var parameters = patternIndexer.PatternSymbol switch + case BoundKind.IndexOrRangeImplicitIndexerAccess: + var patternIndexer = (BoundIndexOrRangeImplicitIndexerAccess)expr; + var parameters = patternIndexer.UnderlyingIndexerOrSliceSymbol switch { PropertySymbol p => p.Parameters, MethodSymbol m => m.Parameters, - _ => throw ExceptionUtilities.UnexpectedValue(patternIndexer.PatternSymbol) + _ => throw ExceptionUtilities.UnexpectedValue(patternIndexer.UnderlyingIndexerOrSliceSymbol) }; return GetInvocationEscapeScope( - patternIndexer.PatternSymbol, + patternIndexer.UnderlyingIndexerOrSliceSymbol, patternIndexer.Receiver, parameters, default, @@ -3040,9 +3040,9 @@ internal static bool CheckValEscape(SyntaxNode node, BoundExpression expr, uint diagnostics, isRefEscape: false); - case BoundKind.IndexOrRangeIndexerFallbackAccess: - var patternIndexer = (BoundIndexOrRangeIndexerFallbackAccess)expr; - var patternSymbol = patternIndexer.PatternSymbol; + case BoundKind.IndexOrRangeImplicitIndexerAccess: + var patternIndexer = (BoundIndexOrRangeImplicitIndexerAccess)expr; + var patternSymbol = patternIndexer.UnderlyingIndexerOrSliceSymbol; var parameters = patternSymbol switch { PropertySymbol p => p.Parameters, diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index dc16e7339a08..6fec83659952 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -7740,7 +7740,7 @@ private BoundExpression BindIndexerAccess(ExpressionSyntax node, BoundExpression if (!lookupResult.IsMultiViable) { - if (TryBindIndexOrRangeIndexerFallback( + if (TryBindIndexOrRangeImplicitIndexer( node, expr, analyzedArguments, @@ -7912,7 +7912,7 @@ private BoundExpression BindIndexerOrIndexedPropertyAccess( if (!analyzedArguments.HasErrors) { - if (TryBindIndexOrRangeIndexerFallback( + if (TryBindIndexOrRangeImplicitIndexer( syntax, receiverOpt, analyzedArguments, @@ -8022,12 +8022,12 @@ private BoundExpression BindIndexerOrIndexedPropertyAccess( } #nullable enable - private bool TryBindIndexOrRangeIndexerFallback( + private bool TryBindIndexOrRangeImplicitIndexer( SyntaxNode syntax, BoundExpression? receiverOpt, AnalyzedArguments arguments, BindingDiagnosticBag diagnostics, - [NotNullWhen(true)] out BoundIndexOrRangeIndexerFallbackAccess? indexerFallbackAccess) + [NotNullWhen(true)] out BoundIndexOrRangeImplicitIndexerAccess? indexerFallbackAccess) { indexerFallbackAccess = null; @@ -8055,13 +8055,13 @@ private bool TryBindIndexOrRangeIndexerFallback( } bool argIsIndex = argIsIndexNotRange.Value(); - if (!TryFindIndexOrRangeIndexerFallback(syntax, receiverOpt, receiverType, argIsIndex: argIsIndex, + if (!TryFindIndexOrRangeImplicitIndexer(syntax, receiverOpt, receiverType, argIsIndex: argIsIndex, out PropertySymbol? lengthOrCountProperty, out Symbol? patternSymbol, diagnostics)) { return false; } - indexerFallbackAccess = new BoundIndexOrRangeIndexerFallbackAccess( + indexerFallbackAccess = new BoundIndexOrRangeImplicitIndexerAccess( syntax, receiverOpt, lengthOrCountProperty, @@ -8097,9 +8097,9 @@ void checkWellKnown(WellKnownMember member) } /// - /// Finds pattern-based fallback indexer and Length/Count property. + /// Finds pattern-based implicit indexer and Length/Count property. /// - private bool TryFindIndexOrRangeIndexerFallback( + private bool TryFindIndexOrRangeImplicitIndexer( SyntaxNode syntax, BoundExpression? receiverOpt, TypeSymbol receiverType, @@ -8121,7 +8121,7 @@ private bool TryFindIndexOrRangeIndexerFallback( var lookupResult = LookupResult.GetInstance(); if (TryLookupLengthOrCount(syntax, receiverType, lookupResult, out lengthOrCountProperty, diagnostics) && - TryFindIndexOrRangeIndexerFallback(syntax, lookupResult, receiverOpt, receiverType, argIsIndex, out patternSymbol, diagnostics)) + TryFindIndexOrRangeImplicitIndexer(syntax, lookupResult, receiverOpt, receiverType, argIsIndex, out patternSymbol, diagnostics)) { var lengthAccess = new BoundPropertyAccess(syntax, receiverOpt, lengthOrCountProperty, LookupResultKind.Viable, lengthOrCountProperty.Type); CheckPropertyValueKind(syntax, lengthAccess, BindValueKind.RValue, checkingReceiver: false, diagnostics); @@ -8136,11 +8136,11 @@ private bool TryFindIndexOrRangeIndexerFallback( } /// - /// Finds pattern-based fallback indexer: + /// Finds pattern-based implicit indexer: /// - for Index indexer, this will find `this[int]`. /// - for Range indexer, this will find `Slice(int, int)` or `string.Substring(int, int)`. /// - private bool TryFindIndexOrRangeIndexerFallback( + private bool TryFindIndexOrRangeImplicitIndexer( SyntaxNode syntax, LookupResult lookupResult, BoundExpression? receiverOpt, diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs index f6bff2be4c66..70d7d6106b0c 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs @@ -376,7 +376,7 @@ private bool TryBindIndexerForPattern( lookupResult.Clear(); var analyzedArguments = AnalyzedArguments.GetInstance(); - analyzedArguments.Arguments.Add(new BoundIndexOrRangeIndexerFallbackValuePlaceholder(syntax, argType)); + analyzedArguments.Arguments.Add(new BoundIndexOrRangeImplicitIndexerValuePlaceholder(syntax, argType)); var receiver = new BoundImplicitReceiver(syntax, receiverType); BoundExpression boundAccess = BindIndexerOrIndexedPropertyAccess(syntax, receiver, indexerGroup, analyzedArguments, bindingDiagnostics); switch (boundAccess) @@ -395,9 +395,9 @@ private bool TryBindIndexerForPattern( } break; - case BoundIndexOrRangeIndexerFallbackAccess boundIndexOrRangeIndexerFallbackAccess: - lengthProperty = boundIndexOrRangeIndexerFallbackAccess.LengthOrCountProperty; - patternSymbol = boundIndexOrRangeIndexerFallbackAccess.PatternSymbol; + case BoundIndexOrRangeImplicitIndexerAccess boundIndexOrRangeImplicitIndexerAccess: + lengthProperty = boundIndexOrRangeImplicitIndexerAccess.LengthOrCountProperty; + patternSymbol = boundIndexOrRangeImplicitIndexerAccess.UnderlyingIndexerOrSliceSymbol; found = true; break; @@ -413,7 +413,7 @@ private bool TryBindIndexerForPattern( // If the argType is missing or the indexer lookup has failed, we will fallback to the implicit indexer support. found = TryLookupLengthOrCount(syntax, receiverType, lookupResult, out lengthProperty, diagnostics) && - TryFindIndexOrRangeIndexerFallback(syntax, lookupResult, receiverOpt: null, receiverType, argIsIndex, out patternSymbol, diagnostics); + TryFindIndexOrRangeImplicitIndexer(syntax, lookupResult, receiverOpt: null, receiverType, argIsIndex, out patternSymbol, diagnostics); done: if (found) @@ -425,8 +425,8 @@ private bool TryBindIndexerForPattern( { if (patternSymbol is not null) { - var indexerFallbackAccess = new BoundIndexOrRangeIndexerFallbackAccess(syntax, new BoundImplicitReceiver(syntax, receiverType), - lengthProperty, patternSymbol, argument: new BoundIndexOrRangeIndexerFallbackValuePlaceholder(syntax, argType), patternSymbol.GetTypeOrReturnType().Type); + var indexerFallbackAccess = new BoundIndexOrRangeImplicitIndexerAccess(syntax, new BoundImplicitReceiver(syntax, receiverType), + lengthProperty, patternSymbol, argument: new BoundIndexOrRangeImplicitIndexerValuePlaceholder(syntax, argType), patternSymbol.GetTypeOrReturnType().Type); if (!CheckValueKind(syntax, indexerFallbackAccess, BindValueKind.RValue, checkingReceiver: false, bindingDiagnostics)) { diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index 6c2058c5874f..67456502a558 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -1556,11 +1556,11 @@ private static PropertySymbol GetPropertySymbol(BoundExpression expr, out BoundE propertySymbol = indexerAccess.Indexer; } break; - case BoundKind.IndexOrRangeIndexerFallbackAccess: + case BoundKind.IndexOrRangeImplicitIndexerAccess: { - var patternIndexer = (BoundIndexOrRangeIndexerFallbackAccess)expr; + var patternIndexer = (BoundIndexOrRangeImplicitIndexerAccess)expr; receiver = patternIndexer.Receiver; - propertySymbol = (PropertySymbol)patternIndexer.PatternSymbol; + propertySymbol = (PropertySymbol)patternIndexer.UnderlyingIndexerOrSliceSymbol; } break; default: diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml index bc687654eff6..27d310249b02 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml @@ -131,10 +131,10 @@ - + @@ -2009,12 +2009,12 @@ - + - + diff --git a/src/Compilers/CSharp/Portable/BoundTree/Expression.cs b/src/Compilers/CSharp/Portable/BoundTree/Expression.cs index 7ac5366965dc..124fb18b4c3d 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/Expression.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/Expression.cs @@ -175,7 +175,7 @@ internal partial class BoundPassByCopy protected override ImmutableArray Children => ImmutableArray.Create(this.Expression); } - internal partial class BoundIndexOrRangeIndexerFallbackAccess + internal partial class BoundIndexOrRangeImplicitIndexerAccess { protected override ImmutableArray Children => ImmutableArray.Create(Receiver, Argument); } diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs index 2d6cb6ff9445..85f47a5192e7 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs @@ -3427,16 +3427,16 @@ private ImmutableArray GetSemanticSymbols( } break; - case BoundKind.IndexOrRangeIndexerFallbackAccess: + case BoundKind.IndexOrRangeImplicitIndexerAccess: { - var indexerAccess = (BoundIndexOrRangeIndexerFallbackAccess)boundNode; + var indexerAccess = (BoundIndexOrRangeImplicitIndexerAccess)boundNode; resultKind = indexerAccess.ResultKind; - // The only time a BoundIndexOrRangeIndexerFallbackAccess is created, overload resolution succeeded + // The only time a BoundIndexOrRangeImplicitIndexerAccess is created, overload resolution succeeded // and returned only 1 result - Debug.Assert(indexerAccess.PatternSymbol is object); - symbols = ImmutableArray.Create(indexerAccess.PatternSymbol); + Debug.Assert(indexerAccess.UnderlyingIndexerOrSliceSymbol is object); + symbols = ImmutableArray.Create(indexerAccess.UnderlyingIndexerOrSliceSymbol); } break; diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs index c43b4198a4f2..877d944d8cd5 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs @@ -1433,9 +1433,9 @@ public override BoundNode VisitIndexerAccess(BoundIndexerAccess node) return null; } - public override BoundNode VisitIndexOrRangeIndexerFallbackAccess(BoundIndexOrRangeIndexerFallbackAccess node) + public override BoundNode VisitIndexOrRangeImplicitIndexerAccess(BoundIndexOrRangeImplicitIndexerAccess node) { - // Index or Range fallback indexers evaluate the following in order: + // Index or Range implicit indexers evaluate the following in order: // 1. The receiver // 2. The Count or Length method off the receiver // 3. The argument to the access @@ -1444,11 +1444,11 @@ public override BoundNode VisitIndexOrRangeIndexerFallbackAccess(BoundIndexOrRan var method = GetReadMethod(node.LengthOrCountProperty); VisitReceiverAfterCall(node.Receiver, method); VisitRvalue(node.Argument); - method = node.PatternSymbol switch + method = node.UnderlyingIndexerOrSliceSymbol switch { PropertySymbol p => GetReadMethod(p), MethodSymbol m => m, - _ => throw ExceptionUtilities.UnexpectedValue(node.PatternSymbol) + _ => throw ExceptionUtilities.UnexpectedValue(node.UnderlyingIndexerOrSliceSymbol) }; VisitReceiverAfterCall(node.Receiver, method); diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index ae890322199a..6529023eb83b 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -8695,7 +8695,7 @@ private TypeWithAnnotations GetDeclaredParameterResult(ParameterSymbol parameter return null; } - public override BoundNode? VisitIndexOrRangeIndexerFallbackAccess(BoundIndexOrRangeIndexerFallbackAccess node) + public override BoundNode? VisitIndexOrRangeImplicitIndexerAccess(BoundIndexOrRangeImplicitIndexerAccess node) { BoundExpression receiver = node.Receiver; var receiverType = VisitRvalueWithState(receiver).Type; @@ -8704,14 +8704,14 @@ private TypeWithAnnotations GetDeclaredParameterResult(ParameterSymbol parameter _ = CheckPossibleNullReceiver(receiver); VisitRvalue(node.Argument); - var patternSymbol = node.PatternSymbol; + var patternSymbol = node.UnderlyingIndexerOrSliceSymbol; if (receiverType is object) { patternSymbol = AsMemberOfType(receiverType, patternSymbol); } SetLvalueResultType(node, patternSymbol.GetTypeOrReturnType()); - SetUpdatedSymbol(node, node.PatternSymbol, patternSymbol); + SetUpdatedSymbol(node, node.UnderlyingIndexerOrSliceSymbol, patternSymbol); return null; } diff --git a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs index 8255dba543dd..3e936cc0d5c4 100644 --- a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs @@ -30,7 +30,7 @@ internal enum BoundKind : byte AwaitableValuePlaceholder, DisposableValuePlaceholder, ObjectOrCollectionValuePlaceholder, - IndexOrRangeIndexerFallbackValuePlaceholder, + IndexOrRangeImplicitIndexerValuePlaceholder, Dup, PassByCopy, BadExpression, @@ -197,7 +197,7 @@ internal enum BoundKind : byte PropertyAccess, EventAccess, IndexerAccess, - IndexOrRangeIndexerFallbackAccess, + IndexOrRangeImplicitIndexerAccess, DynamicIndexerAccess, Lambda, UnboundLambda, @@ -640,18 +640,18 @@ public BoundObjectOrCollectionValuePlaceholder Update(bool isNewInstance, TypeSy } } - internal sealed partial class BoundIndexOrRangeIndexerFallbackValuePlaceholder : BoundValuePlaceholderBase + internal sealed partial class BoundIndexOrRangeImplicitIndexerValuePlaceholder : BoundValuePlaceholderBase { - public BoundIndexOrRangeIndexerFallbackValuePlaceholder(SyntaxNode syntax, TypeSymbol type, bool hasErrors) - : base(BoundKind.IndexOrRangeIndexerFallbackValuePlaceholder, syntax, type, hasErrors) + public BoundIndexOrRangeImplicitIndexerValuePlaceholder(SyntaxNode syntax, TypeSymbol type, bool hasErrors) + : base(BoundKind.IndexOrRangeImplicitIndexerValuePlaceholder, syntax, type, hasErrors) { RoslynDebug.Assert(type is object, "Field 'type' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); } - public BoundIndexOrRangeIndexerFallbackValuePlaceholder(SyntaxNode syntax, TypeSymbol type) - : base(BoundKind.IndexOrRangeIndexerFallbackValuePlaceholder, syntax, type) + public BoundIndexOrRangeImplicitIndexerValuePlaceholder(SyntaxNode syntax, TypeSymbol type) + : base(BoundKind.IndexOrRangeImplicitIndexerValuePlaceholder, syntax, type) { RoslynDebug.Assert(type is object, "Field 'type' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); @@ -661,13 +661,13 @@ public BoundIndexOrRangeIndexerFallbackValuePlaceholder(SyntaxNode syntax, TypeS public new TypeSymbol Type => base.Type!; [DebuggerStepThrough] - public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitIndexOrRangeIndexerFallbackValuePlaceholder(this); + public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitIndexOrRangeImplicitIndexerValuePlaceholder(this); - public BoundIndexOrRangeIndexerFallbackValuePlaceholder Update(TypeSymbol type) + public BoundIndexOrRangeImplicitIndexerValuePlaceholder Update(TypeSymbol type) { if (!TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) { - var result = new BoundIndexOrRangeIndexerFallbackValuePlaceholder(this.Syntax, type, this.HasErrors); + var result = new BoundIndexOrRangeImplicitIndexerValuePlaceholder(this.Syntax, type, this.HasErrors); result.CopyAttributes(this); return result; } @@ -7102,21 +7102,21 @@ public BoundIndexerAccess Update(BoundExpression? receiverOpt, PropertySymbol in } } - internal sealed partial class BoundIndexOrRangeIndexerFallbackAccess : BoundExpression + internal sealed partial class BoundIndexOrRangeImplicitIndexerAccess : BoundExpression { - public BoundIndexOrRangeIndexerFallbackAccess(SyntaxNode syntax, BoundExpression receiver, PropertySymbol lengthOrCountProperty, Symbol patternSymbol, BoundExpression argument, TypeSymbol type, bool hasErrors = false) - : base(BoundKind.IndexOrRangeIndexerFallbackAccess, syntax, type, hasErrors || receiver.HasErrors() || argument.HasErrors()) + public BoundIndexOrRangeImplicitIndexerAccess(SyntaxNode syntax, BoundExpression receiver, PropertySymbol lengthOrCountProperty, Symbol underlyingIndexerOrSliceSymbol, BoundExpression argument, TypeSymbol type, bool hasErrors = false) + : base(BoundKind.IndexOrRangeImplicitIndexerAccess, syntax, type, hasErrors || receiver.HasErrors() || argument.HasErrors()) { RoslynDebug.Assert(receiver is object, "Field 'receiver' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); RoslynDebug.Assert(lengthOrCountProperty is object, "Field 'lengthOrCountProperty' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); - RoslynDebug.Assert(patternSymbol is object, "Field 'patternSymbol' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); + RoslynDebug.Assert(underlyingIndexerOrSliceSymbol is object, "Field 'underlyingIndexerOrSliceSymbol' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); RoslynDebug.Assert(argument is object, "Field 'argument' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); RoslynDebug.Assert(type is object, "Field 'type' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); this.Receiver = receiver; this.LengthOrCountProperty = lengthOrCountProperty; - this.PatternSymbol = patternSymbol; + this.UnderlyingIndexerOrSliceSymbol = underlyingIndexerOrSliceSymbol; this.Argument = argument; } @@ -7127,17 +7127,17 @@ public BoundIndexOrRangeIndexerFallbackAccess(SyntaxNode syntax, BoundExpression public PropertySymbol LengthOrCountProperty { get; } - public Symbol PatternSymbol { get; } + public Symbol UnderlyingIndexerOrSliceSymbol { get; } public BoundExpression Argument { get; } [DebuggerStepThrough] - public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitIndexOrRangeIndexerFallbackAccess(this); + public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitIndexOrRangeImplicitIndexerAccess(this); - public BoundIndexOrRangeIndexerFallbackAccess Update(BoundExpression receiver, PropertySymbol lengthOrCountProperty, Symbol patternSymbol, BoundExpression argument, TypeSymbol type) + public BoundIndexOrRangeImplicitIndexerAccess Update(BoundExpression receiver, PropertySymbol lengthOrCountProperty, Symbol underlyingIndexerOrSliceSymbol, BoundExpression argument, TypeSymbol type) { - if (receiver != this.Receiver || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(lengthOrCountProperty, this.LengthOrCountProperty) || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(patternSymbol, this.PatternSymbol) || argument != this.Argument || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) + if (receiver != this.Receiver || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(lengthOrCountProperty, this.LengthOrCountProperty) || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(underlyingIndexerOrSliceSymbol, this.UnderlyingIndexerOrSliceSymbol) || argument != this.Argument || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) { - var result = new BoundIndexOrRangeIndexerFallbackAccess(this.Syntax, receiver, lengthOrCountProperty, patternSymbol, argument, type, this.HasErrors); + var result = new BoundIndexOrRangeImplicitIndexerAccess(this.Syntax, receiver, lengthOrCountProperty, underlyingIndexerOrSliceSymbol, argument, type, this.HasErrors); result.CopyAttributes(this); return result; } @@ -8489,8 +8489,8 @@ internal R VisitInternal(BoundNode node, A arg) return VisitDisposableValuePlaceholder((BoundDisposableValuePlaceholder)node, arg); case BoundKind.ObjectOrCollectionValuePlaceholder: return VisitObjectOrCollectionValuePlaceholder((BoundObjectOrCollectionValuePlaceholder)node, arg); - case BoundKind.IndexOrRangeIndexerFallbackValuePlaceholder: - return VisitIndexOrRangeIndexerFallbackValuePlaceholder((BoundIndexOrRangeIndexerFallbackValuePlaceholder)node, arg); + case BoundKind.IndexOrRangeImplicitIndexerValuePlaceholder: + return VisitIndexOrRangeImplicitIndexerValuePlaceholder((BoundIndexOrRangeImplicitIndexerValuePlaceholder)node, arg); case BoundKind.Dup: return VisitDup((BoundDup)node, arg); case BoundKind.PassByCopy: @@ -8823,8 +8823,8 @@ internal R VisitInternal(BoundNode node, A arg) return VisitEventAccess((BoundEventAccess)node, arg); case BoundKind.IndexerAccess: return VisitIndexerAccess((BoundIndexerAccess)node, arg); - case BoundKind.IndexOrRangeIndexerFallbackAccess: - return VisitIndexOrRangeIndexerFallbackAccess((BoundIndexOrRangeIndexerFallbackAccess)node, arg); + case BoundKind.IndexOrRangeImplicitIndexerAccess: + return VisitIndexOrRangeImplicitIndexerAccess((BoundIndexOrRangeImplicitIndexerAccess)node, arg); case BoundKind.DynamicIndexerAccess: return VisitDynamicIndexerAccess((BoundDynamicIndexerAccess)node, arg); case BoundKind.Lambda: @@ -8913,7 +8913,7 @@ internal abstract partial class BoundTreeVisitor public virtual R VisitAwaitableValuePlaceholder(BoundAwaitableValuePlaceholder node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitDisposableValuePlaceholder(BoundDisposableValuePlaceholder node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitObjectOrCollectionValuePlaceholder(BoundObjectOrCollectionValuePlaceholder node, A arg) => this.DefaultVisit(node, arg); - public virtual R VisitIndexOrRangeIndexerFallbackValuePlaceholder(BoundIndexOrRangeIndexerFallbackValuePlaceholder node, A arg) => this.DefaultVisit(node, arg); + public virtual R VisitIndexOrRangeImplicitIndexerValuePlaceholder(BoundIndexOrRangeImplicitIndexerValuePlaceholder node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitDup(BoundDup node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitPassByCopy(BoundPassByCopy node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitBadExpression(BoundBadExpression node, A arg) => this.DefaultVisit(node, arg); @@ -9080,7 +9080,7 @@ internal abstract partial class BoundTreeVisitor public virtual R VisitPropertyAccess(BoundPropertyAccess node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitEventAccess(BoundEventAccess node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitIndexerAccess(BoundIndexerAccess node, A arg) => this.DefaultVisit(node, arg); - public virtual R VisitIndexOrRangeIndexerFallbackAccess(BoundIndexOrRangeIndexerFallbackAccess node, A arg) => this.DefaultVisit(node, arg); + public virtual R VisitIndexOrRangeImplicitIndexerAccess(BoundIndexOrRangeImplicitIndexerAccess node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitDynamicIndexerAccess(BoundDynamicIndexerAccess node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitLambda(BoundLambda node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitUnboundLambda(UnboundLambda node, A arg) => this.DefaultVisit(node, arg); @@ -9130,7 +9130,7 @@ internal abstract partial class BoundTreeVisitor public virtual BoundNode? VisitAwaitableValuePlaceholder(BoundAwaitableValuePlaceholder node) => this.DefaultVisit(node); public virtual BoundNode? VisitDisposableValuePlaceholder(BoundDisposableValuePlaceholder node) => this.DefaultVisit(node); public virtual BoundNode? VisitObjectOrCollectionValuePlaceholder(BoundObjectOrCollectionValuePlaceholder node) => this.DefaultVisit(node); - public virtual BoundNode? VisitIndexOrRangeIndexerFallbackValuePlaceholder(BoundIndexOrRangeIndexerFallbackValuePlaceholder node) => this.DefaultVisit(node); + public virtual BoundNode? VisitIndexOrRangeImplicitIndexerValuePlaceholder(BoundIndexOrRangeImplicitIndexerValuePlaceholder node) => this.DefaultVisit(node); public virtual BoundNode? VisitDup(BoundDup node) => this.DefaultVisit(node); public virtual BoundNode? VisitPassByCopy(BoundPassByCopy node) => this.DefaultVisit(node); public virtual BoundNode? VisitBadExpression(BoundBadExpression node) => this.DefaultVisit(node); @@ -9297,7 +9297,7 @@ internal abstract partial class BoundTreeVisitor public virtual BoundNode? VisitPropertyAccess(BoundPropertyAccess node) => this.DefaultVisit(node); public virtual BoundNode? VisitEventAccess(BoundEventAccess node) => this.DefaultVisit(node); public virtual BoundNode? VisitIndexerAccess(BoundIndexerAccess node) => this.DefaultVisit(node); - public virtual BoundNode? VisitIndexOrRangeIndexerFallbackAccess(BoundIndexOrRangeIndexerFallbackAccess node) => this.DefaultVisit(node); + public virtual BoundNode? VisitIndexOrRangeImplicitIndexerAccess(BoundIndexOrRangeImplicitIndexerAccess node) => this.DefaultVisit(node); public virtual BoundNode? VisitDynamicIndexerAccess(BoundDynamicIndexerAccess node) => this.DefaultVisit(node); public virtual BoundNode? VisitLambda(BoundLambda node) => this.DefaultVisit(node); public virtual BoundNode? VisitUnboundLambda(UnboundLambda node) => this.DefaultVisit(node); @@ -9363,7 +9363,7 @@ internal abstract partial class BoundTreeWalker : BoundTreeVisitor public override BoundNode? VisitAwaitableValuePlaceholder(BoundAwaitableValuePlaceholder node) => null; public override BoundNode? VisitDisposableValuePlaceholder(BoundDisposableValuePlaceholder node) => null; public override BoundNode? VisitObjectOrCollectionValuePlaceholder(BoundObjectOrCollectionValuePlaceholder node) => null; - public override BoundNode? VisitIndexOrRangeIndexerFallbackValuePlaceholder(BoundIndexOrRangeIndexerFallbackValuePlaceholder node) => null; + public override BoundNode? VisitIndexOrRangeImplicitIndexerValuePlaceholder(BoundIndexOrRangeImplicitIndexerValuePlaceholder node) => null; public override BoundNode? VisitDup(BoundDup node) => null; public override BoundNode? VisitPassByCopy(BoundPassByCopy node) { @@ -10130,7 +10130,7 @@ internal abstract partial class BoundTreeWalker : BoundTreeVisitor this.VisitList(node.Arguments); return null; } - public override BoundNode? VisitIndexOrRangeIndexerFallbackAccess(BoundIndexOrRangeIndexerFallbackAccess node) + public override BoundNode? VisitIndexOrRangeImplicitIndexerAccess(BoundIndexOrRangeImplicitIndexerAccess node) { this.Visit(node.Receiver); this.Visit(node.Argument); @@ -10358,7 +10358,7 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor TypeSymbol? type = this.VisitType(node.Type); return node.Update(node.IsNewInstance, type); } - public override BoundNode? VisitIndexOrRangeIndexerFallbackValuePlaceholder(BoundIndexOrRangeIndexerFallbackValuePlaceholder node) + public override BoundNode? VisitIndexOrRangeImplicitIndexerValuePlaceholder(BoundIndexOrRangeImplicitIndexerValuePlaceholder node) { TypeSymbol? type = this.VisitType(node.Type); return node.Update(type); @@ -11357,12 +11357,12 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor TypeSymbol? type = this.VisitType(node.Type); return node.Update(receiverOpt, node.Indexer, arguments, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, node.Expanded, node.ArgsToParamsOpt, node.DefaultArguments, node.OriginalIndexersOpt, type); } - public override BoundNode? VisitIndexOrRangeIndexerFallbackAccess(BoundIndexOrRangeIndexerFallbackAccess node) + public override BoundNode? VisitIndexOrRangeImplicitIndexerAccess(BoundIndexOrRangeImplicitIndexerAccess node) { BoundExpression receiver = (BoundExpression)this.Visit(node.Receiver); BoundExpression argument = (BoundExpression)this.Visit(node.Argument); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(receiver, node.LengthOrCountProperty, node.PatternSymbol, argument, type); + return node.Update(receiver, node.LengthOrCountProperty, node.UnderlyingIndexerOrSliceSymbol, argument, type); } public override BoundNode? VisitDynamicIndexerAccess(BoundDynamicIndexerAccess node) { @@ -11707,14 +11707,14 @@ public NullabilityRewriter(ImmutableDictionary new TreeDumperNode("indexOrRangeIndexerFallbackValuePlaceholder", null, new TreeDumperNode[] + public override TreeDumperNode VisitIndexOrRangeImplicitIndexerValuePlaceholder(BoundIndexOrRangeImplicitIndexerValuePlaceholder node, object? arg) => new TreeDumperNode("indexOrRangeImplicitIndexerValuePlaceholder", null, new TreeDumperNode[] { new TreeDumperNode("type", node.Type, null), new TreeDumperNode("isSuppressed", node.IsSuppressed, null), @@ -15732,11 +15732,11 @@ private BoundTreeDumperNodeProducer() new TreeDumperNode("hasErrors", node.HasErrors, null) } ); - public override TreeDumperNode VisitIndexOrRangeIndexerFallbackAccess(BoundIndexOrRangeIndexerFallbackAccess node, object? arg) => new TreeDumperNode("indexOrRangeIndexerFallbackAccess", null, new TreeDumperNode[] + public override TreeDumperNode VisitIndexOrRangeImplicitIndexerAccess(BoundIndexOrRangeImplicitIndexerAccess node, object? arg) => new TreeDumperNode("indexOrRangeImplicitIndexerAccess", null, new TreeDumperNode[] { new TreeDumperNode("receiver", null, new TreeDumperNode[] { Visit(node.Receiver, null) }), new TreeDumperNode("lengthOrCountProperty", node.LengthOrCountProperty, null), - new TreeDumperNode("patternSymbol", node.PatternSymbol, null), + new TreeDumperNode("underlyingIndexerOrSliceSymbol", node.UnderlyingIndexerOrSliceSymbol, null), new TreeDumperNode("argument", null, new TreeDumperNode[] { Visit(node.Argument, null) }), new TreeDumperNode("type", node.Type, null), new TreeDumperNode("isSuppressed", node.IsSuppressed, null), diff --git a/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs b/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs index 82d0568f95b8..bad1fdb98ee5 100644 --- a/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs +++ b/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs @@ -97,14 +97,14 @@ public override BoundNode VisitArrayAccess(BoundArrayAccess node) return base.VisitArrayAccess(node); } - public override BoundNode VisitIndexOrRangeIndexerFallbackAccess(BoundIndexOrRangeIndexerFallbackAccess node) + public override BoundNode VisitIndexOrRangeImplicitIndexerAccess(BoundIndexOrRangeImplicitIndexerAccess node) { if (_inExpressionLambda) { Error(ErrorCode.ERR_ExpressionTreeContainsPatternIndexOrRangeIndexer, node); } - return base.VisitIndexOrRangeIndexerFallbackAccess(node); + return base.VisitIndexOrRangeImplicitIndexerAccess(node); } public override BoundNode VisitFromEndIndexExpression(BoundFromEndIndexExpression node) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs index b7b8595acc89..e50402e40c63 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs @@ -953,13 +953,13 @@ internal static bool CanBePassedByReference(BoundExpression expr) case BoundKind.IndexerAccess: return ((BoundIndexerAccess)expr).Indexer.RefKind != RefKind.None; - case BoundKind.IndexOrRangeIndexerFallbackAccess: - var patternIndexer = (BoundIndexOrRangeIndexerFallbackAccess)expr; - var refKind = patternIndexer.PatternSymbol switch + case BoundKind.IndexOrRangeImplicitIndexerAccess: + var patternIndexer = (BoundIndexOrRangeImplicitIndexerAccess)expr; + var refKind = patternIndexer.UnderlyingIndexerOrSliceSymbol switch { PropertySymbol p => p.RefKind, MethodSymbol m => m.RefKind, - _ => throw ExceptionUtilities.UnexpectedValue(patternIndexer.PatternSymbol) + _ => throw ExceptionUtilities.UnexpectedValue(patternIndexer.UnderlyingIndexerOrSliceSymbol) }; return refKind != RefKind.None; @@ -1048,7 +1048,7 @@ public static void Validate(BoundNode node) return null; } - public override BoundNode? VisitIndexOrRangeIndexerFallbackValuePlaceholder(BoundIndexOrRangeIndexerFallbackValuePlaceholder node) + public override BoundNode? VisitIndexOrRangeImplicitIndexerValuePlaceholder(BoundIndexOrRangeImplicitIndexerValuePlaceholder node) { Fail(node); return null; diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs index 8f2c357f1ae7..8325f5aa2413 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs @@ -34,9 +34,9 @@ private BoundExpression VisitAssignmentOperator(BoundAssignmentOperator node, bo loweredLeft = VisitIndexerAccess((BoundIndexerAccess)left, isLeftOfAssignment: true); break; - case BoundKind.IndexOrRangeIndexerFallbackAccess: - loweredLeft = VisitIndexOrRangeIndexerFallbackAccess( - (BoundIndexOrRangeIndexerFallbackAccess)left, + case BoundKind.IndexOrRangeImplicitIndexerAccess: + loweredLeft = VisitIndexOrRangeImplicitIndexerAccess( + (BoundIndexOrRangeImplicitIndexerAccess)left, isLeftOfAssignment: true); break; diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs index 58c7d8225b60..f23ae886aa91 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs @@ -404,19 +404,19 @@ private BoundIndexerAccess TransformIndexerAccess(BoundIndexerAccess indexerAcce indexerAccess.Type); } - private BoundExpression TransformIndexerFallbackAccess( - BoundIndexOrRangeIndexerFallbackAccess indexerAccess, + private BoundExpression TransformImplicitIndexerAccess( + BoundIndexOrRangeImplicitIndexerAccess indexerAccess, ArrayBuilder stores, ArrayBuilder temps, bool isDynamicAssignment) { - // A fallback indexer is fundamentally a sequence which ends in either - // a conventional indexer access or a method call. The lowering of a - // fallback indexer already lowers everything we need into temps, so + // An implicit indexer is fundamentally a sequence which ends in either + // a conventional indexer access or a method call. The lowering of an + // implicit indexer already lowers everything we need into temps, so // the only thing we need to do is lift the stores and temps out of // the sequence, and use the final expression as the new argument - var sequence = VisitIndexOrRangeIndexerFallbackAccess(indexerAccess, isLeftOfAssignment: true); + var sequence = VisitIndexOrRangeImplicitIndexerAccess(indexerAccess, isLeftOfAssignment: true); stores.AddRange(sequence.SideEffects); temps.AddRange(sequence.Locals); return TransformCompoundAssignmentLHS(sequence.Value, stores, temps, isDynamicAssignment); @@ -585,10 +585,10 @@ private BoundExpression TransformCompoundAssignmentLHS(BoundExpression originalL } break; - case BoundKind.IndexOrRangeIndexerFallbackAccess: + case BoundKind.IndexOrRangeImplicitIndexerAccess: { - var patternIndexerAccess = (BoundIndexOrRangeIndexerFallbackAccess)originalLHS; - RefKind refKind = patternIndexerAccess.PatternSymbol switch + var patternIndexerAccess = (BoundIndexOrRangeImplicitIndexerAccess)originalLHS; + RefKind refKind = patternIndexerAccess.UnderlyingIndexerOrSliceSymbol switch { PropertySymbol p => p.RefKind, MethodSymbol m => m.RefKind, @@ -596,7 +596,7 @@ private BoundExpression TransformCompoundAssignmentLHS(BoundExpression originalL }; if (refKind == RefKind.None) { - return TransformIndexerFallbackAccess(patternIndexerAccess, stores, temps, isDynamicAssignment); + return TransformImplicitIndexerAccess(patternIndexerAccess, stores, temps, isDynamicAssignment); } } break; diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs index a39d3de83285..25964cd1abed 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs @@ -161,23 +161,23 @@ private BoundExpression MakeIndexerAccess( } } } - public override BoundNode VisitIndexOrRangeIndexerFallbackAccess(BoundIndexOrRangeIndexerFallbackAccess node) + public override BoundNode VisitIndexOrRangeImplicitIndexerAccess(BoundIndexOrRangeImplicitIndexerAccess node) { - return VisitIndexOrRangeIndexerFallbackAccess(node, isLeftOfAssignment: false); + return VisitIndexOrRangeImplicitIndexerAccess(node, isLeftOfAssignment: false); } - private BoundSequence VisitIndexOrRangeIndexerFallbackAccess(BoundIndexOrRangeIndexerFallbackAccess node, bool isLeftOfAssignment) + private BoundSequence VisitIndexOrRangeImplicitIndexerAccess(BoundIndexOrRangeImplicitIndexerAccess node, bool isLeftOfAssignment) { if (TypeSymbol.Equals( node.Argument.Type, _compilation.GetWellKnownType(WellKnownType.System_Index), TypeCompareKind.ConsiderEverything)) { - return VisitIndexIndexerFallbackAccess( + return VisitIndexImplicitIndexerAccess( node.Syntax, node.Receiver, node.LengthOrCountProperty, - (PropertySymbol)node.PatternSymbol, + (PropertySymbol)node.UnderlyingIndexerOrSliceSymbol, node.Argument, isLeftOfAssignment: isLeftOfAssignment); } @@ -187,15 +187,15 @@ private BoundSequence VisitIndexOrRangeIndexerFallbackAccess(BoundIndexOrRangeIn node.Argument.Type, _compilation.GetWellKnownType(WellKnownType.System_Range), TypeCompareKind.ConsiderEverything)); - return VisitRangeIndexerFallbackAccess( + return VisitRangeImplicitIndexerAccess( node.Receiver, node.LengthOrCountProperty, - (MethodSymbol)node.PatternSymbol, + (MethodSymbol)node.UnderlyingIndexerOrSliceSymbol, node.Argument); } } - private BoundSequence VisitIndexIndexerFallbackAccess( + private BoundSequence VisitIndexImplicitIndexerAccess( SyntaxNode syntax, BoundExpression receiver, PropertySymbol lengthOrCountProperty, @@ -284,7 +284,7 @@ private BoundExpression MakePatternIndexOffsetExpression( } } - private BoundSequence VisitRangeIndexerFallbackAccess( + private BoundSequence VisitRangeImplicitIndexerAccess( BoundExpression receiver, PropertySymbol lengthOrCountProperty, MethodSymbol sliceMethod, diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index 0ad43d998fa7..196046b70076 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -300,7 +300,7 @@ public CSharpOperationFactory(SemanticModel semanticModel) case BoundKind.StackAllocArrayCreation: case BoundKind.TypeExpression: case BoundKind.TypeOrValueExpression: - case BoundKind.IndexOrRangeIndexerFallbackAccess: + case BoundKind.IndexOrRangeImplicitIndexerAccess: ConstantValue? constantValue = (boundNode as BoundExpression)?.ConstantValue; bool isImplicit = boundNode.WasCompilerGenerated; diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests3.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests3.cs index c683ac336649..1b67c02c8ea8 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests3.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests3.cs @@ -21,7 +21,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests public class PatternMatchingTests3 : PatternMatchingTestBase { [Fact] - public void PropertyPatternSymbolInfo_01() + public void PropertyUnderlyingIndexerOrSliceSymbolInfo_01() { var source = @" @@ -66,7 +66,7 @@ class Point } [Fact] - public void PropertyPatternSymbolInfo_02() + public void PropertyUnderlyingIndexerOrSliceSymbolInfo_02() { var source = @" @@ -131,7 +131,7 @@ interface Point : I1, I2 } [Fact] - public void PropertyPatternSymbolInfo_03() + public void PropertyUnderlyingIndexerOrSliceSymbolInfo_03() { var source = @" @@ -178,7 +178,7 @@ class Point } [Fact] - public void PositionalPatternSymbolInfo_01() + public void PositionalUnderlyingIndexerOrSliceSymbolInfo_01() { var source = @" @@ -223,7 +223,7 @@ class Point } [Fact] - public void PositionalPatternSymbolInfo_02() + public void PositionalUnderlyingIndexerOrSliceSymbolInfo_02() { var source = @" @@ -276,7 +276,7 @@ class Point } [Fact] - public void PositionalPatternSymbolInfo_03() + public void PositionalUnderlyingIndexerOrSliceSymbolInfo_03() { var source = @" @@ -317,7 +317,7 @@ public static void Main() } [Fact] - public void PositionalPatternSymbolInfo_04() + public void PositionalUnderlyingIndexerOrSliceSymbolInfo_04() { var source = @" From 05f3611c9ae8841d9c2b7373191da9ea1ab916a4 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Mon, 1 Nov 2021 09:37:34 -0700 Subject: [PATCH 04/29] More rename --- .../Portable/Binder/Binder.ValueChecks.cs | 30 ++++++++-------- .../Portable/Binder/Binder_Expressions.cs | 36 +++++++++---------- .../CSharp/Portable/Binder/Binder_Patterns.cs | 28 +++++++-------- .../Portable/Binder/Binder_Statements.cs | 7 ++-- .../Compilation/CSharpSemanticModel.cs | 6 ++-- .../Portable/FlowAnalysis/NullableWalker.cs | 8 ++--- .../Lowering/LocalRewriter/LocalRewriter.cs | 6 ++-- ...ocalRewriter_CompoundAssignmentOperator.cs | 6 ++-- .../Semantics/PatternMatchingTests3.cs | 14 ++++---- 9 files changed, 70 insertions(+), 71 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index 3039298668ab..5e0fd185242b 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -590,12 +590,12 @@ internal bool CheckValueKind(SyntaxNode node, BoundExpression expr, BindValueKin diagnostics); case BoundKind.IndexOrRangeImplicitIndexerAccess: - var patternIndexer = (BoundIndexOrRangeImplicitIndexerAccess)expr; + var implicitIndexerAccess = (BoundIndexOrRangeImplicitIndexerAccess)expr; // If we got here this should be an implicit indexer taking a Range, // meaning that the pattern symbol must be a method (either Slice or Substring) return CheckMethodReturnValueKind( - (MethodSymbol)patternIndexer.UnderlyingIndexerOrSliceSymbol, - patternIndexer.Syntax, + (MethodSymbol)implicitIndexerAccess.UnderlyingIndexerOrSliceSymbol, + implicitIndexerAccess.Syntax, node, valueKind, checkingReceiver, @@ -2619,17 +2619,17 @@ internal static uint GetValEscape(BoundExpression expr, uint scopeOfTheContainin isRefEscape: false); case BoundKind.IndexOrRangeImplicitIndexerAccess: - var patternIndexer = (BoundIndexOrRangeImplicitIndexerAccess)expr; - var parameters = patternIndexer.UnderlyingIndexerOrSliceSymbol switch + var implicitIndexerAccess = (BoundIndexOrRangeImplicitIndexerAccess)expr; + var parameters = implicitIndexerAccess.UnderlyingIndexerOrSliceSymbol switch { PropertySymbol p => p.Parameters, MethodSymbol m => m.Parameters, - _ => throw ExceptionUtilities.UnexpectedValue(patternIndexer.UnderlyingIndexerOrSliceSymbol) + _ => throw ExceptionUtilities.UnexpectedValue(implicitIndexerAccess.UnderlyingIndexerOrSliceSymbol) }; return GetInvocationEscapeScope( - patternIndexer.UnderlyingIndexerOrSliceSymbol, - patternIndexer.Receiver, + implicitIndexerAccess.UnderlyingIndexerOrSliceSymbol, + implicitIndexerAccess.Receiver, parameters, default, default, @@ -3041,9 +3041,9 @@ internal static bool CheckValEscape(SyntaxNode node, BoundExpression expr, uint isRefEscape: false); case BoundKind.IndexOrRangeImplicitIndexerAccess: - var patternIndexer = (BoundIndexOrRangeImplicitIndexerAccess)expr; - var patternSymbol = patternIndexer.UnderlyingIndexerOrSliceSymbol; - var parameters = patternSymbol switch + var implicitIndexerAccess = (BoundIndexOrRangeImplicitIndexerAccess)expr; + var underlyingIndexerOrSliceSymbol = implicitIndexerAccess.UnderlyingIndexerOrSliceSymbol; + var parameters = underlyingIndexerOrSliceSymbol switch { PropertySymbol p => p.Parameters, MethodSymbol m => m.Parameters, @@ -3051,11 +3051,11 @@ internal static bool CheckValEscape(SyntaxNode node, BoundExpression expr, uint }; return CheckInvocationEscape( - patternIndexer.Syntax, - patternSymbol, - patternIndexer.Receiver, + implicitIndexerAccess.Syntax, + underlyingIndexerOrSliceSymbol, + implicitIndexerAccess.Receiver, parameters, - ImmutableArray.Create(patternIndexer.Argument), + ImmutableArray.Create(implicitIndexerAccess.Argument), default, default, checkingReceiver, diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index 6fec83659952..070a0807005b 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -7745,9 +7745,9 @@ private BoundExpression BindIndexerAccess(ExpressionSyntax node, BoundExpression expr, analyzedArguments, diagnostics, - out var indexerFallbackAccess)) + out var implicitIndexerAccess)) { - indexerAccessExpression = indexerFallbackAccess; + indexerAccessExpression = implicitIndexerAccess; } else { @@ -7917,9 +7917,9 @@ private BoundExpression BindIndexerOrIndexedPropertyAccess( receiverOpt, analyzedArguments, diagnostics, - out var indexerFallbackAccess)) + out var implicitIndexerAccess)) { - return indexerFallbackAccess; + return implicitIndexerAccess; } else { @@ -8027,9 +8027,9 @@ private bool TryBindIndexOrRangeImplicitIndexer( BoundExpression? receiverOpt, AnalyzedArguments arguments, BindingDiagnosticBag diagnostics, - [NotNullWhen(true)] out BoundIndexOrRangeImplicitIndexerAccess? indexerFallbackAccess) + [NotNullWhen(true)] out BoundIndexOrRangeImplicitIndexerAccess? implicitIndexerAccess) { - indexerFallbackAccess = null; + implicitIndexerAccess = null; // Verify a few things up-front, namely that we have a single argument // to this indexer that has an Index or Range type and that there is @@ -8056,18 +8056,18 @@ private bool TryBindIndexOrRangeImplicitIndexer( bool argIsIndex = argIsIndexNotRange.Value(); if (!TryFindIndexOrRangeImplicitIndexer(syntax, receiverOpt, receiverType, argIsIndex: argIsIndex, - out PropertySymbol? lengthOrCountProperty, out Symbol? patternSymbol, diagnostics)) + out PropertySymbol? lengthOrCountProperty, out Symbol? indexerOrSliceSymbol, diagnostics)) { return false; } - indexerFallbackAccess = new BoundIndexOrRangeImplicitIndexerAccess( + implicitIndexerAccess = new BoundIndexOrRangeImplicitIndexerAccess( syntax, receiverOpt, lengthOrCountProperty, - patternSymbol, + indexerOrSliceSymbol, BindToNaturalType(argument, diagnostics), - patternSymbol.GetTypeOrReturnType().Type); + indexerOrSliceSymbol.GetTypeOrReturnType().Type); if (!argIsIndex) { @@ -8105,7 +8105,7 @@ private bool TryFindIndexOrRangeImplicitIndexer( TypeSymbol receiverType, bool argIsIndex, [NotNullWhen(true)] out PropertySymbol? lengthOrCountProperty, - [NotNullWhen(true)] out Symbol? patternSymbol, + [NotNullWhen(true)] out Symbol? indexerOrSliceSymbol, BindingDiagnosticBag diagnostics) { // SPEC: @@ -8121,7 +8121,7 @@ private bool TryFindIndexOrRangeImplicitIndexer( var lookupResult = LookupResult.GetInstance(); if (TryLookupLengthOrCount(syntax, receiverType, lookupResult, out lengthOrCountProperty, diagnostics) && - TryFindIndexOrRangeImplicitIndexer(syntax, lookupResult, receiverOpt, receiverType, argIsIndex, out patternSymbol, diagnostics)) + TryFindIndexOrRangeImplicitIndexer(syntax, lookupResult, receiverOpt, receiverType, argIsIndex, out indexerOrSliceSymbol, diagnostics)) { var lengthAccess = new BoundPropertyAccess(syntax, receiverOpt, lengthOrCountProperty, LookupResultKind.Viable, lengthOrCountProperty.Type); CheckPropertyValueKind(syntax, lengthAccess, BindValueKind.RValue, checkingReceiver: false, diagnostics); @@ -8130,7 +8130,7 @@ private bool TryFindIndexOrRangeImplicitIndexer( return true; } - patternSymbol = null; + indexerOrSliceSymbol = null; lookupResult.Free(); return false; } @@ -8146,7 +8146,7 @@ private bool TryFindIndexOrRangeImplicitIndexer( BoundExpression? receiverOpt, TypeSymbol receiverType, bool argIsIndex, - [NotNullWhen(true)] out Symbol? patternSymbol, + [NotNullWhen(true)] out Symbol? indexerOrSliceSymbol, BindingDiagnosticBag diagnostics) { var useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); @@ -8178,7 +8178,7 @@ candidate is PropertySymbol property && original.Parameters[0] is { Type: { SpecialType: SpecialType.System_Int32 }, RefKind: RefKind.None }) { // note: implicit copy check on the indexer accessor happens in CheckPropertyValueKind - patternSymbol = property; + indexerOrSliceSymbol = property; property.AddUseSiteInfo(ref useSiteInfo); diagnostics.Add(syntax, useSiteInfo); ReportDiagnosticsIfObsolete(diagnostics, property, syntax, hasBaseReceiver: false); @@ -8194,7 +8194,7 @@ candidate is PropertySymbol property && var substring = (MethodSymbol)Compilation.GetSpecialTypeMember(SpecialMember.System_String__Substring); if (substring is object) { - patternSymbol = substring; + indexerOrSliceSymbol = substring; return true; } } @@ -8227,7 +8227,7 @@ method.OriginalDefinition is var original && original.Parameters[0] is { Type: { SpecialType: SpecialType.System_Int32 }, RefKind: RefKind.None } && original.Parameters[1] is { Type: { SpecialType: SpecialType.System_Int32 }, RefKind: RefKind.None }) { - patternSymbol = method; + indexerOrSliceSymbol = method; method.AddUseSiteInfo(ref useSiteInfo); diagnostics.Add(syntax, useSiteInfo); ReportDiagnosticsIfObsolete(diagnostics, method, syntax, hasBaseReceiver: false); @@ -8238,7 +8238,7 @@ method.OriginalDefinition is var original && } } - patternSymbol = null; + indexerOrSliceSymbol = null; return false; } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs index 70d7d6106b0c..82ee67ff350a 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs @@ -214,9 +214,9 @@ private BoundPattern BindSlicePattern( { sliceType = inputType; } - else if (TryBindIndexerForPattern(node, inputType, argIsIndex: false, out indexerAccess, out Symbol? patternSymbol, lengthProperty: out _, diagnostics, ref hasErrors)) + else if (TryBindIndexerForPattern(node, inputType, argIsIndex: false, out indexerAccess, out Symbol? indexerOrSliceSymbol, lengthProperty: out _, diagnostics, ref hasErrors)) { - if (patternSymbol is MethodSymbol method) + if (indexerOrSliceSymbol is MethodSymbol method) { sliceMethod = method; sliceType = method.ReturnType; @@ -297,9 +297,9 @@ private BoundListPattern BindListPattern( elementType = ((ArrayTypeSymbol)inputType).ElementType; hasErrors |= !TryGetSpecialTypeMember(Compilation, SpecialMember.System_Array__Length, node, diagnostics, out lengthProperty); } - else if (TryBindIndexerForPattern(node, narrowedType, argIsIndex: true, out indexerAccess, out Symbol? patternSymbol, out lengthProperty, diagnostics, ref hasErrors)) + else if (TryBindIndexerForPattern(node, narrowedType, argIsIndex: true, out indexerAccess, out Symbol? indexerOrSliceSymbol, out lengthProperty, diagnostics, ref hasErrors)) { - if (patternSymbol is PropertySymbol indexer) + if (indexerOrSliceSymbol is PropertySymbol indexer) { indexerSymbol = indexer; elementType = indexer.Type; @@ -338,14 +338,14 @@ private bool TryBindIndexerForPattern( TypeSymbol receiverType, bool argIsIndex, out BoundIndexerAccess? indexerAccess, - out Symbol? patternSymbol, + out Symbol? indexerOrSliceSymbol, [NotNullWhen(true)] out PropertySymbol? lengthProperty, BindingDiagnosticBag diagnostics, ref bool hasErrors) { Debug.Assert(!receiverType.IsErrorType()); indexerAccess = null; - patternSymbol = null; + indexerOrSliceSymbol = null; lengthProperty = null; bool found = false; CompoundUseSiteInfo useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); @@ -397,7 +397,7 @@ private bool TryBindIndexerForPattern( case BoundIndexOrRangeImplicitIndexerAccess boundIndexOrRangeImplicitIndexerAccess: lengthProperty = boundIndexOrRangeImplicitIndexerAccess.LengthOrCountProperty; - patternSymbol = boundIndexOrRangeImplicitIndexerAccess.UnderlyingIndexerOrSliceSymbol; + indexerOrSliceSymbol = boundIndexOrRangeImplicitIndexerAccess.UnderlyingIndexerOrSliceSymbol; found = true; break; @@ -413,22 +413,22 @@ private bool TryBindIndexerForPattern( // If the argType is missing or the indexer lookup has failed, we will fallback to the implicit indexer support. found = TryLookupLengthOrCount(syntax, receiverType, lookupResult, out lengthProperty, diagnostics) && - TryFindIndexOrRangeImplicitIndexer(syntax, lookupResult, receiverOpt: null, receiverType, argIsIndex, out patternSymbol, diagnostics); + TryFindIndexOrRangeImplicitIndexer(syntax, lookupResult, receiverOpt: null, receiverType, argIsIndex, out indexerOrSliceSymbol, diagnostics); done: if (found) { - Debug.Assert(indexerAccess is not null ^ patternSymbol is not null); + Debug.Assert(indexerAccess is not null ^ indexerOrSliceSymbol is not null); Debug.Assert(lengthProperty is not null); if (!hasErrors) { - if (patternSymbol is not null) + if (indexerOrSliceSymbol is not null) { - var indexerFallbackAccess = new BoundIndexOrRangeImplicitIndexerAccess(syntax, new BoundImplicitReceiver(syntax, receiverType), - lengthProperty, patternSymbol, argument: new BoundIndexOrRangeImplicitIndexerValuePlaceholder(syntax, argType), patternSymbol.GetTypeOrReturnType().Type); + var implicitIndexerAccess = new BoundIndexOrRangeImplicitIndexerAccess(syntax, new BoundImplicitReceiver(syntax, receiverType), + lengthProperty, indexerOrSliceSymbol, argument: new BoundIndexOrRangeImplicitIndexerValuePlaceholder(syntax, argType), indexerOrSliceSymbol.GetTypeOrReturnType().Type); - if (!CheckValueKind(syntax, indexerFallbackAccess, BindValueKind.RValue, checkingReceiver: false, bindingDiagnostics)) + if (!CheckValueKind(syntax, implicitIndexerAccess, BindValueKind.RValue, checkingReceiver: false, bindingDiagnostics)) { hasErrors = true; } @@ -1485,7 +1485,7 @@ private ImmutableArray BindPropertyPatternClause( bool ignoredHasErrors = false; isLengthOrCount = receiverType.IsSZArray() ? ReferenceEquals(memberSymbol, Compilation.GetSpecialTypeMember(SpecialMember.System_Array__Length)) - : TryBindIndexerForPattern(node, receiverType, argIsIndex: true, indexerAccess: out _, patternSymbol: out _, out PropertySymbol? lengthProperty, BindingDiagnosticBag.Discarded, ref ignoredHasErrors) && + : TryBindIndexerForPattern(node, receiverType, argIsIndex: true, indexerAccess: out _, indexerOrSliceSymbol: out _, out PropertySymbol? lengthProperty, BindingDiagnosticBag.Discarded, ref ignoredHasErrors) && memberSymbol.Equals(lengthProperty, TypeCompareKind.ConsiderEverything); // If Length and Count are both present, only the former is assumed to be non-negative. } } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index 67456502a558..e90c2d1f65b7 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -1558,14 +1558,13 @@ private static PropertySymbol GetPropertySymbol(BoundExpression expr, out BoundE break; case BoundKind.IndexOrRangeImplicitIndexerAccess: { - var patternIndexer = (BoundIndexOrRangeImplicitIndexerAccess)expr; - receiver = patternIndexer.Receiver; - propertySymbol = (PropertySymbol)patternIndexer.UnderlyingIndexerOrSliceSymbol; + var implicitIndexerAccess = (BoundIndexOrRangeImplicitIndexerAccess)expr; + receiver = implicitIndexerAccess.Receiver; + propertySymbol = (PropertySymbol)implicitIndexerAccess.UnderlyingIndexerOrSliceSymbol; } break; default: receiver = null; - propertySymbol = null; propertySyntax = null; return null; } diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs index 85f47a5192e7..843263583c2a 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs @@ -1981,14 +1981,14 @@ internal SymbolInfo GetSymbolInfoForNode( return SymbolInfoFactory.Create(symbols, resultKind, isDynamic); } - private static SymbolInfo GetSymbolInfoForSubpattern(Symbol subpatternSymbol) + private static SymbolInfo GetSymbolInfoForSubpattern(Symbol subunderlyingIndexerOrSliceSymbol) { - if (subpatternSymbol?.OriginalDefinition is ErrorTypeSymbol originalErrorType) + if (subunderlyingIndexerOrSliceSymbol?.OriginalDefinition is ErrorTypeSymbol originalErrorType) { return new SymbolInfo(symbol: null, originalErrorType.CandidateSymbols.GetPublicSymbols(), originalErrorType.ResultKind.ToCandidateReason()); } - return new SymbolInfo(subpatternSymbol.GetPublicSymbol(), CandidateReason.None); + return new SymbolInfo(subunderlyingIndexerOrSliceSymbol.GetPublicSymbol(), CandidateReason.None); } private SymbolInfo GetSymbolInfoForDeconstruction(BoundRecursivePattern pat) diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index 6529023eb83b..0c41fd6b83dd 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -8704,14 +8704,14 @@ private TypeWithAnnotations GetDeclaredParameterResult(ParameterSymbol parameter _ = CheckPossibleNullReceiver(receiver); VisitRvalue(node.Argument); - var patternSymbol = node.UnderlyingIndexerOrSliceSymbol; + var underlyingIndexerOrSliceSymbol = node.UnderlyingIndexerOrSliceSymbol; if (receiverType is object) { - patternSymbol = AsMemberOfType(receiverType, patternSymbol); + underlyingIndexerOrSliceSymbol = AsMemberOfType(receiverType, underlyingIndexerOrSliceSymbol); } - SetLvalueResultType(node, patternSymbol.GetTypeOrReturnType()); - SetUpdatedSymbol(node, node.UnderlyingIndexerOrSliceSymbol, patternSymbol); + SetLvalueResultType(node, underlyingIndexerOrSliceSymbol.GetTypeOrReturnType()); + SetUpdatedSymbol(node, node.UnderlyingIndexerOrSliceSymbol, underlyingIndexerOrSliceSymbol); return null; } diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs index e50402e40c63..f73d26573d3a 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs @@ -954,12 +954,12 @@ internal static bool CanBePassedByReference(BoundExpression expr) return ((BoundIndexerAccess)expr).Indexer.RefKind != RefKind.None; case BoundKind.IndexOrRangeImplicitIndexerAccess: - var patternIndexer = (BoundIndexOrRangeImplicitIndexerAccess)expr; - var refKind = patternIndexer.UnderlyingIndexerOrSliceSymbol switch + var implicitIndexerAccess = (BoundIndexOrRangeImplicitIndexerAccess)expr; + var refKind = implicitIndexerAccess.UnderlyingIndexerOrSliceSymbol switch { PropertySymbol p => p.RefKind, MethodSymbol m => m.RefKind, - _ => throw ExceptionUtilities.UnexpectedValue(patternIndexer.UnderlyingIndexerOrSliceSymbol) + _ => throw ExceptionUtilities.UnexpectedValue(implicitIndexerAccess.UnderlyingIndexerOrSliceSymbol) }; return refKind != RefKind.None; diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs index f23ae886aa91..fce25586ad8d 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs @@ -587,8 +587,8 @@ private BoundExpression TransformCompoundAssignmentLHS(BoundExpression originalL case BoundKind.IndexOrRangeImplicitIndexerAccess: { - var patternIndexerAccess = (BoundIndexOrRangeImplicitIndexerAccess)originalLHS; - RefKind refKind = patternIndexerAccess.UnderlyingIndexerOrSliceSymbol switch + var implicitIndexerAccess = (BoundIndexOrRangeImplicitIndexerAccess)originalLHS; + RefKind refKind = implicitIndexerAccess.UnderlyingIndexerOrSliceSymbol switch { PropertySymbol p => p.RefKind, MethodSymbol m => m.RefKind, @@ -596,7 +596,7 @@ private BoundExpression TransformCompoundAssignmentLHS(BoundExpression originalL }; if (refKind == RefKind.None) { - return TransformImplicitIndexerAccess(patternIndexerAccess, stores, temps, isDynamicAssignment); + return TransformImplicitIndexerAccess(implicitIndexerAccess, stores, temps, isDynamicAssignment); } } break; diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests3.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests3.cs index 1b67c02c8ea8..c683ac336649 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests3.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests3.cs @@ -21,7 +21,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests public class PatternMatchingTests3 : PatternMatchingTestBase { [Fact] - public void PropertyUnderlyingIndexerOrSliceSymbolInfo_01() + public void PropertyPatternSymbolInfo_01() { var source = @" @@ -66,7 +66,7 @@ class Point } [Fact] - public void PropertyUnderlyingIndexerOrSliceSymbolInfo_02() + public void PropertyPatternSymbolInfo_02() { var source = @" @@ -131,7 +131,7 @@ interface Point : I1, I2 } [Fact] - public void PropertyUnderlyingIndexerOrSliceSymbolInfo_03() + public void PropertyPatternSymbolInfo_03() { var source = @" @@ -178,7 +178,7 @@ class Point } [Fact] - public void PositionalUnderlyingIndexerOrSliceSymbolInfo_01() + public void PositionalPatternSymbolInfo_01() { var source = @" @@ -223,7 +223,7 @@ class Point } [Fact] - public void PositionalUnderlyingIndexerOrSliceSymbolInfo_02() + public void PositionalPatternSymbolInfo_02() { var source = @" @@ -276,7 +276,7 @@ class Point } [Fact] - public void PositionalUnderlyingIndexerOrSliceSymbolInfo_03() + public void PositionalPatternSymbolInfo_03() { var source = @" @@ -317,7 +317,7 @@ public static void Main() } [Fact] - public void PositionalUnderlyingIndexerOrSliceSymbolInfo_04() + public void PositionalPatternSymbolInfo_04() { var source = @" From 1c3417a7712c1b1b72fac4acaa2461237975ecc1 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Mon, 1 Nov 2021 09:53:40 -0700 Subject: [PATCH 05/29] fix incorrect rename --- .../CSharp/Portable/Compilation/CSharpSemanticModel.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs index 843263583c2a..85f47a5192e7 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs @@ -1981,14 +1981,14 @@ internal SymbolInfo GetSymbolInfoForNode( return SymbolInfoFactory.Create(symbols, resultKind, isDynamic); } - private static SymbolInfo GetSymbolInfoForSubpattern(Symbol subunderlyingIndexerOrSliceSymbol) + private static SymbolInfo GetSymbolInfoForSubpattern(Symbol subpatternSymbol) { - if (subunderlyingIndexerOrSliceSymbol?.OriginalDefinition is ErrorTypeSymbol originalErrorType) + if (subpatternSymbol?.OriginalDefinition is ErrorTypeSymbol originalErrorType) { return new SymbolInfo(symbol: null, originalErrorType.CandidateSymbols.GetPublicSymbols(), originalErrorType.ResultKind.ToCandidateReason()); } - return new SymbolInfo(subunderlyingIndexerOrSliceSymbol.GetPublicSymbol(), CandidateReason.None); + return new SymbolInfo(subpatternSymbol.GetPublicSymbol(), CandidateReason.None); } private SymbolInfo GetSymbolInfoForDeconstruction(BoundRecursivePattern pat) From 51be8ebba0f10f4582471eacb9eb289ca0dc8dc0 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Mon, 1 Nov 2021 17:13:08 -0700 Subject: [PATCH 06/29] Revert most of the renaming --- .../Portable/Binder/Binder.ValueChecks.cs | 40 +++++----- .../Portable/Binder/Binder_Expressions.cs | 4 +- .../CSharp/Portable/Binder/Binder_Patterns.cs | 12 +-- .../Portable/Binder/Binder_Statements.cs | 6 +- .../CSharp/Portable/BoundTree/BoundNodes.xml | 6 +- .../CSharp/Portable/BoundTree/Expression.cs | 2 +- .../Compilation/CSharpSemanticModel.cs | 10 +-- .../Portable/FlowAnalysis/AbstractFlowPass.cs | 6 +- .../Portable/FlowAnalysis/NullableWalker.cs | 6 +- .../Generated/BoundNodes.xml.Generated.cs | 80 +++++++++---------- .../DiagnosticsPass_ExpressionTrees.cs | 4 +- .../Lowering/LocalRewriter/LocalRewriter.cs | 10 +-- .../LocalRewriter_AssignmentOperator.cs | 6 +- ...ocalRewriter_CompoundAssignmentOperator.cs | 10 +-- .../LocalRewriter_IndexerAccess.cs | 10 +-- .../Operations/CSharpOperationFactory.cs | 2 +- 16 files changed, 107 insertions(+), 107 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index 5e0fd185242b..31dae822d5cd 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -398,16 +398,16 @@ internal bool CheckValueKind(SyntaxNode node, BoundExpression expr, BindValueKin case BoundKind.IndexerAccess: return CheckPropertyValueKind(node, expr, valueKind, checkingReceiver, diagnostics); - case BoundKind.IndexOrRangeImplicitIndexerAccess: - var implicitIndexer = (BoundIndexOrRangeImplicitIndexerAccess)expr; - if (implicitIndexer.UnderlyingIndexerOrSliceSymbol.Kind == SymbolKind.Property) + case BoundKind.IndexOrRangePatternIndexerAccess: + var implicitIndexer = (BoundIndexOrRangePatternIndexerAccess)expr; + if (implicitIndexer.PatternSymbol.Kind == SymbolKind.Property) { - // If this is an Index indexer, UnderlyingIndexerOrSliceSymbol should be a property, pointing to the + // If this is an Index indexer, PatternSymbol should be a property, pointing to the // implicit indexer. If it's a Range access, it will be a method, pointing to a Slice method // and it's handled below as part of invocations. return CheckPropertyValueKind(node, expr, valueKind, checkingReceiver, diagnostics); } - Debug.Assert(implicitIndexer.UnderlyingIndexerOrSliceSymbol.Kind == SymbolKind.Method); + Debug.Assert(implicitIndexer.PatternSymbol.Kind == SymbolKind.Method); break; case BoundKind.EventAccess: @@ -589,12 +589,12 @@ internal bool CheckValueKind(SyntaxNode node, BoundExpression expr, BindValueKin checkingReceiver, diagnostics); - case BoundKind.IndexOrRangeImplicitIndexerAccess: - var implicitIndexerAccess = (BoundIndexOrRangeImplicitIndexerAccess)expr; + case BoundKind.IndexOrRangePatternIndexerAccess: + var implicitIndexerAccess = (BoundIndexOrRangePatternIndexerAccess)expr; // If we got here this should be an implicit indexer taking a Range, // meaning that the pattern symbol must be a method (either Slice or Substring) return CheckMethodReturnValueKind( - (MethodSymbol)implicitIndexerAccess.UnderlyingIndexerOrSliceSymbol, + (MethodSymbol)implicitIndexerAccess.PatternSymbol, implicitIndexerAccess.Syntax, node, valueKind, @@ -2347,12 +2347,12 @@ internal static bool CheckRefEscape(SyntaxNode node, BoundExpression expr, uint diagnostics, isRefEscape: true); - case BoundKind.IndexOrRangeImplicitIndexerAccess: - var implicitIndexer = (BoundIndexOrRangeImplicitIndexerAccess)expr; + case BoundKind.IndexOrRangePatternIndexerAccess: + var implicitIndexer = (BoundIndexOrRangePatternIndexerAccess)expr; RefKind refKind; ImmutableArray parameters; - switch (implicitIndexer.UnderlyingIndexerOrSliceSymbol) + switch (implicitIndexer.PatternSymbol) { case PropertySymbol p: refKind = p.RefKind; @@ -2373,7 +2373,7 @@ internal static bool CheckRefEscape(SyntaxNode node, BoundExpression expr, uint return CheckInvocationEscape( implicitIndexer.Syntax, - implicitIndexer.UnderlyingIndexerOrSliceSymbol, + implicitIndexer.PatternSymbol, implicitIndexer.Receiver, parameters, ImmutableArray.Create(implicitIndexer.Argument), @@ -2618,17 +2618,17 @@ internal static uint GetValEscape(BoundExpression expr, uint scopeOfTheContainin scopeOfTheContainingExpression, isRefEscape: false); - case BoundKind.IndexOrRangeImplicitIndexerAccess: - var implicitIndexerAccess = (BoundIndexOrRangeImplicitIndexerAccess)expr; - var parameters = implicitIndexerAccess.UnderlyingIndexerOrSliceSymbol switch + case BoundKind.IndexOrRangePatternIndexerAccess: + var implicitIndexerAccess = (BoundIndexOrRangePatternIndexerAccess)expr; + var parameters = implicitIndexerAccess.PatternSymbol switch { PropertySymbol p => p.Parameters, MethodSymbol m => m.Parameters, - _ => throw ExceptionUtilities.UnexpectedValue(implicitIndexerAccess.UnderlyingIndexerOrSliceSymbol) + _ => throw ExceptionUtilities.UnexpectedValue(implicitIndexerAccess.PatternSymbol) }; return GetInvocationEscapeScope( - implicitIndexerAccess.UnderlyingIndexerOrSliceSymbol, + implicitIndexerAccess.PatternSymbol, implicitIndexerAccess.Receiver, parameters, default, @@ -3040,9 +3040,9 @@ internal static bool CheckValEscape(SyntaxNode node, BoundExpression expr, uint diagnostics, isRefEscape: false); - case BoundKind.IndexOrRangeImplicitIndexerAccess: - var implicitIndexerAccess = (BoundIndexOrRangeImplicitIndexerAccess)expr; - var underlyingIndexerOrSliceSymbol = implicitIndexerAccess.UnderlyingIndexerOrSliceSymbol; + case BoundKind.IndexOrRangePatternIndexerAccess: + var implicitIndexerAccess = (BoundIndexOrRangePatternIndexerAccess)expr; + var underlyingIndexerOrSliceSymbol = implicitIndexerAccess.PatternSymbol; var parameters = underlyingIndexerOrSliceSymbol switch { PropertySymbol p => p.Parameters, diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index 070a0807005b..e5952a8ddac6 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -8027,7 +8027,7 @@ private bool TryBindIndexOrRangeImplicitIndexer( BoundExpression? receiverOpt, AnalyzedArguments arguments, BindingDiagnosticBag diagnostics, - [NotNullWhen(true)] out BoundIndexOrRangeImplicitIndexerAccess? implicitIndexerAccess) + [NotNullWhen(true)] out BoundIndexOrRangePatternIndexerAccess? implicitIndexerAccess) { implicitIndexerAccess = null; @@ -8061,7 +8061,7 @@ private bool TryBindIndexOrRangeImplicitIndexer( return false; } - implicitIndexerAccess = new BoundIndexOrRangeImplicitIndexerAccess( + implicitIndexerAccess = new BoundIndexOrRangePatternIndexerAccess( syntax, receiverOpt, lengthOrCountProperty, diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs index 82ee67ff350a..ad8db1fcf313 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs @@ -376,7 +376,7 @@ private bool TryBindIndexerForPattern( lookupResult.Clear(); var analyzedArguments = AnalyzedArguments.GetInstance(); - analyzedArguments.Arguments.Add(new BoundIndexOrRangeImplicitIndexerValuePlaceholder(syntax, argType)); + analyzedArguments.Arguments.Add(new BoundIndexOrRangeIndexerPatternValuePlaceholder(syntax, argType)); var receiver = new BoundImplicitReceiver(syntax, receiverType); BoundExpression boundAccess = BindIndexerOrIndexedPropertyAccess(syntax, receiver, indexerGroup, analyzedArguments, bindingDiagnostics); switch (boundAccess) @@ -395,9 +395,9 @@ private bool TryBindIndexerForPattern( } break; - case BoundIndexOrRangeImplicitIndexerAccess boundIndexOrRangeImplicitIndexerAccess: - lengthProperty = boundIndexOrRangeImplicitIndexerAccess.LengthOrCountProperty; - indexerOrSliceSymbol = boundIndexOrRangeImplicitIndexerAccess.UnderlyingIndexerOrSliceSymbol; + case BoundIndexOrRangePatternIndexerAccess boundIndexOrRangePatternIndexerAccess: + lengthProperty = boundIndexOrRangePatternIndexerAccess.LengthOrCountProperty; + indexerOrSliceSymbol = boundIndexOrRangePatternIndexerAccess.PatternSymbol; found = true; break; @@ -425,8 +425,8 @@ private bool TryBindIndexerForPattern( { if (indexerOrSliceSymbol is not null) { - var implicitIndexerAccess = new BoundIndexOrRangeImplicitIndexerAccess(syntax, new BoundImplicitReceiver(syntax, receiverType), - lengthProperty, indexerOrSliceSymbol, argument: new BoundIndexOrRangeImplicitIndexerValuePlaceholder(syntax, argType), indexerOrSliceSymbol.GetTypeOrReturnType().Type); + var implicitIndexerAccess = new BoundIndexOrRangePatternIndexerAccess(syntax, new BoundImplicitReceiver(syntax, receiverType), + lengthProperty, indexerOrSliceSymbol, argument: new BoundIndexOrRangeIndexerPatternValuePlaceholder(syntax, argType), indexerOrSliceSymbol.GetTypeOrReturnType().Type); if (!CheckValueKind(syntax, implicitIndexerAccess, BindValueKind.RValue, checkingReceiver: false, bindingDiagnostics)) { diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index e90c2d1f65b7..3c535c70e614 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -1556,11 +1556,11 @@ private static PropertySymbol GetPropertySymbol(BoundExpression expr, out BoundE propertySymbol = indexerAccess.Indexer; } break; - case BoundKind.IndexOrRangeImplicitIndexerAccess: + case BoundKind.IndexOrRangePatternIndexerAccess: { - var implicitIndexerAccess = (BoundIndexOrRangeImplicitIndexerAccess)expr; + var implicitIndexerAccess = (BoundIndexOrRangePatternIndexerAccess)expr; receiver = implicitIndexerAccess.Receiver; - propertySymbol = (PropertySymbol)implicitIndexerAccess.UnderlyingIndexerOrSliceSymbol; + propertySymbol = (PropertySymbol)implicitIndexerAccess.PatternSymbol; } break; default: diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml index 27d310249b02..d36936e5a08d 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml @@ -134,7 +134,7 @@ This node is used to represent an expression of a certain type, when attempting to bind its Index or Range implicit indexer It does survive past initial binding but will be replaced with a synthesized argument in DAG lowering. --> - + @@ -2009,12 +2009,12 @@ - + - + diff --git a/src/Compilers/CSharp/Portable/BoundTree/Expression.cs b/src/Compilers/CSharp/Portable/BoundTree/Expression.cs index 124fb18b4c3d..ab4a498be4c8 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/Expression.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/Expression.cs @@ -175,7 +175,7 @@ internal partial class BoundPassByCopy protected override ImmutableArray Children => ImmutableArray.Create(this.Expression); } - internal partial class BoundIndexOrRangeImplicitIndexerAccess + internal partial class BoundIndexOrRangePatternIndexerAccess { protected override ImmutableArray Children => ImmutableArray.Create(Receiver, Argument); } diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs index 85f47a5192e7..41190752bebd 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs @@ -3427,16 +3427,16 @@ private ImmutableArray GetSemanticSymbols( } break; - case BoundKind.IndexOrRangeImplicitIndexerAccess: + case BoundKind.IndexOrRangePatternIndexerAccess: { - var indexerAccess = (BoundIndexOrRangeImplicitIndexerAccess)boundNode; + var indexerAccess = (BoundIndexOrRangePatternIndexerAccess)boundNode; resultKind = indexerAccess.ResultKind; - // The only time a BoundIndexOrRangeImplicitIndexerAccess is created, overload resolution succeeded + // The only time a BoundIndexOrRangePatternIndexerAccess is created, overload resolution succeeded // and returned only 1 result - Debug.Assert(indexerAccess.UnderlyingIndexerOrSliceSymbol is object); - symbols = ImmutableArray.Create(indexerAccess.UnderlyingIndexerOrSliceSymbol); + Debug.Assert(indexerAccess.PatternSymbol is object); + symbols = ImmutableArray.Create(indexerAccess.PatternSymbol); } break; diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs index 877d944d8cd5..93658710f94b 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs @@ -1433,7 +1433,7 @@ public override BoundNode VisitIndexerAccess(BoundIndexerAccess node) return null; } - public override BoundNode VisitIndexOrRangeImplicitIndexerAccess(BoundIndexOrRangeImplicitIndexerAccess node) + public override BoundNode VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node) { // Index or Range implicit indexers evaluate the following in order: // 1. The receiver @@ -1444,11 +1444,11 @@ public override BoundNode VisitIndexOrRangeImplicitIndexerAccess(BoundIndexOrRan var method = GetReadMethod(node.LengthOrCountProperty); VisitReceiverAfterCall(node.Receiver, method); VisitRvalue(node.Argument); - method = node.UnderlyingIndexerOrSliceSymbol switch + method = node.PatternSymbol switch { PropertySymbol p => GetReadMethod(p), MethodSymbol m => m, - _ => throw ExceptionUtilities.UnexpectedValue(node.UnderlyingIndexerOrSliceSymbol) + _ => throw ExceptionUtilities.UnexpectedValue(node.PatternSymbol) }; VisitReceiverAfterCall(node.Receiver, method); diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index 0c41fd6b83dd..9474ebfef511 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -8695,7 +8695,7 @@ private TypeWithAnnotations GetDeclaredParameterResult(ParameterSymbol parameter return null; } - public override BoundNode? VisitIndexOrRangeImplicitIndexerAccess(BoundIndexOrRangeImplicitIndexerAccess node) + public override BoundNode? VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node) { BoundExpression receiver = node.Receiver; var receiverType = VisitRvalueWithState(receiver).Type; @@ -8704,14 +8704,14 @@ private TypeWithAnnotations GetDeclaredParameterResult(ParameterSymbol parameter _ = CheckPossibleNullReceiver(receiver); VisitRvalue(node.Argument); - var underlyingIndexerOrSliceSymbol = node.UnderlyingIndexerOrSliceSymbol; + var underlyingIndexerOrSliceSymbol = node.PatternSymbol; if (receiverType is object) { underlyingIndexerOrSliceSymbol = AsMemberOfType(receiverType, underlyingIndexerOrSliceSymbol); } SetLvalueResultType(node, underlyingIndexerOrSliceSymbol.GetTypeOrReturnType()); - SetUpdatedSymbol(node, node.UnderlyingIndexerOrSliceSymbol, underlyingIndexerOrSliceSymbol); + SetUpdatedSymbol(node, node.PatternSymbol, underlyingIndexerOrSliceSymbol); return null; } diff --git a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs index 3e936cc0d5c4..ffe202732660 100644 --- a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs @@ -30,7 +30,7 @@ internal enum BoundKind : byte AwaitableValuePlaceholder, DisposableValuePlaceholder, ObjectOrCollectionValuePlaceholder, - IndexOrRangeImplicitIndexerValuePlaceholder, + IndexOrRangeIndexerPatternValuePlaceholder, Dup, PassByCopy, BadExpression, @@ -197,7 +197,7 @@ internal enum BoundKind : byte PropertyAccess, EventAccess, IndexerAccess, - IndexOrRangeImplicitIndexerAccess, + IndexOrRangePatternIndexerAccess, DynamicIndexerAccess, Lambda, UnboundLambda, @@ -640,18 +640,18 @@ public BoundObjectOrCollectionValuePlaceholder Update(bool isNewInstance, TypeSy } } - internal sealed partial class BoundIndexOrRangeImplicitIndexerValuePlaceholder : BoundValuePlaceholderBase + internal sealed partial class BoundIndexOrRangeIndexerPatternValuePlaceholder : BoundValuePlaceholderBase { - public BoundIndexOrRangeImplicitIndexerValuePlaceholder(SyntaxNode syntax, TypeSymbol type, bool hasErrors) - : base(BoundKind.IndexOrRangeImplicitIndexerValuePlaceholder, syntax, type, hasErrors) + public BoundIndexOrRangeIndexerPatternValuePlaceholder(SyntaxNode syntax, TypeSymbol type, bool hasErrors) + : base(BoundKind.IndexOrRangeIndexerPatternValuePlaceholder, syntax, type, hasErrors) { RoslynDebug.Assert(type is object, "Field 'type' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); } - public BoundIndexOrRangeImplicitIndexerValuePlaceholder(SyntaxNode syntax, TypeSymbol type) - : base(BoundKind.IndexOrRangeImplicitIndexerValuePlaceholder, syntax, type) + public BoundIndexOrRangeIndexerPatternValuePlaceholder(SyntaxNode syntax, TypeSymbol type) + : base(BoundKind.IndexOrRangeIndexerPatternValuePlaceholder, syntax, type) { RoslynDebug.Assert(type is object, "Field 'type' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); @@ -661,13 +661,13 @@ public BoundIndexOrRangeImplicitIndexerValuePlaceholder(SyntaxNode syntax, TypeS public new TypeSymbol Type => base.Type!; [DebuggerStepThrough] - public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitIndexOrRangeImplicitIndexerValuePlaceholder(this); + public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitIndexOrRangeIndexerPatternValuePlaceholder(this); - public BoundIndexOrRangeImplicitIndexerValuePlaceholder Update(TypeSymbol type) + public BoundIndexOrRangeIndexerPatternValuePlaceholder Update(TypeSymbol type) { if (!TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) { - var result = new BoundIndexOrRangeImplicitIndexerValuePlaceholder(this.Syntax, type, this.HasErrors); + var result = new BoundIndexOrRangeIndexerPatternValuePlaceholder(this.Syntax, type, this.HasErrors); result.CopyAttributes(this); return result; } @@ -7102,10 +7102,10 @@ public BoundIndexerAccess Update(BoundExpression? receiverOpt, PropertySymbol in } } - internal sealed partial class BoundIndexOrRangeImplicitIndexerAccess : BoundExpression + internal sealed partial class BoundIndexOrRangePatternIndexerAccess : BoundExpression { - public BoundIndexOrRangeImplicitIndexerAccess(SyntaxNode syntax, BoundExpression receiver, PropertySymbol lengthOrCountProperty, Symbol underlyingIndexerOrSliceSymbol, BoundExpression argument, TypeSymbol type, bool hasErrors = false) - : base(BoundKind.IndexOrRangeImplicitIndexerAccess, syntax, type, hasErrors || receiver.HasErrors() || argument.HasErrors()) + public BoundIndexOrRangePatternIndexerAccess(SyntaxNode syntax, BoundExpression receiver, PropertySymbol lengthOrCountProperty, Symbol underlyingIndexerOrSliceSymbol, BoundExpression argument, TypeSymbol type, bool hasErrors = false) + : base(BoundKind.IndexOrRangePatternIndexerAccess, syntax, type, hasErrors || receiver.HasErrors() || argument.HasErrors()) { RoslynDebug.Assert(receiver is object, "Field 'receiver' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); @@ -7116,7 +7116,7 @@ public BoundIndexOrRangeImplicitIndexerAccess(SyntaxNode syntax, BoundExpression this.Receiver = receiver; this.LengthOrCountProperty = lengthOrCountProperty; - this.UnderlyingIndexerOrSliceSymbol = underlyingIndexerOrSliceSymbol; + this.PatternSymbol = underlyingIndexerOrSliceSymbol; this.Argument = argument; } @@ -7127,17 +7127,17 @@ public BoundIndexOrRangeImplicitIndexerAccess(SyntaxNode syntax, BoundExpression public PropertySymbol LengthOrCountProperty { get; } - public Symbol UnderlyingIndexerOrSliceSymbol { get; } + public Symbol PatternSymbol { get; } public BoundExpression Argument { get; } [DebuggerStepThrough] - public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitIndexOrRangeImplicitIndexerAccess(this); + public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitIndexOrRangePatternIndexerAccess(this); - public BoundIndexOrRangeImplicitIndexerAccess Update(BoundExpression receiver, PropertySymbol lengthOrCountProperty, Symbol underlyingIndexerOrSliceSymbol, BoundExpression argument, TypeSymbol type) + public BoundIndexOrRangePatternIndexerAccess Update(BoundExpression receiver, PropertySymbol lengthOrCountProperty, Symbol underlyingIndexerOrSliceSymbol, BoundExpression argument, TypeSymbol type) { - if (receiver != this.Receiver || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(lengthOrCountProperty, this.LengthOrCountProperty) || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(underlyingIndexerOrSliceSymbol, this.UnderlyingIndexerOrSliceSymbol) || argument != this.Argument || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) + if (receiver != this.Receiver || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(lengthOrCountProperty, this.LengthOrCountProperty) || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(underlyingIndexerOrSliceSymbol, this.PatternSymbol) || argument != this.Argument || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) { - var result = new BoundIndexOrRangeImplicitIndexerAccess(this.Syntax, receiver, lengthOrCountProperty, underlyingIndexerOrSliceSymbol, argument, type, this.HasErrors); + var result = new BoundIndexOrRangePatternIndexerAccess(this.Syntax, receiver, lengthOrCountProperty, underlyingIndexerOrSliceSymbol, argument, type, this.HasErrors); result.CopyAttributes(this); return result; } @@ -8489,8 +8489,8 @@ internal R VisitInternal(BoundNode node, A arg) return VisitDisposableValuePlaceholder((BoundDisposableValuePlaceholder)node, arg); case BoundKind.ObjectOrCollectionValuePlaceholder: return VisitObjectOrCollectionValuePlaceholder((BoundObjectOrCollectionValuePlaceholder)node, arg); - case BoundKind.IndexOrRangeImplicitIndexerValuePlaceholder: - return VisitIndexOrRangeImplicitIndexerValuePlaceholder((BoundIndexOrRangeImplicitIndexerValuePlaceholder)node, arg); + case BoundKind.IndexOrRangeIndexerPatternValuePlaceholder: + return VisitIndexOrRangeIndexerPatternValuePlaceholder((BoundIndexOrRangeIndexerPatternValuePlaceholder)node, arg); case BoundKind.Dup: return VisitDup((BoundDup)node, arg); case BoundKind.PassByCopy: @@ -8823,8 +8823,8 @@ internal R VisitInternal(BoundNode node, A arg) return VisitEventAccess((BoundEventAccess)node, arg); case BoundKind.IndexerAccess: return VisitIndexerAccess((BoundIndexerAccess)node, arg); - case BoundKind.IndexOrRangeImplicitIndexerAccess: - return VisitIndexOrRangeImplicitIndexerAccess((BoundIndexOrRangeImplicitIndexerAccess)node, arg); + case BoundKind.IndexOrRangePatternIndexerAccess: + return VisitIndexOrRangePatternIndexerAccess((BoundIndexOrRangePatternIndexerAccess)node, arg); case BoundKind.DynamicIndexerAccess: return VisitDynamicIndexerAccess((BoundDynamicIndexerAccess)node, arg); case BoundKind.Lambda: @@ -8913,7 +8913,7 @@ internal abstract partial class BoundTreeVisitor public virtual R VisitAwaitableValuePlaceholder(BoundAwaitableValuePlaceholder node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitDisposableValuePlaceholder(BoundDisposableValuePlaceholder node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitObjectOrCollectionValuePlaceholder(BoundObjectOrCollectionValuePlaceholder node, A arg) => this.DefaultVisit(node, arg); - public virtual R VisitIndexOrRangeImplicitIndexerValuePlaceholder(BoundIndexOrRangeImplicitIndexerValuePlaceholder node, A arg) => this.DefaultVisit(node, arg); + public virtual R VisitIndexOrRangeIndexerPatternValuePlaceholder(BoundIndexOrRangeIndexerPatternValuePlaceholder node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitDup(BoundDup node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitPassByCopy(BoundPassByCopy node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitBadExpression(BoundBadExpression node, A arg) => this.DefaultVisit(node, arg); @@ -9080,7 +9080,7 @@ internal abstract partial class BoundTreeVisitor public virtual R VisitPropertyAccess(BoundPropertyAccess node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitEventAccess(BoundEventAccess node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitIndexerAccess(BoundIndexerAccess node, A arg) => this.DefaultVisit(node, arg); - public virtual R VisitIndexOrRangeImplicitIndexerAccess(BoundIndexOrRangeImplicitIndexerAccess node, A arg) => this.DefaultVisit(node, arg); + public virtual R VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitDynamicIndexerAccess(BoundDynamicIndexerAccess node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitLambda(BoundLambda node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitUnboundLambda(UnboundLambda node, A arg) => this.DefaultVisit(node, arg); @@ -9130,7 +9130,7 @@ internal abstract partial class BoundTreeVisitor public virtual BoundNode? VisitAwaitableValuePlaceholder(BoundAwaitableValuePlaceholder node) => this.DefaultVisit(node); public virtual BoundNode? VisitDisposableValuePlaceholder(BoundDisposableValuePlaceholder node) => this.DefaultVisit(node); public virtual BoundNode? VisitObjectOrCollectionValuePlaceholder(BoundObjectOrCollectionValuePlaceholder node) => this.DefaultVisit(node); - public virtual BoundNode? VisitIndexOrRangeImplicitIndexerValuePlaceholder(BoundIndexOrRangeImplicitIndexerValuePlaceholder node) => this.DefaultVisit(node); + public virtual BoundNode? VisitIndexOrRangeIndexerPatternValuePlaceholder(BoundIndexOrRangeIndexerPatternValuePlaceholder node) => this.DefaultVisit(node); public virtual BoundNode? VisitDup(BoundDup node) => this.DefaultVisit(node); public virtual BoundNode? VisitPassByCopy(BoundPassByCopy node) => this.DefaultVisit(node); public virtual BoundNode? VisitBadExpression(BoundBadExpression node) => this.DefaultVisit(node); @@ -9297,7 +9297,7 @@ internal abstract partial class BoundTreeVisitor public virtual BoundNode? VisitPropertyAccess(BoundPropertyAccess node) => this.DefaultVisit(node); public virtual BoundNode? VisitEventAccess(BoundEventAccess node) => this.DefaultVisit(node); public virtual BoundNode? VisitIndexerAccess(BoundIndexerAccess node) => this.DefaultVisit(node); - public virtual BoundNode? VisitIndexOrRangeImplicitIndexerAccess(BoundIndexOrRangeImplicitIndexerAccess node) => this.DefaultVisit(node); + public virtual BoundNode? VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node) => this.DefaultVisit(node); public virtual BoundNode? VisitDynamicIndexerAccess(BoundDynamicIndexerAccess node) => this.DefaultVisit(node); public virtual BoundNode? VisitLambda(BoundLambda node) => this.DefaultVisit(node); public virtual BoundNode? VisitUnboundLambda(UnboundLambda node) => this.DefaultVisit(node); @@ -9363,7 +9363,7 @@ internal abstract partial class BoundTreeWalker : BoundTreeVisitor public override BoundNode? VisitAwaitableValuePlaceholder(BoundAwaitableValuePlaceholder node) => null; public override BoundNode? VisitDisposableValuePlaceholder(BoundDisposableValuePlaceholder node) => null; public override BoundNode? VisitObjectOrCollectionValuePlaceholder(BoundObjectOrCollectionValuePlaceholder node) => null; - public override BoundNode? VisitIndexOrRangeImplicitIndexerValuePlaceholder(BoundIndexOrRangeImplicitIndexerValuePlaceholder node) => null; + public override BoundNode? VisitIndexOrRangeIndexerPatternValuePlaceholder(BoundIndexOrRangeIndexerPatternValuePlaceholder node) => null; public override BoundNode? VisitDup(BoundDup node) => null; public override BoundNode? VisitPassByCopy(BoundPassByCopy node) { @@ -10130,7 +10130,7 @@ internal abstract partial class BoundTreeWalker : BoundTreeVisitor this.VisitList(node.Arguments); return null; } - public override BoundNode? VisitIndexOrRangeImplicitIndexerAccess(BoundIndexOrRangeImplicitIndexerAccess node) + public override BoundNode? VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node) { this.Visit(node.Receiver); this.Visit(node.Argument); @@ -10358,7 +10358,7 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor TypeSymbol? type = this.VisitType(node.Type); return node.Update(node.IsNewInstance, type); } - public override BoundNode? VisitIndexOrRangeImplicitIndexerValuePlaceholder(BoundIndexOrRangeImplicitIndexerValuePlaceholder node) + public override BoundNode? VisitIndexOrRangeIndexerPatternValuePlaceholder(BoundIndexOrRangeIndexerPatternValuePlaceholder node) { TypeSymbol? type = this.VisitType(node.Type); return node.Update(type); @@ -11357,12 +11357,12 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor TypeSymbol? type = this.VisitType(node.Type); return node.Update(receiverOpt, node.Indexer, arguments, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, node.Expanded, node.ArgsToParamsOpt, node.DefaultArguments, node.OriginalIndexersOpt, type); } - public override BoundNode? VisitIndexOrRangeImplicitIndexerAccess(BoundIndexOrRangeImplicitIndexerAccess node) + public override BoundNode? VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node) { BoundExpression receiver = (BoundExpression)this.Visit(node.Receiver); BoundExpression argument = (BoundExpression)this.Visit(node.Argument); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(receiver, node.LengthOrCountProperty, node.UnderlyingIndexerOrSliceSymbol, argument, type); + return node.Update(receiver, node.LengthOrCountProperty, node.PatternSymbol, argument, type); } public override BoundNode? VisitDynamicIndexerAccess(BoundDynamicIndexerAccess node) { @@ -11707,14 +11707,14 @@ public NullabilityRewriter(ImmutableDictionary new TreeDumperNode("indexOrRangeImplicitIndexerValuePlaceholder", null, new TreeDumperNode[] + public override TreeDumperNode VisitIndexOrRangeIndexerPatternValuePlaceholder(BoundIndexOrRangeIndexerPatternValuePlaceholder node, object? arg) => new TreeDumperNode("indexOrRangeImplicitIndexerValuePlaceholder", null, new TreeDumperNode[] { new TreeDumperNode("type", node.Type, null), new TreeDumperNode("isSuppressed", node.IsSuppressed, null), @@ -15732,11 +15732,11 @@ private BoundTreeDumperNodeProducer() new TreeDumperNode("hasErrors", node.HasErrors, null) } ); - public override TreeDumperNode VisitIndexOrRangeImplicitIndexerAccess(BoundIndexOrRangeImplicitIndexerAccess node, object? arg) => new TreeDumperNode("indexOrRangeImplicitIndexerAccess", null, new TreeDumperNode[] + public override TreeDumperNode VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node, object? arg) => new TreeDumperNode("indexOrRangeImplicitIndexerAccess", null, new TreeDumperNode[] { new TreeDumperNode("receiver", null, new TreeDumperNode[] { Visit(node.Receiver, null) }), new TreeDumperNode("lengthOrCountProperty", node.LengthOrCountProperty, null), - new TreeDumperNode("underlyingIndexerOrSliceSymbol", node.UnderlyingIndexerOrSliceSymbol, null), + new TreeDumperNode("underlyingIndexerOrSliceSymbol", node.PatternSymbol, null), new TreeDumperNode("argument", null, new TreeDumperNode[] { Visit(node.Argument, null) }), new TreeDumperNode("type", node.Type, null), new TreeDumperNode("isSuppressed", node.IsSuppressed, null), diff --git a/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs b/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs index bad1fdb98ee5..2f4b93cf5d24 100644 --- a/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs +++ b/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs @@ -97,14 +97,14 @@ public override BoundNode VisitArrayAccess(BoundArrayAccess node) return base.VisitArrayAccess(node); } - public override BoundNode VisitIndexOrRangeImplicitIndexerAccess(BoundIndexOrRangeImplicitIndexerAccess node) + public override BoundNode VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node) { if (_inExpressionLambda) { Error(ErrorCode.ERR_ExpressionTreeContainsPatternIndexOrRangeIndexer, node); } - return base.VisitIndexOrRangeImplicitIndexerAccess(node); + return base.VisitIndexOrRangePatternIndexerAccess(node); } public override BoundNode VisitFromEndIndexExpression(BoundFromEndIndexExpression node) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs index f73d26573d3a..faa82b4f8ecb 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs @@ -953,13 +953,13 @@ internal static bool CanBePassedByReference(BoundExpression expr) case BoundKind.IndexerAccess: return ((BoundIndexerAccess)expr).Indexer.RefKind != RefKind.None; - case BoundKind.IndexOrRangeImplicitIndexerAccess: - var implicitIndexerAccess = (BoundIndexOrRangeImplicitIndexerAccess)expr; - var refKind = implicitIndexerAccess.UnderlyingIndexerOrSliceSymbol switch + case BoundKind.IndexOrRangePatternIndexerAccess: + var implicitIndexerAccess = (BoundIndexOrRangePatternIndexerAccess)expr; + var refKind = implicitIndexerAccess.PatternSymbol switch { PropertySymbol p => p.RefKind, MethodSymbol m => m.RefKind, - _ => throw ExceptionUtilities.UnexpectedValue(implicitIndexerAccess.UnderlyingIndexerOrSliceSymbol) + _ => throw ExceptionUtilities.UnexpectedValue(implicitIndexerAccess.PatternSymbol) }; return refKind != RefKind.None; @@ -1048,7 +1048,7 @@ public static void Validate(BoundNode node) return null; } - public override BoundNode? VisitIndexOrRangeImplicitIndexerValuePlaceholder(BoundIndexOrRangeImplicitIndexerValuePlaceholder node) + public override BoundNode? VisitIndexOrRangeIndexerPatternValuePlaceholder(BoundIndexOrRangeIndexerPatternValuePlaceholder node) { Fail(node); return null; diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs index 8325f5aa2413..fce8d6336746 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs @@ -34,9 +34,9 @@ private BoundExpression VisitAssignmentOperator(BoundAssignmentOperator node, bo loweredLeft = VisitIndexerAccess((BoundIndexerAccess)left, isLeftOfAssignment: true); break; - case BoundKind.IndexOrRangeImplicitIndexerAccess: - loweredLeft = VisitIndexOrRangeImplicitIndexerAccess( - (BoundIndexOrRangeImplicitIndexerAccess)left, + case BoundKind.IndexOrRangePatternIndexerAccess: + loweredLeft = VisitIndexOrRangePatternIndexerAccess( + (BoundIndexOrRangePatternIndexerAccess)left, isLeftOfAssignment: true); break; diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs index fce25586ad8d..41b3f9c91aee 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs @@ -405,7 +405,7 @@ private BoundIndexerAccess TransformIndexerAccess(BoundIndexerAccess indexerAcce } private BoundExpression TransformImplicitIndexerAccess( - BoundIndexOrRangeImplicitIndexerAccess indexerAccess, + BoundIndexOrRangePatternIndexerAccess indexerAccess, ArrayBuilder stores, ArrayBuilder temps, bool isDynamicAssignment) @@ -416,7 +416,7 @@ private BoundExpression TransformImplicitIndexerAccess( // the only thing we need to do is lift the stores and temps out of // the sequence, and use the final expression as the new argument - var sequence = VisitIndexOrRangeImplicitIndexerAccess(indexerAccess, isLeftOfAssignment: true); + var sequence = VisitIndexOrRangePatternIndexerAccess(indexerAccess, isLeftOfAssignment: true); stores.AddRange(sequence.SideEffects); temps.AddRange(sequence.Locals); return TransformCompoundAssignmentLHS(sequence.Value, stores, temps, isDynamicAssignment); @@ -585,10 +585,10 @@ private BoundExpression TransformCompoundAssignmentLHS(BoundExpression originalL } break; - case BoundKind.IndexOrRangeImplicitIndexerAccess: + case BoundKind.IndexOrRangePatternIndexerAccess: { - var implicitIndexerAccess = (BoundIndexOrRangeImplicitIndexerAccess)originalLHS; - RefKind refKind = implicitIndexerAccess.UnderlyingIndexerOrSliceSymbol switch + var implicitIndexerAccess = (BoundIndexOrRangePatternIndexerAccess)originalLHS; + RefKind refKind = implicitIndexerAccess.PatternSymbol switch { PropertySymbol p => p.RefKind, MethodSymbol m => m.RefKind, diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs index 25964cd1abed..0b05fb180f9b 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs @@ -161,12 +161,12 @@ private BoundExpression MakeIndexerAccess( } } } - public override BoundNode VisitIndexOrRangeImplicitIndexerAccess(BoundIndexOrRangeImplicitIndexerAccess node) + public override BoundNode VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node) { - return VisitIndexOrRangeImplicitIndexerAccess(node, isLeftOfAssignment: false); + return VisitIndexOrRangePatternIndexerAccess(node, isLeftOfAssignment: false); } - private BoundSequence VisitIndexOrRangeImplicitIndexerAccess(BoundIndexOrRangeImplicitIndexerAccess node, bool isLeftOfAssignment) + private BoundSequence VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node, bool isLeftOfAssignment) { if (TypeSymbol.Equals( node.Argument.Type, @@ -177,7 +177,7 @@ private BoundSequence VisitIndexOrRangeImplicitIndexerAccess(BoundIndexOrRangeIm node.Syntax, node.Receiver, node.LengthOrCountProperty, - (PropertySymbol)node.UnderlyingIndexerOrSliceSymbol, + (PropertySymbol)node.PatternSymbol, node.Argument, isLeftOfAssignment: isLeftOfAssignment); } @@ -190,7 +190,7 @@ private BoundSequence VisitIndexOrRangeImplicitIndexerAccess(BoundIndexOrRangeIm return VisitRangeImplicitIndexerAccess( node.Receiver, node.LengthOrCountProperty, - (MethodSymbol)node.UnderlyingIndexerOrSliceSymbol, + (MethodSymbol)node.PatternSymbol, node.Argument); } } diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index 196046b70076..36f6d6fa3aea 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -300,7 +300,7 @@ public CSharpOperationFactory(SemanticModel semanticModel) case BoundKind.StackAllocArrayCreation: case BoundKind.TypeExpression: case BoundKind.TypeOrValueExpression: - case BoundKind.IndexOrRangeImplicitIndexerAccess: + case BoundKind.IndexOrRangePatternIndexerAccess: ConstantValue? constantValue = (boundNode as BoundExpression)?.ConstantValue; bool isImplicit = boundNode.WasCompilerGenerated; From c3f161858659acec40442701a73d7d3388c40fa2 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Mon, 1 Nov 2021 18:10:47 -0700 Subject: [PATCH 07/29] Use-site info --- .../Portable/Binder/Binder_Expressions.cs | 10 +--- .../CSharp/Portable/Binder/Binder_Patterns.cs | 3 + .../PatternMatchingTests_ListPatterns.cs | 55 +++++++++++++++++-- 3 files changed, 57 insertions(+), 11 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index e5952a8ddac6..171d27437574 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -7734,6 +7734,7 @@ private BoundExpression BindIndexerAccess(ExpressionSyntax node, BoundExpression LookupOptions lookupOptions = expr.Kind == BoundKind.BaseReference ? LookupOptions.UseBaseReferenceAccessibility : LookupOptions.Default; CompoundUseSiteInfo useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); this.LookupMembersWithFallback(lookupResult, expr.Type, WellKnownMemberNames.Indexer, arity: 0, useSiteInfo: ref useSiteInfo, options: lookupOptions); + diagnostics.Add(node, useSiteInfo); // Store, rather than return, so that we can release resources. BoundExpression indexerAccessExpression; @@ -7751,13 +7752,11 @@ private BoundExpression BindIndexerAccess(ExpressionSyntax node, BoundExpression } else { - diagnostics.Add(node, useSiteInfo); indexerAccessExpression = BadIndexerExpression(node, expr, analyzedArguments, lookupResult.Error, diagnostics); } } else { - diagnostics.Add(node, useSiteInfo); ArrayBuilder indexerGroup = ArrayBuilder.GetInstance(); foreach (Symbol symbol in lookupResult.Symbols) { @@ -7888,6 +7887,7 @@ private BoundExpression BindIndexerOrIndexedPropertyAccess( bool allowRefOmittedArguments = receiverOpt.IsExpressionOfComImportType(); CompoundUseSiteInfo useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); this.OverloadResolution.PropertyOverloadResolution(propertyGroup, receiverOpt, analyzedArguments, overloadResolutionResult, allowRefOmittedArguments, ref useSiteInfo); + diagnostics.Add(syntax, useSiteInfo); BoundExpression propertyAccess; if (analyzedArguments.HasDynamicArgument && overloadResolutionResult.HasAnyApplicableMember) @@ -7939,8 +7939,6 @@ private BoundExpression BindIndexerOrIndexedPropertyAccess( memberGroup: candidates, typeContainingConstructor: null, delegateTypeBeingInvoked: null); - - diagnostics.Add(syntax, useSiteInfo); } } @@ -7961,8 +7959,6 @@ private BoundExpression BindIndexerOrIndexedPropertyAccess( } else { - diagnostics.Add(syntax, useSiteInfo); - MemberResolutionResult resolutionResult = overloadResolutionResult.ValidResult; PropertySymbol property = resolutionResult.Member; RefKind? receiverRefKind = receiverOpt?.GetRefKind(); @@ -8229,7 +8225,7 @@ method.OriginalDefinition is var original && { indexerOrSliceSymbol = method; method.AddUseSiteInfo(ref useSiteInfo); - diagnostics.Add(syntax, useSiteInfo); + diagnostics.ReportUseSite(method, syntax); ReportDiagnosticsIfObsolete(diagnostics, method, syntax, hasBaseReceiver: false); CheckImplicitThisCopyInReadOnlyMember(receiverOpt, method, diagnostics); return true; diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs index ad8db1fcf313..c3cd70407d34 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs @@ -364,6 +364,7 @@ private bool TryBindIndexerForPattern( originalBinder: this, diagnose: false, ref useSiteInfo); + diagnostics.Add(syntax, useSiteInfo); if (lookupResult.IsMultiViable) { @@ -393,6 +394,8 @@ private bool TryBindIndexerForPattern( found = true; break; } + + // TODO2 useSiteInfo? break; case BoundIndexOrRangePatternIndexerAccess boundIndexOrRangePatternIndexerAccess: diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs index d8615041c9b7..d99bdaf444a8 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs @@ -812,7 +812,7 @@ public void M(string s) compilation.MakeMemberMissing(SpecialMember.System_String__Substring); compilation.VerifyEmitDiagnostics( // (6,19): error CS9001: Slice patterns may not be used for a value of type 'string'. - // _ = s is {.. var slice}; + // _ = s is [.. var slice]; Diagnostic(ErrorCode.ERR_UnsupportedTypeForSlicePattern, ".. var slice").WithArguments("string").WithLocation(6, 19), // (7,15): error CS1503: Argument 1: cannot convert from 'System.Range' to 'int' // _ = s[..]; @@ -889,6 +889,7 @@ public static void Main() var isCountable = hasLengthProp || hasCountProp; var isSliceable = implicitRange || explicitRange; var isIndexable = implicitIndex || explicitIndex; + // TODO2 var expectedDiagnostics = new[] { // (13,24): error CS9000: List patterns may not be used for a value of type 'X'. @@ -1375,7 +1376,7 @@ public static void Main() } compilation.VerifyEmitDiagnostics( // (14,29): error CS9001: Slice patterns may not be used for a value of type 'Test1'. - // _ = new Test1() is {..var p}; + // _ = new Test1() is [..var p]; Diagnostic(ErrorCode.ERR_UnsupportedTypeForSlicePattern, "..var p").WithArguments("Test1").WithLocation(14, 29)); } @@ -2187,17 +2188,32 @@ void M(C c) // (6,18): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // _ = c is [var item]; Diagnostic(ErrorCode.ERR_NoTypeDef, "[var item]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(6, 18), + // (6,18): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // _ = c is [var item]; + Diagnostic(ErrorCode.ERR_NoTypeDef, "[var item]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(6, 18), + // (7,18): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // _ = c is [..var rest]; + Diagnostic(ErrorCode.ERR_NoTypeDef, "[..var rest]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(7, 18), // (7,18): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // _ = c is [..var rest]; Diagnostic(ErrorCode.ERR_NoTypeDef, "[..var rest]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(7, 18), // (7,19): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // _ = c is [..var rest]; Diagnostic(ErrorCode.ERR_NoTypeDef, "..var rest").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(7, 19), + // (7,19): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // _ = c is [..var rest]; + Diagnostic(ErrorCode.ERR_NoTypeDef, "..var rest").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(7, 19), + // (8,21): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // var index = c[^1]; + Diagnostic(ErrorCode.ERR_NoTypeDef, "c[^1]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(8, 21), // (8,21): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // var index = c[^1]; Diagnostic(ErrorCode.ERR_NoTypeDef, "c[^1]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(8, 21), // (9,21): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // var range = c[1..^1]; + Diagnostic(ErrorCode.ERR_NoTypeDef, "c[1..^1]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(9, 21), + // (9,21): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // var range = c[1..^1]; Diagnostic(ErrorCode.ERR_NoTypeDef, "c[1..^1]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(9, 21) ); @@ -2263,8 +2279,23 @@ public class C } "; var compilation = CreateCompilation(source, references: new[] { lib2Ref }); - compilation.VerifyEmitDiagnostics(); - CompileAndVerify(compilation, expectedOutput: "(42, 43, 42, 43)"); + compilation.VerifyEmitDiagnostics( + // (4,10): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // if (c is [var item] && c is [..var slice]) + Diagnostic(ErrorCode.ERR_NoTypeDef, "[var item]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(4, 10), + // (4,29): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // if (c is [var item] && c is [..var slice]) + Diagnostic(ErrorCode.ERR_NoTypeDef, "[..var slice]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(4, 29), + // (4,30): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // if (c is [var item] && c is [..var slice]) + Diagnostic(ErrorCode.ERR_NoTypeDef, "..var slice").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(4, 30), + // (6,17): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // var item2 = c[^1]; + Diagnostic(ErrorCode.ERR_NoTypeDef, "c[^1]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(6, 17), + // (7,18): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // var slice2 = c[..]; + Diagnostic(ErrorCode.ERR_NoTypeDef, "c[..]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(7, 18) + ); } [Fact] @@ -2420,18 +2451,33 @@ public class C // (2,16): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // _ = new C() is [var x]; // 1 Diagnostic(ErrorCode.ERR_NoTypeDef, "[var x]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(2, 16), + // (2,16): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // _ = new C() is [var x]; // 1 + Diagnostic(ErrorCode.ERR_NoTypeDef, "[var x]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(2, 16), + // (3,16): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // _ = new C() is [.. var y]; // 2, 3 + Diagnostic(ErrorCode.ERR_NoTypeDef, "[.. var y]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(3, 16), // (3,16): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // _ = new C() is [.. var y]; // 2, 3 Diagnostic(ErrorCode.ERR_NoTypeDef, "[.. var y]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(3, 16), // (3,17): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // _ = new C() is [.. var y]; // 2, 3 Diagnostic(ErrorCode.ERR_NoTypeDef, ".. var y").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(3, 17), + // (3,17): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // _ = new C() is [.. var y]; // 2, 3 + Diagnostic(ErrorCode.ERR_NoTypeDef, ".. var y").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(3, 17), // (4,1): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // new C().Slice(0, 0); // 4 Diagnostic(ErrorCode.ERR_NoTypeDef, "new C().Slice").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(4, 1), // (5,5): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // _ = new C()[^1]; // 5 Diagnostic(ErrorCode.ERR_NoTypeDef, "new C()[^1]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(5, 5), + // (5,5): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // _ = new C()[^1]; // 5 + Diagnostic(ErrorCode.ERR_NoTypeDef, "new C()[^1]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(5, 5), + // (6,5): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // _ = new C()[..]; // 6 + Diagnostic(ErrorCode.ERR_NoTypeDef, "new C()[..]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(6, 5), // (6,5): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // _ = new C()[..]; // 6 Diagnostic(ErrorCode.ERR_NoTypeDef, "new C()[..]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(6, 5) @@ -4003,6 +4049,7 @@ void M(C c) } } "; + // TODO2 probably should be reporting a better error (the use-site error) var compilation = CreateCompilation(source, references: new[] { lib2Ref }); compilation.VerifyEmitDiagnostics( // (6,18): error CS9000: List patterns may not be used for a value of type 'C'. From d69d9201bef219383117e805b6fd85e8ab4b09a4 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Mon, 1 Nov 2021 21:04:53 -0700 Subject: [PATCH 08/29] WIP on refactoring --- .../Portable/Binder/Binder.ValueChecks.cs | 128 +- .../Portable/Binder/Binder_Expressions.cs | 182 +- .../CSharp/Portable/Binder/Binder_Patterns.cs | 253 +-- .../Portable/Binder/Binder_Statements.cs | 19 +- .../Binder/DecisionDagBuilder_ListPatterns.cs | 18 +- .../Portable/BoundTree/BoundDagEvaluation.cs | 4 +- .../BoundIndexOrRangePatternIndexerAccess.cs | 15 + .../CSharp/Portable/BoundTree/BoundNodes.xml | 99 +- .../Compilation/CSharpSemanticModel.cs | 13 +- .../Portable/FlowAnalysis/AbstractFlowPass.cs | 21 +- .../Portable/FlowAnalysis/NullableWalker.cs | 21 +- .../FlowAnalysis/NullableWalker_Patterns.cs | 72 +- .../Generated/BoundNodes.xml.Generated.cs | 561 +++++- .../LocalRewriter.PatternLocalRewriter.cs | 143 +- .../Lowering/LocalRewriter/LocalRewriter.cs | 68 +- ...ocalRewriter_CompoundAssignmentOperator.cs | 8 +- .../LocalRewriter_IndexerAccess.cs | 176 +- .../Operations/CSharpOperationFactory.cs | 6 +- .../Symbols/Source/SourceLocalSymbol.cs | 2 +- .../PatternMatchingTests_ListPatterns.cs | 1710 ++++++++++------- .../PatternParsingTests_ListPatterns.cs | 98 +- 21 files changed, 2260 insertions(+), 1357 deletions(-) create mode 100644 src/Compilers/CSharp/Portable/BoundTree/BoundIndexOrRangePatternIndexerAccess.cs diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index 31dae822d5cd..0200c3ef12a1 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -400,14 +400,14 @@ internal bool CheckValueKind(SyntaxNode node, BoundExpression expr, BindValueKin case BoundKind.IndexOrRangePatternIndexerAccess: var implicitIndexer = (BoundIndexOrRangePatternIndexerAccess)expr; - if (implicitIndexer.PatternSymbol.Kind == SymbolKind.Property) + if (implicitIndexer.IndexerAccess is BoundIndexerAccess implicitIndexerAccess) { // If this is an Index indexer, PatternSymbol should be a property, pointing to the // implicit indexer. If it's a Range access, it will be a method, pointing to a Slice method // and it's handled below as part of invocations. - return CheckPropertyValueKind(node, expr, valueKind, checkingReceiver, diagnostics); + return CheckValueKind(node, implicitIndexerAccess, valueKind, checkingReceiver, diagnostics); } - Debug.Assert(implicitIndexer.PatternSymbol.Kind == SymbolKind.Method); + Debug.Assert(implicitIndexer.IndexerAccess is BoundCall); break; case BoundKind.EventAccess: @@ -591,15 +591,10 @@ internal bool CheckValueKind(SyntaxNode node, BoundExpression expr, BindValueKin case BoundKind.IndexOrRangePatternIndexerAccess: var implicitIndexerAccess = (BoundIndexOrRangePatternIndexerAccess)expr; + Debug.Assert(implicitIndexerAccess.IndexerAccess is BoundCall); // If we got here this should be an implicit indexer taking a Range, // meaning that the pattern symbol must be a method (either Slice or Substring) - return CheckMethodReturnValueKind( - (MethodSymbol)implicitIndexerAccess.PatternSymbol, - implicitIndexerAccess.Syntax, - node, - valueKind, - checkingReceiver, - diagnostics); + return CheckValueKind(node, implicitIndexerAccess.IndexerAccess, valueKind, checkingReceiver, diagnostics); case BoundKind.ConditionalOperator: var conditional = (BoundConditionalOperator)expr; @@ -2348,42 +2343,9 @@ internal static bool CheckRefEscape(SyntaxNode node, BoundExpression expr, uint isRefEscape: true); case BoundKind.IndexOrRangePatternIndexerAccess: - var implicitIndexer = (BoundIndexOrRangePatternIndexerAccess)expr; - RefKind refKind; - ImmutableArray parameters; - - switch (implicitIndexer.PatternSymbol) - { - case PropertySymbol p: - refKind = p.RefKind; - parameters = p.Parameters; - break; - case MethodSymbol m: - refKind = m.RefKind; - parameters = m.Parameters; - break; - default: - throw ExceptionUtilities.Unreachable; - } - - if (refKind == RefKind.None) - { - break; - } - - return CheckInvocationEscape( - implicitIndexer.Syntax, - implicitIndexer.PatternSymbol, - implicitIndexer.Receiver, - parameters, - ImmutableArray.Create(implicitIndexer.Argument), - default, - default, - checkingReceiver, - escapeFrom, - escapeTo, - diagnostics, - isRefEscape: true); + var implicitIndexerAccess = (BoundIndexOrRangePatternIndexerAccess)expr; + // Note: the LengthOrCountAccess use is purely local + return CheckRefEscape(node, implicitIndexerAccess.IndexerAccess, escapeFrom, escapeTo, checkingReceiver, diagnostics); case BoundKind.FunctionPointerInvocation: var functionPointerInvocation = (BoundFunctionPointerInvocation)expr; @@ -2618,24 +2580,19 @@ internal static uint GetValEscape(BoundExpression expr, uint scopeOfTheContainin scopeOfTheContainingExpression, isRefEscape: false); + case BoundKind.IndexOrRangeIndexerPatternReceiverPlaceholder: + return ((BoundIndexOrRangeIndexerPatternReceiverPlaceholder)expr).ValEscape; + + case BoundKind.ListPatternReceiverPlaceholder: + return ((BoundListPatternReceiverPlaceholder)expr).ValEscape; + + case BoundKind.SlicePatternReceiverPlaceholder: + return ((BoundSlicePatternReceiverPlaceholder)expr).ValEscape; + case BoundKind.IndexOrRangePatternIndexerAccess: var implicitIndexerAccess = (BoundIndexOrRangePatternIndexerAccess)expr; - var parameters = implicitIndexerAccess.PatternSymbol switch - { - PropertySymbol p => p.Parameters, - MethodSymbol m => m.Parameters, - _ => throw ExceptionUtilities.UnexpectedValue(implicitIndexerAccess.PatternSymbol) - }; - - return GetInvocationEscapeScope( - implicitIndexerAccess.PatternSymbol, - implicitIndexerAccess.Receiver, - parameters, - default, - default, - default, - scopeOfTheContainingExpression, - isRefEscape: false); + // Note: the LengthOrCountAccess use is purely local + return GetValEscape(implicitIndexerAccess.IndexerAccess, scopeOfTheContainingExpression); case BoundKind.PropertyAccess: var propertyAccess = (BoundPropertyAccess)expr; @@ -3040,29 +2997,34 @@ internal static bool CheckValEscape(SyntaxNode node, BoundExpression expr, uint diagnostics, isRefEscape: false); - case BoundKind.IndexOrRangePatternIndexerAccess: - var implicitIndexerAccess = (BoundIndexOrRangePatternIndexerAccess)expr; - var underlyingIndexerOrSliceSymbol = implicitIndexerAccess.PatternSymbol; - var parameters = underlyingIndexerOrSliceSymbol switch + case BoundKind.IndexOrRangeIndexerPatternReceiverPlaceholder: + if (((BoundIndexOrRangeIndexerPatternReceiverPlaceholder)expr).ValEscape > escapeTo) { - PropertySymbol p => p.Parameters, - MethodSymbol m => m.Parameters, - _ => throw ExceptionUtilities.Unreachable, - }; + Error(diagnostics, ErrorCode.ERR_EscapeLocal, node, expr.Syntax); + return false; + } + return true; - return CheckInvocationEscape( - implicitIndexerAccess.Syntax, - underlyingIndexerOrSliceSymbol, - implicitIndexerAccess.Receiver, - parameters, - ImmutableArray.Create(implicitIndexerAccess.Argument), - default, - default, - checkingReceiver, - escapeFrom, - escapeTo, - diagnostics, - isRefEscape: false); + case BoundKind.ListPatternReceiverPlaceholder: + if (((BoundListPatternReceiverPlaceholder)expr).ValEscape > escapeTo) + { + Error(diagnostics, ErrorCode.ERR_EscapeLocal, node, expr.Syntax); + return false; + } + return true; + + case BoundKind.SlicePatternReceiverPlaceholder: + if (((BoundSlicePatternReceiverPlaceholder)expr).ValEscape > escapeTo) + { + Error(diagnostics, ErrorCode.ERR_EscapeLocal, node, expr.Syntax); + return false; + } + return true; + + case BoundKind.IndexOrRangePatternIndexerAccess: + var implicitIndexerAccess = (BoundIndexOrRangePatternIndexerAccess)expr; + // Note: the LengthOrCountAccess use is purely local + return CheckValEscape(node, implicitIndexerAccess.IndexerAccess, escapeFrom, escapeTo, checkingReceiver, diagnostics); case BoundKind.PropertyAccess: var propertyAccess = (BoundPropertyAccess)expr; diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index e15529080b47..c9813f07bf95 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -7423,7 +7423,7 @@ private BoundExpression BindElementOrIndexerAccess(ExpressionSyntax node, BoundE return BindElementAccessCore(node, expr, analyzedArguments, diagnostics); } - private BoundExpression BadIndexerExpression(ExpressionSyntax node, BoundExpression expr, AnalyzedArguments analyzedArguments, DiagnosticInfo errorOpt, BindingDiagnosticBag diagnostics) + private BoundExpression BadIndexerExpression(SyntaxNode node, BoundExpression expr, AnalyzedArguments analyzedArguments, DiagnosticInfo errorOpt, BindingDiagnosticBag diagnostics) { if (!expr.HasAnyErrors) { @@ -7435,7 +7435,7 @@ private BoundExpression BadIndexerExpression(ExpressionSyntax node, BoundExpress } private BoundExpression BindElementAccessCore( - ExpressionSyntax node, + SyntaxNode node, BoundExpression expr, AnalyzedArguments arguments, BindingDiagnosticBag diagnostics) @@ -7469,7 +7469,7 @@ private BoundExpression BindElementAccessCore( } } - private BoundExpression BindArrayAccess(ExpressionSyntax node, BoundExpression expr, AnalyzedArguments arguments, BindingDiagnosticBag diagnostics) + private BoundExpression BindArrayAccess(SyntaxNode node, BoundExpression expr, AnalyzedArguments arguments, BindingDiagnosticBag diagnostics) { Debug.Assert(node != null); Debug.Assert(expr != null); @@ -7662,7 +7662,7 @@ private BoundExpression TryImplicitConversionToArrayIndex(BoundExpression expr, return result; } - private BoundExpression BindPointerElementAccess(ExpressionSyntax node, BoundExpression expr, AnalyzedArguments analyzedArguments, BindingDiagnosticBag diagnostics) + private BoundExpression BindPointerElementAccess(SyntaxNode node, BoundExpression expr, AnalyzedArguments analyzedArguments, BindingDiagnosticBag diagnostics) { Debug.Assert(node != null); Debug.Assert(expr != null); @@ -7723,7 +7723,7 @@ private static bool ReportRefOrOutArgument(AnalyzedArguments analyzedArguments, return false; } - private BoundExpression BindIndexerAccess(ExpressionSyntax node, BoundExpression expr, AnalyzedArguments analyzedArguments, BindingDiagnosticBag diagnostics) + private BoundExpression BindIndexerAccess(SyntaxNode node, BoundExpression expr, AnalyzedArguments analyzedArguments, BindingDiagnosticBag diagnostics) { Debug.Assert(node != null); Debug.Assert(expr != null); @@ -7910,36 +7910,33 @@ private BoundExpression BindIndexerOrIndexedPropertyAccess( ImmutableArray candidates = propertyGroup.ToImmutable(); - if (!analyzedArguments.HasErrors) + if (TryBindIndexOrRangeImplicitIndexer( + syntax, + receiverOpt, + analyzedArguments, + diagnostics, + out var implicitIndexerAccess)) { - if (TryBindIndexOrRangeImplicitIndexer( - syntax, - receiverOpt, - analyzedArguments, - diagnostics, - out var implicitIndexerAccess)) - { - return implicitIndexerAccess; - } - else - { - // Dev10 uses the "this" keyword as the method name for indexers. - var candidate = candidates[0]; - var name = candidate.IsIndexer ? SyntaxFacts.GetText(SyntaxKind.ThisKeyword) : candidate.Name; - - overloadResolutionResult.ReportDiagnostics( - binder: this, - location: syntax.Location, - nodeOpt: syntax, - diagnostics: diagnostics, - name: name, - receiver: null, - invokedExpression: null, - arguments: analyzedArguments, - memberGroup: candidates, - typeContainingConstructor: null, - delegateTypeBeingInvoked: null); - } + return implicitIndexerAccess; + } + else + { + // Dev10 uses the "this" keyword as the method name for indexers. + var candidate = candidates[0]; + var name = candidate.IsIndexer ? SyntaxFacts.GetText(SyntaxKind.ThisKeyword) : candidate.Name; + + overloadResolutionResult.ReportDiagnostics( + binder: this, + location: syntax.Location, + nodeOpt: syntax, + diagnostics: diagnostics, + name: name, + receiver: null, + invokedExpression: null, + arguments: analyzedArguments, + memberGroup: candidates, + typeContainingConstructor: null, + delegateTypeBeingInvoked: null); } ImmutableArray arguments = BuildArgumentsForErrorRecovery(analyzedArguments, candidates); @@ -8051,19 +8048,34 @@ private bool TryBindIndexOrRangeImplicitIndexer( } bool argIsIndex = argIsIndexNotRange.Value(); - if (!TryFindIndexOrRangeImplicitIndexer(syntax, receiverOpt, receiverType, argIsIndex: argIsIndex, - out PropertySymbol? lengthOrCountProperty, out Symbol? indexerOrSliceSymbol, diagnostics)) + var receiverValEscape = receiverOpt switch + { + BoundIndexOrRangeIndexerPatternReceiverPlaceholder { ValEscape: var valEscape } => valEscape, + BoundListPatternReceiverPlaceholder { ValEscape: var valEscape } => valEscape, + BoundSlicePatternReceiverPlaceholder { ValEscape: var valEscape } => valEscape, + _ => GetValEscape(receiverOpt, LocalScopeDepth) + }; + + var receiverPlaceholder = new BoundIndexOrRangeIndexerPatternReceiverPlaceholder(receiverOpt.Syntax, receiverValEscape, receiverOpt.Type) { WasCompilerGenerated = true }; + if (!TryBindIndexOrRangeImplicitIndexer(syntax, receiverPlaceholder, receiverType, argIsIndex: argIsIndex, + out var lengthOrCountAccess, out var indexerOrSliceAccess, out var argumentPlaceholders, diagnostics)) { return false; } + Debug.Assert(lengthOrCountAccess is BoundPropertyAccess); + Debug.Assert(indexerOrSliceAccess is BoundIndexerAccess or BoundCall); + Debug.Assert(indexerOrSliceAccess.Type is not null); + implicitIndexerAccess = new BoundIndexOrRangePatternIndexerAccess( syntax, receiverOpt, - lengthOrCountProperty, - indexerOrSliceSymbol, - BindToNaturalType(argument, diagnostics), - indexerOrSliceSymbol.GetTypeOrReturnType().Type); + argument: BindToNaturalType(argument, diagnostics), + lengthOrCountAccess: lengthOrCountAccess, + indexerAccess: indexerOrSliceAccess, + receiverPlaceholder, + argumentPlaceholders, + indexerOrSliceAccess.Type); if (!argIsIndex) { @@ -8071,6 +8083,7 @@ private bool TryBindIndexOrRangeImplicitIndexer( checkWellKnown(WellKnownMember.System_Range__get_End); } checkWellKnown(WellKnownMember.System_Index__GetOffset); + // TODO2 check for conversion operator from int? _ = MessageID.IDS_FeatureIndexOperator.CheckFeatureAvailability(diagnostics, syntax); if (arguments.Names.Count > 0) @@ -8095,13 +8108,14 @@ void checkWellKnown(WellKnownMember member) /// /// Finds pattern-based implicit indexer and Length/Count property. /// - private bool TryFindIndexOrRangeImplicitIndexer( + private bool TryBindIndexOrRangeImplicitIndexer( SyntaxNode syntax, BoundExpression? receiverOpt, TypeSymbol receiverType, bool argIsIndex, - [NotNullWhen(true)] out PropertySymbol? lengthOrCountProperty, - [NotNullWhen(true)] out Symbol? indexerOrSliceSymbol, + [NotNullWhen(true)] out BoundExpression? lengthOrCountAccess, + [NotNullWhen(true)] out BoundExpression? implicitIndexerAccess, + out ImmutableArray argumentPlaceholders, BindingDiagnosticBag diagnostics) { // SPEC: @@ -8116,18 +8130,43 @@ private bool TryFindIndexOrRangeImplicitIndexer( var lookupResult = LookupResult.GetInstance(); - if (TryLookupLengthOrCount(syntax, receiverType, lookupResult, out lengthOrCountProperty, diagnostics) && - TryFindIndexOrRangeImplicitIndexer(syntax, lookupResult, receiverOpt, receiverType, argIsIndex, out indexerOrSliceSymbol, diagnostics)) + if (TryBindLengthOrCount(syntax, receiverOpt, receiverType, out lengthOrCountAccess, diagnostics) && + TryFindIndexOrRangeImplicitIndexer(syntax, lookupResult, receiverOpt, receiverType, argIsIndex, out implicitIndexerAccess, out argumentPlaceholders, diagnostics)) + { + CheckValue(lengthOrCountAccess, BindValueKind.RValue, diagnostics); + + lookupResult.Free(); + return true; + } + + lengthOrCountAccess = null; + implicitIndexerAccess = null; + argumentPlaceholders = default; + lookupResult.Free(); + return false; + } + + private bool TryBindLengthOrCount( + SyntaxNode syntax, + BoundExpression? receiverOpt, + TypeSymbol receiverType, + [NotNullWhen(true)] out BoundExpression? lengthOrCountAccess, + BindingDiagnosticBag diagnostics) + { + var lookupResult = LookupResult.GetInstance(); + + if (TryLookupLengthOrCount(syntax, receiverType, lookupResult, out var lengthOrCountProperty, diagnostics)) { - var lengthAccess = new BoundPropertyAccess(syntax, receiverOpt, lengthOrCountProperty, LookupResultKind.Viable, lengthOrCountProperty.Type); - CheckPropertyValueKind(syntax, lengthAccess, BindValueKind.RValue, checkingReceiver: false, diagnostics); + lengthOrCountAccess = BindPropertyAccess(syntax, receiverOpt, lengthOrCountProperty, diagnostics, lookupResult.Kind, hasErrors: false); + ReportDiagnosticsIfObsolete(diagnostics, lengthOrCountProperty, syntax, hasBaseReceiver: false); lookupResult.Free(); return true; } - indexerOrSliceSymbol = null; + lengthOrCountAccess = null; lookupResult.Free(); + return false; } @@ -8142,7 +8181,8 @@ private bool TryFindIndexOrRangeImplicitIndexer( BoundExpression? receiverOpt, TypeSymbol receiverType, bool argIsIndex, - [NotNullWhen(true)] out Symbol? indexerOrSliceSymbol, + [NotNullWhen(true)] out BoundExpression? indexerOrSliceAccess, + out ImmutableArray argumentPlaceholders, BindingDiagnosticBag diagnostics) { var useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); @@ -8173,11 +8213,13 @@ candidate is PropertySymbol property && property.OriginalDefinition is { ParameterCount: 1 } original && original.Parameters[0] is { Type: { SpecialType: SpecialType.System_Int32 }, RefKind: RefKind.None }) { - // note: implicit copy check on the indexer accessor happens in CheckPropertyValueKind - indexerOrSliceSymbol = property; - property.AddUseSiteInfo(ref useSiteInfo); - diagnostics.Add(syntax, useSiteInfo); - ReportDiagnosticsIfObsolete(diagnostics, property, syntax, hasBaseReceiver: false); + var intPlaceholder = new BoundIndexOrRangeIndexerPatternValuePlaceholder(syntax, Compilation.GetSpecialType(SpecialType.System_Int32)) { WasCompilerGenerated = true }; + argumentPlaceholders = ImmutableArray.Create(intPlaceholder); + + var analyzedArguments = AnalyzedArguments.GetInstance(); + analyzedArguments.Arguments.Add(intPlaceholder); + indexerOrSliceAccess = BindIndexedPropertyAccess(syntax, receiverOpt, ImmutableArray.Create(property), analyzedArguments, diagnostics); + analyzedArguments.Free(); return true; } } @@ -8190,7 +8232,7 @@ candidate is PropertySymbol property && var substring = (MethodSymbol)Compilation.GetSpecialTypeMember(SpecialMember.System_String__Substring); if (substring is object) { - indexerOrSliceSymbol = substring; + makeCall(syntax, receiverOpt, substring, out indexerOrSliceAccess, out argumentPlaceholders); return true; } } @@ -8223,19 +8265,44 @@ method.OriginalDefinition is var original && original.Parameters[0] is { Type: { SpecialType: SpecialType.System_Int32 }, RefKind: RefKind.None } && original.Parameters[1] is { Type: { SpecialType: SpecialType.System_Int32 }, RefKind: RefKind.None }) { - indexerOrSliceSymbol = method; method.AddUseSiteInfo(ref useSiteInfo); diagnostics.ReportUseSite(method, syntax); ReportDiagnosticsIfObsolete(diagnostics, method, syntax, hasBaseReceiver: false); CheckImplicitThisCopyInReadOnlyMember(receiverOpt, method, diagnostics); + makeCall(syntax, receiverOpt, method, out indexerOrSliceAccess, out argumentPlaceholders); return true; } } } } - indexerOrSliceSymbol = null; + indexerOrSliceAccess = null; + argumentPlaceholders = default; return false; + + void makeCall(SyntaxNode syntax, BoundExpression? receiverOpt, MethodSymbol method, + out BoundExpression indexerOrSliceAccess, out ImmutableArray argumentPlaceholders) + { + // TODO2 make a property group and reuse an existing binding method? + var startArgumentPlaceholder = new BoundIndexOrRangeIndexerPatternValuePlaceholder(syntax, Compilation.GetSpecialType(SpecialType.System_Int32)) { WasCompilerGenerated = true }; + var endArgumentPlaceholder = new BoundIndexOrRangeIndexerPatternValuePlaceholder(syntax, Compilation.GetSpecialType(SpecialType.System_Int32)) { WasCompilerGenerated = true }; + argumentPlaceholders = ImmutableArray.Create(startArgumentPlaceholder, endArgumentPlaceholder); + + indexerOrSliceAccess = new BoundCall( + syntax, + receiverOpt, + method, + ImmutableArray.Create(startArgumentPlaceholder, endArgumentPlaceholder), + argumentNamesOpt: default, + argumentRefKindsOpt: default, + isDelegateCall: false, + expanded: false, + invokedAsExtensionMethod: false, + argsToParamsOpt: default, + defaultArguments: default, + resultKind: LookupResultKind.Viable, + type: method.ReturnType); + } } private bool TryLookupLengthOrCount( @@ -8249,7 +8316,6 @@ private bool TryLookupLengthOrCount( if (tryLookupLengthOrCount(WellKnownMemberNames.LengthPropertyName, out lengthOrCountProperty, diagnostics) || tryLookupLengthOrCount(WellKnownMemberNames.CountPropertyName, out lengthOrCountProperty, diagnostics)) { - ReportDiagnosticsIfObsolete(diagnostics, lengthOrCountProperty, syntax, hasBaseReceiver: false); return true; } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs index 4850ba887074..ef9ee92fd422 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs @@ -198,47 +198,42 @@ private BoundPattern BindSlicePattern( hasErrors = true; } - BoundIndexerAccess? indexerAccess = null; - MethodSymbol? sliceMethod = null; + BoundExpression? indexerAccess = null; BoundPattern? pattern = null; + BoundSlicePatternReceiverPlaceholder? receiverPlaceholder = null; + BoundSlicePatternUnloweredRangePlaceholder? argumentPlaceholder = null; // We don't require the type to be sliceable if there's no subpattern. if (node.Pattern is not null) { + receiverPlaceholder = new BoundSlicePatternReceiverPlaceholder(node, GetValEscape(inputType, inputValEscape), inputType) { WasCompilerGenerated = true }; + var systemRangeType = GetWellKnownType(WellKnownType.System_Range, diagnostics, node); + argumentPlaceholder = new BoundSlicePatternUnloweredRangePlaceholder(node, systemRangeType) { WasCompilerGenerated = true }; + TypeSymbol sliceType; if (inputType.IsErrorType()) { hasErrors = true; sliceType = inputType; } - else if (inputType.IsSZArray()) - { - sliceType = inputType; - } - else if (TryBindIndexerForPattern(node, inputType, argIsIndex: false, out indexerAccess, out Symbol? indexerOrSliceSymbol, lengthProperty: out _, diagnostics, ref hasErrors)) - { - if (indexerOrSliceSymbol is MethodSymbol method) - { - sliceMethod = method; - sliceType = method.ReturnType; - } - else - { - Debug.Assert(indexerAccess is not null); - sliceType = indexerAccess.Type; - } - } else { - hasErrors = true; - sliceType = CreateErrorType(); - Error(diagnostics, ErrorCode.ERR_UnsupportedTypeForSlicePattern, node, inputType); + var analyzedArguments = AnalyzedArguments.GetInstance(); + analyzedArguments.Arguments.Add(argumentPlaceholder); + + indexerAccess = BindElementAccessCore(node, receiverPlaceholder, analyzedArguments, diagnostics); + indexerAccess = CheckValue(indexerAccess, BindValueKind.RValue, diagnostics); + Debug.Assert(indexerAccess is BoundIndexerAccess or BoundIndexOrRangePatternIndexerAccess or BoundArrayAccess or BoundBadExpression); + analyzedArguments.Free(); + + Debug.Assert(indexerAccess.Type is not null); + sliceType = indexerAccess.Type; } pattern = BindPattern(node.Pattern, sliceType, GetValEscape(sliceType, inputValEscape), permitDesignations, hasErrors, diagnostics); } - return new BoundSlicePattern(node, pattern, indexerAccess, sliceMethod, inputType: inputType, narrowedType: inputType, hasErrors); + return new BoundSlicePattern(node, pattern, indexerAccess, receiverPlaceholder, argumentPlaceholder, inputType: inputType, narrowedType: inputType, hasErrors); } private ImmutableArray BindListPatternSubpatterns( @@ -283,38 +278,25 @@ private BoundListPattern BindListPattern( CheckFeatureAvailability(node, MessageID.IDS_FeatureListPattern, diagnostics); TypeSymbol elementType; - BoundIndexerAccess? indexerAccess = null; - PropertySymbol? indexerSymbol = null; - PropertySymbol? lengthProperty = null; + BoundExpression? indexerAccess = null; + BoundExpression? lengthAccess = null; TypeSymbol narrowedType = inputType.StrippedType(); + BoundListPatternReceiverPlaceholder? receiverPlaceholder; + BoundListPatternUnloweredIndexPlaceholder? argumentPlaceholder; + if (inputType.IsErrorType()) { hasErrors = true; elementType = inputType; - } - else if (inputType.IsSZArray()) - { - elementType = ((ArrayTypeSymbol)inputType).ElementType; - hasErrors |= !TryGetSpecialTypeMember(Compilation, SpecialMember.System_Array__Length, node, diagnostics, out lengthProperty); - } - else if (TryBindIndexerForPattern(node, narrowedType, argIsIndex: true, out indexerAccess, out Symbol? indexerOrSliceSymbol, out lengthProperty, diagnostics, ref hasErrors)) - { - if (indexerOrSliceSymbol is PropertySymbol indexer) - { - indexerSymbol = indexer; - elementType = indexer.Type; - } - else - { - Debug.Assert(indexerAccess is not null); - elementType = indexerAccess.Type; - } + receiverPlaceholder = null; + argumentPlaceholder = null; } else { - hasErrors = true; - elementType = CreateErrorType(); - Error(diagnostics, ErrorCode.ERR_UnsupportedTypeForListPattern, node, inputType); + hasErrors = !BindLengthAndIndexerForListPattern(node, narrowedType, inputValEscape, diagnostics, out indexerAccess, out lengthAccess, out receiverPlaceholder, out argumentPlaceholder); + + Debug.Assert(indexerAccess!.Type is not null); + elementType = indexerAccess.Type; } ImmutableArray subpatterns = BindListPatternSubpatterns( @@ -328,138 +310,74 @@ private BoundListPattern BindListPattern( out Symbol? variableSymbol, out BoundExpression? variableAccess); return new BoundListPattern( - syntax: node, subpatterns: subpatterns, hasSlice: sawSlice, lengthProperty: lengthProperty, - indexerAccess: indexerAccess, indexerSymbol: indexerSymbol, variable: variableSymbol, + syntax: node, subpatterns: subpatterns, hasSlice: sawSlice, lengthAccess: lengthAccess, + indexerAccess: indexerAccess, receiverPlaceholder, argumentPlaceholder, variable: variableSymbol, variableAccess: variableAccess, inputType: inputType, narrowedType: narrowedType, hasErrors); } - private bool TryBindIndexerForPattern( - SyntaxNode syntax, - TypeSymbol receiverType, - bool argIsIndex, - out BoundIndexerAccess? indexerAccess, - out Symbol? indexerOrSliceSymbol, - [NotNullWhen(true)] out PropertySymbol? lengthProperty, - BindingDiagnosticBag diagnostics, - ref bool hasErrors) + /// + /// Types which list-patterns can be used on (ie. countable and indexable ones) are assumed to have + /// non-negative lengths. + /// + private bool IsCountableAndIndexable(SyntaxNode node, TypeSymbol inputType, out PropertySymbol? lengthProperty) { - Debug.Assert(!receiverType.IsErrorType()); - indexerAccess = null; - indexerOrSliceSymbol = null; - lengthProperty = null; - bool found = false; - CompoundUseSiteInfo useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); - var bindingDiagnostics = BindingDiagnosticBag.GetInstance(diagnostics); - TypeSymbol argType = Compilation.GetWellKnownType(argIsIndex ? WellKnownType.System_Index : WellKnownType.System_Range); - var lookupResult = LookupResult.GetInstance(); - if (!argType.IsErrorType()) - { - LookupMembersInType( - lookupResult, - receiverType, - WellKnownMemberNames.Indexer, - arity: 0, - basesBeingResolved: null, - LookupOptions.Default, - originalBinder: this, - diagnose: false, - ref useSiteInfo); - diagnostics.Add(syntax, useSiteInfo); - - if (lookupResult.IsMultiViable) - { - var indexerGroup = ArrayBuilder.GetInstance(lookupResult.Symbols.Count); - foreach (Symbol symbol in lookupResult.Symbols) - { - Debug.Assert(symbol.IsIndexer()); - indexerGroup.Add((PropertySymbol)symbol); - } - lookupResult.Clear(); - - var analyzedArguments = AnalyzedArguments.GetInstance(); - analyzedArguments.Arguments.Add(new BoundIndexOrRangeIndexerPatternValuePlaceholder(syntax, argType)); - var receiver = new BoundImplicitReceiver(syntax, receiverType); - BoundExpression boundAccess = BindIndexerOrIndexedPropertyAccess(syntax, receiver, indexerGroup, analyzedArguments, bindingDiagnostics); - switch (boundAccess) - { - case BoundIndexerAccess boundIndexerAccess: - if (boundIndexerAccess.ResultKind == LookupResultKind.Viable && - boundIndexerAccess.Indexer.GetOwnOrInheritedGetMethod() is { } getMethod && - IsAccessible(getMethod, ref useSiteInfo) && - TryLookupLengthOrCount(syntax, receiverType, lookupResult, out lengthProperty, bindingDiagnostics) && - !getMethod.IsStatic) - { - GetWellKnownTypeMember(argIsIndex ? WellKnownMember.System_Index__ctor : WellKnownMember.System_Range__ctor, bindingDiagnostics, syntax: syntax); - indexerAccess = BindIndexerDefaultArguments(boundIndexerAccess, BindValueKind.RValue, bindingDiagnostics); - found = true; - break; - } - - // TODO2 useSiteInfo? - break; + var diagnostics = BindingDiagnosticBag.GetInstance(); + var success = BindLengthAndIndexerForListPattern(node, inputType, inputValEscape: ExternalScope, diagnostics, indexerAccess: out _, out var lengthAccess, receiverPlaceholder: out _, argumentPlaceholder: out _); + lengthProperty = success ? GetPropertySymbol(lengthAccess, out _, out _) : null; + diagnostics.Free(); + return success; + } - case BoundIndexOrRangePatternIndexerAccess boundIndexOrRangePatternIndexerAccess: - lengthProperty = boundIndexOrRangePatternIndexerAccess.LengthOrCountProperty; - indexerOrSliceSymbol = boundIndexOrRangePatternIndexerAccess.PatternSymbol; - found = true; - break; + private bool BindLengthAndIndexerForListPattern(SyntaxNode node, TypeSymbol inputType, uint inputValEscape, BindingDiagnosticBag diagnostics, + out BoundExpression? indexerAccess, out BoundExpression? lengthAccess, out BoundListPatternReceiverPlaceholder? receiverPlaceholder, out BoundListPatternUnloweredIndexPlaceholder? argumentPlaceholder) + { + var bindingDiagnostics = BindingDiagnosticBag.GetInstance(diagnostics); - case var v: - throw ExceptionUtilities.UnexpectedValue(v.Kind); - } - analyzedArguments.Free(); - indexerGroup.Free(); - goto done; + receiverPlaceholder = new BoundListPatternReceiverPlaceholder(node, GetValEscape(inputType, inputValEscape), inputType) { WasCompilerGenerated = true }; + bool hasErrors = false; + if (inputType.IsSZArray()) + { + hasErrors |= !TryGetSpecialTypeMember(Compilation, SpecialMember.System_Array__Length, node, bindingDiagnostics, out PropertySymbol lengthProperty); + if (lengthProperty is not null) + { + lengthAccess = new BoundPropertyAccess(node, receiverPlaceholder, lengthProperty, LookupResultKind.Viable, lengthProperty.Type); + } + else + { + lengthAccess = new BoundBadExpression(node, LookupResultKind.Empty, ImmutableArray.Empty, ImmutableArray.Empty, CreateErrorType(), hasErrors: true); } - lookupResult.Clear(); } - - // If the argType is missing or the indexer lookup has failed, we will fallback to the implicit indexer support. - found = TryLookupLengthOrCount(syntax, receiverType, lookupResult, out lengthProperty, diagnostics) && - TryFindIndexOrRangeImplicitIndexer(syntax, lookupResult, receiverOpt: null, receiverType, argIsIndex, out indexerOrSliceSymbol, diagnostics); -done: - - if (found) + else { - Debug.Assert(indexerAccess is not null ^ indexerOrSliceSymbol is not null); - Debug.Assert(lengthProperty is not null); + hasErrors |= !TryBindLengthOrCount(node, receiverPlaceholder, receiverPlaceholder.Type, out lengthAccess, bindingDiagnostics); + } - if (!hasErrors) - { - if (indexerOrSliceSymbol is not null) - { - var implicitIndexerAccess = new BoundIndexOrRangePatternIndexerAccess(syntax, new BoundImplicitReceiver(syntax, receiverType), - lengthProperty, indexerOrSliceSymbol, argument: new BoundIndexOrRangeIndexerPatternValuePlaceholder(syntax, argType), indexerOrSliceSymbol.GetTypeOrReturnType().Type); + if (lengthAccess is null) + { + Error(bindingDiagnostics, ErrorCode.ERR_UnsupportedTypeForListPattern, node, inputType); + } + else + { + CheckValue(lengthAccess, BindValueKind.RValue, diagnostics); + } - if (!CheckValueKind(syntax, implicitIndexerAccess, BindValueKind.RValue, checkingReceiver: false, bindingDiagnostics)) - { - hasErrors = true; - } - } - else - { - var lengthAccess = new BoundPropertyAccess(syntax, new BoundImplicitReceiver(syntax, receiverType), lengthProperty, LookupResultKind.Viable, lengthProperty.Type); - if (!CheckValueKind(syntax, lengthAccess, BindValueKind.RValue, checkingReceiver: false, bindingDiagnostics)) - { - hasErrors = true; - } + var analyzedArguments = AnalyzedArguments.GetInstance(); + var systemIndexType = GetWellKnownType(WellKnownType.System_Index, bindingDiagnostics, node); + argumentPlaceholder = new BoundListPatternUnloweredIndexPlaceholder(node, systemIndexType) { WasCompilerGenerated = true }; + analyzedArguments.Arguments.Add(argumentPlaceholder); - if (indexerAccess is not null && !CheckValueKind(syntax, indexerAccess, BindValueKind.RValue, checkingReceiver: false, bindingDiagnostics)) - { - hasErrors = true; - } - } - } + indexerAccess = BindElementAccessCore(node, receiverPlaceholder, analyzedArguments, bindingDiagnostics); + indexerAccess = CheckValue(indexerAccess, BindValueKind.RValue, bindingDiagnostics); + Debug.Assert(indexerAccess is BoundIndexerAccess or BoundIndexOrRangePatternIndexerAccess or BoundArrayAccess or BoundBadExpression or BoundDynamicIndexerAccess); + analyzedArguments.Free(); - // At this point we have succeeded to bind a viable indexer, - // report additional binding diagnostics that we have seen so far - diagnostics.AddRange(bindingDiagnostics); - diagnostics.Add(syntax, useSiteInfo); + if (bindingDiagnostics.AccumulatesDiagnostics && bindingDiagnostics.HasAnyErrors()) + { + hasErrors = true; } - bindingDiagnostics.Free(); - lookupResult.Free(); - return found; + diagnostics.AddRangeAndFree(bindingDiagnostics); + return !hasErrors; } private static BoundPattern BindDiscardPattern(DiscardPatternSyntax node, TypeSymbol inputType) @@ -1485,11 +1403,8 @@ private ImmutableArray BindPropertyPatternClause( TypeSymbol receiverType = member.Receiver?.Type ?? inputType; if (!receiverType.IsErrorType()) { - bool ignoredHasErrors = false; - isLengthOrCount = receiverType.IsSZArray() - ? ReferenceEquals(memberSymbol, Compilation.GetSpecialTypeMember(SpecialMember.System_Array__Length)) - : TryBindIndexerForPattern(node, receiverType, argIsIndex: true, indexerAccess: out _, indexerOrSliceSymbol: out _, out PropertySymbol? lengthProperty, BindingDiagnosticBag.Discarded, ref ignoredHasErrors) && - memberSymbol.Equals(lengthProperty, TypeCompareKind.ConsiderEverything); // If Length and Count are both present, only the former is assumed to be non-negative. + isLengthOrCount = IsCountableAndIndexable(node, receiverType, out PropertySymbol? lengthProperty) && + memberSymbol.Equals(lengthProperty, TypeCompareKind.ConsiderEverything); // If Length and Count are both present, only the former is assumed to be non-negative. } } } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index 3c535c70e614..b8afb9247d94 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -1537,7 +1537,7 @@ private BoundAssignmentOperator BindAssignment( return new BoundAssignmentOperator(node, op1, op2, isRef, type, hasErrors); } - private static PropertySymbol GetPropertySymbol(BoundExpression expr, out BoundExpression receiver, out SyntaxNode propertySyntax) + internal static PropertySymbol GetPropertySymbol(BoundExpression expr, out BoundExpression receiver, out SyntaxNode propertySyntax) { PropertySymbol propertySymbol; switch (expr.Kind) @@ -1560,7 +1560,7 @@ private static PropertySymbol GetPropertySymbol(BoundExpression expr, out BoundE { var implicitIndexerAccess = (BoundIndexOrRangePatternIndexerAccess)expr; receiver = implicitIndexerAccess.Receiver; - propertySymbol = (PropertySymbol)implicitIndexerAccess.PatternSymbol; + propertySymbol = GetPropertySymbol(implicitIndexerAccess.IndexerAccess, out _, out propertySyntax); } break; default: @@ -1592,6 +1592,21 @@ private static PropertySymbol GetPropertySymbol(BoundExpression expr, out BoundE return propertySymbol; } +#nullable enable + internal static Symbol? GetIndexerSymbol(BoundExpression? e) + { + return e switch + { + null => null, + BoundIndexOrRangePatternIndexerAccess implicitIndexerAccess => GetIndexerSymbol(implicitIndexerAccess.IndexerAccess), + BoundIndexerAccess indexerAccess => indexerAccess.Indexer, + BoundCall call => call.Method, + BoundArrayAccess arrayAccess => arrayAccess.ExpressionSymbol, + _ => throw ExceptionUtilities.Unreachable + }; + } +#nullable disable + private static SyntaxNode GetEventName(BoundEventAccess expr) { SyntaxNode syntax = expr.Syntax; diff --git a/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder_ListPatterns.cs b/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder_ListPatterns.cs index 9d8650d4c650..1d742dcffca9 100644 --- a/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder_ListPatterns.cs +++ b/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder_ListPatterns.cs @@ -16,7 +16,7 @@ private Tests MakeTestsAndBindingsForListPattern(BoundDagTemp input, BoundListPa input.Type.Equals(list.InputType, TypeCompareKind.AllIgnoreOptions) && input.Type.StrippedType().Equals(list.NarrowedType, TypeCompareKind.ConsiderEverything) && list.Subpatterns.Count(p => p.Kind == BoundKind.SlicePattern) == (list.HasSlice ? 1 : 0) && - list.LengthProperty is not null); + list.LengthAccess is not null); var syntax = list.Syntax; var subpatterns = list.Subpatterns; @@ -35,8 +35,9 @@ private Tests MakeTestsAndBindingsForListPattern(BoundDagTemp input, BoundListPa } else { - Debug.Assert(list.LengthProperty is not null); - var lengthEvaluation = new BoundDagPropertyEvaluation(syntax, list.LengthProperty, isLengthOrCount: true, input); + Debug.Assert(list.LengthAccess is not null); + var lengthProperty = Binder.GetPropertySymbol(list.LengthAccess, out _, out _); + var lengthEvaluation = new BoundDagPropertyEvaluation(syntax, lengthProperty, isLengthOrCount: true, input); tests.Add(new Tests.One(lengthEvaluation)); var lengthTemp = new BoundDagTemp(syntax, _compilation.GetSpecialType(SpecialType.System_Int32), lengthEvaluation); tests.Add(new Tests.One(list.HasSlice @@ -53,7 +54,11 @@ private Tests MakeTestsAndBindingsForListPattern(BoundDagTemp input, BoundListPa if (slice.Pattern is BoundPattern slicePattern) { - var sliceEvaluation = new BoundDagSliceEvaluation(slicePattern.Syntax, slicePattern.InputType, lengthTemp, startIndex: startIndex, endIndex: index, slice.IndexerAccess, slice.SliceMethod, input); + Debug.Assert(slice.IndexerAccess is not null); + Debug.Assert(index <= 0); + var sliceEvaluation = new BoundDagSliceEvaluation(slicePattern.Syntax, slicePattern.InputType, lengthTemp, startIndex: startIndex, endIndex: index, + slice.IndexerAccess, slice.ReceiverPlaceholder, slice.ArgumentPlaceholder, input); + tests.Add(new Tests.One(sliceEvaluation)); var sliceTemp = new BoundDagTemp(slicePattern.Syntax, slicePattern.InputType, sliceEvaluation); tests.Add(MakeTestsAndBindings(sliceTemp, slicePattern, bindings)); @@ -62,7 +67,10 @@ private Tests MakeTestsAndBindingsForListPattern(BoundDagTemp input, BoundListPa continue; } - var indexEvaluation = new BoundDagIndexerEvaluation(subpattern.Syntax, subpattern.InputType, lengthTemp, index++, list.IndexerAccess, list.IndexerSymbol, input); + Debug.Assert(list.IndexerAccess is not null); + var indexEvaluation = new BoundDagIndexerEvaluation(subpattern.Syntax, subpattern.InputType, lengthTemp, index++, + list.IndexerAccess, list.ReceiverPlaceholder, list.ArgumentPlaceholder, input); + tests.Add(new Tests.One(indexEvaluation)); var indexTemp = new BoundDagTemp(subpattern.Syntax, subpattern.InputType, indexEvaluation); tests.Add(MakeTestsAndBindings(indexTemp, subpattern, bindings)); diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundDagEvaluation.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundDagEvaluation.cs index 3733cc10dbd2..1cd52e33625b 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundDagEvaluation.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundDagEvaluation.cs @@ -39,8 +39,8 @@ private Symbol? Symbol BoundDagTypeEvaluation e => e.Type, BoundDagDeconstructEvaluation e => e.DeconstructMethod, BoundDagIndexEvaluation e => e.Property, - BoundDagSliceEvaluation e => (Symbol?)e.SliceMethod ?? e.IndexerAccess?.Indexer, - BoundDagIndexerEvaluation e => e.IndexerSymbol ?? e.IndexerAccess?.Indexer, + BoundDagSliceEvaluation e => Binder.GetIndexerSymbol(e.IndexerAccess), + BoundDagIndexerEvaluation e => Binder.GetIndexerSymbol(e.IndexerAccess), BoundDagAssignmentEvaluation => null, _ => throw ExceptionUtilities.UnexpectedValue(this.Kind) }; diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundIndexOrRangePatternIndexerAccess.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundIndexOrRangePatternIndexerAccess.cs new file mode 100644 index 000000000000..abed543a0465 --- /dev/null +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundIndexOrRangePatternIndexerAccess.cs @@ -0,0 +1,15 @@ +// 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 BoundIndexOrRangePatternIndexerAccess + { + internal BoundIndexOrRangePatternIndexerAccess WithLengthOrCountAccess(BoundExpression lengthOrCountAccess) + { + return new BoundIndexOrRangePatternIndexerAccess(this.Syntax, this.Receiver, this.Argument, lengthOrCountAccess, + this.IndexerAccess, this.ReceiverPlaceholder, this.ArgumentPlaceholders, this.Type, this.HasErrors); + } + } +} diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml index d36936e5a08d..784ce7130028 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml @@ -79,6 +79,10 @@ + @@ -130,14 +134,39 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1485,22 +1514,40 @@ + + - - - + + + + + + + + + + + - - + + + + + + + + + + @@ -2013,9 +2060,15 @@ - - + + + + + + + + @@ -2174,15 +2227,29 @@ - - - + + + + + + + + + + - - + + + + + + + + + diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs index 41190752bebd..2b02e9bb6e3f 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs @@ -3428,17 +3428,8 @@ private ImmutableArray GetSemanticSymbols( break; case BoundKind.IndexOrRangePatternIndexerAccess: - { - var indexerAccess = (BoundIndexOrRangePatternIndexerAccess)boundNode; - - resultKind = indexerAccess.ResultKind; - - // The only time a BoundIndexOrRangePatternIndexerAccess is created, overload resolution succeeded - // and returned only 1 result - Debug.Assert(indexerAccess.PatternSymbol is object); - symbols = ImmutableArray.Create(indexerAccess.PatternSymbol); - } - break; + return GetSemanticSymbols(((BoundIndexOrRangePatternIndexerAccess)boundNode).IndexerAccess, + boundNodeForSyntacticParent, binderOpt, options, out isDynamic, out resultKind, out memberGroup); case BoundKind.EventAssignmentOperator: var eventAssignment = (BoundEventAssignmentOperator)boundNode; diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs index 93658710f94b..94ce8d9a7f3a 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs @@ -1441,16 +1441,17 @@ public override BoundNode VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRang // 3. The argument to the access // 4. The pattern method VisitRvalue(node.Receiver); - var method = GetReadMethod(node.LengthOrCountProperty); - VisitReceiverAfterCall(node.Receiver, method); - VisitRvalue(node.Argument); - method = node.PatternSymbol switch - { - PropertySymbol p => GetReadMethod(p), - MethodSymbol m => m, - _ => throw ExceptionUtilities.UnexpectedValue(node.PatternSymbol) - }; - VisitReceiverAfterCall(node.Receiver, method); + // TODO2 + //var method = GetReadMethod(node.LengthOrCountProperty); + //VisitReceiverAfterCall(node.Receiver, method); + //VisitRvalue(node.Argument); + //method = node.PatternSymbol switch + //{ + // PropertySymbol p => GetReadMethod(p), + // MethodSymbol m => m, + // _ => throw ExceptionUtilities.UnexpectedValue(node.PatternSymbol) + //}; + //VisitReceiverAfterCall(node.Receiver, method); return null; } diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index 8620733add5a..acb5f1116d42 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -8703,15 +8703,22 @@ private TypeWithAnnotations GetDeclaredParameterResult(ParameterSymbol parameter // after indices have been visited, and only if the receiver has not changed. _ = CheckPossibleNullReceiver(receiver); + VisitRvalue(node.LengthOrCountAccess); + VisitRvalue(node.Argument); - var underlyingIndexerOrSliceSymbol = node.PatternSymbol; - if (receiverType is object) - { - underlyingIndexerOrSliceSymbol = AsMemberOfType(receiverType, underlyingIndexerOrSliceSymbol); - } + VisitRvalue(node.IndexerAccess); + return null; + } - SetLvalueResultType(node, underlyingIndexerOrSliceSymbol.GetTypeOrReturnType()); - SetUpdatedSymbol(node, node.PatternSymbol, underlyingIndexerOrSliceSymbol); + public override BoundNode? VisitIndexOrRangeIndexerPatternValuePlaceholder(BoundIndexOrRangeIndexerPatternValuePlaceholder node) + { + SetNotNullResult(node); + return null; + } + + public override BoundNode? VisitIndexOrRangeIndexerPatternReceiverPlaceholder(BoundIndexOrRangeIndexerPatternReceiverPlaceholder node) + { + SetNotNullResult(node); return null; } diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker_Patterns.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker_Patterns.cs index ecc75fe828cf..de1bf151bc17 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker_Patterns.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker_Patterns.cs @@ -499,32 +499,7 @@ public PossiblyConditionalState Clone() case BoundDagIndexerEvaluation e: { Debug.Assert(inputSlot > 0); - TypeWithAnnotations type; - if (e.IndexerType.IsErrorType()) - { - type = TypeWithAnnotations.Create(isNullableEnabled: true, e.IndexerType, isAnnotated: false); - } - else if (e.IndexerAccess is not null) - { - // this[Index] - Debug.Assert(e.IndexerSymbol is null && e.Input.Type is not ArrayTypeSymbol); - var indexer = AsMemberOfType(inputType, e.IndexerAccess.Indexer); - type = indexer.GetTypeOrReturnType(); - } - else if (e.IndexerSymbol is not null) - { - // this[int] - Debug.Assert(e.Input.Type is not ArrayTypeSymbol); - var indexer = AsMemberOfType(inputType, e.IndexerSymbol); - type = indexer.GetTypeOrReturnType(); - } - else - { - // array[int] - var arrayType = (ArrayTypeSymbol)e.Input.Type; - type = arrayType.ElementTypeWithAnnotations; - } - + TypeWithAnnotations type = getIndexerOutputType(inputType, e.IndexerAccess, isSlice: false); var output = new BoundDagTemp(e.Syntax, type.Type, e); var outputSlot = makeDagTempSlot(type, output); Debug.Assert(outputSlot > 0); @@ -534,34 +509,7 @@ public PossiblyConditionalState Clone() case BoundDagSliceEvaluation e: { Debug.Assert(inputSlot > 0); - TypeWithAnnotations type; - - if (e.SliceType.IsErrorType()) - { - type = TypeWithAnnotations.Create(isNullableEnabled: true, e.SliceType, isAnnotated: false); - } - else if (e.IndexerAccess is not null) - { - // this[Range] - Debug.Assert(e.SliceMethod is null && e.Input.Type is not ArrayTypeSymbol); - var symbol = AsMemberOfType(inputType, e.IndexerAccess.Indexer); - type = symbol.GetTypeOrReturnType(); - } - else if (e.SliceMethod is not null) - { - // Slice(int, int) - Debug.Assert(e.Input.Type is not ArrayTypeSymbol); - var symbol = AsMemberOfType(inputType, e.SliceMethod); - type = symbol.GetTypeOrReturnType(); - } - else - { - // RuntimeHelpers.GetSubArray(T[], Range) - var arrayType = e.Input.Type; - Debug.Assert(arrayType is ArrayTypeSymbol); - type = TypeWithAnnotations.Create(isNullableEnabled: true, arrayType, isAnnotated: false); - } - + TypeWithAnnotations type = getIndexerOutputType(inputType, e.IndexerAccess, isSlice: true); var output = new BoundDagTemp(e.Syntax, type.Type, e); var outputSlot = makeDagTempSlot(type, output); Debug.Assert(outputSlot > 0); @@ -833,6 +781,22 @@ void addTemp(BoundDagEvaluation e, TypeSymbol t, int index = 0) Debug.Assert(outputSlot > 0); addToTempMap(output, outputSlot, type.Type); } + + static TypeWithAnnotations getIndexerOutputType(TypeSymbol inputType, BoundExpression e, bool isSlice) + { + return e switch + { + BoundIndexerAccess indexerAccess => AsMemberOfType(inputType, indexerAccess.Indexer).GetTypeOrReturnType(), + BoundCall call => AsMemberOfType(inputType, call.Method).GetTypeOrReturnType(), + + BoundArrayAccess arrayAccess => isSlice + ? TypeWithAnnotations.Create(isNullableEnabled: true, inputType, isAnnotated: false) + : ((ArrayTypeSymbol)inputType).ElementTypeWithAnnotations, + + BoundIndexOrRangePatternIndexerAccess implicitIndexerAccess => getIndexerOutputType(inputType, implicitIndexerAccess.IndexerAccess, isSlice), + _ => throw ExceptionUtilities.UnexpectedValue(e.Kind) + }; + } } public override BoundNode VisitConvertedSwitchExpression(BoundConvertedSwitchExpression node) diff --git a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs index ffe202732660..742258dbbcf7 100644 --- a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs @@ -31,6 +31,11 @@ internal enum BoundKind : byte DisposableValuePlaceholder, ObjectOrCollectionValuePlaceholder, IndexOrRangeIndexerPatternValuePlaceholder, + IndexOrRangeIndexerPatternReceiverPlaceholder, + ListPatternReceiverPlaceholder, + ListPatternUnloweredIndexPlaceholder, + SlicePatternReceiverPlaceholder, + SlicePatternUnloweredRangePlaceholder, Dup, PassByCopy, BadExpression, @@ -675,6 +680,193 @@ public BoundIndexOrRangeIndexerPatternValuePlaceholder Update(TypeSymbol type) } } + internal sealed partial class BoundIndexOrRangeIndexerPatternReceiverPlaceholder : BoundValuePlaceholderBase + { + public BoundIndexOrRangeIndexerPatternReceiverPlaceholder(SyntaxNode syntax, uint valEscape, TypeSymbol type, bool hasErrors) + : base(BoundKind.IndexOrRangeIndexerPatternReceiverPlaceholder, syntax, type, hasErrors) + { + + RoslynDebug.Assert(type is object, "Field 'type' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); + + this.ValEscape = valEscape; + } + + public BoundIndexOrRangeIndexerPatternReceiverPlaceholder(SyntaxNode syntax, uint valEscape, TypeSymbol type) + : base(BoundKind.IndexOrRangeIndexerPatternReceiverPlaceholder, syntax, type) + { + + RoslynDebug.Assert(type is object, "Field 'type' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); + + this.ValEscape = valEscape; + } + + + public new TypeSymbol Type => base.Type!; + + public uint ValEscape { get; } + [DebuggerStepThrough] + public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitIndexOrRangeIndexerPatternReceiverPlaceholder(this); + + public BoundIndexOrRangeIndexerPatternReceiverPlaceholder Update(uint valEscape, TypeSymbol type) + { + if (valEscape != this.ValEscape || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) + { + var result = new BoundIndexOrRangeIndexerPatternReceiverPlaceholder(this.Syntax, valEscape, type, this.HasErrors); + result.CopyAttributes(this); + return result; + } + return this; + } + } + + internal sealed partial class BoundListPatternReceiverPlaceholder : BoundValuePlaceholderBase + { + public BoundListPatternReceiverPlaceholder(SyntaxNode syntax, uint valEscape, TypeSymbol type, bool hasErrors) + : base(BoundKind.ListPatternReceiverPlaceholder, syntax, type, hasErrors) + { + + RoslynDebug.Assert(type is object, "Field 'type' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); + + this.ValEscape = valEscape; + } + + public BoundListPatternReceiverPlaceholder(SyntaxNode syntax, uint valEscape, TypeSymbol type) + : base(BoundKind.ListPatternReceiverPlaceholder, syntax, type) + { + + RoslynDebug.Assert(type is object, "Field 'type' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); + + this.ValEscape = valEscape; + } + + + public new TypeSymbol Type => base.Type!; + + public uint ValEscape { get; } + [DebuggerStepThrough] + public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitListPatternReceiverPlaceholder(this); + + public BoundListPatternReceiverPlaceholder Update(uint valEscape, TypeSymbol type) + { + if (valEscape != this.ValEscape || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) + { + var result = new BoundListPatternReceiverPlaceholder(this.Syntax, valEscape, type, this.HasErrors); + result.CopyAttributes(this); + return result; + } + return this; + } + } + + internal sealed partial class BoundListPatternUnloweredIndexPlaceholder : BoundValuePlaceholderBase + { + public BoundListPatternUnloweredIndexPlaceholder(SyntaxNode syntax, TypeSymbol type, bool hasErrors) + : base(BoundKind.ListPatternUnloweredIndexPlaceholder, syntax, type, hasErrors) + { + + RoslynDebug.Assert(type is object, "Field 'type' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); + + } + + public BoundListPatternUnloweredIndexPlaceholder(SyntaxNode syntax, TypeSymbol type) + : base(BoundKind.ListPatternUnloweredIndexPlaceholder, syntax, type) + { + + RoslynDebug.Assert(type is object, "Field 'type' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); + + } + + + public new TypeSymbol Type => base.Type!; + [DebuggerStepThrough] + public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitListPatternUnloweredIndexPlaceholder(this); + + public BoundListPatternUnloweredIndexPlaceholder Update(TypeSymbol type) + { + if (!TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) + { + var result = new BoundListPatternUnloweredIndexPlaceholder(this.Syntax, type, this.HasErrors); + result.CopyAttributes(this); + return result; + } + return this; + } + } + + internal sealed partial class BoundSlicePatternReceiverPlaceholder : BoundValuePlaceholderBase + { + public BoundSlicePatternReceiverPlaceholder(SyntaxNode syntax, uint valEscape, TypeSymbol type, bool hasErrors) + : base(BoundKind.SlicePatternReceiverPlaceholder, syntax, type, hasErrors) + { + + RoslynDebug.Assert(type is object, "Field 'type' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); + + this.ValEscape = valEscape; + } + + public BoundSlicePatternReceiverPlaceholder(SyntaxNode syntax, uint valEscape, TypeSymbol type) + : base(BoundKind.SlicePatternReceiverPlaceholder, syntax, type) + { + + RoslynDebug.Assert(type is object, "Field 'type' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); + + this.ValEscape = valEscape; + } + + + public new TypeSymbol Type => base.Type!; + + public uint ValEscape { get; } + [DebuggerStepThrough] + public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitSlicePatternReceiverPlaceholder(this); + + public BoundSlicePatternReceiverPlaceholder Update(uint valEscape, TypeSymbol type) + { + if (valEscape != this.ValEscape || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) + { + var result = new BoundSlicePatternReceiverPlaceholder(this.Syntax, valEscape, type, this.HasErrors); + result.CopyAttributes(this); + return result; + } + return this; + } + } + + internal sealed partial class BoundSlicePatternUnloweredRangePlaceholder : BoundValuePlaceholderBase + { + public BoundSlicePatternUnloweredRangePlaceholder(SyntaxNode syntax, TypeSymbol type, bool hasErrors) + : base(BoundKind.SlicePatternUnloweredRangePlaceholder, syntax, type, hasErrors) + { + + RoslynDebug.Assert(type is object, "Field 'type' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); + + } + + public BoundSlicePatternUnloweredRangePlaceholder(SyntaxNode syntax, TypeSymbol type) + : base(BoundKind.SlicePatternUnloweredRangePlaceholder, syntax, type) + { + + RoslynDebug.Assert(type is object, "Field 'type' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); + + } + + + public new TypeSymbol Type => base.Type!; + [DebuggerStepThrough] + public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitSlicePatternUnloweredRangePlaceholder(this); + + public BoundSlicePatternUnloweredRangePlaceholder Update(TypeSymbol type) + { + if (!TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) + { + var result = new BoundSlicePatternUnloweredRangePlaceholder(this.Syntax, type, this.HasErrors); + result.CopyAttributes(this); + return result; + } + return this; + } + } + internal sealed partial class BoundDup : BoundExpression { public BoundDup(SyntaxNode syntax, RefKind refKind, TypeSymbol? type, bool hasErrors) @@ -5230,19 +5422,21 @@ public BoundDagIndexEvaluation Update(PropertySymbol property, int index, BoundD internal sealed partial class BoundDagIndexerEvaluation : BoundDagEvaluation { - public BoundDagIndexerEvaluation(SyntaxNode syntax, TypeSymbol indexerType, BoundDagTemp lengthTemp, int index, BoundIndexerAccess? indexerAccess, PropertySymbol? indexerSymbol, BoundDagTemp input, bool hasErrors = false) - : base(BoundKind.DagIndexerEvaluation, syntax, input, hasErrors || lengthTemp.HasErrors() || indexerAccess.HasErrors() || input.HasErrors()) + public BoundDagIndexerEvaluation(SyntaxNode syntax, TypeSymbol indexerType, BoundDagTemp lengthTemp, int index, BoundExpression indexerAccess, BoundListPatternReceiverPlaceholder? receiverPlaceholder, BoundListPatternUnloweredIndexPlaceholder? argumentPlaceholder, BoundDagTemp input, bool hasErrors = false) + : base(BoundKind.DagIndexerEvaluation, syntax, input, hasErrors || lengthTemp.HasErrors() || indexerAccess.HasErrors() || receiverPlaceholder.HasErrors() || argumentPlaceholder.HasErrors() || input.HasErrors()) { RoslynDebug.Assert(indexerType is object, "Field 'indexerType' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); RoslynDebug.Assert(lengthTemp is object, "Field 'lengthTemp' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); + RoslynDebug.Assert(indexerAccess is object, "Field 'indexerAccess' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); RoslynDebug.Assert(input is object, "Field 'input' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); this.IndexerType = indexerType; this.LengthTemp = lengthTemp; this.Index = index; this.IndexerAccess = indexerAccess; - this.IndexerSymbol = indexerSymbol; + this.ReceiverPlaceholder = receiverPlaceholder; + this.ArgumentPlaceholder = argumentPlaceholder; } @@ -5252,17 +5446,19 @@ public BoundDagIndexerEvaluation(SyntaxNode syntax, TypeSymbol indexerType, Boun public int Index { get; } - public BoundIndexerAccess? IndexerAccess { get; } + public BoundExpression IndexerAccess { get; } + + public BoundListPatternReceiverPlaceholder? ReceiverPlaceholder { get; } - public PropertySymbol? IndexerSymbol { get; } + public BoundListPatternUnloweredIndexPlaceholder? ArgumentPlaceholder { get; } [DebuggerStepThrough] public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitDagIndexerEvaluation(this); - public BoundDagIndexerEvaluation Update(TypeSymbol indexerType, BoundDagTemp lengthTemp, int index, BoundIndexerAccess? indexerAccess, PropertySymbol? indexerSymbol, BoundDagTemp input) + public BoundDagIndexerEvaluation Update(TypeSymbol indexerType, BoundDagTemp lengthTemp, int index, BoundExpression indexerAccess, BoundListPatternReceiverPlaceholder? receiverPlaceholder, BoundListPatternUnloweredIndexPlaceholder? argumentPlaceholder, BoundDagTemp input) { - if (!TypeSymbol.Equals(indexerType, this.IndexerType, TypeCompareKind.ConsiderEverything) || lengthTemp != this.LengthTemp || index != this.Index || indexerAccess != this.IndexerAccess || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(indexerSymbol, this.IndexerSymbol) || input != this.Input) + if (!TypeSymbol.Equals(indexerType, this.IndexerType, TypeCompareKind.ConsiderEverything) || lengthTemp != this.LengthTemp || index != this.Index || indexerAccess != this.IndexerAccess || receiverPlaceholder != this.ReceiverPlaceholder || argumentPlaceholder != this.ArgumentPlaceholder || input != this.Input) { - var result = new BoundDagIndexerEvaluation(this.Syntax, indexerType, lengthTemp, index, indexerAccess, indexerSymbol, input, this.HasErrors); + var result = new BoundDagIndexerEvaluation(this.Syntax, indexerType, lengthTemp, index, indexerAccess, receiverPlaceholder, argumentPlaceholder, input, this.HasErrors); result.CopyAttributes(this); return result; } @@ -5272,12 +5468,13 @@ public BoundDagIndexerEvaluation Update(TypeSymbol indexerType, BoundDagTemp len internal sealed partial class BoundDagSliceEvaluation : BoundDagEvaluation { - public BoundDagSliceEvaluation(SyntaxNode syntax, TypeSymbol sliceType, BoundDagTemp lengthTemp, int startIndex, int endIndex, BoundIndexerAccess? indexerAccess, MethodSymbol? sliceMethod, BoundDagTemp input, bool hasErrors = false) - : base(BoundKind.DagSliceEvaluation, syntax, input, hasErrors || lengthTemp.HasErrors() || indexerAccess.HasErrors() || input.HasErrors()) + public BoundDagSliceEvaluation(SyntaxNode syntax, TypeSymbol sliceType, BoundDagTemp lengthTemp, int startIndex, int endIndex, BoundExpression indexerAccess, BoundSlicePatternReceiverPlaceholder? receiverPlaceholder, BoundSlicePatternUnloweredRangePlaceholder? argumentPlaceholder, BoundDagTemp input, bool hasErrors = false) + : base(BoundKind.DagSliceEvaluation, syntax, input, hasErrors || lengthTemp.HasErrors() || indexerAccess.HasErrors() || receiverPlaceholder.HasErrors() || argumentPlaceholder.HasErrors() || input.HasErrors()) { RoslynDebug.Assert(sliceType is object, "Field 'sliceType' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); RoslynDebug.Assert(lengthTemp is object, "Field 'lengthTemp' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); + RoslynDebug.Assert(indexerAccess is object, "Field 'indexerAccess' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); RoslynDebug.Assert(input is object, "Field 'input' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); this.SliceType = sliceType; @@ -5285,7 +5482,8 @@ public BoundDagSliceEvaluation(SyntaxNode syntax, TypeSymbol sliceType, BoundDag this.StartIndex = startIndex; this.EndIndex = endIndex; this.IndexerAccess = indexerAccess; - this.SliceMethod = sliceMethod; + this.ReceiverPlaceholder = receiverPlaceholder; + this.ArgumentPlaceholder = argumentPlaceholder; } @@ -5297,17 +5495,19 @@ public BoundDagSliceEvaluation(SyntaxNode syntax, TypeSymbol sliceType, BoundDag public int EndIndex { get; } - public BoundIndexerAccess? IndexerAccess { get; } + public BoundExpression IndexerAccess { get; } - public MethodSymbol? SliceMethod { get; } + public BoundSlicePatternReceiverPlaceholder? ReceiverPlaceholder { get; } + + public BoundSlicePatternUnloweredRangePlaceholder? ArgumentPlaceholder { get; } [DebuggerStepThrough] public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitDagSliceEvaluation(this); - public BoundDagSliceEvaluation Update(TypeSymbol sliceType, BoundDagTemp lengthTemp, int startIndex, int endIndex, BoundIndexerAccess? indexerAccess, MethodSymbol? sliceMethod, BoundDagTemp input) + public BoundDagSliceEvaluation Update(TypeSymbol sliceType, BoundDagTemp lengthTemp, int startIndex, int endIndex, BoundExpression indexerAccess, BoundSlicePatternReceiverPlaceholder? receiverPlaceholder, BoundSlicePatternUnloweredRangePlaceholder? argumentPlaceholder, BoundDagTemp input) { - if (!TypeSymbol.Equals(sliceType, this.SliceType, TypeCompareKind.ConsiderEverything) || lengthTemp != this.LengthTemp || startIndex != this.StartIndex || endIndex != this.EndIndex || indexerAccess != this.IndexerAccess || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(sliceMethod, this.SliceMethod) || input != this.Input) + if (!TypeSymbol.Equals(sliceType, this.SliceType, TypeCompareKind.ConsiderEverything) || lengthTemp != this.LengthTemp || startIndex != this.StartIndex || endIndex != this.EndIndex || indexerAccess != this.IndexerAccess || receiverPlaceholder != this.ReceiverPlaceholder || argumentPlaceholder != this.ArgumentPlaceholder || input != this.Input) { - var result = new BoundDagSliceEvaluation(this.Syntax, sliceType, lengthTemp, startIndex, endIndex, indexerAccess, sliceMethod, input, this.HasErrors); + var result = new BoundDagSliceEvaluation(this.Syntax, sliceType, lengthTemp, startIndex, endIndex, indexerAccess, receiverPlaceholder, argumentPlaceholder, input, this.HasErrors); result.CopyAttributes(this); return result; } @@ -7104,20 +7304,23 @@ public BoundIndexerAccess Update(BoundExpression? receiverOpt, PropertySymbol in internal sealed partial class BoundIndexOrRangePatternIndexerAccess : BoundExpression { - public BoundIndexOrRangePatternIndexerAccess(SyntaxNode syntax, BoundExpression receiver, PropertySymbol lengthOrCountProperty, Symbol underlyingIndexerOrSliceSymbol, BoundExpression argument, TypeSymbol type, bool hasErrors = false) - : base(BoundKind.IndexOrRangePatternIndexerAccess, syntax, type, hasErrors || receiver.HasErrors() || argument.HasErrors()) + public BoundIndexOrRangePatternIndexerAccess(SyntaxNode syntax, BoundExpression receiver, BoundExpression argument, BoundExpression? lengthOrCountAccess, BoundExpression indexerAccess, BoundIndexOrRangeIndexerPatternReceiverPlaceholder receiverPlaceholder, ImmutableArray argumentPlaceholders, TypeSymbol type, bool hasErrors = false) + : base(BoundKind.IndexOrRangePatternIndexerAccess, syntax, type, hasErrors || receiver.HasErrors() || argument.HasErrors() || lengthOrCountAccess.HasErrors() || indexerAccess.HasErrors() || receiverPlaceholder.HasErrors() || argumentPlaceholders.HasErrors()) { RoslynDebug.Assert(receiver is object, "Field 'receiver' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); - RoslynDebug.Assert(lengthOrCountProperty is object, "Field 'lengthOrCountProperty' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); - RoslynDebug.Assert(underlyingIndexerOrSliceSymbol is object, "Field 'underlyingIndexerOrSliceSymbol' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); RoslynDebug.Assert(argument is object, "Field 'argument' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); + RoslynDebug.Assert(indexerAccess is object, "Field 'indexerAccess' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); + RoslynDebug.Assert(receiverPlaceholder is object, "Field 'receiverPlaceholder' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); + RoslynDebug.Assert(!argumentPlaceholders.IsDefault, "Field 'argumentPlaceholders' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); RoslynDebug.Assert(type is object, "Field 'type' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); this.Receiver = receiver; - this.LengthOrCountProperty = lengthOrCountProperty; - this.PatternSymbol = underlyingIndexerOrSliceSymbol; this.Argument = argument; + this.LengthOrCountAccess = lengthOrCountAccess; + this.IndexerAccess = indexerAccess; + this.ReceiverPlaceholder = receiverPlaceholder; + this.ArgumentPlaceholders = argumentPlaceholders; } @@ -7125,19 +7328,23 @@ public BoundIndexOrRangePatternIndexerAccess(SyntaxNode syntax, BoundExpression public BoundExpression Receiver { get; } - public PropertySymbol LengthOrCountProperty { get; } + public BoundExpression Argument { get; } - public Symbol PatternSymbol { get; } + public BoundExpression? LengthOrCountAccess { get; } - public BoundExpression Argument { get; } + public BoundExpression IndexerAccess { get; } + + public BoundIndexOrRangeIndexerPatternReceiverPlaceholder ReceiverPlaceholder { get; } + + public ImmutableArray ArgumentPlaceholders { get; } [DebuggerStepThrough] public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitIndexOrRangePatternIndexerAccess(this); - public BoundIndexOrRangePatternIndexerAccess Update(BoundExpression receiver, PropertySymbol lengthOrCountProperty, Symbol underlyingIndexerOrSliceSymbol, BoundExpression argument, TypeSymbol type) + public BoundIndexOrRangePatternIndexerAccess Update(BoundExpression receiver, BoundExpression argument, BoundExpression? lengthOrCountAccess, BoundExpression indexerAccess, BoundIndexOrRangeIndexerPatternReceiverPlaceholder receiverPlaceholder, ImmutableArray argumentPlaceholders, TypeSymbol type) { - if (receiver != this.Receiver || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(lengthOrCountProperty, this.LengthOrCountProperty) || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(underlyingIndexerOrSliceSymbol, this.PatternSymbol) || argument != this.Argument || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) + if (receiver != this.Receiver || argument != this.Argument || lengthOrCountAccess != this.LengthOrCountAccess || indexerAccess != this.IndexerAccess || receiverPlaceholder != this.ReceiverPlaceholder || argumentPlaceholders != this.ArgumentPlaceholders || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) { - var result = new BoundIndexOrRangePatternIndexerAccess(this.Syntax, receiver, lengthOrCountProperty, underlyingIndexerOrSliceSymbol, argument, type, this.HasErrors); + var result = new BoundIndexOrRangePatternIndexerAccess(this.Syntax, receiver, argument, lengthOrCountAccess, indexerAccess, receiverPlaceholder, argumentPlaceholders, type, this.HasErrors); result.CopyAttributes(this); return result; } @@ -7810,8 +8017,8 @@ public BoundRecursivePattern Update(BoundTypeExpression? declaredType, MethodSym internal sealed partial class BoundListPattern : BoundObjectPattern { - public BoundListPattern(SyntaxNode syntax, ImmutableArray subpatterns, bool hasSlice, PropertySymbol? lengthProperty, BoundIndexerAccess? indexerAccess, PropertySymbol? indexerSymbol, Symbol? variable, BoundExpression? variableAccess, TypeSymbol inputType, TypeSymbol narrowedType, bool hasErrors = false) - : base(BoundKind.ListPattern, syntax, variable, variableAccess, inputType, narrowedType, hasErrors || subpatterns.HasErrors() || indexerAccess.HasErrors() || variableAccess.HasErrors()) + public BoundListPattern(SyntaxNode syntax, ImmutableArray subpatterns, bool hasSlice, BoundExpression? lengthAccess, BoundExpression? indexerAccess, BoundListPatternReceiverPlaceholder? receiverPlaceholder, BoundListPatternUnloweredIndexPlaceholder? argumentPlaceholder, Symbol? variable, BoundExpression? variableAccess, TypeSymbol inputType, TypeSymbol narrowedType, bool hasErrors = false) + : base(BoundKind.ListPattern, syntax, variable, variableAccess, inputType, narrowedType, hasErrors || subpatterns.HasErrors() || lengthAccess.HasErrors() || indexerAccess.HasErrors() || receiverPlaceholder.HasErrors() || argumentPlaceholder.HasErrors() || variableAccess.HasErrors()) { RoslynDebug.Assert(!subpatterns.IsDefault, "Field 'subpatterns' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); @@ -7820,9 +8027,10 @@ public BoundListPattern(SyntaxNode syntax, ImmutableArray subpatte this.Subpatterns = subpatterns; this.HasSlice = hasSlice; - this.LengthProperty = lengthProperty; + this.LengthAccess = lengthAccess; this.IndexerAccess = indexerAccess; - this.IndexerSymbol = indexerSymbol; + this.ReceiverPlaceholder = receiverPlaceholder; + this.ArgumentPlaceholder = argumentPlaceholder; } @@ -7830,19 +8038,21 @@ public BoundListPattern(SyntaxNode syntax, ImmutableArray subpatte public bool HasSlice { get; } - public PropertySymbol? LengthProperty { get; } + public BoundExpression? LengthAccess { get; } - public BoundIndexerAccess? IndexerAccess { get; } + public BoundExpression? IndexerAccess { get; } - public PropertySymbol? IndexerSymbol { get; } + public BoundListPatternReceiverPlaceholder? ReceiverPlaceholder { get; } + + public BoundListPatternUnloweredIndexPlaceholder? ArgumentPlaceholder { get; } [DebuggerStepThrough] public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitListPattern(this); - public BoundListPattern Update(ImmutableArray subpatterns, bool hasSlice, PropertySymbol? lengthProperty, BoundIndexerAccess? indexerAccess, PropertySymbol? indexerSymbol, Symbol? variable, BoundExpression? variableAccess, TypeSymbol inputType, TypeSymbol narrowedType) + public BoundListPattern Update(ImmutableArray subpatterns, bool hasSlice, BoundExpression? lengthAccess, BoundExpression? indexerAccess, BoundListPatternReceiverPlaceholder? receiverPlaceholder, BoundListPatternUnloweredIndexPlaceholder? argumentPlaceholder, Symbol? variable, BoundExpression? variableAccess, TypeSymbol inputType, TypeSymbol narrowedType) { - if (subpatterns != this.Subpatterns || hasSlice != this.HasSlice || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(lengthProperty, this.LengthProperty) || indexerAccess != this.IndexerAccess || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(indexerSymbol, this.IndexerSymbol) || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(variable, this.Variable) || variableAccess != this.VariableAccess || !TypeSymbol.Equals(inputType, this.InputType, TypeCompareKind.ConsiderEverything) || !TypeSymbol.Equals(narrowedType, this.NarrowedType, TypeCompareKind.ConsiderEverything)) + if (subpatterns != this.Subpatterns || hasSlice != this.HasSlice || lengthAccess != this.LengthAccess || indexerAccess != this.IndexerAccess || receiverPlaceholder != this.ReceiverPlaceholder || argumentPlaceholder != this.ArgumentPlaceholder || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(variable, this.Variable) || variableAccess != this.VariableAccess || !TypeSymbol.Equals(inputType, this.InputType, TypeCompareKind.ConsiderEverything) || !TypeSymbol.Equals(narrowedType, this.NarrowedType, TypeCompareKind.ConsiderEverything)) { - var result = new BoundListPattern(this.Syntax, subpatterns, hasSlice, lengthProperty, indexerAccess, indexerSymbol, variable, variableAccess, inputType, narrowedType, this.HasErrors); + var result = new BoundListPattern(this.Syntax, subpatterns, hasSlice, lengthAccess, indexerAccess, receiverPlaceholder, argumentPlaceholder, variable, variableAccess, inputType, narrowedType, this.HasErrors); result.CopyAttributes(this); return result; } @@ -7852,8 +8062,8 @@ public BoundListPattern Update(ImmutableArray subpatterns, bool ha internal sealed partial class BoundSlicePattern : BoundPattern { - public BoundSlicePattern(SyntaxNode syntax, BoundPattern? pattern, BoundIndexerAccess? indexerAccess, MethodSymbol? sliceMethod, TypeSymbol inputType, TypeSymbol narrowedType, bool hasErrors = false) - : base(BoundKind.SlicePattern, syntax, inputType, narrowedType, hasErrors || pattern.HasErrors() || indexerAccess.HasErrors()) + public BoundSlicePattern(SyntaxNode syntax, BoundPattern? pattern, BoundExpression? indexerAccess, BoundSlicePatternReceiverPlaceholder? receiverPlaceholder, BoundSlicePatternUnloweredRangePlaceholder? argumentPlaceholder, TypeSymbol inputType, TypeSymbol narrowedType, bool hasErrors = false) + : base(BoundKind.SlicePattern, syntax, inputType, narrowedType, hasErrors || pattern.HasErrors() || indexerAccess.HasErrors() || receiverPlaceholder.HasErrors() || argumentPlaceholder.HasErrors()) { RoslynDebug.Assert(inputType is object, "Field 'inputType' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); @@ -7861,23 +8071,26 @@ public BoundSlicePattern(SyntaxNode syntax, BoundPattern? pattern, BoundIndexerA this.Pattern = pattern; this.IndexerAccess = indexerAccess; - this.SliceMethod = sliceMethod; + this.ReceiverPlaceholder = receiverPlaceholder; + this.ArgumentPlaceholder = argumentPlaceholder; } public BoundPattern? Pattern { get; } - public BoundIndexerAccess? IndexerAccess { get; } + public BoundExpression? IndexerAccess { get; } + + public BoundSlicePatternReceiverPlaceholder? ReceiverPlaceholder { get; } - public MethodSymbol? SliceMethod { get; } + public BoundSlicePatternUnloweredRangePlaceholder? ArgumentPlaceholder { get; } [DebuggerStepThrough] public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitSlicePattern(this); - public BoundSlicePattern Update(BoundPattern? pattern, BoundIndexerAccess? indexerAccess, MethodSymbol? sliceMethod, TypeSymbol inputType, TypeSymbol narrowedType) + public BoundSlicePattern Update(BoundPattern? pattern, BoundExpression? indexerAccess, BoundSlicePatternReceiverPlaceholder? receiverPlaceholder, BoundSlicePatternUnloweredRangePlaceholder? argumentPlaceholder, TypeSymbol inputType, TypeSymbol narrowedType) { - if (pattern != this.Pattern || indexerAccess != this.IndexerAccess || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(sliceMethod, this.SliceMethod) || !TypeSymbol.Equals(inputType, this.InputType, TypeCompareKind.ConsiderEverything) || !TypeSymbol.Equals(narrowedType, this.NarrowedType, TypeCompareKind.ConsiderEverything)) + if (pattern != this.Pattern || indexerAccess != this.IndexerAccess || receiverPlaceholder != this.ReceiverPlaceholder || argumentPlaceholder != this.ArgumentPlaceholder || !TypeSymbol.Equals(inputType, this.InputType, TypeCompareKind.ConsiderEverything) || !TypeSymbol.Equals(narrowedType, this.NarrowedType, TypeCompareKind.ConsiderEverything)) { - var result = new BoundSlicePattern(this.Syntax, pattern, indexerAccess, sliceMethod, inputType, narrowedType, this.HasErrors); + var result = new BoundSlicePattern(this.Syntax, pattern, indexerAccess, receiverPlaceholder, argumentPlaceholder, inputType, narrowedType, this.HasErrors); result.CopyAttributes(this); return result; } @@ -8491,6 +8704,16 @@ internal R VisitInternal(BoundNode node, A arg) return VisitObjectOrCollectionValuePlaceholder((BoundObjectOrCollectionValuePlaceholder)node, arg); case BoundKind.IndexOrRangeIndexerPatternValuePlaceholder: return VisitIndexOrRangeIndexerPatternValuePlaceholder((BoundIndexOrRangeIndexerPatternValuePlaceholder)node, arg); + case BoundKind.IndexOrRangeIndexerPatternReceiverPlaceholder: + return VisitIndexOrRangeIndexerPatternReceiverPlaceholder((BoundIndexOrRangeIndexerPatternReceiverPlaceholder)node, arg); + case BoundKind.ListPatternReceiverPlaceholder: + return VisitListPatternReceiverPlaceholder((BoundListPatternReceiverPlaceholder)node, arg); + case BoundKind.ListPatternUnloweredIndexPlaceholder: + return VisitListPatternUnloweredIndexPlaceholder((BoundListPatternUnloweredIndexPlaceholder)node, arg); + case BoundKind.SlicePatternReceiverPlaceholder: + return VisitSlicePatternReceiverPlaceholder((BoundSlicePatternReceiverPlaceholder)node, arg); + case BoundKind.SlicePatternUnloweredRangePlaceholder: + return VisitSlicePatternUnloweredRangePlaceholder((BoundSlicePatternUnloweredRangePlaceholder)node, arg); case BoundKind.Dup: return VisitDup((BoundDup)node, arg); case BoundKind.PassByCopy: @@ -8914,6 +9137,11 @@ internal abstract partial class BoundTreeVisitor public virtual R VisitDisposableValuePlaceholder(BoundDisposableValuePlaceholder node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitObjectOrCollectionValuePlaceholder(BoundObjectOrCollectionValuePlaceholder node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitIndexOrRangeIndexerPatternValuePlaceholder(BoundIndexOrRangeIndexerPatternValuePlaceholder node, A arg) => this.DefaultVisit(node, arg); + public virtual R VisitIndexOrRangeIndexerPatternReceiverPlaceholder(BoundIndexOrRangeIndexerPatternReceiverPlaceholder node, A arg) => this.DefaultVisit(node, arg); + public virtual R VisitListPatternReceiverPlaceholder(BoundListPatternReceiverPlaceholder node, A arg) => this.DefaultVisit(node, arg); + public virtual R VisitListPatternUnloweredIndexPlaceholder(BoundListPatternUnloweredIndexPlaceholder node, A arg) => this.DefaultVisit(node, arg); + public virtual R VisitSlicePatternReceiverPlaceholder(BoundSlicePatternReceiverPlaceholder node, A arg) => this.DefaultVisit(node, arg); + public virtual R VisitSlicePatternUnloweredRangePlaceholder(BoundSlicePatternUnloweredRangePlaceholder node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitDup(BoundDup node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitPassByCopy(BoundPassByCopy node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitBadExpression(BoundBadExpression node, A arg) => this.DefaultVisit(node, arg); @@ -9131,6 +9359,11 @@ internal abstract partial class BoundTreeVisitor public virtual BoundNode? VisitDisposableValuePlaceholder(BoundDisposableValuePlaceholder node) => this.DefaultVisit(node); public virtual BoundNode? VisitObjectOrCollectionValuePlaceholder(BoundObjectOrCollectionValuePlaceholder node) => this.DefaultVisit(node); public virtual BoundNode? VisitIndexOrRangeIndexerPatternValuePlaceholder(BoundIndexOrRangeIndexerPatternValuePlaceholder node) => this.DefaultVisit(node); + public virtual BoundNode? VisitIndexOrRangeIndexerPatternReceiverPlaceholder(BoundIndexOrRangeIndexerPatternReceiverPlaceholder node) => this.DefaultVisit(node); + public virtual BoundNode? VisitListPatternReceiverPlaceholder(BoundListPatternReceiverPlaceholder node) => this.DefaultVisit(node); + public virtual BoundNode? VisitListPatternUnloweredIndexPlaceholder(BoundListPatternUnloweredIndexPlaceholder node) => this.DefaultVisit(node); + public virtual BoundNode? VisitSlicePatternReceiverPlaceholder(BoundSlicePatternReceiverPlaceholder node) => this.DefaultVisit(node); + public virtual BoundNode? VisitSlicePatternUnloweredRangePlaceholder(BoundSlicePatternUnloweredRangePlaceholder node) => this.DefaultVisit(node); public virtual BoundNode? VisitDup(BoundDup node) => this.DefaultVisit(node); public virtual BoundNode? VisitPassByCopy(BoundPassByCopy node) => this.DefaultVisit(node); public virtual BoundNode? VisitBadExpression(BoundBadExpression node) => this.DefaultVisit(node); @@ -9364,6 +9597,11 @@ internal abstract partial class BoundTreeWalker : BoundTreeVisitor public override BoundNode? VisitDisposableValuePlaceholder(BoundDisposableValuePlaceholder node) => null; public override BoundNode? VisitObjectOrCollectionValuePlaceholder(BoundObjectOrCollectionValuePlaceholder node) => null; public override BoundNode? VisitIndexOrRangeIndexerPatternValuePlaceholder(BoundIndexOrRangeIndexerPatternValuePlaceholder node) => null; + public override BoundNode? VisitIndexOrRangeIndexerPatternReceiverPlaceholder(BoundIndexOrRangeIndexerPatternReceiverPlaceholder node) => null; + public override BoundNode? VisitListPatternReceiverPlaceholder(BoundListPatternReceiverPlaceholder node) => null; + public override BoundNode? VisitListPatternUnloweredIndexPlaceholder(BoundListPatternUnloweredIndexPlaceholder node) => null; + public override BoundNode? VisitSlicePatternReceiverPlaceholder(BoundSlicePatternReceiverPlaceholder node) => null; + public override BoundNode? VisitSlicePatternUnloweredRangePlaceholder(BoundSlicePatternUnloweredRangePlaceholder node) => null; public override BoundNode? VisitDup(BoundDup node) => null; public override BoundNode? VisitPassByCopy(BoundPassByCopy node) { @@ -9901,6 +10139,8 @@ internal abstract partial class BoundTreeWalker : BoundTreeVisitor { this.Visit(node.LengthTemp); this.Visit(node.IndexerAccess); + this.Visit(node.ReceiverPlaceholder); + this.Visit(node.ArgumentPlaceholder); this.Visit(node.Input); return null; } @@ -9908,6 +10148,8 @@ internal abstract partial class BoundTreeWalker : BoundTreeVisitor { this.Visit(node.LengthTemp); this.Visit(node.IndexerAccess); + this.Visit(node.ReceiverPlaceholder); + this.Visit(node.ArgumentPlaceholder); this.Visit(node.Input); return null; } @@ -10134,6 +10376,10 @@ internal abstract partial class BoundTreeWalker : BoundTreeVisitor { this.Visit(node.Receiver); this.Visit(node.Argument); + this.Visit(node.LengthOrCountAccess); + this.Visit(node.IndexerAccess); + this.Visit(node.ReceiverPlaceholder); + this.VisitList(node.ArgumentPlaceholders); return null; } public override BoundNode? VisitDynamicIndexerAccess(BoundDynamicIndexerAccess node) @@ -10211,7 +10457,10 @@ internal abstract partial class BoundTreeWalker : BoundTreeVisitor public override BoundNode? VisitListPattern(BoundListPattern node) { this.VisitList(node.Subpatterns); + this.Visit(node.LengthAccess); this.Visit(node.IndexerAccess); + this.Visit(node.ReceiverPlaceholder); + this.Visit(node.ArgumentPlaceholder); this.Visit(node.VariableAccess); return null; } @@ -10219,6 +10468,8 @@ internal abstract partial class BoundTreeWalker : BoundTreeVisitor { this.Visit(node.Pattern); this.Visit(node.IndexerAccess); + this.Visit(node.ReceiverPlaceholder); + this.Visit(node.ArgumentPlaceholder); return null; } public override BoundNode? VisitITuplePattern(BoundITuplePattern node) @@ -10363,6 +10614,31 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor TypeSymbol? type = this.VisitType(node.Type); return node.Update(type); } + public override BoundNode? VisitIndexOrRangeIndexerPatternReceiverPlaceholder(BoundIndexOrRangeIndexerPatternReceiverPlaceholder node) + { + TypeSymbol? type = this.VisitType(node.Type); + return node.Update(node.ValEscape, type); + } + public override BoundNode? VisitListPatternReceiverPlaceholder(BoundListPatternReceiverPlaceholder node) + { + TypeSymbol? type = this.VisitType(node.Type); + return node.Update(node.ValEscape, type); + } + public override BoundNode? VisitListPatternUnloweredIndexPlaceholder(BoundListPatternUnloweredIndexPlaceholder node) + { + TypeSymbol? type = this.VisitType(node.Type); + return node.Update(type); + } + public override BoundNode? VisitSlicePatternReceiverPlaceholder(BoundSlicePatternReceiverPlaceholder node) + { + TypeSymbol? type = this.VisitType(node.Type); + return node.Update(node.ValEscape, type); + } + public override BoundNode? VisitSlicePatternUnloweredRangePlaceholder(BoundSlicePatternUnloweredRangePlaceholder node) + { + TypeSymbol? type = this.VisitType(node.Type); + return node.Update(type); + } public override BoundNode? VisitDup(BoundDup node) { TypeSymbol? type = this.VisitType(node.Type); @@ -11065,18 +11341,22 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor public override BoundNode? VisitDagIndexerEvaluation(BoundDagIndexerEvaluation node) { BoundDagTemp lengthTemp = (BoundDagTemp)this.Visit(node.LengthTemp); - BoundIndexerAccess? indexerAccess = (BoundIndexerAccess?)this.Visit(node.IndexerAccess); + BoundExpression indexerAccess = (BoundExpression)this.Visit(node.IndexerAccess); + BoundListPatternReceiverPlaceholder? receiverPlaceholder = (BoundListPatternReceiverPlaceholder?)this.Visit(node.ReceiverPlaceholder); + BoundListPatternUnloweredIndexPlaceholder? argumentPlaceholder = (BoundListPatternUnloweredIndexPlaceholder?)this.Visit(node.ArgumentPlaceholder); BoundDagTemp input = (BoundDagTemp)this.Visit(node.Input); TypeSymbol? indexerType = this.VisitType(node.IndexerType); - return node.Update(indexerType, lengthTemp, node.Index, indexerAccess, node.IndexerSymbol, input); + return node.Update(indexerType, lengthTemp, node.Index, indexerAccess, receiverPlaceholder, argumentPlaceholder, input); } public override BoundNode? VisitDagSliceEvaluation(BoundDagSliceEvaluation node) { BoundDagTemp lengthTemp = (BoundDagTemp)this.Visit(node.LengthTemp); - BoundIndexerAccess? indexerAccess = (BoundIndexerAccess?)this.Visit(node.IndexerAccess); + BoundExpression indexerAccess = (BoundExpression)this.Visit(node.IndexerAccess); + BoundSlicePatternReceiverPlaceholder? receiverPlaceholder = (BoundSlicePatternReceiverPlaceholder?)this.Visit(node.ReceiverPlaceholder); + BoundSlicePatternUnloweredRangePlaceholder? argumentPlaceholder = (BoundSlicePatternUnloweredRangePlaceholder?)this.Visit(node.ArgumentPlaceholder); BoundDagTemp input = (BoundDagTemp)this.Visit(node.Input); TypeSymbol? sliceType = this.VisitType(node.SliceType); - return node.Update(sliceType, lengthTemp, node.StartIndex, node.EndIndex, indexerAccess, node.SliceMethod, input); + return node.Update(sliceType, lengthTemp, node.StartIndex, node.EndIndex, indexerAccess, receiverPlaceholder, argumentPlaceholder, input); } public override BoundNode? VisitDagAssignmentEvaluation(BoundDagAssignmentEvaluation node) { @@ -11361,8 +11641,12 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor { BoundExpression receiver = (BoundExpression)this.Visit(node.Receiver); BoundExpression argument = (BoundExpression)this.Visit(node.Argument); + BoundExpression? lengthOrCountAccess = (BoundExpression?)this.Visit(node.LengthOrCountAccess); + BoundExpression indexerAccess = (BoundExpression)this.Visit(node.IndexerAccess); + BoundIndexOrRangeIndexerPatternReceiverPlaceholder receiverPlaceholder = (BoundIndexOrRangeIndexerPatternReceiverPlaceholder)this.Visit(node.ReceiverPlaceholder); + ImmutableArray argumentPlaceholders = this.VisitList(node.ArgumentPlaceholders); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(receiver, node.LengthOrCountProperty, node.PatternSymbol, argument, type); + return node.Update(receiver, argument, lengthOrCountAccess, indexerAccess, receiverPlaceholder, argumentPlaceholders, type); } public override BoundNode? VisitDynamicIndexerAccess(BoundDynamicIndexerAccess node) { @@ -11475,19 +11759,24 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor public override BoundNode? VisitListPattern(BoundListPattern node) { ImmutableArray subpatterns = this.VisitList(node.Subpatterns); - BoundIndexerAccess? indexerAccess = (BoundIndexerAccess?)this.Visit(node.IndexerAccess); + BoundExpression? lengthAccess = (BoundExpression?)this.Visit(node.LengthAccess); + BoundExpression? indexerAccess = (BoundExpression?)this.Visit(node.IndexerAccess); + BoundListPatternReceiverPlaceholder? receiverPlaceholder = (BoundListPatternReceiverPlaceholder?)this.Visit(node.ReceiverPlaceholder); + BoundListPatternUnloweredIndexPlaceholder? argumentPlaceholder = (BoundListPatternUnloweredIndexPlaceholder?)this.Visit(node.ArgumentPlaceholder); BoundExpression? variableAccess = (BoundExpression?)this.Visit(node.VariableAccess); TypeSymbol? inputType = this.VisitType(node.InputType); TypeSymbol? narrowedType = this.VisitType(node.NarrowedType); - return node.Update(subpatterns, node.HasSlice, node.LengthProperty, indexerAccess, node.IndexerSymbol, node.Variable, variableAccess, inputType, narrowedType); + return node.Update(subpatterns, node.HasSlice, lengthAccess, indexerAccess, receiverPlaceholder, argumentPlaceholder, node.Variable, variableAccess, inputType, narrowedType); } public override BoundNode? VisitSlicePattern(BoundSlicePattern node) { BoundPattern? pattern = (BoundPattern?)this.Visit(node.Pattern); - BoundIndexerAccess? indexerAccess = (BoundIndexerAccess?)this.Visit(node.IndexerAccess); + BoundExpression? indexerAccess = (BoundExpression?)this.Visit(node.IndexerAccess); + BoundSlicePatternReceiverPlaceholder? receiverPlaceholder = (BoundSlicePatternReceiverPlaceholder?)this.Visit(node.ReceiverPlaceholder); + BoundSlicePatternUnloweredRangePlaceholder? argumentPlaceholder = (BoundSlicePatternUnloweredRangePlaceholder?)this.Visit(node.ArgumentPlaceholder); TypeSymbol? inputType = this.VisitType(node.InputType); TypeSymbol? narrowedType = this.VisitType(node.NarrowedType); - return node.Update(pattern, indexerAccess, node.SliceMethod, inputType, narrowedType); + return node.Update(pattern, indexerAccess, receiverPlaceholder, argumentPlaceholder, inputType, narrowedType); } public override BoundNode? VisitITuplePattern(BoundITuplePattern node) { @@ -11719,6 +12008,66 @@ public NullabilityRewriter(ImmutableDictionary argumentPlaceholders = this.VisitList(node.ArgumentPlaceholders); BoundIndexOrRangePatternIndexerAccess updatedNode; if (_updatedNullabilities.TryGetValue(node, out (NullabilityInfo Info, TypeSymbol? Type) infoAndType)) { - updatedNode = node.Update(receiver, lengthOrCountProperty, underlyingIndexerOrSliceSymbol, argument, infoAndType.Type!); + updatedNode = node.Update(receiver, argument, lengthOrCountAccess, indexerAccess, receiverPlaceholder, argumentPlaceholders, infoAndType.Type!); updatedNode.TopLevelNullability = infoAndType.Info; } else { - updatedNode = node.Update(receiver, lengthOrCountProperty, underlyingIndexerOrSliceSymbol, argument, node.Type); + updatedNode = node.Update(receiver, argument, lengthOrCountAccess, indexerAccess, receiverPlaceholder, argumentPlaceholders, node.Type); } return updatedNode; } @@ -13898,25 +14251,27 @@ public NullabilityRewriter(ImmutableDictionary subpatterns = this.VisitList(node.Subpatterns); - BoundIndexerAccess? indexerAccess = (BoundIndexerAccess?)this.Visit(node.IndexerAccess); + BoundExpression? lengthAccess = (BoundExpression?)this.Visit(node.LengthAccess); + BoundExpression? indexerAccess = (BoundExpression?)this.Visit(node.IndexerAccess); + BoundListPatternReceiverPlaceholder? receiverPlaceholder = (BoundListPatternReceiverPlaceholder?)this.Visit(node.ReceiverPlaceholder); + BoundListPatternUnloweredIndexPlaceholder? argumentPlaceholder = (BoundListPatternUnloweredIndexPlaceholder?)this.Visit(node.ArgumentPlaceholder); BoundExpression? variableAccess = (BoundExpression?)this.Visit(node.VariableAccess); - return node.Update(subpatterns, node.HasSlice, lengthProperty, indexerAccess, indexerSymbol, variable, variableAccess, inputType, narrowedType); + return node.Update(subpatterns, node.HasSlice, lengthAccess, indexerAccess, receiverPlaceholder, argumentPlaceholder, variable, variableAccess, inputType, narrowedType); } public override BoundNode? VisitSlicePattern(BoundSlicePattern node) { - MethodSymbol? sliceMethod = GetUpdatedSymbol(node, node.SliceMethod); TypeSymbol inputType = GetUpdatedSymbol(node, node.InputType); TypeSymbol narrowedType = GetUpdatedSymbol(node, node.NarrowedType); BoundPattern? pattern = (BoundPattern?)this.Visit(node.Pattern); - BoundIndexerAccess? indexerAccess = (BoundIndexerAccess?)this.Visit(node.IndexerAccess); - return node.Update(pattern, indexerAccess, sliceMethod, inputType, narrowedType); + BoundExpression? indexerAccess = (BoundExpression?)this.Visit(node.IndexerAccess); + BoundSlicePatternReceiverPlaceholder? receiverPlaceholder = (BoundSlicePatternReceiverPlaceholder?)this.Visit(node.ReceiverPlaceholder); + BoundSlicePatternUnloweredRangePlaceholder? argumentPlaceholder = (BoundSlicePatternUnloweredRangePlaceholder?)this.Visit(node.ArgumentPlaceholder); + return node.Update(pattern, indexerAccess, receiverPlaceholder, argumentPlaceholder, inputType, narrowedType); } public override BoundNode? VisitITuplePattern(BoundITuplePattern node) @@ -14180,7 +14535,45 @@ private BoundTreeDumperNodeProducer() new TreeDumperNode("hasErrors", node.HasErrors, null) } ); - public override TreeDumperNode VisitIndexOrRangeIndexerPatternValuePlaceholder(BoundIndexOrRangeIndexerPatternValuePlaceholder node, object? arg) => new TreeDumperNode("indexOrRangeImplicitIndexerValuePlaceholder", null, new TreeDumperNode[] + public override TreeDumperNode VisitIndexOrRangeIndexerPatternValuePlaceholder(BoundIndexOrRangeIndexerPatternValuePlaceholder node, object? arg) => new TreeDumperNode("indexOrRangeIndexerPatternValuePlaceholder", null, new TreeDumperNode[] + { + new TreeDumperNode("type", node.Type, null), + new TreeDumperNode("isSuppressed", node.IsSuppressed, null), + new TreeDumperNode("hasErrors", node.HasErrors, null) + } + ); + public override TreeDumperNode VisitIndexOrRangeIndexerPatternReceiverPlaceholder(BoundIndexOrRangeIndexerPatternReceiverPlaceholder node, object? arg) => new TreeDumperNode("indexOrRangeIndexerPatternReceiverPlaceholder", null, new TreeDumperNode[] + { + new TreeDumperNode("valEscape", node.ValEscape, null), + new TreeDumperNode("type", node.Type, null), + new TreeDumperNode("isSuppressed", node.IsSuppressed, null), + new TreeDumperNode("hasErrors", node.HasErrors, null) + } + ); + public override TreeDumperNode VisitListPatternReceiverPlaceholder(BoundListPatternReceiverPlaceholder node, object? arg) => new TreeDumperNode("listPatternReceiverPlaceholder", null, new TreeDumperNode[] + { + new TreeDumperNode("valEscape", node.ValEscape, null), + new TreeDumperNode("type", node.Type, null), + new TreeDumperNode("isSuppressed", node.IsSuppressed, null), + new TreeDumperNode("hasErrors", node.HasErrors, null) + } + ); + public override TreeDumperNode VisitListPatternUnloweredIndexPlaceholder(BoundListPatternUnloweredIndexPlaceholder node, object? arg) => new TreeDumperNode("listPatternUnloweredIndexPlaceholder", null, new TreeDumperNode[] + { + new TreeDumperNode("type", node.Type, null), + new TreeDumperNode("isSuppressed", node.IsSuppressed, null), + new TreeDumperNode("hasErrors", node.HasErrors, null) + } + ); + public override TreeDumperNode VisitSlicePatternReceiverPlaceholder(BoundSlicePatternReceiverPlaceholder node, object? arg) => new TreeDumperNode("slicePatternReceiverPlaceholder", null, new TreeDumperNode[] + { + new TreeDumperNode("valEscape", node.ValEscape, null), + new TreeDumperNode("type", node.Type, null), + new TreeDumperNode("isSuppressed", node.IsSuppressed, null), + new TreeDumperNode("hasErrors", node.HasErrors, null) + } + ); + public override TreeDumperNode VisitSlicePatternUnloweredRangePlaceholder(BoundSlicePatternUnloweredRangePlaceholder node, object? arg) => new TreeDumperNode("slicePatternUnloweredRangePlaceholder", null, new TreeDumperNode[] { new TreeDumperNode("type", node.Type, null), new TreeDumperNode("isSuppressed", node.IsSuppressed, null), @@ -15252,7 +15645,8 @@ private BoundTreeDumperNodeProducer() new TreeDumperNode("lengthTemp", null, new TreeDumperNode[] { Visit(node.LengthTemp, null) }), new TreeDumperNode("index", node.Index, null), new TreeDumperNode("indexerAccess", null, new TreeDumperNode[] { Visit(node.IndexerAccess, null) }), - new TreeDumperNode("indexerSymbol", node.IndexerSymbol, null), + new TreeDumperNode("receiverPlaceholder", null, new TreeDumperNode[] { Visit(node.ReceiverPlaceholder, null) }), + new TreeDumperNode("argumentPlaceholder", null, new TreeDumperNode[] { Visit(node.ArgumentPlaceholder, null) }), new TreeDumperNode("input", null, new TreeDumperNode[] { Visit(node.Input, null) }), new TreeDumperNode("hasErrors", node.HasErrors, null) } @@ -15264,7 +15658,8 @@ private BoundTreeDumperNodeProducer() new TreeDumperNode("startIndex", node.StartIndex, null), new TreeDumperNode("endIndex", node.EndIndex, null), new TreeDumperNode("indexerAccess", null, new TreeDumperNode[] { Visit(node.IndexerAccess, null) }), - new TreeDumperNode("sliceMethod", node.SliceMethod, null), + new TreeDumperNode("receiverPlaceholder", null, new TreeDumperNode[] { Visit(node.ReceiverPlaceholder, null) }), + new TreeDumperNode("argumentPlaceholder", null, new TreeDumperNode[] { Visit(node.ArgumentPlaceholder, null) }), new TreeDumperNode("input", null, new TreeDumperNode[] { Visit(node.Input, null) }), new TreeDumperNode("hasErrors", node.HasErrors, null) } @@ -15732,12 +16127,14 @@ private BoundTreeDumperNodeProducer() new TreeDumperNode("hasErrors", node.HasErrors, null) } ); - public override TreeDumperNode VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node, object? arg) => new TreeDumperNode("indexOrRangeImplicitIndexerAccess", null, new TreeDumperNode[] + public override TreeDumperNode VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node, object? arg) => new TreeDumperNode("indexOrRangePatternIndexerAccess", null, new TreeDumperNode[] { new TreeDumperNode("receiver", null, new TreeDumperNode[] { Visit(node.Receiver, null) }), - new TreeDumperNode("lengthOrCountProperty", node.LengthOrCountProperty, null), - new TreeDumperNode("underlyingIndexerOrSliceSymbol", node.PatternSymbol, null), new TreeDumperNode("argument", null, new TreeDumperNode[] { Visit(node.Argument, null) }), + new TreeDumperNode("lengthOrCountAccess", null, new TreeDumperNode[] { Visit(node.LengthOrCountAccess, null) }), + new TreeDumperNode("indexerAccess", null, new TreeDumperNode[] { Visit(node.IndexerAccess, null) }), + new TreeDumperNode("receiverPlaceholder", null, new TreeDumperNode[] { Visit(node.ReceiverPlaceholder, null) }), + new TreeDumperNode("argumentPlaceholders", null, from x in node.ArgumentPlaceholders select Visit(x, null)), new TreeDumperNode("type", node.Type, null), new TreeDumperNode("isSuppressed", node.IsSuppressed, null), new TreeDumperNode("hasErrors", node.HasErrors, null) @@ -15909,9 +16306,10 @@ private BoundTreeDumperNodeProducer() { new TreeDumperNode("subpatterns", null, from x in node.Subpatterns select Visit(x, null)), new TreeDumperNode("hasSlice", node.HasSlice, null), - new TreeDumperNode("lengthProperty", node.LengthProperty, null), + new TreeDumperNode("lengthAccess", null, new TreeDumperNode[] { Visit(node.LengthAccess, null) }), new TreeDumperNode("indexerAccess", null, new TreeDumperNode[] { Visit(node.IndexerAccess, null) }), - new TreeDumperNode("indexerSymbol", node.IndexerSymbol, null), + new TreeDumperNode("receiverPlaceholder", null, new TreeDumperNode[] { Visit(node.ReceiverPlaceholder, null) }), + new TreeDumperNode("argumentPlaceholder", null, new TreeDumperNode[] { Visit(node.ArgumentPlaceholder, null) }), new TreeDumperNode("variable", node.Variable, null), new TreeDumperNode("variableAccess", null, new TreeDumperNode[] { Visit(node.VariableAccess, null) }), new TreeDumperNode("inputType", node.InputType, null), @@ -15923,7 +16321,8 @@ private BoundTreeDumperNodeProducer() { new TreeDumperNode("pattern", null, new TreeDumperNode[] { Visit(node.Pattern, null) }), new TreeDumperNode("indexerAccess", null, new TreeDumperNode[] { Visit(node.IndexerAccess, null) }), - new TreeDumperNode("sliceMethod", node.SliceMethod, null), + new TreeDumperNode("receiverPlaceholder", null, new TreeDumperNode[] { Visit(node.ReceiverPlaceholder, null) }), + new TreeDumperNode("argumentPlaceholder", null, new TreeDumperNode[] { Visit(node.ArgumentPlaceholder, null) }), new TreeDumperNode("inputType", node.InputType, null), new TreeDumperNode("narrowedType", node.NarrowedType, null), new TreeDumperNode("hasErrors", node.HasErrors, null) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.PatternLocalRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.PatternLocalRewriter.cs index 4dc06c07fdb4..a1078911cea9 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.PatternLocalRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.PatternLocalRewriter.cs @@ -245,72 +245,48 @@ void addArg(RefKind refKind, BoundExpression expression) return _factory.AssignmentExpression(output, _factory.Call(input, e.Property.GetMethod, _factory.Literal(e.Index))); } - case BoundDagIndexerEvaluation { IndexerAccess: null, IndexerSymbol: null } e: - { - // array[int] - Debug.Assert(input.Type?.IsSZArray() == true); - BoundExpression access = _factory.ArrayAccess(input, makeImplicitIndexArgument(e.Index, e.LengthTemp)); - var outputTemp = new BoundDagTemp(e.Syntax, e.IndexerType, e); - BoundExpression output = _tempAllocator.GetTemp(outputTemp); - return _factory.AssignmentExpression(output, access); - } - - case BoundDagIndexerEvaluation { IndexerSymbol: PropertySymbol indexer } e: - { - // this[int] - BoundExpression access = _factory.Indexer(input, indexer, makeImplicitIndexArgument(e.Index, e.LengthTemp)); - var outputTemp = new BoundDagTemp(e.Syntax, e.IndexerType, e); - BoundExpression output = _tempAllocator.GetTemp(outputTemp); - return _factory.AssignmentExpression(output, access); - } - - case BoundDagIndexerEvaluation { IndexerAccess: BoundIndexerAccess indexerAccess } e: + case BoundDagIndexerEvaluation e: { // this[Index] - BoundExpression access = makeIndexerAccess(e.Syntax, indexerAccess, makeExplicitIndexArgument(e.Index, e.Index < 0)); - var outputTemp = new BoundDagTemp(e.Syntax, e.IndexerType, e); - BoundExpression output = _tempAllocator.GetTemp(outputTemp); - return _factory.AssignmentExpression(output, access); - } + // this[int] + // array[Index] + _localRewriter.AddPlaceholderReplacement(e.ReceiverPlaceholder, input); + _localRewriter.AddPlaceholderReplacement(e.ArgumentPlaceholder, makeUnloweredIndexArgument(e.Index)); - case BoundDagSliceEvaluation { IndexerAccess: null, SliceMethod: null } e: - { - // RuntimeHelpers.GetSubArray(T[], Range) - TypeSymbol inputType = input.Type; - Debug.Assert(inputType is { }); - Debug.Assert(inputType.IsSZArray()); - Debug.Assert(e.StartIndex >= 0 && e.EndIndex <= 0); + var indexerAccess = e.IndexerAccess; + if (indexerAccess is BoundIndexOrRangePatternIndexerAccess implicitAccess) + { + indexerAccess = implicitAccess.WithLengthOrCountAccess(_tempAllocator.GetTemp(e.LengthTemp)); + } - BoundExpression callExpr = _factory.Call( - receiver: null, - _factory.WellKnownMethod(WellKnownMember.System_Runtime_CompilerServices_RuntimeHelpers__GetSubArray_T) - .Construct(ImmutableArray.Create(((ArrayTypeSymbol)inputType).ElementType)), - ImmutableArray.Create(input, makeExplicitRangeArgument(e))); + var access = (BoundExpression)_localRewriter.Visit(indexerAccess); + _localRewriter.RemovePlaceholderReplacement(e.ReceiverPlaceholder); + _localRewriter.RemovePlaceholderReplacement(e.ArgumentPlaceholder); - var outputTemp = new BoundDagTemp(e.Syntax, e.SliceType, e); + var outputTemp = new BoundDagTemp(e.Syntax, e.IndexerType, e); BoundExpression output = _tempAllocator.GetTemp(outputTemp); - return _factory.AssignmentExpression(output, callExpr); + return _factory.AssignmentExpression(output, access); } - case BoundDagSliceEvaluation { SliceMethod: MethodSymbol sliceMethod } e: + case BoundDagSliceEvaluation e: { + // this[Range] // Slice(int, int) - Debug.Assert(sliceMethod.ParameterCount == 2); - Debug.Assert(sliceMethod.Parameters[0].Type.SpecialType == SpecialType.System_Int32); - Debug.Assert(sliceMethod.Parameters[1].Type.SpecialType == SpecialType.System_Int32); - Debug.Assert(e.StartIndex >= 0 && e.EndIndex <= 0); + // string.Substring(int, int) + // array[Range] + _localRewriter.AddPlaceholderReplacement(e.ReceiverPlaceholder, input); + _localRewriter.AddPlaceholderReplacement(e.ArgumentPlaceholder, makeUnloweredRangeArgument(e)); - BoundExpression lengthArg = _factory.IntSubtract(_tempAllocator.GetTemp(e.LengthTemp), _factory.Literal(e.StartIndex - e.EndIndex)); - BoundExpression callExpr = _factory.Call(input, sliceMethod, _factory.Literal(e.StartIndex), lengthArg); - var outputTemp = new BoundDagTemp(e.Syntax, e.SliceType, e); - BoundExpression output = _tempAllocator.GetTemp(outputTemp); - return _factory.AssignmentExpression(output, callExpr); - } + var indexerAccess = e.IndexerAccess; + if (indexerAccess is BoundIndexOrRangePatternIndexerAccess implicitAccess) + { + indexerAccess = implicitAccess.WithLengthOrCountAccess(_tempAllocator.GetTemp(e.LengthTemp)); + } + + var access = (BoundExpression)_localRewriter.Visit(indexerAccess); + _localRewriter.RemovePlaceholderReplacement(e.ReceiverPlaceholder); + _localRewriter.RemovePlaceholderReplacement(e.ArgumentPlaceholder); - case BoundDagSliceEvaluation { IndexerAccess: BoundIndexerAccess indexerAccess } e: - { - // this[Range] - BoundExpression access = makeIndexerAccess(e.Syntax, indexerAccess, makeExplicitRangeArgument(e)); var outputTemp = new BoundDagTemp(e.Syntax, e.SliceType, e); BoundExpression output = _tempAllocator.GetTemp(outputTemp); return _factory.AssignmentExpression(output, access); @@ -320,52 +296,29 @@ void addArg(RefKind refKind, BoundExpression expression) throw ExceptionUtilities.UnexpectedValue(evaluation); } - BoundExpression makeImplicitIndexArgument(int index, BoundDagTemp lengthTemp) - { - return index < 0 - ? _factory.IntSubtract(_tempAllocator.GetTemp(lengthTemp), _factory.Literal(-index)) - : _factory.Literal(index); - } - - BoundExpression makeExplicitIndexArgument(int index, bool fromEnd) + BoundExpression makeUnloweredIndexArgument(int index) { - return _factory.New( - WellKnownMember.System_Index__ctor, - ImmutableArray.Create( - _factory.Literal(Math.Abs(index)), - _factory.Literal(fromEnd))); - } + // LocalRewriter.MakePatternIndexOffsetExpression understands this format + if (index < 0) + { + MethodSymbol symbolOpt = _factory.WellKnownMember(WellKnownMember.System_Index__ctor) as MethodSymbol; + return new BoundFromEndIndexExpression(_factory.Syntax, _factory.Literal(-index), + methodOpt: symbolOpt, _factory.WellKnownType(WellKnownType.System_Index)); + } - BoundExpression makeExplicitRangeArgument(BoundDagSliceEvaluation e) - { - return _factory.New( - WellKnownMember.System_Range__ctor, - ImmutableArray.Create( - makeExplicitIndexArgument(e.StartIndex, fromEnd: false), - makeExplicitIndexArgument(e.EndIndex, fromEnd: true))); + return _factory.Convert(_factory.WellKnownType(WellKnownType.System_Index), _factory.Literal(index)); } - BoundExpression makeIndexerAccess(SyntaxNode syntax, BoundIndexerAccess indexerAccess, BoundExpression indexerArg) + BoundExpression makeUnloweredRangeArgument(BoundDagSliceEvaluation e) { - ImmutableArray arguments = indexerAccess.Arguments; - Debug.Assert(!arguments.IsDefaultOrEmpty); - - if (arguments[0] is BoundConversion conv) - indexerArg = conv.UpdateOperand(indexerArg); - - return _localRewriter.MakeIndexerAccess( - syntax, - input, - indexerAccess.Indexer, - arguments.SetItem(0, indexerArg), - indexerAccess.ArgumentNamesOpt, - indexerAccess.ArgumentRefKindsOpt, - indexerAccess.Expanded, - indexerAccess.ArgsToParamsOpt, - indexerAccess.DefaultArguments, - indexerAccess.Type, - oldNodeOpt: null, - isLeftOfAssignment: false); + // LocalRewriter.VisitRangeImplicitIndexerAccess understands this format + MethodSymbol symbolOpt1 = _factory.WellKnownMember(WellKnownMember.System_Index__ctor) as MethodSymbol; + var end = new BoundFromEndIndexExpression(_factory.Syntax, _factory.Literal(-e.EndIndex), + methodOpt: symbolOpt1, _factory.WellKnownType(WellKnownType.System_Index)); + + MethodSymbol symbolOpt = _factory.WellKnownMember(WellKnownMember.System_Range__ctor) as MethodSymbol; + return new BoundRangeExpression(e.Syntax, makeUnloweredIndexArgument(e.StartIndex), end, + methodOpt: symbolOpt, _factory.WellKnownType(WellKnownType.System_Range)); } } diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs index faa82b4f8ecb..3b7de93c09d6 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs @@ -124,6 +124,7 @@ public static BoundStatement Rewrite( } #if DEBUG LocalRewritingValidator.Validate(loweredStatement); + localRewriter.AssertNoPlaceholderReplacements(); #endif return loweredStatement; } @@ -413,18 +414,36 @@ private BoundExpression PlaceholderReplacement(BoundValuePlaceholderBase placeho return value; } + private BoundExpression UnwrapPlaceholderIfNeeded(BoundExpression expr) + { + if (expr is BoundValuePlaceholderBase placeholder) + { + expr = PlaceholderReplacement(placeholder); + } + return expr; + } + [Conditional("DEBUG")] private static void AssertPlaceholderReplacement(BoundValuePlaceholderBase placeholder, BoundExpression value) { Debug.Assert(value.Type is { } && (value.Type.Equals(placeholder.Type, TypeCompareKind.AllIgnoreOptions) || value.HasErrors)); } + [Conditional("DEBUG")] + private void AssertNoPlaceholderReplacements() + { + if (_placeholderReplacementMapDoNotUseDirectly is not null) + { + Debug.Assert(_placeholderReplacementMapDoNotUseDirectly.Count == 0); + } + } + /// /// Sets substitution used by the rewriter for a placeholder node. /// Each occurrence of the placeholder node is replaced with the node returned. /// Throws if there is already a substitution. /// - private void AddPlaceholderReplacement(BoundValuePlaceholderBase placeholder, BoundExpression value) + private void AddPlaceholderReplacement(T placeholder, BoundExpression value) where T : BoundValuePlaceholderBase { AssertPlaceholderReplacement(placeholder, value); @@ -954,17 +973,15 @@ internal static bool CanBePassedByReference(BoundExpression expr) return ((BoundIndexerAccess)expr).Indexer.RefKind != RefKind.None; case BoundKind.IndexOrRangePatternIndexerAccess: - var implicitIndexerAccess = (BoundIndexOrRangePatternIndexerAccess)expr; - var refKind = implicitIndexerAccess.PatternSymbol switch - { - PropertySymbol p => p.RefKind, - MethodSymbol m => m.RefKind, - _ => throw ExceptionUtilities.UnexpectedValue(implicitIndexerAccess.PatternSymbol) - }; - return refKind != RefKind.None; + return CanBePassedByReference(((BoundIndexOrRangePatternIndexerAccess)expr).IndexerAccess); + + // TODO2 I'm not sure about this + case BoundKind.IndexOrRangeIndexerPatternReceiverPlaceholder: + case BoundKind.ListPatternReceiverPlaceholder: + case BoundKind.SlicePatternReceiverPlaceholder: + return true; case BoundKind.Conversion: - var conversion = ((BoundConversion)expr); return expr is BoundConversion { Conversion: { IsInterpolatedStringHandler: true }, Type: { IsValueType: true } }; } @@ -988,6 +1005,7 @@ private CompoundUseSiteInfo GetNewCompoundUseSiteInfo() #if DEBUG /// /// Note: do not use a static/singleton instance of this type, as it holds state. + /// Consider generating this type from BoundNodes.xml for easier maintenance. /// private sealed class LocalRewritingValidator : BoundTreeWalkerWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperator { @@ -1054,6 +1072,36 @@ public static void Validate(BoundNode node) return null; } + public override BoundNode? VisitIndexOrRangeIndexerPatternReceiverPlaceholder(BoundIndexOrRangeIndexerPatternReceiverPlaceholder node) + { + Fail(node); + return null; + } + + public override BoundNode? VisitListPatternUnloweredIndexPlaceholder(BoundListPatternUnloweredIndexPlaceholder node) + { + Fail(node); + return null; + } + + public override BoundNode? VisitListPatternReceiverPlaceholder(BoundListPatternReceiverPlaceholder node) + { + Fail(node); + return null; + } + + public override BoundNode? VisitSlicePatternUnloweredRangePlaceholder(BoundSlicePatternUnloweredRangePlaceholder node) + { + Fail(node); + return null; + } + + public override BoundNode? VisitSlicePatternReceiverPlaceholder(BoundSlicePatternReceiverPlaceholder node) + { + Fail(node); + return null; + } + public override BoundNode? VisitInterpolatedStringArgumentPlaceholder(BoundInterpolatedStringArgumentPlaceholder node) { Fail(node); diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs index 41b3f9c91aee..25f2ca4c0ab1 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs @@ -588,13 +588,7 @@ private BoundExpression TransformCompoundAssignmentLHS(BoundExpression originalL case BoundKind.IndexOrRangePatternIndexerAccess: { var implicitIndexerAccess = (BoundIndexOrRangePatternIndexerAccess)originalLHS; - RefKind refKind = implicitIndexerAccess.PatternSymbol switch - { - PropertySymbol p => p.RefKind, - MethodSymbol m => m.RefKind, - var x => throw ExceptionUtilities.UnexpectedValue(x) - }; - if (refKind == RefKind.None) + if (implicitIndexerAccess.GetRefKind() == RefKind.None) { return TransformImplicitIndexerAccess(implicitIndexerAccess, stores, temps, isDynamicAssignment); } diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs index 0b05fb180f9b..afc8adcf3bfb 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs @@ -161,6 +161,37 @@ private BoundExpression MakeIndexerAccess( } } } + + public override BoundNode? VisitListPatternUnloweredIndexPlaceholder(BoundListPatternUnloweredIndexPlaceholder node) + { + return Visit(PlaceholderReplacement(node)); + } + + public override BoundNode? VisitListPatternReceiverPlaceholder(BoundListPatternReceiverPlaceholder node) + { + return PlaceholderReplacement(node); + } + + public override BoundNode? VisitSlicePatternUnloweredRangePlaceholder(BoundSlicePatternUnloweredRangePlaceholder node) + { + return Visit(PlaceholderReplacement(node)); + } + + public override BoundNode? VisitSlicePatternReceiverPlaceholder(BoundSlicePatternReceiverPlaceholder node) + { + return PlaceholderReplacement(node); + } + + public override BoundNode? VisitIndexOrRangeIndexerPatternReceiverPlaceholder(BoundIndexOrRangeIndexerPatternReceiverPlaceholder node) + { + return PlaceholderReplacement(node); + } + + public override BoundNode? VisitIndexOrRangeIndexerPatternValuePlaceholder(BoundIndexOrRangeIndexerPatternValuePlaceholder node) + { + return PlaceholderReplacement(node); + } + public override BoundNode VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node) { return VisitIndexOrRangePatternIndexerAccess(node, isLeftOfAssignment: false); @@ -173,13 +204,7 @@ private BoundSequence VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePat _compilation.GetWellKnownType(WellKnownType.System_Index), TypeCompareKind.ConsiderEverything)) { - return VisitIndexImplicitIndexerAccess( - node.Syntax, - node.Receiver, - node.LengthOrCountProperty, - (PropertySymbol)node.PatternSymbol, - node.Argument, - isLeftOfAssignment: isLeftOfAssignment); + return VisitIndexImplicitIndexerAccess(node, isLeftOfAssignment: isLeftOfAssignment); } else { @@ -187,23 +212,22 @@ private BoundSequence VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePat node.Argument.Type, _compilation.GetWellKnownType(WellKnownType.System_Range), TypeCompareKind.ConsiderEverything)); - return VisitRangeImplicitIndexerAccess( - node.Receiver, - node.LengthOrCountProperty, - (MethodSymbol)node.PatternSymbol, - node.Argument); + + return VisitRangeImplicitIndexerAccess(node); } } - private BoundSequence VisitIndexImplicitIndexerAccess( - SyntaxNode syntax, - BoundExpression receiver, - PropertySymbol lengthOrCountProperty, - PropertySymbol intIndexer, - BoundExpression argument, - bool isLeftOfAssignment) + private BoundSequence VisitIndexImplicitIndexerAccess(BoundIndexOrRangePatternIndexerAccess node, bool isLeftOfAssignment) // TODO2 we're not using isLeftOfAssignment { + Debug.Assert(node.ArgumentPlaceholders.Length == 1); + + Debug.Assert(TypeSymbol.Equals( + node.Argument.Type, + _compilation.GetWellKnownType(WellKnownType.System_Index), + TypeCompareKind.ConsiderEverything)); + var F = _factory; + var receiver = node.Receiver; Debug.Assert(receiver.Type is { }); var receiverLocal = F.StoreToTemp( @@ -211,45 +235,47 @@ private BoundSequence VisitIndexImplicitIndexerAccess( out var receiverStore, // Store the receiver as a ref local if it's a value type to ensure side effects are propagated receiver.Type.IsReferenceType ? RefKind.None : RefKind.Ref); - var indexAccess = MakePatternIndexOffsetExpression(argument, F.Property(receiverLocal, lengthOrCountProperty), out _); + + var receiverPlaceholder = node.ReceiverPlaceholder; + AddPlaceholderReplacement(receiverPlaceholder, receiverLocal); + + Debug.Assert(node.LengthOrCountAccess is not null); + var integerArgument = MakePatternIndexOffsetExpression(node.Argument, VisitExpression(node.LengthOrCountAccess), out _); + Debug.Assert(integerArgument.Type!.SpecialType == SpecialType.System_Int32); + + Debug.Assert(node.ArgumentPlaceholders.Length == 1); + var argumentPlaceholder = node.ArgumentPlaceholders[0]; + + AddPlaceholderReplacement(argumentPlaceholder, integerArgument); + var rewrittenIndexerAccess = VisitExpression(node.IndexerAccess); + RemovePlaceholderReplacement(argumentPlaceholder); + RemovePlaceholderReplacement(receiverPlaceholder); return (BoundSequence)F.Sequence( ImmutableArray.Create(receiverLocal.LocalSymbol), ImmutableArray.Create(receiverStore), - MakeIndexerAccess( - syntax, - receiverLocal, - intIndexer, - ImmutableArray.Create(indexAccess), - default, - default, - expanded: false, - argsToParamsOpt: default, - defaultArguments: default, - intIndexer.Type, - oldNodeOpt: null, - isLeftOfAssignment)); + rewrittenIndexerAccess); } /// - /// Used to construct a pattern index offset expression, of the form + /// Used to construct a pattern index offset expression (of type Int32), of the form /// `unloweredExpr.GetOffset(lengthAccess)` /// where unloweredExpr is an expression of type System.Index and the /// lengthAccess retrieves the length of the indexing target. /// /// The unlowered argument to the indexing expression - /// + /// /// An expression accessing the length of the indexing target. This should /// be a non-side-effecting operation. /// /// /// True if we were able to optimize the - /// to use the operation directly on the receiver, instead of + /// to use the operation directly on the receiver, instead of /// using System.Index helpers. /// private BoundExpression MakePatternIndexOffsetExpression( BoundExpression unloweredExpr, - BoundExpression lengthAccess, + BoundExpression loweredLengthAccess, out bool usedLength) { Debug.Assert(TypeSymbol.Equals( @@ -259,13 +285,22 @@ private BoundExpression MakePatternIndexOffsetExpression( var F = _factory; + // The argument value may be a non-placeholder value (in an element access scenario: `expr[^1]`or `array[^1]`) + // or a placeholder value (in a list-pattern scenario: `expr is [_, var x]` or `array is [_, var x]`). + unloweredExpr = UnwrapPlaceholderIfNeeded(unloweredExpr); + if (unloweredExpr is BoundFromEndIndexExpression hatExpression) { // If the System.Index argument is `^index`, we can replace the // `argument.GetOffset(length)` call with `length - index` Debug.Assert(hatExpression.Operand is { Type: { SpecialType: SpecialType.System_Int32 } }); usedLength = true; - return F.IntSubtract(lengthAccess, VisitExpression(hatExpression.Operand)); + + if (hatExpression.Operand.ConstantValue is { Int32Value: 0 }) + { + return loweredLengthAccess; + } + return F.IntSubtract(loweredLengthAccess, VisitExpression(hatExpression.Operand)); } else if (unloweredExpr is BoundConversion { Operand: { Type: { SpecialType: SpecialType.System_Int32 } } operand }) { @@ -280,18 +315,16 @@ private BoundExpression MakePatternIndexOffsetExpression( return F.Call( VisitExpression(unloweredExpr), WellKnownMember.System_Index__GetOffset, - lengthAccess); + loweredLengthAccess); } } - private BoundSequence VisitRangeImplicitIndexerAccess( - BoundExpression receiver, - PropertySymbol lengthOrCountProperty, - MethodSymbol sliceMethod, - BoundExpression rangeArg) + private BoundSequence VisitRangeImplicitIndexerAccess(BoundIndexOrRangePatternIndexerAccess node) { + Debug.Assert(node.ArgumentPlaceholders.Length == 2); + Debug.Assert(TypeSymbol.Equals( - rangeArg.Type, + node.Argument.Type, _compilation.GetWellKnownType(WellKnownType.System_Range), TypeCompareKind.ConsiderEverything)); @@ -308,12 +341,29 @@ private BoundSequence VisitRangeImplicitIndexerAccess( var localsBuilder = ArrayBuilder.GetInstance(); var sideEffectsBuilder = ArrayBuilder.GetInstance(); - var receiverLocal = F.StoreToTemp(VisitExpression(receiver), out var receiverStore); - var lengthLocal = F.StoreToTemp(F.Property(receiverLocal, lengthOrCountProperty), out var lengthStore); + var receiverLocal = F.StoreToTemp(VisitExpression(node.Receiver), out var receiverStore); + var receiverPlaceholder = node.ReceiverPlaceholder; + AddPlaceholderReplacement(receiverPlaceholder, receiverLocal); + + Debug.Assert(node.LengthOrCountAccess is not null); + + var loweredLengthAccess = VisitExpression(node.LengthOrCountAccess); + BoundAssignmentOperator? lengthStore = null; + LocalSymbol? lengthLocal = null; + // In a list-pattern, the lengthAccess will already be a a local, but in other cases we want to make one + if (CanChangeValueBetweenReads(loweredLengthAccess, localsMayBeAssignedOrCaptured: false)) + { + var boundLocal = F.StoreToTemp(loweredLengthAccess, out lengthStore); + loweredLengthAccess = boundLocal; + lengthLocal = boundLocal.LocalSymbol; + } localsBuilder.Add(receiverLocal.LocalSymbol); sideEffectsBuilder.Add(receiverStore); + // The argument value may be a non-placeholder value (in an element access scenario: expr[..]) + // or a placeholder value (in a slice-pattern scenario: expr is [_, .. var x]). + var rangeArg = UnwrapPlaceholderIfNeeded(node.Argument); BoundExpression startExpr; BoundExpression rangeSizeExpr; if (rangeArg is BoundRangeExpression rangeExpr) @@ -335,7 +385,7 @@ private BoundSequence VisitRangeImplicitIndexerAccess( if (rangeExpr.LeftOperandOpt is BoundExpression left) { var startLocal = F.StoreToTemp( - MakePatternIndexOffsetExpression(rangeExpr.LeftOperandOpt, lengthLocal, out usedLength), + MakePatternIndexOffsetExpression(rangeExpr.LeftOperandOpt, loweredLengthAccess, out usedLength), out var startStore); localsBuilder.Add(startLocal.LocalSymbol); @@ -352,22 +402,22 @@ private BoundSequence VisitRangeImplicitIndexerAccess( { endExpr = MakePatternIndexOffsetExpression( right, - lengthLocal, + loweredLengthAccess, out bool usedLengthTemp); usedLength |= usedLengthTemp; } else { usedLength = true; - endExpr = lengthLocal; + endExpr = loweredLengthAccess; } - if (usedLength) + if (usedLength && lengthStore is not null) { // If we used the length, it needs to be calculated after the receiver (the // first bound node in the builder) and before the first use, which could be the // second or third node in the builder - localsBuilder.Insert(1, lengthLocal.LocalSymbol); + localsBuilder.Insert(1, lengthLocal!); sideEffectsBuilder.Insert(1, lengthStore); } @@ -383,8 +433,11 @@ private BoundSequence VisitRangeImplicitIndexerAccess( { var rangeLocal = F.StoreToTemp(VisitExpression(rangeArg), out var rangeStore); - localsBuilder.Add(lengthLocal.LocalSymbol); - sideEffectsBuilder.Add(lengthStore); + if (lengthStore is not null) + { + localsBuilder.Add(lengthLocal!); + sideEffectsBuilder.Add(lengthStore); + } localsBuilder.Add(rangeLocal.LocalSymbol); sideEffectsBuilder.Add(rangeStore); @@ -392,7 +445,7 @@ private BoundSequence VisitRangeImplicitIndexerAccess( F.Call( F.Call(rangeLocal, F.WellKnownMethod(WellKnownMember.System_Range__get_Start)), F.WellKnownMethod(WellKnownMember.System_Index__GetOffset), - lengthLocal), + loweredLengthAccess), out var startStore); localsBuilder.Add(startLocal.LocalSymbol); @@ -404,7 +457,7 @@ private BoundSequence VisitRangeImplicitIndexerAccess( F.Call( F.Call(rangeLocal, F.WellKnownMethod(WellKnownMember.System_Range__get_End)), F.WellKnownMethod(WellKnownMember.System_Index__GetOffset), - lengthLocal), + loweredLengthAccess), startExpr), out var rangeSizeStore); @@ -413,10 +466,19 @@ private BoundSequence VisitRangeImplicitIndexerAccess( rangeSizeExpr = rangeSizeLocal; } + AddPlaceholderReplacement(node.ArgumentPlaceholders[0], startExpr); + AddPlaceholderReplacement(node.ArgumentPlaceholders[1], rangeSizeExpr); + + var rewrittenIndexerAccess = VisitExpression(node.IndexerAccess); + + RemovePlaceholderReplacement(receiverPlaceholder); + RemovePlaceholderReplacement(node.ArgumentPlaceholders[0]); + RemovePlaceholderReplacement(node.ArgumentPlaceholders[1]); + return (BoundSequence)F.Sequence( localsBuilder.ToImmutableAndFree(), sideEffectsBuilder.ToImmutableAndFree(), - F.Call(receiverLocal, sliceMethod, startExpr, rangeSizeExpr)); + rewrittenIndexerAccess); } } } diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index d9a14df50fcf..82922dfba2b3 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -2273,7 +2273,7 @@ private IOperation CreateBoundSlicePatternOperation(BoundSlicePattern boundNode) sliceSymbol: boundNode.Pattern is null ? null : (boundNode.InputType.IsSZArray() ? (Symbol?)_semanticModel.Compilation.CommonGetWellKnownTypeMember(WellKnownMember.System_Runtime_CompilerServices_RuntimeHelpers__GetSubArray_T) - : (Symbol?)boundNode.SliceMethod ?? boundNode.IndexerAccess?.Indexer).GetPublicSymbol(), + : Binder.GetIndexerSymbol(boundNode.IndexerAccess)).GetPublicSymbol(), pattern: (IPatternOperation?)Create(boundNode.Pattern), inputType: boundNode.InputType.GetPublicSymbol(), narrowedType: boundNode.NarrowedType.GetPublicSymbol(), @@ -2285,8 +2285,8 @@ private IOperation CreateBoundSlicePatternOperation(BoundSlicePattern boundNode) private IOperation CreateBoundListPatternOperation(BoundListPattern boundNode) { return new ListPatternOperation( - lengthSymbol: boundNode.LengthProperty.GetPublicSymbol(), - indexerSymbol: (boundNode.IndexerSymbol ?? boundNode.IndexerAccess?.Indexer).GetPublicSymbol(), + lengthSymbol: Binder.GetPropertySymbol(boundNode.LengthAccess, out _, out _).GetPublicSymbol(), + indexerSymbol: Binder.GetIndexerSymbol(boundNode.IndexerAccess).GetPublicSymbol(), patterns: boundNode.Subpatterns.SelectAsArray((p, fac) => (IPatternOperation)fac.Create(p), this), declaredSymbol: boundNode.Variable.GetPublicSymbol(), inputType: boundNode.InputType.GetPublicSymbol(), diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceLocalSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceLocalSymbol.cs index aea0db3944c6..5e2bbf04a9aa 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceLocalSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceLocalSymbol.cs @@ -806,7 +806,7 @@ protected override TypeWithAnnotations InferTypeOfVarVariable(BindingDiagnosticB if (this._type == null) { - Debug.Assert(this.DeclarationKind == LocalDeclarationKind.DeclarationExpressionVariable); + Debug.Assert(this.DeclarationKind is LocalDeclarationKind.DeclarationExpressionVariable); SetTypeWithAnnotations(TypeWithAnnotations.Create(_nodeBinder.CreateErrorType("var"))); } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs index e15b97bac5d8..3b9d7960bc9e 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs @@ -71,16 +71,20 @@ public static void Main() 111 000"; var verifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); + // PROTOTYPE the call to StoreToTemp in LocalRewriter.VisitIndexImplicitIndexerAccess causes more temps to be used AssertEx.Multiple( () => verifier.VerifyIL("X.Test(System.Span)", @" { - // Code size 69 (0x45) - .maxstack 4 + // Code size 83 (0x53) + .maxstack 3 .locals init (char V_0, //first System.Span V_1, //others char V_2, //last System.Span V_3, - int V_4) + int V_4, + System.Span V_5, + int V_6, + int V_7) IL_0000: ldarg.0 IL_0001: stloc.3 IL_0002: ldloca.s V_3 @@ -88,37 +92,45 @@ .locals init (char V_0, //first IL_0009: stloc.s V_4 IL_000b: ldloc.s V_4 IL_000d: ldc.i4.1 - IL_000e: ble.un.s IL_0036 + IL_000e: ble.un.s IL_0044 IL_0010: ldloca.s V_3 IL_0012: ldc.i4.0 IL_0013: call ""ref char System.Span.this[int].get"" IL_0018: ldind.u2 IL_0019: stloc.0 - IL_001a: ldloca.s V_3 - IL_001c: ldc.i4.1 - IL_001d: ldloc.s V_4 - IL_001f: ldc.i4.2 - IL_0020: sub - IL_0021: call ""System.Span System.Span.Slice(int, int)"" - IL_0026: stloc.1 - IL_0027: ldloca.s V_3 - IL_0029: ldloc.s V_4 - IL_002b: ldc.i4.1 - IL_002c: sub - IL_002d: call ""ref char System.Span.this[int].get"" - IL_0032: ldind.u2 - IL_0033: stloc.2 - IL_0034: br.s IL_0038 - IL_0036: ldc.i4.1 - IL_0037: ret - IL_0038: ldloc.0 - IL_0039: ldloc.2 - IL_003a: bne.un.s IL_0043 - IL_003c: ldloc.1 - IL_003d: call ""bool X.Test(System.Span)"" - IL_0042: ret - IL_0043: ldc.i4.0 - IL_0044: ret + IL_001a: ldloc.3 + IL_001b: stloc.s V_5 + IL_001d: ldc.i4.1 + IL_001e: stloc.s V_6 + IL_0020: ldloc.s V_4 + IL_0022: ldc.i4.1 + IL_0023: sub + IL_0024: ldloc.s V_6 + IL_0026: sub + IL_0027: stloc.s V_7 + IL_0029: ldloca.s V_5 + IL_002b: ldloc.s V_6 + IL_002d: ldloc.s V_7 + IL_002f: call ""System.Span System.Span.Slice(int, int)"" + IL_0034: stloc.1 + IL_0035: ldloca.s V_3 + IL_0037: ldloc.s V_4 + IL_0039: ldc.i4.1 + IL_003a: sub + IL_003b: call ""ref char System.Span.this[int].get"" + IL_0040: ldind.u2 + IL_0041: stloc.2 + IL_0042: br.s IL_0046 + IL_0044: ldc.i4.1 + IL_0045: ret + IL_0046: ldloc.0 + IL_0047: ldloc.2 + IL_0048: bne.un.s IL_0051 + IL_004a: ldloc.1 + IL_004b: call ""bool X.Test(System.Span)"" + IL_0050: ret + IL_0051: ldc.i4.0 + IL_0052: ret } "), () => verifier.VerifyIL("X.Test(char[])", @" @@ -146,16 +158,17 @@ .locals init (char V_0, //first IL_0015: stloc.0 IL_0016: ldloc.3 IL_0017: ldc.i4.1 - IL_0018: ldc.i4.0 - IL_0019: newobj ""System.Index..ctor(int, bool)"" + IL_0018: call ""System.Index System.Index.op_Implicit(int)"" + IL_001d: ldc.i4.1 IL_001e: ldc.i4.1 - IL_001f: ldc.i4.1 - IL_0020: newobj ""System.Index..ctor(int, bool)"" - IL_0025: newobj ""System.Range..ctor(System.Index, System.Index)"" - IL_002a: call ""char[] System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray(char[], System.Range)"" - IL_002f: stloc.1 - IL_0030: ldloc.3 - IL_0031: ldloc.s V_4 + IL_001f: newobj ""System.Index..ctor(int, bool)"" + IL_0024: newobj ""System.Range..ctor(System.Index, System.Index)"" + IL_0029: call ""char[] System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray(char[], System.Range)"" + IL_002e: stloc.1 + IL_002f: ldloc.3 + IL_0030: dup + IL_0031: ldlen + IL_0032: conv.i4 IL_0033: ldc.i4.1 IL_0034: sub IL_0035: ldelem.u2 @@ -175,51 +188,59 @@ .locals init (char V_0, //first "), () => verifier.VerifyIL("X.Test(string)", @" { - // Code size 66 (0x42) - .maxstack 4 + // Code size 77 (0x4d) + .maxstack 3 .locals init (char V_0, //first string V_1, //others char V_2, //last string V_3, - int V_4) + int V_4, + int V_5, + int V_6) IL_0000: ldarg.0 IL_0001: stloc.3 IL_0002: ldloc.3 - IL_0003: brfalse.s IL_0040 + IL_0003: brfalse.s IL_004b IL_0005: ldloc.3 IL_0006: callvirt ""int string.Length.get"" IL_000b: stloc.s V_4 IL_000d: ldloc.s V_4 IL_000f: ldc.i4.1 - IL_0010: ble.un.s IL_0033 + IL_0010: ble.un.s IL_003e IL_0012: ldloc.3 IL_0013: ldc.i4.0 IL_0014: callvirt ""char string.this[int].get"" IL_0019: stloc.0 IL_001a: ldloc.3 IL_001b: ldc.i4.1 - IL_001c: ldloc.s V_4 - IL_001e: ldc.i4.2 - IL_001f: sub - IL_0020: callvirt ""string string.Substring(int, int)"" - IL_0025: stloc.1 - IL_0026: ldloc.3 - IL_0027: ldloc.s V_4 - IL_0029: ldc.i4.1 - IL_002a: sub - IL_002b: callvirt ""char string.this[int].get"" - IL_0030: stloc.2 - IL_0031: br.s IL_0035 - IL_0033: ldc.i4.1 - IL_0034: ret - IL_0035: ldloc.0 - IL_0036: ldloc.2 - IL_0037: bne.un.s IL_0040 - IL_0039: ldloc.1 - IL_003a: call ""bool X.Test(string)"" + IL_001c: stloc.s V_5 + IL_001e: ldloc.s V_4 + IL_0020: ldc.i4.1 + IL_0021: sub + IL_0022: ldloc.s V_5 + IL_0024: sub + IL_0025: stloc.s V_6 + IL_0027: ldloc.s V_5 + IL_0029: ldloc.s V_6 + IL_002b: callvirt ""string string.Substring(int, int)"" + IL_0030: stloc.1 + IL_0031: ldloc.3 + IL_0032: ldloc.s V_4 + IL_0034: ldc.i4.1 + IL_0035: sub + IL_0036: callvirt ""char string.this[int].get"" + IL_003b: stloc.2 + IL_003c: br.s IL_0040 + IL_003e: ldc.i4.1 IL_003f: ret - IL_0040: ldc.i4.0 - IL_0041: ret + IL_0040: ldloc.0 + IL_0041: ldloc.2 + IL_0042: bne.un.s IL_004b + IL_0044: ldloc.1 + IL_0045: call ""bool X.Test(string)"" + IL_004a: ret + IL_004b: ldc.i4.0 + IL_004c: ret } ") ); @@ -253,7 +274,7 @@ class C Diagnostic(ErrorCode.ERR_MisplacedSlicePattern, ".. var y").WithLocation(4, 16) ); - compilation = CreateCompilationWithIndex(source, parseOptions: TestOptions.RegularNext); + compilation = CreateCompilationWithIndexAndRange(source, parseOptions: TestOptions.RegularNext); compilation.VerifyDiagnostics( // (4,16): error CS9002: Slice patterns may only be used once and directly inside a list pattern. // _ = new C() is .. var y; @@ -330,97 +351,94 @@ public static void Main() "; var verifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); + // PROTOTYPE the call to StoreToTemp in LocalRewriter.VisitIndexImplicitIndexerAccess causes more temps to be used AssertEx.Multiple( () => verifier.VerifyIL("X.Test1", @" { - // Code size 31 (0x1f) - .maxstack 3 + // Code size 30 (0x1e) + .maxstack 2 IL_0000: ldarg.0 - IL_0001: brfalse.s IL_001d + IL_0001: brfalse.s IL_001c IL_0003: ldarg.0 IL_0004: callvirt ""int Test1.Length.get"" IL_0009: ldc.i4.1 - IL_000a: bne.un.s IL_001d + IL_000a: bne.un.s IL_001c IL_000c: ldarg.0 IL_000d: ldc.i4.0 - IL_000e: ldc.i4.0 - IL_000f: newobj ""System.Index..ctor(int, bool)"" - IL_0014: callvirt ""int Test1.this[System.Index].get"" - IL_0019: ldc.i4.1 - IL_001a: ceq - IL_001c: ret - IL_001d: ldc.i4.0 - IL_001e: ret + IL_000e: call ""System.Index System.Index.op_Implicit(int)"" + IL_0013: callvirt ""int Test1.this[System.Index].get"" + IL_0018: ldc.i4.1 + IL_0019: ceq + IL_001b: ret + IL_001c: ldc.i4.0 + IL_001d: ret }"), () => verifier.VerifyIL("X.Test2", @" { - // Code size 32 (0x20) + // Code size 31 (0x1f) .maxstack 3 IL_0000: ldarg.0 - IL_0001: brfalse.s IL_001e + IL_0001: brfalse.s IL_001d IL_0003: ldarg.0 IL_0004: callvirt ""int Test2.Length.get"" IL_0009: ldc.i4.1 - IL_000a: bne.un.s IL_001e + IL_000a: bne.un.s IL_001d IL_000c: ldarg.0 IL_000d: ldc.i4.0 - IL_000e: ldc.i4.0 - IL_000f: newobj ""System.Index..ctor(int, bool)"" - IL_0014: ldc.i4.5 - IL_0015: callvirt ""int Test2.this[System.Index, int].get"" - IL_001a: ldc.i4.1 - IL_001b: ceq - IL_001d: ret - IL_001e: ldc.i4.0 - IL_001f: ret + IL_000e: call ""System.Index System.Index.op_Implicit(int)"" + IL_0013: ldc.i4.5 + IL_0014: callvirt ""int Test2.this[System.Index, int].get"" + IL_0019: ldc.i4.1 + IL_001a: ceq + IL_001c: ret + IL_001d: ldc.i4.0 + IL_001e: ret }"), () => verifier.VerifyIL("X.Test3", @" { - // Code size 36 (0x24) + // Code size 35 (0x23) .maxstack 3 IL_0000: ldarg.0 - IL_0001: brfalse.s IL_0022 + IL_0001: brfalse.s IL_0021 IL_0003: ldarg.0 IL_0004: callvirt ""int Test3.Length.get"" IL_0009: ldc.i4.1 - IL_000a: bne.un.s IL_0022 + IL_000a: bne.un.s IL_0021 IL_000c: ldarg.0 IL_000d: ldc.i4.0 - IL_000e: ldc.i4.0 - IL_000f: newobj ""System.Index..ctor(int, bool)"" - IL_0014: call ""int[] System.Array.Empty()"" - IL_0019: callvirt ""int Test3.this[System.Index, params int[]].get"" - IL_001e: ldc.i4.1 - IL_001f: ceq - IL_0021: ret - IL_0022: ldc.i4.0 - IL_0023: ret + IL_000e: call ""System.Index System.Index.op_Implicit(int)"" + IL_0013: call ""int[] System.Array.Empty()"" + IL_0018: callvirt ""int Test3.this[System.Index, params int[]].get"" + IL_001d: ldc.i4.1 + IL_001e: ceq + IL_0020: ret + IL_0021: ldc.i4.0 + IL_0022: ret }"), () => verifier.VerifyIL("X.Test4", @" { - // Code size 44 (0x2c) - .maxstack 6 + // Code size 43 (0x2b) + .maxstack 5 IL_0000: ldarg.0 - IL_0001: brfalse.s IL_002a + IL_0001: brfalse.s IL_0029 IL_0003: ldarg.0 IL_0004: callvirt ""int Test4.Length.get"" IL_0009: ldc.i4.1 - IL_000a: bne.un.s IL_002a + IL_000a: bne.un.s IL_0029 IL_000c: ldarg.0 IL_000d: ldc.i4.1 IL_000e: newarr ""System.Index"" IL_0013: dup IL_0014: ldc.i4.0 IL_0015: ldc.i4.0 - IL_0016: ldc.i4.0 - IL_0017: newobj ""System.Index..ctor(int, bool)"" - IL_001c: stelem ""System.Index"" - IL_0021: callvirt ""int Test4.this[params System.Index[]].get"" - IL_0026: ldc.i4.1 - IL_0027: ceq - IL_0029: ret - IL_002a: ldc.i4.0 - IL_002b: ret + IL_0016: call ""System.Index System.Index.op_Implicit(int)"" + IL_001b: stelem ""System.Index"" + IL_0020: callvirt ""int Test4.this[params System.Index[]].get"" + IL_0025: ldc.i4.1 + IL_0026: ceq + IL_0028: ret + IL_0029: ldc.i4.0 + IL_002a: ret }"), () => verifier.VerifyIL("X.Test5", @" { @@ -517,87 +535,85 @@ public static void Main() True "; var verifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); + // PROTOTYPE the call to StoreToTemp in LocalRewriter.VisitIndexImplicitIndexerAccess causes more temps to be used AssertEx.Multiple( () => verifier.VerifyIL("X.Test1", @" { - // Code size 41 (0x29) + // Code size 40 (0x28) .maxstack 4 IL_0000: ldarg.0 - IL_0001: brfalse.s IL_0027 + IL_0001: brfalse.s IL_0026 IL_0003: ldarg.0 IL_0004: callvirt ""int Test1.Count.get"" IL_0009: pop IL_000a: ldarg.0 IL_000b: ldc.i4.0 - IL_000c: ldc.i4.0 - IL_000d: newobj ""System.Index..ctor(int, bool)"" - IL_0012: ldc.i4.0 - IL_0013: ldc.i4.1 - IL_0014: newobj ""System.Index..ctor(int, bool)"" - IL_0019: newobj ""System.Range..ctor(System.Index, System.Index)"" - IL_001e: callvirt ""int Test1.this[System.Range].get"" - IL_0023: ldc.i4.1 - IL_0024: ceq - IL_0026: ret - IL_0027: ldc.i4.0 - IL_0028: ret + IL_000c: call ""System.Index System.Index.op_Implicit(int)"" + IL_0011: ldc.i4.0 + IL_0012: ldc.i4.1 + IL_0013: newobj ""System.Index..ctor(int, bool)"" + IL_0018: newobj ""System.Range..ctor(System.Index, System.Index)"" + IL_001d: callvirt ""int Test1.this[System.Range].get"" + IL_0022: ldc.i4.1 + IL_0023: ceq + IL_0025: ret + IL_0026: ldc.i4.0 + IL_0027: ret }"), () => verifier.VerifyIL("X.Test2", @" { - // Code size 42 (0x2a) + // Code size 41 (0x29) .maxstack 4 IL_0000: ldarg.0 - IL_0001: brfalse.s IL_0028 + IL_0001: brfalse.s IL_0027 IL_0003: ldarg.0 IL_0004: callvirt ""int Test2.Count.get"" IL_0009: pop IL_000a: ldarg.0 IL_000b: ldc.i4.0 - IL_000c: ldc.i4.0 - IL_000d: newobj ""System.Index..ctor(int, bool)"" - IL_0012: ldc.i4.0 - IL_0013: ldc.i4.1 - IL_0014: newobj ""System.Index..ctor(int, bool)"" - IL_0019: newobj ""System.Range..ctor(System.Index, System.Index)"" - IL_001e: ldc.i4.5 - IL_001f: callvirt ""int Test2.this[System.Range, int].get"" - IL_0024: ldc.i4.1 - IL_0025: ceq - IL_0027: ret - IL_0028: ldc.i4.0 - IL_0029: ret + IL_000c: call ""System.Index System.Index.op_Implicit(int)"" + IL_0011: ldc.i4.0 + IL_0012: ldc.i4.1 + IL_0013: newobj ""System.Index..ctor(int, bool)"" + IL_0018: newobj ""System.Range..ctor(System.Index, System.Index)"" + IL_001d: ldc.i4.5 + IL_001e: callvirt ""int Test2.this[System.Range, int].get"" + IL_0023: ldc.i4.1 + IL_0024: ceq + IL_0026: ret + IL_0027: ldc.i4.0 + IL_0028: ret }"), () => verifier.VerifyIL("X.Test3", @" { - // Code size 46 (0x2e) + // Code size 45 (0x2d) .maxstack 4 IL_0000: ldarg.0 - IL_0001: brfalse.s IL_002c + IL_0001: brfalse.s IL_002b IL_0003: ldarg.0 IL_0004: callvirt ""int Test3.Count.get"" IL_0009: pop IL_000a: ldarg.0 IL_000b: ldc.i4.0 - IL_000c: ldc.i4.0 - IL_000d: newobj ""System.Index..ctor(int, bool)"" - IL_0012: ldc.i4.0 - IL_0013: ldc.i4.1 - IL_0014: newobj ""System.Index..ctor(int, bool)"" - IL_0019: newobj ""System.Range..ctor(System.Index, System.Index)"" - IL_001e: call ""int[] System.Array.Empty()"" - IL_0023: callvirt ""int Test3.this[System.Range, params int[]].get"" - IL_0028: ldc.i4.1 - IL_0029: ceq - IL_002b: ret - IL_002c: ldc.i4.0 - IL_002d: ret + IL_000c: call ""System.Index System.Index.op_Implicit(int)"" + IL_0011: ldc.i4.0 + IL_0012: ldc.i4.1 + IL_0013: newobj ""System.Index..ctor(int, bool)"" + IL_0018: newobj ""System.Range..ctor(System.Index, System.Index)"" + IL_001d: call ""int[] System.Array.Empty()"" + IL_0022: callvirt ""int Test3.this[System.Range, params int[]].get"" + IL_0027: ldc.i4.1 + IL_0028: ceq + IL_002a: ret + IL_002b: ldc.i4.0 + IL_002c: ret }"), () => verifier.VerifyIL("X.Test4", @" { - // Code size 54 (0x36) + // Code size 53 (0x35) .maxstack 7 IL_0000: ldarg.0 - IL_0001: brfalse.s IL_0034 + IL_0001: brfalse.s IL_0033 IL_0003: ldarg.0 IL_0004: callvirt ""int Test4.Count.get"" IL_0009: pop @@ -607,41 +623,46 @@ .maxstack 7 IL_0011: dup IL_0012: ldc.i4.0 IL_0013: ldc.i4.0 - IL_0014: ldc.i4.0 - IL_0015: newobj ""System.Index..ctor(int, bool)"" - IL_001a: ldc.i4.0 - IL_001b: ldc.i4.1 - IL_001c: newobj ""System.Index..ctor(int, bool)"" - IL_0021: newobj ""System.Range..ctor(System.Index, System.Index)"" - IL_0026: stelem ""System.Range"" - IL_002b: callvirt ""int Test4.this[params System.Range[]].get"" - IL_0030: ldc.i4.1 - IL_0031: ceq - IL_0033: ret - IL_0034: ldc.i4.0 - IL_0035: ret + IL_0014: call ""System.Index System.Index.op_Implicit(int)"" + IL_0019: ldc.i4.0 + IL_001a: ldc.i4.1 + IL_001b: newobj ""System.Index..ctor(int, bool)"" + IL_0020: newobj ""System.Range..ctor(System.Index, System.Index)"" + IL_0025: stelem ""System.Range"" + IL_002a: callvirt ""int Test4.this[params System.Range[]].get"" + IL_002f: ldc.i4.1 + IL_0030: ceq + IL_0032: ret + IL_0033: ldc.i4.0 + IL_0034: ret }"), () => verifier.VerifyIL("X.Test5", @" { - // Code size 26 (0x1a) - .maxstack 4 - .locals init (int V_0) + // Code size 30 (0x1e) + .maxstack 3 + .locals init (Test5 V_0, + int V_1, + int V_2) IL_0000: ldarg.0 - IL_0001: brfalse.s IL_0018 + IL_0001: brfalse.s IL_001c IL_0003: ldarg.0 IL_0004: callvirt ""int Test5.Count.get"" - IL_0009: stloc.0 - IL_000a: ldarg.0 + IL_0009: ldarg.0 + IL_000a: stloc.0 IL_000b: ldc.i4.0 - IL_000c: ldloc.0 - IL_000d: ldc.i4.0 + IL_000c: stloc.1 + IL_000d: ldloc.1 IL_000e: sub - IL_000f: callvirt ""int Test5.Slice(int, int)"" - IL_0014: ldc.i4.1 - IL_0015: ceq - IL_0017: ret - IL_0018: ldc.i4.0 - IL_0019: ret + IL_000f: stloc.2 + IL_0010: ldloc.0 + IL_0011: ldloc.1 + IL_0012: ldloc.2 + IL_0013: callvirt ""int Test5.Slice(int, int)"" + IL_0018: ldc.i4.1 + IL_0019: ceq + IL_001b: ret + IL_001c: ldc.i4.0 + IL_001d: ret }") ); } @@ -719,11 +740,14 @@ void M(object o) "; var expectedDiagnostics = new[] { - // error CS9000: List patterns may not be used for a value of type 'object'. - subpattern != "" ? Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, listPattern).WithArguments("object") : null, - // error CS9001: Slice patterns may not be used for a value of type 'object'. - subpattern == ".._" ? Diagnostic(ErrorCode.ERR_UnsupportedTypeForSlicePattern, subpattern).WithArguments("object") : null - }; + // (6,18): error CS9000: List patterns may not be used for a value of type 'object'. + Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, listPattern).WithArguments("object"), + // error CS0021: Cannot apply indexing with [] to an expression of type 'object' + Diagnostic(ErrorCode.ERR_BadIndexLHS, listPattern).WithArguments("object"), + + // error CS0021: Cannot apply indexing with [] to an expression of type 'object' + subpattern == ".._" ? Diagnostic(ErrorCode.ERR_BadIndexLHS, subpattern).WithArguments("object") : null + }; var compilation = CreateCompilationWithIndexAndRange(source, parseOptions: TestOptions.RegularWithListPatterns); compilation.VerifyEmitDiagnostics(expectedDiagnostics.WhereNotNull().ToArray()); } @@ -811,9 +835,9 @@ public void M(string s) var compilation = CreateCompilationWithIndexAndRange(source, parseOptions: TestOptions.RegularWithListPatterns); compilation.MakeMemberMissing(SpecialMember.System_String__Substring); compilation.VerifyEmitDiagnostics( - // (6,19): error CS9001: Slice patterns may not be used for a value of type 'string'. + // (6,19): error CS1503: Argument 1: cannot convert from 'System.Range' to 'int' // _ = s is [.. var slice]; - Diagnostic(ErrorCode.ERR_UnsupportedTypeForSlicePattern, ".. var slice").WithArguments("string").WithLocation(6, 19), + Diagnostic(ErrorCode.ERR_BadArgType, ".. var slice").WithArguments("1", "System.Range", "int").WithLocation(6, 19), // (7,15): error CS1503: Argument 1: cannot convert from 'System.Range' to 'int' // _ = s[..]; Diagnostic(ErrorCode.ERR_BadArgType, "..").WithArguments("1", "System.Range", "int").WithLocation(7, 15) @@ -829,14 +853,18 @@ class X public void M(int[] a) { _ = a is [.. var slice]; + _ = a[..]; } } "; var compilation = CreateCompilationWithIndexAndRange(source, parseOptions: TestOptions.RegularWithListPatterns); compilation.VerifyEmitDiagnostics( - // (6,22): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray' + // (6,19): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray' // _ = a is [.. var slice]; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "var slice").WithArguments("System.Runtime.CompilerServices.RuntimeHelpers", "GetSubArray").WithLocation(6, 22) + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, ".. var slice").WithArguments("System.Runtime.CompilerServices.RuntimeHelpers", "GetSubArray").WithLocation(6, 19), + // (7,15): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray' + // _ = a[..]; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "..").WithArguments("System.Runtime.CompilerServices.RuntimeHelpers", "GetSubArray").WithLocation(7, 15) ); } @@ -853,12 +881,15 @@ public void M(int[] a) } } "; - // PROTOTYPE(list-patterns) Missing diagnostic on `.. var slice`; (this is strange as the test above works) var compilation = CreateCompilationWithIndexAndRange(source, parseOptions: TestOptions.RegularWithListPatterns); compilation.VerifyEmitDiagnostics( + // (6,19): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray' + // _ = a is [.. var slice]; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, ".. var slice").WithArguments("System.Runtime.CompilerServices.RuntimeHelpers", "GetSubArray").WithLocation(6, 19), // (7,15): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray' // _ = a[..]; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "..").WithArguments("System.Runtime.CompilerServices.RuntimeHelpers", "GetSubArray").WithLocation(7, 15)); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "..").WithArguments("System.Runtime.CompilerServices.RuntimeHelpers", "GetSubArray").WithLocation(7, 15) + ); } [Theory] @@ -889,16 +920,16 @@ public static void Main() var isCountable = hasLengthProp || hasCountProp; var isSliceable = implicitRange || explicitRange; var isIndexable = implicitIndex || explicitIndex; - // TODO2 + // TODO2 messy var expectedDiagnostics = new[] { - // (13,24): error CS9000: List patterns may not be used for a value of type 'X'. - // _ = new X() is [.._]; - !isIndexable || !isCountable ? Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[.._]").WithArguments("X").WithLocation(13, 24) : null, - // (13,25): error CS9001: Slice patterns may not be used for a value of type 'X'. - // _ = new X() is [.._]; - !isSliceable || !isCountable ? Diagnostic(ErrorCode.ERR_UnsupportedTypeForSlicePattern, ".._").WithArguments("X").WithLocation(13, 25) : null - }; + // (13,24): error CS0021: Cannot apply indexing with [] to an expression of type 'X' + // _ = new X() is [.._]; + !isIndexable || !isCountable ? Diagnostic(ErrorCode.ERR_BadIndexLHS, "[.._]").WithArguments("X").WithLocation(13, 24) : null, + // (13,25): error CS0021: Cannot apply indexing with [] to an expression of type 'X' + // _ = new X() is [.._]; + !isSliceable || !isCountable ? Diagnostic(ErrorCode.ERR_BadIndexLHS, ".._").WithArguments("X").WithLocation(13, 25) : null + }; compilation.VerifyEmitDiagnostics(expectedDiagnostics.WhereNotNull().ToArray()); } @@ -938,24 +969,30 @@ public void M() "; var compilation = CreateCompilationWithIndexAndRange(source, parseOptions: TestOptions.RegularWithListPatterns); compilation.VerifyEmitDiagnostics( - // (25,28): error CS0619: 'Test1.this[int]' is obsolete: 'error2' + // (25,28): error CS0619: 'Test1.Count' is obsolete: 'error3' // _ = new Test1() is [0]; - Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[0]").WithArguments("Test1.this[int]", "error2").WithLocation(25, 28), + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[0]").WithArguments("Test1.Count", "error3").WithLocation(25, 28), // (25,28): error CS0619: 'Test1.Count' is obsolete: 'error3' // _ = new Test1() is [0]; Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[0]").WithArguments("Test1.Count", "error3").WithLocation(25, 28), - // (26,28): error CS0619: 'Test1.this[int]' is obsolete: 'error2' + // (25,28): error CS0619: 'Test1.this[int]' is obsolete: 'error2' + // _ = new Test1() is [0]; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[0]").WithArguments("Test1.this[int]", "error2").WithLocation(25, 28), + // (26,28): error CS0619: 'Test1.Count' is obsolete: 'error3' // _ = new Test1() is [..0]; - Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[..0]").WithArguments("Test1.this[int]", "error2").WithLocation(26, 28), + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[..0]").WithArguments("Test1.Count", "error3").WithLocation(26, 28), // (26,28): error CS0619: 'Test1.Count' is obsolete: 'error3' // _ = new Test1() is [..0]; Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[..0]").WithArguments("Test1.Count", "error3").WithLocation(26, 28), - // (26,29): error CS0619: 'Test1.Slice(int, int)' is obsolete: 'error1' + // (26,28): error CS0619: 'Test1.this[int]' is obsolete: 'error2' // _ = new Test1() is [..0]; - Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "..0").WithArguments("Test1.Slice(int, int)", "error1").WithLocation(26, 29), + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[..0]").WithArguments("Test1.this[int]", "error2").WithLocation(26, 28), // (26,29): error CS0619: 'Test1.Count' is obsolete: 'error3' // _ = new Test1() is [..0]; Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "..0").WithArguments("Test1.Count", "error3").WithLocation(26, 29), + // (26,29): error CS0619: 'Test1.Slice(int, int)' is obsolete: 'error1' + // _ = new Test1() is [..0]; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "..0").WithArguments("Test1.Slice(int, int)", "error1").WithLocation(26, 29), // (27,28): error CS0619: 'Test2.Length' is obsolete: 'error6' // _ = new Test2() is [0]; Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[0]").WithArguments("Test2.Length", "error6").WithLocation(27, 28), @@ -968,9 +1005,6 @@ public void M() // (28,28): error CS0619: 'Test2.this[Index]' is obsolete: 'error4' // _ = new Test2() is [..0]; Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[..0]").WithArguments("Test2.this[System.Index]", "error4").WithLocation(28, 28), - // (28,29): error CS0619: 'Test2.Length' is obsolete: 'error6' - // _ = new Test2() is [..0]; - Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "..0").WithArguments("Test2.Length", "error6").WithLocation(28, 29), // (28,29): error CS0619: 'Test2.this[Range]' is obsolete: 'error5' // _ = new Test2() is [..0]; Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "..0").WithArguments("Test2.this[System.Range]", "error5").WithLocation(28, 29) @@ -1020,14 +1054,24 @@ class X public void M() { _ = new Test1() is [0]; + _ = new Test1()[^1]; + _ = new Test1() is [..0]; + _ = new Test1()[..0]; + _ = new Test2() is [0]; + _ = new Test2()[^1]; + _ = new Test2() is [..0]; + _ = new Test2()[..0]; } } "; var compilation = CreateCompilationWithIndexAndRange(source); compilation.VerifyEmitDiagnostics( + // (40,28): error CS0619: 'Test1.Count.get' is obsolete: 'error2' + // _ = new Test1() is [0]; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[0]").WithArguments("Test1.Count.get", "error2").WithLocation(40, 28), // (40,28): error CS0619: 'Test1.Count.get' is obsolete: 'error2' // _ = new Test1() is [0]; Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[0]").WithArguments("Test1.Count.get", "error2").WithLocation(40, 28), @@ -1035,38 +1079,60 @@ public void M() // _ = new Test1() is [0]; Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[0]").WithArguments("Test1.this[int].get", "error1").WithLocation(40, 28), - // (41,28): error CS0619: 'Test1.Count.get' is obsolete: 'error2' + // (41,13): error CS0619: 'Test1.Count.get' is obsolete: 'error2' + // _ = new Test1()[^1]; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "new Test1()[^1]").WithArguments("Test1.Count.get", "error2").WithLocation(41, 13), + // (41,13): error CS0619: 'Test1.this[int].get' is obsolete: 'error1' + // _ = new Test1()[^1]; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "new Test1()[^1]").WithArguments("Test1.this[int].get", "error1").WithLocation(41, 13), + + // (43,28): error CS0619: 'Test1.Count.get' is obsolete: 'error2' // _ = new Test1() is [..0]; - Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[..0]").WithArguments("Test1.Count.get", "error2").WithLocation(41, 28), - // (41,28): error CS0619: 'Test1.this[int].get' is obsolete: 'error1' + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[..0]").WithArguments("Test1.Count.get", "error2").WithLocation(43, 28), + // (43,28): error CS0619: 'Test1.Count.get' is obsolete: 'error2' // _ = new Test1() is [..0]; - Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[..0]").WithArguments("Test1.this[int].get", "error1").WithLocation(41, 28), - // (41,29): error CS0619: 'Test1.Slice(int, int)' is obsolete: 'error1' + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[..0]").WithArguments("Test1.Count.get", "error2").WithLocation(43, 28), + // (43,28): error CS0619: 'Test1.this[int].get' is obsolete: 'error1' // _ = new Test1() is [..0]; - Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "..0").WithArguments("Test1.Slice(int, int)", "error1").WithLocation(41, 29), - // (41,29): error CS0619: 'Test1.Count.get' is obsolete: 'error2' + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[..0]").WithArguments("Test1.this[int].get", "error1").WithLocation(43, 28), + // (43,29): error CS0619: 'Test1.Slice(int, int)' is obsolete: 'error1' // _ = new Test1() is [..0]; - Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "..0").WithArguments("Test1.Count.get", "error2").WithLocation(41, 29), + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "..0").WithArguments("Test1.Slice(int, int)", "error1").WithLocation(43, 29), + // (43,29): error CS0619: 'Test1.Count.get' is obsolete: 'error2' + // _ = new Test1() is [..0]; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "..0").WithArguments("Test1.Count.get", "error2").WithLocation(43, 29), + + // (44,13): error CS0619: 'Test1.Slice(int, int)' is obsolete: 'error1' + // _ = new Test1()[..0]; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "new Test1()[..0]").WithArguments("Test1.Slice(int, int)", "error1").WithLocation(44, 13), + // (44,13): error CS0619: 'Test1.Count.get' is obsolete: 'error2' + // _ = new Test1()[..0]; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "new Test1()[..0]").WithArguments("Test1.Count.get", "error2").WithLocation(44, 13), - // (42,28): error CS0619: 'Test2.Length.get' is obsolete: 'error5' + // (46,28): error CS0619: 'Test2.Length.get' is obsolete: 'error5' // _ = new Test2() is [0]; - Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[0]").WithArguments("Test2.Length.get", "error5").WithLocation(42, 28), - // (42,28): error CS0619: 'Test2.this[Index].get' is obsolete: 'error3' + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[0]").WithArguments("Test2.Length.get", "error5").WithLocation(46, 28), + // (46,28): error CS0619: 'Test2.this[Index].get' is obsolete: 'error3' // _ = new Test2() is [0]; - Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[0]").WithArguments("Test2.this[System.Index].get", "error3").WithLocation(42, 28), + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[0]").WithArguments("Test2.this[System.Index].get", "error3").WithLocation(46, 28), - // (43,28): error CS0619: 'Test2.Length.get' is obsolete: 'error5' - // _ = new Test2() is [..0]; - Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[..0]").WithArguments("Test2.Length.get", "error5").WithLocation(43, 28), - // (43,28): error CS0619: 'Test2.this[Index].get' is obsolete: 'error3' + // (47,13): error CS0619: 'Test2.this[Index].get' is obsolete: 'error3' + // _ = new Test2()[^1]; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "new Test2()[^1]").WithArguments("Test2.this[System.Index].get", "error3").WithLocation(47, 13), + + // (49,28): error CS0619: 'Test2.Length.get' is obsolete: 'error5' // _ = new Test2() is [..0]; - Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[..0]").WithArguments("Test2.this[System.Index].get", "error3").WithLocation(43, 28), - // (43,29): error CS0619: 'Test2.Length.get' is obsolete: 'error5' + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[..0]").WithArguments("Test2.Length.get", "error5").WithLocation(49, 28), + // (49,28): error CS0619: 'Test2.this[Index].get' is obsolete: 'error3' // _ = new Test2() is [..0]; - Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "..0").WithArguments("Test2.Length.get", "error5").WithLocation(43, 29), - // (43,29): error CS0619: 'Test2.this[Range].get' is obsolete: 'error4' + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[..0]").WithArguments("Test2.this[System.Index].get", "error3").WithLocation(49, 28), + // (49,29): error CS0619: 'Test2.this[Range].get' is obsolete: 'error4' // _ = new Test2() is [..0]; - Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "..0").WithArguments("Test2.this[System.Range].get", "error4").WithLocation(43, 29) + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "..0").WithArguments("Test2.this[System.Range].get", "error4").WithLocation(49, 29), + + // (50,13): error CS0619: 'Test2.this[Range].get' is obsolete: 'error4' + // _ = new Test2()[..0]; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "new Test2()[..0]").WithArguments("Test2.this[System.Range].get", "error4").WithLocation(50, 13) ); } @@ -1159,6 +1225,9 @@ public void M() "; var compilation = CreateCompilationWithIndexAndRange(source); compilation.VerifyEmitDiagnostics( + // (73,28): error CS0619: 'Base1.Count.get' is obsolete: 'error3' + // _ = new Test1() is [0]; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[0]").WithArguments("Base1.Count.get", "error3").WithLocation(73, 28), // (73,28): error CS0619: 'Base1.Count.get' is obsolete: 'error3' // _ = new Test1() is [0]; Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[0]").WithArguments("Base1.Count.get", "error3").WithLocation(73, 28), @@ -1166,18 +1235,21 @@ public void M() // _ = new Test1() is [0]; Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[0]").WithArguments("Base1.this[int].get", "error2").WithLocation(73, 28), + // (74,28): error CS0619: 'Base1.Count.get' is obsolete: 'error3' + // _ = new Test1() is [..0]; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[..0]").WithArguments("Base1.Count.get", "error3").WithLocation(74, 28), // (74,28): error CS0619: 'Base1.Count.get' is obsolete: 'error3' // _ = new Test1() is [..0]; Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[..0]").WithArguments("Base1.Count.get", "error3").WithLocation(74, 28), // (74,28): error CS0619: 'Base1.this[int].get' is obsolete: 'error2' // _ = new Test1() is [..0]; Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[..0]").WithArguments("Base1.this[int].get", "error2").WithLocation(74, 28), - // (74,29): error CS0619: 'Base1.Slice(int, int)' is obsolete: 'error1' - // _ = new Test1() is [..0]; - Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "..0").WithArguments("Base1.Slice(int, int)", "error1").WithLocation(74, 29), // (74,29): error CS0619: 'Base1.Count.get' is obsolete: 'error3' // _ = new Test1() is [..0]; Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "..0").WithArguments("Base1.Count.get", "error3").WithLocation(74, 29), + // (74,29): error CS0619: 'Base1.Slice(int, int)' is obsolete: 'error1' + // _ = new Test1() is [..0]; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "..0").WithArguments("Base1.Slice(int, int)", "error1").WithLocation(74, 29), // (75,28): error CS0619: 'Base2.Length.get' is obsolete: 'error6' // _ = new Test2() is [0]; @@ -1192,9 +1264,6 @@ public void M() // (76,28): error CS0619: 'Base2.this[Index].get' is obsolete: 'error4' // _ = new Test2() is [..0]; Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[..0]").WithArguments("Base2.this[System.Index].get", "error4").WithLocation(76, 28), - // (76,29): error CS0619: 'Base2.Length.get' is obsolete: 'error6' - // _ = new Test2() is [..0]; - Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "..0").WithArguments("Base2.Length.get", "error6").WithLocation(76, 29), // (76,29): error CS0619: 'Base2.this[Range].get' is obsolete: 'error5' // _ = new Test2() is [..0]; Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "..0").WithArguments("Base2.this[System.Range].get", "error5").WithLocation(76, 29), @@ -1206,12 +1275,12 @@ public void M() // _ = new Test1()[^1]; Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "new Test1()[^1]").WithArguments("Base1.this[int].get", "error2").WithLocation(78, 13), - // (79,13): error CS0619: 'Base1.Slice(int, int)' is obsolete: 'error1' - // _ = new Test1()[..0]; - Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "new Test1()[..0]").WithArguments("Base1.Slice(int, int)", "error1").WithLocation(79, 13), // (79,13): error CS0619: 'Base1.Count.get' is obsolete: 'error3' // _ = new Test1()[..0]; Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "new Test1()[..0]").WithArguments("Base1.Count.get", "error3").WithLocation(79, 13), + // (79,13): error CS0619: 'Base1.Slice(int, int)' is obsolete: 'error1' + // _ = new Test1()[..0]; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "new Test1()[..0]").WithArguments("Base1.Slice(int, int)", "error1").WithLocation(79, 13), // (80,13): error CS0619: 'Base2.this[Index].get' is obsolete: 'error4' // _ = new Test2()[^1]; @@ -1287,6 +1356,7 @@ class X public static void Main() { _ = new Test1() is [0]; + _ = new Test1()[0]; } } "; @@ -1294,9 +1364,12 @@ public static void Main() var csCompilation = CreateCompilation(csSource, parseOptions: TestOptions.RegularWithListPatterns, references: new[] { vbCompilation.EmitToImageReference() }); // PROTOTYPE(list-patterns) Unsupported because the lookup fails not that the indexer is static csCompilation.VerifyEmitDiagnostics( - // (6,28): error CS9000: List patterns may not be used for a value of type 'Test1'. + // (6,28): error CS0021: Cannot apply indexing with [] to an expression of type 'Test1' // _ = new Test1() is [0]; - Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[0]").WithArguments("Test1").WithLocation(6, 28)); + Diagnostic(ErrorCode.ERR_BadIndexLHS, "[0]").WithArguments("Test1").WithLocation(6, 28), + // (7,13): error CS0021: Cannot apply indexing with [] to an expression of type 'Test1' + // _ = new Test1()[0]; + Diagnostic(ErrorCode.ERR_BadIndexLHS, "new Test1()[0]").WithArguments("Test1").WithLocation(7, 13)); } [Theory] @@ -1450,32 +1523,6 @@ public static void Main() CompileAndVerify(compilation, expectedOutput: "True"); } - [Fact] - public void ListPattern_MemberLookup_Fallback_MissingIndexOrRange() - { - var source = @" -using System; -class Test1 -{ - public int this[int i] => 1; - public int Slice(int i, int j) => 2; - public int Count => 1; -} -class X -{ - public static void Main() - { - Console.WriteLine(new Test1() is [1, ..2]); - } -} -"; - var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularWithListPatterns, options: TestOptions.ReleaseExe); - Assert.Null(compilation.GetTypeByMetadataName("System.Index")); - Assert.Null(compilation.GetTypeByMetadataName("System.Range")); - compilation.VerifyEmitDiagnostics(); - CompileAndVerify(compilation, expectedOutput: "True"); - } - [Fact] public void ListPattern_RefReturns() { @@ -1501,41 +1548,39 @@ public static void Main() var verifier = CompileAndVerify(compilation, expectedOutput: "True"); verifier.VerifyIL("X.Main", @" { - // Code size 73 (0x49) + // Code size 71 (0x47) .maxstack 4 .locals init (Test1 V_0) IL_0000: newobj ""Test1..ctor()"" IL_0005: stloc.0 IL_0006: ldloc.0 - IL_0007: brfalse.s IL_0042 + IL_0007: brfalse.s IL_0040 IL_0009: ldloc.0 IL_000a: callvirt ""int Test1.Count.get"" IL_000f: ldc.i4.1 - IL_0010: bne.un.s IL_0042 + IL_0010: bne.un.s IL_0040 IL_0012: ldloc.0 IL_0013: ldc.i4.0 - IL_0014: ldc.i4.0 - IL_0015: newobj ""System.Index..ctor(int, bool)"" - IL_001a: callvirt ""ref int Test1.this[System.Index].get"" - IL_001f: ldind.i4 - IL_0020: ldc.i4.1 - IL_0021: bne.un.s IL_0042 - IL_0023: ldloc.0 - IL_0024: ldc.i4.0 - IL_0025: ldc.i4.0 - IL_0026: newobj ""System.Index..ctor(int, bool)"" - IL_002b: ldc.i4.0 - IL_002c: ldc.i4.1 - IL_002d: newobj ""System.Index..ctor(int, bool)"" - IL_0032: newobj ""System.Range..ctor(System.Index, System.Index)"" - IL_0037: callvirt ""ref int Test1.this[System.Range].get"" - IL_003c: ldind.i4 - IL_003d: ldc.i4.1 - IL_003e: ceq - IL_0040: br.s IL_0043 - IL_0042: ldc.i4.0 - IL_0043: call ""void System.Console.WriteLine(bool)"" - IL_0048: ret + IL_0014: call ""System.Index System.Index.op_Implicit(int)"" + IL_0019: callvirt ""ref int Test1.this[System.Index].get"" + IL_001e: ldind.i4 + IL_001f: ldc.i4.1 + IL_0020: bne.un.s IL_0040 + IL_0022: ldloc.0 + IL_0023: ldc.i4.0 + IL_0024: call ""System.Index System.Index.op_Implicit(int)"" + IL_0029: ldc.i4.0 + IL_002a: ldc.i4.1 + IL_002b: newobj ""System.Index..ctor(int, bool)"" + IL_0030: newobj ""System.Range..ctor(System.Index, System.Index)"" + IL_0035: callvirt ""ref int Test1.this[System.Range].get"" + IL_003a: ldind.i4 + IL_003b: ldc.i4.1 + IL_003c: ceq + IL_003e: br.s IL_0041 + IL_0040: ldc.i4.0 + IL_0041: call ""void System.Console.WriteLine(bool)"" + IL_0046: ret }"); } @@ -1760,7 +1805,7 @@ public static void Main() } } "; - var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularWithListPatterns, options: TestOptions.ReleaseExe); + var compilation = CreateCompilation(new[] { source, TestSources.Index }, options: TestOptions.ReleaseExe); compilation.VerifyEmitDiagnostics(); CompileAndVerify(compilation, expectedOutput: "123"); } @@ -1804,7 +1849,7 @@ public void M(int[] a, int[,] mdarray) } } "; - var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularWithListPatterns); + var compilation = CreateCompilation(new[] { source, TestSources.Index, TestSources.Range, TestSources.GetSubArray }); compilation.VerifyEmitDiagnostics( // (7,13): error CS8518: An expression of type 'int[]' can never match the provided pattern. // _ = a is [] and [1]; // 1 @@ -1872,58 +1917,69 @@ public static void Test1(T t) where T : ICountableViaCount { _ = t is { Count: -1 }; _ = new { t } is { t.Count: -1 }; + _ = t[^1]; // 1 } public static void Test2(T t) where T : ICountableViaLength { _ = t is { Length: -1 }; _ = new { t } is { t.Length: -1 }; + _ = t[^1]; // 2 } public static void Test3(T t) where T : IIndexable, ICountableViaCount { - _ = t is { Count: -1 }; // 1 - _ = new { t } is { t.Count: -1 }; // 2 + _ = t is { Count: -1 }; // 3 + _ = new { t } is { t.Count: -1 }; // 4 + _ = t[^1]; } public static void Test4(T t) where T : IIndexable, ICountableViaLength { - _ = t is { Length: -1 }; // 3 - _ = new { t } is { t.Length: -1 }; // 4 + _ = t is { Length: -1 }; // 5 + _ = new { t } is { t.Length: -1 }; // 6 + _ = t[^1]; } public static void Test5(T t) where T : IIndexable, ICountableViaLength, ICountableViaCount { - _ = t is { Length: -1 }; // 5 + _ = t is { Length: -1 }; // 7 _ = t is { Count: -1 }; - _ = new { t } is { t.Length: -1 }; // 6 + _ = new { t } is { t.Length: -1 }; // 8 _ = new { t } is { t.Count: -1 }; + _ = t[^1]; } } "; - var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularWithListPatterns); + var compilation = CreateCompilation(new[] { source, TestSources.Index, TestSources.Range, TestSources.GetSubArray }); compilation.VerifyEmitDiagnostics( - // (28,13): error CS8518: An expression of type 'T' can never match the provided pattern. - // _ = t is { Count: -1 }; // 1 - Diagnostic(ErrorCode.ERR_IsPatternImpossible, "t is { Count: -1 }").WithArguments("T").WithLocation(28, 13), - // (29,13): error CS8518: An expression of type '' can never match the provided pattern. - // _ = new { t } is { t.Count: -1 }; // 2 - Diagnostic(ErrorCode.ERR_IsPatternImpossible, "new { t } is { t.Count: -1 }").WithArguments("").WithLocation(29, 13), - // (33,13): error CS8518: An expression of type 'T' can never match the provided pattern. - // _ = t is { Length: -1 }; // 3 - Diagnostic(ErrorCode.ERR_IsPatternImpossible, "t is { Length: -1 }").WithArguments("T").WithLocation(33, 13), - // (34,13): error CS8518: An expression of type '' can never match the provided pattern. - // _ = new { t } is { t.Length: -1 }; // 4 - Diagnostic(ErrorCode.ERR_IsPatternImpossible, "new { t } is { t.Length: -1 }").WithArguments("").WithLocation(34, 13), - // (38,13): error CS8518: An expression of type 'T' can never match the provided pattern. + // (20,13): error CS0021: Cannot apply indexing with [] to an expression of type 'T' + // _ = t[^1]; // 1 + Diagnostic(ErrorCode.ERR_BadIndexLHS, "t[^1]").WithArguments("T").WithLocation(20, 13), + // (26,13): error CS0021: Cannot apply indexing with [] to an expression of type 'T' + // _ = t[^1]; // 2 + Diagnostic(ErrorCode.ERR_BadIndexLHS, "t[^1]").WithArguments("T").WithLocation(26, 13), + // (30,13): error CS8518: An expression of type 'T' can never match the provided pattern. + // _ = t is { Count: -1 }; // 3 + Diagnostic(ErrorCode.ERR_IsPatternImpossible, "t is { Count: -1 }").WithArguments("T").WithLocation(30, 13), + // (31,13): error CS8518: An expression of type '' can never match the provided pattern. + // _ = new { t } is { t.Count: -1 }; // 4 + Diagnostic(ErrorCode.ERR_IsPatternImpossible, "new { t } is { t.Count: -1 }").WithArguments("").WithLocation(31, 13), + // (36,13): error CS8518: An expression of type 'T' can never match the provided pattern. // _ = t is { Length: -1 }; // 5 - Diagnostic(ErrorCode.ERR_IsPatternImpossible, "t is { Length: -1 }").WithArguments("T").WithLocation(38, 13), - // (40,13): error CS8518: An expression of type '' can never match the provided pattern. + Diagnostic(ErrorCode.ERR_IsPatternImpossible, "t is { Length: -1 }").WithArguments("T").WithLocation(36, 13), + // (37,13): error CS8518: An expression of type '' can never match the provided pattern. // _ = new { t } is { t.Length: -1 }; // 6 - Diagnostic(ErrorCode.ERR_IsPatternImpossible, "new { t } is { t.Length: -1 }").WithArguments("").WithLocation(40, 13) + Diagnostic(ErrorCode.ERR_IsPatternImpossible, "new { t } is { t.Length: -1 }").WithArguments("").WithLocation(37, 13), + // (42,13): error CS8518: An expression of type 'T' can never match the provided pattern. + // _ = t is { Length: -1 }; // 7 + Diagnostic(ErrorCode.ERR_IsPatternImpossible, "t is { Length: -1 }").WithArguments("T").WithLocation(42, 13), + // (44,13): error CS8518: An expression of type '' can never match the provided pattern. + // _ = new { t } is { t.Length: -1 }; // 8 + Diagnostic(ErrorCode.ERR_IsPatternImpossible, "new { t } is { t.Length: -1 }").WithArguments("").WithLocation(44, 13) ); } [Fact] public void ListPattern_ValEscape() { - CreateCompilationWithMscorlibAndSpan(@" + CreateCompilationWithIndexAndRangeAndSpan(@" using System; public ref struct R { @@ -1999,7 +2055,7 @@ public void M(int[] a) } } "; - var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularWithListPatterns); + var compilation = CreateCompilation(new[] { source, TestSources.Index, TestSources.Range, TestSources.GetSubArray }); compilation.VerifyEmitDiagnostics( // (7,25): error CS0029: Cannot implicitly convert type 'int[]' to 'int' // const int bad = a; @@ -2115,7 +2171,7 @@ public void Test1(int[] a) } } "; - var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularWithListPatterns); + var compilation = CreateCompilation(new[] { source, TestSources.Index, TestSources.Range, TestSources.GetSubArray }); compilation.VerifyEmitDiagnostics( // (8,26): error CS8780: A variable may not be declared within a 'not' or 'or' pattern. // case not [{} y, .. {} z] x: _ = (x, y, z); break; @@ -2175,7 +2231,7 @@ public void Test2(int[] a) } } "; - var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularWithListPatterns); + var compilation = CreateCompilation(new[] { source, TestSources.Index, TestSources.Range, TestSources.GetSubArray }); compilation.VerifyEmitDiagnostics( // (7,19): error CS0165: Use of unassigned local variable 'x' // _ = (x, y, z); // 1 @@ -2420,11 +2476,11 @@ public class C public void ListPattern_ObsoleteLengthAndIndexerAndSlice() { var source = @" -_ = new C() is [var x]; // 1, 2 -_ = new C() is [.. var y]; // 3, 4, 5, 6 -new C().Slice(0, 0); // 7 -_ = new C()[^1]; // 8, 9 -_ = new C()[..]; // 10, 11 +_ = new C() is [var x]; // 1, 2, 3 +_ = new C() is [.. var y]; // 4, 5, 6, 7, 8 +new C().Slice(0, 0); // 9 +_ = new C()[^1]; // 10, 11 +_ = new C()[..]; // 12, 13 class C { @@ -2439,39 +2495,47 @@ class C } "; var comp = CreateCompilation(new[] { source, TestSources.Index, TestSources.Range }); + // Note: duplicate diagnostics are reported on Length because both the list pattern + // and the implicit indexer need it. comp.VerifyDiagnostics( // (2,16): warning CS0612: 'C.Length' is obsolete - // _ = new C() is [var x]; // 1, 2 + // _ = new C() is [var x]; // 1, 2, 3 + Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "[var x]").WithArguments("C.Length").WithLocation(2, 16), + // (2,16): warning CS0612: 'C.Length' is obsolete + // _ = new C() is [var x]; // 1, 2, 3 Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "[var x]").WithArguments("C.Length").WithLocation(2, 16), // (2,16): warning CS0612: 'C.this[int]' is obsolete - // _ = new C() is [var x]; // 1, 2 + // _ = new C() is [var x]; // 1, 2, 3 Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "[var x]").WithArguments("C.this[int]").WithLocation(2, 16), // (3,16): warning CS0612: 'C.Length' is obsolete - // _ = new C() is [.. var y]; // 3, 4, 5, 6 + // _ = new C() is [.. var y]; // 4, 5, 6, 7, 8 + Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "[.. var y]").WithArguments("C.Length").WithLocation(3, 16), + // (3,16): warning CS0612: 'C.Length' is obsolete + // _ = new C() is [.. var y]; // 4, 5, 6, 7, 8 Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "[.. var y]").WithArguments("C.Length").WithLocation(3, 16), // (3,16): warning CS0612: 'C.this[int]' is obsolete - // _ = new C() is [.. var y]; // 3, 4, 5, 6 + // _ = new C() is [.. var y]; // 4, 5, 6, 7, 8 Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "[.. var y]").WithArguments("C.this[int]").WithLocation(3, 16), // (3,17): warning CS0612: 'C.Length' is obsolete - // _ = new C() is [.. var y]; // 3, 4, 5, 6 + // _ = new C() is [.. var y]; // 4, 5, 6, 7, 8 Diagnostic(ErrorCode.WRN_DeprecatedSymbol, ".. var y").WithArguments("C.Length").WithLocation(3, 17), // (3,17): warning CS0612: 'C.Slice(int, int)' is obsolete - // _ = new C() is [.. var y]; // 3, 4, 5, 6 + // _ = new C() is [.. var y]; // 4, 5, 6, 7, 8 Diagnostic(ErrorCode.WRN_DeprecatedSymbol, ".. var y").WithArguments("C.Slice(int, int)").WithLocation(3, 17), // (4,1): warning CS0612: 'C.Slice(int, int)' is obsolete - // new C().Slice(0, 0); // 7 + // new C().Slice(0, 0); // 9 Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "new C().Slice(0, 0)").WithArguments("C.Slice(int, int)").WithLocation(4, 1), // (5,5): warning CS0612: 'C.Length' is obsolete - // _ = new C()[^1]; // 8, 9 + // _ = new C()[^1]; // 10, 11 Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "new C()[^1]").WithArguments("C.Length").WithLocation(5, 5), // (5,5): warning CS0612: 'C.this[int]' is obsolete - // _ = new C()[^1]; // 8, 9 + // _ = new C()[^1]; // 10, 11 Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "new C()[^1]").WithArguments("C.this[int]").WithLocation(5, 5), // (6,5): warning CS0612: 'C.Length' is obsolete - // _ = new C()[..]; // 10, 11 + // _ = new C()[..]; // 12, 13 Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "new C()[..]").WithArguments("C.Length").WithLocation(6, 5), // (6,5): warning CS0612: 'C.Slice(int, int)' is obsolete - // _ = new C()[..]; // 10, 11 + // _ = new C()[..]; // 12, 13 Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "new C()[..]").WithArguments("C.Slice(int, int)").WithLocation(6, 5) ); } @@ -2481,6 +2545,7 @@ public void ListPattern_IndexAndSliceReturnMissingTypes() { var missing_cs = @" public class Missing { } +public class Missing2 { } "; var lib_cs = @" @@ -2488,16 +2553,16 @@ public class C { public int Length => throw null; public Missing this[int i] => throw null; - public Missing Slice(int i, int j) => throw null; + public Missing2 Slice(int i, int j) => throw null; } "; var source = @" -_ = new C() is [var x]; // 1 -_ = new C() is [.. var y]; // 2, 3 -new C().Slice(0, 0); // 4 -_ = new C()[^1]; // 5 -_ = new C()[..]; // 6 +_ = new C() is [var x]; // 1, 2 +_ = new C() is [.. var y]; // 3, 4, 5, 6 +new C().Slice(0, 0); // 7 +_ = new C()[^1]; // 8, 9 +_ = new C()[..]; // 10, 11 "; var missingComp = CreateCompilation(missing_cs, assemblyName: "missing"); missingComp.VerifyDiagnostics(); @@ -2508,38 +2573,38 @@ public class C var comp = CreateCompilation(new[] { source, TestSources.Index, TestSources.Range }, references: new[] { libComp.EmitToImageReference() }); comp.VerifyDiagnostics( // (2,16): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. - // _ = new C() is [var x]; // 1 + // _ = new C() is [var x]; // 1, 2 Diagnostic(ErrorCode.ERR_NoTypeDef, "[var x]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(2, 16), // (2,16): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. - // _ = new C() is [var x]; // 1 + // _ = new C() is [var x]; // 1, 2 Diagnostic(ErrorCode.ERR_NoTypeDef, "[var x]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(2, 16), // (3,16): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. - // _ = new C() is [.. var y]; // 2, 3 + // _ = new C() is [.. var y]; // 3, 4, 5, 6 Diagnostic(ErrorCode.ERR_NoTypeDef, "[.. var y]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(3, 16), // (3,16): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. - // _ = new C() is [.. var y]; // 2, 3 + // _ = new C() is [.. var y]; // 3, 4, 5, 6 Diagnostic(ErrorCode.ERR_NoTypeDef, "[.. var y]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(3, 16), // (3,17): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. - // _ = new C() is [.. var y]; // 2, 3 - Diagnostic(ErrorCode.ERR_NoTypeDef, ".. var y").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(3, 17), - // (3,17): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. - // _ = new C() is [.. var y]; // 2, 3 + // _ = new C() is [.. var y]; // 3, 4, 5, 6 Diagnostic(ErrorCode.ERR_NoTypeDef, ".. var y").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(3, 17), - // (4,1): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. - // new C().Slice(0, 0); // 4 - Diagnostic(ErrorCode.ERR_NoTypeDef, "new C().Slice").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(4, 1), + // (3,17): error CS0012: The type 'Missing2' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // _ = new C() is [.. var y]; // 3, 4, 5, 6 + Diagnostic(ErrorCode.ERR_NoTypeDef, ".. var y").WithArguments("Missing2", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(3, 17), + // (4,1): error CS0012: The type 'Missing2' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // new C().Slice(0, 0); // 7 + Diagnostic(ErrorCode.ERR_NoTypeDef, "new C().Slice").WithArguments("Missing2", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(4, 1), // (5,5): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. - // _ = new C()[^1]; // 5 + // _ = new C()[^1]; // 8, 9 Diagnostic(ErrorCode.ERR_NoTypeDef, "new C()[^1]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(5, 5), // (5,5): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. - // _ = new C()[^1]; // 5 + // _ = new C()[^1]; // 8, 9 Diagnostic(ErrorCode.ERR_NoTypeDef, "new C()[^1]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(5, 5), // (6,5): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. - // _ = new C()[..]; // 6 + // _ = new C()[..]; // 10, 11 Diagnostic(ErrorCode.ERR_NoTypeDef, "new C()[..]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(6, 5), - // (6,5): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. - // _ = new C()[..]; // 6 - Diagnostic(ErrorCode.ERR_NoTypeDef, "new C()[..]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(6, 5) + // (6,5): error CS0012: The type 'Missing2' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // _ = new C()[..]; // 10, 11 + Diagnostic(ErrorCode.ERR_NoTypeDef, "new C()[..]").WithArguments("Missing2", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(6, 5) ); } @@ -2565,7 +2630,7 @@ public void Test(string[]? strings, int[] integers) _ = integers is [..int[] slice4] list4b; } }"; - var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularWithListPatterns); + var compilation = CreateCompilation(new[] { source, TestSources.Index, TestSources.Range, TestSources.GetSubArray }); compilation.VerifyDiagnostics(); var tree = compilation.SyntaxTrees[0]; var nodes = tree.GetRoot().DescendantNodes().OfType(); @@ -2622,7 +2687,7 @@ public void Test(string[] strings, int[] integers) _ = integers is [..{}]; } }"; - var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularWithListPatterns); + var compilation = CreateCompilation(new[] { source, TestSources.Index, TestSources.Range, TestSources.GetSubArray }); compilation.VerifyDiagnostics(); var tree = compilation.SyntaxTrees[0]; var nodes = tree.GetRoot().DescendantNodes() @@ -2667,20 +2732,9 @@ readonly void M(Index i, Range r) _ = this is [2, ..var rest]; } }"; - var comp = CreateCompilationWithIndexAndRange(src, parseOptions: TestOptions.RegularWithListPatterns); - comp.VerifyDiagnostics( - // (11,13): warning CS8656: Call to non-readonly member 'S.Length.get' from a 'readonly' member results in an implicit copy of 'this'. - // _ = this[i]; // 1, 2 - Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S.Length.get", "this").WithLocation(11, 13), - // (11,13): warning CS8656: Call to non-readonly member 'S.this[int].get' from a 'readonly' member results in an implicit copy of 'this'. - // _ = this[i]; // 1, 2 - Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S.this[int].get", "this").WithLocation(11, 13), - // (12,13): warning CS8656: Call to non-readonly member 'S.Length.get' from a 'readonly' member results in an implicit copy of 'this'. - // _ = this[r]; // 3, 4 - Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S.Length.get", "this").WithLocation(12, 13), - // (12,13): warning CS8656: Call to non-readonly member 'S.Slice(int, int)' from a 'readonly' member results in an implicit copy of 'this'. - // _ = this[r]; // 3, 4 - Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S.Slice(int, int)", "this").WithLocation(12, 13)); + // Note: no "warning CS8656: Call to non-readonly member ... from a 'readonly' member results in an implicit copy of 'this'" + var comp = CreateCompilationWithIndexAndRange(src); + comp.VerifyDiagnostics(); } [Fact] @@ -2694,17 +2748,24 @@ class C public void M() { _ = this is [1]; + _ = this[^1]; } } "; - var compilation = CreateCompilation(source); + var compilation = CreateCompilation(new[] { source, TestSources.Index }); compilation.VerifyEmitDiagnostics( // (4,17): error CS0547: 'C.Length': property or indexer cannot have void type // public void Length => throw null; Diagnostic(ErrorCode.ERR_PropertyCantHaveVoidType, "Length").WithArguments("C.Length").WithLocation(4, 17), // (8,21): error CS9000: List patterns may not be used for a value of type 'C'. // _ = this is [1]; - Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[1]").WithArguments("C").WithLocation(8, 21) + Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[1]").WithArguments("C").WithLocation(8, 21), + // (8,21): error CS1503: Argument 1: cannot convert from 'System.Index' to 'int' + // _ = this is [1]; + Diagnostic(ErrorCode.ERR_BadArgType, "[1]").WithArguments("1", "System.Index", "int").WithLocation(8, 21), + // (9,18): error CS1503: Argument 1: cannot convert from 'System.Index' to 'int' + // _ = this[^1]; + Diagnostic(ErrorCode.ERR_BadArgType, "^1").WithArguments("1", "System.Index", "int").WithLocation(9, 18) ); } @@ -2724,17 +2785,17 @@ public void M() } } "; - var compilation = CreateCompilation(source); + var compilation = CreateCompilation(new[] { source, TestSources.Index }); compilation.VerifyEmitDiagnostics( // (9,21): error CS9000: List patterns may not be used for a value of type 'C'. // _ = this is [1]; Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[1]").WithArguments("C").WithLocation(9, 21), - // (10,18): error CS0518: Predefined type 'System.Index' is not defined or imported - // _ = this[^1]; - Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "^1").WithArguments("System.Index").WithLocation(10, 18), - // (10,18): error CS0656: Missing compiler required member 'System.Index..ctor' + // (9,21): error CS1503: Argument 1: cannot convert from 'System.Index' to 'int' + // _ = this is [1]; + Diagnostic(ErrorCode.ERR_BadArgType, "[1]").WithArguments("1", "System.Index", "int").WithLocation(9, 21), + // (10,18): error CS1503: Argument 1: cannot convert from 'System.Index' to 'int' // _ = this[^1]; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "^1").WithArguments("System.Index", ".ctor").WithLocation(10, 18) + Diagnostic(ErrorCode.ERR_BadArgType, "^1").WithArguments("1", "System.Index", "int").WithLocation(10, 18) ); } @@ -2757,20 +2818,24 @@ public void M() { System.Console.Write(used); } + _ = this[..]; } } "; - var compilation = CreateCompilation(source); + var compilation = CreateCompilation(new[] { source, TestSources.Index, TestSources.Range }); compilation.VerifyEmitDiagnostics( - // (11,22): error CS9001: Slice patterns may not be used for a value of type 'C'. + // (11,22): error CS1503: Argument 1: cannot convert from 'System.Range' to 'int' // _ = this is [.._]; - Diagnostic(ErrorCode.ERR_UnsupportedTypeForSlicePattern, ".._").WithArguments("C").WithLocation(11, 22), - // (12,22): error CS9001: Slice patterns may not be used for a value of type 'C'. + Diagnostic(ErrorCode.ERR_BadArgType, ".._").WithArguments("1", "System.Range", "int").WithLocation(11, 22), + // (12,22): error CS1503: Argument 1: cannot convert from 'System.Range' to 'int' // _ = this is [..var unused]; - Diagnostic(ErrorCode.ERR_UnsupportedTypeForSlicePattern, "..var unused").WithArguments("C").WithLocation(12, 22), - // (13,22): error CS9001: Slice patterns may not be used for a value of type 'C'. + Diagnostic(ErrorCode.ERR_BadArgType, "..var unused").WithArguments("1", "System.Range", "int").WithLocation(12, 22), + // (13,22): error CS1503: Argument 1: cannot convert from 'System.Range' to 'int' // if (this is [..var used]) - Diagnostic(ErrorCode.ERR_UnsupportedTypeForSlicePattern, "..var used").WithArguments("C").WithLocation(13, 22) + Diagnostic(ErrorCode.ERR_BadArgType, "..var used").WithArguments("1", "System.Range", "int").WithLocation(13, 22), + // (17,18): error CS1503: Argument 1: cannot convert from 'System.Range' to 'int' + // _ = this[..]; + Diagnostic(ErrorCode.ERR_BadArgType, "..").WithArguments("1", "System.Range", "int").WithLocation(17, 18) ); } @@ -2793,7 +2858,7 @@ public class C public int Slice(int i, int j) {{ System.Console.Write(""Slice ""); return 0; }} }} "; - var compilation = CreateCompilation(source); + var compilation = CreateCompilation(new[] { source, TestSources.Index, TestSources.Range }); compilation.VerifyDiagnostics(); CompileAndVerify(compilation, expectedOutput: expectedOutput); } @@ -2812,11 +2877,11 @@ public void M() { if (new C() is [var item]) // 1 item.ToString(); - _ = new C()[^1]; // 2 - if (new C() is [var item2]) // 3 - item2.Value.ToString(); - _ = new C()[^1]; // 4 + if (new C() is [var item2]) // 2 + item2.Value.ToString(); // 3 + var item22 = new C()[^1]; // 4 + item22.Value.ToString(); // 5 if (new C() is [var item3]) item3.ToString(); @@ -2830,18 +2895,21 @@ public void M() "; var compilation = CreateCompilation(new[] { source, TestSources.Index }); compilation.VerifyEmitDiagnostics( - // (10,29): error CS9000: List patterns may not be used for a value of type 'C'. + // (10,29): error CS1503: Argument 1: cannot convert from 'System.Index' to 'int' // if (new C() is [var item]) // 1 - Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[var item]").WithArguments("C").WithLocation(10, 29), - // (12,26): error CS1503: Argument 1: cannot convert from 'System.Index' to 'int' - // _ = new C()[^1]; // 2 - Diagnostic(ErrorCode.ERR_BadArgType, "^1").WithArguments("1", "System.Index", "int").WithLocation(12, 26), - // (14,30): error CS9000: List patterns may not be used for a value of type 'C'. - // if (new C() is [var item2]) // 3 - Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[var item2]").WithArguments("C").WithLocation(14, 30), - // (16,27): error CS1503: Argument 1: cannot convert from 'System.Index' to 'int?' - // _ = new C()[^1]; // 4 - Diagnostic(ErrorCode.ERR_BadArgType, "^1").WithArguments("1", "System.Index", "int?").WithLocation(16, 27) + Diagnostic(ErrorCode.ERR_BadArgType, "[var item]").WithArguments("1", "System.Index", "int").WithLocation(10, 29), + // (13,30): error CS1503: Argument 1: cannot convert from 'System.Index' to 'int?' + // if (new C() is [var item2]) // 2 + Diagnostic(ErrorCode.ERR_BadArgType, "[var item2]").WithArguments("1", "System.Index", "int?").WithLocation(13, 30), + // (14,19): error CS1061: 'int' does not contain a definition for 'Value' and no accessible extension method 'Value' accepting a first argument of type 'int' could be found (are you missing a using directive or an assembly reference?) + // item2.Value.ToString(); // 3 + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "Value").WithArguments("int", "Value").WithLocation(14, 19), + // (15,36): error CS1503: Argument 1: cannot convert from 'System.Index' to 'int?' + // var item22 = new C()[^1]; // 4 + Diagnostic(ErrorCode.ERR_BadArgType, "^1").WithArguments("1", "System.Index", "int?").WithLocation(15, 36), + // (16,16): error CS1061: 'int' does not contain a definition for 'Value' and no accessible extension method 'Value' accepting a first argument of type 'int' could be found (are you missing a using directive or an assembly reference?) + // item22.Value.ToString(); // 5 + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "Value").WithArguments("int", "Value").WithLocation(16, 16) ); } @@ -2882,7 +2950,7 @@ public void M() } } "; - var compilation = CreateCompilation(source); + var compilation = CreateCompilation(new[] { source, TestSources.Index }); compilation.VerifyEmitDiagnostics( // (13,13): error CS0165: Use of unassigned local variable 'item' // item.ToString(); // 1 @@ -2913,7 +2981,7 @@ public void M() Diagnostic(ErrorCode.ERR_UseDefViolation, "item4").WithArguments("item4").WithLocation(31, 13) ); - var tree = compilation.SyntaxTrees.Single(); + var tree = compilation.SyntaxTrees.First(); var model = compilation.GetSemanticModel(tree, ignoreAccessibility: false); var declarations = tree.GetRoot().DescendantNodes().OfType().ToArray(); @@ -3052,7 +3120,7 @@ public void M() } } "; - var compilation = CreateCompilation(source); + var compilation = CreateCompilation(new[] { source, TestSources.Index }); compilation.VerifyEmitDiagnostics( // (10,13): error CS0165: Use of unassigned local variable 'item' // item.ToString(); // 1 @@ -3077,7 +3145,7 @@ public void M() Diagnostic(ErrorCode.ERR_UseDefViolation, "item4").WithArguments("item4").WithLocation(28, 13) ); - var tree = compilation.SyntaxTrees.Single(); + var tree = compilation.SyntaxTrees.First(); var model = compilation.GetSemanticModel(tree, ignoreAccessibility: false); var declarations = tree.GetRoot().DescendantNodes().OfType().ToArray(); @@ -3095,6 +3163,33 @@ void verify(VarPatternSyntax declaration, string name, string expectedType) } } + [Fact] + public void ListPattern_Nullability_MaybeNullReceiver() + { + var source = @" +#nullable enable +class C +{ + public int Length => throw null!; + public T this[int i] => throw null!; + + public void M(C? c) + { + if (c is [var item]) + item.ToString(); + + _ = c[^1]; + } +} +"; + var compilation = CreateCompilation(new[] { source, TestSources.Index }); + compilation.VerifyEmitDiagnostics( + // (13,13): warning CS8602: Dereference of a possibly null reference. + // _ = c[^1]; + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c").WithLocation(13, 13) + ); + } + [Fact] public void SlicePattern_Nullability() { @@ -3139,7 +3234,7 @@ public void M() } } "; - var compilation = CreateCompilation(source); + var compilation = CreateCompilation(new[] { source, TestSources.Index, TestSources.Range }); compilation.VerifyEmitDiagnostics( // (14,13): error CS0165: Use of unassigned local variable 'rest' // rest.ToString(); // 1 @@ -3173,7 +3268,7 @@ public void M() Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "rest5").WithLocation(36, 13) ); - var tree = compilation.SyntaxTrees.Single(); + var tree = compilation.SyntaxTrees.First(); var model = compilation.GetSemanticModel(tree, ignoreAccessibility: false); var declarations = tree.GetRoot().DescendantNodes().OfType().ToArray(); @@ -3297,7 +3392,7 @@ public void M() } } "; - var compilation = CreateCompilation(source); + var compilation = CreateCompilation(new[] { source, TestSources.Index, TestSources.Range, TestSources.GetSubArray }); compilation.VerifyEmitDiagnostics( // (13,13): warning CS8602: Dereference of a possibly null reference. // rest.ToString(); // 1, 2 @@ -3325,7 +3420,7 @@ public void M() Diagnostic(ErrorCode.ERR_UseDefViolation, "rest4").WithArguments("rest4").WithLocation(28, 13) ); - var tree = compilation.SyntaxTrees.Single(); + var tree = compilation.SyntaxTrees.First(); var model = compilation.GetSemanticModel(tree, ignoreAccessibility: false); var declarations = tree.GetRoot().DescendantNodes().OfType().ToArray(); @@ -3343,6 +3438,34 @@ void verify(VarPatternSyntax declaration, string name, string expectedType) } } + [Fact] + public void SlicePattern_Nullability_MaybeNullReceiver() + { + var source = @" +#nullable enable +class C +{ + public int Length => throw null!; + public T this[int i] => throw null!; + public T Slice(int i, int j) => throw null!; + + public void M(C? c) + { + if (c is [.. var item]) + item.ToString(); + + _ = c[..]; + } +} +"; + var compilation = CreateCompilation(new[] { source, TestSources.Index, TestSources.Range }); + compilation.VerifyEmitDiagnostics( + // (14,13): warning CS8602: Dereference of a possibly null reference. + // _ = c[..]; + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c").WithLocation(14, 13) + ); + } + [Fact] public void SlicePattern_DefiniteAssignment() { @@ -3368,7 +3491,7 @@ public void M() } } "; - var compilation = CreateCompilation(source); + var compilation = CreateCompilation(new[] { source, TestSources.Index, TestSources.Range }); compilation.VerifyEmitDiagnostics( // (17,13): error CS0165: Use of unassigned local variable 'item' // item.ToString(); // 1 @@ -3429,17 +3552,28 @@ class D public void M() { _ = new C() is [var item, ..var rest]; + _ = new C()[^1]; + _ = new C()[..]; } } "; - var compilation = CreateCompilationWithIL(source, il); + var compilation = CreateCompilationWithIL(new[] { source, TestSources.Index, TestSources.Range }, il); compilation.VerifyEmitDiagnostics( // (6,24): error CS9000: List patterns may not be used for a value of type 'C'. // _ = new C() is [var item, ..var rest]; Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[var item, ..var rest]").WithArguments("C").WithLocation(6, 24), - // (6,35): error CS9001: Slice patterns may not be used for a value of type 'C'. + // (6,24): error CS0021: Cannot apply indexing with [] to an expression of type 'C' // _ = new C() is [var item, ..var rest]; - Diagnostic(ErrorCode.ERR_UnsupportedTypeForSlicePattern, "..var rest").WithArguments("C").WithLocation(6, 35) + Diagnostic(ErrorCode.ERR_BadIndexLHS, "[var item, ..var rest]").WithArguments("C").WithLocation(6, 24), + // (6,35): error CS0021: Cannot apply indexing with [] to an expression of type 'C' + // _ = new C() is [var item, ..var rest]; + Diagnostic(ErrorCode.ERR_BadIndexLHS, "..var rest").WithArguments("C").WithLocation(6, 35), + // (7,13): error CS0021: Cannot apply indexing with [] to an expression of type 'C' + // _ = new C()[^1]; + Diagnostic(ErrorCode.ERR_BadIndexLHS, "new C()[^1]").WithArguments("C").WithLocation(7, 13), + // (8,13): error CS0021: Cannot apply indexing with [] to an expression of type 'C' + // _ = new C()[..]; + Diagnostic(ErrorCode.ERR_BadIndexLHS, "new C()[..]").WithArguments("C").WithLocation(8, 13) ); } @@ -3655,9 +3789,12 @@ public void M() // (6,24): error CS9000: List patterns may not be used for a value of type 'C'. // _ = new C() is [var item, ..var rest]; Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[var item, ..var rest]").WithArguments("C").WithLocation(6, 24), - // (6,35): error CS9001: Slice patterns may not be used for a value of type 'C'. + // (6,24): error CS0021: Cannot apply indexing with [] to an expression of type 'C' + // _ = new C() is [var item, ..var rest]; + Diagnostic(ErrorCode.ERR_BadIndexLHS, "[var item, ..var rest]").WithArguments("C").WithLocation(6, 24), + // (6,35): error CS0021: Cannot apply indexing with [] to an expression of type 'C' // _ = new C() is [var item, ..var rest]; - Diagnostic(ErrorCode.ERR_UnsupportedTypeForSlicePattern, "..var rest").WithArguments("C").WithLocation(6, 35) + Diagnostic(ErrorCode.ERR_BadIndexLHS, "..var rest").WithArguments("C").WithLocation(6, 35) ); } @@ -4065,7 +4202,7 @@ void M(dynamic d) } } "; - var compilation = CreateCompilation(source); + var compilation = CreateCompilation(new[] { source, TestSources.Index }); compilation.VerifyEmitDiagnostics( // (6,18): error CS9000: List patterns may not be used for a value of type 'dynamic'. // _ = d is [_]; @@ -4108,18 +4245,17 @@ void M(C c) } } "; - // TODO2 probably should be reporting a better error (the use-site error) var compilation = CreateCompilation(source, references: new[] { lib2Ref }); compilation.VerifyEmitDiagnostics( - // (6,18): error CS9000: List patterns may not be used for a value of type 'C'. + // (6,18): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // _ = c is [var item]; - Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[var item]").WithArguments("C").WithLocation(6, 18), - // (7,18): error CS9000: List patterns may not be used for a value of type 'C'. + Diagnostic(ErrorCode.ERR_NoTypeDef, "[var item]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(6, 18), + // (7,18): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // _ = c is [..var rest]; - Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[..var rest]").WithArguments("C").WithLocation(7, 18), - // (7,19): error CS9001: Slice patterns may not be used for a value of type 'C'. + Diagnostic(ErrorCode.ERR_NoTypeDef, "[..var rest]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(7, 18), + // (7,19): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // _ = c is [..var rest]; - Diagnostic(ErrorCode.ERR_UnsupportedTypeForSlicePattern, "..var rest").WithArguments("C").WithLocation(7, 19), + Diagnostic(ErrorCode.ERR_NoTypeDef, "..var rest").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(7, 19), // (8,21): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // var index = c[^1]; Diagnostic(ErrorCode.ERR_NoTypeDef, "c[^1]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(8, 21), @@ -4147,38 +4283,23 @@ void M() } } "; - var compilation = CreateCompilation(source); + var compilation = CreateCompilation(new[] { source, TestSources.Index, TestSources.Range }); compilation.VerifyEmitDiagnostics( // (5,21): error CS0631: ref and out are not valid in this context // public int this[ref int i] => 0; Diagnostic(ErrorCode.ERR_IllegalRefParam, "ref").WithLocation(5, 21), - // (10,21): error CS9000: List patterns may not be used for a value of type 'C'. + // (10,21): error CS1620: Argument 1 must be passed with the 'ref' keyword // _ = this is [var item, ..var rest]; - Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[var item, ..var rest]").WithArguments("C").WithLocation(10, 21), - // (10,32): error CS9001: Slice patterns may not be used for a value of type 'C'. + Diagnostic(ErrorCode.ERR_BadArgRef, "[var item, ..var rest]").WithArguments("1", "ref").WithLocation(10, 21), + // (10,32): error CS1620: Argument 1 must be passed with the 'ref' keyword // _ = this is [var item, ..var rest]; - Diagnostic(ErrorCode.ERR_UnsupportedTypeForSlicePattern, "..var rest").WithArguments("C").WithLocation(10, 32), - // (11,18): error CS0518: Predefined type 'System.Index' is not defined or imported - // _ = this[^1]; - Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "^1").WithArguments("System.Index").WithLocation(11, 18), - // (11,18): error CS0656: Missing compiler required member 'System.Index..ctor' + Diagnostic(ErrorCode.ERR_BadArgRef, "..var rest").WithArguments("1", "ref").WithLocation(10, 32), + // (11,18): error CS1620: Argument 1 must be passed with the 'ref' keyword // _ = this[^1]; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "^1").WithArguments("System.Index", ".ctor").WithLocation(11, 18), - // (12,18): error CS0518: Predefined type 'System.Range' is not defined or imported - // _ = this[1..^1]; - Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "1..^1").WithArguments("System.Range").WithLocation(12, 18), - // (12,18): error CS0518: Predefined type 'System.Index' is not defined or imported - // _ = this[1..^1]; - Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "1").WithArguments("System.Index").WithLocation(12, 18), - // (12,21): error CS0518: Predefined type 'System.Index' is not defined or imported - // _ = this[1..^1]; - Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "^1").WithArguments("System.Index").WithLocation(12, 21), - // (12,21): error CS0656: Missing compiler required member 'System.Index..ctor' - // _ = this[1..^1]; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "^1").WithArguments("System.Index", ".ctor").WithLocation(12, 21), - // (12,21): error CS0518: Predefined type 'System.Index' is not defined or imported + Diagnostic(ErrorCode.ERR_BadArgRef, "^1").WithArguments("1", "ref").WithLocation(11, 18), + // (12,18): error CS1620: Argument 1 must be passed with the 'ref' keyword // _ = this[1..^1]; - Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "^1").WithArguments("System.Index").WithLocation(12, 21) + Diagnostic(ErrorCode.ERR_BadArgRef, "1..^1").WithArguments("1", "ref").WithLocation(12, 18) ); } @@ -4209,12 +4330,12 @@ void M() // (7,21): error CS0631: ref and out are not valid in this context // public int this[ref Range r] => 0; Diagnostic(ErrorCode.ERR_IllegalRefParam, "ref").WithLocation(7, 21), - // (11,21): error CS9000: List patterns may not be used for a value of type 'C'. + // (11,21): error CS1620: Argument 1 must be passed with the 'ref' keyword // _ = this is [var item, ..var rest]; - Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[var item, ..var rest]").WithArguments("C").WithLocation(11, 21), - // (11,32): error CS9001: Slice patterns may not be used for a value of type 'C'. + Diagnostic(ErrorCode.ERR_BadArgRef, "[var item, ..var rest]").WithArguments("1", "ref").WithLocation(11, 21), + // (11,32): error CS1620: Argument 1 must be passed with the 'ref' keyword // _ = this is [var item, ..var rest]; - Diagnostic(ErrorCode.ERR_UnsupportedTypeForSlicePattern, "..var rest").WithArguments("C").WithLocation(11, 32), + Diagnostic(ErrorCode.ERR_BadArgRef, "..var rest").WithArguments("1", "ref").WithLocation(11, 32), // (12,18): error CS1620: Argument 1 must be passed with the 'ref' keyword // _ = this[^1]; Diagnostic(ErrorCode.ERR_BadArgRef, "^1").WithArguments("1", "ref").WithLocation(12, 18), @@ -4242,35 +4363,20 @@ void M() } } "; - var compilation = CreateCompilation(source); + var compilation = CreateCompilation(new[] { source, TestSources.Index }); compilation.VerifyEmitDiagnostics( - // (10,21): error CS9000: List patterns may not be used for a value of type 'C'. + // (10,21): error CS1503: Argument 1: cannot convert from 'System.Index' to 'in int' // _ = this is [var item, ..var rest]; - Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[var item, ..var rest]").WithArguments("C").WithLocation(10, 21), - // (10,32): error CS9001: Slice patterns may not be used for a value of type 'C'. + Diagnostic(ErrorCode.ERR_BadArgType, "[var item, ..var rest]").WithArguments("1", "System.Index", "in int").WithLocation(10, 21), + // (10,32): error CS0518: Predefined type 'System.Range' is not defined or imported // _ = this is [var item, ..var rest]; - Diagnostic(ErrorCode.ERR_UnsupportedTypeForSlicePattern, "..var rest").WithArguments("C").WithLocation(10, 32), - // (11,18): error CS0518: Predefined type 'System.Index' is not defined or imported - // _ = this[^1]; - Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "^1").WithArguments("System.Index").WithLocation(11, 18), - // (11,18): error CS0656: Missing compiler required member 'System.Index..ctor' + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "..var rest").WithArguments("System.Range").WithLocation(10, 32), + // (11,18): error CS1503: Argument 1: cannot convert from 'System.Index' to 'in int' // _ = this[^1]; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "^1").WithArguments("System.Index", ".ctor").WithLocation(11, 18), + Diagnostic(ErrorCode.ERR_BadArgType, "^1").WithArguments("1", "System.Index", "in int").WithLocation(11, 18), // (12,18): error CS0518: Predefined type 'System.Range' is not defined or imported // _ = this[1..^1]; - Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "1..^1").WithArguments("System.Range").WithLocation(12, 18), - // (12,18): error CS0518: Predefined type 'System.Index' is not defined or imported - // _ = this[1..^1]; - Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "1").WithArguments("System.Index").WithLocation(12, 18), - // (12,21): error CS0518: Predefined type 'System.Index' is not defined or imported - // _ = this[1..^1]; - Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "^1").WithArguments("System.Index").WithLocation(12, 21), - // (12,21): error CS0656: Missing compiler required member 'System.Index..ctor' - // _ = this[1..^1]; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "^1").WithArguments("System.Index", ".ctor").WithLocation(12, 21), - // (12,21): error CS0518: Predefined type 'System.Index' is not defined or imported - // _ = this[1..^1]; - Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "^1").WithArguments("System.Index").WithLocation(12, 21) + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "1..^1").WithArguments("System.Range").WithLocation(12, 18) ); } @@ -4307,7 +4413,7 @@ void M2() verifier.VerifyIL("C.M", @" { - // Code size 79 (0x4f) + // Code size 77 (0x4d) .maxstack 4 .locals init (string V_0, //item string V_1, //rest @@ -4317,37 +4423,35 @@ .locals init (string V_0, //item IL_0000: ldarg.0 IL_0001: stloc.2 IL_0002: ldloc.2 - IL_0003: brfalse.s IL_004e + IL_0003: brfalse.s IL_004c IL_0005: ldloc.2 IL_0006: callvirt ""int C.Length.get"" IL_000b: ldc.i4.1 - IL_000c: blt.s IL_004e + IL_000c: blt.s IL_004c IL_000e: ldloc.2 IL_000f: ldc.i4.0 - IL_0010: ldc.i4.0 - IL_0011: newobj ""System.Index..ctor(int, bool)"" - IL_0016: stloc.3 - IL_0017: ldloca.s V_3 - IL_0019: callvirt ""string C.this[in System.Index].get"" - IL_001e: stloc.0 - IL_001f: ldloc.2 - IL_0020: ldc.i4.1 - IL_0021: ldc.i4.0 - IL_0022: newobj ""System.Index..ctor(int, bool)"" - IL_0027: ldc.i4.0 - IL_0028: ldc.i4.1 - IL_0029: newobj ""System.Index..ctor(int, bool)"" - IL_002e: newobj ""System.Range..ctor(System.Index, System.Index)"" - IL_0033: stloc.s V_4 - IL_0035: ldloca.s V_4 - IL_0037: callvirt ""string C.this[in System.Range].get"" - IL_003c: stloc.1 - IL_003d: ldloc.0 - IL_003e: ldloc.1 - IL_003f: newobj ""System.ValueTuple..ctor(string, string)"" - IL_0044: box ""System.ValueTuple"" - IL_0049: call ""void System.Console.Write(object)"" - IL_004e: ret + IL_0010: call ""System.Index System.Index.op_Implicit(int)"" + IL_0015: stloc.3 + IL_0016: ldloca.s V_3 + IL_0018: callvirt ""string C.this[in System.Index].get"" + IL_001d: stloc.0 + IL_001e: ldloc.2 + IL_001f: ldc.i4.1 + IL_0020: call ""System.Index System.Index.op_Implicit(int)"" + IL_0025: ldc.i4.0 + IL_0026: ldc.i4.1 + IL_0027: newobj ""System.Index..ctor(int, bool)"" + IL_002c: newobj ""System.Range..ctor(System.Index, System.Index)"" + IL_0031: stloc.s V_4 + IL_0033: ldloca.s V_4 + IL_0035: callvirt ""string C.this[in System.Range].get"" + IL_003a: stloc.1 + IL_003b: ldloc.0 + IL_003c: ldloc.1 + IL_003d: newobj ""System.ValueTuple..ctor(string, string)"" + IL_0042: box ""System.ValueTuple"" + IL_0047: call ""void System.Console.Write(object)"" + IL_004c: ret } "); } @@ -4395,7 +4499,7 @@ void M2() verifier.VerifyIL("C.M", @" { - // Code size 82 (0x52) + // Code size 80 (0x50) .maxstack 4 .locals init (string V_0, //item string V_1, //rest @@ -4403,35 +4507,33 @@ .locals init (string V_0, //item IL_0000: ldarg.0 IL_0001: stloc.2 IL_0002: ldloc.2 - IL_0003: brfalse.s IL_0051 + IL_0003: brfalse.s IL_004f IL_0005: ldloc.2 IL_0006: callvirt ""int C.Length.get"" IL_000b: ldc.i4.1 - IL_000c: blt.s IL_0051 + IL_000c: blt.s IL_004f IL_000e: ldloc.2 IL_000f: ldc.i4.0 - IL_0010: ldc.i4.0 - IL_0011: newobj ""System.Index..ctor(int, bool)"" - IL_0016: call ""MyIndex MyIndex.op_Implicit(System.Index)"" - IL_001b: callvirt ""string C.this[MyIndex].get"" - IL_0020: stloc.0 - IL_0021: ldloc.2 - IL_0022: ldc.i4.1 - IL_0023: ldc.i4.0 - IL_0024: newobj ""System.Index..ctor(int, bool)"" - IL_0029: ldc.i4.0 - IL_002a: ldc.i4.1 - IL_002b: newobj ""System.Index..ctor(int, bool)"" - IL_0030: newobj ""System.Range..ctor(System.Index, System.Index)"" - IL_0035: call ""MyRange MyRange.op_Implicit(System.Range)"" - IL_003a: callvirt ""string C.this[MyRange].get"" - IL_003f: stloc.1 - IL_0040: ldloc.0 - IL_0041: ldloc.1 - IL_0042: newobj ""System.ValueTuple..ctor(string, string)"" - IL_0047: box ""System.ValueTuple"" - IL_004c: call ""void System.Console.Write(object)"" - IL_0051: ret + IL_0010: call ""System.Index System.Index.op_Implicit(int)"" + IL_0015: call ""MyIndex MyIndex.op_Implicit(System.Index)"" + IL_001a: callvirt ""string C.this[MyIndex].get"" + IL_001f: stloc.0 + IL_0020: ldloc.2 + IL_0021: ldc.i4.1 + IL_0022: call ""System.Index System.Index.op_Implicit(int)"" + IL_0027: ldc.i4.0 + IL_0028: ldc.i4.1 + IL_0029: newobj ""System.Index..ctor(int, bool)"" + IL_002e: newobj ""System.Range..ctor(System.Index, System.Index)"" + IL_0033: call ""MyRange MyRange.op_Implicit(System.Range)"" + IL_0038: callvirt ""string C.this[MyRange].get"" + IL_003d: stloc.1 + IL_003e: ldloc.0 + IL_003f: ldloc.1 + IL_0040: newobj ""System.ValueTuple..ctor(string, string)"" + IL_0045: box ""System.ValueTuple"" + IL_004a: call ""void System.Console.Write(object)"" + IL_004f: ret } "); } @@ -4451,11 +4553,14 @@ void M(int[] array) } } "; - var compilation = CreateCompilation(source); + var compilation = CreateCompilation(new[] { source, TestSources.Index }); compilation.VerifyEmitDiagnostics( // (9,44): error CS8122: An expression tree may not contain an 'is' pattern-matching operator. // Expression> ok1 = () => array is [_, ..]; - Diagnostic(ErrorCode.ERR_ExpressionTreeContainsIsMatch, "array is [_, ..]").WithLocation(9, 44) + Diagnostic(ErrorCode.ERR_ExpressionTreeContainsIsMatch, "array is [_, ..]").WithLocation(9, 44), + // (9,53): error CS8790: An expression tree may not contain a pattern System.Index or System.Range indexer access + // Expression> ok1 = () => array is [_, ..]; + Diagnostic(ErrorCode.ERR_ExpressionTreeContainsPatternIndexOrRangeIndexer, "[_, ..]").WithLocation(9, 53) ); } @@ -4488,6 +4593,7 @@ public void SlicePattern_ExtensionIgnored() { var src = @" _ = new C() is [..var y]; +_ = new C()[..]; static class Extensions { @@ -4498,11 +4604,14 @@ class C public int Count => throw null; public int this[int i] => throw null; }"; - var comp = CreateCompilation(src); + var comp = CreateCompilation(new[] { src, TestSources.Index, TestSources.Range }); comp.VerifyEmitDiagnostics( - // (2,17): error CS9001: Slice patterns may not be used for a value of type 'C'. + // (2,17): error CS1503: Argument 1: cannot convert from 'System.Range' to 'int' // _ = new C() is [..var y]; - Diagnostic(ErrorCode.ERR_UnsupportedTypeForSlicePattern, "..var y").WithArguments("C").WithLocation(2, 17) + Diagnostic(ErrorCode.ERR_BadArgType, "..var y").WithArguments("1", "System.Range", "int").WithLocation(2, 17), + // (3,13): error CS1503: Argument 1: cannot convert from 'System.Range' to 'int' + // _ = new C()[..]; + Diagnostic(ErrorCode.ERR_BadArgType, "..").WithArguments("1", "System.Range", "int").WithLocation(3, 13) ); } @@ -4515,7 +4624,7 @@ public void SlicePattern_String() System.Console.Write((first, rest).ToString()); } "; - CompileAndVerify(src, expectedOutput: "(a, bc)"); + CompileAndVerify(new[] { src, TestSources.Index, TestSources.Range }, expectedOutput: "(a, bc)"); } [Fact] @@ -4557,7 +4666,7 @@ class C public int Count => throw null; public int this[int i] => throw null; }"; - var comp = CreateCompilation(src); + var comp = CreateCompilation(new[] { src, TestSources.Index }); comp.VerifyEmitDiagnostics( // (2,13): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{ Count: 2 }' is not covered. // _ = new C() switch // 1 @@ -4569,6 +4678,37 @@ class C // _ = new C() switch // 3 Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("{ Count: 1 }").WithLocation(16, 13) ); + + comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (5,5): error CS0518: Predefined type 'System.Index' is not defined or imported + // [_] => 1, + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "[_]").WithArguments("System.Index").WithLocation(5, 5), + // (5,5): error CS0656: Missing compiler required member 'System.Index.GetOffset' + // [_] => 1, + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[_]").WithArguments("System.Index", "GetOffset").WithLocation(5, 5), + // (13,7): error CS8503: A property subpattern requires a reference to the property or field to be matched, e.g. '{ Name: _ }' + // { _, _, .. } => 2, + Diagnostic(ErrorCode.ERR_PropertyPatternNameMissing, "_").WithArguments("_").WithLocation(13, 7), + // (20,5): error CS0518: Predefined type 'System.Index' is not defined or imported + // [_, _] => 2, + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "[_, _]").WithArguments("System.Index").WithLocation(20, 5), + // (20,5): error CS0656: Missing compiler required member 'System.Index.GetOffset' + // [_, _] => 2, + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[_, _]").WithArguments("System.Index", "GetOffset").WithLocation(20, 5), + // (21,5): error CS0518: Predefined type 'System.Index' is not defined or imported + // [_, _, ..] => 3, + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "[_, _, ..]").WithArguments("System.Index").WithLocation(21, 5), + // (21,5): error CS0656: Missing compiler required member 'System.Index.GetOffset' + // [_, _, ..] => 3, + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[_, _, ..]").WithArguments("System.Index", "GetOffset").WithLocation(21, 5), + // (28,5): error CS0518: Predefined type 'System.Index' is not defined or imported + // [_, _] => 2, + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "[_, _]").WithArguments("System.Index").WithLocation(28, 5), + // (28,5): error CS0656: Missing compiler required member 'System.Index.GetOffset' + // [_, _] => 2, + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[_, _]").WithArguments("System.Index", "GetOffset").WithLocation(28, 5) + ); } [Fact] @@ -4601,7 +4741,7 @@ class C public int Count => throw null; public int this[int i] => throw null; }"; - var comp = CreateCompilation(src); + var comp = CreateCompilation(new[] { src, TestSources.Index }); comp.VerifyEmitDiagnostics( // (2,13): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{ Count: 0 }' is not covered. // _ = new C() switch // 1 @@ -4650,7 +4790,7 @@ class C public int Count => throw null!; public string? this[int i] => throw null!; }"; - var comp = CreateCompilation(src); + var comp = CreateCompilation(new[] { src, TestSources.Index }); comp.VerifyEmitDiagnostics( // (3,13): warning CS8655: The switch expression does not handle some null inputs (it is not exhaustive). For example, the pattern '[null]' is not covered. // _ = new C() switch // 1 @@ -4677,7 +4817,7 @@ class C public int Count => throw null; public int this[int i] => throw null; }"; - var comp = CreateCompilation(src); + var comp = CreateCompilation(new[] { src, TestSources.Index }); comp.VerifyEmitDiagnostics( // (2,13): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '[_, 0]' is not covered. // _ = new C() switch // 1 @@ -4701,7 +4841,7 @@ class C public int Count => throw null; public int this[int i] => throw null; }"; - var comp = CreateCompilation(src); + var comp = CreateCompilation(new[] { src, TestSources.Index }); comp.VerifyEmitDiagnostics( // (2,13): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '[0, _]' is not covered. // _ = new C() switch // 1 @@ -4740,7 +4880,7 @@ class C public int Count => throw null; public int this[int i] => throw null; }"; - var comp = CreateCompilation(src); + var comp = CreateCompilation(new[] { src, TestSources.Index }); comp.VerifyEmitDiagnostics( // (2,13): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '[0]' is not covered. // _ = new C() switch // 1 @@ -4770,7 +4910,7 @@ class C public int this[int i] => throw null; } "; - var comp = CreateCompilation(src); + var comp = CreateCompilation(new[] { src, TestSources.Index }); comp.VerifyEmitDiagnostics( // (2,13): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{ Length: 0 }' is not covered. // _ = new C() switch @@ -4794,7 +4934,7 @@ class C public int Count => throw null; public int this[int i] => throw null; }"; - var comp = CreateCompilation(src); + var comp = CreateCompilation(new[] { src, TestSources.Index }); comp.VerifyEmitDiagnostics(); } @@ -4815,7 +4955,7 @@ class C public int this[int i] => throw null; public C Slice(int i, int j) => throw null; }"; - var comp = CreateCompilation(src); + var comp = CreateCompilation(new[] { src, TestSources.Index, TestSources.Range }); comp.VerifyEmitDiagnostics(); } @@ -4838,7 +4978,7 @@ class C class Derived : C { } "; // Note: we don't know how to explain `Derived and [1]` - var comp = CreateCompilation(src); + var comp = CreateCompilation(new[] { src, TestSources.Index }); comp.VerifyEmitDiagnostics( // (2,13): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '_' is not covered. // _ = new C() switch @@ -4862,17 +5002,17 @@ class C public uint Count => throw null!; public int this[int i] => throw null!; }"; - var comp = CreateCompilation(src); + var comp = CreateCompilation(new[] { src, TestSources.Index }); comp.VerifyEmitDiagnostics( // (4,5): error CS9000: List patterns may not be used for a value of type 'C'. // [..] => 1, Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[..]").WithArguments("C").WithLocation(4, 5), - // (7,13): error CS0518: Predefined type 'System.Index' is not defined or imported - // _ = new C()[^1]; // 2 - Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "^1").WithArguments("System.Index").WithLocation(7, 13), - // (7,13): error CS0656: Missing compiler required member 'System.Index..ctor' + // (4,5): error CS1503: Argument 1: cannot convert from 'System.Index' to 'int' + // [..] => 1, + Diagnostic(ErrorCode.ERR_BadArgType, "[..]").WithArguments("1", "System.Index", "int").WithLocation(4, 5), + // (7,13): error CS1503: Argument 1: cannot convert from 'System.Index' to 'int' // _ = new C()[^1]; // 2 - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "^1").WithArguments("System.Index", ".ctor").WithLocation(7, 13) + Diagnostic(ErrorCode.ERR_BadArgType, "^1").WithArguments("1", "System.Index", "int").WithLocation(7, 13) ); } @@ -4892,17 +5032,17 @@ class C public nint Count => throw null!; public int this[int i] => throw null!; }"; - var comp = CreateCompilation(src); + var comp = CreateCompilation(new[] { src, TestSources.Index }); comp.VerifyEmitDiagnostics( // (4,5): error CS9000: List patterns may not be used for a value of type 'C'. // [..] => 1, Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[..]").WithArguments("C").WithLocation(4, 5), - // (7,13): error CS0518: Predefined type 'System.Index' is not defined or imported - // _ = new C()[^1]; // 2, 3 - Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "^1").WithArguments("System.Index").WithLocation(7, 13), - // (7,13): error CS0656: Missing compiler required member 'System.Index..ctor' + // (4,5): error CS1503: Argument 1: cannot convert from 'System.Index' to 'int' + // [..] => 1, + Diagnostic(ErrorCode.ERR_BadArgType, "[..]").WithArguments("1", "System.Index", "int").WithLocation(4, 5), + // (7,13): error CS1503: Argument 1: cannot convert from 'System.Index' to 'int' // _ = new C()[^1]; // 2, 3 - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "^1").WithArguments("System.Index", ".ctor").WithLocation(7, 13) + Diagnostic(ErrorCode.ERR_BadArgType, "^1").WithArguments("1", "System.Index", "int").WithLocation(7, 13) ); } @@ -5330,7 +5470,7 @@ static void Test(C[] a) } } "; - var comp = CreateCompilationWithIndexAndRange(src, parseOptions: TestOptions.RegularWithListPatterns); + var comp = CreateCompilation(new[] { src, TestSources.Index, TestSources.Range, TestSources.GetSubArray }); comp.VerifyEmitDiagnostics( // (12,18): error CS8120: The switch case is unreachable. It has already been handled by a previous case or it is impossible to match. // case [{Y:0, X:> 0}]: @@ -5774,7 +5914,7 @@ class Derived : Base, I2 { } "; - var compilation = CreateCompilation(new[] { source, _iTupleSource }, options: TestOptions.DebugExe); + var compilation = CreateCompilation(new[] { source, TestSources.Index, _iTupleSource }, options: TestOptions.DebugExe); compilation.VerifyDiagnostics( // (11,18): error CS8120: The switch case is unreachable. It has already been handled by a previous case or it is impossible to match. // case [Derived s]: // 1 @@ -5842,7 +5982,7 @@ void Test(int[] a) }; } }"; - var comp = CreateCompilation(src); + var comp = CreateCompilation(new[] { src, TestSources.Index }); comp.VerifyEmitDiagnostics( // (11,18): error CS8120: The switch case is unreachable. It has already been handled by a previous case or it is impossible to match. // case [_]: @@ -5873,6 +6013,7 @@ void Test(int[] a) "); } + // PROTOTYPE this test takes 10 seconds to run... [Theory] [CombinatorialData] public void Subsumption_Slice_00( @@ -6079,6 +6220,17 @@ .locals init (int V_0) verifier.VerifyIL("C.Test2", expectedIl); } + [Fact] + public void LengthPattern_NegativeLengthTest_MissingIndex() + { + var src = @" +int[] a = null; +_ = a is { Length: -1 }; +"; + var comp = CreateCompilation(src); + comp.VerifyDiagnostics(); + } + [Fact] public void LengthPattern_NegativeLengthTest() { @@ -6109,7 +6261,7 @@ public void LengthPattern_NegativeLengthTest() { Length: 1 } => 0, }; "; - var comp = CreateCompilation(src); + var comp = CreateCompilation(new[] { src, TestSources.Index }); comp.VerifyDiagnostics( // (3,5): error CS8518: An expression of type 'int[]' can never match the provided pattern. // _ = a is { Length: -1 }; // 1 @@ -6148,7 +6300,7 @@ public void LengthPattern_NegativeNullHandling_WithNullHandling() { Length: -1 } => 0, // 2 }; "; - var comp = CreateCompilation(src); + var comp = CreateCompilation(new[] { src, TestSources.Index }); comp.VerifyDiagnostics( // (5,7): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern 'not null' is not covered. // _ = a switch // 1 @@ -6173,7 +6325,7 @@ public void LengthPattern_NegativeNullHandling_DuplicateTest() _ => 3, }; "; - var comp = CreateCompilation(src); + var comp = CreateCompilation(new[] { src, TestSources.Index }); comp.VerifyDiagnostics( // (3,5): error CS8518: An expression of type 'int[]' can never match the provided pattern. // _ = a is { Length: -1 } or { Length: -1 }; @@ -6199,7 +6351,7 @@ public void LengthPattern_NegativeRangeTest() { Length: < 0 } => 0, // 3 }; "; - var comp = CreateCompilation(src); + var comp = CreateCompilation(new[] { src, TestSources.Index }); comp.VerifyDiagnostics( // (3,5): error CS8518: An expression of type 'int[]' can never match the provided pattern. // _ = a is { Length: < 0 }; // 1 @@ -6225,12 +6377,15 @@ public void LengthPattern_Switch_NegativeRangeTestByElimination() _ => 3, }; "; - var comp = CreateCompilation(src); + var comp = CreateCompilation(new[] { src, TestSources.Index }); comp.VerifyDiagnostics( // (6,5): error CS8510: The pattern is unreachable. It has already been handled by a previous arm of the switch expression or it is impossible to match. // { Length: <= 0 } => 2, Diagnostic(ErrorCode.ERR_SwitchArmSubsumed, "{ Length: <= 0 }").WithLocation(6, 5) ); + + comp = CreateCompilation(src); + comp.VerifyDiagnostics(); } [Fact, WorkItem(51801, "https://github.com/dotnet/roslyn/issues/51801")] @@ -6264,7 +6419,7 @@ public string M() } } "; - var verifier = CompileAndVerify(source, options: TestOptions.DebugDll); + var verifier = CompileAndVerify(new[] { source, TestSources.Index }, options: TestOptions.DebugDll); verifier.VerifyIL("C.M", @" { // Code size 105 (0x69) @@ -6374,6 +6529,9 @@ class C // (2,16): error CS9000: List patterns may not be used for a value of type 'C'. // _ = new C() is []; Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[]").WithArguments("C").WithLocation(2, 16), + // (2,16): error CS1503: Argument 1: cannot convert from 'System.Index' to 'int' + // _ = new C() is []; + Diagnostic(ErrorCode.ERR_BadArgType, "[]").WithArguments("1", "System.Index", "int").WithLocation(2, 16), // (3,13): error CS1503: Argument 1: cannot convert from 'System.Index' to 'int' // _ = new C()[^1]; Diagnostic(ErrorCode.ERR_BadArgType, "^1").WithArguments("1", "System.Index", "int").WithLocation(3, 13) @@ -6381,19 +6539,21 @@ class C } [Fact] - public void ListPattern_IndexAndRangeNotNecessary() + public void ListPattern_IndexAndRangeAreNecessaryButOptimizedAway() { var source = @" -class C +new C().M(); + +public class C { - public int this[int i] => 1; - public int Length => 0; - public int Slice(int i, int j) => 0; + public int this[int i] => 2; + public int Length => 1; + public int Slice(int i, int j) => 3; - void M() + public void M() { - _ = this is []; - _ = this is [.. var x]; + if (this is [var x] && this is [.. var y]) + System.Console.Write((x, y)); } } "; @@ -6401,46 +6561,83 @@ void M() var compilation = CreateCompilation(source); compilation.MakeTypeMissing(WellKnownType.System_Index); compilation.MakeTypeMissing(WellKnownType.System_Range); + compilation.VerifyDiagnostics( + // (12,21): error CS0518: Predefined type 'System.Index' is not defined or imported + // if (this is [var x] && this is [.. var y]) + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "[var x]").WithArguments("System.Index").WithLocation(12, 21), + // (12,21): error CS0656: Missing compiler required member 'System.Index.GetOffset' + // if (this is [var x] && this is [.. var y]) + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[var x]").WithArguments("System.Index", "GetOffset").WithLocation(12, 21), + // (12,40): error CS0518: Predefined type 'System.Index' is not defined or imported + // if (this is [var x] && this is [.. var y]) + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "[.. var y]").WithArguments("System.Index").WithLocation(12, 40), + // (12,40): error CS0656: Missing compiler required member 'System.Index.GetOffset' + // if (this is [var x] && this is [.. var y]) + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[.. var y]").WithArguments("System.Index", "GetOffset").WithLocation(12, 40), + // (12,41): error CS0518: Predefined type 'System.Range' is not defined or imported + // if (this is [var x] && this is [.. var y]) + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, ".. var y").WithArguments("System.Range").WithLocation(12, 41), + // (12,41): error CS0656: Missing compiler required member 'System.Range.get_Start' + // if (this is [var x] && this is [.. var y]) + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, ".. var y").WithArguments("System.Range", "get_Start").WithLocation(12, 41), + // (12,41): error CS0656: Missing compiler required member 'System.Range.get_End' + // if (this is [var x] && this is [.. var y]) + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, ".. var y").WithArguments("System.Range", "get_End").WithLocation(12, 41), + // (12,41): error CS0656: Missing compiler required member 'System.Index.GetOffset' + // if (this is [var x] && this is [.. var y]) + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, ".. var y").WithArguments("System.Index", "GetOffset").WithLocation(12, 41) + ); - var verifier = CompileAndVerify(compilation); + compilation = CreateCompilation(new[] { source, TestSources.Index, TestSources.Range }); + var verifier = CompileAndVerify(compilation, expectedOutput: "(2, 3)"); verifier.VerifyDiagnostics(); // Note: no Index or Range involved verifier.VerifyIL("C.M", @" { - // Code size 47 (0x2f) - .maxstack 4 - .locals init (C V_0, - int V_1) + // Code size 72 (0x48) + .maxstack 3 + .locals init (int V_0, //x + int V_1, //y + C V_2, + C V_3, + int V_4, + int V_5) IL_0000: ldarg.0 - IL_0001: stloc.0 - IL_0002: ldloc.0 - IL_0003: brfalse.s IL_0010 - IL_0005: ldloc.0 + IL_0001: stloc.2 + IL_0002: ldloc.2 + IL_0003: brfalse.s IL_0047 + IL_0005: ldloc.2 IL_0006: callvirt ""int C.Length.get"" - IL_000b: ldc.i4.0 - IL_000c: ceq - IL_000e: br.s IL_0011 - IL_0010: ldc.i4.0 - IL_0011: pop - IL_0012: ldarg.0 - IL_0013: stloc.0 - IL_0014: ldloc.0 - IL_0015: brfalse.s IL_002c - IL_0017: ldloc.0 - IL_0018: callvirt ""int C.Length.get"" - IL_001d: stloc.1 - IL_001e: ldloc.0 - IL_001f: ldc.i4.0 - IL_0020: ldloc.1 - IL_0021: ldc.i4.0 - IL_0022: sub - IL_0023: callvirt ""int C.Slice(int, int)"" - IL_0028: pop - IL_0029: ldc.i4.1 - IL_002a: br.s IL_002d - IL_002c: ldc.i4.0 - IL_002d: pop - IL_002e: ret + IL_000b: ldc.i4.1 + IL_000c: bne.un.s IL_0047 + IL_000e: ldloc.2 + IL_000f: ldc.i4.0 + IL_0010: callvirt ""int C.this[int].get"" + IL_0015: stloc.0 + IL_0016: ldarg.0 + IL_0017: stloc.2 + IL_0018: ldloc.2 + IL_0019: brfalse.s IL_0047 + IL_001b: ldloc.2 + IL_001c: callvirt ""int C.Length.get"" + IL_0021: ldloc.2 + IL_0022: stloc.3 + IL_0023: ldc.i4.0 + IL_0024: stloc.s V_4 + IL_0026: ldloc.s V_4 + IL_0028: sub + IL_0029: stloc.s V_5 + IL_002b: ldloc.3 + IL_002c: ldloc.s V_4 + IL_002e: ldloc.s V_5 + IL_0030: callvirt ""int C.Slice(int, int)"" + IL_0035: stloc.1 + IL_0036: ldloc.0 + IL_0037: ldloc.1 + IL_0038: newobj ""System.ValueTuple..ctor(int, int)"" + IL_003d: box ""System.ValueTuple"" + IL_0042: call ""void System.Console.Write(object)"" + IL_0047: ret } "); } @@ -6473,7 +6670,9 @@ class C public void ListPattern_SetOnlyIndexers() { var source = @" -_ = new C() is [var x, .. var y]; +_ = new C() is [var x, .. var y]; // 1, 2, 3 +_ = new C()[^1]; // 4 +_ = new C()[..]; // 5 class C { @@ -6485,11 +6684,20 @@ public int this[System.Range r] { set { } } var comp = CreateCompilation(new[] { source, TestSources.Index, TestSources.Range }); comp.VerifyEmitDiagnostics( // (2,16): error CS9000: List patterns may not be used for a value of type 'C'. - // _ = new C() is [var x, .. var y]; + // _ = new C() is [var x, .. var y]; // 1, 2, 3 Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[var x, .. var y]").WithArguments("C").WithLocation(2, 16), - // (2,24): error CS9001: Slice patterns may not be used for a value of type 'C'. - // _ = new C() is [var x, .. var y]; - Diagnostic(ErrorCode.ERR_UnsupportedTypeForSlicePattern, ".. var y").WithArguments("C").WithLocation(2, 24) + // (2,16): error CS0154: The property or indexer 'C.this[Index]' cannot be used in this context because it lacks the get accessor + // _ = new C() is [var x, .. var y]; // 1, 2, 3 + Diagnostic(ErrorCode.ERR_PropertyLacksGet, "[var x, .. var y]").WithArguments("C.this[System.Index]").WithLocation(2, 16), + // (2,24): error CS0154: The property or indexer 'C.this[Range]' cannot be used in this context because it lacks the get accessor + // _ = new C() is [var x, .. var y]; // 1, 2, 3 + Diagnostic(ErrorCode.ERR_PropertyLacksGet, ".. var y").WithArguments("C.this[System.Range]").WithLocation(2, 24), + // (3,5): error CS0154: The property or indexer 'C.this[Index]' cannot be used in this context because it lacks the get accessor + // _ = new C()[^1]; // 4 + Diagnostic(ErrorCode.ERR_PropertyLacksGet, "new C()[^1]").WithArguments("C.this[System.Index]").WithLocation(3, 5), + // (4,5): error CS0154: The property or indexer 'C.this[Range]' cannot be used in this context because it lacks the get accessor + // _ = new C()[..]; // 5 + Diagnostic(ErrorCode.ERR_PropertyLacksGet, "new C()[..]").WithArguments("C.this[System.Range]").WithLocation(4, 5) ); } @@ -6526,43 +6734,41 @@ public static void Print(ConsList? list) var verifier = CompileAndVerify(compilation, verify: Verification.Fails); verifier.VerifyIL("ConsList.Print", @" { - // Code size 84 (0x54) + // Code size 82 (0x52) .maxstack 4 .locals init (object V_0, //head ConsList V_1) //tail IL_0000: ldarg.0 - IL_0001: brfalse.s IL_0036 + IL_0001: brfalse.s IL_0034 IL_0003: ldarg.0 IL_0004: callvirt ""int ConsList.Length.get"" IL_0009: ldc.i4.1 - IL_000a: blt.s IL_0053 + IL_000a: blt.s IL_0051 IL_000c: ldarg.0 IL_000d: ldc.i4.0 - IL_000e: ldc.i4.0 - IL_000f: newobj ""System.Index..ctor(int, bool)"" - IL_0014: callvirt ""object ConsList.this[System.Index].get"" - IL_0019: stloc.0 - IL_001a: ldarg.0 - IL_001b: ldc.i4.1 - IL_001c: ldc.i4.0 - IL_001d: newobj ""System.Index..ctor(int, bool)"" - IL_0022: ldc.i4.0 - IL_0023: ldc.i4.1 - IL_0024: newobj ""System.Index..ctor(int, bool)"" - IL_0029: newobj ""System.Range..ctor(System.Index, System.Index)"" - IL_002e: callvirt ""ConsList ConsList.this[System.Range].get"" - IL_0033: stloc.1 - IL_0034: br.s IL_0037 - IL_0036: ret - IL_0037: ldloc.0 - IL_0038: callvirt ""string object.ToString()"" - IL_003d: ldstr "" "" - IL_0042: call ""string string.Concat(string, string)"" - IL_0047: call ""void System.Console.Write(string)"" - IL_004c: ldloc.1 - IL_004d: call ""void ConsList.Print(ConsList)"" - IL_0052: ret - IL_0053: ret + IL_000e: call ""System.Index System.Index.op_Implicit(int)"" + IL_0013: callvirt ""object ConsList.this[System.Index].get"" + IL_0018: stloc.0 + IL_0019: ldarg.0 + IL_001a: ldc.i4.1 + IL_001b: call ""System.Index System.Index.op_Implicit(int)"" + IL_0020: ldc.i4.0 + IL_0021: ldc.i4.1 + IL_0022: newobj ""System.Index..ctor(int, bool)"" + IL_0027: newobj ""System.Range..ctor(System.Index, System.Index)"" + IL_002c: callvirt ""ConsList ConsList.this[System.Range].get"" + IL_0031: stloc.1 + IL_0032: br.s IL_0035 + IL_0034: ret + IL_0035: ldloc.0 + IL_0036: callvirt ""string object.ToString()"" + IL_003b: ldstr "" "" + IL_0040: call ""string string.Concat(string, string)"" + IL_0045: call ""void System.Console.Write(string)"" + IL_004a: ldloc.1 + IL_004b: call ""void ConsList.Print(ConsList)"" + IL_0050: ret + IL_0051: ret } "); } @@ -6620,4 +6826,210 @@ .locals init (object V_0, //head } "); } + + [Fact] + public void Simple_IndexIndexer() + { + var source = @" +if (new C() is [var x]) +{ + System.Console.Write((x, new C()[^1])); +} + +class C +{ + public int Length => 1; + public int this[System.Index i] => 42; +} +"; + var comp = CreateCompilation(new[] { source, TestSources.Index, TestSources.Range }); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "(42, 42)"); + } + + [Fact] + public void Simple_IntIndexer_MissingIndex() + { + var source = @" +if (new C() is [var x]) +{ + System.Console.Write(x); +} + +class C +{ + public int Count => 1; + public int this[int i] => 42; +} +"; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (2,16): error CS0518: Predefined type 'System.Index' is not defined or imported + // if (new C() is [var x]) + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "[var x]").WithArguments("System.Index").WithLocation(2, 16), + // (2,16): error CS0656: Missing compiler required member 'System.Index.GetOffset' + // if (new C() is [var x]) + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[var x]").WithArguments("System.Index", "GetOffset").WithLocation(2, 16) + ); + } + + [Fact] + public void Simple_IntIndexer() + { + var source = @" +if (new C() is [var x]) +{ + System.Console.Write((x, new C()[^1])); +} + +class C +{ + public int Count => 1; + public int this[int i] => 42; +} +"; + var comp = CreateCompilation(new[] { source, TestSources.Index, TestSources.Range }); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "(42, 42)"); + } + + [Fact] + public void Simple_String() + { + var source = @" +if (""42"" is [var x, var y]) +{ + System.Console.Write((x, y)); +} +"; + var comp = CreateCompilation(new[] { source, TestSources.Index, TestSources.Range }); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "(4, 2)"); + } + + [Fact] + public void Simple_Array() + { + var source = @" +if (new[] { 4, 2 } is [var x, _]) +{ + var y = new[] { 4, 2 }[^1]; + System.Console.Write((x, y)); +} +"; + var comp = CreateCompilation(new[] { source, TestSources.Index, TestSources.Range }); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "(4, 2)"); + } + + [Theory] + [InlineData("{ 4, 0, 0 }", "[var x, _, _]", "[0]")] + [InlineData("{ 0, 4, 0 }", "[_, var x, _]", "[1]")] + [InlineData("{ 0, 0, 4 }", "[_, _, var x]", "[2]")] + [InlineData("{ 4, 0, 0 }", "[.., var x, _, _]", "[^3]")] + [InlineData("{ 0, 4, 0 }", "[.., _, var x, _]", "[^2]")] + [InlineData("{ 0, 0, 4 }", "[.., _, _, var x]", "[^1]")] + public void Simple_Array_VerifyIndexMaths(string data, string pattern, string elementAccess) + { + var source = $@" +if (new[] {data} is {pattern}) +{{ + var y = new[] {data}{elementAccess}; + System.Console.Write((x, y)); +}} +"; + var comp = CreateCompilation(new[] { source, TestSources.Index, TestSources.Range }); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "(4, 4)"); + } + + [Fact] + public void Simple_IndexIndexer_Slice() + { + var source = @" +if (new C() is [.. var x]) +{ + System.Console.Write((x, new C()[..])); +} + +class C +{ + public int Length => 0; + public int this[System.Index i] => throw null; + public int this[System.Range r] => 42; +} +"; + var comp = CreateCompilation(new[] { source, TestSources.Index, TestSources.Range }); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "(42, 42)"); + } + + [Fact] + public void Simple_IntIndexer_Slice() + { + var source = @" +if (new C() is [.. var x]) +{ + System.Console.Write((x, new C()[..])); +} + +class C +{ + public int Length => 1; + public int this[System.Index i] => throw null; + public int Slice(int i, int j) => 42; +} +"; + var comp = CreateCompilation(new[] { source, TestSources.Index, TestSources.Range }); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "(42, 42)"); + } + + [Fact] + public void Simple_String_Slice() + { + var source = @" +if (""0420"" is [_, .. var x, _]) +{ + System.Console.Write(x); +} +"; + var comp = CreateCompilation(new[] { source, TestSources.Index, TestSources.Range }); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "42"); + } + + [Fact] + public void Simple_Array_Slice() + { + var source = @" +if (new[] { 0, 4, 2, 0 } is [_, .. var x, _]) +{ + var y = new[] { 0, 4, 2, 0 }[1..^1]; + System.Console.Write((x[0], x[1], y[0], y[1])); +} +"; + var comp = CreateCompilation(new[] { source, TestSources.Index, TestSources.Range, TestSources.GetSubArray }); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "(4, 2, 4, 2)"); + } + + [Theory] + [InlineData("{ 4, 2, 0, 0, 0 }", "[.. var x, _, _, _]", "[0..^3]")] + [InlineData("{ 0, 4, 2, 0, 0 }", "[_, .. var x, _, _]", "[1..^2]")] + [InlineData("{ 0, 0, 4, 2, 0 }", "[_, _, .. var x, _]", "[2..^1]")] + [InlineData("{ 0, 0, 0, 4, 2 }", "[_, _, _, .. var x]", "[3..^0]")] + public void Simple_Array_Slice_VerifyRangeMaths(string data, string pattern, string elementAccess) + { + var source = $@" +if (new[] {data} is {pattern}) +{{ + var y = new[] {data}{elementAccess}; + System.Console.Write((x[0], x[1], x.Length, y[0], y[1], y.Length)); +}} +"; + var comp = CreateCompilation(new[] { source, TestSources.Index, TestSources.Range, TestSources.GetSubArray }); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "(4, 2, 2, 4, 2, 2)"); + } } diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests_ListPatterns.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests_ListPatterns.cs index dd6a6e6ccdf7..b83cebde3d77 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests_ListPatterns.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests_ListPatterns.cs @@ -11,6 +11,8 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests { public class PatternParsingTests_ListPatterns : ParsingTests { + private static CSharpParseOptions RegularWithoutListPatterns => TestOptions.Regular10; + private new void UsingExpression(string text, params DiagnosticDescription[] expectedErrors) { UsingExpression(text, options: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Preview), expectedErrors); @@ -24,57 +26,71 @@ public PatternParsingTests_ListPatterns(ITestOutputHelper output) : base(output) public void ListPattern_00() { UsingExpression(@"c is [[]]"); + verify(); - N(SyntaxKind.IsPatternExpression); + UsingExpression(@"c is [[]]", RegularWithoutListPatterns); + verify(); + + void verify() { - N(SyntaxKind.IdentifierName); - { - N(SyntaxKind.IdentifierToken, "c"); - } - N(SyntaxKind.IsKeyword); - N(SyntaxKind.ListPattern); + N(SyntaxKind.IsPatternExpression); { - N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "c"); + } + N(SyntaxKind.IsKeyword); N(SyntaxKind.ListPattern); { N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.ListPattern); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.CloseBracketToken); + } N(SyntaxKind.CloseBracketToken); } - N(SyntaxKind.CloseBracketToken); } + EOF(); } - EOF(); } [Fact] public void ListPattern_01() { UsingExpression(@"c is [[],] v"); + verify(); - N(SyntaxKind.IsPatternExpression); + UsingExpression(@"c is [[],] v", RegularWithoutListPatterns); + verify(); + + void verify() { - N(SyntaxKind.IdentifierName); - { - N(SyntaxKind.IdentifierToken, "c"); - } - N(SyntaxKind.IsKeyword); - N(SyntaxKind.ListPattern); + N(SyntaxKind.IsPatternExpression); { - N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "c"); + } + N(SyntaxKind.IsKeyword); N(SyntaxKind.ListPattern); { N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.ListPattern); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.CloseBracketToken); + } + N(SyntaxKind.CommaToken); N(SyntaxKind.CloseBracketToken); - } - N(SyntaxKind.CommaToken); - N(SyntaxKind.CloseBracketToken); - N(SyntaxKind.SingleVariableDesignation); - { - N(SyntaxKind.IdentifierToken, "v"); + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "v"); + } } } + EOF(); } - EOF(); } [Fact] @@ -427,25 +443,32 @@ public void NoRegressionOnArrayTypePattern_02() public void SlicePattern_01() { UsingExpression(@"c is [..]"); + verify(); - N(SyntaxKind.IsPatternExpression); + UsingExpression(@"c is [..]", RegularWithoutListPatterns); + verify(); + + void verify() { - N(SyntaxKind.IdentifierName); - { - N(SyntaxKind.IdentifierToken, "c"); - } - N(SyntaxKind.IsKeyword); - N(SyntaxKind.ListPattern); + N(SyntaxKind.IsPatternExpression); { - N(SyntaxKind.OpenBracketToken); - N(SyntaxKind.SlicePattern); + N(SyntaxKind.IdentifierName); { - N(SyntaxKind.DotDotToken); + N(SyntaxKind.IdentifierToken, "c"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.ListPattern); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.SlicePattern); + { + N(SyntaxKind.DotDotToken); + } + N(SyntaxKind.CloseBracketToken); } - N(SyntaxKind.CloseBracketToken); } + EOF(); } - EOF(); } [Fact] @@ -1078,3 +1101,4 @@ public void SlicePattern_19() } } } + From 39ea1f69d48ada20677a2a2add9746784d0f4b6f Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Mon, 8 Nov 2021 12:52:56 -0800 Subject: [PATCH 09/29] update tests --- .../Portable/Binder/Binder.ValueChecks.cs | 3 + .../Test/Emit/CodeGen/IndexAndRangeTests.cs | 36 ++ .../PatternMatchingTests_ListPatterns.cs | 470 ++++++++++++++---- 3 files changed, 416 insertions(+), 93 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index 0200c3ef12a1..52b08fbc647a 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -596,6 +596,9 @@ internal bool CheckValueKind(SyntaxNode node, BoundExpression expr, BindValueKin // meaning that the pattern symbol must be a method (either Slice or Substring) return CheckValueKind(node, implicitIndexerAccess.IndexerAccess, valueKind, checkingReceiver, diagnostics); + case BoundKind.IndexOrRangeIndexerPatternReceiverPlaceholder: + return true; + case BoundKind.ConditionalOperator: var conditional = (BoundConditionalOperator)expr; diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/IndexAndRangeTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/IndexAndRangeTests.cs index 1824287c21d3..72b824204dc9 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/IndexAndRangeTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/IndexAndRangeTests.cs @@ -310,6 +310,42 @@ .locals init (int[] V_0, //array "); } + [Fact] + public void PatternIndexCompoundOperator_InReadonlyMethod() + { + var src = @" +using System; +struct S +{ + private readonly int[] _array; + private int _counter; + + public S(int[] a) + { + _array = a; + _counter = 0; + } + public int Length + { + get => throw null; + } + public int this[int index] + { + get => throw null; + set => throw null; + } + + readonly void M() + { + this[^1] += 5; + } +} +"; + // TODO2 missing "error CS1604: Cannot assign to 'this[^1]' because it is read-only" + var comp = CreateCompilationWithIndexAndRangeAndSpan(src); + comp.VerifyDiagnostics(); + } + [Fact] [WorkItem(37789, "https://github.com/dotnet/roslyn/issues/37789")] public void PatternRangeCompoundOperator() diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs index 3b9d7960bc9e..2c7a58e48a40 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs @@ -892,47 +892,6 @@ public void M(int[] a) ); } - [Theory] - [PairwiseData] - public void ListPattern_MissingMembers( - bool implicitIndex, bool explicitIndex, - bool implicitRange, bool explicitRange, - bool hasLengthProp, bool hasCountProp) - { - var source = @$" -class X -{{ - {(implicitIndex ? "public int this[int i] => 1;" : null)} - {(explicitIndex ? "public int this[System.Index i] => 1;" : null)} - {(explicitRange ? "public int this[System.Range i] => 1;" : null)} - {(implicitRange ? "public int Slice(int i, int j) => 1;" : null)} - {(hasLengthProp ? "public int Length => 1;" : null)} - {(hasCountProp ? "public int Count => 1;" : null)} - - public static void Main() - {{ - _ = new X() is [.._]; - }} -}} -"; - var compilation = CreateCompilationWithIndexAndRange(source, parseOptions: TestOptions.RegularWithListPatterns, options: TestOptions.ReleaseExe); - - var isCountable = hasLengthProp || hasCountProp; - var isSliceable = implicitRange || explicitRange; - var isIndexable = implicitIndex || explicitIndex; - // TODO2 messy - var expectedDiagnostics = new[] - { - // (13,24): error CS0021: Cannot apply indexing with [] to an expression of type 'X' - // _ = new X() is [.._]; - !isIndexable || !isCountable ? Diagnostic(ErrorCode.ERR_BadIndexLHS, "[.._]").WithArguments("X").WithLocation(13, 24) : null, - // (13,25): error CS0021: Cannot apply indexing with [] to an expression of type 'X' - // _ = new X() is [.._]; - !isSliceable || !isCountable ? Diagnostic(ErrorCode.ERR_BadIndexLHS, ".._").WithArguments("X").WithLocation(13, 25) : null - }; - compilation.VerifyEmitDiagnostics(expectedDiagnostics.WhereNotNull().ToArray()); - } - [Fact] public void ListPattern_ObsoleteMembers() { @@ -1372,85 +1331,410 @@ public static void Main() Diagnostic(ErrorCode.ERR_BadIndexLHS, "new Test1()[0]").WithArguments("Test1").WithLocation(7, 13)); } - [Theory] - [InlineData("public int this[Index i] { set {} }")] - [InlineData("public int this[Index i] { private get => 0; set {} }")] - [InlineData("public int this[int i, int ignored = 0] => 0;")] - [InlineData("public int this[long i, int ignored = 0] => 0;")] - [InlineData("public int this[long i] => 0;")] - [InlineData("public int this[params int[] i] => 0;")] - [InlineData("private int this[Index i] => 0;")] - [InlineData("public int this[Index i] => 0;", true)] - public void ListPattern_MemberLookup_Index_ErrorCases(string indexer, bool valid = false) + [Fact] + public void ListPattern_MemberLookup_Index_ErrorCases_1() { var source = @" using System; + +_ = new Test1() is [0]; +_ = new Test1()[^1]; + class Test1 { - " + indexer + @" + public int this[Index i] { set {} } public int Length => 0; } -class X +"; + var compilation = CreateCompilationWithIndexAndRange(source); + compilation.VerifyEmitDiagnostics( + // (4,20): error CS0154: The property or indexer 'Test1.this[Index]' cannot be used in this context because it lacks the get accessor + // _ = new Test1() is [0]; + Diagnostic(ErrorCode.ERR_PropertyLacksGet, "[0]").WithArguments("Test1.this[System.Index]").WithLocation(4, 20), + // (5,5): error CS0154: The property or indexer 'Test1.this[Index]' cannot be used in this context because it lacks the get accessor + // _ = new Test1()[^1]; + Diagnostic(ErrorCode.ERR_PropertyLacksGet, "new Test1()[^1]").WithArguments("Test1.this[System.Index]").WithLocation(5, 5) + ); + } + + [Fact] + public void ListPattern_MemberLookup_Index_ErrorCases_2() + { + var source = @" +using System; + +_ = new Test1() is [0]; +_ = new Test1()[^1]; + +class Test1 { - public static void Main() + public int this[Index i] { private get => 0; set {} } + public int Length => 0; +} +"; + var compilation = CreateCompilationWithIndexAndRange(source); + compilation.VerifyEmitDiagnostics( + // (4,20): error CS0271: The property or indexer 'Test1.this[Index]' cannot be used in this context because the get accessor is inaccessible + // _ = new Test1() is [0]; + Diagnostic(ErrorCode.ERR_InaccessibleGetter, "[0]").WithArguments("Test1.this[System.Index]").WithLocation(4, 20), + // (5,5): error CS0271: The property or indexer 'Test1.this[Index]' cannot be used in this context because the get accessor is inaccessible + // _ = new Test1()[^1]; + Diagnostic(ErrorCode.ERR_InaccessibleGetter, "new Test1()[^1]").WithArguments("Test1.this[System.Index]").WithLocation(5, 5) + ); + } + + [Fact] + public void ListPattern_MemberLookup_Index_ErrorCases_3() { - _ = new Test1() is [0]; - } + var source = @" +using System; + +_ = new Test1() is [0]; +_ = new Test1()[^1]; + +class Test1 +{ + public int this[int i, int ignored = 0] => 0; + public int Length => 0; } "; - var compilation = CreateCompilationWithIndexAndRange(source, parseOptions: TestOptions.RegularWithListPatterns, options: TestOptions.ReleaseExe); - if (valid) - { - compilation.VerifyEmitDiagnostics(); - return; - } + var compilation = CreateCompilationWithIndexAndRange(source); compilation.VerifyEmitDiagnostics( - // (12,28): error CS9000: List patterns may not be used for a value of type 'Test1'. - // _ = new Test1() is [0]; - Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[0]").WithArguments("Test1").WithLocation(12, 28)); + // (4,20): error CS1503: Argument 1: cannot convert from 'System.Index' to 'int' + // _ = new Test1() is [0]; + Diagnostic(ErrorCode.ERR_BadArgType, "[0]").WithArguments("1", "System.Index", "int").WithLocation(4, 20), + // (5,17): error CS1503: Argument 1: cannot convert from 'System.Index' to 'int' + // _ = new Test1()[^1]; + Diagnostic(ErrorCode.ERR_BadArgType, "^1").WithArguments("1", "System.Index", "int").WithLocation(5, 17) + ); } - [Theory] - [InlineData("public int this[Range i] { set {} }")] - [InlineData("public int this[Range i] { private get => 0; set {} }")] - [InlineData("public int Slice(int i, int j, int ignored = 0) => 0;")] - [InlineData("public int Slice(int i, int j, params int[] ignored) => 0;")] - [InlineData("public int Slice(long i, long j) => 0;")] - [InlineData("public int Slice(params int[] i) => 0;")] - [InlineData("private int Slice(int i, int j) => 0;")] - [InlineData("public void Slice(int i, int j) {}")] - [InlineData("public int this[Range i] => 0;", true)] - [InlineData("public int Slice(int i, int j) => 0;", true)] - public void ListPattern_MemberLookup_Range_ErrorCases(string member, bool valid = false) + [Fact] + public void ListPattern_MemberLookup_Index_ErrorCases_4() + { + var source = @" +using System; + +_ = new Test1() is [0]; +_ = new Test1()[^1]; + +class Test1 +{ + public int this[long i, int ignored = 0] => 0; + public int Length => 0; +} +"; + var compilation = CreateCompilationWithIndexAndRange(source); + compilation.VerifyEmitDiagnostics( + // (4,20): error CS1503: Argument 1: cannot convert from 'System.Index' to 'long' + // _ = new Test1() is [0]; + Diagnostic(ErrorCode.ERR_BadArgType, "[0]").WithArguments("1", "System.Index", "long").WithLocation(4, 20), + // (5,17): error CS1503: Argument 1: cannot convert from 'System.Index' to 'long' + // _ = new Test1()[^1]; + Diagnostic(ErrorCode.ERR_BadArgType, "^1").WithArguments("1", "System.Index", "long").WithLocation(5, 17) + ); + } + + [Fact] + public void ListPattern_MemberLookup_Index_ErrorCases_5() + { + var source = @" +using System; + +_ = new Test1() is [0]; +_ = new Test1()[^1]; + +class Test1 +{ + public int this[long i] => 0; + public int Length => 0; +} +"; + var compilation = CreateCompilationWithIndexAndRange(source); + compilation.VerifyEmitDiagnostics( + // (4,20): error CS1503: Argument 1: cannot convert from 'System.Index' to 'long' + // _ = new Test1() is [0]; + Diagnostic(ErrorCode.ERR_BadArgType, "[0]").WithArguments("1", "System.Index", "long").WithLocation(4, 20), + // (5,17): error CS1503: Argument 1: cannot convert from 'System.Index' to 'long' + // _ = new Test1()[^1]; + Diagnostic(ErrorCode.ERR_BadArgType, "^1").WithArguments("1", "System.Index", "long").WithLocation(5, 17) + ); + } + + [Fact] + public void ListPattern_MemberLookup_Index_ErrorCases_6() { var source = @" -#pragma warning disable 8019 // Unused using using System; + +_ = new Test1() is [0]; +_ = new Test1()[^1]; + class Test1 { - " + member + @" + public int this[params int[] i] => 0; + public int Length => 0; +} +"; + var compilation = CreateCompilationWithIndexAndRange(source); + compilation.VerifyEmitDiagnostics( + // (4,20): error CS1503: Argument 1: cannot convert from 'System.Index' to 'int' + // _ = new Test1() is [0]; + Diagnostic(ErrorCode.ERR_BadArgType, "[0]").WithArguments("1", "System.Index", "int").WithLocation(4, 20), + // (5,17): error CS1503: Argument 1: cannot convert from 'System.Index' to 'int' + // _ = new Test1()[^1]; + Diagnostic(ErrorCode.ERR_BadArgType, "^1").WithArguments("1", "System.Index", "int").WithLocation(5, 17) + ); + } + + [Fact] + public void ListPattern_MemberLookup_Index_ErrorCases_7() + { + var source = @" +using System; + +_ = new Test1() is [0]; +_ = new Test1()[^1]; + +class Test1 +{ + private int this[Index i] => 0; + public int Length => 0; +} +"; + var compilation = CreateCompilationWithIndexAndRange(source); + compilation.VerifyEmitDiagnostics( + // (4,20): error CS0122: 'Test1.this[Index]' is inaccessible due to its protection level + // _ = new Test1() is [0]; + Diagnostic(ErrorCode.ERR_BadAccess, "[0]").WithArguments("Test1.this[System.Index]").WithLocation(4, 20), + // (5,5): error CS0122: 'Test1.this[Index]' is inaccessible due to its protection level + // _ = new Test1()[^1]; + Diagnostic(ErrorCode.ERR_BadAccess, "new Test1()[^1]").WithArguments("Test1.this[System.Index]").WithLocation(5, 5) + ); + } + + [Fact] + public void ListPattern_MemberLookup_Range_ErrorCases_1() + { + var source = @" +using System; + +_ = new Test1() is [..var p]; +_ = new Test1() is [..]; +_ = new Test1()[..]; + +class Test1 +{ + public int this[Range i] { set {} } public int this[int i] => throw new(); public int Length => 0; } -class X +"; + var compilation = CreateCompilationWithIndexAndRange(source); + compilation.VerifyEmitDiagnostics( + // (4,21): error CS0154: The property or indexer 'Test1.this[Range]' cannot be used in this context because it lacks the get accessor + // _ = new Test1() is [..var p]; + Diagnostic(ErrorCode.ERR_PropertyLacksGet, "..var p").WithArguments("Test1.this[System.Range]").WithLocation(4, 21), + // (6,5): error CS0154: The property or indexer 'Test1.this[Range]' cannot be used in this context because it lacks the get accessor + // _ = new Test1()[..]; + Diagnostic(ErrorCode.ERR_PropertyLacksGet, "new Test1()[..]").WithArguments("Test1.this[System.Range]").WithLocation(6, 5) + ); + } + + [Fact] + public void ListPattern_MemberLookup_Range_ErrorCases_2() + { + var source = @" +using System; + +_ = new Test1() is [..var p]; +_ = new Test1() is [..]; +_ = new Test1()[..]; + +class Test1 { - public static void Main() + public int this[Range i] { private get => 0; set {} } + public int this[int i] => throw new(); + public int Length => 0; +} +"; + var compilation = CreateCompilationWithIndexAndRange(source); + compilation.VerifyEmitDiagnostics( + // (4,21): error CS0271: The property or indexer 'Test1.this[Range]' cannot be used in this context because the get accessor is inaccessible + // _ = new Test1() is [..var p]; + Diagnostic(ErrorCode.ERR_InaccessibleGetter, "..var p").WithArguments("Test1.this[System.Range]").WithLocation(4, 21), + // (6,5): error CS0271: The property or indexer 'Test1.this[Range]' cannot be used in this context because the get accessor is inaccessible + // _ = new Test1()[..]; + Diagnostic(ErrorCode.ERR_InaccessibleGetter, "new Test1()[..]").WithArguments("Test1.this[System.Range]").WithLocation(6, 5) + ); + } + + [Fact] + public void ListPattern_MemberLookup_Range_ErrorCases_3() { - _ = new Test1() is [..var p]; - _ = new Test1() is [..]; - } + var source = @" +using System; + +_ = new Test1() is [..var p]; +_ = new Test1() is [..]; +_ = new Test1()[..]; + +class Test1 +{ + public int Slice(int i, int j, int ignored = 0) => 0; + public int this[int i] => throw new(); + public int Length => 0; } "; - var compilation = CreateCompilationWithIndexAndRange(source, parseOptions: TestOptions.RegularWithListPatterns, options: TestOptions.ReleaseExe); - if (valid) - { - compilation.VerifyDiagnostics(); - return; - } + var compilation = CreateCompilationWithIndexAndRange(source); compilation.VerifyEmitDiagnostics( - // (14,29): error CS9001: Slice patterns may not be used for a value of type 'Test1'. - // _ = new Test1() is [..var p]; - Diagnostic(ErrorCode.ERR_UnsupportedTypeForSlicePattern, "..var p").WithArguments("Test1").WithLocation(14, 29)); + // (4,21): error CS1503: Argument 1: cannot convert from 'System.Range' to 'int' + // _ = new Test1() is [..var p]; + Diagnostic(ErrorCode.ERR_BadArgType, "..var p").WithArguments("1", "System.Range", "int").WithLocation(4, 21), + // (6,17): error CS1503: Argument 1: cannot convert from 'System.Range' to 'int' + // _ = new Test1()[..]; + Diagnostic(ErrorCode.ERR_BadArgType, "..").WithArguments("1", "System.Range", "int").WithLocation(6, 17) + ); + } + + [Fact] + public void ListPattern_MemberLookup_Range_ErrorCases_4() + { + var source = @" +using System; + +_ = new Test1() is [..var p]; +_ = new Test1() is [..]; +_ = new Test1()[..]; + +class Test1 +{ + public int Slice(int i, int j, params int[] ignored) => 0; + public int this[int i] => throw new(); + public int Length => 0; +} +"; + var compilation = CreateCompilationWithIndexAndRange(source); + compilation.VerifyEmitDiagnostics( + // (4,21): error CS1503: Argument 1: cannot convert from 'System.Range' to 'int' + // _ = new Test1() is [..var p]; + Diagnostic(ErrorCode.ERR_BadArgType, "..var p").WithArguments("1", "System.Range", "int").WithLocation(4, 21), + // (6,17): error CS1503: Argument 1: cannot convert from 'System.Range' to 'int' + // _ = new Test1()[..]; + Diagnostic(ErrorCode.ERR_BadArgType, "..").WithArguments("1", "System.Range", "int").WithLocation(6, 17) + ); + } + + [Fact] + public void ListPattern_MemberLookup_Range_ErrorCases_5() + { + var source = @" +using System; + +_ = new Test1() is [..var p]; +_ = new Test1() is [..]; +_ = new Test1()[..]; + +class Test1 +{ + public int Slice(long i, long j) => 0; + public int this[int i] => throw new(); + public int Length => 0; +} +"; + var compilation = CreateCompilationWithIndexAndRange(source); + compilation.VerifyEmitDiagnostics( + // (4,21): error CS1503: Argument 1: cannot convert from 'System.Range' to 'int' + // _ = new Test1() is [..var p]; + Diagnostic(ErrorCode.ERR_BadArgType, "..var p").WithArguments("1", "System.Range", "int").WithLocation(4, 21), + // (6,17): error CS1503: Argument 1: cannot convert from 'System.Range' to 'int' + // _ = new Test1()[..]; + Diagnostic(ErrorCode.ERR_BadArgType, "..").WithArguments("1", "System.Range", "int").WithLocation(6, 17) + ); + } + + [Fact] + public void ListPattern_MemberLookup_Range_ErrorCases_6() + { + var source = @" +using System; + +_ = new Test1() is [..var p]; +_ = new Test1() is [..]; +_ = new Test1()[..]; + +class Test1 +{ + public int Slice(params int[] i) => 0; + public int this[int i] => throw new(); + public int Length => 0; +} +"; + var compilation = CreateCompilationWithIndexAndRange(source); + compilation.VerifyEmitDiagnostics( + // (4,21): error CS1503: Argument 1: cannot convert from 'System.Range' to 'int' + // _ = new Test1() is [..var p]; + Diagnostic(ErrorCode.ERR_BadArgType, "..var p").WithArguments("1", "System.Range", "int").WithLocation(4, 21), + // (6,17): error CS1503: Argument 1: cannot convert from 'System.Range' to 'int' + // _ = new Test1()[..]; + Diagnostic(ErrorCode.ERR_BadArgType, "..").WithArguments("1", "System.Range", "int").WithLocation(6, 17) + ); + } + + [Fact] + public void ListPattern_MemberLookup_Range_ErrorCases_7() + { + var source = @" +using System; + +_ = new Test1() is [..var p]; +_ = new Test1() is [..]; +_ = new Test1()[..]; + +class Test1 +{ + private int Slice(int i, int j) => 0; + public int this[int i] => throw new(); + public int Length => 0; +} +"; + var compilation = CreateCompilationWithIndexAndRange(source); + compilation.VerifyEmitDiagnostics( + // (4,21): error CS1503: Argument 1: cannot convert from 'System.Range' to 'int' + // _ = new Test1() is [..var p]; + Diagnostic(ErrorCode.ERR_BadArgType, "..var p").WithArguments("1", "System.Range", "int").WithLocation(4, 21), + // (6,17): error CS1503: Argument 1: cannot convert from 'System.Range' to 'int' + // _ = new Test1()[..]; + Diagnostic(ErrorCode.ERR_BadArgType, "..").WithArguments("1", "System.Range", "int").WithLocation(6, 17) + ); + } + + [Fact] + public void ListPattern_MemberLookup_Range_ErrorCases_8() + { + var source = @" +using System; + +_ = new Test1() is [..var p]; +_ = new Test1() is [..]; +_ = new Test1()[..]; + +class Test1 +{ + public void Slice(int i, int j) {} + public int this[int i] => throw new(); + public int Length => 0; +} +"; + var compilation = CreateCompilationWithIndexAndRange(source); + compilation.VerifyEmitDiagnostics( + // (4,21): error CS1503: Argument 1: cannot convert from 'System.Range' to 'int' + // _ = new Test1() is [..var p]; + Diagnostic(ErrorCode.ERR_BadArgType, "..var p").WithArguments("1", "System.Range", "int").WithLocation(4, 21), + // (6,17): error CS1503: Argument 1: cannot convert from 'System.Range' to 'int' + // _ = new Test1()[..]; + Diagnostic(ErrorCode.ERR_BadArgType, "..").WithArguments("1", "System.Range", "int").WithLocation(6, 17) + ); } [Fact] From 91a7ffa4e29b6c42e94e55818199e7972c6eb55e Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Mon, 8 Nov 2021 14:22:58 -0800 Subject: [PATCH 10/29] CheckValue on placeholder --- .../Portable/Binder/Binder.ValueChecks.cs | 39 ++++++++++++++- .../Portable/Binder/Binder_Expressions.cs | 2 +- .../Portable/Binder/Binder_Invocation.cs | 2 +- .../CSharp/Portable/BoundTree/BoundNodes.xml | 2 + .../Generated/BoundNodes.xml.Generated.cs | 48 +++++++++++-------- .../Lowering/LocalRewriter/LocalRewriter.cs | 6 ++- ...ocalRewriter_CompoundAssignmentOperator.cs | 3 +- .../LocalRewriter_IndexerAccess.cs | 2 + .../Test/Emit/CodeGen/IndexAndRangeTests.cs | 18 ++++--- .../PatternMatchingTests_ListPatterns.cs | 40 +++++++++++++++- 10 files changed, 128 insertions(+), 34 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index 52b08fbc647a..6556f3e007fe 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -597,7 +597,11 @@ internal bool CheckValueKind(SyntaxNode node, BoundExpression expr, BindValueKin return CheckValueKind(node, implicitIndexerAccess.IndexerAccess, valueKind, checkingReceiver, diagnostics); case BoundKind.IndexOrRangeIndexerPatternReceiverPlaceholder: - return true; + var receiverPlaceholder = (BoundIndexOrRangeIndexerPatternReceiverPlaceholder)expr; + return CheckValueKind(node, receiverPlaceholder.Expression, valueKind, checkingReceiver, diagnostics); + + case BoundKind.DeconstructValuePlaceholder: + break; case BoundKind.ConditionalOperator: var conditional = (BoundConditionalOperator)expr; @@ -622,6 +626,10 @@ internal bool CheckValueKind(SyntaxNode node, BoundExpression expr, BindValueKin case BoundKind.AssignmentOperator: var assignment = (BoundAssignmentOperator)expr; return CheckSimpleAssignmentValueKind(node, assignment, valueKind, diagnostics); + + default: + Debug.Assert(expr is not BoundValuePlaceholderBase, $"Placeholder kind {expr.Kind} should be explicitly handled"); + break; } // At this point we should have covered all the possible cases for anything that is not a strict RValue. @@ -629,6 +637,35 @@ internal bool CheckValueKind(SyntaxNode node, BoundExpression expr, BindValueKin return false; } + private static BoundExpression UnwrapPlaceholdersIfNeeded(BoundExpression? e) + { + switch (e) + { + case null: + return null; + + case BoundIndexOrRangeIndexerPatternReceiverPlaceholder placeholder: + return placeholder.Expression; + + case BoundSlicePatternReceiverPlaceholder: + case BoundListPatternReceiverPlaceholder: + case BoundObjectOrCollectionValuePlaceholder: + case BoundAwaitableValuePlaceholder: + case BoundInterpolatedStringHandlerPlaceholder: + case BoundDisposableValuePlaceholder: + // TODO2 + return e; + + case BoundDeconstructValuePlaceholder: + // PROTOTYPE file issue + return e; + + default: + Debug.Assert(e is not BoundValuePlaceholderBase, $"Placeholder kind {e.Kind} should be explicitly handled"); + return e; + } + } + private static bool CheckNotNamespaceOrType(BoundExpression expr, BindingDiagnosticBag diagnostics) { switch (expr.Kind) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index c9813f07bf95..3022dbd324fe 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -8056,7 +8056,7 @@ private bool TryBindIndexOrRangeImplicitIndexer( _ => GetValEscape(receiverOpt, LocalScopeDepth) }; - var receiverPlaceholder = new BoundIndexOrRangeIndexerPatternReceiverPlaceholder(receiverOpt.Syntax, receiverValEscape, receiverOpt.Type) { WasCompilerGenerated = true }; + var receiverPlaceholder = new BoundIndexOrRangeIndexerPatternReceiverPlaceholder(receiverOpt.Syntax, receiverValEscape, receiverOpt, receiverOpt.Type) { WasCompilerGenerated = true }; if (!TryBindIndexOrRangeImplicitIndexer(syntax, receiverPlaceholder, receiverType, argIsIndex: argIsIndex, out var lengthOrCountAccess, out var indexerOrSliceAccess, out var argumentPlaceholders, diagnostics)) { diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs index 3dd80a377c30..dd4199939de1 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs @@ -1464,7 +1464,7 @@ internal bool CheckImplicitThisCopyInReadOnlyMember(BoundExpression receiver, Me { // For now we are warning only in implicit copy scenarios that are only possible with readonly members. // Eventually we will warn on implicit value copies in more scenarios. See https://github.com/dotnet/roslyn/issues/33968. - if (receiver is BoundThisReference && + if (UnwrapPlaceholdersIfNeeded(receiver) is BoundThisReference && receiver.Type.IsValueType && ContainingMemberOrLambda is MethodSymbol containingMethod && containingMethod.IsEffectivelyReadOnly && diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml index 784ce7130028..448a5769f87e 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml @@ -143,6 +143,8 @@ + + diff --git a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs index 742258dbbcf7..ac770728c57d 100644 --- a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs @@ -682,36 +682,31 @@ public BoundIndexOrRangeIndexerPatternValuePlaceholder Update(TypeSymbol type) internal sealed partial class BoundIndexOrRangeIndexerPatternReceiverPlaceholder : BoundValuePlaceholderBase { - public BoundIndexOrRangeIndexerPatternReceiverPlaceholder(SyntaxNode syntax, uint valEscape, TypeSymbol type, bool hasErrors) - : base(BoundKind.IndexOrRangeIndexerPatternReceiverPlaceholder, syntax, type, hasErrors) - { - - RoslynDebug.Assert(type is object, "Field 'type' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); - - this.ValEscape = valEscape; - } - - public BoundIndexOrRangeIndexerPatternReceiverPlaceholder(SyntaxNode syntax, uint valEscape, TypeSymbol type) - : base(BoundKind.IndexOrRangeIndexerPatternReceiverPlaceholder, syntax, type) + public BoundIndexOrRangeIndexerPatternReceiverPlaceholder(SyntaxNode syntax, uint valEscape, BoundExpression expression, TypeSymbol type, bool hasErrors = false) + : base(BoundKind.IndexOrRangeIndexerPatternReceiverPlaceholder, syntax, type, hasErrors || expression.HasErrors()) { + RoslynDebug.Assert(expression is object, "Field 'expression' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); RoslynDebug.Assert(type is object, "Field 'type' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); this.ValEscape = valEscape; + this.Expression = expression; } public new TypeSymbol Type => base.Type!; public uint ValEscape { get; } + + public BoundExpression Expression { get; } [DebuggerStepThrough] public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitIndexOrRangeIndexerPatternReceiverPlaceholder(this); - public BoundIndexOrRangeIndexerPatternReceiverPlaceholder Update(uint valEscape, TypeSymbol type) + public BoundIndexOrRangeIndexerPatternReceiverPlaceholder Update(uint valEscape, BoundExpression expression, TypeSymbol type) { - if (valEscape != this.ValEscape || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) + if (valEscape != this.ValEscape || expression != this.Expression || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) { - var result = new BoundIndexOrRangeIndexerPatternReceiverPlaceholder(this.Syntax, valEscape, type, this.HasErrors); + var result = new BoundIndexOrRangeIndexerPatternReceiverPlaceholder(this.Syntax, valEscape, expression, type, this.HasErrors); result.CopyAttributes(this); return result; } @@ -9597,7 +9592,11 @@ internal abstract partial class BoundTreeWalker : BoundTreeVisitor public override BoundNode? VisitDisposableValuePlaceholder(BoundDisposableValuePlaceholder node) => null; public override BoundNode? VisitObjectOrCollectionValuePlaceholder(BoundObjectOrCollectionValuePlaceholder node) => null; public override BoundNode? VisitIndexOrRangeIndexerPatternValuePlaceholder(BoundIndexOrRangeIndexerPatternValuePlaceholder node) => null; - public override BoundNode? VisitIndexOrRangeIndexerPatternReceiverPlaceholder(BoundIndexOrRangeIndexerPatternReceiverPlaceholder node) => null; + public override BoundNode? VisitIndexOrRangeIndexerPatternReceiverPlaceholder(BoundIndexOrRangeIndexerPatternReceiverPlaceholder node) + { + this.Visit(node.Expression); + return null; + } public override BoundNode? VisitListPatternReceiverPlaceholder(BoundListPatternReceiverPlaceholder node) => null; public override BoundNode? VisitListPatternUnloweredIndexPlaceholder(BoundListPatternUnloweredIndexPlaceholder node) => null; public override BoundNode? VisitSlicePatternReceiverPlaceholder(BoundSlicePatternReceiverPlaceholder node) => null; @@ -10616,8 +10615,9 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor } public override BoundNode? VisitIndexOrRangeIndexerPatternReceiverPlaceholder(BoundIndexOrRangeIndexerPatternReceiverPlaceholder node) { + BoundExpression expression = (BoundExpression)this.Visit(node.Expression); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(node.ValEscape, type); + return node.Update(node.ValEscape, expression, type); } public override BoundNode? VisitListPatternReceiverPlaceholder(BoundListPatternReceiverPlaceholder node) { @@ -12010,13 +12010,18 @@ public NullabilityRewriter(ImmutableDictionary new TreeDumperNode("indexOrRangeIndexerPatternReceiverPlaceholder", null, new TreeDumperNode[] { new TreeDumperNode("valEscape", node.ValEscape, null), + new TreeDumperNode("expression", null, new TreeDumperNode[] { Visit(node.Expression, null) }), new TreeDumperNode("type", node.Type, null), new TreeDumperNode("isSuppressed", node.IsSuppressed, null), new TreeDumperNode("hasErrors", node.HasErrors, null) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs index 3b7de93c09d6..9f504f732549 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs @@ -975,16 +975,20 @@ internal static bool CanBePassedByReference(BoundExpression expr) case BoundKind.IndexOrRangePatternIndexerAccess: return CanBePassedByReference(((BoundIndexOrRangePatternIndexerAccess)expr).IndexerAccess); - // TODO2 I'm not sure about this case BoundKind.IndexOrRangeIndexerPatternReceiverPlaceholder: + case BoundKind.IndexOrRangeIndexerPatternValuePlaceholder: case BoundKind.ListPatternReceiverPlaceholder: + case BoundKind.ListPatternUnloweredIndexPlaceholder: case BoundKind.SlicePatternReceiverPlaceholder: + case BoundKind.SlicePatternUnloweredRangePlaceholder: return true; case BoundKind.Conversion: return expr is BoundConversion { Conversion: { IsInterpolatedStringHandler: true }, Type: { IsValueType: true } }; } + Debug.Assert(expr is not BoundValuePlaceholderBase, $"Placeholder kind {expr.Kind} must be handled explicitly"); + return false; } diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs index 25f2ca4c0ab1..1ef391db932c 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs @@ -588,7 +588,8 @@ private BoundExpression TransformCompoundAssignmentLHS(BoundExpression originalL case BoundKind.IndexOrRangePatternIndexerAccess: { var implicitIndexerAccess = (BoundIndexOrRangePatternIndexerAccess)originalLHS; - if (implicitIndexerAccess.GetRefKind() == RefKind.None) + Debug.Assert(implicitIndexerAccess.Argument.Type!.Equals(_compilation.GetWellKnownType(WellKnownType.System_Index))); + if (implicitIndexerAccess.IndexerAccess.GetRefKind() == RefKind.None) { return TransformImplicitIndexerAccess(implicitIndexerAccess, stores, temps, isDynamicAssignment); } diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs index afc8adcf3bfb..e32d4a0a1328 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs @@ -220,6 +220,7 @@ private BoundSequence VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePat private BoundSequence VisitIndexImplicitIndexerAccess(BoundIndexOrRangePatternIndexerAccess node, bool isLeftOfAssignment) // TODO2 we're not using isLeftOfAssignment { Debug.Assert(node.ArgumentPlaceholders.Length == 1); + Debug.Assert(node.IndexerAccess is BoundIndexerAccess); Debug.Assert(TypeSymbol.Equals( node.Argument.Type, @@ -322,6 +323,7 @@ private BoundExpression MakePatternIndexOffsetExpression( private BoundSequence VisitRangeImplicitIndexerAccess(BoundIndexOrRangePatternIndexerAccess node) { Debug.Assert(node.ArgumentPlaceholders.Length == 2); + Debug.Assert(node.IndexerAccess is BoundCall); Debug.Assert(TypeSymbol.Equals( node.Argument.Type, diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/IndexAndRangeTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/IndexAndRangeTests.cs index 72b824204dc9..27f791be3737 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/IndexAndRangeTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/IndexAndRangeTests.cs @@ -314,11 +314,10 @@ .locals init (int[] V_0, //array public void PatternIndexCompoundOperator_InReadonlyMethod() { var src = @" -using System; -struct S +public struct S { - private readonly int[] _array; - private int _counter; + public readonly int[] _array; + public int _counter; public S(int[] a) { @@ -341,9 +340,16 @@ readonly void M() } } "; - // TODO2 missing "error CS1604: Cannot assign to 'this[^1]' because it is read-only" + // TODO2 why no CS8656 on the indexer? var comp = CreateCompilationWithIndexAndRangeAndSpan(src); - comp.VerifyDiagnostics(); + comp.VerifyDiagnostics( + // (24,9): warning CS8656: Call to non-readonly member 'S.Length.get' from a 'readonly' member results in an implicit copy of 'this'. + // this[^1] += 5; + Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S.Length.get", "this").WithLocation(24, 9), + // (24,9): error CS1604: Cannot assign to 'this[^1]' because it is read-only + // this[^1] += 5; + Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "this[^1]").WithArguments("this[^1]").WithLocation(24, 9) + ); } [Fact] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs index 2c7a58e48a40..92e256ab13be 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs @@ -3016,7 +3016,43 @@ readonly void M(Index i, Range r) _ = this is [2, ..var rest]; } }"; - // Note: no "warning CS8656: Call to non-readonly member ... from a 'readonly' member results in an implicit copy of 'this'" + var comp = CreateCompilationWithIndexAndRange(src); + comp.VerifyDiagnostics( + // (11,13): warning CS8656: Call to non-readonly member 'S.Length.get' from a 'readonly' member results in an implicit copy of 'this'. + // _ = this[i]; // 1, 2 + Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S.Length.get", "this").WithLocation(11, 13), + // (11,13): warning CS8656: Call to non-readonly member 'S.this[int].get' from a 'readonly' member results in an implicit copy of 'this'. + // _ = this[i]; // 1, 2 + Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S.this[int].get", "this").WithLocation(11, 13), + // (12,13): warning CS8656: Call to non-readonly member 'S.Slice(int, int)' from a 'readonly' member results in an implicit copy of 'this'. + // _ = this[r]; // 3, 4 + Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S.Slice(int, int)", "this").WithLocation(12, 13), + // (12,13): warning CS8656: Call to non-readonly member 'S.Length.get' from a 'readonly' member results in an implicit copy of 'this'. + // _ = this[r]; // 3, 4 + Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S.Length.get", "this").WithLocation(12, 13) + ); + } + + [Fact] + public void PatternIndexRangeReadOnly_02() + { + var src = @" +using System; +struct S +{ + public readonly int this[int i] => 0; + public readonly int Length => 0; + public readonly int Slice(int x, int y) => 0; + + readonly void M(Index i, Range r) + { + _ = this[i]; + _ = this[r]; + + _ = this is [1]; + _ = this is [2, ..var rest]; + } +}"; var comp = CreateCompilationWithIndexAndRange(src); comp.VerifyDiagnostics(); } @@ -6297,7 +6333,7 @@ void Test(int[] a) "); } - // PROTOTYPE this test takes 10 seconds to run... + // PROTOTYPE this test takes 7 seconds to run... [Theory] [CombinatorialData] public void Subsumption_Slice_00( From 0f924db704583c238ffc34b3687369f9c42c766e Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Thu, 11 Nov 2021 10:29:13 -0800 Subject: [PATCH 11/29] Redo lowering change. Store receiver in IndexerAccess --- .../Portable/Binder/Binder.ValueChecks.cs | 32 +- .../Portable/Binder/Binder_Expressions.cs | 46 +-- .../Portable/Binder/Binder_Invocation.cs | 2 +- .../CSharp/Portable/Binder/Binder_Patterns.cs | 2 +- .../Portable/Binder/Binder_Statements.cs | 13 +- .../CSharp/Portable/BoundTree/BoundCall.cs | 17 + .../BoundIndexOrRangePatternIndexerAccess.cs | 24 +- .../Portable/BoundTree/BoundIndexerAccess.cs | 15 + .../CSharp/Portable/BoundTree/BoundNodes.xml | 11 +- .../CSharp/Portable/BoundTree/Expression.cs | 2 +- .../Portable/FlowAnalysis/AbstractFlowPass.cs | 20 +- .../Portable/FlowAnalysis/NullableWalker.cs | 5 +- .../Generated/BoundNodes.xml.Generated.cs | 88 +++-- .../Lowering/LocalRewriter/LocalRewriter.cs | 9 - ...ocalRewriter_CompoundAssignmentOperator.cs | 4 +- .../LocalRewriter_IndexerAccess.cs | 176 +++++++--- .../Test/Emit/CodeGen/IndexAndRangeTests.cs | 4 - .../IOperationTests_IIsPatternExpression.cs | 37 ++- .../PatternMatchingTests_ListPatterns.cs | 305 ++++++++++-------- 19 files changed, 469 insertions(+), 343 deletions(-) create mode 100644 src/Compilers/CSharp/Portable/BoundTree/BoundCall.cs create mode 100644 src/Compilers/CSharp/Portable/BoundTree/BoundIndexerAccess.cs diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index 2d225471b984..d9c384c33172 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -597,8 +597,7 @@ internal bool CheckValueKind(SyntaxNode node, BoundExpression expr, BindValueKin return CheckValueKind(node, implicitIndexerAccess.IndexerAccess, valueKind, checkingReceiver, diagnostics); case BoundKind.IndexOrRangeIndexerPatternReceiverPlaceholder: - var receiverPlaceholder = (BoundIndexOrRangeIndexerPatternReceiverPlaceholder)expr; - return CheckValueKind(node, receiverPlaceholder.Expression, valueKind, checkingReceiver, diagnostics); + break; case BoundKind.DeconstructValuePlaceholder: break; @@ -637,35 +636,6 @@ internal bool CheckValueKind(SyntaxNode node, BoundExpression expr, BindValueKin return false; } - private static BoundExpression UnwrapPlaceholdersIfNeeded(BoundExpression? e) - { - switch (e) - { - case null: - return null; - - case BoundIndexOrRangeIndexerPatternReceiverPlaceholder placeholder: - return placeholder.Expression; - - case BoundSlicePatternReceiverPlaceholder: - case BoundListPatternReceiverPlaceholder: - case BoundObjectOrCollectionValuePlaceholder: - case BoundAwaitableValuePlaceholder: - case BoundInterpolatedStringHandlerPlaceholder: - case BoundDisposableValuePlaceholder: - // TODO2 - return e; - - case BoundDeconstructValuePlaceholder: - // PROTOTYPE file issue - return e; - - default: - Debug.Assert(e is not BoundValuePlaceholderBase, $"Placeholder kind {e.Kind} should be explicitly handled"); - return e; - } - } - private static bool CheckNotNamespaceOrType(BoundExpression expr, BindingDiagnosticBag diagnostics) { switch (expr.Kind) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index f0b285d9fd1f..c6e716120da9 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -8074,14 +8074,13 @@ private bool TryBindIndexOrRangeImplicitIndexer( bool argIsIndex = argIsIndexNotRange.Value(); var receiverValEscape = receiverOpt switch { - BoundIndexOrRangeIndexerPatternReceiverPlaceholder { ValEscape: var valEscape } => valEscape, BoundListPatternReceiverPlaceholder { ValEscape: var valEscape } => valEscape, BoundSlicePatternReceiverPlaceholder { ValEscape: var valEscape } => valEscape, _ => GetValEscape(receiverOpt, LocalScopeDepth) }; - var receiverPlaceholder = new BoundIndexOrRangeIndexerPatternReceiverPlaceholder(receiverOpt.Syntax, receiverValEscape, receiverOpt, receiverOpt.Type) { WasCompilerGenerated = true }; - if (!TryBindIndexOrRangeImplicitIndexer(syntax, receiverPlaceholder, receiverType, argIsIndex: argIsIndex, + var receiverPlaceholder = new BoundIndexOrRangeIndexerPatternReceiverPlaceholder(receiverOpt.Syntax, receiverValEscape, receiverOpt.Type) { WasCompilerGenerated = true }; + if (!TryBindIndexOrRangeImplicitIndexerParts(syntax, receiverPlaceholder, receiverOpt, argIsIndex: argIsIndex, out var lengthOrCountAccess, out var indexerOrSliceAccess, out var argumentPlaceholders, diagnostics)) { return false; @@ -8093,11 +8092,10 @@ private bool TryBindIndexOrRangeImplicitIndexer( implicitIndexerAccess = new BoundIndexOrRangePatternIndexerAccess( syntax, - receiverOpt, argument: BindToNaturalType(argument, diagnostics), lengthOrCountAccess: lengthOrCountAccess, - indexerAccess: indexerOrSliceAccess, receiverPlaceholder, + indexerAccess: indexerOrSliceAccess, argumentPlaceholders, indexerOrSliceAccess.Type); @@ -8131,11 +8129,13 @@ void checkWellKnown(WellKnownMember member) /// /// Finds pattern-based implicit indexer and Length/Count property. + /// We'll use the receiver placeholder to bind the length access, but the real + /// receiver to bind the indexer access. /// - private bool TryBindIndexOrRangeImplicitIndexer( + private bool TryBindIndexOrRangeImplicitIndexerParts( SyntaxNode syntax, - BoundExpression? receiverOpt, - TypeSymbol receiverType, + BoundExpression receiverPlaceholder, + BoundExpression receiver, bool argIsIndex, [NotNullWhen(true)] out BoundExpression? lengthOrCountAccess, [NotNullWhen(true)] out BoundExpression? implicitIndexerAccess, @@ -8154,8 +8154,8 @@ private bool TryBindIndexOrRangeImplicitIndexer( var lookupResult = LookupResult.GetInstance(); - if (TryBindLengthOrCount(syntax, receiverOpt, receiverType, out lengthOrCountAccess, diagnostics) && - TryFindIndexOrRangeImplicitIndexer(syntax, lookupResult, receiverOpt, receiverType, argIsIndex, out implicitIndexerAccess, out argumentPlaceholders, diagnostics)) + if (TryBindLengthOrCount(syntax, receiverPlaceholder, out lengthOrCountAccess, diagnostics) && + TryFindIndexOrRangeImplicitIndexer(syntax, lookupResult, receiver, argIsIndex, out implicitIndexerAccess, out argumentPlaceholders, diagnostics)) { CheckValue(lengthOrCountAccess, BindValueKind.RValue, diagnostics); @@ -8172,16 +8172,16 @@ private bool TryBindIndexOrRangeImplicitIndexer( private bool TryBindLengthOrCount( SyntaxNode syntax, - BoundExpression? receiverOpt, - TypeSymbol receiverType, + BoundExpression receiver, [NotNullWhen(true)] out BoundExpression? lengthOrCountAccess, BindingDiagnosticBag diagnostics) { var lookupResult = LookupResult.GetInstance(); - if (TryLookupLengthOrCount(syntax, receiverType, lookupResult, out var lengthOrCountProperty, diagnostics)) + Debug.Assert(receiver.Type is not null); + if (TryLookupLengthOrCount(syntax, receiver.Type, lookupResult, out var lengthOrCountProperty, diagnostics)) { - lengthOrCountAccess = BindPropertyAccess(syntax, receiverOpt, lengthOrCountProperty, diagnostics, lookupResult.Kind, hasErrors: false); + lengthOrCountAccess = BindPropertyAccess(syntax, receiver, lengthOrCountProperty, diagnostics, lookupResult.Kind, hasErrors: false); ReportDiagnosticsIfObsolete(diagnostics, lengthOrCountProperty, syntax, hasBaseReceiver: false); lookupResult.Free(); @@ -8202,13 +8202,13 @@ private bool TryBindLengthOrCount( private bool TryFindIndexOrRangeImplicitIndexer( SyntaxNode syntax, LookupResult lookupResult, - BoundExpression? receiverOpt, - TypeSymbol receiverType, + BoundExpression receiver, bool argIsIndex, [NotNullWhen(true)] out BoundExpression? indexerOrSliceAccess, out ImmutableArray argumentPlaceholders, BindingDiagnosticBag diagnostics) { + Debug.Assert(receiver.Type is not null); var useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); Debug.Assert(lookupResult.IsClear); @@ -8218,7 +8218,7 @@ private bool TryFindIndexOrRangeImplicitIndexer( LookupMembersInType( lookupResult, - receiverType, + receiver.Type, WellKnownMemberNames.Indexer, arity: 0, basesBeingResolved: null, @@ -8242,21 +8242,21 @@ candidate is PropertySymbol property && var analyzedArguments = AnalyzedArguments.GetInstance(); analyzedArguments.Arguments.Add(intPlaceholder); - indexerOrSliceAccess = BindIndexedPropertyAccess(syntax, receiverOpt, ImmutableArray.Create(property), analyzedArguments, diagnostics); + indexerOrSliceAccess = BindIndexedPropertyAccess(syntax, receiver, ImmutableArray.Create(property), analyzedArguments, diagnostics); analyzedArguments.Free(); return true; } } } } - else if (receiverType.SpecialType == SpecialType.System_String) + else if (receiver.Type.SpecialType == SpecialType.System_String) { Debug.Assert(!argIsIndex); // Look for Substring var substring = (MethodSymbol)Compilation.GetSpecialTypeMember(SpecialMember.System_String__Substring); if (substring is object) { - makeCall(syntax, receiverOpt, substring, out indexerOrSliceAccess, out argumentPlaceholders); + makeCall(syntax, receiver, substring, out indexerOrSliceAccess, out argumentPlaceholders); return true; } } @@ -8267,7 +8267,7 @@ candidate is PropertySymbol property && LookupMembersInType( lookupResult, - receiverType, + receiver.Type, WellKnownMemberNames.SliceMethodName, arity: 0, basesBeingResolved: null, @@ -8292,8 +8292,8 @@ method.OriginalDefinition is var original && method.AddUseSiteInfo(ref useSiteInfo); diagnostics.ReportUseSite(method, syntax); ReportDiagnosticsIfObsolete(diagnostics, method, syntax, hasBaseReceiver: false); - CheckImplicitThisCopyInReadOnlyMember(receiverOpt, method, diagnostics); - makeCall(syntax, receiverOpt, method, out indexerOrSliceAccess, out argumentPlaceholders); + CheckImplicitThisCopyInReadOnlyMember(receiver, method, diagnostics); + makeCall(syntax, receiver, method, out indexerOrSliceAccess, out argumentPlaceholders); return true; } } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs index dd4199939de1..3dd80a377c30 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs @@ -1464,7 +1464,7 @@ internal bool CheckImplicitThisCopyInReadOnlyMember(BoundExpression receiver, Me { // For now we are warning only in implicit copy scenarios that are only possible with readonly members. // Eventually we will warn on implicit value copies in more scenarios. See https://github.com/dotnet/roslyn/issues/33968. - if (UnwrapPlaceholdersIfNeeded(receiver) is BoundThisReference && + if (receiver is BoundThisReference && receiver.Type.IsValueType && ContainingMemberOrLambda is MethodSymbol containingMethod && containingMethod.IsEffectivelyReadOnly && diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs index ef9ee92fd422..126fb11e6b47 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs @@ -349,7 +349,7 @@ private bool BindLengthAndIndexerForListPattern(SyntaxNode node, TypeSymbol inpu } else { - hasErrors |= !TryBindLengthOrCount(node, receiverPlaceholder, receiverPlaceholder.Type, out lengthAccess, bindingDiagnostics); + hasErrors |= !TryBindLengthOrCount(node, receiverPlaceholder, out lengthAccess, bindingDiagnostics); } if (lengthAccess is null) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index b8afb9247d94..184931669917 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -1539,6 +1539,13 @@ private BoundAssignmentOperator BindAssignment( internal static PropertySymbol GetPropertySymbol(BoundExpression expr, out BoundExpression receiver, out SyntaxNode propertySyntax) { + if (expr is null) + { + receiver = null; + propertySyntax = null; + return null; + } + PropertySymbol propertySymbol; switch (expr.Kind) { @@ -1559,8 +1566,7 @@ internal static PropertySymbol GetPropertySymbol(BoundExpression expr, out Bound case BoundKind.IndexOrRangePatternIndexerAccess: { var implicitIndexerAccess = (BoundIndexOrRangePatternIndexerAccess)expr; - receiver = implicitIndexerAccess.Receiver; - propertySymbol = GetPropertySymbol(implicitIndexerAccess.IndexerAccess, out _, out propertySyntax); + propertySymbol = GetPropertySymbol(implicitIndexerAccess.IndexerAccess, out receiver, out propertySyntax); } break; default: @@ -1602,7 +1608,8 @@ internal static PropertySymbol GetPropertySymbol(BoundExpression expr, out Bound BoundIndexerAccess indexerAccess => indexerAccess.Indexer, BoundCall call => call.Method, BoundArrayAccess arrayAccess => arrayAccess.ExpressionSymbol, - _ => throw ExceptionUtilities.Unreachable + BoundBadExpression => null, + _ => throw ExceptionUtilities.UnexpectedValue(e.Kind) }; } #nullable disable diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundCall.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundCall.cs new file mode 100644 index 000000000000..af2bc9a55950 --- /dev/null +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundCall.cs @@ -0,0 +1,17 @@ +// 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. + +using System.Diagnostics; + +namespace Microsoft.CodeAnalysis.CSharp +{ + internal partial class BoundCall + { + internal BoundCall WithReceiver(BoundExpression receiver) + { + Debug.Assert(!this.InvokedAsExtensionMethod); + return this.Update(receiver, this.Method, this.Arguments); + } + } +} diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundIndexOrRangePatternIndexerAccess.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundIndexOrRangePatternIndexerAccess.cs index abed543a0465..bfbdb22cd733 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundIndexOrRangePatternIndexerAccess.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundIndexOrRangePatternIndexerAccess.cs @@ -2,14 +2,34 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Diagnostics; +using Roslyn.Utilities; + namespace Microsoft.CodeAnalysis.CSharp { internal partial class BoundIndexOrRangePatternIndexerAccess { internal BoundIndexOrRangePatternIndexerAccess WithLengthOrCountAccess(BoundExpression lengthOrCountAccess) { - return new BoundIndexOrRangePatternIndexerAccess(this.Syntax, this.Receiver, this.Argument, lengthOrCountAccess, - this.IndexerAccess, this.ReceiverPlaceholder, this.ArgumentPlaceholders, this.Type, this.HasErrors); + return new BoundIndexOrRangePatternIndexerAccess(this.Syntax, this.Argument, lengthOrCountAccess, this.ReceiverPlaceholder, + this.IndexerAccess, this.ArgumentPlaceholders, this.Type, this.HasErrors); + } + + // The receiver expression is the receiver of IndexerAccess. + // The LengthOrCountAccess uses a placeholder as receiver. + internal BoundExpression GetReceiver() + { + var receiver = this.IndexerAccess switch + { + BoundArrayAccess { Expression: var r } => r, + BoundIndexerAccess { ReceiverOpt: var r } => r, + BoundCall { ReceiverOpt: var r } => r, + _ => throw ExceptionUtilities.UnexpectedValue(this.IndexerAccess.Kind) + }; + + Debug.Assert(receiver is not null); + return receiver; } + } } diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundIndexerAccess.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundIndexerAccess.cs new file mode 100644 index 000000000000..cb3134de33b2 --- /dev/null +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundIndexerAccess.cs @@ -0,0 +1,15 @@ +// 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 BoundIndexerAccess + { + internal BoundIndexerAccess WithReceiver(BoundExpression receiver) + { + return this.Update(receiver, this.Indexer, this.Arguments, this.ArgumentNamesOpt, + this.ArgumentRefKindsOpt, this.Expanded, this.ArgsToParamsOpt, this.DefaultArguments, this.Type); + } + } +} diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml index 448a5769f87e..a29a6ba956e3 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml @@ -143,8 +143,6 @@ - - @@ -2061,14 +2059,17 @@ - - - + + + + + + diff --git a/src/Compilers/CSharp/Portable/BoundTree/Expression.cs b/src/Compilers/CSharp/Portable/BoundTree/Expression.cs index ab4a498be4c8..05ecbe01376d 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/Expression.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/Expression.cs @@ -177,7 +177,7 @@ internal partial class BoundPassByCopy internal partial class BoundIndexOrRangePatternIndexerAccess { - protected override ImmutableArray Children => ImmutableArray.Create(Receiver, Argument); + protected override ImmutableArray Children => ImmutableArray.Create(this.GetReceiver(), Argument); } internal partial class BoundFunctionPointerInvocation : IBoundInvalidNode diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs index d3a0c2cc7c1e..1a921557fc6a 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs @@ -1390,21 +1390,11 @@ public override BoundNode VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRang { // Index or Range implicit indexers evaluate the following in order: // 1. The receiver - // 2. The Count or Length method off the receiver - // 3. The argument to the access - // 4. The pattern method - VisitRvalue(node.Receiver); - // TODO2 - //var method = GetReadMethod(node.LengthOrCountProperty); - //VisitReceiverAfterCall(node.Receiver, method); - //VisitRvalue(node.Argument); - //method = node.PatternSymbol switch - //{ - // PropertySymbol p => GetReadMethod(p), - // MethodSymbol m => m, - // _ => throw ExceptionUtilities.UnexpectedValue(node.PatternSymbol) - //}; - //VisitReceiverAfterCall(node.Receiver, method); + // 2. The argument to the access + // 3. The Count or Length method off the receiver + // 4. The underlying indexer access or method call + VisitRvalue(node.GetReceiver()); + VisitRvalue(node.Argument); return null; } diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index acb5f1116d42..c715a5005b05 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -8697,15 +8697,14 @@ private TypeWithAnnotations GetDeclaredParameterResult(ParameterSymbol parameter public override BoundNode? VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node) { - BoundExpression receiver = node.Receiver; + BoundExpression receiver = node.GetReceiver(); var receiverType = VisitRvalueWithState(receiver).Type; // https://github.com/dotnet/roslyn/issues/30598: Mark receiver as not null // after indices have been visited, and only if the receiver has not changed. _ = CheckPossibleNullReceiver(receiver); - VisitRvalue(node.LengthOrCountAccess); - VisitRvalue(node.Argument); + VisitRvalue(node.LengthOrCountAccess); VisitRvalue(node.IndexerAccess); return null; } diff --git a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs index ac770728c57d..d34324d26f46 100644 --- a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs @@ -682,31 +682,36 @@ public BoundIndexOrRangeIndexerPatternValuePlaceholder Update(TypeSymbol type) internal sealed partial class BoundIndexOrRangeIndexerPatternReceiverPlaceholder : BoundValuePlaceholderBase { - public BoundIndexOrRangeIndexerPatternReceiverPlaceholder(SyntaxNode syntax, uint valEscape, BoundExpression expression, TypeSymbol type, bool hasErrors = false) - : base(BoundKind.IndexOrRangeIndexerPatternReceiverPlaceholder, syntax, type, hasErrors || expression.HasErrors()) + public BoundIndexOrRangeIndexerPatternReceiverPlaceholder(SyntaxNode syntax, uint valEscape, TypeSymbol type, bool hasErrors) + : base(BoundKind.IndexOrRangeIndexerPatternReceiverPlaceholder, syntax, type, hasErrors) + { + + RoslynDebug.Assert(type is object, "Field 'type' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); + + this.ValEscape = valEscape; + } + + public BoundIndexOrRangeIndexerPatternReceiverPlaceholder(SyntaxNode syntax, uint valEscape, TypeSymbol type) + : base(BoundKind.IndexOrRangeIndexerPatternReceiverPlaceholder, syntax, type) { - RoslynDebug.Assert(expression is object, "Field 'expression' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); RoslynDebug.Assert(type is object, "Field 'type' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); this.ValEscape = valEscape; - this.Expression = expression; } public new TypeSymbol Type => base.Type!; public uint ValEscape { get; } - - public BoundExpression Expression { get; } [DebuggerStepThrough] public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitIndexOrRangeIndexerPatternReceiverPlaceholder(this); - public BoundIndexOrRangeIndexerPatternReceiverPlaceholder Update(uint valEscape, BoundExpression expression, TypeSymbol type) + public BoundIndexOrRangeIndexerPatternReceiverPlaceholder Update(uint valEscape, TypeSymbol type) { - if (valEscape != this.ValEscape || expression != this.Expression || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) + if (valEscape != this.ValEscape || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) { - var result = new BoundIndexOrRangeIndexerPatternReceiverPlaceholder(this.Syntax, valEscape, expression, type, this.HasErrors); + var result = new BoundIndexOrRangeIndexerPatternReceiverPlaceholder(this.Syntax, valEscape, type, this.HasErrors); result.CopyAttributes(this); return result; } @@ -7299,47 +7304,43 @@ public BoundIndexerAccess Update(BoundExpression? receiverOpt, PropertySymbol in internal sealed partial class BoundIndexOrRangePatternIndexerAccess : BoundExpression { - public BoundIndexOrRangePatternIndexerAccess(SyntaxNode syntax, BoundExpression receiver, BoundExpression argument, BoundExpression? lengthOrCountAccess, BoundExpression indexerAccess, BoundIndexOrRangeIndexerPatternReceiverPlaceholder receiverPlaceholder, ImmutableArray argumentPlaceholders, TypeSymbol type, bool hasErrors = false) - : base(BoundKind.IndexOrRangePatternIndexerAccess, syntax, type, hasErrors || receiver.HasErrors() || argument.HasErrors() || lengthOrCountAccess.HasErrors() || indexerAccess.HasErrors() || receiverPlaceholder.HasErrors() || argumentPlaceholders.HasErrors()) + public BoundIndexOrRangePatternIndexerAccess(SyntaxNode syntax, BoundExpression argument, BoundExpression? lengthOrCountAccess, BoundIndexOrRangeIndexerPatternReceiverPlaceholder receiverPlaceholder, BoundExpression indexerAccess, ImmutableArray argumentPlaceholders, TypeSymbol type, bool hasErrors = false) + : base(BoundKind.IndexOrRangePatternIndexerAccess, syntax, type, hasErrors || argument.HasErrors() || lengthOrCountAccess.HasErrors() || receiverPlaceholder.HasErrors() || indexerAccess.HasErrors() || argumentPlaceholders.HasErrors()) { - RoslynDebug.Assert(receiver is object, "Field 'receiver' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); RoslynDebug.Assert(argument is object, "Field 'argument' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); - RoslynDebug.Assert(indexerAccess is object, "Field 'indexerAccess' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); RoslynDebug.Assert(receiverPlaceholder is object, "Field 'receiverPlaceholder' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); + RoslynDebug.Assert(indexerAccess is object, "Field 'indexerAccess' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); RoslynDebug.Assert(!argumentPlaceholders.IsDefault, "Field 'argumentPlaceholders' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); RoslynDebug.Assert(type is object, "Field 'type' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); - this.Receiver = receiver; this.Argument = argument; this.LengthOrCountAccess = lengthOrCountAccess; - this.IndexerAccess = indexerAccess; this.ReceiverPlaceholder = receiverPlaceholder; + this.IndexerAccess = indexerAccess; this.ArgumentPlaceholders = argumentPlaceholders; } public new TypeSymbol Type => base.Type!; - public BoundExpression Receiver { get; } - public BoundExpression Argument { get; } public BoundExpression? LengthOrCountAccess { get; } - public BoundExpression IndexerAccess { get; } - public BoundIndexOrRangeIndexerPatternReceiverPlaceholder ReceiverPlaceholder { get; } + public BoundExpression IndexerAccess { get; } + public ImmutableArray ArgumentPlaceholders { get; } [DebuggerStepThrough] public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitIndexOrRangePatternIndexerAccess(this); - public BoundIndexOrRangePatternIndexerAccess Update(BoundExpression receiver, BoundExpression argument, BoundExpression? lengthOrCountAccess, BoundExpression indexerAccess, BoundIndexOrRangeIndexerPatternReceiverPlaceholder receiverPlaceholder, ImmutableArray argumentPlaceholders, TypeSymbol type) + public BoundIndexOrRangePatternIndexerAccess Update(BoundExpression argument, BoundExpression? lengthOrCountAccess, BoundIndexOrRangeIndexerPatternReceiverPlaceholder receiverPlaceholder, BoundExpression indexerAccess, ImmutableArray argumentPlaceholders, TypeSymbol type) { - if (receiver != this.Receiver || argument != this.Argument || lengthOrCountAccess != this.LengthOrCountAccess || indexerAccess != this.IndexerAccess || receiverPlaceholder != this.ReceiverPlaceholder || argumentPlaceholders != this.ArgumentPlaceholders || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) + if (argument != this.Argument || lengthOrCountAccess != this.LengthOrCountAccess || receiverPlaceholder != this.ReceiverPlaceholder || indexerAccess != this.IndexerAccess || argumentPlaceholders != this.ArgumentPlaceholders || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) { - var result = new BoundIndexOrRangePatternIndexerAccess(this.Syntax, receiver, argument, lengthOrCountAccess, indexerAccess, receiverPlaceholder, argumentPlaceholders, type, this.HasErrors); + var result = new BoundIndexOrRangePatternIndexerAccess(this.Syntax, argument, lengthOrCountAccess, receiverPlaceholder, indexerAccess, argumentPlaceholders, type, this.HasErrors); result.CopyAttributes(this); return result; } @@ -9592,11 +9593,7 @@ internal abstract partial class BoundTreeWalker : BoundTreeVisitor public override BoundNode? VisitDisposableValuePlaceholder(BoundDisposableValuePlaceholder node) => null; public override BoundNode? VisitObjectOrCollectionValuePlaceholder(BoundObjectOrCollectionValuePlaceholder node) => null; public override BoundNode? VisitIndexOrRangeIndexerPatternValuePlaceholder(BoundIndexOrRangeIndexerPatternValuePlaceholder node) => null; - public override BoundNode? VisitIndexOrRangeIndexerPatternReceiverPlaceholder(BoundIndexOrRangeIndexerPatternReceiverPlaceholder node) - { - this.Visit(node.Expression); - return null; - } + public override BoundNode? VisitIndexOrRangeIndexerPatternReceiverPlaceholder(BoundIndexOrRangeIndexerPatternReceiverPlaceholder node) => null; public override BoundNode? VisitListPatternReceiverPlaceholder(BoundListPatternReceiverPlaceholder node) => null; public override BoundNode? VisitListPatternUnloweredIndexPlaceholder(BoundListPatternUnloweredIndexPlaceholder node) => null; public override BoundNode? VisitSlicePatternReceiverPlaceholder(BoundSlicePatternReceiverPlaceholder node) => null; @@ -10373,11 +10370,10 @@ internal abstract partial class BoundTreeWalker : BoundTreeVisitor } public override BoundNode? VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node) { - this.Visit(node.Receiver); this.Visit(node.Argument); this.Visit(node.LengthOrCountAccess); - this.Visit(node.IndexerAccess); this.Visit(node.ReceiverPlaceholder); + this.Visit(node.IndexerAccess); this.VisitList(node.ArgumentPlaceholders); return null; } @@ -10615,9 +10611,8 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor } public override BoundNode? VisitIndexOrRangeIndexerPatternReceiverPlaceholder(BoundIndexOrRangeIndexerPatternReceiverPlaceholder node) { - BoundExpression expression = (BoundExpression)this.Visit(node.Expression); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(node.ValEscape, expression, type); + return node.Update(node.ValEscape, type); } public override BoundNode? VisitListPatternReceiverPlaceholder(BoundListPatternReceiverPlaceholder node) { @@ -11639,14 +11634,13 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor } public override BoundNode? VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node) { - BoundExpression receiver = (BoundExpression)this.Visit(node.Receiver); BoundExpression argument = (BoundExpression)this.Visit(node.Argument); BoundExpression? lengthOrCountAccess = (BoundExpression?)this.Visit(node.LengthOrCountAccess); - BoundExpression indexerAccess = (BoundExpression)this.Visit(node.IndexerAccess); BoundIndexOrRangeIndexerPatternReceiverPlaceholder receiverPlaceholder = (BoundIndexOrRangeIndexerPatternReceiverPlaceholder)this.Visit(node.ReceiverPlaceholder); + BoundExpression indexerAccess = (BoundExpression)this.Visit(node.IndexerAccess); ImmutableArray argumentPlaceholders = this.VisitList(node.ArgumentPlaceholders); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(receiver, argument, lengthOrCountAccess, indexerAccess, receiverPlaceholder, argumentPlaceholders, type); + return node.Update(argument, lengthOrCountAccess, receiverPlaceholder, indexerAccess, argumentPlaceholders, type); } public override BoundNode? VisitDynamicIndexerAccess(BoundDynamicIndexerAccess node) { @@ -12010,18 +12004,13 @@ public NullabilityRewriter(ImmutableDictionary argumentPlaceholders = this.VisitList(node.ArgumentPlaceholders); BoundIndexOrRangePatternIndexerAccess updatedNode; if (_updatedNullabilities.TryGetValue(node, out (NullabilityInfo Info, TypeSymbol? Type) infoAndType)) { - updatedNode = node.Update(receiver, argument, lengthOrCountAccess, indexerAccess, receiverPlaceholder, argumentPlaceholders, infoAndType.Type!); + updatedNode = node.Update(argument, lengthOrCountAccess, receiverPlaceholder, indexerAccess, argumentPlaceholders, infoAndType.Type!); updatedNode.TopLevelNullability = infoAndType.Info; } else { - updatedNode = node.Update(receiver, argument, lengthOrCountAccess, indexerAccess, receiverPlaceholder, argumentPlaceholders, node.Type); + updatedNode = node.Update(argument, lengthOrCountAccess, receiverPlaceholder, indexerAccess, argumentPlaceholders, node.Type); } return updatedNode; } @@ -14550,7 +14538,6 @@ private BoundTreeDumperNodeProducer() public override TreeDumperNode VisitIndexOrRangeIndexerPatternReceiverPlaceholder(BoundIndexOrRangeIndexerPatternReceiverPlaceholder node, object? arg) => new TreeDumperNode("indexOrRangeIndexerPatternReceiverPlaceholder", null, new TreeDumperNode[] { new TreeDumperNode("valEscape", node.ValEscape, null), - new TreeDumperNode("expression", null, new TreeDumperNode[] { Visit(node.Expression, null) }), new TreeDumperNode("type", node.Type, null), new TreeDumperNode("isSuppressed", node.IsSuppressed, null), new TreeDumperNode("hasErrors", node.HasErrors, null) @@ -16135,11 +16122,10 @@ private BoundTreeDumperNodeProducer() ); public override TreeDumperNode VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node, object? arg) => new TreeDumperNode("indexOrRangePatternIndexerAccess", null, new TreeDumperNode[] { - new TreeDumperNode("receiver", null, new TreeDumperNode[] { Visit(node.Receiver, null) }), new TreeDumperNode("argument", null, new TreeDumperNode[] { Visit(node.Argument, null) }), new TreeDumperNode("lengthOrCountAccess", null, new TreeDumperNode[] { Visit(node.LengthOrCountAccess, null) }), - new TreeDumperNode("indexerAccess", null, new TreeDumperNode[] { Visit(node.IndexerAccess, null) }), new TreeDumperNode("receiverPlaceholder", null, new TreeDumperNode[] { Visit(node.ReceiverPlaceholder, null) }), + new TreeDumperNode("indexerAccess", null, new TreeDumperNode[] { Visit(node.IndexerAccess, null) }), new TreeDumperNode("argumentPlaceholders", null, from x in node.ArgumentPlaceholders select Visit(x, null)), new TreeDumperNode("type", node.Type, null), new TreeDumperNode("isSuppressed", node.IsSuppressed, null), diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs index 94403a73d16e..8f4953792fc9 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs @@ -414,15 +414,6 @@ private BoundExpression PlaceholderReplacement(BoundValuePlaceholderBase placeho return value; } - private BoundExpression UnwrapPlaceholderIfNeeded(BoundExpression expr) - { - if (expr is BoundValuePlaceholderBase placeholder) - { - expr = PlaceholderReplacement(placeholder); - } - return expr; - } - [Conditional("DEBUG")] private static void AssertPlaceholderReplacement(BoundValuePlaceholderBase placeholder, BoundExpression value) { diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs index 1ef391db932c..69925358154b 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs @@ -588,7 +588,9 @@ private BoundExpression TransformCompoundAssignmentLHS(BoundExpression originalL case BoundKind.IndexOrRangePatternIndexerAccess: { var implicitIndexerAccess = (BoundIndexOrRangePatternIndexerAccess)originalLHS; - Debug.Assert(implicitIndexerAccess.Argument.Type!.Equals(_compilation.GetWellKnownType(WellKnownType.System_Index))); + Debug.Assert(implicitIndexerAccess.Argument.Type!.Equals(_compilation.GetWellKnownType(WellKnownType.System_Index)) + || implicitIndexerAccess.Argument.Type!.Equals(_compilation.GetWellKnownType(WellKnownType.System_Range))); + if (implicitIndexerAccess.IndexerAccess.GetRefKind() == RefKind.None) { return TransformImplicitIndexerAccess(implicitIndexerAccess, stores, temps, isDynamicAssignment); diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs index 960c3850d0da..bd32d6b3fe4e 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Immutable; using System.Diagnostics; +using System.Linq; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.PooledObjects; @@ -117,6 +118,8 @@ private BoundExpression MakeIndexerAccess( // This is an indexer set access. We return a BoundIndexerAccess node here. // This node will be rewritten with MakePropertyAssignment when rewriting the enclosing BoundAssignmentOperator. + arguments = unwrapPlaceholdersIfNeeded(arguments); + return oldNodeOpt != null ? oldNodeOpt.Update(rewrittenReceiver, indexer, arguments, argumentNamesOpt, argumentRefKindsOpt, expanded, argsToParamsOpt, defaultArguments, type) : new BoundIndexerAccess(syntax, rewrittenReceiver, indexer, arguments, argumentNamesOpt, argumentRefKindsOpt, expanded, argsToParamsOpt, defaultArguments, type); @@ -160,6 +163,56 @@ private BoundExpression MakeIndexerAccess( type); } } + + ImmutableArray unwrapPlaceholdersIfNeeded(ImmutableArray arguments) + { + if (!arguments.Any(a => a is BoundIndexOrRangeIndexerPatternValuePlaceholder)) + { + return arguments; + } + + return arguments.SelectAsArray(a => unwrapPlaceholderIfNeeded(a)); + } + + BoundExpression unwrapPlaceholderIfNeeded(BoundExpression argument) + { + if (argument is BoundIndexOrRangeIndexerPatternValuePlaceholder placeholder) + { + return PlaceholderReplacement(placeholder); + } + + return argument; + } + } + + public override BoundNode? VisitListPatternUnloweredIndexPlaceholder(BoundListPatternUnloweredIndexPlaceholder node) + { + return Visit(PlaceholderReplacement(node)); + } + + public override BoundNode? VisitListPatternReceiverPlaceholder(BoundListPatternReceiverPlaceholder node) + { + return PlaceholderReplacement(node); + } + + public override BoundNode? VisitSlicePatternUnloweredRangePlaceholder(BoundSlicePatternUnloweredRangePlaceholder node) + { + return Visit(PlaceholderReplacement(node)); + } + + public override BoundNode? VisitSlicePatternReceiverPlaceholder(BoundSlicePatternReceiverPlaceholder node) + { + return PlaceholderReplacement(node); + } + + public override BoundNode? VisitIndexOrRangeIndexerPatternReceiverPlaceholder(BoundIndexOrRangeIndexerPatternReceiverPlaceholder node) + { + return PlaceholderReplacement(node); + } + + public override BoundNode? VisitIndexOrRangeIndexerPatternValuePlaceholder(BoundIndexOrRangeIndexerPatternValuePlaceholder node) + { + return PlaceholderReplacement(node); } public override BoundNode VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node) @@ -174,13 +227,7 @@ private BoundSequence VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePat _compilation.GetWellKnownType(WellKnownType.System_Index), TypeCompareKind.ConsiderEverything)) { - return VisitIndexPatternIndexerAccess( - node.Syntax, - node.Receiver, - node.LengthOrCountProperty, - (PropertySymbol)node.PatternSymbol, - node.Argument, - isLeftOfAssignment: isLeftOfAssignment); + return VisitIndexPatternIndexerAccess(node, isLeftOfAssignment: isLeftOfAssignment); } else { @@ -188,28 +235,28 @@ private BoundSequence VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePat node.Argument.Type, _compilation.GetWellKnownType(WellKnownType.System_Range), TypeCompareKind.ConsiderEverything)); - return VisitRangePatternIndexerAccess( - node.Receiver, - node.LengthOrCountProperty, - (MethodSymbol)node.PatternSymbol, - node.Argument); + Debug.Assert(!isLeftOfAssignment || node.IndexerAccess.GetRefKind() == RefKind.Ref); + + return VisitRangePatternIndexerAccess(node); } } - - private BoundSequence VisitIndexPatternIndexerAccess( - SyntaxNode syntax, - BoundExpression receiver, - PropertySymbol lengthOrCountProperty, - PropertySymbol intIndexer, - BoundExpression argument, - bool isLeftOfAssignment) + private BoundSequence VisitIndexPatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node, bool isLeftOfAssignment) { + Debug.Assert(node.ArgumentPlaceholders.Length == 1); + Debug.Assert(node.IndexerAccess is BoundIndexerAccess); + + Debug.Assert(TypeSymbol.Equals( + node.Argument.Type, + _compilation.GetWellKnownType(WellKnownType.System_Index), + TypeCompareKind.ConsiderEverything)); + var F = _factory; var locals = ArrayBuilder.GetInstance(2); var sideeffects = ArrayBuilder.GetInstance(2); + var receiver = node.GetReceiver(); Debug.Assert(receiver.Type is { }); var receiverLocal = F.StoreToTemp( VisitExpression(receiver), @@ -219,9 +266,11 @@ private BoundSequence VisitIndexPatternIndexerAccess( locals.Add(receiverLocal.LocalSymbol); sideeffects.Add(receiverStore); - BoundExpression makeOffsetInput = DetermineMakePatternIndexOffsetExpressionStrategy(argument, out PatternIndexOffsetLoweringStrategy strategy); - BoundExpression indexAccess; + BoundExpression makeOffsetInput = DetermineMakePatternIndexOffsetExpressionStrategy(node.Argument, out PatternIndexOffsetLoweringStrategy strategy); + BoundExpression integerArgument; + var receiverPlaceholder = node.ReceiverPlaceholder; + AddPlaceholderReplacement(receiverPlaceholder, receiverLocal); switch (strategy) { case PatternIndexOffsetLoweringStrategy.SubtractFromLength: @@ -233,37 +282,33 @@ private BoundSequence VisitIndexPatternIndexerAccess( sideeffects.Add(inputStore); } - indexAccess = MakePatternIndexOffsetExpression(makeOffsetInput, F.Property(receiverLocal, lengthOrCountProperty), strategy); + integerArgument = MakePatternIndexOffsetExpression(makeOffsetInput, VisitExpression(node.LengthOrCountAccess), strategy); break; case PatternIndexOffsetLoweringStrategy.UseAsIs: - indexAccess = MakePatternIndexOffsetExpression(makeOffsetInput, lengthAccess: null, strategy); + integerArgument = MakePatternIndexOffsetExpression(makeOffsetInput, lengthAccess: null, strategy); break; case PatternIndexOffsetLoweringStrategy.UseGetOffsetAPI: - indexAccess = MakePatternIndexOffsetExpression(makeOffsetInput, F.Property(receiverLocal, lengthOrCountProperty), strategy); + integerArgument = MakePatternIndexOffsetExpression(makeOffsetInput, VisitExpression(node.LengthOrCountAccess), strategy); break; default: throw ExceptionUtilities.UnexpectedValue(strategy); } + RemovePlaceholderReplacement(receiverPlaceholder); + + var indexerAccess = (BoundIndexerAccess)node.IndexerAccess; + Debug.Assert(node.ArgumentPlaceholders.Length == 1); + var argumentPlaceholder = node.ArgumentPlaceholders[0]; + AddPlaceholderReplacement(argumentPlaceholder, integerArgument); + var rewrittenIndexerAccess = VisitIndexerAccess(indexerAccess.WithReceiver(receiverLocal), isLeftOfAssignment); + RemovePlaceholderReplacement(argumentPlaceholder); return (BoundSequence)F.Sequence( locals.ToImmutableAndFree(), sideeffects.ToImmutableAndFree(), - MakeIndexerAccess( - syntax, - receiverLocal, - intIndexer, - ImmutableArray.Create(indexAccess), - default, - default, - expanded: false, - argsToParamsOpt: default, - defaultArguments: default, - intIndexer.Type, - oldNodeOpt: null, - isLeftOfAssignment)); + rewrittenIndexerAccess); } /// @@ -349,6 +394,11 @@ private BoundExpression DetermineMakePatternIndexOffsetExpressionStrategy( _compilation.GetWellKnownType(WellKnownType.System_Index), TypeCompareKind.ConsiderEverything)); + if (unloweredExpr is BoundListPatternUnloweredIndexPlaceholder placeholder) + { + unloweredExpr = PlaceholderReplacement(placeholder); + } + if (unloweredExpr is BoundFromEndIndexExpression hatExpression) { // If the System.Index argument is `^index`, we can replace the @@ -372,14 +422,13 @@ private BoundExpression DetermineMakePatternIndexOffsetExpressionStrategy( } } - private BoundSequence VisitRangePatternIndexerAccess( - BoundExpression receiver, - PropertySymbol lengthOrCountProperty, - MethodSymbol sliceMethod, - BoundExpression rangeArg) + private BoundSequence VisitRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node) { + Debug.Assert(node.ArgumentPlaceholders.Length == 2); + Debug.Assert(node.IndexerAccess is BoundCall); + Debug.Assert(TypeSymbol.Equals( - rangeArg.Type, + node.Argument.Type, _compilation.GetWellKnownType(WellKnownType.System_Range), TypeCompareKind.ConsiderEverything)); @@ -393,6 +442,9 @@ private BoundSequence VisitRangePatternIndexerAccess( var F = _factory; + var receiver = node.GetReceiver(); + var rangeArg = node.Argument; + var localsBuilder = ArrayBuilder.GetInstance(); var sideEffectsBuilder = ArrayBuilder.GetInstance(); @@ -401,6 +453,11 @@ private BoundSequence VisitRangePatternIndexerAccess( localsBuilder.Add(receiverLocal.LocalSymbol); sideEffectsBuilder.Add(receiverStore); + if (rangeArg is BoundSlicePatternUnloweredRangePlaceholder placeholder) + { + rangeArg = PlaceholderReplacement(placeholder); + } + BoundExpression startExpr; BoundExpression rangeSizeExpr; if (rangeArg is BoundRangeExpression rangeExpr) @@ -525,7 +582,7 @@ private BoundSequence VisitRangePatternIndexerAccess( if ((rewriteFlags & useLength) != 0) { - lengthAccess = F.Property(receiverLocal, lengthOrCountProperty); + lengthAccess = rewriteLengthAccess(node, receiverLocal); if ((rewriteFlags & captureLength) != 0) { @@ -567,7 +624,9 @@ private BoundSequence VisitRangePatternIndexerAccess( localsBuilder.Add(rangeLocal.LocalSymbol); sideEffectsBuilder.Add(rangeStore); - var lengthLocal = F.StoreToTemp(F.Property(receiverLocal, lengthOrCountProperty), out var lengthStore); + var lengthAccess = rewriteLengthAccess(node, receiverLocal); + + var lengthLocal = F.StoreToTemp(lengthAccess, out var lengthStore); localsBuilder.Add(lengthLocal.LocalSymbol); sideEffectsBuilder.Add(lengthStore); @@ -596,10 +655,33 @@ private BoundSequence VisitRangePatternIndexerAccess( rangeSizeExpr = rangeSizeLocal; } + Debug.Assert(node.ArgumentPlaceholders.Length == 2); + AddPlaceholderReplacement(node.ArgumentPlaceholders[0], startExpr); + AddPlaceholderReplacement(node.ArgumentPlaceholders[1], rangeSizeExpr); + + var sliceCall = (BoundCall)node.IndexerAccess; + var rewrittenIndexerAccess = VisitExpression(sliceCall.WithReceiver(receiverLocal)); + + RemovePlaceholderReplacement(node.ArgumentPlaceholders[0]); + RemovePlaceholderReplacement(node.ArgumentPlaceholders[1]); + return (BoundSequence)F.Sequence( localsBuilder.ToImmutableAndFree(), sideEffectsBuilder.ToImmutableAndFree(), - F.Call(receiverLocal, sliceMethod, startExpr, rangeSizeExpr)); + rewrittenIndexerAccess); + + BoundExpression rewriteLengthAccess(BoundIndexOrRangePatternIndexerAccess node, BoundLocal receiverLocal) + { + var receiverPlaceholder = node.ReceiverPlaceholder; + AddPlaceholderReplacement(receiverPlaceholder, receiverLocal); + + Debug.Assert(node.LengthOrCountAccess is not null); + var lengthAccess = VisitExpression(node.LengthOrCountAccess); + + RemovePlaceholderReplacement(receiverPlaceholder); + + return lengthAccess; + } } } } diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/IndexAndRangeTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/IndexAndRangeTests.cs index bbf981e30c99..d8b2632009b7 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/IndexAndRangeTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/IndexAndRangeTests.cs @@ -340,12 +340,8 @@ readonly void M() } } "; - // TODO2 why no CS8656 on the indexer? var comp = CreateCompilationWithIndexAndRangeAndSpan(src); comp.VerifyDiagnostics( - // (24,9): warning CS8656: Call to non-readonly member 'S.Length.get' from a 'readonly' member results in an implicit copy of 'this'. - // this[^1] += 5; - Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S.Length.get", "this").WithLocation(24, 9), // (24,9): error CS1604: Cannot assign to 'this[^1]' because it is read-only // this[^1] += 5; Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "this[^1]").WithArguments("this[^1]").WithLocation(24, 9) diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs index 01f90e7d4127..4d3a5caff352 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs @@ -2114,7 +2114,7 @@ void M(int[] o) "; var expectedDiagnostics = DiagnosticDescription.None; - var comp = CreateCompilation(source); + var comp = CreateCompilationWithIndex(source); VerifyOperationTreeAndDiagnosticsForTest(comp, expectedOperationTree, expectedDiagnostics); } @@ -2236,14 +2236,17 @@ void M() Value: IInstanceReferenceOperation (ReferenceKind: ContainingTypeInstance) (OperationKind.InstanceReference, Type: X) (Syntax: 'this') Pattern: - IListPatternOperation (OperationKind.ListPattern, Type: null, IsInvalid) (Syntax: '[]') (InputType: X, NarrowedType: X, DeclaredSymbol: null, LengthSymbol: null, IndexerSymbol: null) + IListPatternOperation (OperationKind.ListPattern, Type: null, IsInvalid) (Syntax: '[]') (InputType: X, NarrowedType: X, DeclaredSymbol: null, LengthSymbol: null, IndexerSymbol: System.Int32 X.this[System.Int32 i] { get; }) Patterns (0) "; var expectedDiagnostics = new[] { - // (8,31): error CS9200: List patterns may not be used for a value of type 'X'. + // (8,31): error CS8977: List patterns may not be used for a value of type 'X'. + // _ = /**/this is []/**/; + Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[]").WithArguments("X").WithLocation(8, 31), + // (8,31): error CS0518: Predefined type 'System.Index' is not defined or imported // _ = /**/this is []/**/; - Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[]").WithArguments("X").WithLocation(8, 31) + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "[]").WithArguments("System.Index").WithLocation(8, 31) }; var comp = CreateCompilation(source); @@ -2275,12 +2278,12 @@ void M() "; var expectedDiagnostics = new[] { - // (8,31): error CS9200: List patterns may not be used for a value of type 'X'. + // (8,31): error CS0021: Cannot apply indexing with [] to an expression of type 'X' // _ = /**/this is []/**/; - Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[]").WithArguments("X").WithLocation(8, 31) + Diagnostic(ErrorCode.ERR_BadIndexLHS, "[]").WithArguments("X").WithLocation(8, 31) }; - var comp = CreateCompilation(source); + var comp = CreateCompilationWithIndex(source); VerifyOperationTreeAndDiagnosticsForTest(comp, expectedOperationTree, expectedDiagnostics); } @@ -2307,20 +2310,20 @@ void M() Pattern: IListPatternOperation (OperationKind.ListPattern, Type: null, IsInvalid) (Syntax: '[.. 0]') (InputType: X, NarrowedType: X, DeclaredSymbol: null, LengthSymbol: System.Int32 X.Count { get; }, IndexerSymbol: System.Int32 X.this[System.Int32 i] { get; }) Patterns (1): - ISlicePatternOperation (OperationKind.SlicePattern, Type: null, IsInvalid) (Syntax: '.. 0') (InputType: X, NarrowedType: X, SliceSymbol: null + ISlicePatternOperation (OperationKind.SlicePattern, Type: null, IsInvalid) (Syntax: '.. 0') (InputType: X, NarrowedType: X, SliceSymbol: System.Int32 X.this[System.Int32 i] { get; } Pattern: - IConstantPatternOperation (OperationKind.ConstantPattern, Type: null, IsInvalid) (Syntax: '0') (InputType: ?, NarrowedType: System.Int32) + IConstantPatternOperation (OperationKind.ConstantPattern, Type: null, IsInvalid) (Syntax: '0') (InputType: System.Int32, NarrowedType: System.Int32) Value: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0, IsInvalid) (Syntax: '0') "; var expectedDiagnostics = new[] { - // (9,32): error CS9201: Slice patterns may not be used for a value of type 'X'. + // (9,32): error CS1503: Argument 1: cannot convert from 'System.Range' to 'int' // _ = /**/this is [.. 0]/**/; - Diagnostic(ErrorCode.ERR_UnsupportedTypeForSlicePattern, ".. 0").WithArguments("X").WithLocation(9, 32) + Diagnostic(ErrorCode.ERR_BadArgType, ".. 0").WithArguments("1", "System.Range", "int").WithLocation(9, 32) }; - var comp = CreateCompilation(source); + var comp = CreateCompilationWithIndexAndRange(source); VerifyOperationTreeAndDiagnosticsForTest(comp, expectedOperationTree, expectedDiagnostics); } @@ -2375,7 +2378,7 @@ void M(int[] a) Value: IParameterReferenceOperation: a (OperationKind.ParameterReference, Type: System.Int32[]) (Syntax: 'a') Pattern: - ISlicePatternOperation (OperationKind.SlicePattern, Type: null, IsInvalid) (Syntax: '.. 42') (InputType: System.Int32[], NarrowedType: System.Int32[], SliceSymbol: null + ISlicePatternOperation (OperationKind.SlicePattern, Type: null, IsInvalid) (Syntax: '.. 42') (InputType: System.Int32[], NarrowedType: System.Int32[], SliceSymbol: T[] System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray(T[] array, System.Range range) Pattern: IConstantPatternOperation (OperationKind.ConstantPattern, Type: null, IsInvalid) (Syntax: '42') (InputType: System.Int32[], NarrowedType: System.Int32[]) Value: @@ -2394,7 +2397,7 @@ void M(int[] a) Diagnostic(ErrorCode.ERR_NoImplicitConv, "42").WithArguments("int", "int[]").WithLocation(6, 31) }; - var comp = CreateCompilation(source); + var comp = CreateCompilation(new[] { source, TestSources.Index, TestSources.Range, TestSources.GetSubArray }); VerifyOperationTreeAndDiagnosticsForTest(comp, expectedOperationTree, expectedDiagnostics); } @@ -2433,7 +2436,7 @@ void M(int[] o) IConstantPatternOperation (OperationKind.ConstantPattern, Type: null) (Syntax: '1') (InputType: System.Int32, NarrowedType: System.Int32) Value: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') - ISlicePatternOperation (OperationKind.SlicePattern, Type: null) (Syntax: '.. var slice') (InputType: System.Int32[], NarrowedType: System.Int32[], SliceSymbol: null + ISlicePatternOperation (OperationKind.SlicePattern, Type: null) (Syntax: '.. var slice') (InputType: System.Int32[], NarrowedType: System.Int32[], SliceSymbol: T[] System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray(T[] array, System.Range range) Pattern: IDeclarationPatternOperation (OperationKind.DeclarationPattern, Type: null) (Syntax: 'var slice') (InputType: System.Int32[], NarrowedType: System.Int32[], DeclaredSymbol: System.Int32[]? slice, MatchesNull: True) IConstantPatternOperation (OperationKind.ConstantPattern, Type: null) (Syntax: '2') (InputType: System.Int32, NarrowedType: System.Int32) @@ -2450,7 +2453,9 @@ void M(int[] o) var expectedDiagnostics = DiagnosticDescription.None; - VerifyFlowGraphAndDiagnosticsForTest(source, expectedFlowGraph, expectedDiagnostics, parseOptions: TestOptions.RegularWithExtendedPropertyPatterns); + VerifyFlowGraphAndDiagnosticsForTest(new[] { source, TestSources.Index, TestSources.Range, TestSources.GetSubArray }, + expectedFlowGraph, expectedDiagnostics, parseOptions: TestOptions.RegularWithExtendedPropertyPatterns); } } } + diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs index 92e256ab13be..318c0a482875 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs @@ -71,20 +71,17 @@ public static void Main() 111 000"; var verifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); - // PROTOTYPE the call to StoreToTemp in LocalRewriter.VisitIndexImplicitIndexerAccess causes more temps to be used AssertEx.Multiple( () => verifier.VerifyIL("X.Test(System.Span)", @" { - // Code size 83 (0x53) - .maxstack 3 + // Code size 74 (0x4a) + .maxstack 4 .locals init (char V_0, //first System.Span V_1, //others char V_2, //last System.Span V_3, int V_4, - System.Span V_5, - int V_6, - int V_7) + System.Span V_5) IL_0000: ldarg.0 IL_0001: stloc.3 IL_0002: ldloca.s V_3 @@ -92,7 +89,7 @@ .locals init (char V_0, //first IL_0009: stloc.s V_4 IL_000b: ldloc.s V_4 IL_000d: ldc.i4.1 - IL_000e: ble.un.s IL_0044 + IL_000e: ble.un.s IL_003b IL_0010: ldloca.s V_3 IL_0012: ldc.i4.0 IL_0013: call ""ref char System.Span.this[int].get"" @@ -100,37 +97,33 @@ .locals init (char V_0, //first IL_0019: stloc.0 IL_001a: ldloc.3 IL_001b: stloc.s V_5 - IL_001d: ldc.i4.1 - IL_001e: stloc.s V_6 + IL_001d: ldloca.s V_5 + IL_001f: ldc.i4.1 IL_0020: ldloc.s V_4 IL_0022: ldc.i4.1 IL_0023: sub - IL_0024: ldloc.s V_6 - IL_0026: sub - IL_0027: stloc.s V_7 - IL_0029: ldloca.s V_5 - IL_002b: ldloc.s V_6 - IL_002d: ldloc.s V_7 - IL_002f: call ""System.Span System.Span.Slice(int, int)"" - IL_0034: stloc.1 - IL_0035: ldloca.s V_3 - IL_0037: ldloc.s V_4 - IL_0039: ldc.i4.1 - IL_003a: sub - IL_003b: call ""ref char System.Span.this[int].get"" - IL_0040: ldind.u2 - IL_0041: stloc.2 - IL_0042: br.s IL_0046 - IL_0044: ldc.i4.1 - IL_0045: ret - IL_0046: ldloc.0 - IL_0047: ldloc.2 - IL_0048: bne.un.s IL_0051 - IL_004a: ldloc.1 - IL_004b: call ""bool X.Test(System.Span)"" - IL_0050: ret - IL_0051: ldc.i4.0 - IL_0052: ret + IL_0024: ldc.i4.1 + IL_0025: sub + IL_0026: call ""System.Span System.Span.Slice(int, int)"" + IL_002b: stloc.1 + IL_002c: ldloca.s V_3 + IL_002e: ldloc.s V_4 + IL_0030: ldc.i4.1 + IL_0031: sub + IL_0032: call ""ref char System.Span.this[int].get"" + IL_0037: ldind.u2 + IL_0038: stloc.2 + IL_0039: br.s IL_003d + IL_003b: ldc.i4.1 + IL_003c: ret + IL_003d: ldloc.0 + IL_003e: ldloc.2 + IL_003f: bne.un.s IL_0048 + IL_0041: ldloc.1 + IL_0042: call ""bool X.Test(System.Span)"" + IL_0047: ret + IL_0048: ldc.i4.0 + IL_0049: ret } "), () => verifier.VerifyIL("X.Test(char[])", @" @@ -188,59 +181,53 @@ .locals init (char V_0, //first "), () => verifier.VerifyIL("X.Test(string)", @" { - // Code size 77 (0x4d) - .maxstack 3 + // Code size 68 (0x44) + .maxstack 4 .locals init (char V_0, //first string V_1, //others char V_2, //last string V_3, - int V_4, - int V_5, - int V_6) + int V_4) IL_0000: ldarg.0 IL_0001: stloc.3 IL_0002: ldloc.3 - IL_0003: brfalse.s IL_004b + IL_0003: brfalse.s IL_0042 IL_0005: ldloc.3 IL_0006: callvirt ""int string.Length.get"" IL_000b: stloc.s V_4 IL_000d: ldloc.s V_4 IL_000f: ldc.i4.1 - IL_0010: ble.un.s IL_003e + IL_0010: ble.un.s IL_0035 IL_0012: ldloc.3 IL_0013: ldc.i4.0 IL_0014: callvirt ""char string.this[int].get"" IL_0019: stloc.0 IL_001a: ldloc.3 IL_001b: ldc.i4.1 - IL_001c: stloc.s V_5 - IL_001e: ldloc.s V_4 + IL_001c: ldloc.s V_4 + IL_001e: ldc.i4.1 + IL_001f: sub IL_0020: ldc.i4.1 IL_0021: sub - IL_0022: ldloc.s V_5 - IL_0024: sub - IL_0025: stloc.s V_6 - IL_0027: ldloc.s V_5 - IL_0029: ldloc.s V_6 - IL_002b: callvirt ""string string.Substring(int, int)"" - IL_0030: stloc.1 - IL_0031: ldloc.3 - IL_0032: ldloc.s V_4 - IL_0034: ldc.i4.1 - IL_0035: sub - IL_0036: callvirt ""char string.this[int].get"" - IL_003b: stloc.2 - IL_003c: br.s IL_0040 - IL_003e: ldc.i4.1 - IL_003f: ret - IL_0040: ldloc.0 - IL_0041: ldloc.2 - IL_0042: bne.un.s IL_004b - IL_0044: ldloc.1 - IL_0045: call ""bool X.Test(string)"" - IL_004a: ret - IL_004b: ldc.i4.0 - IL_004c: ret + IL_0022: callvirt ""string string.Substring(int, int)"" + IL_0027: stloc.1 + IL_0028: ldloc.3 + IL_0029: ldloc.s V_4 + IL_002b: ldc.i4.1 + IL_002c: sub + IL_002d: callvirt ""char string.this[int].get"" + IL_0032: stloc.2 + IL_0033: br.s IL_0037 + IL_0035: ldc.i4.1 + IL_0036: ret + IL_0037: ldloc.0 + IL_0038: ldloc.2 + IL_0039: bne.un.s IL_0042 + IL_003b: ldloc.1 + IL_003c: call ""bool X.Test(string)"" + IL_0041: ret + IL_0042: ldc.i4.0 + IL_0043: ret } ") ); @@ -638,32 +625,25 @@ .maxstack 7 }"), () => verifier.VerifyIL("X.Test5", @" { - // Code size 30 (0x1e) + // Code size 24 (0x18) .maxstack 3 - .locals init (Test5 V_0, - int V_1, - int V_2) + .locals init (int V_0) IL_0000: ldarg.0 - IL_0001: brfalse.s IL_001c + IL_0001: brfalse.s IL_0016 IL_0003: ldarg.0 IL_0004: callvirt ""int Test5.Count.get"" - IL_0009: ldarg.0 - IL_000a: stloc.0 + IL_0009: stloc.0 + IL_000a: ldarg.0 IL_000b: ldc.i4.0 - IL_000c: stloc.1 - IL_000d: ldloc.1 - IL_000e: sub - IL_000f: stloc.2 - IL_0010: ldloc.0 - IL_0011: ldloc.1 - IL_0012: ldloc.2 - IL_0013: callvirt ""int Test5.Slice(int, int)"" - IL_0018: ldc.i4.1 - IL_0019: ceq - IL_001b: ret - IL_001c: ldc.i4.0 - IL_001d: ret -}") + IL_000c: ldloc.0 + IL_000d: callvirt ""int Test5.Slice(int, int)"" + IL_0012: ldc.i4.1 + IL_0013: ceq + IL_0015: ret + IL_0016: ldc.i4.0 + IL_0017: ret +} +") ); } @@ -3009,27 +2989,23 @@ struct S readonly void M(Index i, Range r) { - _ = this[i]; // 1, 2 - _ = this[r]; // 3, 4 + _ = this[i]; // 1 + _ = this[r]; // 2 _ = this is [1]; _ = this is [2, ..var rest]; } }"; + // Note: we're unable to report this warning on binding Length properties (as part of implicit indexers) + // because of our use of placeholders. var comp = CreateCompilationWithIndexAndRange(src); comp.VerifyDiagnostics( - // (11,13): warning CS8656: Call to non-readonly member 'S.Length.get' from a 'readonly' member results in an implicit copy of 'this'. - // _ = this[i]; // 1, 2 - Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S.Length.get", "this").WithLocation(11, 13), // (11,13): warning CS8656: Call to non-readonly member 'S.this[int].get' from a 'readonly' member results in an implicit copy of 'this'. - // _ = this[i]; // 1, 2 + // _ = this[i]; // 1 Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S.this[int].get", "this").WithLocation(11, 13), // (12,13): warning CS8656: Call to non-readonly member 'S.Slice(int, int)' from a 'readonly' member results in an implicit copy of 'this'. - // _ = this[r]; // 3, 4 - Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S.Slice(int, int)", "this").WithLocation(12, 13), - // (12,13): warning CS8656: Call to non-readonly member 'S.Length.get' from a 'readonly' member results in an implicit copy of 'this'. - // _ = this[r]; // 3, 4 - Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S.Length.get", "this").WithLocation(12, 13) + // _ = this[r]; // 2 + Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S.Slice(int, int)", "this").WithLocation(12, 13) ); } @@ -6914,22 +6890,20 @@ public void M() // Note: no Index or Range involved verifier.VerifyIL("C.M", @" { - // Code size 72 (0x48) + // Code size 61 (0x3d) .maxstack 3 .locals init (int V_0, //x int V_1, //y C V_2, - C V_3, - int V_4, - int V_5) + int V_3) IL_0000: ldarg.0 IL_0001: stloc.2 IL_0002: ldloc.2 - IL_0003: brfalse.s IL_0047 + IL_0003: brfalse.s IL_003c IL_0005: ldloc.2 IL_0006: callvirt ""int C.Length.get"" IL_000b: ldc.i4.1 - IL_000c: bne.un.s IL_0047 + IL_000c: bne.un.s IL_003c IL_000e: ldloc.2 IL_000f: ldc.i4.0 IL_0010: callvirt ""int C.this[int].get"" @@ -6937,27 +6911,21 @@ .locals init (int V_0, //x IL_0016: ldarg.0 IL_0017: stloc.2 IL_0018: ldloc.2 - IL_0019: brfalse.s IL_0047 + IL_0019: brfalse.s IL_003c IL_001b: ldloc.2 IL_001c: callvirt ""int C.Length.get"" - IL_0021: ldloc.2 - IL_0022: stloc.3 + IL_0021: stloc.3 + IL_0022: ldloc.2 IL_0023: ldc.i4.0 - IL_0024: stloc.s V_4 - IL_0026: ldloc.s V_4 - IL_0028: sub - IL_0029: stloc.s V_5 - IL_002b: ldloc.3 - IL_002c: ldloc.s V_4 - IL_002e: ldloc.s V_5 - IL_0030: callvirt ""int C.Slice(int, int)"" - IL_0035: stloc.1 - IL_0036: ldloc.0 - IL_0037: ldloc.1 - IL_0038: newobj ""System.ValueTuple..ctor(int, int)"" - IL_003d: box ""System.ValueTuple"" - IL_0042: call ""void System.Console.Write(object)"" - IL_0047: ret + IL_0024: ldloc.3 + IL_0025: callvirt ""int C.Slice(int, int)"" + IL_002a: stloc.1 + IL_002b: ldloc.0 + IL_002c: ldloc.1 + IL_002d: newobj ""System.ValueTuple..ctor(int, int)"" + IL_0032: box ""System.ValueTuple"" + IL_0037: call ""void System.Console.Write(object)"" + IL_003c: ret } "); } @@ -7319,19 +7287,96 @@ public void Simple_String_Slice() CompileAndVerify(comp, expectedOutput: "42"); } - [Fact] + [Fact, WorkItem(57728, "https://github.com/dotnet/roslyn/issues/57728")] public void Simple_Array_Slice() { var source = @" -if (new[] { 0, 4, 2, 0 } is [_, .. var x, _]) +class C { - var y = new[] { 0, 4, 2, 0 }[1..^1]; - System.Console.Write((x[0], x[1], y[0], y[1])); + public static void Main() + { + if (new[] { 0, 4, 2, 0 } is [_, .. var x, _]) + { + var y = new[] { 0, 4, 2, 0 }[1..^1]; + System.Console.Write((x[0], x[1], y[0], y[1])); + } + } } "; - var comp = CreateCompilation(new[] { source, TestSources.Index, TestSources.Range, TestSources.GetSubArray }); + var comp = CreateCompilation(new[] { source, TestSources.Index, TestSources.Range, TestSources.GetSubArray }, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics(); - CompileAndVerify(comp, expectedOutput: "(4, 2, 4, 2)"); + var verifier = CompileAndVerify(comp, expectedOutput: "(4, 2, 4, 2)"); + // we use Array.Length to get the length, but should be using ldlen + // Tracked by https://github.com/dotnet/roslyn/issues/57728 + verifier.VerifyIL("C.Main", @" +{ + // Code size 118 (0x76) + .maxstack 5 + .locals init (int[] V_0, //x + int[] V_1, + int[] V_2) //y + IL_0000: ldc.i4.4 + IL_0001: newarr ""int"" + IL_0006: dup + IL_0007: ldc.i4.1 + IL_0008: ldc.i4.4 + IL_0009: stelem.i4 + IL_000a: dup + IL_000b: ldc.i4.2 + IL_000c: ldc.i4.2 + IL_000d: stelem.i4 + IL_000e: stloc.1 + IL_000f: ldloc.1 + IL_0010: brfalse.s IL_0075 + IL_0012: ldloc.1 + IL_0013: callvirt ""int System.Array.Length.get"" + IL_0018: ldc.i4.2 + IL_0019: blt.s IL_0075 + IL_001b: ldloc.1 + IL_001c: ldc.i4.1 + IL_001d: call ""System.Index System.Index.op_Implicit(int)"" + IL_0022: ldc.i4.1 + IL_0023: ldc.i4.1 + IL_0024: newobj ""System.Index..ctor(int, bool)"" + IL_0029: newobj ""System.Range..ctor(System.Index, System.Index)"" + IL_002e: call ""int[] System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray(int[], System.Range)"" + IL_0033: stloc.0 + IL_0034: ldc.i4.4 + IL_0035: newarr ""int"" + IL_003a: dup + IL_003b: ldc.i4.1 + IL_003c: ldc.i4.4 + IL_003d: stelem.i4 + IL_003e: dup + IL_003f: ldc.i4.2 + IL_0040: ldc.i4.2 + IL_0041: stelem.i4 + IL_0042: ldc.i4.1 + IL_0043: call ""System.Index System.Index.op_Implicit(int)"" + IL_0048: ldc.i4.1 + IL_0049: ldc.i4.1 + IL_004a: newobj ""System.Index..ctor(int, bool)"" + IL_004f: newobj ""System.Range..ctor(System.Index, System.Index)"" + IL_0054: call ""int[] System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray(int[], System.Range)"" + IL_0059: stloc.2 + IL_005a: ldloc.0 + IL_005b: ldc.i4.0 + IL_005c: ldelem.i4 + IL_005d: ldloc.0 + IL_005e: ldc.i4.1 + IL_005f: ldelem.i4 + IL_0060: ldloc.2 + IL_0061: ldc.i4.0 + IL_0062: ldelem.i4 + IL_0063: ldloc.2 + IL_0064: ldc.i4.1 + IL_0065: ldelem.i4 + IL_0066: newobj ""System.ValueTuple..ctor(int, int, int, int)"" + IL_006b: box ""System.ValueTuple"" + IL_0070: call ""void System.Console.Write(object)"" + IL_0075: ret +} +"); } [Theory] From d1288ed7d174c1a3b499622ebbc4b03599763fcf Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Fri, 12 Nov 2021 11:10:39 -0800 Subject: [PATCH 12/29] Address feedback --- .../Portable/Binder/Binder.ValueChecks.cs | 10 +- .../Portable/Binder/Binder_Expressions.cs | 21 +-- .../CSharp/Portable/Binder/Binder_Patterns.cs | 50 ++++--- .../Binder/DecisionDagBuilder_ListPatterns.cs | 6 + .../BoundIndexOrRangePatternIndexerAccess.cs | 4 +- .../CSharp/Portable/BoundTree/BoundNodes.xml | 57 +++++--- .../CSharp/Portable/Errors/ErrorCode.cs | 7 +- .../CSharp/Portable/Errors/MessageID.cs | 2 +- .../Generated/BoundNodes.xml.Generated.cs | 123 +++++++++--------- .../LocalRewriter.PatternLocalRewriter.cs | 20 +-- .../LocalRewriter.PlaceholderReplacer.cs | 55 ++++++++ .../Lowering/LocalRewriter/LocalRewriter.cs | 12 +- .../LocalRewriter_IndexerAccess.cs | 22 +--- .../Test/Emit/CodeGen/IndexAndRangeTests.cs | 3 +- .../IOperationTests_IIsPatternExpression.cs | 3 - .../PatternMatchingTests_ListPatterns.cs | 52 +++----- 16 files changed, 246 insertions(+), 201 deletions(-) create mode 100644 src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.PlaceholderReplacer.cs diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index d9c384c33172..152776c977a2 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -400,15 +400,7 @@ internal bool CheckValueKind(SyntaxNode node, BoundExpression expr, BindValueKin case BoundKind.IndexOrRangePatternIndexerAccess: var implicitIndexer = (BoundIndexOrRangePatternIndexerAccess)expr; - if (implicitIndexer.IndexerAccess is BoundIndexerAccess implicitIndexerAccess) - { - // If this is an Index indexer, PatternSymbol should be a property, pointing to the - // implicit indexer. If it's a Range access, it will be a method, pointing to a Slice method - // and it's handled below as part of invocations. - return CheckValueKind(node, implicitIndexerAccess, valueKind, checkingReceiver, diagnostics); - } - Debug.Assert(implicitIndexer.IndexerAccess is BoundCall); - break; + return CheckValueKind(node, implicitIndexer.IndexerAccess, valueKind, checkingReceiver, diagnostics); case BoundKind.EventAccess: return CheckEventValueKind((BoundEventAccess)expr, valueKind, diagnostics); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index c6e716120da9..86361ed0a024 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -8155,7 +8155,7 @@ private bool TryBindIndexOrRangeImplicitIndexerParts( var lookupResult = LookupResult.GetInstance(); if (TryBindLengthOrCount(syntax, receiverPlaceholder, out lengthOrCountAccess, diagnostics) && - TryFindIndexOrRangeImplicitIndexer(syntax, lookupResult, receiver, argIsIndex, out implicitIndexerAccess, out argumentPlaceholders, diagnostics)) + TryBindIndexOrRangeImplicitIndexer(syntax, lookupResult, receiver, argIsIndex, out implicitIndexerAccess, out argumentPlaceholders, diagnostics)) { CheckValue(lengthOrCountAccess, BindValueKind.RValue, diagnostics); @@ -8173,7 +8173,7 @@ private bool TryBindIndexOrRangeImplicitIndexerParts( private bool TryBindLengthOrCount( SyntaxNode syntax, BoundExpression receiver, - [NotNullWhen(true)] out BoundExpression? lengthOrCountAccess, + out BoundExpression lengthOrCountAccess, BindingDiagnosticBag diagnostics) { var lookupResult = LookupResult.GetInstance(); @@ -8181,25 +8181,25 @@ private bool TryBindLengthOrCount( Debug.Assert(receiver.Type is not null); if (TryLookupLengthOrCount(syntax, receiver.Type, lookupResult, out var lengthOrCountProperty, diagnostics)) { - lengthOrCountAccess = BindPropertyAccess(syntax, receiver, lengthOrCountProperty, diagnostics, lookupResult.Kind, hasErrors: false); + lengthOrCountAccess = BindPropertyAccess(syntax, receiver, lengthOrCountProperty, diagnostics, lookupResult.Kind, hasErrors: false).MakeCompilerGenerated(); ReportDiagnosticsIfObsolete(diagnostics, lengthOrCountProperty, syntax, hasBaseReceiver: false); lookupResult.Free(); return true; } - lengthOrCountAccess = null; + lengthOrCountAccess = BadExpression(syntax); lookupResult.Free(); return false; } /// - /// Finds pattern-based implicit indexer: + /// Binds pattern-based implicit indexer: /// - for Index indexer, this will find `this[int]`. /// - for Range indexer, this will find `Slice(int, int)` or `string.Substring(int, int)`. /// - private bool TryFindIndexOrRangeImplicitIndexer( + private bool TryBindIndexOrRangeImplicitIndexer( SyntaxNode syntax, LookupResult lookupResult, BoundExpression receiver, @@ -8242,7 +8242,10 @@ candidate is PropertySymbol property && var analyzedArguments = AnalyzedArguments.GetInstance(); analyzedArguments.Arguments.Add(intPlaceholder); - indexerOrSliceAccess = BindIndexedPropertyAccess(syntax, receiver, ImmutableArray.Create(property), analyzedArguments, diagnostics); + var properties = ArrayBuilder.GetInstance(); + properties.AddRange(property); + indexerOrSliceAccess = BindIndexerOrIndexedPropertyAccess(syntax, receiver, properties, analyzedArguments, diagnostics).MakeCompilerGenerated(); + properties.Free(); analyzedArguments.Free(); return true; } @@ -8304,7 +8307,7 @@ method.OriginalDefinition is var original && argumentPlaceholders = default; return false; - void makeCall(SyntaxNode syntax, BoundExpression? receiverOpt, MethodSymbol method, + void makeCall(SyntaxNode syntax, BoundExpression receiver, MethodSymbol method, out BoundExpression indexerOrSliceAccess, out ImmutableArray argumentPlaceholders) { // TODO2 make a property group and reuse an existing binding method? @@ -8314,7 +8317,7 @@ void makeCall(SyntaxNode syntax, BoundExpression? receiverOpt, MethodSymbol meth indexerOrSliceAccess = new BoundCall( syntax, - receiverOpt, + receiver, method, ImmutableArray.Create(startArgumentPlaceholder, endArgumentPlaceholder), argumentNamesOpt: default, diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs index 126fb11e6b47..1df41e3d4ad3 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs @@ -201,14 +201,14 @@ private BoundPattern BindSlicePattern( BoundExpression? indexerAccess = null; BoundPattern? pattern = null; BoundSlicePatternReceiverPlaceholder? receiverPlaceholder = null; - BoundSlicePatternUnloweredRangePlaceholder? argumentPlaceholder = null; + BoundSlicePatternRangePlaceholder? argumentPlaceholder = null; // We don't require the type to be sliceable if there's no subpattern. if (node.Pattern is not null) { receiverPlaceholder = new BoundSlicePatternReceiverPlaceholder(node, GetValEscape(inputType, inputValEscape), inputType) { WasCompilerGenerated = true }; var systemRangeType = GetWellKnownType(WellKnownType.System_Range, diagnostics, node); - argumentPlaceholder = new BoundSlicePatternUnloweredRangePlaceholder(node, systemRangeType) { WasCompilerGenerated = true }; + argumentPlaceholder = new BoundSlicePatternRangePlaceholder(node, systemRangeType) { WasCompilerGenerated = true }; TypeSymbol sliceType; if (inputType.IsErrorType()) @@ -221,7 +221,7 @@ private BoundPattern BindSlicePattern( var analyzedArguments = AnalyzedArguments.GetInstance(); analyzedArguments.Arguments.Add(argumentPlaceholder); - indexerAccess = BindElementAccessCore(node, receiverPlaceholder, analyzedArguments, diagnostics); + indexerAccess = BindElementAccessCore(node, receiverPlaceholder, analyzedArguments, diagnostics).MakeCompilerGenerated(); indexerAccess = CheckValue(indexerAccess, BindValueKind.RValue, diagnostics); Debug.Assert(indexerAccess is BoundIndexerAccess or BoundIndexOrRangePatternIndexerAccess or BoundArrayAccess or BoundBadExpression); analyzedArguments.Free(); @@ -278,16 +278,18 @@ private BoundListPattern BindListPattern( CheckFeatureAvailability(node, MessageID.IDS_FeatureListPattern, diagnostics); TypeSymbol elementType; - BoundExpression? indexerAccess = null; - BoundExpression? lengthAccess = null; + BoundExpression? indexerAccess; + BoundExpression? lengthAccess; TypeSymbol narrowedType = inputType.StrippedType(); BoundListPatternReceiverPlaceholder? receiverPlaceholder; - BoundListPatternUnloweredIndexPlaceholder? argumentPlaceholder; + BoundListPatternIndexPlaceholder? argumentPlaceholder; if (inputType.IsErrorType()) { hasErrors = true; elementType = inputType; + indexerAccess = null; + lengthAccess = null; receiverPlaceholder = null; argumentPlaceholder = null; } @@ -321,18 +323,22 @@ private BoundListPattern BindListPattern( /// private bool IsCountableAndIndexable(SyntaxNode node, TypeSymbol inputType, out PropertySymbol? lengthProperty) { - var diagnostics = BindingDiagnosticBag.GetInstance(); - var success = BindLengthAndIndexerForListPattern(node, inputType, inputValEscape: ExternalScope, diagnostics, indexerAccess: out _, out var lengthAccess, receiverPlaceholder: out _, argumentPlaceholder: out _); + var success = BindLengthAndIndexerForListPattern(node, inputType, inputValEscape: ExternalScope, BindingDiagnosticBag.Discarded, + indexerAccess: out _, out var lengthAccess, receiverPlaceholder: out _, argumentPlaceholder: out _); lengthProperty = success ? GetPropertySymbol(lengthAccess, out _, out _) : null; - diagnostics.Free(); return success; } private bool BindLengthAndIndexerForListPattern(SyntaxNode node, TypeSymbol inputType, uint inputValEscape, BindingDiagnosticBag diagnostics, - out BoundExpression? indexerAccess, out BoundExpression? lengthAccess, out BoundListPatternReceiverPlaceholder? receiverPlaceholder, out BoundListPatternUnloweredIndexPlaceholder? argumentPlaceholder) + out BoundExpression indexerAccess, out BoundExpression lengthAccess, out BoundListPatternReceiverPlaceholder? receiverPlaceholder, out BoundListPatternIndexPlaceholder argumentPlaceholder) { var bindingDiagnostics = BindingDiagnosticBag.GetInstance(diagnostics); + if (inputType.IsDynamic()) + { + Error(bindingDiagnostics, ErrorCode.ERR_UnsupportedTypeForListPattern, node, inputType); + } + receiverPlaceholder = new BoundListPatternReceiverPlaceholder(node, GetValEscape(inputType, inputValEscape), inputType) { WasCompilerGenerated = true }; bool hasErrors = false; if (inputType.IsSZArray()) @@ -340,11 +346,11 @@ private bool BindLengthAndIndexerForListPattern(SyntaxNode node, TypeSymbol inpu hasErrors |= !TryGetSpecialTypeMember(Compilation, SpecialMember.System_Array__Length, node, bindingDiagnostics, out PropertySymbol lengthProperty); if (lengthProperty is not null) { - lengthAccess = new BoundPropertyAccess(node, receiverPlaceholder, lengthProperty, LookupResultKind.Viable, lengthProperty.Type); + lengthAccess = new BoundPropertyAccess(node, receiverPlaceholder, lengthProperty, LookupResultKind.Viable, lengthProperty.Type) { WasCompilerGenerated = true }; } else { - lengthAccess = new BoundBadExpression(node, LookupResultKind.Empty, ImmutableArray.Empty, ImmutableArray.Empty, CreateErrorType(), hasErrors: true); + lengthAccess = new BoundBadExpression(node, LookupResultKind.Empty, ImmutableArray.Empty, ImmutableArray.Empty, CreateErrorType(), hasErrors: true) { WasCompilerGenerated = true }; } } else @@ -352,32 +358,20 @@ private bool BindLengthAndIndexerForListPattern(SyntaxNode node, TypeSymbol inpu hasErrors |= !TryBindLengthOrCount(node, receiverPlaceholder, out lengthAccess, bindingDiagnostics); } - if (lengthAccess is null) - { - Error(bindingDiagnostics, ErrorCode.ERR_UnsupportedTypeForListPattern, node, inputType); - } - else - { - CheckValue(lengthAccess, BindValueKind.RValue, diagnostics); - } + lengthAccess = CheckValue(lengthAccess, BindValueKind.RValue, diagnostics); var analyzedArguments = AnalyzedArguments.GetInstance(); var systemIndexType = GetWellKnownType(WellKnownType.System_Index, bindingDiagnostics, node); - argumentPlaceholder = new BoundListPatternUnloweredIndexPlaceholder(node, systemIndexType) { WasCompilerGenerated = true }; + argumentPlaceholder = new BoundListPatternIndexPlaceholder(node, systemIndexType) { WasCompilerGenerated = true }; analyzedArguments.Arguments.Add(argumentPlaceholder); - indexerAccess = BindElementAccessCore(node, receiverPlaceholder, analyzedArguments, bindingDiagnostics); + indexerAccess = BindElementAccessCore(node, receiverPlaceholder, analyzedArguments, bindingDiagnostics).MakeCompilerGenerated(); indexerAccess = CheckValue(indexerAccess, BindValueKind.RValue, bindingDiagnostics); Debug.Assert(indexerAccess is BoundIndexerAccess or BoundIndexOrRangePatternIndexerAccess or BoundArrayAccess or BoundBadExpression or BoundDynamicIndexerAccess); analyzedArguments.Free(); - if (bindingDiagnostics.AccumulatesDiagnostics && bindingDiagnostics.HasAnyErrors()) - { - hasErrors = true; - } - diagnostics.AddRangeAndFree(bindingDiagnostics); - return !hasErrors; + return !hasErrors && lengthAccess?.HasErrors == false && !indexerAccess.HasErrors; } private static BoundPattern BindDiscardPattern(DiscardPatternSyntax node, TypeSymbol inputType) diff --git a/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder_ListPatterns.cs b/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder_ListPatterns.cs index 1d742dcffca9..3b5790f62f3d 100644 --- a/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder_ListPatterns.cs +++ b/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder_ListPatterns.cs @@ -56,6 +56,9 @@ private Tests MakeTestsAndBindingsForListPattern(BoundDagTemp input, BoundListPa { Debug.Assert(slice.IndexerAccess is not null); Debug.Assert(index <= 0); + Debug.Assert(slice.ReceiverPlaceholder is not null); + Debug.Assert(slice.ArgumentPlaceholder is not null); + var sliceEvaluation = new BoundDagSliceEvaluation(slicePattern.Syntax, slicePattern.InputType, lengthTemp, startIndex: startIndex, endIndex: index, slice.IndexerAccess, slice.ReceiverPlaceholder, slice.ArgumentPlaceholder, input); @@ -68,6 +71,9 @@ private Tests MakeTestsAndBindingsForListPattern(BoundDagTemp input, BoundListPa } Debug.Assert(list.IndexerAccess is not null); + Debug.Assert(list.ReceiverPlaceholder is not null); + Debug.Assert(list.ArgumentPlaceholder is not null); + var indexEvaluation = new BoundDagIndexerEvaluation(subpattern.Syntax, subpattern.InputType, lengthTemp, index++, list.IndexerAccess, list.ReceiverPlaceholder, list.ArgumentPlaceholder, input); diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundIndexOrRangePatternIndexerAccess.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundIndexOrRangePatternIndexerAccess.cs index bfbdb22cd733..5e4c86eb9336 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundIndexOrRangePatternIndexerAccess.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundIndexOrRangePatternIndexerAccess.cs @@ -11,8 +11,8 @@ internal partial class BoundIndexOrRangePatternIndexerAccess { internal BoundIndexOrRangePatternIndexerAccess WithLengthOrCountAccess(BoundExpression lengthOrCountAccess) { - return new BoundIndexOrRangePatternIndexerAccess(this.Syntax, this.Argument, lengthOrCountAccess, this.ReceiverPlaceholder, - this.IndexerAccess, this.ArgumentPlaceholders, this.Type, this.HasErrors); + return this.Update(this.Argument, lengthOrCountAccess, this.ReceiverPlaceholder, + this.IndexerAccess, this.ArgumentPlaceholders, this.Type); } // The receiver expression is the receiver of IndexerAccess. diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml index a29a6ba956e3..a0e77f5aed11 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml @@ -152,7 +152,7 @@ - + @@ -163,7 +163,7 @@ - + @@ -1522,14 +1522,17 @@ - + - - + + - - + + @@ -1538,14 +1541,17 @@ - + - + - + @@ -2062,13 +2068,16 @@ - + - + - - + + @@ -2230,29 +2239,35 @@ - + - - + + - + - - + + - + diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 8829f21805f6..1dc742d5b3bd 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -2007,13 +2007,12 @@ internal enum ErrorCode #region diagnostics introduced for C# 11.0 - ERR_UnsupportedTypeForListPattern = 9000, - ERR_UnsupportedTypeForSlicePattern = 9001, - ERR_MisplacedSlicePattern = 9002, + ERR_CannotBeMadeNullable = 8977, + ERR_UnsupportedTypeForListPattern = 8978, + ERR_MisplacedSlicePattern = 8979, #endregion - ERR_CannotBeMadeNullable = 8977, // Note: you will need to re-generate compiler code after adding warnings (eng\generate-compiler-code.cmd) } diff --git a/src/Compilers/CSharp/Portable/Errors/MessageID.cs b/src/Compilers/CSharp/Portable/Errors/MessageID.cs index ba0d723615b9..c3ba3421130e 100644 --- a/src/Compilers/CSharp/Portable/Errors/MessageID.cs +++ b/src/Compilers/CSharp/Portable/Errors/MessageID.cs @@ -346,7 +346,7 @@ internal static LanguageVersion RequiredVersion(this MessageID feature) // Checks are in the LanguageParser unless otherwise noted. switch (feature) { - // PREFER reporting diagnostics in binding when diagnostics do not affect the shape of the tree + // PREFER reporting diagnostics in binding when diagnostics do not affect the shape of the syntax tree // C# preview features. case MessageID.IDS_FeatureStaticAbstractMembersInInterfaces: // semantic check diff --git a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs index d34324d26f46..317deef47a8f 100644 --- a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs @@ -33,9 +33,9 @@ internal enum BoundKind : byte IndexOrRangeIndexerPatternValuePlaceholder, IndexOrRangeIndexerPatternReceiverPlaceholder, ListPatternReceiverPlaceholder, - ListPatternUnloweredIndexPlaceholder, + ListPatternIndexPlaceholder, SlicePatternReceiverPlaceholder, - SlicePatternUnloweredRangePlaceholder, + SlicePatternRangePlaceholder, Dup, PassByCopy, BadExpression, @@ -758,18 +758,18 @@ public BoundListPatternReceiverPlaceholder Update(uint valEscape, TypeSymbol typ } } - internal sealed partial class BoundListPatternUnloweredIndexPlaceholder : BoundValuePlaceholderBase + internal sealed partial class BoundListPatternIndexPlaceholder : BoundValuePlaceholderBase { - public BoundListPatternUnloweredIndexPlaceholder(SyntaxNode syntax, TypeSymbol type, bool hasErrors) - : base(BoundKind.ListPatternUnloweredIndexPlaceholder, syntax, type, hasErrors) + public BoundListPatternIndexPlaceholder(SyntaxNode syntax, TypeSymbol type, bool hasErrors) + : base(BoundKind.ListPatternIndexPlaceholder, syntax, type, hasErrors) { RoslynDebug.Assert(type is object, "Field 'type' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); } - public BoundListPatternUnloweredIndexPlaceholder(SyntaxNode syntax, TypeSymbol type) - : base(BoundKind.ListPatternUnloweredIndexPlaceholder, syntax, type) + public BoundListPatternIndexPlaceholder(SyntaxNode syntax, TypeSymbol type) + : base(BoundKind.ListPatternIndexPlaceholder, syntax, type) { RoslynDebug.Assert(type is object, "Field 'type' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); @@ -779,13 +779,13 @@ public BoundListPatternUnloweredIndexPlaceholder(SyntaxNode syntax, TypeSymbol t public new TypeSymbol Type => base.Type!; [DebuggerStepThrough] - public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitListPatternUnloweredIndexPlaceholder(this); + public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitListPatternIndexPlaceholder(this); - public BoundListPatternUnloweredIndexPlaceholder Update(TypeSymbol type) + public BoundListPatternIndexPlaceholder Update(TypeSymbol type) { if (!TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) { - var result = new BoundListPatternUnloweredIndexPlaceholder(this.Syntax, type, this.HasErrors); + var result = new BoundListPatternIndexPlaceholder(this.Syntax, type, this.HasErrors); result.CopyAttributes(this); return result; } @@ -832,18 +832,18 @@ public BoundSlicePatternReceiverPlaceholder Update(uint valEscape, TypeSymbol ty } } - internal sealed partial class BoundSlicePatternUnloweredRangePlaceholder : BoundValuePlaceholderBase + internal sealed partial class BoundSlicePatternRangePlaceholder : BoundValuePlaceholderBase { - public BoundSlicePatternUnloweredRangePlaceholder(SyntaxNode syntax, TypeSymbol type, bool hasErrors) - : base(BoundKind.SlicePatternUnloweredRangePlaceholder, syntax, type, hasErrors) + public BoundSlicePatternRangePlaceholder(SyntaxNode syntax, TypeSymbol type, bool hasErrors) + : base(BoundKind.SlicePatternRangePlaceholder, syntax, type, hasErrors) { RoslynDebug.Assert(type is object, "Field 'type' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); } - public BoundSlicePatternUnloweredRangePlaceholder(SyntaxNode syntax, TypeSymbol type) - : base(BoundKind.SlicePatternUnloweredRangePlaceholder, syntax, type) + public BoundSlicePatternRangePlaceholder(SyntaxNode syntax, TypeSymbol type) + : base(BoundKind.SlicePatternRangePlaceholder, syntax, type) { RoslynDebug.Assert(type is object, "Field 'type' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); @@ -853,13 +853,13 @@ public BoundSlicePatternUnloweredRangePlaceholder(SyntaxNode syntax, TypeSymbol public new TypeSymbol Type => base.Type!; [DebuggerStepThrough] - public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitSlicePatternUnloweredRangePlaceholder(this); + public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitSlicePatternRangePlaceholder(this); - public BoundSlicePatternUnloweredRangePlaceholder Update(TypeSymbol type) + public BoundSlicePatternRangePlaceholder Update(TypeSymbol type) { if (!TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) { - var result = new BoundSlicePatternUnloweredRangePlaceholder(this.Syntax, type, this.HasErrors); + var result = new BoundSlicePatternRangePlaceholder(this.Syntax, type, this.HasErrors); result.CopyAttributes(this); return result; } @@ -5422,7 +5422,7 @@ public BoundDagIndexEvaluation Update(PropertySymbol property, int index, BoundD internal sealed partial class BoundDagIndexerEvaluation : BoundDagEvaluation { - public BoundDagIndexerEvaluation(SyntaxNode syntax, TypeSymbol indexerType, BoundDagTemp lengthTemp, int index, BoundExpression indexerAccess, BoundListPatternReceiverPlaceholder? receiverPlaceholder, BoundListPatternUnloweredIndexPlaceholder? argumentPlaceholder, BoundDagTemp input, bool hasErrors = false) + public BoundDagIndexerEvaluation(SyntaxNode syntax, TypeSymbol indexerType, BoundDagTemp lengthTemp, int index, BoundExpression indexerAccess, BoundListPatternReceiverPlaceholder? receiverPlaceholder, BoundListPatternIndexPlaceholder? argumentPlaceholder, BoundDagTemp input, bool hasErrors = false) : base(BoundKind.DagIndexerEvaluation, syntax, input, hasErrors || lengthTemp.HasErrors() || indexerAccess.HasErrors() || receiverPlaceholder.HasErrors() || argumentPlaceholder.HasErrors() || input.HasErrors()) { @@ -5450,11 +5450,11 @@ public BoundDagIndexerEvaluation(SyntaxNode syntax, TypeSymbol indexerType, Boun public BoundListPatternReceiverPlaceholder? ReceiverPlaceholder { get; } - public BoundListPatternUnloweredIndexPlaceholder? ArgumentPlaceholder { get; } + public BoundListPatternIndexPlaceholder? ArgumentPlaceholder { get; } [DebuggerStepThrough] public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitDagIndexerEvaluation(this); - public BoundDagIndexerEvaluation Update(TypeSymbol indexerType, BoundDagTemp lengthTemp, int index, BoundExpression indexerAccess, BoundListPatternReceiverPlaceholder? receiverPlaceholder, BoundListPatternUnloweredIndexPlaceholder? argumentPlaceholder, BoundDagTemp input) + public BoundDagIndexerEvaluation Update(TypeSymbol indexerType, BoundDagTemp lengthTemp, int index, BoundExpression indexerAccess, BoundListPatternReceiverPlaceholder? receiverPlaceholder, BoundListPatternIndexPlaceholder? argumentPlaceholder, BoundDagTemp input) { if (!TypeSymbol.Equals(indexerType, this.IndexerType, TypeCompareKind.ConsiderEverything) || lengthTemp != this.LengthTemp || index != this.Index || indexerAccess != this.IndexerAccess || receiverPlaceholder != this.ReceiverPlaceholder || argumentPlaceholder != this.ArgumentPlaceholder || input != this.Input) { @@ -5468,7 +5468,7 @@ public BoundDagIndexerEvaluation Update(TypeSymbol indexerType, BoundDagTemp len internal sealed partial class BoundDagSliceEvaluation : BoundDagEvaluation { - public BoundDagSliceEvaluation(SyntaxNode syntax, TypeSymbol sliceType, BoundDagTemp lengthTemp, int startIndex, int endIndex, BoundExpression indexerAccess, BoundSlicePatternReceiverPlaceholder? receiverPlaceholder, BoundSlicePatternUnloweredRangePlaceholder? argumentPlaceholder, BoundDagTemp input, bool hasErrors = false) + public BoundDagSliceEvaluation(SyntaxNode syntax, TypeSymbol sliceType, BoundDagTemp lengthTemp, int startIndex, int endIndex, BoundExpression indexerAccess, BoundSlicePatternReceiverPlaceholder? receiverPlaceholder, BoundSlicePatternRangePlaceholder? argumentPlaceholder, BoundDagTemp input, bool hasErrors = false) : base(BoundKind.DagSliceEvaluation, syntax, input, hasErrors || lengthTemp.HasErrors() || indexerAccess.HasErrors() || receiverPlaceholder.HasErrors() || argumentPlaceholder.HasErrors() || input.HasErrors()) { @@ -5499,11 +5499,11 @@ public BoundDagSliceEvaluation(SyntaxNode syntax, TypeSymbol sliceType, BoundDag public BoundSlicePatternReceiverPlaceholder? ReceiverPlaceholder { get; } - public BoundSlicePatternUnloweredRangePlaceholder? ArgumentPlaceholder { get; } + public BoundSlicePatternRangePlaceholder? ArgumentPlaceholder { get; } [DebuggerStepThrough] public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitDagSliceEvaluation(this); - public BoundDagSliceEvaluation Update(TypeSymbol sliceType, BoundDagTemp lengthTemp, int startIndex, int endIndex, BoundExpression indexerAccess, BoundSlicePatternReceiverPlaceholder? receiverPlaceholder, BoundSlicePatternUnloweredRangePlaceholder? argumentPlaceholder, BoundDagTemp input) + public BoundDagSliceEvaluation Update(TypeSymbol sliceType, BoundDagTemp lengthTemp, int startIndex, int endIndex, BoundExpression indexerAccess, BoundSlicePatternReceiverPlaceholder? receiverPlaceholder, BoundSlicePatternRangePlaceholder? argumentPlaceholder, BoundDagTemp input) { if (!TypeSymbol.Equals(sliceType, this.SliceType, TypeCompareKind.ConsiderEverything) || lengthTemp != this.LengthTemp || startIndex != this.StartIndex || endIndex != this.EndIndex || indexerAccess != this.IndexerAccess || receiverPlaceholder != this.ReceiverPlaceholder || argumentPlaceholder != this.ArgumentPlaceholder || input != this.Input) { @@ -7304,11 +7304,12 @@ public BoundIndexerAccess Update(BoundExpression? receiverOpt, PropertySymbol in internal sealed partial class BoundIndexOrRangePatternIndexerAccess : BoundExpression { - public BoundIndexOrRangePatternIndexerAccess(SyntaxNode syntax, BoundExpression argument, BoundExpression? lengthOrCountAccess, BoundIndexOrRangeIndexerPatternReceiverPlaceholder receiverPlaceholder, BoundExpression indexerAccess, ImmutableArray argumentPlaceholders, TypeSymbol type, bool hasErrors = false) + public BoundIndexOrRangePatternIndexerAccess(SyntaxNode syntax, BoundExpression argument, BoundExpression lengthOrCountAccess, BoundIndexOrRangeIndexerPatternReceiverPlaceholder receiverPlaceholder, BoundExpression indexerAccess, ImmutableArray argumentPlaceholders, TypeSymbol type, bool hasErrors = false) : base(BoundKind.IndexOrRangePatternIndexerAccess, syntax, type, hasErrors || argument.HasErrors() || lengthOrCountAccess.HasErrors() || receiverPlaceholder.HasErrors() || indexerAccess.HasErrors() || argumentPlaceholders.HasErrors()) { RoslynDebug.Assert(argument is object, "Field 'argument' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); + RoslynDebug.Assert(lengthOrCountAccess is object, "Field 'lengthOrCountAccess' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); RoslynDebug.Assert(receiverPlaceholder is object, "Field 'receiverPlaceholder' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); RoslynDebug.Assert(indexerAccess is object, "Field 'indexerAccess' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); RoslynDebug.Assert(!argumentPlaceholders.IsDefault, "Field 'argumentPlaceholders' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); @@ -7326,7 +7327,7 @@ public BoundIndexOrRangePatternIndexerAccess(SyntaxNode syntax, BoundExpression public BoundExpression Argument { get; } - public BoundExpression? LengthOrCountAccess { get; } + public BoundExpression LengthOrCountAccess { get; } public BoundIndexOrRangeIndexerPatternReceiverPlaceholder ReceiverPlaceholder { get; } @@ -7336,7 +7337,7 @@ public BoundIndexOrRangePatternIndexerAccess(SyntaxNode syntax, BoundExpression [DebuggerStepThrough] public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitIndexOrRangePatternIndexerAccess(this); - public BoundIndexOrRangePatternIndexerAccess Update(BoundExpression argument, BoundExpression? lengthOrCountAccess, BoundIndexOrRangeIndexerPatternReceiverPlaceholder receiverPlaceholder, BoundExpression indexerAccess, ImmutableArray argumentPlaceholders, TypeSymbol type) + public BoundIndexOrRangePatternIndexerAccess Update(BoundExpression argument, BoundExpression lengthOrCountAccess, BoundIndexOrRangeIndexerPatternReceiverPlaceholder receiverPlaceholder, BoundExpression indexerAccess, ImmutableArray argumentPlaceholders, TypeSymbol type) { if (argument != this.Argument || lengthOrCountAccess != this.LengthOrCountAccess || receiverPlaceholder != this.ReceiverPlaceholder || indexerAccess != this.IndexerAccess || argumentPlaceholders != this.ArgumentPlaceholders || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) { @@ -8013,7 +8014,7 @@ public BoundRecursivePattern Update(BoundTypeExpression? declaredType, MethodSym internal sealed partial class BoundListPattern : BoundObjectPattern { - public BoundListPattern(SyntaxNode syntax, ImmutableArray subpatterns, bool hasSlice, BoundExpression? lengthAccess, BoundExpression? indexerAccess, BoundListPatternReceiverPlaceholder? receiverPlaceholder, BoundListPatternUnloweredIndexPlaceholder? argumentPlaceholder, Symbol? variable, BoundExpression? variableAccess, TypeSymbol inputType, TypeSymbol narrowedType, bool hasErrors = false) + public BoundListPattern(SyntaxNode syntax, ImmutableArray subpatterns, bool hasSlice, BoundExpression? lengthAccess, BoundExpression? indexerAccess, BoundListPatternReceiverPlaceholder? receiverPlaceholder, BoundListPatternIndexPlaceholder? argumentPlaceholder, Symbol? variable, BoundExpression? variableAccess, TypeSymbol inputType, TypeSymbol narrowedType, bool hasErrors = false) : base(BoundKind.ListPattern, syntax, variable, variableAccess, inputType, narrowedType, hasErrors || subpatterns.HasErrors() || lengthAccess.HasErrors() || indexerAccess.HasErrors() || receiverPlaceholder.HasErrors() || argumentPlaceholder.HasErrors() || variableAccess.HasErrors()) { @@ -8040,11 +8041,11 @@ public BoundListPattern(SyntaxNode syntax, ImmutableArray subpatte public BoundListPatternReceiverPlaceholder? ReceiverPlaceholder { get; } - public BoundListPatternUnloweredIndexPlaceholder? ArgumentPlaceholder { get; } + public BoundListPatternIndexPlaceholder? ArgumentPlaceholder { get; } [DebuggerStepThrough] public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitListPattern(this); - public BoundListPattern Update(ImmutableArray subpatterns, bool hasSlice, BoundExpression? lengthAccess, BoundExpression? indexerAccess, BoundListPatternReceiverPlaceholder? receiverPlaceholder, BoundListPatternUnloweredIndexPlaceholder? argumentPlaceholder, Symbol? variable, BoundExpression? variableAccess, TypeSymbol inputType, TypeSymbol narrowedType) + public BoundListPattern Update(ImmutableArray subpatterns, bool hasSlice, BoundExpression? lengthAccess, BoundExpression? indexerAccess, BoundListPatternReceiverPlaceholder? receiverPlaceholder, BoundListPatternIndexPlaceholder? argumentPlaceholder, Symbol? variable, BoundExpression? variableAccess, TypeSymbol inputType, TypeSymbol narrowedType) { if (subpatterns != this.Subpatterns || hasSlice != this.HasSlice || lengthAccess != this.LengthAccess || indexerAccess != this.IndexerAccess || receiverPlaceholder != this.ReceiverPlaceholder || argumentPlaceholder != this.ArgumentPlaceholder || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(variable, this.Variable) || variableAccess != this.VariableAccess || !TypeSymbol.Equals(inputType, this.InputType, TypeCompareKind.ConsiderEverything) || !TypeSymbol.Equals(narrowedType, this.NarrowedType, TypeCompareKind.ConsiderEverything)) { @@ -8058,7 +8059,7 @@ public BoundListPattern Update(ImmutableArray subpatterns, bool ha internal sealed partial class BoundSlicePattern : BoundPattern { - public BoundSlicePattern(SyntaxNode syntax, BoundPattern? pattern, BoundExpression? indexerAccess, BoundSlicePatternReceiverPlaceholder? receiverPlaceholder, BoundSlicePatternUnloweredRangePlaceholder? argumentPlaceholder, TypeSymbol inputType, TypeSymbol narrowedType, bool hasErrors = false) + public BoundSlicePattern(SyntaxNode syntax, BoundPattern? pattern, BoundExpression? indexerAccess, BoundSlicePatternReceiverPlaceholder? receiverPlaceholder, BoundSlicePatternRangePlaceholder? argumentPlaceholder, TypeSymbol inputType, TypeSymbol narrowedType, bool hasErrors = false) : base(BoundKind.SlicePattern, syntax, inputType, narrowedType, hasErrors || pattern.HasErrors() || indexerAccess.HasErrors() || receiverPlaceholder.HasErrors() || argumentPlaceholder.HasErrors()) { @@ -8078,11 +8079,11 @@ public BoundSlicePattern(SyntaxNode syntax, BoundPattern? pattern, BoundExpressi public BoundSlicePatternReceiverPlaceholder? ReceiverPlaceholder { get; } - public BoundSlicePatternUnloweredRangePlaceholder? ArgumentPlaceholder { get; } + public BoundSlicePatternRangePlaceholder? ArgumentPlaceholder { get; } [DebuggerStepThrough] public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitSlicePattern(this); - public BoundSlicePattern Update(BoundPattern? pattern, BoundExpression? indexerAccess, BoundSlicePatternReceiverPlaceholder? receiverPlaceholder, BoundSlicePatternUnloweredRangePlaceholder? argumentPlaceholder, TypeSymbol inputType, TypeSymbol narrowedType) + public BoundSlicePattern Update(BoundPattern? pattern, BoundExpression? indexerAccess, BoundSlicePatternReceiverPlaceholder? receiverPlaceholder, BoundSlicePatternRangePlaceholder? argumentPlaceholder, TypeSymbol inputType, TypeSymbol narrowedType) { if (pattern != this.Pattern || indexerAccess != this.IndexerAccess || receiverPlaceholder != this.ReceiverPlaceholder || argumentPlaceholder != this.ArgumentPlaceholder || !TypeSymbol.Equals(inputType, this.InputType, TypeCompareKind.ConsiderEverything) || !TypeSymbol.Equals(narrowedType, this.NarrowedType, TypeCompareKind.ConsiderEverything)) { @@ -8704,12 +8705,12 @@ internal R VisitInternal(BoundNode node, A arg) return VisitIndexOrRangeIndexerPatternReceiverPlaceholder((BoundIndexOrRangeIndexerPatternReceiverPlaceholder)node, arg); case BoundKind.ListPatternReceiverPlaceholder: return VisitListPatternReceiverPlaceholder((BoundListPatternReceiverPlaceholder)node, arg); - case BoundKind.ListPatternUnloweredIndexPlaceholder: - return VisitListPatternUnloweredIndexPlaceholder((BoundListPatternUnloweredIndexPlaceholder)node, arg); + case BoundKind.ListPatternIndexPlaceholder: + return VisitListPatternIndexPlaceholder((BoundListPatternIndexPlaceholder)node, arg); case BoundKind.SlicePatternReceiverPlaceholder: return VisitSlicePatternReceiverPlaceholder((BoundSlicePatternReceiverPlaceholder)node, arg); - case BoundKind.SlicePatternUnloweredRangePlaceholder: - return VisitSlicePatternUnloweredRangePlaceholder((BoundSlicePatternUnloweredRangePlaceholder)node, arg); + case BoundKind.SlicePatternRangePlaceholder: + return VisitSlicePatternRangePlaceholder((BoundSlicePatternRangePlaceholder)node, arg); case BoundKind.Dup: return VisitDup((BoundDup)node, arg); case BoundKind.PassByCopy: @@ -9135,9 +9136,9 @@ internal abstract partial class BoundTreeVisitor public virtual R VisitIndexOrRangeIndexerPatternValuePlaceholder(BoundIndexOrRangeIndexerPatternValuePlaceholder node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitIndexOrRangeIndexerPatternReceiverPlaceholder(BoundIndexOrRangeIndexerPatternReceiverPlaceholder node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitListPatternReceiverPlaceholder(BoundListPatternReceiverPlaceholder node, A arg) => this.DefaultVisit(node, arg); - public virtual R VisitListPatternUnloweredIndexPlaceholder(BoundListPatternUnloweredIndexPlaceholder node, A arg) => this.DefaultVisit(node, arg); + public virtual R VisitListPatternIndexPlaceholder(BoundListPatternIndexPlaceholder node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitSlicePatternReceiverPlaceholder(BoundSlicePatternReceiverPlaceholder node, A arg) => this.DefaultVisit(node, arg); - public virtual R VisitSlicePatternUnloweredRangePlaceholder(BoundSlicePatternUnloweredRangePlaceholder node, A arg) => this.DefaultVisit(node, arg); + public virtual R VisitSlicePatternRangePlaceholder(BoundSlicePatternRangePlaceholder node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitDup(BoundDup node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitPassByCopy(BoundPassByCopy node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitBadExpression(BoundBadExpression node, A arg) => this.DefaultVisit(node, arg); @@ -9357,9 +9358,9 @@ internal abstract partial class BoundTreeVisitor public virtual BoundNode? VisitIndexOrRangeIndexerPatternValuePlaceholder(BoundIndexOrRangeIndexerPatternValuePlaceholder node) => this.DefaultVisit(node); public virtual BoundNode? VisitIndexOrRangeIndexerPatternReceiverPlaceholder(BoundIndexOrRangeIndexerPatternReceiverPlaceholder node) => this.DefaultVisit(node); public virtual BoundNode? VisitListPatternReceiverPlaceholder(BoundListPatternReceiverPlaceholder node) => this.DefaultVisit(node); - public virtual BoundNode? VisitListPatternUnloweredIndexPlaceholder(BoundListPatternUnloweredIndexPlaceholder node) => this.DefaultVisit(node); + public virtual BoundNode? VisitListPatternIndexPlaceholder(BoundListPatternIndexPlaceholder node) => this.DefaultVisit(node); public virtual BoundNode? VisitSlicePatternReceiverPlaceholder(BoundSlicePatternReceiverPlaceholder node) => this.DefaultVisit(node); - public virtual BoundNode? VisitSlicePatternUnloweredRangePlaceholder(BoundSlicePatternUnloweredRangePlaceholder node) => this.DefaultVisit(node); + public virtual BoundNode? VisitSlicePatternRangePlaceholder(BoundSlicePatternRangePlaceholder node) => this.DefaultVisit(node); public virtual BoundNode? VisitDup(BoundDup node) => this.DefaultVisit(node); public virtual BoundNode? VisitPassByCopy(BoundPassByCopy node) => this.DefaultVisit(node); public virtual BoundNode? VisitBadExpression(BoundBadExpression node) => this.DefaultVisit(node); @@ -9595,9 +9596,9 @@ internal abstract partial class BoundTreeWalker : BoundTreeVisitor public override BoundNode? VisitIndexOrRangeIndexerPatternValuePlaceholder(BoundIndexOrRangeIndexerPatternValuePlaceholder node) => null; public override BoundNode? VisitIndexOrRangeIndexerPatternReceiverPlaceholder(BoundIndexOrRangeIndexerPatternReceiverPlaceholder node) => null; public override BoundNode? VisitListPatternReceiverPlaceholder(BoundListPatternReceiverPlaceholder node) => null; - public override BoundNode? VisitListPatternUnloweredIndexPlaceholder(BoundListPatternUnloweredIndexPlaceholder node) => null; + public override BoundNode? VisitListPatternIndexPlaceholder(BoundListPatternIndexPlaceholder node) => null; public override BoundNode? VisitSlicePatternReceiverPlaceholder(BoundSlicePatternReceiverPlaceholder node) => null; - public override BoundNode? VisitSlicePatternUnloweredRangePlaceholder(BoundSlicePatternUnloweredRangePlaceholder node) => null; + public override BoundNode? VisitSlicePatternRangePlaceholder(BoundSlicePatternRangePlaceholder node) => null; public override BoundNode? VisitDup(BoundDup node) => null; public override BoundNode? VisitPassByCopy(BoundPassByCopy node) { @@ -10619,7 +10620,7 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor TypeSymbol? type = this.VisitType(node.Type); return node.Update(node.ValEscape, type); } - public override BoundNode? VisitListPatternUnloweredIndexPlaceholder(BoundListPatternUnloweredIndexPlaceholder node) + public override BoundNode? VisitListPatternIndexPlaceholder(BoundListPatternIndexPlaceholder node) { TypeSymbol? type = this.VisitType(node.Type); return node.Update(type); @@ -10629,7 +10630,7 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor TypeSymbol? type = this.VisitType(node.Type); return node.Update(node.ValEscape, type); } - public override BoundNode? VisitSlicePatternUnloweredRangePlaceholder(BoundSlicePatternUnloweredRangePlaceholder node) + public override BoundNode? VisitSlicePatternRangePlaceholder(BoundSlicePatternRangePlaceholder node) { TypeSymbol? type = this.VisitType(node.Type); return node.Update(type); @@ -11338,7 +11339,7 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor BoundDagTemp lengthTemp = (BoundDagTemp)this.Visit(node.LengthTemp); BoundExpression indexerAccess = (BoundExpression)this.Visit(node.IndexerAccess); BoundListPatternReceiverPlaceholder? receiverPlaceholder = (BoundListPatternReceiverPlaceholder?)this.Visit(node.ReceiverPlaceholder); - BoundListPatternUnloweredIndexPlaceholder? argumentPlaceholder = (BoundListPatternUnloweredIndexPlaceholder?)this.Visit(node.ArgumentPlaceholder); + BoundListPatternIndexPlaceholder? argumentPlaceholder = (BoundListPatternIndexPlaceholder?)this.Visit(node.ArgumentPlaceholder); BoundDagTemp input = (BoundDagTemp)this.Visit(node.Input); TypeSymbol? indexerType = this.VisitType(node.IndexerType); return node.Update(indexerType, lengthTemp, node.Index, indexerAccess, receiverPlaceholder, argumentPlaceholder, input); @@ -11348,7 +11349,7 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor BoundDagTemp lengthTemp = (BoundDagTemp)this.Visit(node.LengthTemp); BoundExpression indexerAccess = (BoundExpression)this.Visit(node.IndexerAccess); BoundSlicePatternReceiverPlaceholder? receiverPlaceholder = (BoundSlicePatternReceiverPlaceholder?)this.Visit(node.ReceiverPlaceholder); - BoundSlicePatternUnloweredRangePlaceholder? argumentPlaceholder = (BoundSlicePatternUnloweredRangePlaceholder?)this.Visit(node.ArgumentPlaceholder); + BoundSlicePatternRangePlaceholder? argumentPlaceholder = (BoundSlicePatternRangePlaceholder?)this.Visit(node.ArgumentPlaceholder); BoundDagTemp input = (BoundDagTemp)this.Visit(node.Input); TypeSymbol? sliceType = this.VisitType(node.SliceType); return node.Update(sliceType, lengthTemp, node.StartIndex, node.EndIndex, indexerAccess, receiverPlaceholder, argumentPlaceholder, input); @@ -11635,7 +11636,7 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor public override BoundNode? VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node) { BoundExpression argument = (BoundExpression)this.Visit(node.Argument); - BoundExpression? lengthOrCountAccess = (BoundExpression?)this.Visit(node.LengthOrCountAccess); + BoundExpression lengthOrCountAccess = (BoundExpression)this.Visit(node.LengthOrCountAccess); BoundIndexOrRangeIndexerPatternReceiverPlaceholder receiverPlaceholder = (BoundIndexOrRangeIndexerPatternReceiverPlaceholder)this.Visit(node.ReceiverPlaceholder); BoundExpression indexerAccess = (BoundExpression)this.Visit(node.IndexerAccess); ImmutableArray argumentPlaceholders = this.VisitList(node.ArgumentPlaceholders); @@ -11756,7 +11757,7 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor BoundExpression? lengthAccess = (BoundExpression?)this.Visit(node.LengthAccess); BoundExpression? indexerAccess = (BoundExpression?)this.Visit(node.IndexerAccess); BoundListPatternReceiverPlaceholder? receiverPlaceholder = (BoundListPatternReceiverPlaceholder?)this.Visit(node.ReceiverPlaceholder); - BoundListPatternUnloweredIndexPlaceholder? argumentPlaceholder = (BoundListPatternUnloweredIndexPlaceholder?)this.Visit(node.ArgumentPlaceholder); + BoundListPatternIndexPlaceholder? argumentPlaceholder = (BoundListPatternIndexPlaceholder?)this.Visit(node.ArgumentPlaceholder); BoundExpression? variableAccess = (BoundExpression?)this.Visit(node.VariableAccess); TypeSymbol? inputType = this.VisitType(node.InputType); TypeSymbol? narrowedType = this.VisitType(node.NarrowedType); @@ -11767,7 +11768,7 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor BoundPattern? pattern = (BoundPattern?)this.Visit(node.Pattern); BoundExpression? indexerAccess = (BoundExpression?)this.Visit(node.IndexerAccess); BoundSlicePatternReceiverPlaceholder? receiverPlaceholder = (BoundSlicePatternReceiverPlaceholder?)this.Visit(node.ReceiverPlaceholder); - BoundSlicePatternUnloweredRangePlaceholder? argumentPlaceholder = (BoundSlicePatternUnloweredRangePlaceholder?)this.Visit(node.ArgumentPlaceholder); + BoundSlicePatternRangePlaceholder? argumentPlaceholder = (BoundSlicePatternRangePlaceholder?)this.Visit(node.ArgumentPlaceholder); TypeSymbol? inputType = this.VisitType(node.InputType); TypeSymbol? narrowedType = this.VisitType(node.NarrowedType); return node.Update(pattern, indexerAccess, receiverPlaceholder, argumentPlaceholder, inputType, narrowedType); @@ -12026,14 +12027,14 @@ public NullabilityRewriter(ImmutableDictionary argumentPlaceholders = this.VisitList(node.ArgumentPlaceholders); @@ -14251,7 +14252,7 @@ public NullabilityRewriter(ImmutableDictionary new TreeDumperNode("listPatternUnloweredIndexPlaceholder", null, new TreeDumperNode[] + public override TreeDumperNode VisitListPatternIndexPlaceholder(BoundListPatternIndexPlaceholder node, object? arg) => new TreeDumperNode("listPatternIndexPlaceholder", null, new TreeDumperNode[] { new TreeDumperNode("type", node.Type, null), new TreeDumperNode("isSuppressed", node.IsSuppressed, null), @@ -14566,7 +14567,7 @@ private BoundTreeDumperNodeProducer() new TreeDumperNode("hasErrors", node.HasErrors, null) } ); - public override TreeDumperNode VisitSlicePatternUnloweredRangePlaceholder(BoundSlicePatternUnloweredRangePlaceholder node, object? arg) => new TreeDumperNode("slicePatternUnloweredRangePlaceholder", null, new TreeDumperNode[] + public override TreeDumperNode VisitSlicePatternRangePlaceholder(BoundSlicePatternRangePlaceholder node, object? arg) => new TreeDumperNode("slicePatternRangePlaceholder", null, new TreeDumperNode[] { new TreeDumperNode("type", node.Type, null), new TreeDumperNode("isSuppressed", node.IsSuppressed, null), diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.PatternLocalRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.PatternLocalRewriter.cs index a1078911cea9..bbf5c96d5818 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.PatternLocalRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.PatternLocalRewriter.cs @@ -250,8 +250,6 @@ void addArg(RefKind refKind, BoundExpression expression) // this[Index] // this[int] // array[Index] - _localRewriter.AddPlaceholderReplacement(e.ReceiverPlaceholder, input); - _localRewriter.AddPlaceholderReplacement(e.ArgumentPlaceholder, makeUnloweredIndexArgument(e.Index)); var indexerAccess = e.IndexerAccess; if (indexerAccess is BoundIndexOrRangePatternIndexerAccess implicitAccess) @@ -259,9 +257,13 @@ void addArg(RefKind refKind, BoundExpression expression) indexerAccess = implicitAccess.WithLengthOrCountAccess(_tempAllocator.GetTemp(e.LengthTemp)); } + var placeholderValues = PooledDictionary.GetInstance(); + placeholderValues.Add(e.ReceiverPlaceholder, input); + placeholderValues.Add(e.ArgumentPlaceholder, makeUnloweredIndexArgument(e.Index)); + indexerAccess = PlaceholderReplacer.Replace(placeholderValues, indexerAccess); + placeholderValues.Free(); + var access = (BoundExpression)_localRewriter.Visit(indexerAccess); - _localRewriter.RemovePlaceholderReplacement(e.ReceiverPlaceholder); - _localRewriter.RemovePlaceholderReplacement(e.ArgumentPlaceholder); var outputTemp = new BoundDagTemp(e.Syntax, e.IndexerType, e); BoundExpression output = _tempAllocator.GetTemp(outputTemp); @@ -274,8 +276,6 @@ void addArg(RefKind refKind, BoundExpression expression) // Slice(int, int) // string.Substring(int, int) // array[Range] - _localRewriter.AddPlaceholderReplacement(e.ReceiverPlaceholder, input); - _localRewriter.AddPlaceholderReplacement(e.ArgumentPlaceholder, makeUnloweredRangeArgument(e)); var indexerAccess = e.IndexerAccess; if (indexerAccess is BoundIndexOrRangePatternIndexerAccess implicitAccess) @@ -283,9 +283,13 @@ void addArg(RefKind refKind, BoundExpression expression) indexerAccess = implicitAccess.WithLengthOrCountAccess(_tempAllocator.GetTemp(e.LengthTemp)); } + var placeholderValues = PooledDictionary.GetInstance(); + placeholderValues.Add(e.ReceiverPlaceholder, input); + placeholderValues.Add(e.ArgumentPlaceholder, makeUnloweredRangeArgument(e)); + indexerAccess = PlaceholderReplacer.Replace(placeholderValues, indexerAccess); + placeholderValues.Free(); + var access = (BoundExpression)_localRewriter.Visit(indexerAccess); - _localRewriter.RemovePlaceholderReplacement(e.ReceiverPlaceholder); - _localRewriter.RemovePlaceholderReplacement(e.ArgumentPlaceholder); var outputTemp = new BoundDagTemp(e.Syntax, e.SliceType, e); BoundExpression output = _tempAllocator.GetTemp(outputTemp); diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.PlaceholderReplacer.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.PlaceholderReplacer.cs new file mode 100644 index 000000000000..4b6f0f634012 --- /dev/null +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.PlaceholderReplacer.cs @@ -0,0 +1,55 @@ +// 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. + +using System.Collections.Generic; +using System.Diagnostics; + +namespace Microsoft.CodeAnalysis.CSharp; + +internal sealed partial class LocalRewriter +{ + private sealed class PlaceholderReplacer : BoundTreeRewriterWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperator + { + private readonly Dictionary _placeholders; + + PlaceholderReplacer(Dictionary placeholders) + { + _placeholders = placeholders; + } + + public static BoundExpression Replace(Dictionary placeholders, BoundExpression expr) + { + var result = new PlaceholderReplacer(placeholders).Visit(expr); + Debug.Assert(result is not null); + return (BoundExpression)result; + } + + private BoundNode ReplacePlaceholder(BoundValuePlaceholderBase placeholder) + { + var value = _placeholders[placeholder]; + Debug.Assert(value is not null); + return value; + } + + public override BoundNode? VisitListPatternReceiverPlaceholder(BoundListPatternReceiverPlaceholder node) + { + return ReplacePlaceholder(node); + } + + public override BoundNode? VisitListPatternIndexPlaceholder(BoundListPatternIndexPlaceholder node) + { + return ReplacePlaceholder(node); + } + + public override BoundNode? VisitSlicePatternReceiverPlaceholder(BoundSlicePatternReceiverPlaceholder node) + { + return ReplacePlaceholder(node); + } + + public override BoundNode? VisitSlicePatternRangePlaceholder(BoundSlicePatternRangePlaceholder node) + { + return ReplacePlaceholder(node); + } + } +} diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs index 8f4953792fc9..3cbc6245f369 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs @@ -434,7 +434,7 @@ private void AssertNoPlaceholderReplacements() /// Each occurrence of the placeholder node is replaced with the node returned. /// Throws if there is already a substitution. /// - private void AddPlaceholderReplacement(T placeholder, BoundExpression value) where T : BoundValuePlaceholderBase + private void AddPlaceholderReplacement(BoundValuePlaceholderBase placeholder, BoundExpression value) { AssertPlaceholderReplacement(placeholder, value); @@ -971,11 +971,13 @@ internal static bool CanBePassedByReference(BoundExpression expr) case BoundKind.IndexOrRangeIndexerPatternReceiverPlaceholder: case BoundKind.IndexOrRangeIndexerPatternValuePlaceholder: case BoundKind.ListPatternReceiverPlaceholder: - case BoundKind.ListPatternUnloweredIndexPlaceholder: case BoundKind.SlicePatternReceiverPlaceholder: - case BoundKind.SlicePatternUnloweredRangePlaceholder: return true; + case BoundKind.SlicePatternRangePlaceholder: + case BoundKind.ListPatternIndexPlaceholder: + throw ExceptionUtilities.UnexpectedValue(expr.Kind); + case BoundKind.Conversion: return expr is BoundConversion { Conversion: { IsInterpolatedStringHandler: true }, Type: { IsValueType: true } }; } @@ -1075,7 +1077,7 @@ public static void Validate(BoundNode node) return null; } - public override BoundNode? VisitListPatternUnloweredIndexPlaceholder(BoundListPatternUnloweredIndexPlaceholder node) + public override BoundNode? VisitListPatternIndexPlaceholder(BoundListPatternIndexPlaceholder node) { Fail(node); return null; @@ -1087,7 +1089,7 @@ public static void Validate(BoundNode node) return null; } - public override BoundNode? VisitSlicePatternUnloweredRangePlaceholder(BoundSlicePatternUnloweredRangePlaceholder node) + public override BoundNode? VisitSlicePatternRangePlaceholder(BoundSlicePatternRangePlaceholder node) { Fail(node); return null; diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs index bd32d6b3fe4e..cd0e4a95f5a4 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs @@ -185,24 +185,24 @@ BoundExpression unwrapPlaceholderIfNeeded(BoundExpression argument) } } - public override BoundNode? VisitListPatternUnloweredIndexPlaceholder(BoundListPatternUnloweredIndexPlaceholder node) + public override BoundNode? VisitListPatternIndexPlaceholder(BoundListPatternIndexPlaceholder node) { - return Visit(PlaceholderReplacement(node)); + throw ExceptionUtilities.Unreachable; } public override BoundNode? VisitListPatternReceiverPlaceholder(BoundListPatternReceiverPlaceholder node) { - return PlaceholderReplacement(node); + throw ExceptionUtilities.Unreachable; } - public override BoundNode? VisitSlicePatternUnloweredRangePlaceholder(BoundSlicePatternUnloweredRangePlaceholder node) + public override BoundNode? VisitSlicePatternRangePlaceholder(BoundSlicePatternRangePlaceholder node) { - return Visit(PlaceholderReplacement(node)); + throw ExceptionUtilities.Unreachable; } public override BoundNode? VisitSlicePatternReceiverPlaceholder(BoundSlicePatternReceiverPlaceholder node) { - return PlaceholderReplacement(node); + throw ExceptionUtilities.Unreachable; } public override BoundNode? VisitIndexOrRangeIndexerPatternReceiverPlaceholder(BoundIndexOrRangeIndexerPatternReceiverPlaceholder node) @@ -394,11 +394,6 @@ private BoundExpression DetermineMakePatternIndexOffsetExpressionStrategy( _compilation.GetWellKnownType(WellKnownType.System_Index), TypeCompareKind.ConsiderEverything)); - if (unloweredExpr is BoundListPatternUnloweredIndexPlaceholder placeholder) - { - unloweredExpr = PlaceholderReplacement(placeholder); - } - if (unloweredExpr is BoundFromEndIndexExpression hatExpression) { // If the System.Index argument is `^index`, we can replace the @@ -453,11 +448,6 @@ private BoundSequence VisitRangePatternIndexerAccess(BoundIndexOrRangePatternInd localsBuilder.Add(receiverLocal.LocalSymbol); sideEffectsBuilder.Add(receiverStore); - if (rangeArg is BoundSlicePatternUnloweredRangePlaceholder placeholder) - { - rangeArg = PlaceholderReplacement(placeholder); - } - BoundExpression startExpr; BoundExpression rangeSizeExpr; if (rangeArg is BoundRangeExpression rangeExpr) diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/IndexAndRangeTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/IndexAndRangeTests.cs index d8b2632009b7..7ef09c190628 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/IndexAndRangeTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/IndexAndRangeTests.cs @@ -3286,7 +3286,7 @@ public static void Main() "); } - [Fact] + [Fact, WorkItem(57745, "https://github.com/dotnet/roslyn/issues/57745")] public void ObsoleteRangeType() { var source = @" @@ -3319,6 +3319,7 @@ public readonly struct Range } "; // Note: we currently don't report Obsolete diagnostic on either Index or Range + // Tracked by https://github.com/dotnet/roslyn/issues/57745 var comp = CreateCompilation(new[] { source, TestSources.Index }); comp.VerifyDiagnostics(); } diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs index 4d3a5caff352..4ae52b7ee64e 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs @@ -2241,9 +2241,6 @@ void M() "; var expectedDiagnostics = new[] { - // (8,31): error CS8977: List patterns may not be used for a value of type 'X'. - // _ = /**/this is []/**/; - Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[]").WithArguments("X").WithLocation(8, 31), // (8,31): error CS0518: Predefined type 'System.Index' is not defined or imported // _ = /**/this is []/**/; Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "[]").WithArguments("System.Index").WithLocation(8, 31) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs index 318c0a482875..b4324cf9712e 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs @@ -720,8 +720,6 @@ void M(object o) "; var expectedDiagnostics = new[] { - // (6,18): error CS9000: List patterns may not be used for a value of type 'object'. - Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, listPattern).WithArguments("object"), // error CS0021: Cannot apply indexing with [] to an expression of type 'object' Diagnostic(ErrorCode.ERR_BadIndexLHS, listPattern).WithArguments("object"), @@ -3053,9 +3051,6 @@ public void M() // (4,17): error CS0547: 'C.Length': property or indexer cannot have void type // public void Length => throw null; Diagnostic(ErrorCode.ERR_PropertyCantHaveVoidType, "Length").WithArguments("C.Length").WithLocation(4, 17), - // (8,21): error CS9000: List patterns may not be used for a value of type 'C'. - // _ = this is [1]; - Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[1]").WithArguments("C").WithLocation(8, 21), // (8,21): error CS1503: Argument 1: cannot convert from 'System.Index' to 'int' // _ = this is [1]; Diagnostic(ErrorCode.ERR_BadArgType, "[1]").WithArguments("1", "System.Index", "int").WithLocation(8, 21), @@ -3083,9 +3078,6 @@ public void M() "; var compilation = CreateCompilation(new[] { source, TestSources.Index }); compilation.VerifyEmitDiagnostics( - // (9,21): error CS9000: List patterns may not be used for a value of type 'C'. - // _ = this is [1]; - Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[1]").WithArguments("C").WithLocation(9, 21), // (9,21): error CS1503: Argument 1: cannot convert from 'System.Index' to 'int' // _ = this is [1]; Diagnostic(ErrorCode.ERR_BadArgType, "[1]").WithArguments("1", "System.Index", "int").WithLocation(9, 21), @@ -3855,9 +3847,6 @@ public void M() "; var compilation = CreateCompilationWithIL(new[] { source, TestSources.Index, TestSources.Range }, il); compilation.VerifyEmitDiagnostics( - // (6,24): error CS9000: List patterns may not be used for a value of type 'C'. - // _ = new C() is [var item, ..var rest]; - Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[var item, ..var rest]").WithArguments("C").WithLocation(6, 24), // (6,24): error CS0021: Cannot apply indexing with [] to an expression of type 'C' // _ = new C() is [var item, ..var rest]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "[var item, ..var rest]").WithArguments("C").WithLocation(6, 24), @@ -4082,9 +4071,6 @@ public void M() "; var compilation = CreateCompilationWithIL(source, il); compilation.VerifyEmitDiagnostics( - // (6,24): error CS9000: List patterns may not be used for a value of type 'C'. - // _ = new C() is [var item, ..var rest]; - Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[var item, ..var rest]").WithArguments("C").WithLocation(6, 24), // (6,24): error CS0021: Cannot apply indexing with [] to an expression of type 'C' // _ = new C() is [var item, ..var rest]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "[var item, ..var rest]").WithArguments("C").WithLocation(6, 24), @@ -4938,7 +4924,7 @@ public void ListPattern_Exhaustiveness_Count() { { Count: 0 } => 0, // missing - { _, _, .. } => 2, + [ _, _, .. ] => 2, }; _ = new C() switch // 3 @@ -4967,9 +4953,9 @@ class C // (2,13): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{ Count: 2 }' is not covered. // _ = new C() switch // 1 Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("{ Count: 2 }").WithLocation(2, 13), - // (13,7): error CS8503: A property subpattern requires a reference to the property or field to be matched, e.g. '{ Name: _ }' - // { _, _, .. } => 2, - Diagnostic(ErrorCode.ERR_PropertyPatternNameMissing, "_").WithArguments("_").WithLocation(13, 7), + // (9,13): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{ Count: 1 }' is not covered. + // _ = new C() switch // 2 + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("{ Count: 1 }").WithLocation(9, 13), // (16,13): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{ Count: 1 }' is not covered. // _ = new C() switch // 3 Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("{ Count: 1 }").WithLocation(16, 13) @@ -4977,15 +4963,27 @@ class C comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( + // (2,13): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{ Count: 2 }' is not covered. + // _ = new C() switch // 1 + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("{ Count: 2 }").WithLocation(2, 13), // (5,5): error CS0518: Predefined type 'System.Index' is not defined or imported // [_] => 1, Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "[_]").WithArguments("System.Index").WithLocation(5, 5), // (5,5): error CS0656: Missing compiler required member 'System.Index.GetOffset' // [_] => 1, Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[_]").WithArguments("System.Index", "GetOffset").WithLocation(5, 5), - // (13,7): error CS8503: A property subpattern requires a reference to the property or field to be matched, e.g. '{ Name: _ }' - // { _, _, .. } => 2, - Diagnostic(ErrorCode.ERR_PropertyPatternNameMissing, "_").WithArguments("_").WithLocation(13, 7), + // (9,13): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{ Count: 1 }' is not covered. + // _ = new C() switch // 2 + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("{ Count: 1 }").WithLocation(9, 13), + // (13,5): error CS0518: Predefined type 'System.Index' is not defined or imported + // [ _, _, .. ] => 2, + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "[ _, _, .. ]").WithArguments("System.Index").WithLocation(13, 5), + // (13,5): error CS0656: Missing compiler required member 'System.Index.GetOffset' + // [ _, _, .. ] => 2, + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[ _, _, .. ]").WithArguments("System.Index", "GetOffset").WithLocation(13, 5), + // (16,13): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{ Count: 1 }' is not covered. + // _ = new C() switch // 3 + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("{ Count: 1 }").WithLocation(16, 13), // (20,5): error CS0518: Predefined type 'System.Index' is not defined or imported // [_, _] => 2, Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "[_, _]").WithArguments("System.Index").WithLocation(20, 5), @@ -5300,9 +5298,6 @@ class C }"; var comp = CreateCompilation(new[] { src, TestSources.Index }); comp.VerifyEmitDiagnostics( - // (4,5): error CS9000: List patterns may not be used for a value of type 'C'. - // [..] => 1, - Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[..]").WithArguments("C").WithLocation(4, 5), // (4,5): error CS1503: Argument 1: cannot convert from 'System.Index' to 'int' // [..] => 1, Diagnostic(ErrorCode.ERR_BadArgType, "[..]").WithArguments("1", "System.Index", "int").WithLocation(4, 5), @@ -5330,9 +5325,6 @@ class C }"; var comp = CreateCompilation(new[] { src, TestSources.Index }); comp.VerifyEmitDiagnostics( - // (4,5): error CS9000: List patterns may not be used for a value of type 'C'. - // [..] => 1, - Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[..]").WithArguments("C").WithLocation(4, 5), // (4,5): error CS1503: Argument 1: cannot convert from 'System.Index' to 'int' // [..] => 1, Diagnostic(ErrorCode.ERR_BadArgType, "[..]").WithArguments("1", "System.Index", "int").WithLocation(4, 5), @@ -6822,9 +6814,6 @@ class C "; var compilation = CreateCompilationWithIndex(source); compilation.VerifyEmitDiagnostics( - // (2,16): error CS9000: List patterns may not be used for a value of type 'C'. - // _ = new C() is []; - Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[]").WithArguments("C").WithLocation(2, 16), // (2,16): error CS1503: Argument 1: cannot convert from 'System.Index' to 'int' // _ = new C() is []; Diagnostic(ErrorCode.ERR_BadArgType, "[]").WithArguments("1", "System.Index", "int").WithLocation(2, 16), @@ -6971,9 +6960,6 @@ public int this[System.Range r] { set { } } "; var comp = CreateCompilation(new[] { source, TestSources.Index, TestSources.Range }); comp.VerifyEmitDiagnostics( - // (2,16): error CS9000: List patterns may not be used for a value of type 'C'. - // _ = new C() is [var x, .. var y]; // 1, 2, 3 - Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[var x, .. var y]").WithArguments("C").WithLocation(2, 16), // (2,16): error CS0154: The property or indexer 'C.this[Index]' cannot be used in this context because it lacks the get accessor // _ = new C() is [var x, .. var y]; // 1, 2, 3 Diagnostic(ErrorCode.ERR_PropertyLacksGet, "[var x, .. var y]").WithArguments("C.this[System.Index]").WithLocation(2, 16), From adfee13e2ae0c26715626b3b4afbf651e3757884 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Fri, 12 Nov 2021 16:41:11 -0800 Subject: [PATCH 13/29] Address feedback (2) --- .../Portable/Binder/Binder_Expressions.cs | 55 +++++++---------- .../CSharp/Portable/Binder/Binder_Patterns.cs | 11 +++- .../Portable/Binder/Binder_Statements.cs | 12 ++-- .../Binder/DecisionDagBuilder_ListPatterns.cs | 1 + .../Portable/BoundTree/BoundDagEvaluation.cs | 4 +- .../BoundIndexOrRangePatternIndexerAccess.cs | 1 - .../NullableWalker.DebugVerifier.cs | 6 ++ .../Portable/FlowAnalysis/NullableWalker.cs | 8 --- .../Generated/BoundNodes.xml.Generated.cs | 61 +++++++++---------- .../LocalRewriter.PatternLocalRewriter.cs | 12 ++-- .../LocalRewriter.PlaceholderReplacer.cs | 11 ++++ .../Lowering/LocalRewriter/LocalRewriter.cs | 9 ++- .../LocalRewriter_IndexerAccess.cs | 29 ++++----- .../Operations/CSharpOperationFactory.cs | 6 +- .../Symbols/Source/SourceLocalSymbol.cs | 2 +- .../IOperationTests_IIsPatternExpression.cs | 6 +- .../PatternMatchingTests_ListPatterns.cs | 24 ++++---- .../Core/Portable/WellKnownMember.cs | 2 + .../Core/Portable/WellKnownMembers.cs | 10 +++ 19 files changed, 146 insertions(+), 124 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index 86361ed0a024..e09a075df1d8 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -8105,7 +8105,6 @@ private bool TryBindIndexOrRangeImplicitIndexer( checkWellKnown(WellKnownMember.System_Range__get_End); } checkWellKnown(WellKnownMember.System_Index__GetOffset); - // TODO2 check for conversion operator from int? _ = MessageID.IDS_FeatureIndexOperator.CheckFeatureAvailability(diagnostics, syntax); if (arguments.Names.Count > 0) @@ -8152,21 +8151,15 @@ private bool TryBindIndexOrRangeImplicitIndexerParts( // 2. For Index: Has an accessible indexer with a single int parameter // For Range: Has an accessible Slice method that takes two int parameters - var lookupResult = LookupResult.GetInstance(); - if (TryBindLengthOrCount(syntax, receiverPlaceholder, out lengthOrCountAccess, diagnostics) && - TryBindIndexOrRangeImplicitIndexer(syntax, lookupResult, receiver, argIsIndex, out implicitIndexerAccess, out argumentPlaceholders, diagnostics)) + TryBindIndexOrRangeImplicitIndexer(syntax, receiver, argIsIndex, out implicitIndexerAccess, out argumentPlaceholders, diagnostics)) { - CheckValue(lengthOrCountAccess, BindValueKind.RValue, diagnostics); - - lookupResult.Free(); return true; } lengthOrCountAccess = null; implicitIndexerAccess = null; argumentPlaceholders = default; - lookupResult.Free(); return false; } @@ -8181,8 +8174,10 @@ private bool TryBindLengthOrCount( Debug.Assert(receiver.Type is not null); if (TryLookupLengthOrCount(syntax, receiver.Type, lookupResult, out var lengthOrCountProperty, diagnostics)) { - lengthOrCountAccess = BindPropertyAccess(syntax, receiver, lengthOrCountProperty, diagnostics, lookupResult.Kind, hasErrors: false).MakeCompilerGenerated(); + diagnostics.ReportUseSite(lengthOrCountProperty, syntax); ReportDiagnosticsIfObsolete(diagnostics, lengthOrCountProperty, syntax, hasBaseReceiver: false); + lengthOrCountAccess = BindPropertyAccess(syntax, receiver, lengthOrCountProperty, diagnostics, lookupResult.Kind, hasErrors: false).MakeCompilerGenerated(); + lengthOrCountAccess = CheckValue(lengthOrCountAccess, BindValueKind.RValue, diagnostics); lookupResult.Free(); return true; @@ -8201,7 +8196,6 @@ private bool TryBindLengthOrCount( /// private bool TryBindIndexOrRangeImplicitIndexer( SyntaxNode syntax, - LookupResult lookupResult, BoundExpression receiver, bool argIsIndex, [NotNullWhen(true)] out BoundExpression? indexerOrSliceAccess, @@ -8210,8 +8204,8 @@ private bool TryBindIndexOrRangeImplicitIndexer( { Debug.Assert(receiver.Type is not null); var useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); + var lookupResult = LookupResult.GetInstance(); - Debug.Assert(lookupResult.IsClear); if (argIsIndex) { // Look for `T this[int i]` indexer @@ -8247,6 +8241,7 @@ candidate is PropertySymbol property && indexerOrSliceAccess = BindIndexerOrIndexedPropertyAccess(syntax, receiver, properties, analyzedArguments, diagnostics).MakeCompilerGenerated(); properties.Free(); analyzedArguments.Free(); + lookupResult.Free(); return true; } } @@ -8260,6 +8255,7 @@ candidate is PropertySymbol property && if (substring is object) { makeCall(syntax, receiver, substring, out indexerOrSliceAccess, out argumentPlaceholders); + lookupResult.Free(); return true; } } @@ -8292,11 +8288,8 @@ method.OriginalDefinition is var original && original.Parameters[0] is { Type: { SpecialType: SpecialType.System_Int32 }, RefKind: RefKind.None } && original.Parameters[1] is { Type: { SpecialType: SpecialType.System_Int32 }, RefKind: RefKind.None }) { - method.AddUseSiteInfo(ref useSiteInfo); - diagnostics.ReportUseSite(method, syntax); - ReportDiagnosticsIfObsolete(diagnostics, method, syntax, hasBaseReceiver: false); - CheckImplicitThisCopyInReadOnlyMember(receiver, method, diagnostics); makeCall(syntax, receiver, method, out indexerOrSliceAccess, out argumentPlaceholders); + lookupResult.Free(); return true; } } @@ -8305,30 +8298,28 @@ method.OriginalDefinition is var original && indexerOrSliceAccess = null; argumentPlaceholders = default; + lookupResult.Free(); return false; void makeCall(SyntaxNode syntax, BoundExpression receiver, MethodSymbol method, out BoundExpression indexerOrSliceAccess, out ImmutableArray argumentPlaceholders) { - // TODO2 make a property group and reuse an existing binding method? var startArgumentPlaceholder = new BoundIndexOrRangeIndexerPatternValuePlaceholder(syntax, Compilation.GetSpecialType(SpecialType.System_Int32)) { WasCompilerGenerated = true }; - var endArgumentPlaceholder = new BoundIndexOrRangeIndexerPatternValuePlaceholder(syntax, Compilation.GetSpecialType(SpecialType.System_Int32)) { WasCompilerGenerated = true }; - argumentPlaceholders = ImmutableArray.Create(startArgumentPlaceholder, endArgumentPlaceholder); + var lengthArgumentPlaceholder = new BoundIndexOrRangeIndexerPatternValuePlaceholder(syntax, Compilation.GetSpecialType(SpecialType.System_Int32)) { WasCompilerGenerated = true }; + argumentPlaceholders = ImmutableArray.Create(startArgumentPlaceholder, lengthArgumentPlaceholder); - indexerOrSliceAccess = new BoundCall( - syntax, - receiver, - method, - ImmutableArray.Create(startArgumentPlaceholder, endArgumentPlaceholder), - argumentNamesOpt: default, - argumentRefKindsOpt: default, - isDelegateCall: false, - expanded: false, - invokedAsExtensionMethod: false, - argsToParamsOpt: default, - defaultArguments: default, - resultKind: LookupResultKind.Viable, - type: method.ReturnType); + var analyzedArguments = AnalyzedArguments.GetInstance(); + analyzedArguments.Arguments.Add(startArgumentPlaceholder); + analyzedArguments.Arguments.Add(lengthArgumentPlaceholder); + + var boundMethodGroup = new BoundMethodGroup( + syntax, typeArgumentsOpt: default, method.Name, ImmutableArray.Create(method), + method, lookupError: null, BoundMethodGroupFlags.None, functionType: null, receiver, LookupResultKind.Viable); + + indexerOrSliceAccess = BindMethodGroupInvocation(syntax, syntax, method.Name, boundMethodGroup, analyzedArguments, + diagnostics, queryClause: null, allowUnexpandedForm: false, anyApplicableCandidates: out bool _); + + analyzedArguments.Free(); } } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs index 1df41e3d4ad3..6900fd663b55 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs @@ -358,8 +358,6 @@ private bool BindLengthAndIndexerForListPattern(SyntaxNode node, TypeSymbol inpu hasErrors |= !TryBindLengthOrCount(node, receiverPlaceholder, out lengthAccess, bindingDiagnostics); } - lengthAccess = CheckValue(lengthAccess, BindValueKind.RValue, diagnostics); - var analyzedArguments = AnalyzedArguments.GetInstance(); var systemIndexType = GetWellKnownType(WellKnownType.System_Index, bindingDiagnostics, node); argumentPlaceholder = new BoundListPatternIndexPlaceholder(node, systemIndexType) { WasCompilerGenerated = true }; @@ -371,6 +369,15 @@ private bool BindLengthAndIndexerForListPattern(SyntaxNode node, TypeSymbol inpu analyzedArguments.Free(); diagnostics.AddRangeAndFree(bindingDiagnostics); + + if (!systemIndexType.HasUseSiteError) + { + // Check required well-known member. They may not be needed + // during lowering, but it's simpler to always require them to prevent + // the user from getting surprising errors when optimizations fail + _ = GetWellKnownTypeMember(WellKnownMember.System_Index__op_Implicit_FromInt32, diagnostics, syntax: node); + } + return !hasErrors && lengthAccess?.HasErrors == false && !indexerAccess.HasErrors; } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index 184931669917..9380c2dd9922 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -1599,15 +1599,19 @@ internal static PropertySymbol GetPropertySymbol(BoundExpression expr, out Bound } #nullable enable - internal static Symbol? GetIndexerSymbol(BoundExpression? e) + internal static Symbol? GetIndexerOrImplicitIndexerSymbol(BoundExpression? e) { return e switch { null => null, - BoundIndexOrRangePatternIndexerAccess implicitIndexerAccess => GetIndexerSymbol(implicitIndexerAccess.IndexerAccess), + // this[Index], this[Range] BoundIndexerAccess indexerAccess => indexerAccess.Indexer, - BoundCall call => call.Method, - BoundArrayAccess arrayAccess => arrayAccess.ExpressionSymbol, + // Slice(int, int), Substring(int, int) + BoundIndexOrRangePatternIndexerAccess { IndexerAccess: BoundCall call } => call.Method, + // this[int] + BoundIndexOrRangePatternIndexerAccess { IndexerAccess: BoundIndexerAccess indexerAccess } => indexerAccess.Indexer, + // array[int] + BoundArrayAccess => null, BoundBadExpression => null, _ => throw ExceptionUtilities.UnexpectedValue(e.Kind) }; diff --git a/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder_ListPatterns.cs b/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder_ListPatterns.cs index 3b5790f62f3d..1a23e72c651d 100644 --- a/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder_ListPatterns.cs +++ b/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder_ListPatterns.cs @@ -37,6 +37,7 @@ private Tests MakeTestsAndBindingsForListPattern(BoundDagTemp input, BoundListPa { Debug.Assert(list.LengthAccess is not null); var lengthProperty = Binder.GetPropertySymbol(list.LengthAccess, out _, out _); + Debug.Assert(lengthProperty is not null); var lengthEvaluation = new BoundDagPropertyEvaluation(syntax, lengthProperty, isLengthOrCount: true, input); tests.Add(new Tests.One(lengthEvaluation)); var lengthTemp = new BoundDagTemp(syntax, _compilation.GetSpecialType(SpecialType.System_Int32), lengthEvaluation); diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundDagEvaluation.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundDagEvaluation.cs index 1cd52e33625b..5aec0f98c779 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundDagEvaluation.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundDagEvaluation.cs @@ -39,8 +39,8 @@ private Symbol? Symbol BoundDagTypeEvaluation e => e.Type, BoundDagDeconstructEvaluation e => e.DeconstructMethod, BoundDagIndexEvaluation e => e.Property, - BoundDagSliceEvaluation e => Binder.GetIndexerSymbol(e.IndexerAccess), - BoundDagIndexerEvaluation e => Binder.GetIndexerSymbol(e.IndexerAccess), + BoundDagSliceEvaluation e => Binder.GetIndexerOrImplicitIndexerSymbol(e.IndexerAccess), + BoundDagIndexerEvaluation e => Binder.GetIndexerOrImplicitIndexerSymbol(e.IndexerAccess), BoundDagAssignmentEvaluation => null, _ => throw ExceptionUtilities.UnexpectedValue(this.Kind) }; diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundIndexOrRangePatternIndexerAccess.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundIndexOrRangePatternIndexerAccess.cs index 5e4c86eb9336..1774249b84a3 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundIndexOrRangePatternIndexerAccess.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundIndexOrRangePatternIndexerAccess.cs @@ -30,6 +30,5 @@ internal BoundExpression GetReceiver() Debug.Assert(receiver is not null); return receiver; } - } } diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.DebugVerifier.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.DebugVerifier.cs index 5671ba2698c2..c5566fe60179 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.DebugVerifier.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.DebugVerifier.cs @@ -273,6 +273,12 @@ private void VisitBinaryOperatorChildren(BoundBinaryOperatorBase node) base.VisitInterpolatedString(node); return null; } + + public override BoundNode? VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node) + { + Visit(node.IndexerAccess); + return null; + } } #endif } diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index c715a5005b05..1f5a78b6ea90 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -8697,14 +8697,6 @@ private TypeWithAnnotations GetDeclaredParameterResult(ParameterSymbol parameter public override BoundNode? VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node) { - BoundExpression receiver = node.GetReceiver(); - var receiverType = VisitRvalueWithState(receiver).Type; - // https://github.com/dotnet/roslyn/issues/30598: Mark receiver as not null - // after indices have been visited, and only if the receiver has not changed. - _ = CheckPossibleNullReceiver(receiver); - - VisitRvalue(node.Argument); - VisitRvalue(node.LengthOrCountAccess); VisitRvalue(node.IndexerAccess); return null; } diff --git a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs index 317deef47a8f..993e91923d79 100644 --- a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs @@ -5422,13 +5422,15 @@ public BoundDagIndexEvaluation Update(PropertySymbol property, int index, BoundD internal sealed partial class BoundDagIndexerEvaluation : BoundDagEvaluation { - public BoundDagIndexerEvaluation(SyntaxNode syntax, TypeSymbol indexerType, BoundDagTemp lengthTemp, int index, BoundExpression indexerAccess, BoundListPatternReceiverPlaceholder? receiverPlaceholder, BoundListPatternIndexPlaceholder? argumentPlaceholder, BoundDagTemp input, bool hasErrors = false) + public BoundDagIndexerEvaluation(SyntaxNode syntax, TypeSymbol indexerType, BoundDagTemp lengthTemp, int index, BoundExpression indexerAccess, BoundListPatternReceiverPlaceholder receiverPlaceholder, BoundListPatternIndexPlaceholder argumentPlaceholder, BoundDagTemp input, bool hasErrors = false) : base(BoundKind.DagIndexerEvaluation, syntax, input, hasErrors || lengthTemp.HasErrors() || indexerAccess.HasErrors() || receiverPlaceholder.HasErrors() || argumentPlaceholder.HasErrors() || input.HasErrors()) { RoslynDebug.Assert(indexerType is object, "Field 'indexerType' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); RoslynDebug.Assert(lengthTemp is object, "Field 'lengthTemp' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); RoslynDebug.Assert(indexerAccess is object, "Field 'indexerAccess' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); + RoslynDebug.Assert(receiverPlaceholder is object, "Field 'receiverPlaceholder' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); + RoslynDebug.Assert(argumentPlaceholder is object, "Field 'argumentPlaceholder' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); RoslynDebug.Assert(input is object, "Field 'input' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); this.IndexerType = indexerType; @@ -5448,13 +5450,13 @@ public BoundDagIndexerEvaluation(SyntaxNode syntax, TypeSymbol indexerType, Boun public BoundExpression IndexerAccess { get; } - public BoundListPatternReceiverPlaceholder? ReceiverPlaceholder { get; } + public BoundListPatternReceiverPlaceholder ReceiverPlaceholder { get; } - public BoundListPatternIndexPlaceholder? ArgumentPlaceholder { get; } + public BoundListPatternIndexPlaceholder ArgumentPlaceholder { get; } [DebuggerStepThrough] public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitDagIndexerEvaluation(this); - public BoundDagIndexerEvaluation Update(TypeSymbol indexerType, BoundDagTemp lengthTemp, int index, BoundExpression indexerAccess, BoundListPatternReceiverPlaceholder? receiverPlaceholder, BoundListPatternIndexPlaceholder? argumentPlaceholder, BoundDagTemp input) + public BoundDagIndexerEvaluation Update(TypeSymbol indexerType, BoundDagTemp lengthTemp, int index, BoundExpression indexerAccess, BoundListPatternReceiverPlaceholder receiverPlaceholder, BoundListPatternIndexPlaceholder argumentPlaceholder, BoundDagTemp input) { if (!TypeSymbol.Equals(indexerType, this.IndexerType, TypeCompareKind.ConsiderEverything) || lengthTemp != this.LengthTemp || index != this.Index || indexerAccess != this.IndexerAccess || receiverPlaceholder != this.ReceiverPlaceholder || argumentPlaceholder != this.ArgumentPlaceholder || input != this.Input) { @@ -5468,13 +5470,15 @@ public BoundDagIndexerEvaluation Update(TypeSymbol indexerType, BoundDagTemp len internal sealed partial class BoundDagSliceEvaluation : BoundDagEvaluation { - public BoundDagSliceEvaluation(SyntaxNode syntax, TypeSymbol sliceType, BoundDagTemp lengthTemp, int startIndex, int endIndex, BoundExpression indexerAccess, BoundSlicePatternReceiverPlaceholder? receiverPlaceholder, BoundSlicePatternRangePlaceholder? argumentPlaceholder, BoundDagTemp input, bool hasErrors = false) + public BoundDagSliceEvaluation(SyntaxNode syntax, TypeSymbol sliceType, BoundDagTemp lengthTemp, int startIndex, int endIndex, BoundExpression indexerAccess, BoundSlicePatternReceiverPlaceholder receiverPlaceholder, BoundSlicePatternRangePlaceholder argumentPlaceholder, BoundDagTemp input, bool hasErrors = false) : base(BoundKind.DagSliceEvaluation, syntax, input, hasErrors || lengthTemp.HasErrors() || indexerAccess.HasErrors() || receiverPlaceholder.HasErrors() || argumentPlaceholder.HasErrors() || input.HasErrors()) { RoslynDebug.Assert(sliceType is object, "Field 'sliceType' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); RoslynDebug.Assert(lengthTemp is object, "Field 'lengthTemp' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); RoslynDebug.Assert(indexerAccess is object, "Field 'indexerAccess' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); + RoslynDebug.Assert(receiverPlaceholder is object, "Field 'receiverPlaceholder' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); + RoslynDebug.Assert(argumentPlaceholder is object, "Field 'argumentPlaceholder' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); RoslynDebug.Assert(input is object, "Field 'input' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); this.SliceType = sliceType; @@ -5497,13 +5501,13 @@ public BoundDagSliceEvaluation(SyntaxNode syntax, TypeSymbol sliceType, BoundDag public BoundExpression IndexerAccess { get; } - public BoundSlicePatternReceiverPlaceholder? ReceiverPlaceholder { get; } + public BoundSlicePatternReceiverPlaceholder ReceiverPlaceholder { get; } - public BoundSlicePatternRangePlaceholder? ArgumentPlaceholder { get; } + public BoundSlicePatternRangePlaceholder ArgumentPlaceholder { get; } [DebuggerStepThrough] public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitDagSliceEvaluation(this); - public BoundDagSliceEvaluation Update(TypeSymbol sliceType, BoundDagTemp lengthTemp, int startIndex, int endIndex, BoundExpression indexerAccess, BoundSlicePatternReceiverPlaceholder? receiverPlaceholder, BoundSlicePatternRangePlaceholder? argumentPlaceholder, BoundDagTemp input) + public BoundDagSliceEvaluation Update(TypeSymbol sliceType, BoundDagTemp lengthTemp, int startIndex, int endIndex, BoundExpression indexerAccess, BoundSlicePatternReceiverPlaceholder receiverPlaceholder, BoundSlicePatternRangePlaceholder argumentPlaceholder, BoundDagTemp input) { if (!TypeSymbol.Equals(sliceType, this.SliceType, TypeCompareKind.ConsiderEverything) || lengthTemp != this.LengthTemp || startIndex != this.StartIndex || endIndex != this.EndIndex || indexerAccess != this.IndexerAccess || receiverPlaceholder != this.ReceiverPlaceholder || argumentPlaceholder != this.ArgumentPlaceholder || input != this.Input) { @@ -10372,9 +10376,7 @@ internal abstract partial class BoundTreeWalker : BoundTreeVisitor public override BoundNode? VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node) { this.Visit(node.Argument); - this.Visit(node.LengthOrCountAccess); this.Visit(node.ReceiverPlaceholder); - this.Visit(node.IndexerAccess); this.VisitList(node.ArgumentPlaceholders); return null; } @@ -10453,8 +10455,6 @@ internal abstract partial class BoundTreeWalker : BoundTreeVisitor public override BoundNode? VisitListPattern(BoundListPattern node) { this.VisitList(node.Subpatterns); - this.Visit(node.LengthAccess); - this.Visit(node.IndexerAccess); this.Visit(node.ReceiverPlaceholder); this.Visit(node.ArgumentPlaceholder); this.Visit(node.VariableAccess); @@ -10463,7 +10463,6 @@ internal abstract partial class BoundTreeWalker : BoundTreeVisitor public override BoundNode? VisitSlicePattern(BoundSlicePattern node) { this.Visit(node.Pattern); - this.Visit(node.IndexerAccess); this.Visit(node.ReceiverPlaceholder); this.Visit(node.ArgumentPlaceholder); return null; @@ -11338,8 +11337,8 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor { BoundDagTemp lengthTemp = (BoundDagTemp)this.Visit(node.LengthTemp); BoundExpression indexerAccess = (BoundExpression)this.Visit(node.IndexerAccess); - BoundListPatternReceiverPlaceholder? receiverPlaceholder = (BoundListPatternReceiverPlaceholder?)this.Visit(node.ReceiverPlaceholder); - BoundListPatternIndexPlaceholder? argumentPlaceholder = (BoundListPatternIndexPlaceholder?)this.Visit(node.ArgumentPlaceholder); + BoundListPatternReceiverPlaceholder receiverPlaceholder = (BoundListPatternReceiverPlaceholder)this.Visit(node.ReceiverPlaceholder); + BoundListPatternIndexPlaceholder argumentPlaceholder = (BoundListPatternIndexPlaceholder)this.Visit(node.ArgumentPlaceholder); BoundDagTemp input = (BoundDagTemp)this.Visit(node.Input); TypeSymbol? indexerType = this.VisitType(node.IndexerType); return node.Update(indexerType, lengthTemp, node.Index, indexerAccess, receiverPlaceholder, argumentPlaceholder, input); @@ -11348,8 +11347,8 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor { BoundDagTemp lengthTemp = (BoundDagTemp)this.Visit(node.LengthTemp); BoundExpression indexerAccess = (BoundExpression)this.Visit(node.IndexerAccess); - BoundSlicePatternReceiverPlaceholder? receiverPlaceholder = (BoundSlicePatternReceiverPlaceholder?)this.Visit(node.ReceiverPlaceholder); - BoundSlicePatternRangePlaceholder? argumentPlaceholder = (BoundSlicePatternRangePlaceholder?)this.Visit(node.ArgumentPlaceholder); + BoundSlicePatternReceiverPlaceholder receiverPlaceholder = (BoundSlicePatternReceiverPlaceholder)this.Visit(node.ReceiverPlaceholder); + BoundSlicePatternRangePlaceholder argumentPlaceholder = (BoundSlicePatternRangePlaceholder)this.Visit(node.ArgumentPlaceholder); BoundDagTemp input = (BoundDagTemp)this.Visit(node.Input); TypeSymbol? sliceType = this.VisitType(node.SliceType); return node.Update(sliceType, lengthTemp, node.StartIndex, node.EndIndex, indexerAccess, receiverPlaceholder, argumentPlaceholder, input); @@ -11636,9 +11635,9 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor public override BoundNode? VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node) { BoundExpression argument = (BoundExpression)this.Visit(node.Argument); - BoundExpression lengthOrCountAccess = (BoundExpression)this.Visit(node.LengthOrCountAccess); + BoundExpression lengthOrCountAccess = node.LengthOrCountAccess; BoundIndexOrRangeIndexerPatternReceiverPlaceholder receiverPlaceholder = (BoundIndexOrRangeIndexerPatternReceiverPlaceholder)this.Visit(node.ReceiverPlaceholder); - BoundExpression indexerAccess = (BoundExpression)this.Visit(node.IndexerAccess); + BoundExpression indexerAccess = node.IndexerAccess; ImmutableArray argumentPlaceholders = this.VisitList(node.ArgumentPlaceholders); TypeSymbol? type = this.VisitType(node.Type); return node.Update(argument, lengthOrCountAccess, receiverPlaceholder, indexerAccess, argumentPlaceholders, type); @@ -11754,8 +11753,8 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor public override BoundNode? VisitListPattern(BoundListPattern node) { ImmutableArray subpatterns = this.VisitList(node.Subpatterns); - BoundExpression? lengthAccess = (BoundExpression?)this.Visit(node.LengthAccess); - BoundExpression? indexerAccess = (BoundExpression?)this.Visit(node.IndexerAccess); + BoundExpression? lengthAccess = node.LengthAccess; + BoundExpression? indexerAccess = node.IndexerAccess; BoundListPatternReceiverPlaceholder? receiverPlaceholder = (BoundListPatternReceiverPlaceholder?)this.Visit(node.ReceiverPlaceholder); BoundListPatternIndexPlaceholder? argumentPlaceholder = (BoundListPatternIndexPlaceholder?)this.Visit(node.ArgumentPlaceholder); BoundExpression? variableAccess = (BoundExpression?)this.Visit(node.VariableAccess); @@ -11766,7 +11765,7 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor public override BoundNode? VisitSlicePattern(BoundSlicePattern node) { BoundPattern? pattern = (BoundPattern?)this.Visit(node.Pattern); - BoundExpression? indexerAccess = (BoundExpression?)this.Visit(node.IndexerAccess); + BoundExpression? indexerAccess = node.IndexerAccess; BoundSlicePatternReceiverPlaceholder? receiverPlaceholder = (BoundSlicePatternReceiverPlaceholder?)this.Visit(node.ReceiverPlaceholder); BoundSlicePatternRangePlaceholder? argumentPlaceholder = (BoundSlicePatternRangePlaceholder?)this.Visit(node.ArgumentPlaceholder); TypeSymbol? inputType = this.VisitType(node.InputType); @@ -13256,8 +13255,8 @@ public NullabilityRewriter(ImmutableDictionary argumentPlaceholders = this.VisitList(node.ArgumentPlaceholders); BoundIndexOrRangePatternIndexerAccess updatedNode; @@ -14249,8 +14248,8 @@ public NullabilityRewriter(ImmutableDictionary subpatterns = this.VisitList(node.Subpatterns); - BoundExpression? lengthAccess = (BoundExpression?)this.Visit(node.LengthAccess); - BoundExpression? indexerAccess = (BoundExpression?)this.Visit(node.IndexerAccess); + BoundExpression? lengthAccess = node.LengthAccess; + BoundExpression? indexerAccess = node.IndexerAccess; BoundListPatternReceiverPlaceholder? receiverPlaceholder = (BoundListPatternReceiverPlaceholder?)this.Visit(node.ReceiverPlaceholder); BoundListPatternIndexPlaceholder? argumentPlaceholder = (BoundListPatternIndexPlaceholder?)this.Visit(node.ArgumentPlaceholder); BoundExpression? variableAccess = (BoundExpression?)this.Visit(node.VariableAccess); @@ -14262,7 +14261,7 @@ public NullabilityRewriter(ImmutableDictionary (IPatternOperation)fac.Create(p), this), declaredSymbol: boundNode.Variable.GetPublicSymbol(), inputType: boundNode.InputType.GetPublicSymbol(), diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceLocalSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceLocalSymbol.cs index 5e2bbf04a9aa..aea0db3944c6 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceLocalSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceLocalSymbol.cs @@ -806,7 +806,7 @@ protected override TypeWithAnnotations InferTypeOfVarVariable(BindingDiagnosticB if (this._type == null) { - Debug.Assert(this.DeclarationKind is LocalDeclarationKind.DeclarationExpressionVariable); + Debug.Assert(this.DeclarationKind == LocalDeclarationKind.DeclarationExpressionVariable); SetTypeWithAnnotations(TypeWithAnnotations.Create(_nodeBinder.CreateErrorType("var"))); } diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs index 4ae52b7ee64e..e296ce8b4406 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs @@ -2141,7 +2141,7 @@ void M(int[] o) IConstantPatternOperation (OperationKind.ConstantPattern, Type: null) (Syntax: '42') (InputType: System.Int32, NarrowedType: System.Int32) Value: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 42) (Syntax: '42') - ISlicePatternOperation (OperationKind.SlicePattern, Type: null) (Syntax: '.. var slice') (InputType: System.Int32[], NarrowedType: System.Int32[], SliceSymbol: T[] System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray(T[] array, System.Range range) + ISlicePatternOperation (OperationKind.SlicePattern, Type: null) (Syntax: '.. var slice') (InputType: System.Int32[], NarrowedType: System.Int32[], SliceSymbol: null Pattern: IDeclarationPatternOperation (OperationKind.DeclarationPattern, Type: null) (Syntax: 'var slice') (InputType: System.Int32[], NarrowedType: System.Int32[], DeclaredSymbol: System.Int32[]? slice, MatchesNull: True) "; @@ -2375,7 +2375,7 @@ void M(int[] a) Value: IParameterReferenceOperation: a (OperationKind.ParameterReference, Type: System.Int32[]) (Syntax: 'a') Pattern: - ISlicePatternOperation (OperationKind.SlicePattern, Type: null, IsInvalid) (Syntax: '.. 42') (InputType: System.Int32[], NarrowedType: System.Int32[], SliceSymbol: T[] System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray(T[] array, System.Range range) + ISlicePatternOperation (OperationKind.SlicePattern, Type: null, IsInvalid) (Syntax: '.. 42') (InputType: System.Int32[], NarrowedType: System.Int32[], SliceSymbol: null Pattern: IConstantPatternOperation (OperationKind.ConstantPattern, Type: null, IsInvalid) (Syntax: '42') (InputType: System.Int32[], NarrowedType: System.Int32[]) Value: @@ -2433,7 +2433,7 @@ void M(int[] o) IConstantPatternOperation (OperationKind.ConstantPattern, Type: null) (Syntax: '1') (InputType: System.Int32, NarrowedType: System.Int32) Value: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') - ISlicePatternOperation (OperationKind.SlicePattern, Type: null) (Syntax: '.. var slice') (InputType: System.Int32[], NarrowedType: System.Int32[], SliceSymbol: T[] System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray(T[] array, System.Range range) + ISlicePatternOperation (OperationKind.SlicePattern, Type: null) (Syntax: '.. var slice') (InputType: System.Int32[], NarrowedType: System.Int32[], SliceSymbol: null Pattern: IDeclarationPatternOperation (OperationKind.DeclarationPattern, Type: null) (Syntax: 'var slice') (InputType: System.Int32[], NarrowedType: System.Int32[], DeclaredSymbol: System.Int32[]? slice, MatchesNull: True) IConstantPatternOperation (OperationKind.ConstantPattern, Type: null) (Syntax: '2') (InputType: System.Int32, NarrowedType: System.Int32) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs index b4324cf9712e..592e9faa50c2 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs @@ -754,20 +754,21 @@ public static void Main() { _ = new X() is [1]; _ = new X() is [.. 1]; + _ = new X()[^1]; } } "; var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularWithListPatterns, options: TestOptions.ReleaseExe); compilation.VerifyEmitDiagnostics( - // (20,24): error CS0656: Missing compiler required member 'System.Index..ctor' + // (20,24): error CS0656: Missing compiler required member 'System.Index.op_Implicit' // _ = new X() is [1]; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[1]").WithArguments("System.Index", ".ctor").WithLocation(20, 24), - // (21,24): error CS0656: Missing compiler required member 'System.Index..ctor' + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[1]").WithArguments("System.Index", "op_Implicit").WithLocation(20, 24), + // (21,24): error CS0656: Missing compiler required member 'System.Index.op_Implicit' // _ = new X() is [.. 1]; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[.. 1]").WithArguments("System.Index", ".ctor").WithLocation(21, 24), - // (21,25): error CS0656: Missing compiler required member 'System.Range..ctor' - // _ = new X() is [.. 1]; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, ".. 1").WithArguments("System.Range", ".ctor").WithLocation(21, 25) + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[.. 1]").WithArguments("System.Index", "op_Implicit").WithLocation(21, 24), + // (22,21): error CS0656: Missing compiler required member 'System.Index..ctor' + // _ = new X()[^1]; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "^1").WithArguments("System.Index", ".ctor").WithLocation(22, 21) ); } @@ -1304,6 +1305,9 @@ public static void Main() // (6,28): error CS0021: Cannot apply indexing with [] to an expression of type 'Test1' // _ = new Test1() is [0]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "[0]").WithArguments("Test1").WithLocation(6, 28), + // (6,28): error CS0656: Missing compiler required member 'System.Index.op_Implicit' + // _ = new Test1() is [0]; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[0]").WithArguments("System.Index", "op_Implicit").WithLocation(6, 28), // (7,13): error CS0021: Cannot apply indexing with [] to an expression of type 'Test1' // _ = new Test1()[0]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "new Test1()[0]").WithArguments("Test1").WithLocation(7, 13)); @@ -4839,10 +4843,7 @@ void M(int[] array) compilation.VerifyEmitDiagnostics( // (9,44): error CS8122: An expression tree may not contain an 'is' pattern-matching operator. // Expression> ok1 = () => array is [_, ..]; - Diagnostic(ErrorCode.ERR_ExpressionTreeContainsIsMatch, "array is [_, ..]").WithLocation(9, 44), - // (9,53): error CS8790: An expression tree may not contain a pattern System.Index or System.Range indexer access - // Expression> ok1 = () => array is [_, ..]; - Diagnostic(ErrorCode.ERR_ExpressionTreeContainsPatternIndexOrRangeIndexer, "[_, ..]").WithLocation(9, 53) + Diagnostic(ErrorCode.ERR_ExpressionTreeContainsIsMatch, "array is [_, ..]").WithLocation(9, 44) ); } @@ -6301,7 +6302,6 @@ void Test(int[] a) "); } - // PROTOTYPE this test takes 7 seconds to run... [Theory] [CombinatorialData] public void Subsumption_Slice_00( diff --git a/src/Compilers/Core/Portable/WellKnownMember.cs b/src/Compilers/Core/Portable/WellKnownMember.cs index 7df1c2d23a98..e0868c6410ac 100644 --- a/src/Compilers/Core/Portable/WellKnownMember.cs +++ b/src/Compilers/Core/Portable/WellKnownMember.cs @@ -515,6 +515,8 @@ internal enum WellKnownMember System_Runtime_CompilerServices_DefaultInterpolatedStringHandler__ToStringAndClear, + System_Index__op_Implicit_FromInt32, + Count // Remember to update the AllWellKnownTypeMembers tests when making changes here diff --git a/src/Compilers/Core/Portable/WellKnownMembers.cs b/src/Compilers/Core/Portable/WellKnownMembers.cs index e0fb3cdc96a9..d6cae7785832 100644 --- a/src/Compilers/Core/Portable/WellKnownMembers.cs +++ b/src/Compilers/Core/Portable/WellKnownMembers.cs @@ -3526,6 +3526,15 @@ static WellKnownMembers() 0, // Arity 0, // Method Signature (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_String, // Return Type + + // System_Index__op_Implicit_FromInt32 + (byte)(MemberFlags.Method | MemberFlags.Static), // Flags + (byte)WellKnownType.ExtSentinel, (byte)(WellKnownType.System_Index - WellKnownType.ExtSentinel), // DeclaringTypeId + 0, // Arity + 1, // Method Signature + (byte)SignatureTypeCode.TypeHandle, (byte)WellKnownType.ExtSentinel, (byte)(WellKnownType.System_Index - WellKnownType.ExtSentinel), // Return Type + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Int32, + }; string[] allNames = new string[(int)WellKnownMember.Count] @@ -3969,6 +3978,7 @@ static WellKnownMembers() "Append", // System_Text_StringBuilder__AppendObject ".ctor", // System_Text_StringBuilder__ctor "ToStringAndClear", // System_Runtime_CompilerServices_DefaultInterpolatedStringHandler__ToStringAndClear + "op_Implicit", // System_Index__op_Implicit_FromInt32 }; s_descriptors = MemberDescriptor.InitializeFromStream(new System.IO.MemoryStream(initializationBytes, writable: false), allNames); From dbdbe399265e97c6f12fd793d5de9a23047f1461 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Mon, 15 Nov 2021 11:20:22 -0800 Subject: [PATCH 14/29] Address feedback (3) --- .../Portable/Binder/Binder_Expressions.cs | 246 +++++++++--------- .../Portable/FlowAnalysis/NullableWalker.cs | 1 + .../Test/Emit/CodeGen/IndexAndRangeTests.cs | 49 ++++ .../PatternMatchingTests_ListPatterns.cs | 84 +++++- 4 files changed, 257 insertions(+), 123 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index e09a075df1d8..0792a722d214 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -6763,7 +6763,8 @@ private BoundExpression BindMemberOfType( left = ReplaceTypeOrValueReceiver(left, symbol.IsStatic || symbol.Kind == SymbolKind.NamedType, diagnostics); // Events are handled later as we don't know yet if we are binding to the event or it's backing field. - if (symbol.Kind != SymbolKind.Event) + // Properties are handled in BindPropertyAccess + if (symbol.Kind is not SymbolKind.Event or SymbolKind.Property) { ReportDiagnosticsIfObsolete(diagnostics, symbol, node, hasBaseReceiver: left.Kind == BoundKind.BaseReference); } @@ -7126,6 +7127,8 @@ private BoundExpression BindPropertyAccess( LookupResultKind lookupResult, bool hasErrors) { + ReportDiagnosticsIfObsolete(diagnostics, propertySymbol, node, hasBaseReceiver: receiver.Kind == BoundKind.BaseReference); + bool hasError = this.CheckInstanceOrStatic(node, receiver, propertySymbol, ref lookupResult, diagnostics); if (!propertySymbol.IsStatic) @@ -8152,7 +8155,7 @@ private bool TryBindIndexOrRangeImplicitIndexerParts( // For Range: Has an accessible Slice method that takes two int parameters if (TryBindLengthOrCount(syntax, receiverPlaceholder, out lengthOrCountAccess, diagnostics) && - TryBindIndexOrRangeImplicitIndexer(syntax, receiver, argIsIndex, out implicitIndexerAccess, out argumentPlaceholders, diagnostics)) + tryBindIndexOrRangeImplicitIndexer(syntax, receiver, argIsIndex, out implicitIndexerAccess, out argumentPlaceholders, diagnostics)) { return true; } @@ -8161,145 +8164,121 @@ private bool TryBindIndexOrRangeImplicitIndexerParts( implicitIndexerAccess = null; argumentPlaceholders = default; return false; - } - - private bool TryBindLengthOrCount( - SyntaxNode syntax, - BoundExpression receiver, - out BoundExpression lengthOrCountAccess, - BindingDiagnosticBag diagnostics) - { - var lookupResult = LookupResult.GetInstance(); - Debug.Assert(receiver.Type is not null); - if (TryLookupLengthOrCount(syntax, receiver.Type, lookupResult, out var lengthOrCountProperty, diagnostics)) + // Binds pattern-based implicit indexer: + // - for Index indexer, this will find `this[int]`. + // - for Range indexer, this will find `Slice(int, int)` or `string.Substring(int, int)`. + bool tryBindIndexOrRangeImplicitIndexer( + SyntaxNode syntax, + BoundExpression receiver, + bool argIsIndex, + [NotNullWhen(true)] out BoundExpression? indexerOrSliceAccess, + out ImmutableArray argumentPlaceholders, + BindingDiagnosticBag diagnostics) { - diagnostics.ReportUseSite(lengthOrCountProperty, syntax); - ReportDiagnosticsIfObsolete(diagnostics, lengthOrCountProperty, syntax, hasBaseReceiver: false); - lengthOrCountAccess = BindPropertyAccess(syntax, receiver, lengthOrCountProperty, diagnostics, lookupResult.Kind, hasErrors: false).MakeCompilerGenerated(); - lengthOrCountAccess = CheckValue(lengthOrCountAccess, BindValueKind.RValue, diagnostics); - - lookupResult.Free(); - return true; - } - - lengthOrCountAccess = BadExpression(syntax); - lookupResult.Free(); - - return false; - } - - /// - /// Binds pattern-based implicit indexer: - /// - for Index indexer, this will find `this[int]`. - /// - for Range indexer, this will find `Slice(int, int)` or `string.Substring(int, int)`. - /// - private bool TryBindIndexOrRangeImplicitIndexer( - SyntaxNode syntax, - BoundExpression receiver, - bool argIsIndex, - [NotNullWhen(true)] out BoundExpression? indexerOrSliceAccess, - out ImmutableArray argumentPlaceholders, - BindingDiagnosticBag diagnostics) - { - Debug.Assert(receiver.Type is not null); - var useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); - var lookupResult = LookupResult.GetInstance(); + Debug.Assert(receiver.Type is not null); + var useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); // TODO2 need to report + var lookupResult = LookupResult.GetInstance(); - if (argIsIndex) - { - // Look for `T this[int i]` indexer + if (argIsIndex) + { + // Look for `T this[int i]` indexer - LookupMembersInType( - lookupResult, - receiver.Type, - WellKnownMemberNames.Indexer, - arity: 0, - basesBeingResolved: null, - LookupOptions.Default, - originalBinder: this, - diagnose: false, - ref useSiteInfo); + LookupMembersInType( + lookupResult, + receiver.Type, + WellKnownMemberNames.Indexer, + arity: 0, + basesBeingResolved: null, + LookupOptions.Default, + originalBinder: this, + diagnose: false, + ref useSiteInfo); - if (lookupResult.IsMultiViable) - { - foreach (var candidate in lookupResult.Symbols) + if (lookupResult.IsMultiViable) { - if (!candidate.IsStatic && - candidate is PropertySymbol property && - IsAccessible(property, ref useSiteInfo) && - property.OriginalDefinition is { ParameterCount: 1 } original && - original.Parameters[0] is { Type: { SpecialType: SpecialType.System_Int32 }, RefKind: RefKind.None }) + foreach (var candidate in lookupResult.Symbols) { - var intPlaceholder = new BoundIndexOrRangeIndexerPatternValuePlaceholder(syntax, Compilation.GetSpecialType(SpecialType.System_Int32)) { WasCompilerGenerated = true }; - argumentPlaceholders = ImmutableArray.Create(intPlaceholder); - - var analyzedArguments = AnalyzedArguments.GetInstance(); - analyzedArguments.Arguments.Add(intPlaceholder); - var properties = ArrayBuilder.GetInstance(); - properties.AddRange(property); - indexerOrSliceAccess = BindIndexerOrIndexedPropertyAccess(syntax, receiver, properties, analyzedArguments, diagnostics).MakeCompilerGenerated(); - properties.Free(); - analyzedArguments.Free(); - lookupResult.Free(); - return true; + if (!candidate.IsStatic && + candidate is PropertySymbol property && + IsAccessible(property, ref useSiteInfo) && + property.OriginalDefinition is { ParameterCount: 1 } original && + original.Parameters[0] is { Type: { SpecialType: SpecialType.System_Int32 }, RefKind: RefKind.None }) + { + diagnostics.Add(syntax, useSiteInfo); + + var intPlaceholder = new BoundIndexOrRangeIndexerPatternValuePlaceholder(syntax, Compilation.GetSpecialType(SpecialType.System_Int32)) { WasCompilerGenerated = true }; + argumentPlaceholders = ImmutableArray.Create(intPlaceholder); + + var analyzedArguments = AnalyzedArguments.GetInstance(); + analyzedArguments.Arguments.Add(intPlaceholder); + var properties = ArrayBuilder.GetInstance(); + properties.AddRange(property); + indexerOrSliceAccess = BindIndexerOrIndexedPropertyAccess(syntax, receiver, properties, analyzedArguments, diagnostics).MakeCompilerGenerated(); + properties.Free(); + analyzedArguments.Free(); + lookupResult.Free(); + return true; + } } } } - } - else if (receiver.Type.SpecialType == SpecialType.System_String) - { - Debug.Assert(!argIsIndex); - // Look for Substring - var substring = (MethodSymbol)Compilation.GetSpecialTypeMember(SpecialMember.System_String__Substring); - if (substring is object) + else if (receiver.Type.SpecialType == SpecialType.System_String) { - makeCall(syntax, receiver, substring, out indexerOrSliceAccess, out argumentPlaceholders); - lookupResult.Free(); - return true; + Debug.Assert(!argIsIndex); + // Look for Substring + var substring = (MethodSymbol)Compilation.GetSpecialTypeMember(SpecialMember.System_String__Substring); + if (substring is object) + { + diagnostics.Add(syntax, useSiteInfo); + makeCall(syntax, receiver, substring, out indexerOrSliceAccess, out argumentPlaceholders); + lookupResult.Free(); + return true; + } } - } - else - { - Debug.Assert(!argIsIndex); - // Look for `T Slice(int, int)` indexer + else + { + Debug.Assert(!argIsIndex); + // Look for `T Slice(int, int)` indexer - LookupMembersInType( - lookupResult, - receiver.Type, - WellKnownMemberNames.SliceMethodName, - arity: 0, - basesBeingResolved: null, - LookupOptions.Default, - originalBinder: this, - diagnose: false, - ref useSiteInfo); + LookupMembersInType( + lookupResult, + receiver.Type, + WellKnownMemberNames.SliceMethodName, + arity: 0, + basesBeingResolved: null, + LookupOptions.Default, + originalBinder: this, + diagnose: false, + ref useSiteInfo); - if (lookupResult.IsMultiViable) - { - foreach (var candidate in lookupResult.Symbols) + if (lookupResult.IsMultiViable) { - if (!candidate.IsStatic && - IsAccessible(candidate, ref useSiteInfo) && - candidate is MethodSymbol method && - method.OriginalDefinition is var original && - !original.ReturnsVoid && - original.ParameterCount == 2 && - original.Parameters[0] is { Type: { SpecialType: SpecialType.System_Int32 }, RefKind: RefKind.None } && - original.Parameters[1] is { Type: { SpecialType: SpecialType.System_Int32 }, RefKind: RefKind.None }) + foreach (var candidate in lookupResult.Symbols) { - makeCall(syntax, receiver, method, out indexerOrSliceAccess, out argumentPlaceholders); - lookupResult.Free(); - return true; + if (!candidate.IsStatic && + IsAccessible(candidate, ref useSiteInfo) && + candidate is MethodSymbol method && + method.OriginalDefinition is var original && + !original.ReturnsVoid && + original.ParameterCount == 2 && + original.Parameters[0] is { Type: { SpecialType: SpecialType.System_Int32 }, RefKind: RefKind.None } && + original.Parameters[1] is { Type: { SpecialType: SpecialType.System_Int32 }, RefKind: RefKind.None }) + { + diagnostics.Add(syntax, useSiteInfo); + makeCall(syntax, receiver, method, out indexerOrSliceAccess, out argumentPlaceholders); + lookupResult.Free(); + return true; + } } } } - } - indexerOrSliceAccess = null; - argumentPlaceholders = default; - lookupResult.Free(); - return false; + indexerOrSliceAccess = null; + argumentPlaceholders = default; + lookupResult.Free(); + return false; + } void makeCall(SyntaxNode syntax, BoundExpression receiver, MethodSymbol method, out BoundExpression indexerOrSliceAccess, out ImmutableArray argumentPlaceholders) @@ -8323,6 +8302,31 @@ void makeCall(SyntaxNode syntax, BoundExpression receiver, MethodSymbol method, } } + private bool TryBindLengthOrCount( + SyntaxNode syntax, + BoundExpression receiver, + out BoundExpression lengthOrCountAccess, + BindingDiagnosticBag diagnostics) + { + var lookupResult = LookupResult.GetInstance(); + + Debug.Assert(receiver.Type is not null); + if (TryLookupLengthOrCount(syntax, receiver.Type, lookupResult, out var lengthOrCountProperty, diagnostics)) + { + diagnostics.ReportUseSite(lengthOrCountProperty, syntax); + lengthOrCountAccess = BindPropertyAccess(syntax, receiver, lengthOrCountProperty, diagnostics, lookupResult.Kind, hasErrors: false).MakeCompilerGenerated(); + lengthOrCountAccess = CheckValue(lengthOrCountAccess, BindValueKind.RValue, diagnostics); + + lookupResult.Free(); + return true; + } + + lengthOrCountAccess = BadExpression(syntax); + lookupResult.Free(); + + return false; + } + private bool TryLookupLengthOrCount( SyntaxNode syntax, TypeSymbol receiverType, diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index 1f5a78b6ea90..43b8e215925a 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -8698,6 +8698,7 @@ private TypeWithAnnotations GetDeclaredParameterResult(ParameterSymbol parameter public override BoundNode? VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node) { VisitRvalue(node.IndexerAccess); + SetResult(node, ResultType, LvalueResultType); return null; } diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/IndexAndRangeTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/IndexAndRangeTests.cs index 7ef09c190628..7a9ae0037048 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/IndexAndRangeTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/IndexAndRangeTests.cs @@ -3323,5 +3323,54 @@ public readonly struct Range var comp = CreateCompilation(new[] { source, TestSources.Index }); comp.VerifyDiagnostics(); } + + [Fact] + public void ImplicitIndexerAccessAsLValue() + { + var source = @" +#nullable enable + +object? o1 = new object(); +M(o1)[^1] = null; // 1 +M(o1)[..] = null; // 2 + +_ = M(o1)[^1].ToString(); +_ = M(o1)[..].ToString(); + +object o2 = null; // 3 +M(o2)[^1] = null; +M(o2)[..] = null; + +_ = M(o2)[^1].ToString(); // 4 +_ = M(o2)[..].ToString(); // 5 + +static C M(T t) => throw null!; + +class C +{ + public int Length => 0; + public ref T this[int i] => throw null!; + public ref T Slice(int i, int j) => throw null!; +} +"; + var comp = CreateCompilation(new[] { source, TestSources.Index, TestSources.Range }); + comp.VerifyDiagnostics( + // (5,13): warning CS8625: Cannot convert null literal to non-nullable reference type. + // M(o1)[^1] = null; // 1 + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(5, 13), + // (6,13): warning CS8625: Cannot convert null literal to non-nullable reference type. + // M(o1)[..] = null; // 2 + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(6, 13), + // (11,13): warning CS8600: Converting null literal or possible null value to non-nullable type. + // object o2 = null; // 3 + Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null").WithLocation(11, 13), + // (15,5): warning CS8602: Dereference of a possibly null reference. + // _ = M(o2)[^1].ToString(); // 4 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "M(o2)[^1]").WithLocation(15, 5), + // (16,5): warning CS8602: Dereference of a possibly null reference. + // _ = M(o2)[..].ToString(); // 5 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "M(o2)[..]").WithLocation(16, 5) + ); + } } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs index 592e9faa50c2..513514a90aae 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs @@ -4480,6 +4480,7 @@ public void M() public void ListPattern_Dynamic() { var source = @" +#nullable enable class C { void M(dynamic d) @@ -4490,9 +4491,9 @@ void M(dynamic d) "; var compilation = CreateCompilation(new[] { source, TestSources.Index }); compilation.VerifyEmitDiagnostics( - // (6,18): error CS9000: List patterns may not be used for a value of type 'dynamic'. + // (7,18): error CS8978: List patterns may not be used for a value of type 'dynamic'. // _ = d is [_]; - Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[_]").WithArguments("dynamic").WithLocation(6, 18) + Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[_]").WithArguments("dynamic").WithLocation(7, 18) ); } @@ -6773,6 +6774,85 @@ .locals init (C V_0, }"); } + [Fact, WorkItem(51801, "https://github.com/dotnet/roslyn/issues/51801")] + public void LengthOverrideLacksAccessor() + { + var source = @" +#nullable enable +using System.Runtime.CompilerServices; + +class Base +{ + public virtual int Length { get { return 2; } set { } } +} + +class C : Base +{ + public override int Length { set { } } + public object this[int i] { get { return 1; } set { } } + + public string? Value { get; } + + public string M() + { + switch (this) + { + case [1, 1]: + return Value; + default: + return Value; + } + } +} +"; + var verifier = CompileAndVerify(new[] { source, TestSources.Index }); + verifier.VerifyIL("C.M", @" +{ + // Code size 78 (0x4e) + .maxstack 2 + .locals init (C V_0, + object V_1, + object V_2) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: brfalse.s IL_0047 + IL_0005: ldloc.0 + IL_0006: callvirt ""int Base.Length.get"" + IL_000b: ldc.i4.2 + IL_000c: bne.un.s IL_0047 + IL_000e: ldloc.0 + IL_000f: ldc.i4.0 + IL_0010: callvirt ""object C.this[int].get"" + IL_0015: stloc.1 + IL_0016: ldloc.1 + IL_0017: isinst ""int"" + IL_001c: brfalse.s IL_0047 + IL_001e: ldloc.1 + IL_001f: unbox.any ""int"" + IL_0024: ldc.i4.1 + IL_0025: bne.un.s IL_0047 + IL_0027: ldloc.0 + IL_0028: ldc.i4.1 + IL_0029: callvirt ""object C.this[int].get"" + IL_002e: stloc.2 + IL_002f: ldloc.2 + IL_0030: isinst ""int"" + IL_0035: brfalse.s IL_0047 + IL_0037: ldloc.2 + IL_0038: unbox.any ""int"" + IL_003d: ldc.i4.1 + IL_003e: pop + IL_003f: pop + IL_0040: ldarg.0 + IL_0041: call ""string C.Value.get"" + IL_0046: ret + IL_0047: ldarg.0 + IL_0048: call ""string C.Value.get"" + IL_004d: ret +}"); + } + [Fact] public void ListPattern_LengthAndCountAreOrthogonal() { From a1a4243ca13fd62df93bf7ea26d5ab5f166f20e6 Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Wed, 17 Nov 2021 07:27:49 -0800 Subject: [PATCH 15/29] Disable caching that is not necessary during a list pattern evaluation. --- ...ocalRewriter_CompoundAssignmentOperator.cs | 14 ++-- .../LocalRewriter_IndexerAccess.cs | 71 ++++++++++++------- .../PatternMatchingTests_ListPatterns.cs | 61 ++++++++-------- 3 files changed, 83 insertions(+), 63 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs index 69925358154b..4b1106813186 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs @@ -416,10 +416,16 @@ private BoundExpression TransformImplicitIndexerAccess( // the only thing we need to do is lift the stores and temps out of // the sequence, and use the final expression as the new argument - var sequence = VisitIndexOrRangePatternIndexerAccess(indexerAccess, isLeftOfAssignment: true); - stores.AddRange(sequence.SideEffects); - temps.AddRange(sequence.Locals); - return TransformCompoundAssignmentLHS(sequence.Value, stores, temps, isDynamicAssignment); + var access = VisitIndexOrRangePatternIndexerAccess(indexerAccess, isLeftOfAssignment: true); + + if (access is BoundSequence sequence) + { + stores.AddRange(sequence.SideEffects); + temps.AddRange(sequence.Locals); + access = sequence.Value; + } + + return TransformCompoundAssignmentLHS(access, stores, temps, isDynamicAssignment); } /// diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs index 7a2844ede631..052d3e1cf3b9 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs @@ -220,7 +220,7 @@ public override BoundNode VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRang return VisitIndexOrRangePatternIndexerAccess(node, isLeftOfAssignment: false); } - private BoundSequence VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node, bool isLeftOfAssignment) + private BoundExpression VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node, bool isLeftOfAssignment) { if (TypeSymbol.Equals( node.Argument.Type, @@ -241,7 +241,7 @@ private BoundSequence VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePat } } - private BoundSequence VisitIndexPatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node, bool isLeftOfAssignment) + private BoundExpression VisitIndexPatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node, bool isLeftOfAssignment) { Debug.Assert(node.ArgumentPlaceholders.Length == 1); Debug.Assert(node.IndexerAccess is BoundIndexerAccess); @@ -256,15 +256,24 @@ private BoundSequence VisitIndexPatternIndexerAccess(BoundIndexOrRangePatternInd var locals = ArrayBuilder.GetInstance(2); var sideeffects = ArrayBuilder.GetInstance(2); - var receiver = node.GetReceiver(); - Debug.Assert(receiver.Type is { }); - var receiverLocal = F.StoreToTemp( - VisitExpression(receiver), - out var receiverStore, - // Store the receiver as a ref local if it's a value type to ensure side effects are propagated - receiver.Type.IsReferenceType ? RefKind.None : RefKind.Ref); - locals.Add(receiverLocal.LocalSymbol); - sideeffects.Add(receiverStore); + var receiver = VisitExpression(node.GetReceiver()); + + // Do not capture receiver if it is a local or parameter and we are evaluating a pattern + // If length access is a local, then we are evaluating a pattern + if (node.LengthOrCountAccess.Kind is not BoundKind.Local || receiver.Kind is not BoundKind.Local or BoundKind.Parameter) + { + Debug.Assert(receiver.Type is { }); + + var receiverLocal = F.StoreToTemp( + receiver, + out var receiverStore, + // Store the receiver as a ref local if it's a value type to ensure side effects are propagated + receiver.Type.IsReferenceType ? RefKind.None : RefKind.Ref); + locals.Add(receiverLocal.LocalSymbol); + sideeffects.Add(receiverStore); + + receiver = receiverLocal; + } BoundExpression makeOffsetInput = DetermineMakePatternIndexOffsetExpressionStrategy(node.Argument, out PatternIndexOffsetLoweringStrategy strategy); BoundExpression integerArgument; @@ -280,7 +289,7 @@ private BoundSequence VisitIndexPatternIndexerAccess(BoundIndexOrRangePatternInd sideeffects.Add(inputStore); } - integerArgument = MakePatternIndexOffsetExpression(makeOffsetInput, RewriteLengthAccess(node, receiverLocal), strategy); + integerArgument = MakePatternIndexOffsetExpression(makeOffsetInput, RewriteLengthAccess(node, receiver), strategy); break; case PatternIndexOffsetLoweringStrategy.UseAsIs: @@ -288,7 +297,7 @@ private BoundSequence VisitIndexPatternIndexerAccess(BoundIndexOrRangePatternInd break; case PatternIndexOffsetLoweringStrategy.UseGetOffsetAPI: - integerArgument = MakePatternIndexOffsetExpression(makeOffsetInput, RewriteLengthAccess(node, receiverLocal), strategy); + integerArgument = MakePatternIndexOffsetExpression(makeOffsetInput, RewriteLengthAccess(node, receiver), strategy); break; default: @@ -299,10 +308,10 @@ private BoundSequence VisitIndexPatternIndexerAccess(BoundIndexOrRangePatternInd Debug.Assert(node.ArgumentPlaceholders.Length == 1); var argumentPlaceholder = node.ArgumentPlaceholders[0]; AddPlaceholderReplacement(argumentPlaceholder, integerArgument); - var rewrittenIndexerAccess = VisitIndexerAccess(indexerAccess.WithReceiver(receiverLocal), isLeftOfAssignment); + var rewrittenIndexerAccess = VisitIndexerAccess(indexerAccess.WithReceiver(receiver), isLeftOfAssignment); RemovePlaceholderReplacement(argumentPlaceholder); - return (BoundSequence)F.Sequence( + return F.Sequence( locals.ToImmutableAndFree(), sideeffects.ToImmutableAndFree(), rewrittenIndexerAccess); @@ -414,7 +423,7 @@ private BoundExpression DetermineMakePatternIndexOffsetExpressionStrategy( } } - private BoundSequence VisitRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node) + private BoundExpression VisitRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node) { Debug.Assert(node.ArgumentPlaceholders.Length == 2); Debug.Assert(node.IndexerAccess is BoundCall); @@ -434,16 +443,23 @@ private BoundSequence VisitRangePatternIndexerAccess(BoundIndexOrRangePatternInd var F = _factory; - var receiver = node.GetReceiver(); + var receiver = VisitExpression(node.GetReceiver()); var rangeArg = node.Argument; var localsBuilder = ArrayBuilder.GetInstance(); var sideEffectsBuilder = ArrayBuilder.GetInstance(); - var receiverLocal = F.StoreToTemp(VisitExpression(receiver), out var receiverStore); + // Do not capture receiver if it is a local or parameter and we are evaluating a pattern + // If length access is a local, then we are evaluating a pattern + if (node.LengthOrCountAccess.Kind is not BoundKind.Local || receiver.Kind is not BoundKind.Local or BoundKind.Parameter) + { + var receiverLocal = F.StoreToTemp(receiver, out var receiverStore); + + localsBuilder.Add(receiverLocal.LocalSymbol); + sideEffectsBuilder.Add(receiverStore); - localsBuilder.Add(receiverLocal.LocalSymbol); - sideEffectsBuilder.Add(receiverStore); + receiver = receiverLocal; + } BoundExpression startExpr; BoundExpression rangeSizeExpr; @@ -569,9 +585,10 @@ private BoundSequence VisitRangePatternIndexerAccess(BoundIndexOrRangePatternInd if ((rewriteFlags & useLength) != 0) { - lengthAccess = RewriteLengthAccess(node, receiverLocal); + lengthAccess = RewriteLengthAccess(node, receiver); - if ((rewriteFlags & captureLength) != 0) + // If length access is a local, then we are evaluating a pattern and don't need to capture the value. + if ((rewriteFlags & captureLength) != 0 && lengthAccess.Kind is not BoundKind.Local) { var lengthLocal = F.StoreToTemp(lengthAccess, out var lengthStore); localsBuilder.Add(lengthLocal.LocalSymbol); @@ -611,7 +628,7 @@ private BoundSequence VisitRangePatternIndexerAccess(BoundIndexOrRangePatternInd localsBuilder.Add(rangeLocal.LocalSymbol); sideEffectsBuilder.Add(rangeStore); - var lengthAccess = RewriteLengthAccess(node, receiverLocal); + var lengthAccess = RewriteLengthAccess(node, receiver); var lengthLocal = F.StoreToTemp(lengthAccess, out var lengthStore); localsBuilder.Add(lengthLocal.LocalSymbol); @@ -647,21 +664,21 @@ private BoundSequence VisitRangePatternIndexerAccess(BoundIndexOrRangePatternInd AddPlaceholderReplacement(node.ArgumentPlaceholders[1], rangeSizeExpr); var sliceCall = (BoundCall)node.IndexerAccess; - var rewrittenIndexerAccess = VisitExpression(sliceCall.WithReceiver(receiverLocal)); + var rewrittenIndexerAccess = VisitExpression(sliceCall.WithReceiver(receiver)); RemovePlaceholderReplacement(node.ArgumentPlaceholders[0]); RemovePlaceholderReplacement(node.ArgumentPlaceholders[1]); - return (BoundSequence)F.Sequence( + return F.Sequence( localsBuilder.ToImmutableAndFree(), sideEffectsBuilder.ToImmutableAndFree(), rewrittenIndexerAccess); } - BoundExpression RewriteLengthAccess(BoundIndexOrRangePatternIndexerAccess node, BoundLocal receiverLocal) + BoundExpression RewriteLengthAccess(BoundIndexOrRangePatternIndexerAccess node, BoundExpression receiver) { var receiverPlaceholder = node.ReceiverPlaceholder; - AddPlaceholderReplacement(receiverPlaceholder, receiverLocal); + AddPlaceholderReplacement(receiverPlaceholder, receiver); Debug.Assert(node.LengthOrCountAccess is not null); var lengthAccess = VisitExpression(node.LengthOrCountAccess); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs index 513514a90aae..e5f299708ae1 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs @@ -74,14 +74,13 @@ public static void Main() AssertEx.Multiple( () => verifier.VerifyIL("X.Test(System.Span)", @" { - // Code size 74 (0x4a) + // Code size 71 (0x47) .maxstack 4 .locals init (char V_0, //first System.Span V_1, //others char V_2, //last System.Span V_3, - int V_4, - System.Span V_5) + int V_4) IL_0000: ldarg.0 IL_0001: stloc.3 IL_0002: ldloca.s V_3 @@ -89,41 +88,39 @@ .locals init (char V_0, //first IL_0009: stloc.s V_4 IL_000b: ldloc.s V_4 IL_000d: ldc.i4.1 - IL_000e: ble.un.s IL_003b + IL_000e: ble.un.s IL_0038 IL_0010: ldloca.s V_3 IL_0012: ldc.i4.0 IL_0013: call ""ref char System.Span.this[int].get"" IL_0018: ldind.u2 IL_0019: stloc.0 - IL_001a: ldloc.3 - IL_001b: stloc.s V_5 - IL_001d: ldloca.s V_5 + IL_001a: ldloca.s V_3 + IL_001c: ldc.i4.1 + IL_001d: ldloc.s V_4 IL_001f: ldc.i4.1 - IL_0020: ldloc.s V_4 - IL_0022: ldc.i4.1 - IL_0023: sub - IL_0024: ldc.i4.1 - IL_0025: sub - IL_0026: call ""System.Span System.Span.Slice(int, int)"" - IL_002b: stloc.1 - IL_002c: ldloca.s V_3 - IL_002e: ldloc.s V_4 - IL_0030: ldc.i4.1 - IL_0031: sub - IL_0032: call ""ref char System.Span.this[int].get"" - IL_0037: ldind.u2 - IL_0038: stloc.2 - IL_0039: br.s IL_003d - IL_003b: ldc.i4.1 - IL_003c: ret - IL_003d: ldloc.0 - IL_003e: ldloc.2 - IL_003f: bne.un.s IL_0048 - IL_0041: ldloc.1 - IL_0042: call ""bool X.Test(System.Span)"" - IL_0047: ret - IL_0048: ldc.i4.0 - IL_0049: ret + IL_0020: sub + IL_0021: ldc.i4.1 + IL_0022: sub + IL_0023: call ""System.Span System.Span.Slice(int, int)"" + IL_0028: stloc.1 + IL_0029: ldloca.s V_3 + IL_002b: ldloc.s V_4 + IL_002d: ldc.i4.1 + IL_002e: sub + IL_002f: call ""ref char System.Span.this[int].get"" + IL_0034: ldind.u2 + IL_0035: stloc.2 + IL_0036: br.s IL_003a + IL_0038: ldc.i4.1 + IL_0039: ret + IL_003a: ldloc.0 + IL_003b: ldloc.2 + IL_003c: bne.un.s IL_0045 + IL_003e: ldloc.1 + IL_003f: call ""bool X.Test(System.Span)"" + IL_0044: ret + IL_0045: ldc.i4.0 + IL_0046: ret } "), () => verifier.VerifyIL("X.Test(char[])", @" From 05edc547cc8e5870c758298273948de9bf4a1efa Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Tue, 16 Nov 2021 20:12:48 -0800 Subject: [PATCH 16/29] Address feedback (4) --- .../Portable/Binder/Binder.ValueChecks.cs | 6 +- .../Portable/Binder/Binder_Expressions.cs | 101 +++++++++--------- .../CSharp/Portable/Binder/Binder_Lookup.cs | 8 ++ .../CSharp/Portable/Binder/Binder_Patterns.cs | 20 ++-- .../CSharp/Portable/BoundTree/BoundNodes.xml | 16 +-- .../NullableWalker.DebugVerifier.cs | 1 + .../Portable/FlowAnalysis/NullableWalker.cs | 1 + .../Generated/BoundNodes.xml.Generated.cs | 22 +++- .../LocalRewriter.PatternLocalRewriter.cs | 4 +- .../LocalRewriter.PlaceholderReplacer.cs | 20 ++-- .../LocalRewriter_IndexerAccess.cs | 2 +- .../Test/Emit/CodeGen/IndexAndRangeTests.cs | 71 ++++++++++++ .../PatternMatchingTests_ListPatterns.cs | 13 +-- .../Symbol/Symbols/MissingSpecialMember.cs | 1 + .../WellKnownTypeValidationTests.vb | 6 +- 15 files changed, 192 insertions(+), 100 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index 152776c977a2..3143a6a25a5f 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -582,11 +582,7 @@ internal bool CheckValueKind(SyntaxNode node, BoundExpression expr, BindValueKin diagnostics); case BoundKind.IndexOrRangePatternIndexerAccess: - var implicitIndexerAccess = (BoundIndexOrRangePatternIndexerAccess)expr; - Debug.Assert(implicitIndexerAccess.IndexerAccess is BoundCall); - // If we got here this should be an implicit indexer taking a Range, - // meaning that the pattern symbol must be a method (either Slice or Substring) - return CheckValueKind(node, implicitIndexerAccess.IndexerAccess, valueKind, checkingReceiver, diagnostics); + throw ExceptionUtilities.Unreachable; case BoundKind.IndexOrRangeIndexerPatternReceiverPlaceholder: break; diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index 0792a722d214..7112a6065e9a 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -1728,7 +1728,7 @@ private bool IsBadLocalOrParameterCapture(Symbol symbol, TypeSymbol type, RefKin private BoundExpression BindNonMethod(SimpleNameSyntax node, Symbol symbol, BindingDiagnosticBag diagnostics, LookupResultKind resultKind, bool indexed, bool isError) { // Events are handled later as we don't know yet if we are binding to the event or it's backing field. - if (symbol.Kind != SymbolKind.Event) + if (symbol.Kind is not SymbolKind.Event and not SymbolKind.Property) { ReportDiagnosticsIfObsolete(diagnostics, symbol, node, hasBaseReceiver: false); } @@ -6764,7 +6764,7 @@ private BoundExpression BindMemberOfType( // Events are handled later as we don't know yet if we are binding to the event or it's backing field. // Properties are handled in BindPropertyAccess - if (symbol.Kind is not SymbolKind.Event or SymbolKind.Property) + if (symbol.Kind is not SymbolKind.Event and not SymbolKind.Property) { ReportDiagnosticsIfObsolete(diagnostics, symbol, node, hasBaseReceiver: left.Kind == BoundKind.BaseReference); } @@ -7119,15 +7119,16 @@ private bool InEnumMemberInitializer() return this.InFieldInitializer && (object)containingType != null && containingType.IsEnumType(); } +#nullable enable private BoundExpression BindPropertyAccess( SyntaxNode node, - BoundExpression receiver, + BoundExpression? receiver, PropertySymbol propertySymbol, BindingDiagnosticBag diagnostics, LookupResultKind lookupResult, bool hasErrors) { - ReportDiagnosticsIfObsolete(diagnostics, propertySymbol, node, hasBaseReceiver: receiver.Kind == BoundKind.BaseReference); + ReportDiagnosticsIfObsolete(diagnostics, propertySymbol, node, hasBaseReceiver: receiver?.Kind == BoundKind.BaseReference); bool hasError = this.CheckInstanceOrStatic(node, receiver, propertySymbol, ref lookupResult, diagnostics); @@ -7138,6 +7139,7 @@ private BoundExpression BindPropertyAccess( return new BoundPropertyAccess(node, receiver, propertySymbol, lookupResult, propertySymbol.Type, hasErrors: (hasErrors || hasError)); } +#nullable disable private void CheckReceiverAndRuntimeSupportForSymbolAccess(SyntaxNode node, BoundExpression receiverOpt, Symbol symbol, BindingDiagnosticBag diagnostics) { @@ -7413,6 +7415,7 @@ private BoundExpression BindElementAccess(ExpressionSyntax node, BoundExpression if (receiver.Kind == BoundKind.PropertyGroup) { var propertyGroup = (BoundPropertyGroup)receiver; + Debug.Assert(propertyGroup.ReceiverOpt is not null); return BindIndexedPropertyAccess(node, propertyGroup.ReceiverOpt, propertyGroup.Properties, analyzedArguments, diagnostics); } @@ -7843,21 +7846,25 @@ private BoundExpression BindIndexedPropertyAccess(BoundPropertyGroup propertyGro } var arguments = AnalyzedArguments.GetInstance(); + Debug.Assert(receiverOpt is not null); var result = BindIndexedPropertyAccess(syntax, receiverOpt, properties, arguments, diagnostics); arguments.Free(); return result; } - private BoundExpression BindIndexedPropertyAccess(SyntaxNode syntax, BoundExpression receiverOpt, ImmutableArray propertyGroup, AnalyzedArguments arguments, BindingDiagnosticBag diagnostics) +#nullable enable + private BoundExpression BindIndexedPropertyAccess(SyntaxNode syntax, BoundExpression receiver, ImmutableArray propertyGroup, AnalyzedArguments arguments, BindingDiagnosticBag diagnostics) { + Debug.Assert(receiver is not null); // TODO: We're creating an extra copy of the properties array in BindIndexerOrIndexedProperty // converting the ArrayBuilder to ImmutableArray. Avoid the extra copy. var properties = ArrayBuilder.GetInstance(); properties.AddRange(propertyGroup); - var result = BindIndexerOrIndexedPropertyAccess(syntax, receiverOpt, properties, arguments, diagnostics); + var result = BindIndexerOrIndexedPropertyAccess(syntax, receiver, properties, arguments, diagnostics); properties.Free(); return result; } +#nullable disable private BoundExpression BindDynamicIndexer( SyntaxNode syntax, @@ -7905,15 +7912,16 @@ private BoundExpression BindDynamicIndexer( private BoundExpression BindIndexerOrIndexedPropertyAccess( SyntaxNode syntax, - BoundExpression receiverOpt, + BoundExpression receiver, ArrayBuilder propertyGroup, AnalyzedArguments analyzedArguments, BindingDiagnosticBag diagnostics) { + Debug.Assert(receiver is not null); OverloadResolutionResult overloadResolutionResult = OverloadResolutionResult.GetInstance(); - bool allowRefOmittedArguments = receiverOpt.IsExpressionOfComImportType(); + bool allowRefOmittedArguments = receiver.IsExpressionOfComImportType(); CompoundUseSiteInfo useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); - this.OverloadResolution.PropertyOverloadResolution(propertyGroup, receiverOpt, analyzedArguments, overloadResolutionResult, allowRefOmittedArguments, ref useSiteInfo); + this.OverloadResolution.PropertyOverloadResolution(propertyGroup, receiver, analyzedArguments, overloadResolutionResult, allowRefOmittedArguments, ref useSiteInfo); diagnostics.Add(syntax, useSiteInfo); BoundExpression propertyAccess; @@ -7923,9 +7931,9 @@ private BoundExpression BindIndexerOrIndexedPropertyAccess( // and an ambiguity error may be reported. Also additional checks are performed in runtime final validation // that are not performed at compile-time. // Only if the set of final applicable candidates is empty we know for sure the call will fail at runtime. - var finalApplicableCandidates = GetCandidatesPassingFinalValidation(syntax, overloadResolutionResult, receiverOpt, default(ImmutableArray), diagnostics); + var finalApplicableCandidates = GetCandidatesPassingFinalValidation(syntax, overloadResolutionResult, receiver, default(ImmutableArray), diagnostics); overloadResolutionResult.Free(); - return BindDynamicIndexer(syntax, receiverOpt, analyzedArguments, finalApplicableCandidates, diagnostics); + return BindDynamicIndexer(syntax, receiver, analyzedArguments, finalApplicableCandidates, diagnostics); } ImmutableArray argumentNames = analyzedArguments.GetNames(); @@ -7939,7 +7947,7 @@ private BoundExpression BindIndexerOrIndexedPropertyAccess( if (TryBindIndexOrRangeImplicitIndexer( syntax, - receiverOpt, + receiver, analyzedArguments, diagnostics, out var implicitIndexerAccess)) @@ -7974,7 +7982,7 @@ private BoundExpression BindIndexerOrIndexedPropertyAccess( propertyAccess = BoundIndexerAccess.ErrorAccess( syntax, - receiverOpt, + receiver, property, arguments, argumentNames, @@ -7985,21 +7993,21 @@ private BoundExpression BindIndexerOrIndexedPropertyAccess( { MemberResolutionResult resolutionResult = overloadResolutionResult.ValidResult; PropertySymbol property = resolutionResult.Member; - RefKind? receiverRefKind = receiverOpt?.GetRefKind(); - uint receiverEscapeScope = property.RequiresInstanceReceiver && receiverOpt != null - ? receiverRefKind?.IsWritableReference() == true ? GetRefEscape(receiverOpt, LocalScopeDepth) : GetValEscape(receiverOpt, LocalScopeDepth) + RefKind? receiverRefKind = receiver.GetRefKind(); + uint receiverEscapeScope = property.RequiresInstanceReceiver && receiver != null + ? receiverRefKind?.IsWritableReference() == true ? GetRefEscape(receiver, LocalScopeDepth) : GetValEscape(receiver, LocalScopeDepth) : Binder.ExternalScope; - this.CoerceArguments(resolutionResult, analyzedArguments.Arguments, diagnostics, receiverOpt?.Type, receiverEscapeScope); + this.CoerceArguments(resolutionResult, analyzedArguments.Arguments, diagnostics, receiver.Type, receiverEscapeScope); var isExpanded = resolutionResult.Result.Kind == MemberResolutionKind.ApplicableInExpandedForm; var argsToParams = resolutionResult.Result.ArgsToParamsOpt; - ReportDiagnosticsIfObsolete(diagnostics, property, syntax, hasBaseReceiver: receiverOpt != null && receiverOpt.Kind == BoundKind.BaseReference); + ReportDiagnosticsIfObsolete(diagnostics, property, syntax, hasBaseReceiver: receiver != null && receiver.Kind == BoundKind.BaseReference); // Make sure that the result of overload resolution is valid. - var gotError = MemberGroupFinalValidationAccessibilityChecks(receiverOpt, property, syntax, diagnostics, invokedAsExtensionMethod: false); + var gotError = MemberGroupFinalValidationAccessibilityChecks(receiver, property, syntax, diagnostics, invokedAsExtensionMethod: false); - var receiver = ReplaceTypeOrValueReceiver(receiverOpt, property.IsStatic, diagnostics); + receiver = ReplaceTypeOrValueReceiver(receiver, property.IsStatic, diagnostics); if (!gotError && receiver != null && receiver.Kind == BoundKind.ThisReference && receiver.WasCompilerGenerated) { @@ -8044,11 +8052,12 @@ private BoundExpression BindIndexerOrIndexedPropertyAccess( #nullable enable private bool TryBindIndexOrRangeImplicitIndexer( SyntaxNode syntax, - BoundExpression? receiverOpt, + BoundExpression receiver, AnalyzedArguments arguments, BindingDiagnosticBag diagnostics, [NotNullWhen(true)] out BoundIndexOrRangePatternIndexerAccess? implicitIndexerAccess) { + Debug.Assert(receiver is not null); implicitIndexerAccess = null; // Verify a few things up-front, namely that we have a single argument @@ -8068,22 +8077,16 @@ private bool TryBindIndexOrRangeImplicitIndexer( TypeSymbol.Equals(argType, Compilation.GetWellKnownType(WellKnownType.System_Range), TypeCompareKind.ConsiderEverything) ? ThreeState.False : ThreeState.Unknown; - if (!argIsIndexNotRange.HasValue() || - !(receiverOpt?.Type is TypeSymbol receiverType)) + Debug.Assert(receiver.Type is not null); + if (!argIsIndexNotRange.HasValue()) { return false; } bool argIsIndex = argIsIndexNotRange.Value(); - var receiverValEscape = receiverOpt switch - { - BoundListPatternReceiverPlaceholder { ValEscape: var valEscape } => valEscape, - BoundSlicePatternReceiverPlaceholder { ValEscape: var valEscape } => valEscape, - _ => GetValEscape(receiverOpt, LocalScopeDepth) - }; - - var receiverPlaceholder = new BoundIndexOrRangeIndexerPatternReceiverPlaceholder(receiverOpt.Syntax, receiverValEscape, receiverOpt.Type) { WasCompilerGenerated = true }; - if (!TryBindIndexOrRangeImplicitIndexerParts(syntax, receiverPlaceholder, receiverOpt, argIsIndex: argIsIndex, + var receiverValEscape = GetValEscape(receiver, LocalScopeDepth); + var receiverPlaceholder = new BoundIndexOrRangeIndexerPatternReceiverPlaceholder(receiver.Syntax, receiverValEscape, receiver.Type) { WasCompilerGenerated = true }; + if (!TryBindIndexOrRangeImplicitIndexerParts(syntax, receiverPlaceholder, receiver, argIsIndex: argIsIndex, out var lengthOrCountAccess, out var indexerOrSliceAccess, out var argumentPlaceholders, diagnostics)) { return false; @@ -8140,7 +8143,7 @@ private bool TryBindIndexOrRangeImplicitIndexerParts( BoundExpression receiver, bool argIsIndex, [NotNullWhen(true)] out BoundExpression? lengthOrCountAccess, - [NotNullWhen(true)] out BoundExpression? implicitIndexerAccess, + [NotNullWhen(true)] out BoundExpression? indexerOrSliceAccess, out ImmutableArray argumentPlaceholders, BindingDiagnosticBag diagnostics) { @@ -8155,20 +8158,20 @@ private bool TryBindIndexOrRangeImplicitIndexerParts( // For Range: Has an accessible Slice method that takes two int parameters if (TryBindLengthOrCount(syntax, receiverPlaceholder, out lengthOrCountAccess, diagnostics) && - tryBindIndexOrRangeImplicitIndexer(syntax, receiver, argIsIndex, out implicitIndexerAccess, out argumentPlaceholders, diagnostics)) + tryBindUnderlyingIndexerOrSliceAccess(syntax, receiver, argIsIndex, out indexerOrSliceAccess, out argumentPlaceholders, diagnostics)) { return true; } lengthOrCountAccess = null; - implicitIndexerAccess = null; + indexerOrSliceAccess = null; argumentPlaceholders = default; return false; // Binds pattern-based implicit indexer: // - for Index indexer, this will find `this[int]`. // - for Range indexer, this will find `Slice(int, int)` or `string.Substring(int, int)`. - bool tryBindIndexOrRangeImplicitIndexer( + bool tryBindUnderlyingIndexerOrSliceAccess( SyntaxNode syntax, BoundExpression receiver, bool argIsIndex, @@ -8177,7 +8180,7 @@ bool tryBindIndexOrRangeImplicitIndexer( BindingDiagnosticBag diagnostics) { Debug.Assert(receiver.Type is not null); - var useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); // TODO2 need to report + var useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); var lookupResult = LookupResult.GetInstance(); if (argIsIndex) @@ -8194,6 +8197,7 @@ bool tryBindIndexOrRangeImplicitIndexer( originalBinder: this, diagnose: false, ref useSiteInfo); + diagnostics.Add(syntax, useSiteInfo); if (lookupResult.IsMultiViable) { @@ -8201,12 +8205,10 @@ bool tryBindIndexOrRangeImplicitIndexer( { if (!candidate.IsStatic && candidate is PropertySymbol property && - IsAccessible(property, ref useSiteInfo) && + IsAccessible(property, syntax, diagnostics) && property.OriginalDefinition is { ParameterCount: 1 } original && original.Parameters[0] is { Type: { SpecialType: SpecialType.System_Int32 }, RefKind: RefKind.None }) { - diagnostics.Add(syntax, useSiteInfo); - var intPlaceholder = new BoundIndexOrRangeIndexerPatternValuePlaceholder(syntax, Compilation.GetSpecialType(SpecialType.System_Int32)) { WasCompilerGenerated = true }; argumentPlaceholders = ImmutableArray.Create(intPlaceholder); @@ -8230,7 +8232,6 @@ candidate is PropertySymbol property && var substring = (MethodSymbol)Compilation.GetSpecialTypeMember(SpecialMember.System_String__Substring); if (substring is object) { - diagnostics.Add(syntax, useSiteInfo); makeCall(syntax, receiver, substring, out indexerOrSliceAccess, out argumentPlaceholders); lookupResult.Free(); return true; @@ -8251,13 +8252,14 @@ candidate is PropertySymbol property && originalBinder: this, diagnose: false, ref useSiteInfo); + diagnostics.Add(syntax, useSiteInfo); if (lookupResult.IsMultiViable) { foreach (var candidate in lookupResult.Symbols) { if (!candidate.IsStatic && - IsAccessible(candidate, ref useSiteInfo) && + IsAccessible(candidate, syntax, diagnostics) && candidate is MethodSymbol method && method.OriginalDefinition is var original && !original.ReturnsVoid && @@ -8265,7 +8267,6 @@ method.OriginalDefinition is var original && original.Parameters[0] is { Type: { SpecialType: SpecialType.System_Int32 }, RefKind: RefKind.None } && original.Parameters[1] is { Type: { SpecialType: SpecialType.System_Int32 }, RefKind: RefKind.None }) { - diagnostics.Add(syntax, useSiteInfo); makeCall(syntax, receiver, method, out indexerOrSliceAccess, out argumentPlaceholders); lookupResult.Free(); return true; @@ -8296,7 +8297,7 @@ void makeCall(SyntaxNode syntax, BoundExpression receiver, MethodSymbol method, method, lookupError: null, BoundMethodGroupFlags.None, functionType: null, receiver, LookupResultKind.Viable); indexerOrSliceAccess = BindMethodGroupInvocation(syntax, syntax, method.Name, boundMethodGroup, analyzedArguments, - diagnostics, queryClause: null, allowUnexpandedForm: false, anyApplicableCandidates: out bool _); + diagnostics, queryClause: null, allowUnexpandedForm: false, anyApplicableCandidates: out bool _).MakeCompilerGenerated(); analyzedArguments.Free(); } @@ -8313,7 +8314,6 @@ private bool TryBindLengthOrCount( Debug.Assert(receiver.Type is not null); if (TryLookupLengthOrCount(syntax, receiver.Type, lookupResult, out var lengthOrCountProperty, diagnostics)) { - diagnostics.ReportUseSite(lengthOrCountProperty, syntax); lengthOrCountAccess = BindPropertyAccess(syntax, receiver, lengthOrCountProperty, diagnostics, lookupResult.Kind, hasErrors: false).MakeCompilerGenerated(); lengthOrCountAccess = CheckValue(lengthOrCountAccess, BindValueKind.RValue, diagnostics); @@ -8335,15 +8335,15 @@ private bool TryLookupLengthOrCount( BindingDiagnosticBag diagnostics) { Debug.Assert(lookupResult.IsClear); - if (tryLookupLengthOrCount(WellKnownMemberNames.LengthPropertyName, out lengthOrCountProperty, diagnostics) || - tryLookupLengthOrCount(WellKnownMemberNames.CountPropertyName, out lengthOrCountProperty, diagnostics)) + if (tryLookupLengthOrCount(syntax, WellKnownMemberNames.LengthPropertyName, out lengthOrCountProperty, diagnostics) || + tryLookupLengthOrCount(syntax, WellKnownMemberNames.CountPropertyName, out lengthOrCountProperty, diagnostics)) { return true; } return false; - bool tryLookupLengthOrCount(string propertyName, [NotNullWhen(true)] out PropertySymbol? valid, BindingDiagnosticBag diagnostics) + bool tryLookupLengthOrCount(SyntaxNode syntax, string propertyName, [NotNullWhen(true)] out PropertySymbol? valid, BindingDiagnosticBag diagnostics) { var useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); LookupMembersInType( @@ -8356,6 +8356,7 @@ bool tryLookupLengthOrCount(string propertyName, [NotNullWhen(true)] out Propert originalBinder: this, diagnose: false, useSiteInfo: ref useSiteInfo); + diagnostics.Add(syntax, useSiteInfo); if (lookupResult.IsSingleViable && lookupResult.Symbols[0] is PropertySymbol property && @@ -8363,12 +8364,10 @@ lookupResult.Symbols[0] is PropertySymbol property && getMethod.ReturnType.SpecialType == SpecialType.System_Int32 && getMethod.RefKind == RefKind.None && !getMethod.IsStatic && - IsAccessible(getMethod, ref useSiteInfo)) + IsAccessible(getMethod, syntax, diagnostics)) { lookupResult.Clear(); valid = property; - property.AddUseSiteInfo(ref useSiteInfo); - diagnostics.Add(syntax, useSiteInfo); return true; } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs index 548aecbcd5bf..939e022b6486 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs @@ -1587,6 +1587,14 @@ internal bool IsAccessible(Symbol symbol, ref CompoundUseSiteInfo /// Check whether "symbol" is accessible from this binder. /// Also checks protected access via "accessThroughType", and sets "failedThroughTypeCheck" if fails diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs index 6900fd663b55..b2b9acce62c6 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs @@ -223,7 +223,7 @@ private BoundPattern BindSlicePattern( indexerAccess = BindElementAccessCore(node, receiverPlaceholder, analyzedArguments, diagnostics).MakeCompilerGenerated(); indexerAccess = CheckValue(indexerAccess, BindValueKind.RValue, diagnostics); - Debug.Assert(indexerAccess is BoundIndexerAccess or BoundIndexOrRangePatternIndexerAccess or BoundArrayAccess or BoundBadExpression); + Debug.Assert(indexerAccess is BoundIndexerAccess or BoundIndexOrRangePatternIndexerAccess or BoundArrayAccess or BoundBadExpression or BoundDynamicIndexerAccess); analyzedArguments.Free(); Debug.Assert(indexerAccess.Type is not null); @@ -332,18 +332,16 @@ private bool IsCountableAndIndexable(SyntaxNode node, TypeSymbol inputType, out private bool BindLengthAndIndexerForListPattern(SyntaxNode node, TypeSymbol inputType, uint inputValEscape, BindingDiagnosticBag diagnostics, out BoundExpression indexerAccess, out BoundExpression lengthAccess, out BoundListPatternReceiverPlaceholder? receiverPlaceholder, out BoundListPatternIndexPlaceholder argumentPlaceholder) { - var bindingDiagnostics = BindingDiagnosticBag.GetInstance(diagnostics); - if (inputType.IsDynamic()) { - Error(bindingDiagnostics, ErrorCode.ERR_UnsupportedTypeForListPattern, node, inputType); + Error(diagnostics, ErrorCode.ERR_UnsupportedTypeForListPattern, node, inputType); } receiverPlaceholder = new BoundListPatternReceiverPlaceholder(node, GetValEscape(inputType, inputValEscape), inputType) { WasCompilerGenerated = true }; bool hasErrors = false; if (inputType.IsSZArray()) { - hasErrors |= !TryGetSpecialTypeMember(Compilation, SpecialMember.System_Array__Length, node, bindingDiagnostics, out PropertySymbol lengthProperty); + hasErrors |= !TryGetSpecialTypeMember(Compilation, SpecialMember.System_Array__Length, node, diagnostics, out PropertySymbol lengthProperty); if (lengthProperty is not null) { lengthAccess = new BoundPropertyAccess(node, receiverPlaceholder, lengthProperty, LookupResultKind.Viable, lengthProperty.Type) { WasCompilerGenerated = true }; @@ -355,21 +353,19 @@ private bool BindLengthAndIndexerForListPattern(SyntaxNode node, TypeSymbol inpu } else { - hasErrors |= !TryBindLengthOrCount(node, receiverPlaceholder, out lengthAccess, bindingDiagnostics); + hasErrors |= !TryBindLengthOrCount(node, receiverPlaceholder, out lengthAccess, diagnostics); } var analyzedArguments = AnalyzedArguments.GetInstance(); - var systemIndexType = GetWellKnownType(WellKnownType.System_Index, bindingDiagnostics, node); + var systemIndexType = GetWellKnownType(WellKnownType.System_Index, diagnostics, node); argumentPlaceholder = new BoundListPatternIndexPlaceholder(node, systemIndexType) { WasCompilerGenerated = true }; analyzedArguments.Arguments.Add(argumentPlaceholder); - indexerAccess = BindElementAccessCore(node, receiverPlaceholder, analyzedArguments, bindingDiagnostics).MakeCompilerGenerated(); - indexerAccess = CheckValue(indexerAccess, BindValueKind.RValue, bindingDiagnostics); + indexerAccess = BindElementAccessCore(node, receiverPlaceholder, analyzedArguments, diagnostics).MakeCompilerGenerated(); + indexerAccess = CheckValue(indexerAccess, BindValueKind.RValue, diagnostics); Debug.Assert(indexerAccess is BoundIndexerAccess or BoundIndexOrRangePatternIndexerAccess or BoundArrayAccess or BoundBadExpression or BoundDynamicIndexerAccess); analyzedArguments.Free(); - diagnostics.AddRangeAndFree(bindingDiagnostics); - if (!systemIndexType.HasUseSiteError) { // Check required well-known member. They may not be needed @@ -378,7 +374,7 @@ private bool BindLengthAndIndexerForListPattern(SyntaxNode node, TypeSymbol inpu _ = GetWellKnownTypeMember(WellKnownMember.System_Index__op_Implicit_FromInt32, diagnostics, syntax: node); } - return !hasErrors && lengthAccess?.HasErrors == false && !indexerAccess.HasErrors; + return !hasErrors && !lengthAccess.HasErrors && !indexerAccess.HasErrors; } private static BoundPattern BindDiscardPattern(DiscardPatternSyntax node, TypeSymbol inputType) diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml index a0e77f5aed11..dc4a76bfe9e1 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml @@ -86,6 +86,10 @@ + + + + - + - + - + - + @@ -2242,7 +2246,7 @@ @@ -2258,7 +2262,7 @@ diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.DebugVerifier.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.DebugVerifier.cs index c5566fe60179..73ca053d681f 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.DebugVerifier.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.DebugVerifier.cs @@ -276,6 +276,7 @@ private void VisitBinaryOperatorChildren(BoundBinaryOperatorBase node) public override BoundNode? VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node) { + Visit(node.Argument); Visit(node.IndexerAccess); return null; } diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index 43b8e215925a..52d895a1f8cd 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -8697,6 +8697,7 @@ private TypeWithAnnotations GetDeclaredParameterResult(ParameterSymbol parameter public override BoundNode? VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node) { + VisitRvalue(node.Argument); VisitRvalue(node.IndexerAccess); SetResult(node, ResultType, LvalueResultType); return null; diff --git a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs index 993e91923d79..1be4b59a930f 100644 --- a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs @@ -437,6 +437,20 @@ protected BoundValuePlaceholderBase(BoundKind kind, SyntaxNode syntax, TypeSymbo } + internal abstract partial class BoundEarlyValuePlaceholderBase : BoundValuePlaceholderBase + { + protected BoundEarlyValuePlaceholderBase(BoundKind kind, SyntaxNode syntax, TypeSymbol? type, bool hasErrors) + : base(kind, syntax, type, hasErrors) + { + } + + protected BoundEarlyValuePlaceholderBase(BoundKind kind, SyntaxNode syntax, TypeSymbol? type) + : base(kind, syntax, type) + { + } + + } + internal sealed partial class BoundValuePlaceholder : BoundValuePlaceholderBase { public BoundValuePlaceholder(SyntaxNode syntax, TypeSymbol? type, bool hasErrors) @@ -719,7 +733,7 @@ public BoundIndexOrRangeIndexerPatternReceiverPlaceholder Update(uint valEscape, } } - internal sealed partial class BoundListPatternReceiverPlaceholder : BoundValuePlaceholderBase + internal sealed partial class BoundListPatternReceiverPlaceholder : BoundEarlyValuePlaceholderBase { public BoundListPatternReceiverPlaceholder(SyntaxNode syntax, uint valEscape, TypeSymbol type, bool hasErrors) : base(BoundKind.ListPatternReceiverPlaceholder, syntax, type, hasErrors) @@ -758,7 +772,7 @@ public BoundListPatternReceiverPlaceholder Update(uint valEscape, TypeSymbol typ } } - internal sealed partial class BoundListPatternIndexPlaceholder : BoundValuePlaceholderBase + internal sealed partial class BoundListPatternIndexPlaceholder : BoundEarlyValuePlaceholderBase { public BoundListPatternIndexPlaceholder(SyntaxNode syntax, TypeSymbol type, bool hasErrors) : base(BoundKind.ListPatternIndexPlaceholder, syntax, type, hasErrors) @@ -793,7 +807,7 @@ public BoundListPatternIndexPlaceholder Update(TypeSymbol type) } } - internal sealed partial class BoundSlicePatternReceiverPlaceholder : BoundValuePlaceholderBase + internal sealed partial class BoundSlicePatternReceiverPlaceholder : BoundEarlyValuePlaceholderBase { public BoundSlicePatternReceiverPlaceholder(SyntaxNode syntax, uint valEscape, TypeSymbol type, bool hasErrors) : base(BoundKind.SlicePatternReceiverPlaceholder, syntax, type, hasErrors) @@ -832,7 +846,7 @@ public BoundSlicePatternReceiverPlaceholder Update(uint valEscape, TypeSymbol ty } } - internal sealed partial class BoundSlicePatternRangePlaceholder : BoundValuePlaceholderBase + internal sealed partial class BoundSlicePatternRangePlaceholder : BoundEarlyValuePlaceholderBase { public BoundSlicePatternRangePlaceholder(SyntaxNode syntax, TypeSymbol type, bool hasErrors) : base(BoundKind.SlicePatternRangePlaceholder, syntax, type, hasErrors) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.PatternLocalRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.PatternLocalRewriter.cs index a194c4274565..9e35f3eb68d4 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.PatternLocalRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.PatternLocalRewriter.cs @@ -257,7 +257,7 @@ void addArg(RefKind refKind, BoundExpression expression) indexerAccess = implicitAccess.WithLengthOrCountAccess(_tempAllocator.GetTemp(e.LengthTemp)); } - var placeholderValues = PooledDictionary.GetInstance(); + var placeholderValues = PooledDictionary.GetInstance(); placeholderValues.Add(e.ReceiverPlaceholder, input); placeholderValues.Add(e.ArgumentPlaceholder, makeUnloweredIndexArgument(e.Index)); indexerAccess = PlaceholderReplacer.Replace(placeholderValues, indexerAccess); @@ -283,7 +283,7 @@ void addArg(RefKind refKind, BoundExpression expression) indexerAccess = implicitAccess.WithLengthOrCountAccess(_tempAllocator.GetTemp(e.LengthTemp)); } - var placeholderValues = PooledDictionary.GetInstance(); + var placeholderValues = PooledDictionary.GetInstance(); placeholderValues.Add(e.ReceiverPlaceholder, input); placeholderValues.Add(e.ArgumentPlaceholder, makeUnloweredRangeArgument(e)); indexerAccess = PlaceholderReplacer.Replace(placeholderValues, indexerAccess); diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.PlaceholderReplacer.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.PlaceholderReplacer.cs index fc34f94f14b5..57c9febc6c21 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.PlaceholderReplacer.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.PlaceholderReplacer.cs @@ -11,21 +11,21 @@ internal sealed partial class LocalRewriter { private sealed class PlaceholderReplacer : BoundTreeRewriterWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperator { - private readonly Dictionary _placeholders; + private readonly Dictionary _placeholders; - PlaceholderReplacer(Dictionary placeholders) + private PlaceholderReplacer(Dictionary placeholders) { _placeholders = placeholders; } - public static BoundExpression Replace(Dictionary placeholders, BoundExpression expr) + public static BoundExpression Replace(Dictionary placeholders, BoundExpression expr) { var result = new PlaceholderReplacer(placeholders).Visit(expr); Debug.Assert(result is not null); return (BoundExpression)result; } - private BoundNode ReplacePlaceholder(BoundValuePlaceholderBase placeholder) + private BoundNode ReplacePlaceholder(BoundEarlyValuePlaceholderBase placeholder) { var value = _placeholders[placeholder]; Debug.Assert(value is not null); @@ -36,29 +36,27 @@ private BoundNode ReplacePlaceholder(BoundValuePlaceholderBase placeholder) { var argument = (BoundExpression)this.Visit(node.Argument); var lengthOrCountAccess = (BoundExpression)this.Visit(node.LengthOrCountAccess); - var receiverPlaceholder = (BoundIndexOrRangeIndexerPatternReceiverPlaceholder)this.Visit(node.ReceiverPlaceholder); var indexerAccess = (BoundExpression)this.Visit(node.IndexerAccess); - var argumentPlaceholders = this.VisitList(node.ArgumentPlaceholders); var type = this.VisitType(node.Type); - return node.Update(argument, lengthOrCountAccess, receiverPlaceholder, indexerAccess, argumentPlaceholders, type); + return node.Update(argument, lengthOrCountAccess, node.ReceiverPlaceholder, indexerAccess, node.ArgumentPlaceholders, type); } - public override BoundNode? VisitListPatternReceiverPlaceholder(BoundListPatternReceiverPlaceholder node) + public override BoundNode VisitListPatternReceiverPlaceholder(BoundListPatternReceiverPlaceholder node) { return ReplacePlaceholder(node); } - public override BoundNode? VisitListPatternIndexPlaceholder(BoundListPatternIndexPlaceholder node) + public override BoundNode VisitListPatternIndexPlaceholder(BoundListPatternIndexPlaceholder node) { return ReplacePlaceholder(node); } - public override BoundNode? VisitSlicePatternReceiverPlaceholder(BoundSlicePatternReceiverPlaceholder node) + public override BoundNode VisitSlicePatternReceiverPlaceholder(BoundSlicePatternReceiverPlaceholder node) { return ReplacePlaceholder(node); } - public override BoundNode? VisitSlicePatternRangePlaceholder(BoundSlicePatternRangePlaceholder node) + public override BoundNode VisitSlicePatternRangePlaceholder(BoundSlicePatternRangePlaceholder node) { return ReplacePlaceholder(node); } diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs index 052d3e1cf3b9..e01c9053caf5 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs @@ -675,7 +675,7 @@ private BoundExpression VisitRangePatternIndexerAccess(BoundIndexOrRangePatternI rewrittenIndexerAccess); } - BoundExpression RewriteLengthAccess(BoundIndexOrRangePatternIndexerAccess node, BoundExpression receiver) + private BoundExpression RewriteLengthAccess(BoundIndexOrRangePatternIndexerAccess node, BoundExpression receiver) { var receiverPlaceholder = node.ReceiverPlaceholder; AddPlaceholderReplacement(receiverPlaceholder, receiver); diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/IndexAndRangeTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/IndexAndRangeTests.cs index 7a9ae0037048..cc2327b589a0 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/IndexAndRangeTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/IndexAndRangeTests.cs @@ -3372,5 +3372,76 @@ class C Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "M(o2)[..]").WithLocation(16, 5) ); } + + [Fact] + public void NullableIndexerArgument() + { + var source = @" +#nullable enable +var c = new C(); + +object o1 = null; +_ = c[M1(o1.ToString())]; + +object o2 = null; +_ = c[M2(o2.ToString())]; + +static System.Index M1(object? o) => throw null!; +static System.Range M2(object? o) => throw null!; + +class C +{ + public int Length => 0; + public int this[int i] => throw null!; + public int Slice(int i, int j) => throw null!; +} +"; + var comp = CreateCompilation(new[] { source, TestSources.Index, TestSources.Range }); + comp.VerifyDiagnostics( + // (5,13): warning CS8600: Converting null literal or possible null value to non-nullable type. + // object o1 = null; + Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null").WithLocation(5, 13), + // (6,10): warning CS8602: Dereference of a possibly null reference. + // _ = c[M1(o1.ToString())]; + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "o1").WithLocation(6, 10), + // (8,13): warning CS8600: Converting null literal or possible null value to non-nullable type. + // object o2 = null; + Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null").WithLocation(8, 13), + // (9,10): warning CS8602: Dereference of a possibly null reference. + // _ = c[M2(o2.ToString())]; + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "o2").WithLocation(9, 10) + ); + } + + [Fact] + public void SemanticModelOnReceiver() + { + var source = @" +var c = new C(); + +_ = c[^1]; +_ = c[..]; + +class C +{ + public int Length => 0; + public int this[int i] => throw null!; + public int Slice(int i, int j) => throw null!; +} +"; + var comp = CreateCompilation(new[] { source, TestSources.Index, TestSources.Range }); + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var receivers = tree.GetRoot().DescendantNodes().OfType().Select(e => e.Expression).ToArray(); + Assert.Equal(2, receivers.Length); + var model = comp.GetSemanticModel(tree, ignoreAccessibility: false); + + Assert.Equal("c", receivers[0].ToString()); + Assert.Equal("C", model.GetTypeInfo(receivers[0]).Type.ToTestDisplayString()); + + Assert.Equal("c", receivers[1].ToString()); + Assert.Equal("C", model.GetTypeInfo(receivers[1]).Type.ToTestDisplayString()); + } } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs index e5f299708ae1..145de20ba5ab 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs @@ -335,7 +335,6 @@ public static void Main() "; var verifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); - // PROTOTYPE the call to StoreToTemp in LocalRewriter.VisitIndexImplicitIndexerAccess causes more temps to be used AssertEx.Multiple( () => verifier.VerifyIL("X.Test1", @" { @@ -519,7 +518,6 @@ public static void Main() True "; var verifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); - // PROTOTYPE the call to StoreToTemp in LocalRewriter.VisitIndexImplicitIndexerAccess causes more temps to be used AssertEx.Multiple( () => verifier.VerifyIL("X.Test1", @" { @@ -4482,15 +4480,18 @@ class C { void M(dynamic d) { - _ = d is [_]; + _ = d is [_, .._]; } } "; var compilation = CreateCompilation(new[] { source, TestSources.Index }); compilation.VerifyEmitDiagnostics( - // (7,18): error CS8978: List patterns may not be used for a value of type 'dynamic'. - // _ = d is [_]; - Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[_]").WithArguments("dynamic").WithLocation(7, 18) + // (7,18): error CS8979: List patterns may not be used for a value of type 'dynamic'. + // _ = d is [_, .._]; + Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[_, .._]").WithArguments("dynamic").WithLocation(7, 18), + // (7,22): error CS0518: Predefined type 'System.Range' is not defined or imported + // _ = d is [_, .._]; + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, ".._").WithArguments("System.Range").WithLocation(7, 22) ); } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs index f1511be8f3a6..a0cc6ec261cc 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs @@ -964,6 +964,7 @@ public void AllWellKnownTypeMembers() case WellKnownMember.System_Runtime_CompilerServices_NativeIntegerAttribute__ctor: case WellKnownMember.System_Runtime_CompilerServices_NativeIntegerAttribute__ctorTransformFlags: case WellKnownMember.System_Runtime_CompilerServices_DefaultInterpolatedStringHandler__ToStringAndClear: + case WellKnownMember.System_Index__op_Implicit_FromInt32: // Not yet in the platform. continue; case WellKnownMember.Microsoft_CodeAnalysis_Runtime_Instrumentation__CreatePayloadForMethodsSpanningSingleFile: diff --git a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb index a8b6b5df00e1..5454d830e773 100644 --- a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb +++ b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb @@ -716,7 +716,8 @@ End Namespace WellKnownMember.System_Runtime_CompilerServices_ITuple__get_Item, WellKnownMember.System_Runtime_CompilerServices_ITuple__get_Length, WellKnownMember.System_Runtime_CompilerServices_SwitchExpressionException__ctor, - WellKnownMember.System_Runtime_CompilerServices_SwitchExpressionException__ctorObject + WellKnownMember.System_Runtime_CompilerServices_SwitchExpressionException__ctorObject, + WellKnownMember.System_Index__op_Implicit_FromInt32 ' Not always available. Continue For End Select @@ -858,7 +859,8 @@ End Namespace WellKnownMember.System_Runtime_CompilerServices_ITuple__get_Item, WellKnownMember.System_Runtime_CompilerServices_ITuple__get_Length, WellKnownMember.System_Runtime_CompilerServices_SwitchExpressionException__ctor, - WellKnownMember.System_Runtime_CompilerServices_SwitchExpressionException__ctorObject + WellKnownMember.System_Runtime_CompilerServices_SwitchExpressionException__ctorObject, + WellKnownMember.System_Index__op_Implicit_FromInt32 ' Not always available. Continue For End Select From 1912f3777c932f4930ba2c8dbf9c6e514d93aa4c Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Wed, 17 Nov 2021 11:39:04 -0800 Subject: [PATCH 17/29] Fix NullabilityRewriter --- .../CSharp/Portable/BoundTree/BoundNodes.xml | 2 +- .../Portable/BoundTree/NullabilityRewriter.cs | 19 +++++++++++++++++ .../Generated/BoundNodes.xml.Generated.cs | 21 ------------------- 3 files changed, 20 insertions(+), 22 deletions(-) diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml index dc4a76bfe9e1..1cb701175278 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml @@ -2066,7 +2066,7 @@ - + diff --git a/src/Compilers/CSharp/Portable/BoundTree/NullabilityRewriter.cs b/src/Compilers/CSharp/Portable/BoundTree/NullabilityRewriter.cs index 9fee831e3ffd..a5293b7f22d0 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/NullabilityRewriter.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/NullabilityRewriter.cs @@ -162,6 +162,25 @@ Symbol remapLocal(SourceLocalSymbol local) } } + public override BoundNode? VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node) + { + BoundExpression argument = (BoundExpression)this.Visit(node.Argument); + BoundExpression lengthOrCountAccess = node.LengthOrCountAccess; + BoundExpression indexerAccess = (BoundExpression)this.Visit(node.IndexerAccess); + BoundIndexOrRangePatternIndexerAccess updatedNode; + + if (_updatedNullabilities.TryGetValue(node, out (NullabilityInfo Info, TypeSymbol? Type) infoAndType)) + { + updatedNode = node.Update(argument, lengthOrCountAccess, node.ReceiverPlaceholder, indexerAccess, node.ArgumentPlaceholders, infoAndType.Type!); + updatedNode.TopLevelNullability = infoAndType.Info; + } + else + { + updatedNode = node.Update(argument, lengthOrCountAccess, node.ReceiverPlaceholder, indexerAccess, node.ArgumentPlaceholders, node.Type); + } + return updatedNode; + } + private ImmutableArray GetUpdatedArray(BoundNode expr, ImmutableArray symbols) where T : Symbol? { if (symbols.IsDefaultOrEmpty) diff --git a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs index 1be4b59a930f..a50454b7af09 100644 --- a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs @@ -14013,27 +14013,6 @@ public NullabilityRewriter(ImmutableDictionary argumentPlaceholders = this.VisitList(node.ArgumentPlaceholders); - BoundIndexOrRangePatternIndexerAccess updatedNode; - - if (_updatedNullabilities.TryGetValue(node, out (NullabilityInfo Info, TypeSymbol? Type) infoAndType)) - { - updatedNode = node.Update(argument, lengthOrCountAccess, receiverPlaceholder, indexerAccess, argumentPlaceholders, infoAndType.Type!); - updatedNode.TopLevelNullability = infoAndType.Info; - } - else - { - updatedNode = node.Update(argument, lengthOrCountAccess, receiverPlaceholder, indexerAccess, argumentPlaceholders, node.Type); - } - return updatedNode; - } - public override BoundNode? VisitDynamicIndexerAccess(BoundDynamicIndexerAccess node) { ImmutableArray applicableIndexers = GetUpdatedArray(node, node.ApplicableIndexers); From cd501b497bf2fca358d944655a0e9f8a5ae7f0ab Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Wed, 17 Nov 2021 12:14:44 -0800 Subject: [PATCH 18/29] tweaks --- .../Portable/Binder/Binder_Expressions.cs | 5 +- .../LocalRewriter_IndexerAccess.cs | 4 +- .../PatternMatchingTests_ListPatterns.cs | 57 +++++++++++++++++++ .../QuickInfo/SemanticQuickInfoSourceTests.cs | 4 +- 4 files changed, 64 insertions(+), 6 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index 7112a6065e9a..00051a70ab3a 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -1728,7 +1728,7 @@ private bool IsBadLocalOrParameterCapture(Symbol symbol, TypeSymbol type, RefKin private BoundExpression BindNonMethod(SimpleNameSyntax node, Symbol symbol, BindingDiagnosticBag diagnostics, LookupResultKind resultKind, bool indexed, bool isError) { // Events are handled later as we don't know yet if we are binding to the event or it's backing field. - if (symbol.Kind is not SymbolKind.Event and not SymbolKind.Property) + if (symbol.Kind is not (SymbolKind.Event or SymbolKind.Property)) { ReportDiagnosticsIfObsolete(diagnostics, symbol, node, hasBaseReceiver: false); } @@ -6764,7 +6764,7 @@ private BoundExpression BindMemberOfType( // Events are handled later as we don't know yet if we are binding to the event or it's backing field. // Properties are handled in BindPropertyAccess - if (symbol.Kind is not SymbolKind.Event and not SymbolKind.Property) + if (symbol.Kind is not (SymbolKind.Event and SymbolKind.Property)) { ReportDiagnosticsIfObsolete(diagnostics, symbol, node, hasBaseReceiver: left.Kind == BoundKind.BaseReference); } @@ -8314,6 +8314,7 @@ private bool TryBindLengthOrCount( Debug.Assert(receiver.Type is not null); if (TryLookupLengthOrCount(syntax, receiver.Type, lookupResult, out var lengthOrCountProperty, diagnostics)) { + diagnostics.ReportUseSite(lengthOrCountProperty, syntax); lengthOrCountAccess = BindPropertyAccess(syntax, receiver, lengthOrCountProperty, diagnostics, lookupResult.Kind, hasErrors: false).MakeCompilerGenerated(); lengthOrCountAccess = CheckValue(lengthOrCountAccess, BindValueKind.RValue, diagnostics); diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs index e01c9053caf5..16c82e2a0ed2 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs @@ -260,7 +260,7 @@ private BoundExpression VisitIndexPatternIndexerAccess(BoundIndexOrRangePatternI // Do not capture receiver if it is a local or parameter and we are evaluating a pattern // If length access is a local, then we are evaluating a pattern - if (node.LengthOrCountAccess.Kind is not BoundKind.Local || receiver.Kind is not BoundKind.Local or BoundKind.Parameter) + if (node.LengthOrCountAccess.Kind is not BoundKind.Local || receiver.Kind is not (BoundKind.Local or BoundKind.Parameter)) { Debug.Assert(receiver.Type is { }); @@ -451,7 +451,7 @@ private BoundExpression VisitRangePatternIndexerAccess(BoundIndexOrRangePatternI // Do not capture receiver if it is a local or parameter and we are evaluating a pattern // If length access is a local, then we are evaluating a pattern - if (node.LengthOrCountAccess.Kind is not BoundKind.Local || receiver.Kind is not BoundKind.Local or BoundKind.Parameter) + if (node.LengthOrCountAccess.Kind is not BoundKind.Local || receiver.Kind is not (BoundKind.Local or BoundKind.Parameter)) { var receiverLocal = F.StoreToTemp(receiver, out var receiverStore); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs index 145de20ba5ab..557b3ba27dee 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs @@ -2972,6 +2972,63 @@ void verify(PropertyPatternClauseSyntax clause, string syntax, string type) } } + [Fact] + public void ListPattern_Symbols_WithoutIndexOrRangeOrGetSubArray() + { + var source = @" +#nullable enable +class X +{ + public void Test(int[] integers) + { + _ = integers is [var item, ..var slice]; + } +}"; + var compilation = CreateCompilation(source); + compilation.VerifyDiagnostics( + // (7,25): error CS0518: Predefined type 'System.Index' is not defined or imported + // _ = integers is [var item, ..var slice]; + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "[var item, ..var slice]").WithArguments("System.Index").WithLocation(7, 25), + // (7,36): error CS0518: Predefined type 'System.Range' is not defined or imported + // _ = integers is [var item, ..var slice]; + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "..var slice").WithArguments("System.Range").WithLocation(7, 36) + ); + + var tree = compilation.SyntaxTrees.First(); + var model = compilation.GetSemanticModel(tree); + var designations = tree.GetRoot().DescendantNodes().OfType().ToArray(); + + var itemDesignation = designations[0]; + Assert.Equal("item", itemDesignation.ToString()); + + var symbol = model.GetDeclaredSymbol(itemDesignation); + Assert.Equal(SymbolKind.Local, symbol.Kind); + Assert.Equal("System.Int32", ((ILocalSymbol)symbol).Type.ToTestDisplayString()); + + var typeInfo = model.GetTypeInfo(itemDesignation); + Assert.Null(typeInfo.Type); + Assert.Null(typeInfo.ConvertedType); + + typeInfo = model.GetTypeInfo(itemDesignation.Parent); + Assert.Equal("System.Int32", typeInfo.Type.ToTestDisplayString()); + Assert.Equal("System.Int32", typeInfo.ConvertedType.ToTestDisplayString()); + + var sliceDesignation = designations[1]; + Assert.Equal("slice", sliceDesignation.ToString()); + + symbol = model.GetDeclaredSymbol(sliceDesignation); + Assert.Equal(SymbolKind.Local, symbol.Kind); + Assert.Equal("System.Int32", ((ILocalSymbol)symbol).Type.ToTestDisplayString()); + + typeInfo = model.GetTypeInfo(sliceDesignation); + Assert.Null(typeInfo.Type); + Assert.Null(typeInfo.ConvertedType); + + typeInfo = model.GetTypeInfo(sliceDesignation.Parent); + Assert.Equal("System.Int32", typeInfo.Type.ToTestDisplayString()); + Assert.Equal("System.Int32", typeInfo.ConvertedType.ToTestDisplayString()); + } + [Fact] public void PatternIndexRangeReadOnly_01() { diff --git a/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs b/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs index 4b65529fbc8d..bd6fa8efe567 100644 --- a/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs +++ b/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs @@ -7617,7 +7617,7 @@ void M(char[] array) { } } -}", +}" + TestSources.Index + TestSources.Range, MainDescription("char[]")); } @@ -7633,7 +7633,7 @@ void M(char[] array) { } } -}", +}" + TestSources.Index + TestSources.Range, MainDescription($"({FeaturesResources.local_variable}) char[]? one")); } From aab5d7413e6151ce9c393601f9af05e1f5e71daa Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Wed, 17 Nov 2021 12:33:05 -0800 Subject: [PATCH 19/29] compiler generated --- src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index 00051a70ab3a..af368ac4f370 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -8294,7 +8294,8 @@ void makeCall(SyntaxNode syntax, BoundExpression receiver, MethodSymbol method, var boundMethodGroup = new BoundMethodGroup( syntax, typeArgumentsOpt: default, method.Name, ImmutableArray.Create(method), - method, lookupError: null, BoundMethodGroupFlags.None, functionType: null, receiver, LookupResultKind.Viable); + method, lookupError: null, BoundMethodGroupFlags.None, functionType: null, receiver, LookupResultKind.Viable) + { WasCompilerGenerated = true }; indexerOrSliceAccess = BindMethodGroupInvocation(syntax, syntax, method.Name, boundMethodGroup, analyzedArguments, diagnostics, queryClause: null, allowUnexpandedForm: false, anyApplicableCandidates: out bool _).MakeCompilerGenerated(); From 8be23fabae2f47b4dc328be2afa542fc10e85a79 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Wed, 17 Nov 2021 14:14:22 -0800 Subject: [PATCH 20/29] or --- src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index af368ac4f370..6f98922bdcc9 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -6764,7 +6764,7 @@ private BoundExpression BindMemberOfType( // Events are handled later as we don't know yet if we are binding to the event or it's backing field. // Properties are handled in BindPropertyAccess - if (symbol.Kind is not (SymbolKind.Event and SymbolKind.Property)) + if (symbol.Kind is not (SymbolKind.Event or SymbolKind.Property)) { ReportDiagnosticsIfObsolete(diagnostics, symbol, node, hasBaseReceiver: left.Kind == BoundKind.BaseReference); } From 6c132faa6562bf2a6acebd3a17bebfceb4a3742d Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Wed, 17 Nov 2021 15:44:58 -0800 Subject: [PATCH 21/29] Fix order of evaluation for nullability --- .../Portable/FlowAnalysis/NullableWalker.cs | 50 ++++++++++++++++++- .../Test/Emit/CodeGen/IndexAndRangeTests.cs | 44 ++++++++++++++++ 2 files changed, 93 insertions(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index 52d895a1f8cd..c526a14abdcd 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -192,6 +192,8 @@ public VisitArgumentResult(VisitResult visitResult, Optional stateFo /// private PooledDictionary? _awaitablePlaceholdersOpt; + private PooledDictionary? _unvisitedPlaceholdersOpt; + /// /// Variables instances for each lambda or local function defined within the analyzed region. /// @@ -368,8 +370,11 @@ private void SetAnalyzedNullability(BoundExpression? expr, VisitResult result, b protected override void Free() { + AssertNoUnvisitedPlaceholderReplacements(); + _nestedFunctionVariables?.Free(); _awaitablePlaceholdersOpt?.Free(); + _unvisitedPlaceholdersOpt?.Free(); _methodGroupReceiverMapOpt?.Free(); _placeholderLocalsOpt?.Free(); _variables.Free(); @@ -454,6 +459,39 @@ protected override int AddVariable(VariableIdentifier identifier) return _variables.Add(identifier); } + private void AddUnvisitedPlaceholder(BoundValuePlaceholderBase placeholder, BoundExpression unvisited) + { + _unvisitedPlaceholdersOpt ??= PooledDictionary.GetInstance(); + _unvisitedPlaceholdersOpt.Add(placeholder, unvisited); + } + + private void RemoveUnvisitedPlaceholder(BoundValuePlaceholderBase placeholder) + { + Debug.Assert(_unvisitedPlaceholdersOpt is not null); + _unvisitedPlaceholdersOpt.Remove(placeholder); + } + + private bool TryReplaceUnvisitedPlaceholder(BoundValuePlaceholderBase placeholder, [NotNullWhen(true)] out BoundExpression? unvisited) + { + if (_unvisitedPlaceholdersOpt is not null && + _unvisitedPlaceholdersOpt.TryGetValue(placeholder, out unvisited)) + { + return true; + } + + unvisited = null; + return false; + } + + [Conditional("DEBUG")] + private void AssertNoUnvisitedPlaceholderReplacements() + { + if (_unvisitedPlaceholdersOpt is not null) + { + Debug.Assert(_unvisitedPlaceholdersOpt.Count == 0); + } + } + protected override ImmutableArray Scan(ref bool badRegion) { if (_returnTypesOpt != null) @@ -8697,14 +8735,24 @@ private TypeWithAnnotations GetDeclaredParameterResult(ParameterSymbol parameter public override BoundNode? VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node) { - VisitRvalue(node.Argument); + // The argument will be visited as part of VisitIndexOrRangeIndexerPatternValuePlaceholder (for the first argument) + // to maintain proper order of evaluation + AddUnvisitedPlaceholder(node.ArgumentPlaceholders[0], node.Argument); VisitRvalue(node.IndexerAccess); + RemoveUnvisitedPlaceholder(node.ArgumentPlaceholders[0]); + SetResult(node, ResultType, LvalueResultType); return null; } public override BoundNode? VisitIndexOrRangeIndexerPatternValuePlaceholder(BoundIndexOrRangeIndexerPatternValuePlaceholder node) { + // We use this placeholder as trigger to visit the Argument of implicit indexer access (after its Receiver) + if (TryReplaceUnvisitedPlaceholder(node, out var unvisited)) + { + VisitRvalue(unvisited); + } + SetNotNullResult(node); return null; } diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/IndexAndRangeTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/IndexAndRangeTests.cs index cc2327b589a0..d9b7370fb709 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/IndexAndRangeTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/IndexAndRangeTests.cs @@ -3443,5 +3443,49 @@ class C Assert.Equal("c", receivers[1].ToString()); Assert.Equal("C", model.GetTypeInfo(receivers[1]).Type.ToTestDisplayString()); } + + [Fact] + public void Nullable_OrderOfEvaluation() + { + var source = @" +#nullable enable +C? c = null; +_ = (c = new C())[M1(c.ToString())]; + +c = null; +_ = (c = new C())[M2(c.ToString())]; + +c = null; +_ = c[M1((c = new C()).ToString())]; // 1 + +string? s = null; +_ = (s = string.Empty)[M1(s.ToString())]; + +s = null; +_ = (s = string.Empty)[M2(s.ToString())]; + +int[]? a = null; +_ = (a = new[] { 1 })[M1(a.ToString())]; + +a = null; +_ = (a = new[] { 1 })[M2(a.ToString())]; + +static System.Index M1(object? o) => throw null!; +static System.Range M2(object? o) => throw null!; + +class C +{ + public int Length => 0; + public int this[int i] => throw null!; + public int Slice(int i, int j) => throw null!; +} +"; + var comp = CreateCompilation(new[] { source, TestSources.Index, TestSources.Range, TestSources.GetSubArray }); + comp.VerifyDiagnostics( + // (10,5): warning CS8602: Dereference of a possibly null reference. + // _ = c[M1((c = new C()).ToString())]; // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c").WithLocation(10, 5) + ); + } } } From a4ba54b06da645920b2c61af190acd844f311c00 Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Thu, 18 Nov 2021 07:38:15 -0800 Subject: [PATCH 22/29] Clean up handling of an implicit indexer as a target of an assignment in LocalRewriter. Closes #57810. --- .../LocalRewriter_IndexerAccess.cs | 55 +++++++++++-------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs index 16c82e2a0ed2..e9c304f0b97a 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs @@ -118,8 +118,6 @@ private BoundExpression MakeIndexerAccess( // This is an indexer set access. We return a BoundIndexerAccess node here. // This node will be rewritten with MakePropertyAssignment when rewriting the enclosing BoundAssignmentOperator. - arguments = unwrapPlaceholdersIfNeeded(arguments); - return oldNodeOpt != null ? oldNodeOpt.Update(rewrittenReceiver, indexer, arguments, argumentNamesOpt, argumentRefKindsOpt, expanded, argsToParamsOpt, defaultArguments, type) : new BoundIndexerAccess(syntax, rewrittenReceiver, indexer, arguments, argumentNamesOpt, argumentRefKindsOpt, expanded, argsToParamsOpt, defaultArguments, type); @@ -163,26 +161,6 @@ private BoundExpression MakeIndexerAccess( type); } } - - ImmutableArray unwrapPlaceholdersIfNeeded(ImmutableArray arguments) - { - if (!arguments.Any(a => a is BoundIndexOrRangeIndexerPatternValuePlaceholder)) - { - return arguments; - } - - return arguments.SelectAsArray(a => unwrapPlaceholderIfNeeded(a)); - } - - BoundExpression unwrapPlaceholderIfNeeded(BoundExpression argument) - { - if (argument is BoundIndexOrRangeIndexerPatternValuePlaceholder placeholder) - { - return PlaceholderReplacement(placeholder); - } - - return argument; - } } public override BoundNode? VisitListPatternIndexPlaceholder(BoundListPatternIndexPlaceholder node) @@ -308,7 +286,38 @@ private BoundExpression VisitIndexPatternIndexerAccess(BoundIndexOrRangePatternI Debug.Assert(node.ArgumentPlaceholders.Length == 1); var argumentPlaceholder = node.ArgumentPlaceholders[0]; AddPlaceholderReplacement(argumentPlaceholder, integerArgument); - var rewrittenIndexerAccess = VisitIndexerAccess(indexerAccess.WithReceiver(receiver), isLeftOfAssignment); + + BoundExpression rewrittenIndexerAccess; + + if (isLeftOfAssignment && indexerAccess.Indexer.RefKind == RefKind.None) + { + ImmutableArray rewrittenArguments = VisitArguments( + indexerAccess.Arguments, + indexerAccess.Indexer, + indexerAccess.ArgsToParamsOpt, + indexerAccess.ArgumentRefKindsOpt, + ref receiver, + out ArrayBuilder? temps); + + if (temps is not null) + { + locals.AddRange(temps); + temps.Free(); + } + + rewrittenIndexerAccess = indexerAccess.Update( + receiver, indexerAccess.Indexer, rewrittenArguments, + indexerAccess.ArgumentNamesOpt, indexerAccess.ArgumentRefKindsOpt, + indexerAccess.Expanded, + indexerAccess.ArgsToParamsOpt, + indexerAccess.DefaultArguments, + indexerAccess.Type); + } + else + { + rewrittenIndexerAccess = VisitIndexerAccess(indexerAccess.WithReceiver(receiver), isLeftOfAssignment); + } + RemovePlaceholderReplacement(argumentPlaceholder); return F.Sequence( From dff82c5d7a1ffc2d5adfee4cb66dd81ca0737069 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Thu, 18 Nov 2021 09:35:27 -0800 Subject: [PATCH 23/29] Address feedback (5) --- .../Binder/DecisionDagBuilder_ListPatterns.cs | 2 + .../BoundIndexOrRangePatternIndexerAccess.cs | 1 - .../CSharp/Portable/BoundTree/BoundNodes.xml | 6 +-- .../Portable/FlowAnalysis/NullableWalker.cs | 46 +++++++++++-------- .../Semantic/Semantics/IndexAndRangeTests.cs | 28 ++++------- 5 files changed, 41 insertions(+), 42 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder_ListPatterns.cs b/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder_ListPatterns.cs index 1a23e72c651d..09b4db76aab9 100644 --- a/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder_ListPatterns.cs +++ b/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder_ListPatterns.cs @@ -60,6 +60,7 @@ private Tests MakeTestsAndBindingsForListPattern(BoundDagTemp input, BoundListPa Debug.Assert(slice.ReceiverPlaceholder is not null); Debug.Assert(slice.ArgumentPlaceholder is not null); + Debug.Assert(slice.IndexerAccess is BoundIndexerAccess or BoundIndexOrRangePatternIndexerAccess or BoundArrayAccess); var sliceEvaluation = new BoundDagSliceEvaluation(slicePattern.Syntax, slicePattern.InputType, lengthTemp, startIndex: startIndex, endIndex: index, slice.IndexerAccess, slice.ReceiverPlaceholder, slice.ArgumentPlaceholder, input); @@ -75,6 +76,7 @@ private Tests MakeTestsAndBindingsForListPattern(BoundDagTemp input, BoundListPa Debug.Assert(list.ReceiverPlaceholder is not null); Debug.Assert(list.ArgumentPlaceholder is not null); + Debug.Assert(list.IndexerAccess is BoundIndexerAccess or BoundIndexOrRangePatternIndexerAccess or BoundArrayAccess); var indexEvaluation = new BoundDagIndexerEvaluation(subpattern.Syntax, subpattern.InputType, lengthTemp, index++, list.IndexerAccess, list.ReceiverPlaceholder, list.ArgumentPlaceholder, input); diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundIndexOrRangePatternIndexerAccess.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundIndexOrRangePatternIndexerAccess.cs index 1774249b84a3..4502d745e836 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundIndexOrRangePatternIndexerAccess.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundIndexOrRangePatternIndexerAccess.cs @@ -21,7 +21,6 @@ internal BoundExpression GetReceiver() { var receiver = this.IndexerAccess switch { - BoundArrayAccess { Expression: var r } => r, BoundIndexerAccess { ReceiverOpt: var r } => r, BoundCall { ReceiverOpt: var r } => r, _ => throw ExceptionUtilities.UnexpectedValue(this.IndexerAccess.Kind) diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml index 1cb701175278..d44942f741f5 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml @@ -1527,7 +1527,7 @@ @@ -1546,7 +1546,7 @@ @@ -2078,7 +2078,7 @@ diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index c526a14abdcd..55e9d661d36b 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -192,7 +192,10 @@ public VisitArgumentResult(VisitResult visitResult, Optional stateFo /// private PooledDictionary? _awaitablePlaceholdersOpt; - private PooledDictionary? _unvisitedPlaceholdersOpt; + // Note: at the moment, we have two kinds of placeholders. Those that we visit first (and store the result from and substitute later) + // and those that we visit after subsitution. + // Ideally, we should be able to align on the first kind of design, then we could remove the second kind. + private PooledDictionary? _placeholdersToUnvisitedExpressionOpt; /// /// Variables instances for each lambda or local function defined within the analyzed region. @@ -370,11 +373,11 @@ private void SetAnalyzedNullability(BoundExpression? expr, VisitResult result, b protected override void Free() { - AssertNoUnvisitedPlaceholderReplacements(); + AssertNoPlaceholderReplacements(); _nestedFunctionVariables?.Free(); _awaitablePlaceholdersOpt?.Free(); - _unvisitedPlaceholdersOpt?.Free(); + _placeholdersToUnvisitedExpressionOpt?.Free(); _methodGroupReceiverMapOpt?.Free(); _placeholderLocalsOpt?.Free(); _variables.Free(); @@ -459,22 +462,25 @@ protected override int AddVariable(VariableIdentifier identifier) return _variables.Add(identifier); } - private void AddUnvisitedPlaceholder(BoundValuePlaceholderBase placeholder, BoundExpression unvisited) + // Note: this is for placeholders to unvisited expressions + private void AddPlaceholder(BoundValuePlaceholderBase placeholder, BoundExpression unvisited) { - _unvisitedPlaceholdersOpt ??= PooledDictionary.GetInstance(); - _unvisitedPlaceholdersOpt.Add(placeholder, unvisited); + _placeholdersToUnvisitedExpressionOpt ??= PooledDictionary.GetInstance(); + _placeholdersToUnvisitedExpressionOpt.Add(placeholder, unvisited); } - private void RemoveUnvisitedPlaceholder(BoundValuePlaceholderBase placeholder) + // Note: this is for placeholders to unvisited expressions + private void RemovePlaceholder(BoundValuePlaceholderBase placeholder) { - Debug.Assert(_unvisitedPlaceholdersOpt is not null); - _unvisitedPlaceholdersOpt.Remove(placeholder); + Debug.Assert(_placeholdersToUnvisitedExpressionOpt is not null); + _placeholdersToUnvisitedExpressionOpt.Remove(placeholder); } - private bool TryReplaceUnvisitedPlaceholder(BoundValuePlaceholderBase placeholder, [NotNullWhen(true)] out BoundExpression? unvisited) + // Note: this is for placeholders to unvisited expressions + private bool TryReplacePlaceholder(BoundValuePlaceholderBase placeholder, [NotNullWhen(true)] out BoundExpression? unvisited) { - if (_unvisitedPlaceholdersOpt is not null && - _unvisitedPlaceholdersOpt.TryGetValue(placeholder, out unvisited)) + if (_placeholdersToUnvisitedExpressionOpt is not null && + _placeholdersToUnvisitedExpressionOpt.TryGetValue(placeholder, out unvisited)) { return true; } @@ -484,11 +490,15 @@ private bool TryReplaceUnvisitedPlaceholder(BoundValuePlaceholderBase placeholde } [Conditional("DEBUG")] - private void AssertNoUnvisitedPlaceholderReplacements() + private void AssertNoPlaceholderReplacements() { - if (_unvisitedPlaceholdersOpt is not null) + if (_awaitablePlaceholdersOpt is not null) { - Debug.Assert(_unvisitedPlaceholdersOpt.Count == 0); + Debug.Assert(_awaitablePlaceholdersOpt.Count == 0); + } + if (_placeholdersToUnvisitedExpressionOpt is not null) + { + Debug.Assert(_placeholdersToUnvisitedExpressionOpt.Count == 0); } } @@ -8737,9 +8747,9 @@ private TypeWithAnnotations GetDeclaredParameterResult(ParameterSymbol parameter { // The argument will be visited as part of VisitIndexOrRangeIndexerPatternValuePlaceholder (for the first argument) // to maintain proper order of evaluation - AddUnvisitedPlaceholder(node.ArgumentPlaceholders[0], node.Argument); + AddPlaceholder(node.ArgumentPlaceholders[0], node.Argument); VisitRvalue(node.IndexerAccess); - RemoveUnvisitedPlaceholder(node.ArgumentPlaceholders[0]); + RemovePlaceholder(node.ArgumentPlaceholders[0]); SetResult(node, ResultType, LvalueResultType); return null; @@ -8748,7 +8758,7 @@ private TypeWithAnnotations GetDeclaredParameterResult(ParameterSymbol parameter public override BoundNode? VisitIndexOrRangeIndexerPatternValuePlaceholder(BoundIndexOrRangeIndexerPatternValuePlaceholder node) { // We use this placeholder as trigger to visit the Argument of implicit indexer access (after its Receiver) - if (TryReplaceUnvisitedPlaceholder(node, out var unvisited)) + if (TryReplacePlaceholder(node, out var unvisited)) { VisitRvalue(unvisited); } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/IndexAndRangeTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/IndexAndRangeTests.cs index 3ff9534660d4..43ebe5b15c4d 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/IndexAndRangeTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/IndexAndRangeTests.cs @@ -106,17 +106,13 @@ readonly void M(Index i, Range r) _ = this[r]; // 3, 4 } }"; + // Note: due to our design for bound tree (LengthAccess is bound with placeholder) and CheckValue not being able + // to handle placeholders, we miss diagnostics on Length access var comp = CreateCompilationWithIndexAndRange(src); comp.VerifyDiagnostics( - // (11,13): warning CS8656: Call to non-readonly member 'S.Length.get' from a 'readonly' member results in an implicit copy of 'this'. - // _ = this[i]; // 1, 2 - Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S.Length.get", "this").WithLocation(11, 13), // (11,13): warning CS8656: Call to non-readonly member 'S.this[int].get' from a 'readonly' member results in an implicit copy of 'this'. // _ = this[i]; // 1, 2 Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S.this[int].get", "this").WithLocation(11, 13), - // (12,13): warning CS8656: Call to non-readonly member 'S.Length.get' from a 'readonly' member results in an implicit copy of 'this'. - // _ = this[r]; // 3, 4 - Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S.Length.get", "this").WithLocation(12, 13), // (12,13): warning CS8656: Call to non-readonly member 'S.Slice(int, int)' from a 'readonly' member results in an implicit copy of 'this'. // _ = this[r]; // 3, 4 Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S.Slice(int, int)", "this").WithLocation(12, 13)); @@ -166,14 +162,10 @@ readonly void M(Index i, Range r) _ = this[r]; // 2 } }"; + // Note: due to our design for bound tree (LengthAccess is bound with placeholder) and CheckValue not being able + // to handle placeholders, we miss diagnostics on Length access var comp = CreateCompilationWithIndexAndRange(src); - comp.VerifyDiagnostics( - // (11,13): warning CS8656: Call to non-readonly member 'S.Length.get' from a 'readonly' member results in an implicit copy of 'this'. - // _ = this[i]; // 1 - Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S.Length.get", "this").WithLocation(11, 13), - // (12,13): warning CS8656: Call to non-readonly member 'S.Length.get' from a 'readonly' member results in an implicit copy of 'this'. - // _ = this[r]; // 2 - Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S.Length.get", "this").WithLocation(12, 13)); + comp.VerifyDiagnostics(); } [Fact] @@ -193,14 +185,10 @@ readonly void M(Index i, Range r) _ = this[r]; // 2 } }"; + // Note: due to our design for bound tree (LengthAccess is bound with placeholder) and CheckValue not being able + // to handle placeholders, we miss diagnostics on Length access var comp = CreateCompilationWithIndexAndRange(src); - comp.VerifyDiagnostics( - // (11,13): warning CS8656: Call to non-readonly member 'S.Count.get' from a 'readonly' member results in an implicit copy of 'this'. - // _ = this[i]; // 1 - Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S.Count.get", "this").WithLocation(11, 13), - // (12,13): warning CS8656: Call to non-readonly member 'S.Count.get' from a 'readonly' member results in an implicit copy of 'this'. - // _ = this[r]; // 2 - Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S.Count.get", "this").WithLocation(12, 13)); + comp.VerifyDiagnostics(); } [Fact] From 9def40beb74d5abe078576aee3af9e1628b75b3e Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Thu, 18 Nov 2021 10:14:52 -0800 Subject: [PATCH 24/29] Improve SemanticModel handling of receiver under BoundIndexOrRangePatternIndexerAccess. --- .../CSharp/Portable/BoundTree/BoundNodes.xml | 16 +++++----- .../MemberSemanticModel.NodeMapBuilder.cs | 8 +++++ .../Generated/BoundNodes.xml.Generated.cs | 29 ++++++++----------- .../LocalRewriter.PlaceholderReplacer.cs | 9 ------ 4 files changed, 29 insertions(+), 33 deletions(-) diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml index d44942f741f5..4a4a2e1f9eec 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml @@ -2075,16 +2075,18 @@ - + - + - + @@ -2252,10 +2254,10 @@ - + - + @@ -2268,10 +2270,10 @@ - + - + diff --git a/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.NodeMapBuilder.cs b/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.NodeMapBuilder.cs index fe0f0f661c74..c55ad26d1c1d 100644 --- a/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.NodeMapBuilder.cs +++ b/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.NodeMapBuilder.cs @@ -258,6 +258,14 @@ public override BoundNode VisitQueryClause(BoundQueryClause node) return null; } + public override BoundNode VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node) + { + // We only need to visit receiver and the argument. SemanticModel isn't interested in anything else. + Visit(node.GetReceiver()); + this.Visit(node.Argument); + return null; + } + public override BoundNode VisitRangeVariable(BoundRangeVariable node) { return null; diff --git a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs index a50454b7af09..c1f1041348af 100644 --- a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs @@ -10390,8 +10390,7 @@ internal abstract partial class BoundTreeWalker : BoundTreeVisitor public override BoundNode? VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node) { this.Visit(node.Argument); - this.Visit(node.ReceiverPlaceholder); - this.VisitList(node.ArgumentPlaceholders); + this.Visit(node.IndexerAccess); return null; } public override BoundNode? VisitDynamicIndexerAccess(BoundDynamicIndexerAccess node) @@ -10469,16 +10468,12 @@ internal abstract partial class BoundTreeWalker : BoundTreeVisitor public override BoundNode? VisitListPattern(BoundListPattern node) { this.VisitList(node.Subpatterns); - this.Visit(node.ReceiverPlaceholder); - this.Visit(node.ArgumentPlaceholder); this.Visit(node.VariableAccess); return null; } public override BoundNode? VisitSlicePattern(BoundSlicePattern node) { this.Visit(node.Pattern); - this.Visit(node.ReceiverPlaceholder); - this.Visit(node.ArgumentPlaceholder); return null; } public override BoundNode? VisitITuplePattern(BoundITuplePattern node) @@ -11650,9 +11645,9 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor { BoundExpression argument = (BoundExpression)this.Visit(node.Argument); BoundExpression lengthOrCountAccess = node.LengthOrCountAccess; - BoundIndexOrRangeIndexerPatternReceiverPlaceholder receiverPlaceholder = (BoundIndexOrRangeIndexerPatternReceiverPlaceholder)this.Visit(node.ReceiverPlaceholder); - BoundExpression indexerAccess = node.IndexerAccess; - ImmutableArray argumentPlaceholders = this.VisitList(node.ArgumentPlaceholders); + BoundIndexOrRangeIndexerPatternReceiverPlaceholder receiverPlaceholder = node.ReceiverPlaceholder; + BoundExpression indexerAccess = (BoundExpression)this.Visit(node.IndexerAccess); + ImmutableArray argumentPlaceholders = node.ArgumentPlaceholders; TypeSymbol? type = this.VisitType(node.Type); return node.Update(argument, lengthOrCountAccess, receiverPlaceholder, indexerAccess, argumentPlaceholders, type); } @@ -11769,8 +11764,8 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor ImmutableArray subpatterns = this.VisitList(node.Subpatterns); BoundExpression? lengthAccess = node.LengthAccess; BoundExpression? indexerAccess = node.IndexerAccess; - BoundListPatternReceiverPlaceholder? receiverPlaceholder = (BoundListPatternReceiverPlaceholder?)this.Visit(node.ReceiverPlaceholder); - BoundListPatternIndexPlaceholder? argumentPlaceholder = (BoundListPatternIndexPlaceholder?)this.Visit(node.ArgumentPlaceholder); + BoundListPatternReceiverPlaceholder? receiverPlaceholder = node.ReceiverPlaceholder; + BoundListPatternIndexPlaceholder? argumentPlaceholder = node.ArgumentPlaceholder; BoundExpression? variableAccess = (BoundExpression?)this.Visit(node.VariableAccess); TypeSymbol? inputType = this.VisitType(node.InputType); TypeSymbol? narrowedType = this.VisitType(node.NarrowedType); @@ -11780,8 +11775,8 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor { BoundPattern? pattern = (BoundPattern?)this.Visit(node.Pattern); BoundExpression? indexerAccess = node.IndexerAccess; - BoundSlicePatternReceiverPlaceholder? receiverPlaceholder = (BoundSlicePatternReceiverPlaceholder?)this.Visit(node.ReceiverPlaceholder); - BoundSlicePatternRangePlaceholder? argumentPlaceholder = (BoundSlicePatternRangePlaceholder?)this.Visit(node.ArgumentPlaceholder); + BoundSlicePatternReceiverPlaceholder? receiverPlaceholder = node.ReceiverPlaceholder; + BoundSlicePatternRangePlaceholder? argumentPlaceholder = node.ArgumentPlaceholder; TypeSymbol? inputType = this.VisitType(node.InputType); TypeSymbol? narrowedType = this.VisitType(node.NarrowedType); return node.Update(pattern, indexerAccess, receiverPlaceholder, argumentPlaceholder, inputType, narrowedType); @@ -14243,8 +14238,8 @@ public NullabilityRewriter(ImmutableDictionary subpatterns = this.VisitList(node.Subpatterns); BoundExpression? lengthAccess = node.LengthAccess; BoundExpression? indexerAccess = node.IndexerAccess; - BoundListPatternReceiverPlaceholder? receiverPlaceholder = (BoundListPatternReceiverPlaceholder?)this.Visit(node.ReceiverPlaceholder); - BoundListPatternIndexPlaceholder? argumentPlaceholder = (BoundListPatternIndexPlaceholder?)this.Visit(node.ArgumentPlaceholder); + BoundListPatternReceiverPlaceholder? receiverPlaceholder = node.ReceiverPlaceholder; + BoundListPatternIndexPlaceholder? argumentPlaceholder = node.ArgumentPlaceholder; BoundExpression? variableAccess = (BoundExpression?)this.Visit(node.VariableAccess); return node.Update(subpatterns, node.HasSlice, lengthAccess, indexerAccess, receiverPlaceholder, argumentPlaceholder, variable, variableAccess, inputType, narrowedType); } @@ -14255,8 +14250,8 @@ public NullabilityRewriter(ImmutableDictionary Date: Thu, 18 Nov 2021 15:15:49 -0800 Subject: [PATCH 25/29] Check implicit copy directly --- .../Portable/Binder/Binder_Expressions.cs | 14 ++++++---- .../CSharp/Portable/Binder/Binder_Patterns.cs | 2 +- .../Semantic/Semantics/IndexAndRangeTests.cs | 28 +++++++++++++------ .../PatternMatchingTests_ListPatterns.cs | 16 +++++++---- 4 files changed, 40 insertions(+), 20 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index 6f98922bdcc9..49b05e816468 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -8157,7 +8157,7 @@ private bool TryBindIndexOrRangeImplicitIndexerParts( // 2. For Index: Has an accessible indexer with a single int parameter // For Range: Has an accessible Slice method that takes two int parameters - if (TryBindLengthOrCount(syntax, receiverPlaceholder, out lengthOrCountAccess, diagnostics) && + if (TryBindLengthOrCount(syntax, receiverPlaceholder, receiver, out lengthOrCountAccess, diagnostics) && tryBindUnderlyingIndexerOrSliceAccess(syntax, receiver, argIsIndex, out indexerOrSliceAccess, out argumentPlaceholders, diagnostics)) { return true; @@ -8306,19 +8306,23 @@ void makeCall(SyntaxNode syntax, BoundExpression receiver, MethodSymbol method, private bool TryBindLengthOrCount( SyntaxNode syntax, - BoundExpression receiver, + BoundExpression receiverPlaceholder, + BoundExpression? receiver, out BoundExpression lengthOrCountAccess, BindingDiagnosticBag diagnostics) { var lookupResult = LookupResult.GetInstance(); - Debug.Assert(receiver.Type is not null); - if (TryLookupLengthOrCount(syntax, receiver.Type, lookupResult, out var lengthOrCountProperty, diagnostics)) + Debug.Assert(receiverPlaceholder.Type is not null); + if (TryLookupLengthOrCount(syntax, receiverPlaceholder.Type, lookupResult, out var lengthOrCountProperty, diagnostics)) { diagnostics.ReportUseSite(lengthOrCountProperty, syntax); - lengthOrCountAccess = BindPropertyAccess(syntax, receiver, lengthOrCountProperty, diagnostics, lookupResult.Kind, hasErrors: false).MakeCompilerGenerated(); + lengthOrCountAccess = BindPropertyAccess(syntax, receiverPlaceholder, lengthOrCountProperty, diagnostics, lookupResult.Kind, hasErrors: false).MakeCompilerGenerated(); lengthOrCountAccess = CheckValue(lengthOrCountAccess, BindValueKind.RValue, diagnostics); + // CheckValue does not handle placeholders well yet, so we're doing some extra check for `this` receiver + CheckImplicitThisCopyInReadOnlyMember(receiver, lengthOrCountProperty.GetOwnOrInheritedGetMethod(), diagnostics); + lookupResult.Free(); return true; } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs index b2b9acce62c6..a7cf7a2bceb6 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs @@ -353,7 +353,7 @@ private bool BindLengthAndIndexerForListPattern(SyntaxNode node, TypeSymbol inpu } else { - hasErrors |= !TryBindLengthOrCount(node, receiverPlaceholder, out lengthAccess, diagnostics); + hasErrors |= !TryBindLengthOrCount(node, receiverPlaceholder, receiver: null, out lengthAccess, diagnostics); } var analyzedArguments = AnalyzedArguments.GetInstance(); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/IndexAndRangeTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/IndexAndRangeTests.cs index 43ebe5b15c4d..3ff9534660d4 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/IndexAndRangeTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/IndexAndRangeTests.cs @@ -106,13 +106,17 @@ readonly void M(Index i, Range r) _ = this[r]; // 3, 4 } }"; - // Note: due to our design for bound tree (LengthAccess is bound with placeholder) and CheckValue not being able - // to handle placeholders, we miss diagnostics on Length access var comp = CreateCompilationWithIndexAndRange(src); comp.VerifyDiagnostics( + // (11,13): warning CS8656: Call to non-readonly member 'S.Length.get' from a 'readonly' member results in an implicit copy of 'this'. + // _ = this[i]; // 1, 2 + Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S.Length.get", "this").WithLocation(11, 13), // (11,13): warning CS8656: Call to non-readonly member 'S.this[int].get' from a 'readonly' member results in an implicit copy of 'this'. // _ = this[i]; // 1, 2 Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S.this[int].get", "this").WithLocation(11, 13), + // (12,13): warning CS8656: Call to non-readonly member 'S.Length.get' from a 'readonly' member results in an implicit copy of 'this'. + // _ = this[r]; // 3, 4 + Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S.Length.get", "this").WithLocation(12, 13), // (12,13): warning CS8656: Call to non-readonly member 'S.Slice(int, int)' from a 'readonly' member results in an implicit copy of 'this'. // _ = this[r]; // 3, 4 Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S.Slice(int, int)", "this").WithLocation(12, 13)); @@ -162,10 +166,14 @@ readonly void M(Index i, Range r) _ = this[r]; // 2 } }"; - // Note: due to our design for bound tree (LengthAccess is bound with placeholder) and CheckValue not being able - // to handle placeholders, we miss diagnostics on Length access var comp = CreateCompilationWithIndexAndRange(src); - comp.VerifyDiagnostics(); + comp.VerifyDiagnostics( + // (11,13): warning CS8656: Call to non-readonly member 'S.Length.get' from a 'readonly' member results in an implicit copy of 'this'. + // _ = this[i]; // 1 + Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S.Length.get", "this").WithLocation(11, 13), + // (12,13): warning CS8656: Call to non-readonly member 'S.Length.get' from a 'readonly' member results in an implicit copy of 'this'. + // _ = this[r]; // 2 + Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S.Length.get", "this").WithLocation(12, 13)); } [Fact] @@ -185,10 +193,14 @@ readonly void M(Index i, Range r) _ = this[r]; // 2 } }"; - // Note: due to our design for bound tree (LengthAccess is bound with placeholder) and CheckValue not being able - // to handle placeholders, we miss diagnostics on Length access var comp = CreateCompilationWithIndexAndRange(src); - comp.VerifyDiagnostics(); + comp.VerifyDiagnostics( + // (11,13): warning CS8656: Call to non-readonly member 'S.Count.get' from a 'readonly' member results in an implicit copy of 'this'. + // _ = this[i]; // 1 + Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S.Count.get", "this").WithLocation(11, 13), + // (12,13): warning CS8656: Call to non-readonly member 'S.Count.get' from a 'readonly' member results in an implicit copy of 'this'. + // _ = this[r]; // 2 + Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S.Count.get", "this").WithLocation(12, 13)); } [Fact] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs index 557b3ba27dee..c91986d609bf 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs @@ -3043,22 +3043,26 @@ struct S readonly void M(Index i, Range r) { - _ = this[i]; // 1 - _ = this[r]; // 2 + _ = this[i]; // 1, 2 + _ = this[r]; // 3, 4 _ = this is [1]; _ = this is [2, ..var rest]; } }"; - // Note: we're unable to report this warning on binding Length properties (as part of implicit indexers) - // because of our use of placeholders. var comp = CreateCompilationWithIndexAndRange(src); comp.VerifyDiagnostics( + // (11,13): warning CS8656: Call to non-readonly member 'S.Length.get' from a 'readonly' member results in an implicit copy of 'this'. + // _ = this[i]; // 1, 2 + Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S.Length.get", "this").WithLocation(11, 13), // (11,13): warning CS8656: Call to non-readonly member 'S.this[int].get' from a 'readonly' member results in an implicit copy of 'this'. - // _ = this[i]; // 1 + // _ = this[i]; // 1, 2 Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S.this[int].get", "this").WithLocation(11, 13), + // (12,13): warning CS8656: Call to non-readonly member 'S.Length.get' from a 'readonly' member results in an implicit copy of 'this'. + // _ = this[r]; // 3, 4 + Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S.Length.get", "this").WithLocation(12, 13), // (12,13): warning CS8656: Call to non-readonly member 'S.Slice(int, int)' from a 'readonly' member results in an implicit copy of 'this'. - // _ = this[r]; // 2 + // _ = this[r]; // 3, 4 Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S.Slice(int, int)", "this").WithLocation(12, 13) ); } From dea39c59861ccbd2d9266bfbf7d0b4c5c81b8b91 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Fri, 19 Nov 2021 09:18:18 -0800 Subject: [PATCH 26/29] Address feedback (6) --- .../Portable/Binder/Binder_Expressions.cs | 28 ++--- .../CSharp/Portable/Binder/Binder_Patterns.cs | 14 ++- .../Portable/Binder/Binder_Statements.cs | 1 + .../Portable/BoundTree/BoundDagEvaluation.cs | 9 +- .../Portable/FlowAnalysis/NullableWalker.cs | 13 +- .../Test/Emit/CodeGen/IndexAndRangeTests.cs | 3 + .../IOperationTests_IIsPatternExpression.cs | 79 +++++++++++- .../PatternMatchingTests_ListPatterns.cs | 112 +++++++++++++++++- 8 files changed, 232 insertions(+), 27 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index 49b05e816468..914c8584d6a4 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -7826,7 +7826,8 @@ private BoundExpression BindIndexerAccess(SyntaxNode node, BoundExpression expr, private BoundExpression BindIndexedPropertyAccess(BoundPropertyGroup propertyGroup, bool mustHaveAllOptionalParameters, BindingDiagnosticBag diagnostics) { var syntax = propertyGroup.Syntax; - var receiverOpt = propertyGroup.ReceiverOpt; + var receiver = propertyGroup.ReceiverOpt; + Debug.Assert(receiver is not null); var properties = propertyGroup.Properties; if (properties.All(s_isIndexedPropertyWithNonOptionalArguments)) @@ -7837,7 +7838,7 @@ private BoundExpression BindIndexedPropertyAccess(BoundPropertyGroup propertyGro properties[0].ToDisplayString(s_propertyGroupFormat)); return BoundIndexerAccess.ErrorAccess( syntax, - receiverOpt, + receiver, CreateErrorPropertySymbol(properties), ImmutableArray.Empty, default(ImmutableArray), @@ -7846,8 +7847,7 @@ private BoundExpression BindIndexedPropertyAccess(BoundPropertyGroup propertyGro } var arguments = AnalyzedArguments.GetInstance(); - Debug.Assert(receiverOpt is not null); - var result = BindIndexedPropertyAccess(syntax, receiverOpt, properties, arguments, diagnostics); + var result = BindIndexedPropertyAccess(syntax, receiver, properties, arguments, diagnostics); arguments.Free(); return result; } @@ -7946,11 +7946,11 @@ private BoundExpression BindIndexerOrIndexedPropertyAccess( ImmutableArray candidates = propertyGroup.ToImmutable(); if (TryBindIndexOrRangeImplicitIndexer( - syntax, - receiver, - analyzedArguments, - diagnostics, - out var implicitIndexerAccess)) + syntax, + receiver, + analyzedArguments, + diagnostics, + out var implicitIndexerAccess)) { return implicitIndexerAccess; } @@ -8087,7 +8087,7 @@ private bool TryBindIndexOrRangeImplicitIndexer( var receiverValEscape = GetValEscape(receiver, LocalScopeDepth); var receiverPlaceholder = new BoundIndexOrRangeIndexerPatternReceiverPlaceholder(receiver.Syntax, receiverValEscape, receiver.Type) { WasCompilerGenerated = true }; if (!TryBindIndexOrRangeImplicitIndexerParts(syntax, receiverPlaceholder, receiver, argIsIndex: argIsIndex, - out var lengthOrCountAccess, out var indexerOrSliceAccess, out var argumentPlaceholders, diagnostics)) + out var lengthOrCountAccess, out var indexerOrSliceAccess, out var argumentPlaceholders, diagnostics)) { return false; } @@ -8207,7 +8207,7 @@ bool tryBindUnderlyingIndexerOrSliceAccess( candidate is PropertySymbol property && IsAccessible(property, syntax, diagnostics) && property.OriginalDefinition is { ParameterCount: 1 } original && - original.Parameters[0] is { Type: { SpecialType: SpecialType.System_Int32 }, RefKind: RefKind.None }) + original.Parameters[0] is { Type.SpecialType: SpecialType.System_Int32, RefKind: RefKind.None }) { var intPlaceholder = new BoundIndexOrRangeIndexerPatternValuePlaceholder(syntax, Compilation.GetSpecialType(SpecialType.System_Int32)) { WasCompilerGenerated = true }; argumentPlaceholders = ImmutableArray.Create(intPlaceholder); @@ -8229,7 +8229,7 @@ candidate is PropertySymbol property && { Debug.Assert(!argIsIndex); // Look for Substring - var substring = (MethodSymbol)Compilation.GetSpecialTypeMember(SpecialMember.System_String__Substring); + var substring = (MethodSymbol)GetSpecialTypeMember(SpecialMember.System_String__Substring, diagnostics, syntax); if (substring is object) { makeCall(syntax, receiver, substring, out indexerOrSliceAccess, out argumentPlaceholders); @@ -8264,8 +8264,8 @@ candidate is MethodSymbol method && method.OriginalDefinition is var original && !original.ReturnsVoid && original.ParameterCount == 2 && - original.Parameters[0] is { Type: { SpecialType: SpecialType.System_Int32 }, RefKind: RefKind.None } && - original.Parameters[1] is { Type: { SpecialType: SpecialType.System_Int32 }, RefKind: RefKind.None }) + original.Parameters[0] is { Type.SpecialType: SpecialType.System_Int32, RefKind: RefKind.None } && + original.Parameters[1] is { Type.SpecialType: SpecialType.System_Int32, RefKind: RefKind.None }) { makeCall(syntax, receiver, method, out indexerOrSliceAccess, out argumentPlaceholders); lookupResult.Free(); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs index a7cf7a2bceb6..b497a748149c 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs @@ -226,6 +226,11 @@ private BoundPattern BindSlicePattern( Debug.Assert(indexerAccess is BoundIndexerAccess or BoundIndexOrRangePatternIndexerAccess or BoundArrayAccess or BoundBadExpression or BoundDynamicIndexerAccess); analyzedArguments.Free(); + if (!systemRangeType.HasUseSiteError) + { + _ = GetWellKnownTypeMember(WellKnownMember.System_Range__ctor, diagnostics, syntax: node); + } + Debug.Assert(indexerAccess.Type is not null); sliceType = indexerAccess.Type; } @@ -233,6 +238,7 @@ private BoundPattern BindSlicePattern( pattern = BindPattern(node.Pattern, sliceType, GetValEscape(sliceType, inputValEscape), permitDesignations, hasErrors, diagnostics); } + Debug.Assert(GetIndexerOrImplicitIndexerSymbol(indexerAccess) is var _); return new BoundSlicePattern(node, pattern, indexerAccess, receiverPlaceholder, argumentPlaceholder, inputType: inputType, narrowedType: inputType, hasErrors); } @@ -295,7 +301,7 @@ private BoundListPattern BindListPattern( } else { - hasErrors = !BindLengthAndIndexerForListPattern(node, narrowedType, inputValEscape, diagnostics, out indexerAccess, out lengthAccess, out receiverPlaceholder, out argumentPlaceholder); + hasErrors |= !BindLengthAndIndexerForListPattern(node, narrowedType, inputValEscape, diagnostics, out indexerAccess, out lengthAccess, out receiverPlaceholder, out argumentPlaceholder); Debug.Assert(indexerAccess!.Type is not null); elementType = indexerAccess.Type; @@ -311,6 +317,7 @@ private BoundListPattern BindListPattern( inputValEscape, permitDesignations, typeSyntax: null, diagnostics, ref hasErrors, out Symbol? variableSymbol, out BoundExpression? variableAccess); + Debug.Assert(GetIndexerOrImplicitIndexerSymbol(indexerAccess) is var _); return new BoundListPattern( syntax: node, subpatterns: subpatterns, hasSlice: sawSlice, lengthAccess: lengthAccess, indexerAccess: indexerAccess, receiverPlaceholder, argumentPlaceholder, variable: variableSymbol, @@ -332,13 +339,14 @@ private bool IsCountableAndIndexable(SyntaxNode node, TypeSymbol inputType, out private bool BindLengthAndIndexerForListPattern(SyntaxNode node, TypeSymbol inputType, uint inputValEscape, BindingDiagnosticBag diagnostics, out BoundExpression indexerAccess, out BoundExpression lengthAccess, out BoundListPatternReceiverPlaceholder? receiverPlaceholder, out BoundListPatternIndexPlaceholder argumentPlaceholder) { + bool hasErrors = false; if (inputType.IsDynamic()) { + hasErrors |= true; Error(diagnostics, ErrorCode.ERR_UnsupportedTypeForListPattern, node, inputType); } receiverPlaceholder = new BoundListPatternReceiverPlaceholder(node, GetValEscape(inputType, inputValEscape), inputType) { WasCompilerGenerated = true }; - bool hasErrors = false; if (inputType.IsSZArray()) { hasErrors |= !TryGetSpecialTypeMember(Compilation, SpecialMember.System_Array__Length, node, diagnostics, out PropertySymbol lengthProperty); @@ -372,6 +380,8 @@ private bool BindLengthAndIndexerForListPattern(SyntaxNode node, TypeSymbol inpu // during lowering, but it's simpler to always require them to prevent // the user from getting surprising errors when optimizations fail _ = GetWellKnownTypeMember(WellKnownMember.System_Index__op_Implicit_FromInt32, diagnostics, syntax: node); + + _ = GetWellKnownTypeMember(WellKnownMember.System_Index__ctor, diagnostics, syntax: node); } return !hasErrors && !lengthAccess.HasErrors && !indexerAccess.HasErrors; diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index 9380c2dd9922..a9eb814de85a 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -1612,6 +1612,7 @@ internal static PropertySymbol GetPropertySymbol(BoundExpression expr, out Bound BoundIndexOrRangePatternIndexerAccess { IndexerAccess: BoundIndexerAccess indexerAccess } => indexerAccess.Indexer, // array[int] BoundArrayAccess => null, + BoundDynamicIndexerAccess => null, BoundBadExpression => null, _ => throw ExceptionUtilities.UnexpectedValue(e.Kind) }; diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundDagEvaluation.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundDagEvaluation.cs index 5aec0f98c779..a20ecdb9d972 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundDagEvaluation.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundDagEvaluation.cs @@ -32,18 +32,21 @@ private Symbol? Symbol { get { - return this switch + var result = this switch { BoundDagFieldEvaluation e => e.Field.CorrespondingTupleField ?? e.Field, BoundDagPropertyEvaluation e => e.Property, BoundDagTypeEvaluation e => e.Type, BoundDagDeconstructEvaluation e => e.DeconstructMethod, BoundDagIndexEvaluation e => e.Property, - BoundDagSliceEvaluation e => Binder.GetIndexerOrImplicitIndexerSymbol(e.IndexerAccess), - BoundDagIndexerEvaluation e => Binder.GetIndexerOrImplicitIndexerSymbol(e.IndexerAccess), + 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), BoundDagAssignmentEvaluation => null, _ => throw ExceptionUtilities.UnexpectedValue(this.Kind) }; + + Debug.Assert(result is not null || this is BoundDagAssignmentEvaluation); + return result; } } diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index 55e9d661d36b..a81225762ab0 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -195,6 +195,7 @@ public VisitArgumentResult(VisitResult visitResult, Optional stateFo // Note: at the moment, we have two kinds of placeholders. Those that we visit first (and store the result from and substitute later) // and those that we visit after subsitution. // Ideally, we should be able to align on the first kind of design, then we could remove the second kind. + // Removing this map is tracked as part of https://github.com/dotnet/roslyn/issues/57855 private PooledDictionary? _placeholdersToUnvisitedExpressionOpt; /// @@ -462,21 +463,27 @@ protected override int AddVariable(VariableIdentifier identifier) return _variables.Add(identifier); } - // Note: this is for placeholders to unvisited expressions + /// + /// This is for placeholders to unvisited expressions + /// private void AddPlaceholder(BoundValuePlaceholderBase placeholder, BoundExpression unvisited) { _placeholdersToUnvisitedExpressionOpt ??= PooledDictionary.GetInstance(); _placeholdersToUnvisitedExpressionOpt.Add(placeholder, unvisited); } - // Note: this is for placeholders to unvisited expressions + /// + /// This is for placeholders to unvisited expressions + /// private void RemovePlaceholder(BoundValuePlaceholderBase placeholder) { Debug.Assert(_placeholdersToUnvisitedExpressionOpt is not null); _placeholdersToUnvisitedExpressionOpt.Remove(placeholder); } - // Note: this is for placeholders to unvisited expressions + /// + /// This is for placeholders to unvisited expressions + /// private bool TryReplacePlaceholder(BoundValuePlaceholderBase placeholder, [NotNullWhen(true)] out BoundExpression? unvisited) { if (_placeholdersToUnvisitedExpressionOpt is not null && diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/IndexAndRangeTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/IndexAndRangeTests.cs index d9b7370fb709..d0e9bd5c84b3 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/IndexAndRangeTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/IndexAndRangeTests.cs @@ -342,6 +342,9 @@ readonly void M() "; var comp = CreateCompilationWithIndexAndRangeAndSpan(src); comp.VerifyDiagnostics( + // (24,9): warning CS8656: Call to non-readonly member 'S.Length.get' from a 'readonly' member results in an implicit copy of 'this'. + // this[^1] += 5; + Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S.Length.get", "this").WithLocation(24, 9), // (24,9): error CS1604: Cannot assign to 'this[^1]' because it is read-only // this[^1] += 5; Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "this[^1]").WithArguments("this[^1]").WithLocation(24, 9) diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs index e296ce8b4406..79b3158d0aff 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs @@ -2151,6 +2151,84 @@ void M(int[] o) VerifyOperationTreeAndDiagnosticsForTest(comp, expectedOperationTree, expectedDiagnostics); } + [CompilerTrait(CompilerFeature.IOperation)] + [Fact] + public void TestIsPatternExpression_ListPatterns_Array_Nullability() + { + string source = @" +#nullable enable +class X +{ + void M(string s) + { + s = null; + var a = new[] { s }; + _ = /**/a is [var item]/**/; + } +} +"; + string expectedOperationTree = @" +IIsPatternOperation (OperationKind.IsPattern, Type: System.Boolean) (Syntax: 'a is [var item]') + Value: + ILocalReferenceOperation: a (OperationKind.LocalReference, Type: System.String?[]) (Syntax: 'a') + Pattern: + IListPatternOperation (OperationKind.ListPattern, Type: null) (Syntax: '[var item]') (InputType: System.String[], NarrowedType: System.String[], DeclaredSymbol: null, LengthSymbol: System.Int32 System.Array.Length { get; }, IndexerSymbol: null) + Patterns (1): + IDeclarationPatternOperation (OperationKind.DeclarationPattern, Type: null) (Syntax: 'var item') (InputType: System.String, NarrowedType: System.String, DeclaredSymbol: System.String? item, MatchesNull: True) +"; + var expectedDiagnostics = new[] + { + // (7,13): warning CS8600: Converting null literal or possible null value to non-nullable type. + // s = null; + Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null").WithLocation(7, 13) + }; + + var comp = CreateCompilationWithIndexAndRange(new[] { source, TestSources.GetSubArray }); + VerifyOperationTreeAndDiagnosticsForTest(comp, expectedOperationTree, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation)] + [Fact, WorkItem(57884, "https://github.com/dotnet/roslyn/issues/57884")] + public void TestIsPatternExpression_ListPatterns_Collection_Nullability() + { + string source = @" +#nullable enable +class X +{ + static X Create(U u) => throw null!; + void M(string s) + { + s = null; + var a = Create(s); + _ = /**/a is [var item]/**/; + } + public int Length => throw null!; + public char this[int i] => throw null!; +} +"; + // The LengthSymbol and IndexerSymbol should reflect updated nullability: + // LengthSymbol: System.Int32 X.Length { get; }, IndexerSymbol: System.Char X.this[System.Int32 i] { get; }) + // Tracked by https://github.com/dotnet/roslyn/issues/57884 + string expectedOperationTree = @" +IIsPatternOperation (OperationKind.IsPattern, Type: System.Boolean) (Syntax: 'a is [var item]') + Value: + ILocalReferenceOperation: a (OperationKind.LocalReference, Type: X) (Syntax: 'a') + Pattern: + IListPatternOperation (OperationKind.ListPattern, Type: null) (Syntax: '[var item]') (InputType: X, NarrowedType: X, DeclaredSymbol: null, LengthSymbol: System.Int32 X.Length { get; }, IndexerSymbol: System.Char X.this[System.Int32 i] { get; }) + Patterns (1): + IDeclarationPatternOperation (OperationKind.DeclarationPattern, Type: null) (Syntax: 'var item') (InputType: System.Char, NarrowedType: System.Char, DeclaredSymbol: System.Char item, MatchesNull: True) +"; + var expectedDiagnostics = new[] + { + // (8,13): warning CS8600: Converting null literal or possible null value to non-nullable type. + // s = null; + Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null").WithLocation(8, 13) + }; + + var comp = CreateCompilationWithIndexAndRange(new[] { source, TestSources.GetSubArray }); + VerifyOperationTreeAndDiagnosticsForTest(comp, expectedOperationTree, expectedDiagnostics); + } + [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void TestIsPatternExpression_ListPatterns_Span_01() @@ -2455,4 +2533,3 @@ void M(int[] o) } } } - diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs index c91986d609bf..28aacbe8efbc 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs @@ -793,6 +793,104 @@ public void M(int[] a) Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[.._]").WithArguments("System.Array", "Length").WithLocation(7, 18)); } + [Fact] + public void ListPattern_MissingMembers_IndexCtor() + { + var source = @" +class X +{ + public int Length => throw null; + public int this[System.Index i] => throw null; + public int this[System.Range r] => throw null; + + public void M() + { + _ = this is [0]; + _ = this is [.., 0]; + _ = this[^1]; + _ = this[..]; + } +} +"; + var compilation = CreateCompilationWithIndexAndRange(source); + compilation.MakeMemberMissing(WellKnownMember.System_Index__ctor); + compilation.VerifyEmitDiagnostics( + // (10,21): error CS0656: Missing compiler required member 'System.Index..ctor' + // _ = this is [0]; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[0]").WithArguments("System.Index", ".ctor").WithLocation(10, 21), + // (11,21): error CS0656: Missing compiler required member 'System.Index..ctor' + // _ = this is [.., 0]; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[.., 0]").WithArguments("System.Index", ".ctor").WithLocation(11, 21), + // (12,18): error CS0656: Missing compiler required member 'System.Index..ctor' + // _ = this[^1]; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "^1").WithArguments("System.Index", ".ctor").WithLocation(12, 18) + ); + } + + [Fact] + public void ListPattern_MissingMembers_RangeCtor() + { + var source = @" +class X +{ + public int Length => throw null; + public int this[System.Index i] => throw null; + public int this[System.Range r] => throw null; + + public void M() + { + _ = this is [0]; + + _ = this is [.._]; + _ = this is [0, .._]; + _ = this is [.._, 0]; + _ = this is [0, .._, 0]; + + _ = this[^1]; + + _ = this[..]; + _ = this[1..]; + _ = this[..^1]; + _ = this[1..^1]; + } +} +"; + var compilation = CreateCompilationWithIndexAndRange(source); + compilation.MakeMemberMissing(WellKnownMember.System_Range__ctor); + compilation.MakeMemberMissing(WellKnownMember.System_Range__get_All); + compilation.MakeMemberMissing(WellKnownMember.System_Range__StartAt); + compilation.MakeMemberMissing(WellKnownMember.System_Range__EndAt); + // Note: slice patterns always use range expressions with start and end. + // But range syntax binds differently depending whether start/end are there and depending on what members are available. + + compilation.VerifyEmitDiagnostics( + // (12,22): error CS0656: Missing compiler required member 'System.Range..ctor' + // _ = this is [.._]; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, ".._").WithArguments("System.Range", ".ctor").WithLocation(12, 22), + // (13,25): error CS0656: Missing compiler required member 'System.Range..ctor' + // _ = this is [0, .._]; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, ".._").WithArguments("System.Range", ".ctor").WithLocation(13, 25), + // (14,22): error CS0656: Missing compiler required member 'System.Range..ctor' + // _ = this is [.._, 0]; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, ".._").WithArguments("System.Range", ".ctor").WithLocation(14, 22), + // (15,25): error CS0656: Missing compiler required member 'System.Range..ctor' + // _ = this is [0, .._, 0]; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, ".._").WithArguments("System.Range", ".ctor").WithLocation(15, 25), + // (19,18): error CS0656: Missing compiler required member 'System.Range..ctor' + // _ = this[..]; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "..").WithArguments("System.Range", ".ctor").WithLocation(19, 18), + // (20,18): error CS0656: Missing compiler required member 'System.Range..ctor' + // _ = this[1..]; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "1..").WithArguments("System.Range", ".ctor").WithLocation(20, 18), + // (21,18): error CS0656: Missing compiler required member 'System.Range..ctor' + // _ = this[..^1]; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "..^1").WithArguments("System.Range", ".ctor").WithLocation(21, 18), + // (22,18): error CS0656: Missing compiler required member 'System.Range..ctor' + // _ = this[1..^1]; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "1..^1").WithArguments("System.Range", ".ctor").WithLocation(22, 18) + ); + } + [Fact] public void ListPattern_MissingMembers_Substring() { @@ -806,12 +904,18 @@ public void M(string s) } } "; - var compilation = CreateCompilationWithIndexAndRange(source, parseOptions: TestOptions.RegularWithListPatterns); + var compilation = CreateCompilationWithIndexAndRange(source); compilation.MakeMemberMissing(SpecialMember.System_String__Substring); compilation.VerifyEmitDiagnostics( + // (6,19): error CS0656: Missing compiler required member 'System.String.Substring' + // _ = s is [.. var slice]; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, ".. var slice").WithArguments("System.String", "Substring").WithLocation(6, 19), // (6,19): error CS1503: Argument 1: cannot convert from 'System.Range' to 'int' // _ = s is [.. var slice]; Diagnostic(ErrorCode.ERR_BadArgType, ".. var slice").WithArguments("1", "System.Range", "int").WithLocation(6, 19), + // (7,13): error CS0656: Missing compiler required member 'System.String.Substring' + // _ = s[..]; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "s[..]").WithArguments("System.String", "Substring").WithLocation(7, 13), // (7,15): error CS1503: Argument 1: cannot convert from 'System.Range' to 'int' // _ = s[..]; Diagnostic(ErrorCode.ERR_BadArgType, "..").WithArguments("1", "System.Range", "int").WithLocation(7, 15) @@ -3044,7 +3148,7 @@ struct S readonly void M(Index i, Range r) { _ = this[i]; // 1, 2 - _ = this[r]; // 3, 4 + _ = this[r]; // 3, 4 _ = this is [1]; _ = this is [2, ..var rest]; @@ -3059,10 +3163,10 @@ readonly void M(Index i, Range r) // _ = this[i]; // 1, 2 Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S.this[int].get", "this").WithLocation(11, 13), // (12,13): warning CS8656: Call to non-readonly member 'S.Length.get' from a 'readonly' member results in an implicit copy of 'this'. - // _ = this[r]; // 3, 4 + // _ = this[r]; // 3, 4 Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S.Length.get", "this").WithLocation(12, 13), // (12,13): warning CS8656: Call to non-readonly member 'S.Slice(int, int)' from a 'readonly' member results in an implicit copy of 'this'. - // _ = this[r]; // 3, 4 + // _ = this[r]; // 3, 4 Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S.Slice(int, int)", "this").WithLocation(12, 13) ); } From 174d3f2bdd2bef8c229e745e0f89393884dabecd Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Fri, 19 Nov 2021 13:34:27 -0800 Subject: [PATCH 27/29] Fix 2 test baselines with missing members --- .../PatternMatchingTests_ListPatterns.cs | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs index 28aacbe8efbc..22e45cee24ea 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs @@ -1401,15 +1401,24 @@ public static void Main() var csCompilation = CreateCompilation(csSource, parseOptions: TestOptions.RegularWithListPatterns, references: new[] { vbCompilation.EmitToImageReference() }); // PROTOTYPE(list-patterns) Unsupported because the lookup fails not that the indexer is static csCompilation.VerifyEmitDiagnostics( - // (6,28): error CS0021: Cannot apply indexing with [] to an expression of type 'Test1' - // _ = new Test1() is [0]; - Diagnostic(ErrorCode.ERR_BadIndexLHS, "[0]").WithArguments("Test1").WithLocation(6, 28), - // (6,28): error CS0656: Missing compiler required member 'System.Index.op_Implicit' - // _ = new Test1() is [0]; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[0]").WithArguments("System.Index", "op_Implicit").WithLocation(6, 28), - // (7,13): error CS0021: Cannot apply indexing with [] to an expression of type 'Test1' - // _ = new Test1()[0]; - Diagnostic(ErrorCode.ERR_BadIndexLHS, "new Test1()[0]").WithArguments("Test1").WithLocation(7, 13)); + // (20,24): error CS0656: Missing compiler required member 'System.Index.op_Implicit' + // _ = new X() is [1]; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[1]").WithArguments("System.Index", "op_Implicit").WithLocation(20, 24), + // (20,24): error CS0656: Missing compiler required member 'System.Index..ctor' + // _ = new X() is [1]; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[1]").WithArguments("System.Index", ".ctor").WithLocation(20, 24), + // (21,24): error CS0656: Missing compiler required member 'System.Index.op_Implicit' + // _ = new X() is [.. 1]; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[.. 1]").WithArguments("System.Index", "op_Implicit").WithLocation(21, 24), + // (21,24): error CS0656: Missing compiler required member 'System.Index..ctor' + // _ = new X() is [.. 1]; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[.. 1]").WithArguments("System.Index", ".ctor").WithLocation(21, 24), + // (21,25): error CS0656: Missing compiler required member 'System.Range..ctor' + // _ = new X() is [.. 1]; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, ".. 1").WithArguments("System.Range", ".ctor").WithLocation(21, 25), + // (22,21): error CS0656: Missing compiler required member 'System.Index..ctor' + // _ = new X()[^1]; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "^1").WithArguments("System.Index", ".ctor").WithLocation(22, 21)); } [Fact] From bcc3336f921c54568f7b492866c6da8be43d0f6f Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Fri, 19 Nov 2021 16:49:06 -0800 Subject: [PATCH 28/29] tweak --- .../PatternMatchingTests_ListPatterns.cs | 41 ++++++++++--------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs index 22e45cee24ea..d5c191d7da40 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs @@ -753,14 +753,23 @@ public static void Main() } } "; - var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularWithListPatterns, options: TestOptions.ReleaseExe); + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularWithListPatterns); compilation.VerifyEmitDiagnostics( // (20,24): error CS0656: Missing compiler required member 'System.Index.op_Implicit' // _ = new X() is [1]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[1]").WithArguments("System.Index", "op_Implicit").WithLocation(20, 24), + // (20,24): error CS0656: Missing compiler required member 'System.Index..ctor' + // _ = new X() is [1]; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[1]").WithArguments("System.Index", ".ctor").WithLocation(20, 24), // (21,24): error CS0656: Missing compiler required member 'System.Index.op_Implicit' // _ = new X() is [.. 1]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[.. 1]").WithArguments("System.Index", "op_Implicit").WithLocation(21, 24), + // (21,24): error CS0656: Missing compiler required member 'System.Index..ctor' + // _ = new X() is [.. 1]; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[.. 1]").WithArguments("System.Index", ".ctor").WithLocation(21, 24), + // (21,25): error CS0656: Missing compiler required member 'System.Range..ctor' + // _ = new X() is [.. 1]; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, ".. 1").WithArguments("System.Range", ".ctor").WithLocation(21, 25), // (22,21): error CS0656: Missing compiler required member 'System.Index..ctor' // _ = new X()[^1]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "^1").WithArguments("System.Index", ".ctor").WithLocation(22, 21) @@ -1401,24 +1410,18 @@ public static void Main() var csCompilation = CreateCompilation(csSource, parseOptions: TestOptions.RegularWithListPatterns, references: new[] { vbCompilation.EmitToImageReference() }); // PROTOTYPE(list-patterns) Unsupported because the lookup fails not that the indexer is static csCompilation.VerifyEmitDiagnostics( - // (20,24): error CS0656: Missing compiler required member 'System.Index.op_Implicit' - // _ = new X() is [1]; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[1]").WithArguments("System.Index", "op_Implicit").WithLocation(20, 24), - // (20,24): error CS0656: Missing compiler required member 'System.Index..ctor' - // _ = new X() is [1]; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[1]").WithArguments("System.Index", ".ctor").WithLocation(20, 24), - // (21,24): error CS0656: Missing compiler required member 'System.Index.op_Implicit' - // _ = new X() is [.. 1]; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[.. 1]").WithArguments("System.Index", "op_Implicit").WithLocation(21, 24), - // (21,24): error CS0656: Missing compiler required member 'System.Index..ctor' - // _ = new X() is [.. 1]; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[.. 1]").WithArguments("System.Index", ".ctor").WithLocation(21, 24), - // (21,25): error CS0656: Missing compiler required member 'System.Range..ctor' - // _ = new X() is [.. 1]; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, ".. 1").WithArguments("System.Range", ".ctor").WithLocation(21, 25), - // (22,21): error CS0656: Missing compiler required member 'System.Index..ctor' - // _ = new X()[^1]; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "^1").WithArguments("System.Index", ".ctor").WithLocation(22, 21)); + // (6,28): error CS0021: Cannot apply indexing with [] to an expression of type 'Test1' + // _ = new Test1() is [0]; + Diagnostic(ErrorCode.ERR_BadIndexLHS, "[0]").WithArguments("Test1").WithLocation(6, 28), + // (6,28): error CS0656: Missing compiler required member 'System.Index.op_Implicit' + // _ = new Test1() is [0]; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[0]").WithArguments("System.Index", "op_Implicit").WithLocation(6, 28), + // (6,28): error CS0656: Missing compiler required member 'System.Index..ctor' + // _ = new Test1() is [0]; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[0]").WithArguments("System.Index", ".ctor").WithLocation(6, 28), + // (7,13): error CS0021: Cannot apply indexing with [] to an expression of type 'Test1' + // _ = new Test1()[0]; + Diagnostic(ErrorCode.ERR_BadIndexLHS, "new Test1()[0]").WithArguments("Test1").WithLocation(7, 13)); } [Fact] From 76d6d7b6dc07df80c0bcbc5223af35dc41ad0e50 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Sat, 20 Nov 2021 10:09:49 -0800 Subject: [PATCH 29/29] Rename to ImplicitIndexer and IndexerOrSliceAccess --- .../Portable/Binder/Binder.ValueChecks.cs | 36 ++--- .../Portable/Binder/Binder_Expressions.cs | 20 +-- .../CSharp/Portable/Binder/Binder_Patterns.cs | 4 +- .../Portable/Binder/Binder_Statements.cs | 10 +- .../Binder/DecisionDagBuilder_ListPatterns.cs | 4 +- .../BoundIndexOrRangePatternIndexerAccess.cs | 10 +- .../CSharp/Portable/BoundTree/BoundNodes.xml | 20 +-- .../CSharp/Portable/BoundTree/Expression.cs | 2 +- .../Portable/BoundTree/NullabilityRewriter.cs | 6 +- .../CSharp/Portable/CSharpResources.resx | 2 +- .../Compilation/CSharpSemanticModel.cs | 4 +- .../MemberSemanticModel.NodeMapBuilder.cs | 2 +- .../CSharp/Portable/Errors/ErrorCode.cs | 2 +- .../Portable/FlowAnalysis/AbstractFlowPass.cs | 2 +- .../NullableWalker.DebugVerifier.cs | 4 +- .../Portable/FlowAnalysis/NullableWalker.cs | 10 +- .../FlowAnalysis/NullableWalker_Patterns.cs | 2 +- .../Generated/BoundNodes.xml.Generated.cs | 124 +++++++++--------- .../DiagnosticsPass_ExpressionTrees.cs | 8 +- .../LocalRewriter.PatternLocalRewriter.cs | 4 +- .../Lowering/LocalRewriter/LocalRewriter.cs | 12 +- .../LocalRewriter_AssignmentOperator.cs | 6 +- ...ocalRewriter_CompoundAssignmentOperator.cs | 10 +- .../LocalRewriter_IndexerAccess.cs | 26 ++-- .../Operations/CSharpOperationFactory.cs | 2 +- .../Portable/xlf/CSharpResources.cs.xlf | 4 +- .../Portable/xlf/CSharpResources.de.xlf | 4 +- .../Portable/xlf/CSharpResources.es.xlf | 4 +- .../Portable/xlf/CSharpResources.fr.xlf | 4 +- .../Portable/xlf/CSharpResources.it.xlf | 4 +- .../Portable/xlf/CSharpResources.ja.xlf | 4 +- .../Portable/xlf/CSharpResources.ko.xlf | 4 +- .../Portable/xlf/CSharpResources.pl.xlf | 4 +- .../Portable/xlf/CSharpResources.pt-BR.xlf | 4 +- .../Portable/xlf/CSharpResources.ru.xlf | 4 +- .../Portable/xlf/CSharpResources.tr.xlf | 4 +- .../Portable/xlf/CSharpResources.zh-Hans.xlf | 4 +- .../Portable/xlf/CSharpResources.zh-Hant.xlf | 4 +- .../Test/Emit/CodeGen/IndexAndRangeTests.cs | 8 +- 39 files changed, 196 insertions(+), 196 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index 3143a6a25a5f..2c8af2db7072 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -398,9 +398,9 @@ internal bool CheckValueKind(SyntaxNode node, BoundExpression expr, BindValueKin case BoundKind.IndexerAccess: return CheckPropertyValueKind(node, expr, valueKind, checkingReceiver, diagnostics); - case BoundKind.IndexOrRangePatternIndexerAccess: - var implicitIndexer = (BoundIndexOrRangePatternIndexerAccess)expr; - return CheckValueKind(node, implicitIndexer.IndexerAccess, valueKind, checkingReceiver, diagnostics); + case BoundKind.ImplicitIndexerAccess: + var implicitIndexer = (BoundImplicitIndexerAccess)expr; + return CheckValueKind(node, implicitIndexer.IndexerOrSliceAccess, valueKind, checkingReceiver, diagnostics); case BoundKind.EventAccess: return CheckEventValueKind((BoundEventAccess)expr, valueKind, diagnostics); @@ -581,10 +581,10 @@ internal bool CheckValueKind(SyntaxNode node, BoundExpression expr, BindValueKin checkingReceiver, diagnostics); - case BoundKind.IndexOrRangePatternIndexerAccess: + case BoundKind.ImplicitIndexerAccess: throw ExceptionUtilities.Unreachable; - case BoundKind.IndexOrRangeIndexerPatternReceiverPlaceholder: + case BoundKind.ImplicitIndexerReceiverPlaceholder: break; case BoundKind.DeconstructValuePlaceholder: @@ -2340,10 +2340,10 @@ internal static bool CheckRefEscape(SyntaxNode node, BoundExpression expr, uint diagnostics, isRefEscape: true); - case BoundKind.IndexOrRangePatternIndexerAccess: - var implicitIndexerAccess = (BoundIndexOrRangePatternIndexerAccess)expr; + case BoundKind.ImplicitIndexerAccess: + var implicitIndexerAccess = (BoundImplicitIndexerAccess)expr; // Note: the LengthOrCountAccess use is purely local - return CheckRefEscape(node, implicitIndexerAccess.IndexerAccess, escapeFrom, escapeTo, checkingReceiver, diagnostics); + return CheckRefEscape(node, implicitIndexerAccess.IndexerOrSliceAccess, escapeFrom, escapeTo, checkingReceiver, diagnostics); case BoundKind.FunctionPointerInvocation: var functionPointerInvocation = (BoundFunctionPointerInvocation)expr; @@ -2578,8 +2578,8 @@ internal static uint GetValEscape(BoundExpression expr, uint scopeOfTheContainin scopeOfTheContainingExpression, isRefEscape: false); - case BoundKind.IndexOrRangeIndexerPatternReceiverPlaceholder: - return ((BoundIndexOrRangeIndexerPatternReceiverPlaceholder)expr).ValEscape; + case BoundKind.ImplicitIndexerReceiverPlaceholder: + return ((BoundImplicitIndexerReceiverPlaceholder)expr).ValEscape; case BoundKind.ListPatternReceiverPlaceholder: return ((BoundListPatternReceiverPlaceholder)expr).ValEscape; @@ -2587,10 +2587,10 @@ internal static uint GetValEscape(BoundExpression expr, uint scopeOfTheContainin case BoundKind.SlicePatternReceiverPlaceholder: return ((BoundSlicePatternReceiverPlaceholder)expr).ValEscape; - case BoundKind.IndexOrRangePatternIndexerAccess: - var implicitIndexerAccess = (BoundIndexOrRangePatternIndexerAccess)expr; + case BoundKind.ImplicitIndexerAccess: + var implicitIndexerAccess = (BoundImplicitIndexerAccess)expr; // Note: the LengthOrCountAccess use is purely local - return GetValEscape(implicitIndexerAccess.IndexerAccess, scopeOfTheContainingExpression); + return GetValEscape(implicitIndexerAccess.IndexerOrSliceAccess, scopeOfTheContainingExpression); case BoundKind.PropertyAccess: var propertyAccess = (BoundPropertyAccess)expr; @@ -2995,8 +2995,8 @@ internal static bool CheckValEscape(SyntaxNode node, BoundExpression expr, uint diagnostics, isRefEscape: false); - case BoundKind.IndexOrRangeIndexerPatternReceiverPlaceholder: - if (((BoundIndexOrRangeIndexerPatternReceiverPlaceholder)expr).ValEscape > escapeTo) + case BoundKind.ImplicitIndexerReceiverPlaceholder: + if (((BoundImplicitIndexerReceiverPlaceholder)expr).ValEscape > escapeTo) { Error(diagnostics, ErrorCode.ERR_EscapeLocal, node, expr.Syntax); return false; @@ -3019,10 +3019,10 @@ internal static bool CheckValEscape(SyntaxNode node, BoundExpression expr, uint } return true; - case BoundKind.IndexOrRangePatternIndexerAccess: - var implicitIndexerAccess = (BoundIndexOrRangePatternIndexerAccess)expr; + case BoundKind.ImplicitIndexerAccess: + var implicitIndexerAccess = (BoundImplicitIndexerAccess)expr; // Note: the LengthOrCountAccess use is purely local - return CheckValEscape(node, implicitIndexerAccess.IndexerAccess, escapeFrom, escapeTo, checkingReceiver, diagnostics); + return CheckValEscape(node, implicitIndexerAccess.IndexerOrSliceAccess, escapeFrom, escapeTo, checkingReceiver, diagnostics); case BoundKind.PropertyAccess: var propertyAccess = (BoundPropertyAccess)expr; diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index 914c8584d6a4..e8151a8c999a 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -8055,7 +8055,7 @@ private bool TryBindIndexOrRangeImplicitIndexer( BoundExpression receiver, AnalyzedArguments arguments, BindingDiagnosticBag diagnostics, - [NotNullWhen(true)] out BoundIndexOrRangePatternIndexerAccess? implicitIndexerAccess) + [NotNullWhen(true)] out BoundImplicitIndexerAccess? implicitIndexerAccess) { Debug.Assert(receiver is not null); implicitIndexerAccess = null; @@ -8085,7 +8085,7 @@ private bool TryBindIndexOrRangeImplicitIndexer( bool argIsIndex = argIsIndexNotRange.Value(); var receiverValEscape = GetValEscape(receiver, LocalScopeDepth); - var receiverPlaceholder = new BoundIndexOrRangeIndexerPatternReceiverPlaceholder(receiver.Syntax, receiverValEscape, receiver.Type) { WasCompilerGenerated = true }; + var receiverPlaceholder = new BoundImplicitIndexerReceiverPlaceholder(receiver.Syntax, receiverValEscape, receiver.Type) { WasCompilerGenerated = true }; if (!TryBindIndexOrRangeImplicitIndexerParts(syntax, receiverPlaceholder, receiver, argIsIndex: argIsIndex, out var lengthOrCountAccess, out var indexerOrSliceAccess, out var argumentPlaceholders, diagnostics)) { @@ -8096,12 +8096,12 @@ private bool TryBindIndexOrRangeImplicitIndexer( Debug.Assert(indexerOrSliceAccess is BoundIndexerAccess or BoundCall); Debug.Assert(indexerOrSliceAccess.Type is not null); - implicitIndexerAccess = new BoundIndexOrRangePatternIndexerAccess( + implicitIndexerAccess = new BoundImplicitIndexerAccess( syntax, argument: BindToNaturalType(argument, diagnostics), lengthOrCountAccess: lengthOrCountAccess, receiverPlaceholder, - indexerAccess: indexerOrSliceAccess, + indexerOrSliceAccess: indexerOrSliceAccess, argumentPlaceholders, indexerOrSliceAccess.Type); @@ -8144,7 +8144,7 @@ private bool TryBindIndexOrRangeImplicitIndexerParts( bool argIsIndex, [NotNullWhen(true)] out BoundExpression? lengthOrCountAccess, [NotNullWhen(true)] out BoundExpression? indexerOrSliceAccess, - out ImmutableArray argumentPlaceholders, + out ImmutableArray argumentPlaceholders, BindingDiagnosticBag diagnostics) { // SPEC: @@ -8176,7 +8176,7 @@ bool tryBindUnderlyingIndexerOrSliceAccess( BoundExpression receiver, bool argIsIndex, [NotNullWhen(true)] out BoundExpression? indexerOrSliceAccess, - out ImmutableArray argumentPlaceholders, + out ImmutableArray argumentPlaceholders, BindingDiagnosticBag diagnostics) { Debug.Assert(receiver.Type is not null); @@ -8209,7 +8209,7 @@ candidate is PropertySymbol property && property.OriginalDefinition is { ParameterCount: 1 } original && original.Parameters[0] is { Type.SpecialType: SpecialType.System_Int32, RefKind: RefKind.None }) { - var intPlaceholder = new BoundIndexOrRangeIndexerPatternValuePlaceholder(syntax, Compilation.GetSpecialType(SpecialType.System_Int32)) { WasCompilerGenerated = true }; + var intPlaceholder = new BoundImplicitIndexerValuePlaceholder(syntax, Compilation.GetSpecialType(SpecialType.System_Int32)) { WasCompilerGenerated = true }; argumentPlaceholders = ImmutableArray.Create(intPlaceholder); var analyzedArguments = AnalyzedArguments.GetInstance(); @@ -8282,10 +8282,10 @@ method.OriginalDefinition is var original && } void makeCall(SyntaxNode syntax, BoundExpression receiver, MethodSymbol method, - out BoundExpression indexerOrSliceAccess, out ImmutableArray argumentPlaceholders) + out BoundExpression indexerOrSliceAccess, out ImmutableArray argumentPlaceholders) { - var startArgumentPlaceholder = new BoundIndexOrRangeIndexerPatternValuePlaceholder(syntax, Compilation.GetSpecialType(SpecialType.System_Int32)) { WasCompilerGenerated = true }; - var lengthArgumentPlaceholder = new BoundIndexOrRangeIndexerPatternValuePlaceholder(syntax, Compilation.GetSpecialType(SpecialType.System_Int32)) { WasCompilerGenerated = true }; + var startArgumentPlaceholder = new BoundImplicitIndexerValuePlaceholder(syntax, Compilation.GetSpecialType(SpecialType.System_Int32)) { WasCompilerGenerated = true }; + var lengthArgumentPlaceholder = new BoundImplicitIndexerValuePlaceholder(syntax, Compilation.GetSpecialType(SpecialType.System_Int32)) { WasCompilerGenerated = true }; argumentPlaceholders = ImmutableArray.Create(startArgumentPlaceholder, lengthArgumentPlaceholder); var analyzedArguments = AnalyzedArguments.GetInstance(); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs index b497a748149c..10ef681c61de 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs @@ -223,7 +223,7 @@ private BoundPattern BindSlicePattern( indexerAccess = BindElementAccessCore(node, receiverPlaceholder, analyzedArguments, diagnostics).MakeCompilerGenerated(); indexerAccess = CheckValue(indexerAccess, BindValueKind.RValue, diagnostics); - Debug.Assert(indexerAccess is BoundIndexerAccess or BoundIndexOrRangePatternIndexerAccess or BoundArrayAccess or BoundBadExpression or BoundDynamicIndexerAccess); + Debug.Assert(indexerAccess is BoundIndexerAccess or BoundImplicitIndexerAccess or BoundArrayAccess or BoundBadExpression or BoundDynamicIndexerAccess); analyzedArguments.Free(); if (!systemRangeType.HasUseSiteError) @@ -371,7 +371,7 @@ private bool BindLengthAndIndexerForListPattern(SyntaxNode node, TypeSymbol inpu indexerAccess = BindElementAccessCore(node, receiverPlaceholder, analyzedArguments, diagnostics).MakeCompilerGenerated(); indexerAccess = CheckValue(indexerAccess, BindValueKind.RValue, diagnostics); - Debug.Assert(indexerAccess is BoundIndexerAccess or BoundIndexOrRangePatternIndexerAccess or BoundArrayAccess or BoundBadExpression or BoundDynamicIndexerAccess); + Debug.Assert(indexerAccess is BoundIndexerAccess or BoundImplicitIndexerAccess or BoundArrayAccess or BoundBadExpression or BoundDynamicIndexerAccess); analyzedArguments.Free(); if (!systemIndexType.HasUseSiteError) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index a9eb814de85a..fa52da51cd7b 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -1563,10 +1563,10 @@ internal static PropertySymbol GetPropertySymbol(BoundExpression expr, out Bound propertySymbol = indexerAccess.Indexer; } break; - case BoundKind.IndexOrRangePatternIndexerAccess: + case BoundKind.ImplicitIndexerAccess: { - var implicitIndexerAccess = (BoundIndexOrRangePatternIndexerAccess)expr; - propertySymbol = GetPropertySymbol(implicitIndexerAccess.IndexerAccess, out receiver, out propertySyntax); + var implicitIndexerAccess = (BoundImplicitIndexerAccess)expr; + propertySymbol = GetPropertySymbol(implicitIndexerAccess.IndexerOrSliceAccess, out receiver, out propertySyntax); } break; default: @@ -1607,9 +1607,9 @@ internal static PropertySymbol GetPropertySymbol(BoundExpression expr, out Bound // this[Index], this[Range] BoundIndexerAccess indexerAccess => indexerAccess.Indexer, // Slice(int, int), Substring(int, int) - BoundIndexOrRangePatternIndexerAccess { IndexerAccess: BoundCall call } => call.Method, + BoundImplicitIndexerAccess { IndexerOrSliceAccess: BoundCall call } => call.Method, // this[int] - BoundIndexOrRangePatternIndexerAccess { IndexerAccess: BoundIndexerAccess indexerAccess } => indexerAccess.Indexer, + BoundImplicitIndexerAccess { IndexerOrSliceAccess: BoundIndexerAccess indexerAccess } => indexerAccess.Indexer, // array[int] BoundArrayAccess => null, BoundDynamicIndexerAccess => null, diff --git a/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder_ListPatterns.cs b/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder_ListPatterns.cs index 09b4db76aab9..9654b37e324d 100644 --- a/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder_ListPatterns.cs +++ b/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder_ListPatterns.cs @@ -60,7 +60,7 @@ private Tests MakeTestsAndBindingsForListPattern(BoundDagTemp input, BoundListPa Debug.Assert(slice.ReceiverPlaceholder is not null); Debug.Assert(slice.ArgumentPlaceholder is not null); - Debug.Assert(slice.IndexerAccess is BoundIndexerAccess or BoundIndexOrRangePatternIndexerAccess or BoundArrayAccess); + Debug.Assert(slice.IndexerAccess is BoundIndexerAccess or BoundImplicitIndexerAccess or BoundArrayAccess); var sliceEvaluation = new BoundDagSliceEvaluation(slicePattern.Syntax, slicePattern.InputType, lengthTemp, startIndex: startIndex, endIndex: index, slice.IndexerAccess, slice.ReceiverPlaceholder, slice.ArgumentPlaceholder, input); @@ -76,7 +76,7 @@ private Tests MakeTestsAndBindingsForListPattern(BoundDagTemp input, BoundListPa Debug.Assert(list.ReceiverPlaceholder is not null); Debug.Assert(list.ArgumentPlaceholder is not null); - Debug.Assert(list.IndexerAccess is BoundIndexerAccess or BoundIndexOrRangePatternIndexerAccess or BoundArrayAccess); + Debug.Assert(list.IndexerAccess is BoundIndexerAccess or BoundImplicitIndexerAccess or BoundArrayAccess); var indexEvaluation = new BoundDagIndexerEvaluation(subpattern.Syntax, subpattern.InputType, lengthTemp, index++, list.IndexerAccess, list.ReceiverPlaceholder, list.ArgumentPlaceholder, input); diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundIndexOrRangePatternIndexerAccess.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundIndexOrRangePatternIndexerAccess.cs index 4502d745e836..8c962909e29a 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundIndexOrRangePatternIndexerAccess.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundIndexOrRangePatternIndexerAccess.cs @@ -7,23 +7,23 @@ namespace Microsoft.CodeAnalysis.CSharp { - internal partial class BoundIndexOrRangePatternIndexerAccess + internal partial class BoundImplicitIndexerAccess { - internal BoundIndexOrRangePatternIndexerAccess WithLengthOrCountAccess(BoundExpression lengthOrCountAccess) + internal BoundImplicitIndexerAccess WithLengthOrCountAccess(BoundExpression lengthOrCountAccess) { return this.Update(this.Argument, lengthOrCountAccess, this.ReceiverPlaceholder, - this.IndexerAccess, this.ArgumentPlaceholders, this.Type); + this.IndexerOrSliceAccess, this.ArgumentPlaceholders, this.Type); } // The receiver expression is the receiver of IndexerAccess. // The LengthOrCountAccess uses a placeholder as receiver. internal BoundExpression GetReceiver() { - var receiver = this.IndexerAccess switch + var receiver = this.IndexerOrSliceAccess switch { BoundIndexerAccess { ReceiverOpt: var r } => r, BoundCall { ReceiverOpt: var r } => r, - _ => throw ExceptionUtilities.UnexpectedValue(this.IndexerAccess.Kind) + _ => throw ExceptionUtilities.UnexpectedValue(this.IndexerOrSliceAccess.Kind) }; Debug.Assert(receiver is not null); diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml index 4a4a2e1f9eec..3fa57b3b3227 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml @@ -139,12 +139,12 @@ - + - + @@ -1527,7 +1527,7 @@ @@ -1546,7 +1546,7 @@ @@ -2066,7 +2066,7 @@ - + @@ -2075,7 +2075,7 @@ - + - + - + @@ -2248,7 +2248,7 @@ @@ -2264,7 +2264,7 @@ diff --git a/src/Compilers/CSharp/Portable/BoundTree/Expression.cs b/src/Compilers/CSharp/Portable/BoundTree/Expression.cs index 05ecbe01376d..c4e7e4e5e9d4 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/Expression.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/Expression.cs @@ -175,7 +175,7 @@ internal partial class BoundPassByCopy protected override ImmutableArray Children => ImmutableArray.Create(this.Expression); } - internal partial class BoundIndexOrRangePatternIndexerAccess + internal partial class BoundImplicitIndexerAccess { protected override ImmutableArray Children => ImmutableArray.Create(this.GetReceiver(), Argument); } diff --git a/src/Compilers/CSharp/Portable/BoundTree/NullabilityRewriter.cs b/src/Compilers/CSharp/Portable/BoundTree/NullabilityRewriter.cs index a5293b7f22d0..0b6e7ba72394 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/NullabilityRewriter.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/NullabilityRewriter.cs @@ -162,12 +162,12 @@ Symbol remapLocal(SourceLocalSymbol local) } } - public override BoundNode? VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node) + public override BoundNode? VisitImplicitIndexerAccess(BoundImplicitIndexerAccess node) { BoundExpression argument = (BoundExpression)this.Visit(node.Argument); BoundExpression lengthOrCountAccess = node.LengthOrCountAccess; - BoundExpression indexerAccess = (BoundExpression)this.Visit(node.IndexerAccess); - BoundIndexOrRangePatternIndexerAccess updatedNode; + BoundExpression indexerAccess = (BoundExpression)this.Visit(node.IndexerOrSliceAccess); + BoundImplicitIndexerAccess updatedNode; if (_updatedNullabilities.TryGetValue(node, out (NullabilityInfo Info, TypeSymbol? Type) infoAndType)) { diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index d5138e696b9c..310b6e2406fc 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -6247,7 +6247,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ target-typed object creation - + An expression tree may not contain a pattern System.Index or System.Range indexer access diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs index 2b02e9bb6e3f..3c36ed0efcc3 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs @@ -3427,8 +3427,8 @@ private ImmutableArray GetSemanticSymbols( } break; - case BoundKind.IndexOrRangePatternIndexerAccess: - return GetSemanticSymbols(((BoundIndexOrRangePatternIndexerAccess)boundNode).IndexerAccess, + case BoundKind.ImplicitIndexerAccess: + return GetSemanticSymbols(((BoundImplicitIndexerAccess)boundNode).IndexerOrSliceAccess, boundNodeForSyntacticParent, binderOpt, options, out isDynamic, out resultKind, out memberGroup); case BoundKind.EventAssignmentOperator: diff --git a/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.NodeMapBuilder.cs b/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.NodeMapBuilder.cs index c55ad26d1c1d..5af55ba42c49 100644 --- a/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.NodeMapBuilder.cs +++ b/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.NodeMapBuilder.cs @@ -258,7 +258,7 @@ public override BoundNode VisitQueryClause(BoundQueryClause node) return null; } - public override BoundNode VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node) + public override BoundNode VisitImplicitIndexerAccess(BoundImplicitIndexerAccess node) { // We only need to visit receiver and the argument. SemanticModel isn't interested in anything else. Visit(node.GetReceiver()); diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 5894c0768d87..ec97b5c00562 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1796,7 +1796,7 @@ internal enum ErrorCode ERR_CannotUseReducedExtensionMethodInAddressOf = 8788, ERR_CannotUseFunctionPointerAsFixedLocal = 8789, - ERR_ExpressionTreeContainsPatternIndexOrRangeIndexer = 8790, + ERR_ExpressionTreeContainsPatternImplicitIndexer = 8790, ERR_ExpressionTreeContainsFromEndIndexExpression = 8791, ERR_ExpressionTreeContainsRangeExpression = 8792, WRN_GivenExpressionAlwaysMatchesPattern = 8793, diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs index 1a921557fc6a..a06dd736cfd5 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs @@ -1386,7 +1386,7 @@ public override BoundNode VisitIndexerAccess(BoundIndexerAccess node) return null; } - public override BoundNode VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node) + public override BoundNode VisitImplicitIndexerAccess(BoundImplicitIndexerAccess node) { // Index or Range implicit indexers evaluate the following in order: // 1. The receiver diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.DebugVerifier.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.DebugVerifier.cs index 73ca053d681f..a1a719d0dc63 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.DebugVerifier.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.DebugVerifier.cs @@ -274,10 +274,10 @@ private void VisitBinaryOperatorChildren(BoundBinaryOperatorBase node) return null; } - public override BoundNode? VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node) + public override BoundNode? VisitImplicitIndexerAccess(BoundImplicitIndexerAccess node) { Visit(node.Argument); - Visit(node.IndexerAccess); + Visit(node.IndexerOrSliceAccess); return null; } } diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index a81225762ab0..79f81cd5fa59 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -8750,19 +8750,19 @@ private TypeWithAnnotations GetDeclaredParameterResult(ParameterSymbol parameter return null; } - public override BoundNode? VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node) + public override BoundNode? VisitImplicitIndexerAccess(BoundImplicitIndexerAccess node) { - // The argument will be visited as part of VisitIndexOrRangeIndexerPatternValuePlaceholder (for the first argument) + // The argument will be visited as part of VisitImplicitIndexerValuePlaceholder (for the first argument) // to maintain proper order of evaluation AddPlaceholder(node.ArgumentPlaceholders[0], node.Argument); - VisitRvalue(node.IndexerAccess); + VisitRvalue(node.IndexerOrSliceAccess); RemovePlaceholder(node.ArgumentPlaceholders[0]); SetResult(node, ResultType, LvalueResultType); return null; } - public override BoundNode? VisitIndexOrRangeIndexerPatternValuePlaceholder(BoundIndexOrRangeIndexerPatternValuePlaceholder node) + public override BoundNode? VisitImplicitIndexerValuePlaceholder(BoundImplicitIndexerValuePlaceholder node) { // We use this placeholder as trigger to visit the Argument of implicit indexer access (after its Receiver) if (TryReplacePlaceholder(node, out var unvisited)) @@ -8774,7 +8774,7 @@ private TypeWithAnnotations GetDeclaredParameterResult(ParameterSymbol parameter return null; } - public override BoundNode? VisitIndexOrRangeIndexerPatternReceiverPlaceholder(BoundIndexOrRangeIndexerPatternReceiverPlaceholder node) + public override BoundNode? VisitImplicitIndexerReceiverPlaceholder(BoundImplicitIndexerReceiverPlaceholder node) { SetNotNullResult(node); return null; diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker_Patterns.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker_Patterns.cs index de1bf151bc17..5506d8e04b5d 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker_Patterns.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker_Patterns.cs @@ -793,7 +793,7 @@ static TypeWithAnnotations getIndexerOutputType(TypeSymbol inputType, BoundExpre ? TypeWithAnnotations.Create(isNullableEnabled: true, inputType, isAnnotated: false) : ((ArrayTypeSymbol)inputType).ElementTypeWithAnnotations, - BoundIndexOrRangePatternIndexerAccess implicitIndexerAccess => getIndexerOutputType(inputType, implicitIndexerAccess.IndexerAccess, isSlice), + BoundImplicitIndexerAccess implicitIndexerAccess => getIndexerOutputType(inputType, implicitIndexerAccess.IndexerOrSliceAccess, isSlice), _ => throw ExceptionUtilities.UnexpectedValue(e.Kind) }; } diff --git a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs index c1f1041348af..19c0f70810bb 100644 --- a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs @@ -30,8 +30,8 @@ internal enum BoundKind : byte AwaitableValuePlaceholder, DisposableValuePlaceholder, ObjectOrCollectionValuePlaceholder, - IndexOrRangeIndexerPatternValuePlaceholder, - IndexOrRangeIndexerPatternReceiverPlaceholder, + ImplicitIndexerValuePlaceholder, + ImplicitIndexerReceiverPlaceholder, ListPatternReceiverPlaceholder, ListPatternIndexPlaceholder, SlicePatternReceiverPlaceholder, @@ -202,7 +202,7 @@ internal enum BoundKind : byte PropertyAccess, EventAccess, IndexerAccess, - IndexOrRangePatternIndexerAccess, + ImplicitIndexerAccess, DynamicIndexerAccess, Lambda, UnboundLambda, @@ -659,18 +659,18 @@ public BoundObjectOrCollectionValuePlaceholder Update(bool isNewInstance, TypeSy } } - internal sealed partial class BoundIndexOrRangeIndexerPatternValuePlaceholder : BoundValuePlaceholderBase + internal sealed partial class BoundImplicitIndexerValuePlaceholder : BoundValuePlaceholderBase { - public BoundIndexOrRangeIndexerPatternValuePlaceholder(SyntaxNode syntax, TypeSymbol type, bool hasErrors) - : base(BoundKind.IndexOrRangeIndexerPatternValuePlaceholder, syntax, type, hasErrors) + public BoundImplicitIndexerValuePlaceholder(SyntaxNode syntax, TypeSymbol type, bool hasErrors) + : base(BoundKind.ImplicitIndexerValuePlaceholder, syntax, type, hasErrors) { RoslynDebug.Assert(type is object, "Field 'type' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); } - public BoundIndexOrRangeIndexerPatternValuePlaceholder(SyntaxNode syntax, TypeSymbol type) - : base(BoundKind.IndexOrRangeIndexerPatternValuePlaceholder, syntax, type) + public BoundImplicitIndexerValuePlaceholder(SyntaxNode syntax, TypeSymbol type) + : base(BoundKind.ImplicitIndexerValuePlaceholder, syntax, type) { RoslynDebug.Assert(type is object, "Field 'type' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); @@ -680,13 +680,13 @@ public BoundIndexOrRangeIndexerPatternValuePlaceholder(SyntaxNode syntax, TypeSy public new TypeSymbol Type => base.Type!; [DebuggerStepThrough] - public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitIndexOrRangeIndexerPatternValuePlaceholder(this); + public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitImplicitIndexerValuePlaceholder(this); - public BoundIndexOrRangeIndexerPatternValuePlaceholder Update(TypeSymbol type) + public BoundImplicitIndexerValuePlaceholder Update(TypeSymbol type) { if (!TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) { - var result = new BoundIndexOrRangeIndexerPatternValuePlaceholder(this.Syntax, type, this.HasErrors); + var result = new BoundImplicitIndexerValuePlaceholder(this.Syntax, type, this.HasErrors); result.CopyAttributes(this); return result; } @@ -694,10 +694,10 @@ public BoundIndexOrRangeIndexerPatternValuePlaceholder Update(TypeSymbol type) } } - internal sealed partial class BoundIndexOrRangeIndexerPatternReceiverPlaceholder : BoundValuePlaceholderBase + internal sealed partial class BoundImplicitIndexerReceiverPlaceholder : BoundValuePlaceholderBase { - public BoundIndexOrRangeIndexerPatternReceiverPlaceholder(SyntaxNode syntax, uint valEscape, TypeSymbol type, bool hasErrors) - : base(BoundKind.IndexOrRangeIndexerPatternReceiverPlaceholder, syntax, type, hasErrors) + public BoundImplicitIndexerReceiverPlaceholder(SyntaxNode syntax, uint valEscape, TypeSymbol type, bool hasErrors) + : base(BoundKind.ImplicitIndexerReceiverPlaceholder, syntax, type, hasErrors) { RoslynDebug.Assert(type is object, "Field 'type' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); @@ -705,8 +705,8 @@ public BoundIndexOrRangeIndexerPatternReceiverPlaceholder(SyntaxNode syntax, uin this.ValEscape = valEscape; } - public BoundIndexOrRangeIndexerPatternReceiverPlaceholder(SyntaxNode syntax, uint valEscape, TypeSymbol type) - : base(BoundKind.IndexOrRangeIndexerPatternReceiverPlaceholder, syntax, type) + public BoundImplicitIndexerReceiverPlaceholder(SyntaxNode syntax, uint valEscape, TypeSymbol type) + : base(BoundKind.ImplicitIndexerReceiverPlaceholder, syntax, type) { RoslynDebug.Assert(type is object, "Field 'type' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); @@ -719,13 +719,13 @@ public BoundIndexOrRangeIndexerPatternReceiverPlaceholder(SyntaxNode syntax, uin public uint ValEscape { get; } [DebuggerStepThrough] - public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitIndexOrRangeIndexerPatternReceiverPlaceholder(this); + public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitImplicitIndexerReceiverPlaceholder(this); - public BoundIndexOrRangeIndexerPatternReceiverPlaceholder Update(uint valEscape, TypeSymbol type) + public BoundImplicitIndexerReceiverPlaceholder Update(uint valEscape, TypeSymbol type) { if (valEscape != this.ValEscape || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) { - var result = new BoundIndexOrRangeIndexerPatternReceiverPlaceholder(this.Syntax, valEscape, type, this.HasErrors); + var result = new BoundImplicitIndexerReceiverPlaceholder(this.Syntax, valEscape, type, this.HasErrors); result.CopyAttributes(this); return result; } @@ -7320,23 +7320,23 @@ public BoundIndexerAccess Update(BoundExpression? receiverOpt, PropertySymbol in } } - internal sealed partial class BoundIndexOrRangePatternIndexerAccess : BoundExpression + internal sealed partial class BoundImplicitIndexerAccess : BoundExpression { - public BoundIndexOrRangePatternIndexerAccess(SyntaxNode syntax, BoundExpression argument, BoundExpression lengthOrCountAccess, BoundIndexOrRangeIndexerPatternReceiverPlaceholder receiverPlaceholder, BoundExpression indexerAccess, ImmutableArray argumentPlaceholders, TypeSymbol type, bool hasErrors = false) - : base(BoundKind.IndexOrRangePatternIndexerAccess, syntax, type, hasErrors || argument.HasErrors() || lengthOrCountAccess.HasErrors() || receiverPlaceholder.HasErrors() || indexerAccess.HasErrors() || argumentPlaceholders.HasErrors()) + public BoundImplicitIndexerAccess(SyntaxNode syntax, BoundExpression argument, BoundExpression lengthOrCountAccess, BoundImplicitIndexerReceiverPlaceholder receiverPlaceholder, BoundExpression indexerOrSliceAccess, ImmutableArray argumentPlaceholders, TypeSymbol type, bool hasErrors = false) + : base(BoundKind.ImplicitIndexerAccess, syntax, type, hasErrors || argument.HasErrors() || lengthOrCountAccess.HasErrors() || receiverPlaceholder.HasErrors() || indexerOrSliceAccess.HasErrors() || argumentPlaceholders.HasErrors()) { RoslynDebug.Assert(argument is object, "Field 'argument' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); RoslynDebug.Assert(lengthOrCountAccess is object, "Field 'lengthOrCountAccess' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); RoslynDebug.Assert(receiverPlaceholder is object, "Field 'receiverPlaceholder' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); - RoslynDebug.Assert(indexerAccess is object, "Field 'indexerAccess' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); + RoslynDebug.Assert(indexerOrSliceAccess is object, "Field 'indexerOrSliceAccess' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); RoslynDebug.Assert(!argumentPlaceholders.IsDefault, "Field 'argumentPlaceholders' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); RoslynDebug.Assert(type is object, "Field 'type' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); this.Argument = argument; this.LengthOrCountAccess = lengthOrCountAccess; this.ReceiverPlaceholder = receiverPlaceholder; - this.IndexerAccess = indexerAccess; + this.IndexerOrSliceAccess = indexerOrSliceAccess; this.ArgumentPlaceholders = argumentPlaceholders; } @@ -7347,19 +7347,19 @@ public BoundIndexOrRangePatternIndexerAccess(SyntaxNode syntax, BoundExpression public BoundExpression LengthOrCountAccess { get; } - public BoundIndexOrRangeIndexerPatternReceiverPlaceholder ReceiverPlaceholder { get; } + public BoundImplicitIndexerReceiverPlaceholder ReceiverPlaceholder { get; } - public BoundExpression IndexerAccess { get; } + public BoundExpression IndexerOrSliceAccess { get; } - public ImmutableArray ArgumentPlaceholders { get; } + public ImmutableArray ArgumentPlaceholders { get; } [DebuggerStepThrough] - public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitIndexOrRangePatternIndexerAccess(this); + public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitImplicitIndexerAccess(this); - public BoundIndexOrRangePatternIndexerAccess Update(BoundExpression argument, BoundExpression lengthOrCountAccess, BoundIndexOrRangeIndexerPatternReceiverPlaceholder receiverPlaceholder, BoundExpression indexerAccess, ImmutableArray argumentPlaceholders, TypeSymbol type) + public BoundImplicitIndexerAccess Update(BoundExpression argument, BoundExpression lengthOrCountAccess, BoundImplicitIndexerReceiverPlaceholder receiverPlaceholder, BoundExpression indexerOrSliceAccess, ImmutableArray argumentPlaceholders, TypeSymbol type) { - if (argument != this.Argument || lengthOrCountAccess != this.LengthOrCountAccess || receiverPlaceholder != this.ReceiverPlaceholder || indexerAccess != this.IndexerAccess || argumentPlaceholders != this.ArgumentPlaceholders || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) + if (argument != this.Argument || lengthOrCountAccess != this.LengthOrCountAccess || receiverPlaceholder != this.ReceiverPlaceholder || indexerOrSliceAccess != this.IndexerOrSliceAccess || argumentPlaceholders != this.ArgumentPlaceholders || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) { - var result = new BoundIndexOrRangePatternIndexerAccess(this.Syntax, argument, lengthOrCountAccess, receiverPlaceholder, indexerAccess, argumentPlaceholders, type, this.HasErrors); + var result = new BoundImplicitIndexerAccess(this.Syntax, argument, lengthOrCountAccess, receiverPlaceholder, indexerOrSliceAccess, argumentPlaceholders, type, this.HasErrors); result.CopyAttributes(this); return result; } @@ -8717,10 +8717,10 @@ internal R VisitInternal(BoundNode node, A arg) return VisitDisposableValuePlaceholder((BoundDisposableValuePlaceholder)node, arg); case BoundKind.ObjectOrCollectionValuePlaceholder: return VisitObjectOrCollectionValuePlaceholder((BoundObjectOrCollectionValuePlaceholder)node, arg); - case BoundKind.IndexOrRangeIndexerPatternValuePlaceholder: - return VisitIndexOrRangeIndexerPatternValuePlaceholder((BoundIndexOrRangeIndexerPatternValuePlaceholder)node, arg); - case BoundKind.IndexOrRangeIndexerPatternReceiverPlaceholder: - return VisitIndexOrRangeIndexerPatternReceiverPlaceholder((BoundIndexOrRangeIndexerPatternReceiverPlaceholder)node, arg); + case BoundKind.ImplicitIndexerValuePlaceholder: + return VisitImplicitIndexerValuePlaceholder((BoundImplicitIndexerValuePlaceholder)node, arg); + case BoundKind.ImplicitIndexerReceiverPlaceholder: + return VisitImplicitIndexerReceiverPlaceholder((BoundImplicitIndexerReceiverPlaceholder)node, arg); case BoundKind.ListPatternReceiverPlaceholder: return VisitListPatternReceiverPlaceholder((BoundListPatternReceiverPlaceholder)node, arg); case BoundKind.ListPatternIndexPlaceholder: @@ -9061,8 +9061,8 @@ internal R VisitInternal(BoundNode node, A arg) return VisitEventAccess((BoundEventAccess)node, arg); case BoundKind.IndexerAccess: return VisitIndexerAccess((BoundIndexerAccess)node, arg); - case BoundKind.IndexOrRangePatternIndexerAccess: - return VisitIndexOrRangePatternIndexerAccess((BoundIndexOrRangePatternIndexerAccess)node, arg); + case BoundKind.ImplicitIndexerAccess: + return VisitImplicitIndexerAccess((BoundImplicitIndexerAccess)node, arg); case BoundKind.DynamicIndexerAccess: return VisitDynamicIndexerAccess((BoundDynamicIndexerAccess)node, arg); case BoundKind.Lambda: @@ -9151,8 +9151,8 @@ internal abstract partial class BoundTreeVisitor public virtual R VisitAwaitableValuePlaceholder(BoundAwaitableValuePlaceholder node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitDisposableValuePlaceholder(BoundDisposableValuePlaceholder node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitObjectOrCollectionValuePlaceholder(BoundObjectOrCollectionValuePlaceholder node, A arg) => this.DefaultVisit(node, arg); - public virtual R VisitIndexOrRangeIndexerPatternValuePlaceholder(BoundIndexOrRangeIndexerPatternValuePlaceholder node, A arg) => this.DefaultVisit(node, arg); - public virtual R VisitIndexOrRangeIndexerPatternReceiverPlaceholder(BoundIndexOrRangeIndexerPatternReceiverPlaceholder node, A arg) => this.DefaultVisit(node, arg); + public virtual R VisitImplicitIndexerValuePlaceholder(BoundImplicitIndexerValuePlaceholder node, A arg) => this.DefaultVisit(node, arg); + public virtual R VisitImplicitIndexerReceiverPlaceholder(BoundImplicitIndexerReceiverPlaceholder node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitListPatternReceiverPlaceholder(BoundListPatternReceiverPlaceholder node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitListPatternIndexPlaceholder(BoundListPatternIndexPlaceholder node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitSlicePatternReceiverPlaceholder(BoundSlicePatternReceiverPlaceholder node, A arg) => this.DefaultVisit(node, arg); @@ -9323,7 +9323,7 @@ internal abstract partial class BoundTreeVisitor public virtual R VisitPropertyAccess(BoundPropertyAccess node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitEventAccess(BoundEventAccess node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitIndexerAccess(BoundIndexerAccess node, A arg) => this.DefaultVisit(node, arg); - public virtual R VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node, A arg) => this.DefaultVisit(node, arg); + public virtual R VisitImplicitIndexerAccess(BoundImplicitIndexerAccess node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitDynamicIndexerAccess(BoundDynamicIndexerAccess node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitLambda(BoundLambda node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitUnboundLambda(UnboundLambda node, A arg) => this.DefaultVisit(node, arg); @@ -9373,8 +9373,8 @@ internal abstract partial class BoundTreeVisitor public virtual BoundNode? VisitAwaitableValuePlaceholder(BoundAwaitableValuePlaceholder node) => this.DefaultVisit(node); public virtual BoundNode? VisitDisposableValuePlaceholder(BoundDisposableValuePlaceholder node) => this.DefaultVisit(node); public virtual BoundNode? VisitObjectOrCollectionValuePlaceholder(BoundObjectOrCollectionValuePlaceholder node) => this.DefaultVisit(node); - public virtual BoundNode? VisitIndexOrRangeIndexerPatternValuePlaceholder(BoundIndexOrRangeIndexerPatternValuePlaceholder node) => this.DefaultVisit(node); - public virtual BoundNode? VisitIndexOrRangeIndexerPatternReceiverPlaceholder(BoundIndexOrRangeIndexerPatternReceiverPlaceholder node) => this.DefaultVisit(node); + public virtual BoundNode? VisitImplicitIndexerValuePlaceholder(BoundImplicitIndexerValuePlaceholder node) => this.DefaultVisit(node); + public virtual BoundNode? VisitImplicitIndexerReceiverPlaceholder(BoundImplicitIndexerReceiverPlaceholder node) => this.DefaultVisit(node); public virtual BoundNode? VisitListPatternReceiverPlaceholder(BoundListPatternReceiverPlaceholder node) => this.DefaultVisit(node); public virtual BoundNode? VisitListPatternIndexPlaceholder(BoundListPatternIndexPlaceholder node) => this.DefaultVisit(node); public virtual BoundNode? VisitSlicePatternReceiverPlaceholder(BoundSlicePatternReceiverPlaceholder node) => this.DefaultVisit(node); @@ -9545,7 +9545,7 @@ internal abstract partial class BoundTreeVisitor public virtual BoundNode? VisitPropertyAccess(BoundPropertyAccess node) => this.DefaultVisit(node); public virtual BoundNode? VisitEventAccess(BoundEventAccess node) => this.DefaultVisit(node); public virtual BoundNode? VisitIndexerAccess(BoundIndexerAccess node) => this.DefaultVisit(node); - public virtual BoundNode? VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node) => this.DefaultVisit(node); + public virtual BoundNode? VisitImplicitIndexerAccess(BoundImplicitIndexerAccess node) => this.DefaultVisit(node); public virtual BoundNode? VisitDynamicIndexerAccess(BoundDynamicIndexerAccess node) => this.DefaultVisit(node); public virtual BoundNode? VisitLambda(BoundLambda node) => this.DefaultVisit(node); public virtual BoundNode? VisitUnboundLambda(UnboundLambda node) => this.DefaultVisit(node); @@ -9611,8 +9611,8 @@ internal abstract partial class BoundTreeWalker : BoundTreeVisitor public override BoundNode? VisitAwaitableValuePlaceholder(BoundAwaitableValuePlaceholder node) => null; public override BoundNode? VisitDisposableValuePlaceholder(BoundDisposableValuePlaceholder node) => null; public override BoundNode? VisitObjectOrCollectionValuePlaceholder(BoundObjectOrCollectionValuePlaceholder node) => null; - public override BoundNode? VisitIndexOrRangeIndexerPatternValuePlaceholder(BoundIndexOrRangeIndexerPatternValuePlaceholder node) => null; - public override BoundNode? VisitIndexOrRangeIndexerPatternReceiverPlaceholder(BoundIndexOrRangeIndexerPatternReceiverPlaceholder node) => null; + public override BoundNode? VisitImplicitIndexerValuePlaceholder(BoundImplicitIndexerValuePlaceholder node) => null; + public override BoundNode? VisitImplicitIndexerReceiverPlaceholder(BoundImplicitIndexerReceiverPlaceholder node) => null; public override BoundNode? VisitListPatternReceiverPlaceholder(BoundListPatternReceiverPlaceholder node) => null; public override BoundNode? VisitListPatternIndexPlaceholder(BoundListPatternIndexPlaceholder node) => null; public override BoundNode? VisitSlicePatternReceiverPlaceholder(BoundSlicePatternReceiverPlaceholder node) => null; @@ -10387,10 +10387,10 @@ internal abstract partial class BoundTreeWalker : BoundTreeVisitor this.VisitList(node.Arguments); return null; } - public override BoundNode? VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node) + public override BoundNode? VisitImplicitIndexerAccess(BoundImplicitIndexerAccess node) { this.Visit(node.Argument); - this.Visit(node.IndexerAccess); + this.Visit(node.IndexerOrSliceAccess); return null; } public override BoundNode? VisitDynamicIndexerAccess(BoundDynamicIndexerAccess node) @@ -10613,12 +10613,12 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor TypeSymbol? type = this.VisitType(node.Type); return node.Update(node.IsNewInstance, type); } - public override BoundNode? VisitIndexOrRangeIndexerPatternValuePlaceholder(BoundIndexOrRangeIndexerPatternValuePlaceholder node) + public override BoundNode? VisitImplicitIndexerValuePlaceholder(BoundImplicitIndexerValuePlaceholder node) { TypeSymbol? type = this.VisitType(node.Type); return node.Update(type); } - public override BoundNode? VisitIndexOrRangeIndexerPatternReceiverPlaceholder(BoundIndexOrRangeIndexerPatternReceiverPlaceholder node) + public override BoundNode? VisitImplicitIndexerReceiverPlaceholder(BoundImplicitIndexerReceiverPlaceholder node) { TypeSymbol? type = this.VisitType(node.Type); return node.Update(node.ValEscape, type); @@ -11641,15 +11641,15 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor TypeSymbol? type = this.VisitType(node.Type); return node.Update(receiverOpt, node.Indexer, arguments, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, node.Expanded, node.ArgsToParamsOpt, node.DefaultArguments, node.OriginalIndexersOpt, type); } - public override BoundNode? VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node) + public override BoundNode? VisitImplicitIndexerAccess(BoundImplicitIndexerAccess node) { BoundExpression argument = (BoundExpression)this.Visit(node.Argument); BoundExpression lengthOrCountAccess = node.LengthOrCountAccess; - BoundIndexOrRangeIndexerPatternReceiverPlaceholder receiverPlaceholder = node.ReceiverPlaceholder; - BoundExpression indexerAccess = (BoundExpression)this.Visit(node.IndexerAccess); - ImmutableArray argumentPlaceholders = node.ArgumentPlaceholders; + BoundImplicitIndexerReceiverPlaceholder receiverPlaceholder = node.ReceiverPlaceholder; + BoundExpression indexerOrSliceAccess = (BoundExpression)this.Visit(node.IndexerOrSliceAccess); + ImmutableArray argumentPlaceholders = node.ArgumentPlaceholders; TypeSymbol? type = this.VisitType(node.Type); - return node.Update(argument, lengthOrCountAccess, receiverPlaceholder, indexerAccess, argumentPlaceholders, type); + return node.Update(argument, lengthOrCountAccess, receiverPlaceholder, indexerOrSliceAccess, argumentPlaceholders, type); } public override BoundNode? VisitDynamicIndexerAccess(BoundDynamicIndexerAccess node) { @@ -11999,26 +11999,26 @@ public NullabilityRewriter(ImmutableDictionary new TreeDumperNode("indexOrRangeIndexerPatternValuePlaceholder", null, new TreeDumperNode[] + public override TreeDumperNode VisitImplicitIndexerValuePlaceholder(BoundImplicitIndexerValuePlaceholder node, object? arg) => new TreeDumperNode("implicitIndexerValuePlaceholder", null, new TreeDumperNode[] { new TreeDumperNode("type", node.Type, null), new TreeDumperNode("isSuppressed", node.IsSuppressed, null), new TreeDumperNode("hasErrors", node.HasErrors, null) } ); - public override TreeDumperNode VisitIndexOrRangeIndexerPatternReceiverPlaceholder(BoundIndexOrRangeIndexerPatternReceiverPlaceholder node, object? arg) => new TreeDumperNode("indexOrRangeIndexerPatternReceiverPlaceholder", null, new TreeDumperNode[] + public override TreeDumperNode VisitImplicitIndexerReceiverPlaceholder(BoundImplicitIndexerReceiverPlaceholder node, object? arg) => new TreeDumperNode("implicitIndexerReceiverPlaceholder", null, new TreeDumperNode[] { new TreeDumperNode("valEscape", node.ValEscape, null), new TreeDumperNode("type", node.Type, null), @@ -16108,12 +16108,12 @@ private BoundTreeDumperNodeProducer() new TreeDumperNode("hasErrors", node.HasErrors, null) } ); - public override TreeDumperNode VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node, object? arg) => new TreeDumperNode("indexOrRangePatternIndexerAccess", null, new TreeDumperNode[] + public override TreeDumperNode VisitImplicitIndexerAccess(BoundImplicitIndexerAccess node, object? arg) => new TreeDumperNode("implicitIndexerAccess", null, new TreeDumperNode[] { new TreeDumperNode("argument", null, new TreeDumperNode[] { Visit(node.Argument, null) }), new TreeDumperNode("lengthOrCountAccess", null, new TreeDumperNode[] { Visit(node.LengthOrCountAccess, null) }), new TreeDumperNode("receiverPlaceholder", null, new TreeDumperNode[] { Visit(node.ReceiverPlaceholder, null) }), - new TreeDumperNode("indexerAccess", null, new TreeDumperNode[] { Visit(node.IndexerAccess, null) }), + new TreeDumperNode("indexerOrSliceAccess", null, new TreeDumperNode[] { Visit(node.IndexerOrSliceAccess, null) }), new TreeDumperNode("argumentPlaceholders", null, from x in node.ArgumentPlaceholders select Visit(x, null)), new TreeDumperNode("type", node.Type, null), new TreeDumperNode("isSuppressed", node.IsSuppressed, null), diff --git a/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs b/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs index 2f4b93cf5d24..c8f6b337365a 100644 --- a/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs +++ b/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs @@ -91,20 +91,20 @@ public override BoundNode VisitArrayAccess(BoundArrayAccess node) node.Indices.Length == 1 && node.Indices[0].Type!.SpecialType == SpecialType.None) { - Error(ErrorCode.ERR_ExpressionTreeContainsPatternIndexOrRangeIndexer, node); + Error(ErrorCode.ERR_ExpressionTreeContainsPatternImplicitIndexer, node); } return base.VisitArrayAccess(node); } - public override BoundNode VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node) + public override BoundNode VisitImplicitIndexerAccess(BoundImplicitIndexerAccess node) { if (_inExpressionLambda) { - Error(ErrorCode.ERR_ExpressionTreeContainsPatternIndexOrRangeIndexer, node); + Error(ErrorCode.ERR_ExpressionTreeContainsPatternImplicitIndexer, node); } - return base.VisitIndexOrRangePatternIndexerAccess(node); + return base.VisitImplicitIndexerAccess(node); } public override BoundNode VisitFromEndIndexExpression(BoundFromEndIndexExpression node) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.PatternLocalRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.PatternLocalRewriter.cs index 9e35f3eb68d4..1bb4ae224a45 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.PatternLocalRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.PatternLocalRewriter.cs @@ -252,7 +252,7 @@ void addArg(RefKind refKind, BoundExpression expression) // array[Index] var indexerAccess = e.IndexerAccess; - if (indexerAccess is BoundIndexOrRangePatternIndexerAccess implicitAccess) + if (indexerAccess is BoundImplicitIndexerAccess implicitAccess) { indexerAccess = implicitAccess.WithLengthOrCountAccess(_tempAllocator.GetTemp(e.LengthTemp)); } @@ -278,7 +278,7 @@ void addArg(RefKind refKind, BoundExpression expression) // array[Range] var indexerAccess = e.IndexerAccess; - if (indexerAccess is BoundIndexOrRangePatternIndexerAccess implicitAccess) + if (indexerAccess is BoundImplicitIndexerAccess implicitAccess) { indexerAccess = implicitAccess.WithLengthOrCountAccess(_tempAllocator.GetTemp(e.LengthTemp)); } diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs index 949e2963dbb0..a229e3b555a8 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs @@ -965,14 +965,14 @@ internal static bool CanBePassedByReference(BoundExpression expr) case BoundKind.IndexerAccess: return ((BoundIndexerAccess)expr).Indexer.RefKind != RefKind.None; - case BoundKind.IndexOrRangePatternIndexerAccess: - return CanBePassedByReference(((BoundIndexOrRangePatternIndexerAccess)expr).IndexerAccess); + case BoundKind.ImplicitIndexerAccess: + return CanBePassedByReference(((BoundImplicitIndexerAccess)expr).IndexerOrSliceAccess); - case BoundKind.IndexOrRangeIndexerPatternReceiverPlaceholder: + case BoundKind.ImplicitIndexerReceiverPlaceholder: // That placeholder is always replaced with a temp local return true; - case BoundKind.IndexOrRangeIndexerPatternValuePlaceholder: + case BoundKind.ImplicitIndexerValuePlaceholder: // Implicit Index or Range indexers only have by-value parameters: // this[int], Slice(int, int), Substring(int, int) return false; @@ -1070,13 +1070,13 @@ public static void Validate(BoundNode node) return null; } - public override BoundNode? VisitIndexOrRangeIndexerPatternValuePlaceholder(BoundIndexOrRangeIndexerPatternValuePlaceholder node) + public override BoundNode? VisitImplicitIndexerValuePlaceholder(BoundImplicitIndexerValuePlaceholder node) { Fail(node); return null; } - public override BoundNode? VisitIndexOrRangeIndexerPatternReceiverPlaceholder(BoundIndexOrRangeIndexerPatternReceiverPlaceholder node) + public override BoundNode? VisitImplicitIndexerReceiverPlaceholder(BoundImplicitIndexerReceiverPlaceholder node) { Fail(node); return null; diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs index fce8d6336746..cc988be9fa1c 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs @@ -34,9 +34,9 @@ private BoundExpression VisitAssignmentOperator(BoundAssignmentOperator node, bo loweredLeft = VisitIndexerAccess((BoundIndexerAccess)left, isLeftOfAssignment: true); break; - case BoundKind.IndexOrRangePatternIndexerAccess: - loweredLeft = VisitIndexOrRangePatternIndexerAccess( - (BoundIndexOrRangePatternIndexerAccess)left, + case BoundKind.ImplicitIndexerAccess: + loweredLeft = VisitImplicitIndexerAccess( + (BoundImplicitIndexerAccess)left, isLeftOfAssignment: true); break; diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs index 4b1106813186..bff709c7b4ed 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs @@ -405,7 +405,7 @@ private BoundIndexerAccess TransformIndexerAccess(BoundIndexerAccess indexerAcce } private BoundExpression TransformImplicitIndexerAccess( - BoundIndexOrRangePatternIndexerAccess indexerAccess, + BoundImplicitIndexerAccess indexerAccess, ArrayBuilder stores, ArrayBuilder temps, bool isDynamicAssignment) @@ -416,7 +416,7 @@ private BoundExpression TransformImplicitIndexerAccess( // the only thing we need to do is lift the stores and temps out of // the sequence, and use the final expression as the new argument - var access = VisitIndexOrRangePatternIndexerAccess(indexerAccess, isLeftOfAssignment: true); + var access = VisitImplicitIndexerAccess(indexerAccess, isLeftOfAssignment: true); if (access is BoundSequence sequence) { @@ -591,13 +591,13 @@ private BoundExpression TransformCompoundAssignmentLHS(BoundExpression originalL } break; - case BoundKind.IndexOrRangePatternIndexerAccess: + case BoundKind.ImplicitIndexerAccess: { - var implicitIndexerAccess = (BoundIndexOrRangePatternIndexerAccess)originalLHS; + var implicitIndexerAccess = (BoundImplicitIndexerAccess)originalLHS; Debug.Assert(implicitIndexerAccess.Argument.Type!.Equals(_compilation.GetWellKnownType(WellKnownType.System_Index)) || implicitIndexerAccess.Argument.Type!.Equals(_compilation.GetWellKnownType(WellKnownType.System_Range))); - if (implicitIndexerAccess.IndexerAccess.GetRefKind() == RefKind.None) + if (implicitIndexerAccess.IndexerOrSliceAccess.GetRefKind() == RefKind.None) { return TransformImplicitIndexerAccess(implicitIndexerAccess, stores, temps, isDynamicAssignment); } diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs index e9c304f0b97a..74256e10c766 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs @@ -183,22 +183,22 @@ private BoundExpression MakeIndexerAccess( throw ExceptionUtilities.Unreachable; } - public override BoundNode? VisitIndexOrRangeIndexerPatternReceiverPlaceholder(BoundIndexOrRangeIndexerPatternReceiverPlaceholder node) + public override BoundNode? VisitImplicitIndexerReceiverPlaceholder(BoundImplicitIndexerReceiverPlaceholder node) { return PlaceholderReplacement(node); } - public override BoundNode? VisitIndexOrRangeIndexerPatternValuePlaceholder(BoundIndexOrRangeIndexerPatternValuePlaceholder node) + public override BoundNode? VisitImplicitIndexerValuePlaceholder(BoundImplicitIndexerValuePlaceholder node) { return PlaceholderReplacement(node); } - public override BoundNode VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node) + public override BoundNode VisitImplicitIndexerAccess(BoundImplicitIndexerAccess node) { - return VisitIndexOrRangePatternIndexerAccess(node, isLeftOfAssignment: false); + return VisitImplicitIndexerAccess(node, isLeftOfAssignment: false); } - private BoundExpression VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node, bool isLeftOfAssignment) + private BoundExpression VisitImplicitIndexerAccess(BoundImplicitIndexerAccess node, bool isLeftOfAssignment) { if (TypeSymbol.Equals( node.Argument.Type, @@ -213,16 +213,16 @@ private BoundExpression VisitIndexOrRangePatternIndexerAccess(BoundIndexOrRangeP node.Argument.Type, _compilation.GetWellKnownType(WellKnownType.System_Range), TypeCompareKind.ConsiderEverything)); - Debug.Assert(!isLeftOfAssignment || node.IndexerAccess.GetRefKind() == RefKind.Ref); + Debug.Assert(!isLeftOfAssignment || node.IndexerOrSliceAccess.GetRefKind() == RefKind.Ref); return VisitRangePatternIndexerAccess(node); } } - private BoundExpression VisitIndexPatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node, bool isLeftOfAssignment) + private BoundExpression VisitIndexPatternIndexerAccess(BoundImplicitIndexerAccess node, bool isLeftOfAssignment) { Debug.Assert(node.ArgumentPlaceholders.Length == 1); - Debug.Assert(node.IndexerAccess is BoundIndexerAccess); + Debug.Assert(node.IndexerOrSliceAccess is BoundIndexerAccess); Debug.Assert(TypeSymbol.Equals( node.Argument.Type, @@ -282,7 +282,7 @@ private BoundExpression VisitIndexPatternIndexerAccess(BoundIndexOrRangePatternI throw ExceptionUtilities.UnexpectedValue(strategy); } - var indexerAccess = (BoundIndexerAccess)node.IndexerAccess; + var indexerAccess = (BoundIndexerAccess)node.IndexerOrSliceAccess; Debug.Assert(node.ArgumentPlaceholders.Length == 1); var argumentPlaceholder = node.ArgumentPlaceholders[0]; AddPlaceholderReplacement(argumentPlaceholder, integerArgument); @@ -432,10 +432,10 @@ private BoundExpression DetermineMakePatternIndexOffsetExpressionStrategy( } } - private BoundExpression VisitRangePatternIndexerAccess(BoundIndexOrRangePatternIndexerAccess node) + private BoundExpression VisitRangePatternIndexerAccess(BoundImplicitIndexerAccess node) { Debug.Assert(node.ArgumentPlaceholders.Length == 2); - Debug.Assert(node.IndexerAccess is BoundCall); + Debug.Assert(node.IndexerOrSliceAccess is BoundCall); Debug.Assert(TypeSymbol.Equals( node.Argument.Type, @@ -672,7 +672,7 @@ private BoundExpression VisitRangePatternIndexerAccess(BoundIndexOrRangePatternI AddPlaceholderReplacement(node.ArgumentPlaceholders[0], startExpr); AddPlaceholderReplacement(node.ArgumentPlaceholders[1], rangeSizeExpr); - var sliceCall = (BoundCall)node.IndexerAccess; + var sliceCall = (BoundCall)node.IndexerOrSliceAccess; var rewrittenIndexerAccess = VisitExpression(sliceCall.WithReceiver(receiver)); RemovePlaceholderReplacement(node.ArgumentPlaceholders[0]); @@ -684,7 +684,7 @@ private BoundExpression VisitRangePatternIndexerAccess(BoundIndexOrRangePatternI rewrittenIndexerAccess); } - private BoundExpression RewriteLengthAccess(BoundIndexOrRangePatternIndexerAccess node, BoundExpression receiver) + private BoundExpression RewriteLengthAccess(BoundImplicitIndexerAccess node, BoundExpression receiver) { var receiverPlaceholder = node.ReceiverPlaceholder; AddPlaceholderReplacement(receiverPlaceholder, receiver); diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index fefabc9cc18d..40bdcafe62ca 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -304,7 +304,7 @@ public CSharpOperationFactory(SemanticModel semanticModel) case BoundKind.StackAllocArrayCreation: case BoundKind.TypeExpression: case BoundKind.TypeOrValueExpression: - case BoundKind.IndexOrRangePatternIndexerAccess: + case BoundKind.ImplicitIndexerAccess: ConstantValue? constantValue = (boundNode as BoundExpression)?.ConstantValue; bool isImplicit = boundNode.WasCompilerGenerated; diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 42d21f1f8ce1..58d4b5275b7e 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -462,9 +462,9 @@ Strom výrazů nesmí obsahovat převod obslužné rutiny interpolovaného řetězce. - + An expression tree may not contain a pattern System.Index or System.Range indexer access - Strom výrazů možná neobsahuje vzor přístupu indexeru System.Index nebo System.Range. + An expression tree may not contain a pattern System.Index or System.Range indexer access diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 921085a9e063..bbeec52af850 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -462,9 +462,9 @@ Eine Ausdrucksbaumstruktur darf keine Handler-Konvertierung einer interpolierten Zeichenfolge enthalten. - + An expression tree may not contain a pattern System.Index or System.Range indexer access - Eine Ausdrucksbaumstruktur darf keinen System.Index- oder System.Range-Musterindexerzugriff enthalten. + An expression tree may not contain a pattern System.Index or System.Range indexer access diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index bddfacb8361c..e17bac038c14 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -462,9 +462,9 @@ Un árbol de (la) expresión no puede contener una conversión de controlador de cadena interpolada. - + An expression tree may not contain a pattern System.Index or System.Range indexer access - Un árbol de expresión no puede contener un patrón System.Index o un acceso a indizador System.Range. + An expression tree may not contain a pattern System.Index or System.Range indexer access diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index dbddc7e275af..3c1a7323e1e3 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -462,9 +462,9 @@ Une arborescence de l’expression ne peut pas contenir une conversion de gestionnaire de chaîne interpolée. - + An expression tree may not contain a pattern System.Index or System.Range indexer access - Une arborescence de l'expression ne peut pas contenir de modèle d'accès à l'indexeur System.Index ou System.Range + An expression tree may not contain a pattern System.Index or System.Range indexer access diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 1fbfca36fb9c..7254c4dc7cfc 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -462,9 +462,9 @@ Un albero delle espressioni non può contenere una conversione del gestore di stringhe interpolate. - + An expression tree may not contain a pattern System.Index or System.Range indexer access - Un albero delle espressioni non può contenere un accesso a indicizzatore System.Index o System.Range di criterio + An expression tree may not contain a pattern System.Index or System.Range indexer access diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index d87ef54ade11..a3333ff6a3b0 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -462,9 +462,9 @@ 式ツリーには、補間された文字列ハンドラー変換を含めることはできません。 - + An expression tree may not contain a pattern System.Index or System.Range indexer access - 式ツリーに、System.Index または System.Range インデクサー アクセスのパターンを含めることはできません + An expression tree may not contain a pattern System.Index or System.Range indexer access diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index cec232c894b2..3c378cc72993 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -462,9 +462,9 @@ 식 트리에는 보간된 문자열 처리기 변환이 포함될 수 없습니다. - + An expression tree may not contain a pattern System.Index or System.Range indexer access - 식 트리에는 System.Index 또는 System.Range 패턴의 인덱서 액세스를 포함할 수 없습니다. + An expression tree may not contain a pattern System.Index or System.Range indexer access diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 8153770850ab..f464377ad0ef 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -462,9 +462,9 @@ Drzewo wyrażenia nie może zawierać konwersji procedury obsługi ciągu interpolowanego. - + An expression tree may not contain a pattern System.Index or System.Range indexer access - Drzewo wyrażenia nie może zawierać dostępu do indeksatora z wzorcem System.Index lub System.Range + An expression tree may not contain a pattern System.Index or System.Range indexer access diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 23ec05803cd4..12929320d290 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -462,9 +462,9 @@ Uma árvore de expressão pode não conter uma conversão de manipulador de cadeia de caracteres interpolada. - + An expression tree may not contain a pattern System.Index or System.Range indexer access - Uma árvore de expressão não pode conter um padrão System.Index ou acesso do indexador System.Range + An expression tree may not contain a pattern System.Index or System.Range indexer access diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 21ccda40aa5b..411f8ec8d2c4 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -462,9 +462,9 @@ Дерево выражения не может содержать преобразование обработчика интерполированных строк. - + An expression tree may not contain a pattern System.Index or System.Range indexer access - Дерево выражения не может содержать доступ к индексатору System.Index или System.Range шаблона. + An expression tree may not contain a pattern System.Index or System.Range indexer access diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 9d3463c9abac..ab0f6af30f93 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -462,9 +462,9 @@ Bir ifade ağacı, düz metin arasına kod eklenmiş dize işleyicisi dönüşümü içeremez. - + An expression tree may not contain a pattern System.Index or System.Range indexer access - İfade ağacı, desen System.Index veya System.Range dizin oluşturucu erişimi içeremez + An expression tree may not contain a pattern System.Index or System.Range indexer access diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 6c96b4ed6982..693c13a71cd4 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -462,9 +462,9 @@ 表达式树可能不包含内插字符串处理程序转换。 - + An expression tree may not contain a pattern System.Index or System.Range indexer access - 表达式树不能包含模式 System.Index 或 System.Range 索引器访问 + An expression tree may not contain a pattern System.Index or System.Range indexer access diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index ad2e0abc1c31..70686e09b9e9 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -462,9 +462,9 @@ 運算式樹狀架構不可包含差補字串處理常式轉換。 - + An expression tree may not contain a pattern System.Index or System.Range indexer access - 運算式樹狀架構不可包含 System.Index 或 System.Range 索引子存取模式 + An expression tree may not contain a pattern System.Index or System.Range indexer access diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/IndexAndRangeTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/IndexAndRangeTests.cs index d0e9bd5c84b3..d8ae19764645 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/IndexAndRangeTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/IndexAndRangeTests.cs @@ -72,16 +72,16 @@ static void Main() comp.VerifyEmitDiagnostics( // (16,55): error CS8790: An expression tree may not contain a pattern System.Index or System.Range indexer access // Expression> e = (int[] a) => a[new Index(0, true)]; // 1 - Diagnostic(ErrorCode.ERR_ExpressionTreeContainsPatternIndexOrRangeIndexer, "a[new Index(0, true)]").WithLocation(16, 55), + Diagnostic(ErrorCode.ERR_ExpressionTreeContainsPatternImplicitIndexer, "a[new Index(0, true)]").WithLocation(16, 55), // (17,64): error CS8790: An expression tree may not contain a pattern System.Index or System.Range indexer access // Expression, int>> e2 = (List a) => a[new Index(0, true)]; // 2 - Diagnostic(ErrorCode.ERR_ExpressionTreeContainsPatternIndexOrRangeIndexer, "a[new Index(0, true)]").WithLocation(17, 64), + Diagnostic(ErrorCode.ERR_ExpressionTreeContainsPatternImplicitIndexer, "a[new Index(0, true)]").WithLocation(17, 64), // (19,58): error CS8790: An expression tree may not contain a pattern System.Index or System.Range indexer access // Expression> e3 = (int[] a) => a[new Range(0, 1)]; // 3 - Diagnostic(ErrorCode.ERR_ExpressionTreeContainsPatternIndexOrRangeIndexer, "a[new Range(0, 1)]").WithLocation(19, 58), + Diagnostic(ErrorCode.ERR_ExpressionTreeContainsPatternImplicitIndexer, "a[new Range(0, 1)]").WithLocation(19, 58), // (20,46): error CS8790: An expression tree may not contain a pattern System.Index or System.Range indexer access // Expression> e4 = (S s) => s[new Range(0, 1)]; // 4 - Diagnostic(ErrorCode.ERR_ExpressionTreeContainsPatternIndexOrRangeIndexer, "s[new Range(0, 1)]").WithLocation(20, 46) + Diagnostic(ErrorCode.ERR_ExpressionTreeContainsPatternImplicitIndexer, "s[new Range(0, 1)]").WithLocation(20, 46) ); }