Binder_Patterns.cs 52.8 KB
Newer Older
1 2 3 4
// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.

using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
5
using Roslyn.Utilities;
6
using System.Collections.Generic;
7
using System.Collections.Immutable;
8
using System.Diagnostics;
9 10
using System;
using Microsoft.CodeAnalysis.PooledObjects;
11 12 13 14 15 16 17

namespace Microsoft.CodeAnalysis.CSharp
{
    partial class Binder
    {
        private BoundExpression BindIsPatternExpression(IsPatternExpressionSyntax node, DiagnosticBag diagnostics)
        {
18 19 20
            BoundExpression expression = BindValue(node.Expression, diagnostics, BindValueKind.RValue);
            bool hasErrors = IsOperandErrors(node, ref expression, diagnostics);
            TypeSymbol expressionType = expression.Type;
21
            if ((object)expressionType == null || expressionType.SpecialType == SpecialType.System_Void)
22 23 24 25
            {
                if (!hasErrors)
                {
                    // value expected
26
                    diagnostics.Add(ErrorCode.ERR_BadPatternExpression, node.Expression.Location, expression.Display);
27 28
                    hasErrors = true;
                }
29 30

                expression = BadExpression(expression.Syntax, expression);
31 32
            }

33 34
            uint inputValEscape = GetValEscape(expression, LocalScopeDepth);
            BoundPattern pattern = BindPattern(node.Pattern, expression.Type, inputValEscape, hasErrors, diagnostics);
35 36 37 38 39 40
            hasErrors |= pattern.HasErrors;
            return MakeIsPatternExpression(
                node, expression, pattern, GetSpecialType(SpecialType.System_Boolean, diagnostics, node),
                hasErrors, diagnostics);
        }

41 42 43 44 45 46 47
        private BoundExpression MakeIsPatternExpression(
            SyntaxNode node,
            BoundExpression expression,
            BoundPattern pattern,
            TypeSymbol boolType,
            bool hasErrors,
            DiagnosticBag diagnostics)
48 49 50 51 52 53 54 55 56 57
        {
            // Note that these labels are for the convenience of the compilation of patterns, and are not actually emitted into the lowered code.
            LabelSymbol whenTrueLabel = new GeneratedLabelSymbol("isPatternSuccess");
            LabelSymbol whenFalseLabel = new GeneratedLabelSymbol("isPatternFailure");
            BoundDecisionDag decisionDag = DecisionDagBuilder.CreateDecisionDagForIsPattern(
                this.Compilation, pattern.Syntax, expression, pattern, whenTrueLabel: whenTrueLabel, whenFalseLabel: whenFalseLabel, diagnostics);
            if (!hasErrors && !decisionDag.ReachableLabels.Contains(whenTrueLabel))
            {
                diagnostics.Add(ErrorCode.ERR_IsPatternImpossible, node.Location, expression.Type);
                hasErrors = true;
58 59
            }

60
            if (expression.ConstantValue != null)
61
            {
62
                decisionDag = decisionDag.SimplifyDecisionDagIfConstantInput(expression);
63 64 65 66 67 68 69 70 71 72 73
                if (!hasErrors)
                {
                    if (!decisionDag.ReachableLabels.Contains(whenTrueLabel))
                    {
                        diagnostics.Add(ErrorCode.WRN_GivenExpressionNeverMatchesPattern, node.Location);
                    }
                    else if (!decisionDag.ReachableLabels.Contains(whenFalseLabel) && pattern.Kind == BoundKind.ConstantPattern)
                    {
                        diagnostics.Add(ErrorCode.WRN_GivenExpressionAlwaysMatchesConstant, node.Location);
                    }
                }
74 75
            }

76 77
            return new BoundIsPatternExpression(
                node, expression, pattern, decisionDag, whenTrueLabel: whenTrueLabel, whenFalseLabel: whenFalseLabel, boolType, hasErrors);
78 79
        }

80 81
        private BoundExpression BindSwitchExpression(SwitchExpressionSyntax node, DiagnosticBag diagnostics)
        {
82 83 84 85 86
            Debug.Assert(node != null);
            Binder switchBinder = this.GetBinder(node);
            return switchBinder.BindSwitchExpressionCore(node, switchBinder, diagnostics);
        }

87 88 89 90
        internal virtual BoundExpression BindSwitchExpressionCore(
            SwitchExpressionSyntax node,
            Binder originalBinder,
            DiagnosticBag diagnostics)
91 92
        {
            return this.Next.BindSwitchExpressionCore(node, originalBinder, diagnostics);
93 94
        }

95 96
        internal BoundPattern BindPattern(
            PatternSyntax node,
97
            TypeSymbol inputType,
98
            uint inputValEscape,
99
            bool hasErrors,
100
            DiagnosticBag diagnostics)
101 102 103
        {
            switch (node.Kind())
            {
104
                case SyntaxKind.DiscardPattern:
105
                    return BindDiscardPattern((DiscardPatternSyntax)node, inputType);
106

107
                case SyntaxKind.DeclarationPattern:
108
                    return BindDeclarationPattern((DeclarationPatternSyntax)node, inputType, inputValEscape, hasErrors, diagnostics);
109

110
                case SyntaxKind.ConstantPattern:
111
                    return BindConstantPattern((ConstantPatternSyntax)node, inputType, hasErrors, diagnostics);
112

113
                case SyntaxKind.RecursivePattern:
114
                    return BindRecursivePattern((RecursivePatternSyntax)node, inputType, inputValEscape, hasErrors, diagnostics);
115

116
                case SyntaxKind.VarPattern:
117
                    return BindVarPattern((VarPatternSyntax)node, inputType, inputValEscape, hasErrors, diagnostics);
118

119
                default:
120
                    throw ExceptionUtilities.UnexpectedValue(node.Kind());
121 122
            }
        }
123

124
        private BoundPattern BindDiscardPattern(DiscardPatternSyntax node, TypeSymbol inputType)
125
        {
126
            return new BoundDiscardPattern(node, inputType);
127 128
        }

129
        private BoundConstantPattern BindConstantPattern(
130
            ConstantPatternSyntax node,
131
            TypeSymbol inputType,
132
            bool hasErrors,
133
            DiagnosticBag diagnostics)
134
        {
135
            SyntaxNode innerExpression = node.Expression.SkipParens();
136 137 138 139 140 141
            if (innerExpression.Kind() == SyntaxKind.DefaultLiteralExpression)
            {
                diagnostics.Add(ErrorCode.ERR_DefaultPattern, innerExpression.Location);
                hasErrors = true;
            }

142
            return BindConstantPattern(node, inputType, node.Expression, hasErrors, diagnostics, out _);
143 144
        }

145
        internal BoundConstantPattern BindConstantPattern(
146
            SyntaxNode node,
147
            TypeSymbol inputType,
148
            ExpressionSyntax patternExpression,
149 150
            bool hasErrors,
            DiagnosticBag diagnostics,
151
            out bool wasExpression)
152
        {
153
            BoundExpression expression = BindValue(patternExpression, diagnostics, BindValueKind.RValue);
154
            ConstantValue constantValueOpt = null;
155 156
            BoundExpression convertedExpression = ConvertPatternExpression(
                inputType, patternExpression, expression, out constantValueOpt, hasErrors, diagnostics);
157
            wasExpression = expression.Type?.IsErrorType() != true;
158
            if (!convertedExpression.HasErrors && constantValueOpt == null)
159
            {
160
                diagnostics.Add(ErrorCode.ERR_ConstantExpected, patternExpression.Location);
N
Neal Gafter 已提交
161 162
                hasErrors = true;
            }
163

N
Neal Gafter 已提交
164
            if (convertedExpression.Type is null && constantValueOpt != ConstantValue.Null)
165 166 167
            {
                Debug.Assert(hasErrors);
                convertedExpression = new BoundConversion(
168 169
                    convertedExpression.Syntax, convertedExpression, Conversion.NoConversion, isBaseConversion: false, @checked: false,
                    explicitCastInCode: false, constantValueOpt: constantValueOpt, conversionGroupOpt: default, type: CreateErrorType(), hasErrors: true)
170
                    { WasCompilerGenerated = true };
171 172
            }

173
            return new BoundConstantPattern(node, convertedExpression, constantValueOpt ?? ConstantValue.Bad, inputType, hasErrors);
174 175
        }

176 177 178 179 180 181 182
        internal BoundExpression ConvertPatternExpression(
            TypeSymbol inputType,
            CSharpSyntaxNode node,
            BoundExpression expression,
            out ConstantValue constantValue,
            bool hasErrors,
            DiagnosticBag diagnostics)
183
        {
184
            BoundExpression convertedExpression;
185 186 187

            // If we are pattern-matching against an open type, we do not convert the constant to the type of the input.
            // This permits us to match a value of type `IComparable<T>` with a pattern of type `int`.
188 189
            bool inputContainsTypeParameter = inputType.ContainsTypeParameter();
            if (inputContainsTypeParameter)
190
            {
191
                convertedExpression = expression;
192
                if (!hasErrors)
193
                {
194
                    HashSet<DiagnosticInfo> useSiteDiagnostics = null;
195
                    if (expression.ConstantValue == ConstantValue.Null)
196
                    {
197 198 199 200 201 202 203 204 205
                        if (inputType.IsNonNullableValueType())
                        {
                            // We do not permit matching null against a struct type.
                            diagnostics.Add(ErrorCode.ERR_ValueCantBeNull, expression.Syntax.Location, inputType);
                        }
                    }
                    else if (ExpressionOfTypeMatchesPatternType(Conversions, inputType, expression.Type, ref useSiteDiagnostics, out _, operandConstantValue: null) == false)
                    {
                        diagnostics.Add(ErrorCode.ERR_PatternWrongType, expression.Syntax.Location, inputType, expression.Display);
206 207
                    }

208 209
                    diagnostics.Add(node, useSiteDiagnostics);
                }
210 211 212
            }
            else
            {
213 214 215
                // This will allow user-defined conversions, even though they're not permitted here.  This is acceptable
                // because the result of a user-defined conversion does not have a ConstantValue. A constant pattern
                // requires a constant value so we'll report a diagnostic to that effect later.
216 217 218
                convertedExpression = GenerateConversionForAssignment(inputType, expression, diagnostics);

                if (convertedExpression.Kind == BoundKind.Conversion)
219
                {
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
                    var conversion = (BoundConversion)convertedExpression;
                    BoundExpression operand = conversion.Operand;
                    if (inputType.IsNullableType() && (convertedExpression.ConstantValue == null || !convertedExpression.ConstantValue.IsNull))
                    {
                        // Null is a special case here because we want to compare null to the Nullable<T> itself, not to the underlying type.
                        var discardedDiagnostics = DiagnosticBag.GetInstance(); // We are not intested in the diagnostic that get created here
                        convertedExpression = CreateConversion(operand, inputType.GetNullableUnderlyingType(), discardedDiagnostics);
                        discardedDiagnostics.Free();
                    }
                    else if ((conversion.ConversionKind == ConversionKind.Boxing || conversion.ConversionKind == ConversionKind.ImplicitReference)
                        && operand.ConstantValue != null && convertedExpression.ConstantValue == null)
                    {
                        // A boxed constant (or string converted to object) is a special case because we prefer
                        // to compare to the pre-converted value by casting the input value to the type of the constant
                        // (that is, unboxing or downcasting it) and then testing the resulting value using primitives.
                        // That is much more efficient than calling object.Equals(x, y), and we can share the downcasted
                        // input value among many constant tests.
                        convertedExpression = operand;
                    }
239 240
                    else if (conversion.ConversionKind == ConversionKind.NullToPointer ||
                        (conversion.ConversionKind == ConversionKind.NoConversion && convertedExpression.Type?.IsErrorType() == true))
241 242 243
                    {
                        convertedExpression = operand;
                    }
244
                }
245 246
            }

G
gafter 已提交
247 248
            constantValue = convertedExpression.ConstantValue;
            return convertedExpression;
249 250
        }

251 252 253
        /// <summary>
        /// Check that the pattern type is valid for the operand. Return true if an error was reported.
        /// </summary>
254
        private bool CheckValidPatternType(
255
            SyntaxNode typeSyntax,
256
            TypeSymbol inputType,
257
            TypeSymbol patternType,
258
            bool patternTypeWasInSource,
259
            DiagnosticBag diagnostics)
260
        {
261
            Debug.Assert((object)inputType != null);
262
            Debug.Assert((object)patternType != null);
263

264
            if (inputType.IsErrorType() || patternType.IsErrorType())
265 266 267
            {
                return false;
            }
268 269 270 271 272 273
            else if (inputType.TypeKind == TypeKind.Pointer || patternType.TypeKind == TypeKind.Pointer)
            {
                // pattern-matching is not permitted for pointer types
                diagnostics.Add(ErrorCode.ERR_PointerTypeInPatternMatching, typeSyntax.Location);
                return true;
            }
274
            else if (patternType.IsNullableType() && patternTypeWasInSource)
275 276
            {
                // It is an error to use pattern-matching with a nullable type, because you'll never get null. Use the underlying type.
277 278
                Error(diagnostics, ErrorCode.ERR_PatternNullableType, typeSyntax, patternType, patternType.GetNullableUnderlyingType());
                return true;
279
            }
280 281 282 283 284
            else if (patternType.IsStatic)
            {
                Error(diagnostics, ErrorCode.ERR_VarDeclIsStaticClass, typeSyntax, patternType);
                return true;
            }
285
            else
286
            {
287 288 289 290 291 292
                if (patternType.IsDynamic())
                {
                    Error(diagnostics, ErrorCode.ERR_PatternDynamicType, typeSyntax);
                    return true;
                }

293
                HashSet<DiagnosticInfo> useSiteDiagnostics = null;
294 295
                bool? matchPossible = ExpressionOfTypeMatchesPatternType(
                    Conversions, inputType, patternType, ref useSiteDiagnostics, out Conversion conversion, operandConstantValue: null, operandCouldBeNull: true);
296
                diagnostics.Add(typeSyntax, useSiteDiagnostics);
297
                if (matchPossible != false)
298
                {
299
                    if (!conversion.Exists && (inputType.ContainsTypeParameter() || patternType.ContainsTypeParameter()))
300 301 302 303
                    {
                        // permit pattern-matching when one of the types is an open type in C# 7.1.
                        LanguageVersion requiredVersion = MessageID.IDS_FeatureGenericPatternMatching.RequiredVersion();
                        if (requiredVersion > Compilation.LanguageVersion)
304
                        {
305
                            Error(diagnostics, ErrorCode.ERR_PatternWrongGenericTypeInVersion, typeSyntax,
306
                                inputType, patternType,
307 308
                                Compilation.LanguageVersion.ToDisplayString(),
                                new CSharpRequiredLanguageVersion(requiredVersion));
309 310
                            return true;
                        }
311 312 313 314
                    }
                }
                else
                {
315
                    Error(diagnostics, ErrorCode.ERR_PatternWrongType, typeSyntax, inputType, patternType);
316
                    return true;
317 318
                }
            }
N
Neal Gafter 已提交
319

320 321 322
            return false;
        }

323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344
        /// <summary>
        /// Does an expression of type <paramref name="expressionType"/> "match" a pattern that looks for
        /// type <paramref name="patternType"/>?
        /// 'true' if the matched type catches all of them, 'false' if it catches none of them, and
        /// 'null' if it might catch some of them.
        /// </summary>
        internal static bool? ExpressionOfTypeMatchesPatternType(
            Conversions conversions,
            TypeSymbol expressionType,
            TypeSymbol patternType,
            ref HashSet<DiagnosticInfo> useSiteDiagnostics,
            out Conversion conversion,
            ConstantValue operandConstantValue = null,
            bool operandCouldBeNull = false)
        {
            Debug.Assert((object)expressionType != null);
            if (expressionType.IsDynamic())
            {
                // if operand is the dynamic type, we do the same thing as though it were object
                expressionType = conversions.CorLibrary.GetSpecialType(SpecialType.System_Object);
            }

345
            conversion = conversions.ClassifyBuiltInConversion(expressionType, patternType, ref useSiteDiagnostics);
346
            ConstantValue result = Binder.GetIsOperatorConstantResult(expressionType, patternType, conversion.Kind, operandConstantValue, operandCouldBeNull);
347 348 349 350 351 352 353
            return
                (result == null) ? (bool?)null :
                (result == ConstantValue.True) ? true :
                (result == ConstantValue.False) ? false :
                throw ExceptionUtilities.UnexpectedValue(result);
        }

354
        private BoundPattern BindDeclarationPattern(
355
            DeclarationPatternSyntax node,
356
            TypeSymbol inputType,
357
            uint inputValEscape,
358 359
            bool hasErrors,
            DiagnosticBag diagnostics)
360
        {
361
            TypeSyntax typeSyntax = node.Type;
362
            BoundTypeExpression boundDeclType = BindPatternType(typeSyntax, inputType, diagnostics, ref hasErrors);
363
            TypeSymbol declType = boundDeclType.Type;
364
            inputValEscape = GetValEscape(declType, inputValEscape);
365 366 367
            BindPatternDesignation(
                node.Designation, boundDeclType.Type, inputValEscape, typeSyntax, diagnostics,
                ref hasErrors, out Symbol variableSymbol, out BoundExpression variableAccess);
368
            return new BoundDeclarationPattern(node, variableSymbol, variableAccess, boundDeclType, isVar: false, inputType, hasErrors);
369 370 371 372
        }

        private BoundTypeExpression BindPatternType(
            TypeSyntax typeSyntax,
373
            TypeSymbol inputType,
374
            DiagnosticBag diagnostics,
375
            ref bool hasErrors)
376
        {
377
            Debug.Assert(inputType != (object)null);
378
            Debug.Assert(!typeSyntax.IsVar); // if the syntax had `var`, it would have been parsed as a var pattern.
379
            TypeSymbolWithAnnotations declType = BindType(typeSyntax, diagnostics, out AliasSymbol aliasOpt);
380 381 382
            Debug.Assert(!declType.IsNull);
            BoundTypeExpression boundDeclType = new BoundTypeExpression(typeSyntax, aliasOpt, inferredType: false, type: declType.TypeSymbol);
            hasErrors |= CheckValidPatternType(typeSyntax, inputType, declType.TypeSymbol, patternTypeWasInSource: true, diagnostics: diagnostics);
383 384 385 386 387 388
            return boundDeclType;
        }

        private void BindPatternDesignation(
            VariableDesignationSyntax designation,
            TypeSymbol declType,
389
            uint inputValEscape,
390 391 392 393 394 395 396
            TypeSyntax typeSyntax,
            DiagnosticBag diagnostics,
            ref bool hasErrors,
            out Symbol variableSymbol,
            out BoundExpression variableAccess)
        {
            switch (designation)
397
            {
398
                case SingleVariableDesignationSyntax singleVariableDesignation:
399
                    SyntaxToken identifier = singleVariableDesignation.Identifier;
400 401
                    SourceLocalSymbol localSymbol = this.LookupLocal(identifier);

402 403 404
                    if (localSymbol != (object)null)
                    {
                        if ((InConstructorInitializer || InFieldInitializer) && ContainingMemberOrLambda.ContainingSymbol.Kind == SymbolKind.NamedType)
405
                            CheckFeatureAvailability(designation, MessageID.IDS_FeatureExpressionVariablesInQueriesAndInitializers, diagnostics);
406

407
                        localSymbol.SetType(TypeSymbolWithAnnotations.Create(declType));
408
                        localSymbol.SetValEscape(GetValEscape(declType, inputValEscape));
409 410 411 412 413 414 415 416

                        // Check for variable declaration errors.
                        hasErrors |= localSymbol.ScopeBinder.ValidateDeclarationNameConflictsInScope(localSymbol, diagnostics);

                        if (!hasErrors)
                            hasErrors = CheckRestrictedTypeInAsync(this.ContainingMemberOrLambda, declType, diagnostics, typeSyntax ?? (SyntaxNode)designation);

                        variableSymbol = localSymbol;
417
                        variableAccess = new BoundLocal(
418
                            syntax: designation, localSymbol: localSymbol, constantValueOpt: null, type: declType);
419 420 421 422 423
                        return;
                    }
                    else
                    {
                        // We should have the right binder in the chain for a script or interactive, so we use the field for the pattern.
424
                        Debug.Assert(designation.SyntaxTree.Options.Kind != SourceCodeKind.Regular);
425 426
                        GlobalExpressionVariable expressionVariableField = LookupDeclaredField(singleVariableDesignation);
                        DiagnosticBag tempDiagnostics = DiagnosticBag.GetInstance();
427
                        expressionVariableField.SetType(TypeSymbolWithAnnotations.Create(declType), tempDiagnostics);
428
                        tempDiagnostics.Free();
429
                        BoundExpression receiver = SynthesizeReceiver(designation, expressionVariableField, diagnostics);
430 431

                        variableSymbol = expressionVariableField;
432
                        variableAccess = new BoundFieldAccess(
433
                            syntax: designation, receiver: receiver, fieldSymbol: expressionVariableField, constantValueOpt: null, hasErrors: hasErrors);
434 435 436 437 438 439 440
                        return;
                    }
                case DiscardDesignationSyntax _:
                case null:
                    variableSymbol = null;
                    variableAccess = null;
                    return;
441
                default:
442
                    throw ExceptionUtilities.UnexpectedValue(designation.Kind());
443
            }
444
        }
445

446 447 448 449 450 451 452 453 454 455
        /// <summary>
        /// Compute the val escape of an expression of the given <paramref name="type"/>, which is known to be derived
        /// from an expression whose escape scope is <paramref name="possibleValEscape"/>. By the language rules, the
        /// result is either that same scope (if the type is a ref struct type) or <see cref="Binder.ExternalScope"/>.
        /// </summary>
        private static uint GetValEscape(TypeSymbol type, uint possibleValEscape)
        {
            return type.IsByRefLikeType ? possibleValEscape : Binder.ExternalScope;
        }

456 457 458 459 460 461
        TypeSymbol BindRecursivePatternType(
            TypeSyntax typeSyntax,
            TypeSymbol inputType,
            DiagnosticBag diagnostics,
            ref bool hasErrors,
            out BoundTypeExpression boundDeclType)
462 463
        {
            if (typeSyntax != null)
464
            {
465
                boundDeclType = BindPatternType(typeSyntax, inputType, diagnostics, ref hasErrors);
466 467 468 469 470
                return boundDeclType.Type;
            }
            else
            {
                boundDeclType = null;
471
                return inputType.StrippedType(); // remove the nullable part of the input's type
472 473
            }
        }
N
Neal Gafter 已提交
474

475 476 477 478 479 480 481 482 483 484
        // Work around https://github.com/dotnet/roslyn/issues/20648: The compiler's internal APIs such as `declType.IsTupleType`
        // do not correctly treat the non-generic struct `System.ValueTuple` as a tuple type.  We explicitly perform the tests
        // required to identify it.  When that bug is fixed we should be able to remove this code and its callers.
        internal static bool IsZeroElementTupleType(TypeSymbol type)
        {
            return type.IsStructType() && type.Name == "ValueTuple" && type.GetArity() == 0 &&
                type.ContainingSymbol is var declContainer && declContainer.Kind == SymbolKind.Namespace && declContainer.Name == "System" &&
                (declContainer.ContainingSymbol as NamespaceSymbol)?.IsGlobalNamespace == true;
        }

485
        private BoundPattern BindRecursivePattern(RecursivePatternSyntax node, TypeSymbol inputType, uint inputValEscape, bool hasErrors, DiagnosticBag diagnostics)
486
        {
487 488 489 490 491 492 493
            if (inputType.IsPointerType())
            {
                diagnostics.Add(ErrorCode.ERR_PointerTypeInPatternMatching, node.Location);
                hasErrors = true;
                inputType = CreateErrorType();
            }

494
            TypeSyntax typeSyntax = node.Type;
495
            TypeSymbol declType = BindRecursivePatternType(typeSyntax, inputType, diagnostics, ref hasErrors, out BoundTypeExpression boundDeclType);
496
            inputValEscape = GetValEscape(declType, inputValEscape);
497

498
            MethodSymbol deconstructMethod = null;
499
            ImmutableArray<BoundSubpattern> deconstructionSubpatterns = default;
500
            if (node.DeconstructionPatternClause != null)
501
            {
502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545
                DeconstructionPatternClauseSyntax deconstructClause = node.DeconstructionPatternClause;
                var patternsBuilder = ArrayBuilder<BoundSubpattern>.GetInstance(deconstructClause.Subpatterns.Count);
                if (IsZeroElementTupleType(declType))
                {
                    // Work around https://github.com/dotnet/roslyn/issues/20648: The compiler's internal APIs such as `declType.IsTupleType`
                    // do not correctly treat the non-generic struct `System.ValueTuple` as a tuple type.  We explicitly perform the tests
                    // required to identify it.  When that bug is fixed we should be able to remove this if statement.
                    BindValueTupleSubpatterns(
                        deconstructClause, declType, ImmutableArray<TypeSymbolWithAnnotations>.Empty, inputValEscape, ref hasErrors, patternsBuilder, diagnostics);
                }
                else if (declType.IsTupleType)
                {
                    // It is a tuple type. Work according to its elements
                    BindValueTupleSubpatterns(deconstructClause, declType, declType.TupleElementTypes, inputValEscape, ref hasErrors, patternsBuilder, diagnostics);
                }
                else
                {
                    // It is not a tuple type. Seek an appropriate Deconstruct method.
                    var inputPlaceholder = new BoundImplicitReceiver(deconstructClause, declType); // A fake receiver expression to permit us to reuse binding logic
                    var deconstructDiagnostics = DiagnosticBag.GetInstance();
                    BoundExpression deconstruct = MakeDeconstructInvocationExpression(
                        deconstructClause.Subpatterns.Count, inputPlaceholder, deconstructClause,
                        deconstructDiagnostics, outPlaceholders: out ImmutableArray<BoundDeconstructValuePlaceholder> outPlaceholders,
                        out bool anyDeconstructCandidates);
                    if (!anyDeconstructCandidates &&
                        ShouldUseITupleForRecursivePattern(node, declType, diagnostics, out var iTupleType, out var iTupleGetLength, out var iTupleGetItem))
                    {
                        // There was no Deconstruct, but the constraints for the use of ITuple are satisfied.
                        // Use that and forget any errors from trying to bind Deconstruct.
                        deconstructDiagnostics.Free();
                        BindITupleSubpatterns(deconstructClause, patternsBuilder, diagnostics);
                        deconstructionSubpatterns = patternsBuilder.ToImmutableAndFree();
                        return new BoundITuplePattern(node, iTupleGetLength, iTupleGetItem, deconstructionSubpatterns, inputType, hasErrors);
                    }
                    else
                    {
                        diagnostics.AddRangeAndFree(deconstructDiagnostics);
                    }

                    deconstructMethod = BindDeconstructSubpatterns(
                        deconstructClause, inputValEscape, deconstruct, outPlaceholders, patternsBuilder, ref hasErrors, diagnostics);
                }

                deconstructionSubpatterns = patternsBuilder.ToImmutableAndFree();
546 547
            }

548 549
            ImmutableArray<BoundSubpattern> properties = default;
            if (node.PropertyPatternClause != null)
550
            {
551
                properties = BindPropertyPatternClause(node.PropertyPatternClause, declType, inputValEscape, diagnostics, ref hasErrors);
552 553
            }

554 555 556
            BindPatternDesignation(
                node.Designation, declType, inputValEscape, typeSyntax, diagnostics,
                ref hasErrors, out Symbol variableSymbol, out BoundExpression variableAccess);
557
            return new BoundRecursivePattern(
558
                syntax: node, declaredType: boundDeclType, inputType: inputType, deconstructMethod: deconstructMethod,
559 560
                deconstruction: deconstructionSubpatterns, properties: properties, variable: variableSymbol,
                variableAccess: variableAccess, hasErrors: hasErrors);
561 562
        }

563 564 565 566 567 568 569
        private MethodSymbol BindDeconstructSubpatterns(
            DeconstructionPatternClauseSyntax node,
            uint inputValEscape,
            BoundExpression deconstruct,
            ImmutableArray<BoundDeconstructValuePlaceholder> outPlaceholders,
            ArrayBuilder<BoundSubpattern> patterns,
            ref bool hasErrors,
570 571
            DiagnosticBag diagnostics)
        {
572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612
            var deconstructMethod = deconstruct.ExpressionSymbol as MethodSymbol;
            if (deconstructMethod is null)
                hasErrors = true;

            int skippedExtensionParameters = deconstructMethod?.IsExtensionMethod == true ? 1 : 0;
            for (int i = 0; i < node.Subpatterns.Count; i++)
            {
                var subPattern = node.Subpatterns[i];
                bool isError = outPlaceholders.IsDefaultOrEmpty || i >= outPlaceholders.Length;
                TypeSymbol elementType = isError ? CreateErrorType() : outPlaceholders[i].Type;
                ParameterSymbol parameter = null;
                if (subPattern.NameColon != null && !isError)
                {
                    // Check that the given name is the same as the corresponding parameter of the method.
                    string name = subPattern.NameColon.Name.Identifier.ValueText;
                    int parameterIndex = i + skippedExtensionParameters;
                    if (parameterIndex < deconstructMethod.ParameterCount)
                    {
                        parameter = deconstructMethod.Parameters[parameterIndex];
                        string parameterName = parameter.Name;
                        if (name != parameterName)
                        {
                            diagnostics.Add(ErrorCode.ERR_DeconstructParameterNameMismatch, subPattern.NameColon.Name.Location, name, parameterName);
                        }
                    }
                }

                var boundSubpattern = new BoundSubpattern(
                    subPattern,
                    parameter,
                    BindPattern(subPattern.Pattern, elementType, GetValEscape(elementType, inputValEscape), isError, diagnostics)
                    );
                patterns.Add(boundSubpattern);
            }

            return deconstructMethod;
        }

        private void BindITupleSubpatterns(
            DeconstructionPatternClauseSyntax node,
            ArrayBuilder<BoundSubpattern> patterns,
613 614
            DiagnosticBag diagnostics)
        {
615 616
            // Since the input has been cast to ITuple, it must be escapable.
            const uint valEscape = Binder.ExternalScope;
617
            var objectType = Compilation.GetSpecialType(SpecialType.System_Object);
618
            foreach (var subpatternSyntax in node.Subpatterns)
619 620 621 622 623 624 625 626 627 628
            {
                if (subpatternSyntax.NameColon != null)
                {
                    // error: name not permitted in ITuple deconstruction
                    diagnostics.Add(ErrorCode.ERR_ArgumentNameInITuplePattern, subpatternSyntax.NameColon.Location);
                }

                var boundSubpattern = new BoundSubpattern(
                    subpatternSyntax,
                    null,
629
                    BindPattern(subpatternSyntax.Pattern, objectType, valEscape, hasErrors: false, diagnostics));
630 631
                patterns.Add(boundSubpattern);
            }
632
        }
633

634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650
        private void BindITupleSubpatterns(
            ParenthesizedVariableDesignationSyntax node,
            ArrayBuilder<BoundSubpattern> patterns,
            DiagnosticBag diagnostics)
        {
            // Since the input has been cast to ITuple, it must be escapable.
            const uint valEscape = Binder.ExternalScope;
            var objectType = Compilation.GetSpecialType(SpecialType.System_Object);
            foreach (var variable in node.Variables)
            {
                BoundPattern pattern = BindVarDesignation(variable, objectType, valEscape, hasErrors: false, diagnostics);
                var boundSubpattern = new BoundSubpattern(
                    variable,
                    null,
                    pattern);
                patterns.Add(boundSubpattern);
            }
651 652
        }

653
        private void BindValueTupleSubpatterns(
654
            DeconstructionPatternClauseSyntax node,
655
            TypeSymbol declType,
656
            ImmutableArray<TypeSymbolWithAnnotations> elementTypes,
657
            uint inputValEscape,
658 659 660
            ref bool hasErrors,
            ArrayBuilder<BoundSubpattern> patterns,
            DiagnosticBag diagnostics)
661
        {
662
            if (elementTypes.Length != node.Subpatterns.Count && !hasErrors)
663
            {
664 665
                diagnostics.Add(ErrorCode.ERR_WrongNumberOfSubpatterns, node.Location, declType, elementTypes.Length, node.Subpatterns.Count);
                hasErrors = true;
666
            }
667 668

            for (int i = 0; i < node.Subpatterns.Count; i++)
669
            {
670 671 672 673 674
                var subpatternSyntax = node.Subpatterns[i];
                bool isError = i >= elementTypes.Length;
                TypeSymbol elementType = isError ? CreateErrorType() : elementTypes[i].TypeSymbol;
                FieldSymbol foundField = null;
                if (subpatternSyntax.NameColon != null && !isError)
675
                {
676 677
                    string name = subpatternSyntax.NameColon.Name.Identifier.ValueText;
                    foundField = CheckIsTupleElement(subpatternSyntax.NameColon.Name, (NamedTypeSymbol)declType, name, i, diagnostics);
678 679
                }

680 681 682 683 684
                BoundSubpattern boundSubpattern = new BoundSubpattern(
                    subpatternSyntax,
                    foundField,
                    BindPattern(subpatternSyntax.Pattern, elementType, GetValEscape(elementType, inputValEscape), isError, diagnostics));
                patterns.Add(boundSubpattern);
685 686 687
            }
        }

688
        private bool ShouldUseITupleForRecursivePattern(
689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712
            RecursivePatternSyntax node,
            TypeSymbol declType,
            DiagnosticBag diagnostics,
            out NamedTypeSymbol iTupleType,
            out MethodSymbol iTupleGetLength,
            out MethodSymbol iTupleGetItem)
        {
            iTupleType = null;
            iTupleGetLength = iTupleGetItem = null;
            if (node.Type != null)
            {
                // ITuple matching only applies if no type is given explicitly.
                return false;
            }

            if (node.PropertyPatternClause != null)
            {
                // ITuple matching only applies if there is no property pattern part.
                return false;
            }

            if (node.DeconstructionPatternClause == null)
            {
                // ITuple matching only applies if there is a deconstruction pattern part.
713
                // This can only occur as a result of syntax error recovery, if at all.
714 715 716
                return false;
            }

717
            if (node.Designation?.Kind() == SyntaxKind.SingleVariableDesignation)
718
            {
719
                // ITuple matching only applies if there is no variable declared (what type would the variable be?)
720 721 722
                return false;
            }

723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738
            return ShouldUseITuple(node, declType, diagnostics, out iTupleType, out iTupleGetLength, out iTupleGetItem);
        }

        private bool ShouldUseITuple(
            SyntaxNode node,
            TypeSymbol declType,
            DiagnosticBag diagnostics,
            out NamedTypeSymbol iTupleType,
            out MethodSymbol iTupleGetLength,
            out MethodSymbol iTupleGetItem)
        {
            iTupleType = null;
            iTupleGetLength = iTupleGetItem = null;
            Debug.Assert(!declType.IsTupleType);
            Debug.Assert(!IsZeroElementTupleType(declType));

739 740 741 742 743 744 745 746 747 748 749 750 751
            if (Compilation.LanguageVersion < MessageID.IDS_FeatureRecursivePatterns.RequiredVersion())
            {
                return false;
            }

            iTupleType = Compilation.GetWellKnownType(WellKnownType.System_Runtime_CompilerServices_ITuple);
            if (iTupleType.TypeKind != TypeKind.Interface)
            {
                // When compiling to a platform that lacks the interface ITuple (i.e. it is an error type), we simply do not match using it.
                return false;
            }

            // Resolution 2017-11-20 LDM: permit matching via ITuple only for `object`, `ITuple`, and types that are
752
            // declared to implement `ITuple`.
753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781
            if (declType != (object)Compilation.GetSpecialType(SpecialType.System_Object) &&
                declType != (object)Compilation.DynamicType &&
                declType != (object)iTupleType &&
                !hasBaseInterface(declType, iTupleType))
            {
                return false;
            }

            // Ensure ITuple has a Length and indexer
            iTupleGetLength = (MethodSymbol)Compilation.GetWellKnownTypeMember(WellKnownMember.System_Runtime_CompilerServices_ITuple__get_Length);
            iTupleGetItem = (MethodSymbol)Compilation.GetWellKnownTypeMember(WellKnownMember.System_Runtime_CompilerServices_ITuple__get_Item);
            if (iTupleGetLength is null || iTupleGetItem is null)
            {
                // This might not result in an ideal diagnostic
                return false;
            }

            // passed all the filters; permit using ITuple
            return true;

            bool hasBaseInterface(TypeSymbol type, NamedTypeSymbol possibleBaseInterface)
            {
                HashSet<DiagnosticInfo> useSiteDiagnostics = null;
                var result = Compilation.Conversions.ClassifyBuiltInConversion(type, possibleBaseInterface, ref useSiteDiagnostics).IsImplicit;
                diagnostics.Add(node, useSiteDiagnostics);
                return result;
            }
        }

782 783 784
        /// <summary>
        /// Check that the given name designates a tuple element at the given index, and return that element.
        /// </summary>
785
        private static FieldSymbol CheckIsTupleElement(SyntaxNode node, NamedTypeSymbol tupleType, string name, int tupleIndex, DiagnosticBag diagnostics)
786 787 788 789 790 791 792 793 794 795 796
        {
            FieldSymbol foundElement = null;
            foreach (var symbol in tupleType.GetMembers(name))
            {
                if (symbol is FieldSymbol field && field.IsTupleElement())
                {
                    foundElement = field;
                    break;
                }
            }

N
Neal Gafter 已提交
797
            if (foundElement is null || foundElement.TupleElementIndex != tupleIndex)
798
            {
N
Neal Gafter 已提交
799
                diagnostics.Add(ErrorCode.ERR_TupleElementNameMismatch, node.Location, name, $"Item{tupleIndex+1}");
800 801 802 803 804
            }

            return foundElement;
        }

805
        private BoundPattern BindVarPattern(VarPatternSyntax node, TypeSymbol inputType, uint inputValEscape, bool hasErrors, DiagnosticBag diagnostics)
806
        {
807 808 809 810 811 812 813
            if (inputType.IsPointerType() && node.Designation.Kind() == SyntaxKind.ParenthesizedVariableDesignation)
            {
                diagnostics.Add(ErrorCode.ERR_PointerTypeInPatternMatching, node.Location);
                hasErrors = true;
                inputType = CreateErrorType();
            }

814
            TypeSymbol declType = inputType;
N
Neal Gafter 已提交
815
            Symbol foundSymbol = BindTypeOrAliasOrKeyword(node.VarKeyword, node, diagnostics, out bool isVar).NamespaceOrTypeSymbol;
816 817 818
            if (!isVar)
            {
                // Give an error if there is a bindable type "var" in scope
819
                diagnostics.Add(ErrorCode.ERR_VarMayNotBindToType, node.VarKeyword.GetLocation(), foundSymbol.ToDisplayString());
820 821 822
                hasErrors = true;
            }

823
            return BindVarDesignation(node.Designation, inputType, inputValEscape, hasErrors, diagnostics);
824 825
        }

826 827 828 829 830 831
        private BoundPattern BindVarDesignation(
            VariableDesignationSyntax node,
            TypeSymbol inputType,
            uint inputValEscape,
            bool hasErrors,
            DiagnosticBag diagnostics)
832
        {
833
            switch (node.Kind())
834 835 836
            {
                case SyntaxKind.DiscardDesignation:
                    {
837
                        return new BoundDiscardPattern(node, inputType);
838 839 840
                    }
                case SyntaxKind.SingleVariableDesignation:
                    {
841
                        BindPatternDesignation(
842 843
                            designation: node, declType: inputType, inputValEscape: inputValEscape, typeSyntax: null, diagnostics: diagnostics, hasErrors: ref hasErrors,
                            variableSymbol: out Symbol variableSymbol, variableAccess: out BoundExpression variableAccess);
844
                        var boundOperandType = new BoundTypeExpression(syntax: node, aliasOpt: null, type: inputType); // fake a type expression for the variable's type
845 846
                        // We continue to use a BoundDeclarationPattern for the var pattern, as they have more in common.
                        return new BoundDeclarationPattern(
847
                            node.Parent.Kind() == SyntaxKind.VarPattern ? node.Parent : node, // for `var x` use whole pattern, otherwise use designation for the syntax
848
                            variableSymbol, variableAccess, boundOperandType, isVar: true, inputType: inputType, hasErrors: hasErrors);
849 850 851
                    }
                case SyntaxKind.ParenthesizedVariableDesignation:
                    {
852
                        var tupleDesignation = (ParenthesizedVariableDesignationSyntax)node;
853
                        var subPatterns = ArrayBuilder<BoundSubpattern>.GetInstance(tupleDesignation.Variables.Count);
854
                        MethodSymbol deconstructMethod = null;
855 856 857 858 859 860 861 862 863 864
                        var strippedInputType = inputType.StrippedType();

                        if (IsZeroElementTupleType(strippedInputType))
                        {
                            // Work around https://github.com/dotnet/roslyn/issues/20648: The compiler's internal APIs such as `declType.IsTupleType`
                            // do not correctly treat the non-generic struct `System.ValueTuple` as a tuple type.  We explicitly perform the tests
                            // required to identify it.  When that bug is fixed we should be able to remove this if statement.
                            addSubpatternsForTuple(ImmutableArray<TypeSymbolWithAnnotations>.Empty);
                        }
                        else if (strippedInputType.IsTupleType)
865 866
                        {
                            // It is a tuple type. Work according to its elements
867
                            addSubpatternsForTuple(strippedInputType.TupleElementTypes);
868 869 870 871
                        }
                        else
                        {
                            // It is not a tuple type. Seek an appropriate Deconstruct method.
872 873
                            var inputPlaceholder = new BoundImplicitReceiver(node, strippedInputType); // A fake receiver expression to permit us to reuse binding logic
                            var deconstructDiagnostics = DiagnosticBag.GetInstance();
874
                            BoundExpression deconstruct = MakeDeconstructInvocationExpression(
875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891
                                tupleDesignation.Variables.Count, inputPlaceholder, node, deconstructDiagnostics,
                                outPlaceholders: out ImmutableArray<BoundDeconstructValuePlaceholder> outPlaceholders,
                                out bool anyDeconstructCandidates);
                            if (!anyDeconstructCandidates &&
                                ShouldUseITuple(node, strippedInputType, diagnostics, out var iTupleType, out var iTupleGetLength, out var iTupleGetItem))
                            {
                                // There was no applicable candidate Deconstruct, and the constraints for the use of ITuple are satisfied.
                                // Use that and forget any errors from trying to bind Deconstruct.
                                deconstructDiagnostics.Free();
                                BindITupleSubpatterns(tupleDesignation, subPatterns, diagnostics);
                                return new BoundITuplePattern(node, iTupleGetLength, iTupleGetItem, subPatterns.ToImmutableAndFree(), strippedInputType, hasErrors);
                            }
                            else
                            {
                                diagnostics.AddRangeAndFree(deconstructDiagnostics);
                            }

892
                            deconstructMethod = deconstruct.ExpressionSymbol as MethodSymbol;
893 894 895
                            if (!hasErrors)
                                hasErrors = outPlaceholders.IsDefaultOrEmpty || tupleDesignation.Variables.Count != outPlaceholders.Length;

896 897
                            for (int i = 0; i < tupleDesignation.Variables.Count; i++)
                            {
898
                                var variable = tupleDesignation.Variables[i];
899
                                bool isError = outPlaceholders.IsDefaultOrEmpty || i >= outPlaceholders.Length;
900
                                TypeSymbol elementType = isError ? CreateErrorType() : outPlaceholders[i].Type;
901
                                BoundPattern pattern = BindVarDesignation(variable, elementType, GetValEscape(elementType, inputValEscape), isError, diagnostics);
902
                                subPatterns.Add(new BoundSubpattern(variable, symbol: null, pattern));
903 904 905 906
                            }
                        }

                        return new BoundRecursivePattern(
907 908
                            syntax: node, declaredType: null, inputType: inputType, deconstructMethod: deconstructMethod,
                            deconstruction: subPatterns.ToImmutableAndFree(), properties: default, variable: null, variableAccess: null, hasErrors: hasErrors);
909 910 911 912 913 914

                        void addSubpatternsForTuple(ImmutableArray<TypeSymbolWithAnnotations> elementTypes)
                        {
                            if (elementTypes.Length != tupleDesignation.Variables.Count && !hasErrors)
                            {
                                diagnostics.Add(ErrorCode.ERR_WrongNumberOfSubpatterns, tupleDesignation.Location,
N
Neal Gafter 已提交
915
                                    strippedInputType, elementTypes.Length, tupleDesignation.Variables.Count);
916 917 918 919 920 921 922 923 924 925 926
                                hasErrors = true;
                            }
                            for (int i = 0; i < tupleDesignation.Variables.Count; i++)
                            {
                                var variable = tupleDesignation.Variables[i];
                                bool isError = i >= elementTypes.Length;
                                TypeSymbol elementType = isError ? CreateErrorType() : elementTypes[i].TypeSymbol;
                                BoundPattern pattern = BindVarDesignation(variable, elementType, GetValEscape(elementType, inputValEscape), isError, diagnostics);
                                subPatterns.Add(new BoundSubpattern(variable, symbol: null, pattern));
                            }
                        }
927 928 929
                    }
                default:
                    {
930
                        throw ExceptionUtilities.UnexpectedValue(node.Kind());
931 932 933 934
                    }
            }
        }

935 936
        ImmutableArray<BoundSubpattern> BindPropertyPatternClause(
            PropertyPatternClauseSyntax node,
937
            TypeSymbol inputType,
938
            uint inputValEscape,
939 940 941
            DiagnosticBag diagnostics,
            ref bool hasErrors)
        {
942
            var builder = ArrayBuilder<BoundSubpattern>.GetInstance(node.Subpatterns.Count);
943
            foreach (SubpatternSyntax p in node.Subpatterns)
944
            {
945 946
                IdentifierNameSyntax name = p.NameColon?.Name;
                PatternSyntax pattern = p.Pattern;
N
Neal Gafter 已提交
947 948
                Symbol member = null;
                TypeSymbol memberType;
949 950 951 952 953
                if (name == null)
                {
                    if (!hasErrors)
                        diagnostics.Add(ErrorCode.ERR_PropertyPatternNameMissing, pattern.Location, pattern);

N
Neal Gafter 已提交
954
                    memberType = CreateErrorType();
955 956 957 958
                    hasErrors = true;
                }
                else
                {
959
                    member = LookupMemberForPropertyPattern(inputType, name, diagnostics, ref hasErrors, out memberType);
960 961
                }

962
                BoundPattern boundPattern = BindPattern(pattern, memberType, GetValEscape(memberType, inputValEscape), hasErrors, diagnostics);
963
                builder.Add(new BoundSubpattern(p, member, boundPattern));
964 965 966 967 968
            }

            return builder.ToImmutableAndFree();
        }

969 970
        private Symbol LookupMemberForPropertyPattern(
            TypeSymbol inputType, IdentifierNameSyntax name, DiagnosticBag diagnostics, ref bool hasErrors, out TypeSymbol memberType)
971
        {
972
            Symbol symbol = BindPropertyPatternMember(inputType, name, ref hasErrors, diagnostics);
973

974
            if (inputType.IsErrorType() || hasErrors || symbol == (object)null)
N
Neal Gafter 已提交
975
                memberType = CreateErrorType();
976
            else
977
                memberType = symbol.GetTypeOrReturnType().TypeSymbol;
978 979

            return symbol;
980
        }
981

982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998
        private Symbol BindPropertyPatternMember(
            TypeSymbol inputType,
            IdentifierNameSyntax memberName,
            ref bool hasErrors,
            DiagnosticBag diagnostics)
        {
            // TODO: consider refactoring out common code with BindObjectInitializerMember
            BoundImplicitReceiver implicitReceiver = new BoundImplicitReceiver(memberName, inputType);
            string name = memberName.Identifier.ValueText;

            BoundExpression boundMember = BindInstanceMemberAccess(
                node: memberName,
                right: memberName,
                boundLeft: implicitReceiver,
                rightName: name,
                rightArity: 0,
                typeArgumentsSyntax: default(SeparatedSyntaxList<TypeSyntax>),
999
                typeArguments: default(ImmutableArray<TypeSymbolWithAnnotations>),
1000
                invoked: false,
1001
                indexed: false,
1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039
                diagnostics: diagnostics);

            if (boundMember.Kind == BoundKind.PropertyGroup)
            {
                boundMember = BindIndexedPropertyAccess(
                    (BoundPropertyGroup)boundMember, mustHaveAllOptionalParameters: true, diagnostics: diagnostics);
            }

            hasErrors |= boundMember.HasAnyErrors || implicitReceiver.HasAnyErrors;

            switch (boundMember.Kind)
            {
                case BoundKind.FieldAccess:
                case BoundKind.PropertyAccess:
                    break;

                case BoundKind.IndexerAccess:
                case BoundKind.DynamicIndexerAccess:
                case BoundKind.EventAccess:
                default:
                    if (!hasErrors)
                    {
                        switch (boundMember.ResultKind)
                        {
                            case LookupResultKind.Empty:
                                Error(diagnostics, ErrorCode.ERR_NoSuchMember, memberName, implicitReceiver.Type, name);
                                break;

                            case LookupResultKind.Inaccessible:
                                boundMember = CheckValue(boundMember, BindValueKind.RValue, diagnostics);
                                Debug.Assert(boundMember.HasAnyErrors);
                                break;

                            default:
                                Error(diagnostics, ErrorCode.ERR_PropertyLacksGet, memberName, name);
                                break;
                        }
                    }
1040 1041

                    hasErrors = true;
1042
                    return boundMember.ExpressionSymbol;
1043 1044
            }

1045 1046
            if (hasErrors || !CheckValueKind(node: memberName.Parent, expr: boundMember, valueKind: BindValueKind.RValue,
                                             checkingReceiver: false, diagnostics: diagnostics))
1047
            {
1048
                hasErrors = true;
1049 1050 1051 1052
            }

            return boundMember.ExpressionSymbol;
        }
1053 1054
    }
}