Binder_Patterns.cs 28.0 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
            var expression = BindValue(node.Expression, diagnostics, BindValueKind.RValue);
19 20
            var hasErrors = IsOperandErrors(node, ref expression, diagnostics);
            var expressionType = expression.Type;
21
            if ((object)expressionType == null || expressionType.SpecialType == SpecialType.System_Void)
22 23 24 25 26 27 28 29 30 31
            {
                expressionType = CreateErrorType();
                if (!hasErrors)
                {
                    // value expected
                    diagnostics.Add(ErrorCode.ERR_BadIsPatternExpression, node.Expression.Location, expression.Display);
                    hasErrors = true;
                }
            }

32
            var pattern = BindPattern(node.Pattern, expressionType, hasErrors, diagnostics);
33 34 35 36 37
            if (!hasErrors && pattern is BoundDeclarationPattern p && !p.IsVar && expression.ConstantValue == ConstantValue.Null)
            {
                diagnostics.Add(ErrorCode.WRN_IsAlwaysFalse, node.Location, p.DeclaredType.Type);
            }

38 39
            return new BoundIsPatternExpression(
                node, expression, pattern, GetSpecialType(SpecialType.System_Boolean, diagnostics, node), hasErrors);
40 41
        }

42 43 44 45 46
        private BoundExpression BindSwitchExpression(SwitchExpressionSyntax node, DiagnosticBag diagnostics)
        {
            throw new NotImplementedException();
        }

47 48 49 50
        internal BoundPattern BindPattern(
            PatternSyntax node,
            TypeSymbol operandType,
            bool hasErrors,
51
            DiagnosticBag diagnostics)
52 53 54
        {
            switch (node.Kind())
            {
55 56 57
                case SyntaxKind.DiscardPattern:
                    return BindDiscardPattern((DiscardPatternSyntax)node, operandType, hasErrors, diagnostics);

58
                case SyntaxKind.DeclarationPattern:
59
                    return BindDeclarationPattern((DeclarationPatternSyntax)node, operandType, hasErrors, diagnostics);
60

61
                case SyntaxKind.ConstantPattern:
62
                    return BindConstantPattern((ConstantPatternSyntax)node, operandType, hasErrors, diagnostics);
63

64
                case SyntaxKind.DeconstructionPattern:
65 66
                    return BindDeconstructionPattern((DeconstructionPatternSyntax)node, operandType, hasErrors, diagnostics);

67
                case SyntaxKind.PropertyPattern:
68
                    return BindPropertyPattern((PropertyPatternSyntax)node, operandType, hasErrors, diagnostics);
69

70
                default:
71
                    throw ExceptionUtilities.UnexpectedValue(node.Kind());
72 73
            }
        }
74

75 76 77 78 79 80
        private BoundPattern BindDiscardPattern(DiscardPatternSyntax node, TypeSymbol operandType, bool hasErrors, DiagnosticBag diagnostics)
        {
            // TODO(patterns2): give an error if there is a bindable `_` in scope.
            return new BoundDiscardPattern(node);
        }

81
        private BoundConstantPattern BindConstantPattern(
82 83 84
            ConstantPatternSyntax node,
            TypeSymbol operandType,
            bool hasErrors,
85
            DiagnosticBag diagnostics)
86
        {
87 88 89 90 91 92 93 94
            var innerExpression = node.Expression.SkipParens();
            if (innerExpression.Kind() == SyntaxKind.DefaultLiteralExpression)
            {
                diagnostics.Add(ErrorCode.ERR_DefaultPattern, innerExpression.Location);
                hasErrors = true;
            }

            return BindConstantPattern(node, operandType, node.Expression, hasErrors, diagnostics, out bool wasExpression);
95 96
        }

97
        internal BoundConstantPattern BindConstantPattern(
98
            CSharpSyntaxNode node,
99 100
            TypeSymbol operandType,
            ExpressionSyntax patternExpression,
101 102
            bool hasErrors,
            DiagnosticBag diagnostics,
103
            out bool wasExpression)
104
        {
105 106
            var expression = BindValue(patternExpression, diagnostics, BindValueKind.RValue);
            ConstantValue constantValueOpt = null;
107
            var convertedExpression = ConvertPatternExpression(operandType, patternExpression, expression, ref constantValueOpt, diagnostics);
108
            wasExpression = expression.Type?.IsErrorType() != true;
109
            if (!convertedExpression.HasErrors && constantValueOpt == null)
110
            {
111
                diagnostics.Add(ErrorCode.ERR_ConstantExpected, patternExpression.Location);
N
Neal Gafter 已提交
112 113
                hasErrors = true;
            }
114

115
            return new BoundConstantPattern(node, convertedExpression, constantValueOpt, hasErrors);
116 117
        }

G
gafter 已提交
118
        internal BoundExpression ConvertPatternExpression(TypeSymbol inputType, CSharpSyntaxNode node, BoundExpression expression, ref ConstantValue constantValue, DiagnosticBag diagnostics)
119 120 121 122
        {
            // NOTE: This will allow user-defined conversions, even though they're not allowed here.  This is acceptable
            // because the result of a user-defined conversion does not have a ConstantValue and we'll report a diagnostic
            // to that effect later.
G
gafter 已提交
123
            BoundExpression convertedExpression = GenerateConversionForAssignment(inputType, expression, diagnostics);
124

G
gafter 已提交
125
            if (convertedExpression.Kind == BoundKind.Conversion)
126
            {
G
gafter 已提交
127
                var conversion = (BoundConversion)convertedExpression;
128
                var operand = conversion.Operand;
G
gafter 已提交
129
                if (inputType.IsNullableType() && (convertedExpression.ConstantValue == null || !convertedExpression.ConstantValue.IsNull))
130 131 132
                {
                    // 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
G
gafter 已提交
133
                    convertedExpression = CreateConversion(operand, inputType.GetNullableUnderlyingType(), discardedDiagnostics);
134 135
                    discardedDiagnostics.Free();
                }
136
                else if ((conversion.ConversionKind == ConversionKind.Boxing || conversion.ConversionKind == ConversionKind.ImplicitReference)
G
gafter 已提交
137
                    && operand.ConstantValue != null && convertedExpression.ConstantValue == null)
138
                {
139 140 141 142 143
                    // 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.
G
gafter 已提交
144
                    convertedExpression = operand;
145
                }
146 147 148 149
                else if (conversion.ConversionKind == ConversionKind.NoConversion && convertedExpression.Type?.IsErrorType() == true)
                {
                    convertedExpression = operand;
                }
150 151
            }

G
gafter 已提交
152 153
            constantValue = convertedExpression.ConstantValue;
            return convertedExpression;
154 155
        }

156 157 158
        /// <summary>
        /// Check that the pattern type is valid for the operand. Return true if an error was reported.
        /// </summary>
159 160 161 162
        private bool CheckValidPatternType(
            CSharpSyntaxNode typeSyntax,
            TypeSymbol operandType,
            TypeSymbol patternType,
163
            bool patternTypeWasInSource,
164 165
            bool isVar,
            DiagnosticBag diagnostics)
166
        {
167 168
            Debug.Assert((object)operandType != null);
            Debug.Assert((object)patternType != null);
169

170
            if (operandType.IsErrorType() || patternType.IsErrorType())
171 172 173
            {
                return false;
            }
174
            else if (patternType.IsNullableType() && !isVar && patternTypeWasInSource)
175 176
            {
                // It is an error to use pattern-matching with a nullable type, because you'll never get null. Use the underlying type.
177 178
                Error(diagnostics, ErrorCode.ERR_PatternNullableType, typeSyntax, patternType, patternType.GetNullableUnderlyingType());
                return true;
179
            }
180 181 182 183 184
            else if (patternType.IsStatic)
            {
                Error(diagnostics, ErrorCode.ERR_VarDeclIsStaticClass, typeSyntax, patternType);
                return true;
            }
185 186
            else if (!isVar)
            {
187 188 189 190 191 192
                if (patternType.IsDynamic())
                {
                    Error(diagnostics, ErrorCode.ERR_PatternDynamicType, typeSyntax);
                    return true;
                }

193
                HashSet<DiagnosticInfo> useSiteDiagnostics = null;
194
                var matchPossible = ExpressionOfTypeMatchesPatternType(Conversions, operandType, patternType, ref useSiteDiagnostics, out Conversion conversion, operandConstantValue: null, operandCouldBeNull: true);
195
                diagnostics.Add(typeSyntax, useSiteDiagnostics);
196
                if (matchPossible != false)
197
                {
198 199 200 201 202
                    if (!conversion.Exists && (operandType.ContainsTypeParameter() || patternType.ContainsTypeParameter()))
                    {
                        // 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)
203
                        {
204 205 206 207
                            Error(diagnostics, ErrorCode.ERR_PatternWrongGenericTypeInVersion, typeSyntax,
                                operandType, patternType,
                                Compilation.LanguageVersion.ToDisplayString(),
                                new CSharpRequiredLanguageVersion(requiredVersion));
208 209
                            return true;
                        }
210 211 212 213 214 215
                    }
                }
                else
                {
                    Error(diagnostics, ErrorCode.ERR_PatternWrongType, typeSyntax, operandType, patternType);
                    return true;
216 217
                }
            }
N
Neal Gafter 已提交
218

219 220 221
            return false;
        }

222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
        /// <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);
            }

            conversion = conversions.ClassifyConversionFromType(expressionType, patternType, ref useSiteDiagnostics);
            var result = Binder.GetIsOperatorConstantResult(expressionType, patternType, conversion.Kind, operandConstantValue, operandCouldBeNull);
            return
                (result == null) ? (bool?)null :
                (result == ConstantValue.True) ? true :
                (result == ConstantValue.False) ? false :
                throw ExceptionUtilities.UnexpectedValue(result);
        }

253
        private BoundPattern BindDeclarationPattern(
254 255 256 257
            DeclarationPatternSyntax node,
            TypeSymbol operandType,
            bool hasErrors,
            DiagnosticBag diagnostics)
258 259
        {
            var typeSyntax = node.Type;
N
Neal Gafter 已提交
260
            var boundDeclType = BindPatternType(typeSyntax, operandType, ref hasErrors, out bool isVar, diagnostics);
261 262
            var declType = boundDeclType.Type;

N
Neal Gafter 已提交
263
            BindPatternDesignation(node, node.Designation, declType, typeSyntax, diagnostics, ref hasErrors, out Symbol variableSymbol, out BoundExpression variableAccess);
264 265 266 267 268 269 270 271 272 273 274
            return new BoundDeclarationPattern(node, variableSymbol, variableAccess, boundDeclType, isVar, hasErrors);
        }

        private BoundTypeExpression BindPatternType(
            TypeSyntax typeSyntax,
            TypeSymbol operandType,
            ref bool hasErrors,
            out bool isVar,
            DiagnosticBag diagnostics)
        {
            Debug.Assert(operandType != (object)null);
275 276 277

            AliasSymbol aliasOpt;
            TypeSymbol declType = BindType(typeSyntax, diagnostics, out isVar, out aliasOpt);
278
            if (isVar)
279 280 281 282
            {
                declType = operandType;
            }

283 284 285
            if (declType == (object)null)
            {
                Debug.Assert(hasErrors);
286
                declType = this.CreateErrorType("var");
287 288
            }

289
            var boundDeclType = new BoundTypeExpression(typeSyntax, aliasOpt, inferredType: isVar, type: declType);
290
            if (IsOperatorErrors(typeSyntax, operandType, boundDeclType, diagnostics))
291 292 293 294 295
            {
                hasErrors = true;
            }
            else
            {
296
                hasErrors |= CheckValidPatternType(typeSyntax, operandType, declType,
297
                                                   isVar: isVar, patternTypeWasInSource: true, diagnostics: diagnostics);
298 299
            }

300 301 302 303 304 305 306 307 308 309 310 311 312 313
            return boundDeclType;
        }

        private void BindPatternDesignation(
            PatternSyntax node,
            VariableDesignationSyntax designation,
            TypeSymbol declType,
            TypeSyntax typeSyntax,
            DiagnosticBag diagnostics,
            ref bool hasErrors,
            out Symbol variableSymbol,
            out BoundExpression variableAccess)
        {
            switch (designation)
314
            {
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358
                case SingleVariableDesignationSyntax singleVariableDesignation:
                    var identifier = singleVariableDesignation.Identifier;
                    SourceLocalSymbol localSymbol = this.LookupLocal(identifier);

                    if (localSymbol != (object)null)
                    {
                        if ((InConstructorInitializer || InFieldInitializer) && ContainingMemberOrLambda.ContainingSymbol.Kind == SymbolKind.NamedType)
                        {
                            Error(diagnostics, ErrorCode.ERR_ExpressionVariableInConstructorOrFieldInitializer, node);
                        }

                        localSymbol.SetType(declType);

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

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

                        variableSymbol = localSymbol;
                        variableAccess = new BoundLocal(node, localSymbol, null, declType);
                        return;
                    }
                    else
                    {
                        // We should have the right binder in the chain for a script or interactive, so we use the field for the pattern.
                        Debug.Assert(node.SyntaxTree.Options.Kind != SourceCodeKind.Regular);
                        GlobalExpressionVariable expressionVariableField = LookupDeclaredField(singleVariableDesignation);
                        DiagnosticBag tempDiagnostics = DiagnosticBag.GetInstance();
                        expressionVariableField.SetType(declType, tempDiagnostics);
                        tempDiagnostics.Free();
                        BoundExpression receiver = SynthesizeReceiver(node, expressionVariableField, diagnostics);

                        variableSymbol = expressionVariableField;
                        variableAccess = new BoundFieldAccess(node, receiver, expressionVariableField, null, hasErrors);
                        return;
                    }
                case DiscardDesignationSyntax _:
                case null:
                    variableSymbol = null;
                    variableAccess = null;
                    return;
359
                default:
360
                    throw ExceptionUtilities.UnexpectedValue(designation.Kind());
361 362
            }

363
        }
364

365 366 367
        TypeSymbol BindRecursivePatternType(TypeSyntax typeSyntax, TypeSymbol operandType, ref bool hasErrors, out BoundTypeExpression boundDeclType, DiagnosticBag diagnostics)
        {
            if (typeSyntax != null)
368
            {
N
Neal Gafter 已提交
369
                boundDeclType = BindPatternType(typeSyntax, operandType, ref hasErrors, out bool isVar, diagnostics);
370
                if (isVar)
371
                {
372 373 374 375 376 377 378
                    // The type `var` is not permitted in recursive patterns. If you want the type inferred, just omit it.
                    if (!hasErrors)
                    {
                        diagnostics.Add(ErrorCode.ERR_InferredRecursivePatternType, typeSyntax.Location);
                    }

                    boundDeclType = new BoundTypeExpression(typeSyntax, aliasOpt: null, inferredType: true, type: operandType.StrippedType(), hasErrors: hasErrors = true);
379
                }
380

381 382 383 384 385 386 387 388
                return boundDeclType.Type;
            }
            else
            {
                boundDeclType = null;
                return operandType.StrippedType(); // remove the nullable part of the input's type
            }
        }
N
Neal Gafter 已提交
389

390 391 392
        private BoundPattern BindDeconstructionPattern(DeconstructionPatternSyntax node, TypeSymbol operandType, bool hasErrors, DiagnosticBag diagnostics)
        {
            var typeSyntax = node.Type;
N
Neal Gafter 已提交
393
            TypeSymbol declType = BindRecursivePatternType(typeSyntax, operandType, ref hasErrors, out BoundTypeExpression boundDeclType, diagnostics);
394

395 396 397 398 399 400 401
            var patterns = ArrayBuilder<BoundPattern>.GetInstance();
            MethodSymbol deconstructMethod = null;
            if (declType.IsTupleType)
            {
                // It is a tuple type. Work according to its elements
                var elementTypes = declType.TupleElementTypes;
                if (elementTypes.Length != node.SubPatterns.Count && !hasErrors)
402
                {
403 404 405 406 407 408 409 410 411 412 413
                    var location = new SourceLocation(node.SyntaxTree, new Text.TextSpan(node.OpenParenToken.SpanStart, node.CloseParenToken.Span.End - node.OpenParenToken.SpanStart));
                    diagnostics.Add(ErrorCode.ERR_WrongNumberOfSubpatterns, location, declType.TupleElementTypes, elementTypes.Length, node.SubPatterns.Count);
                    hasErrors = true;
                }
                for (int i = 0; i < node.SubPatterns.Count; i++)
                {
                    bool isError = i >= elementTypes.Length;
                    var elementType = isError ? CreateErrorType() : elementTypes[i];
                    // PROTOTYPE(patterns2): Check that node.SubPatterns[i].NameColon?.Name corresponds to tuple element i of declType.
                    var boundSubpattern = BindPattern(node.SubPatterns[i].Pattern, elementType, isError, diagnostics);
                    patterns.Add(boundSubpattern);
414 415 416
                }
            }
            else
417
            {
418 419 420 421
                // It is not a tuple type. Seek an appropriate Deconstruct method.

                var inputPlaceholder = new BoundImplicitReceiver(node, declType); // A fake receiver expression to permit us to reuse binding logic
                var deconstruct = MakeDeconstructInvocationExpression(
N
Neal Gafter 已提交
422
                    node.SubPatterns.Count, inputPlaceholder, node, diagnostics, out ImmutableArray<BoundDeconstructValuePlaceholder> outPlaceholders, requireTwoOrMoreElements: false);
423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445
                deconstructMethod = deconstruct.ExpressionSymbol as MethodSymbol;
                // PROTOTYPE(patterns2): Set and check the deconstructMethod

                for (int i = 0; i < node.SubPatterns.Count; i++)
                {
                    bool isError = outPlaceholders.IsDefaultOrEmpty || i >= outPlaceholders.Length;
                    var elementType = isError ? CreateErrorType() : outPlaceholders[i].Type;
                    // PROTOTYPE(patterns2): Check that node.SubPatterns[i].NameColon?.Name corresponds to parameter i of the method. Or,
                    // better yet, include those names in the AnalyzedArguments used in MakeDeconstructInvocationExpression so they are
                    // used to disambiguate.
                    var boundSubpattern = BindPattern(node.SubPatterns[i].Pattern, elementType, isError, diagnostics);
                    patterns.Add(boundSubpattern);
                }

                // PROTOTYPE(patterns2): If no Deconstruct method is found, try casting to `ITuple`.
            }

            ImmutableArray<(Symbol property, BoundPattern pattern)> propertiesOpt = default;
            if (node.PropertySubpattern != null)
            {
                propertiesOpt = BindPropertySubpattern(node.PropertySubpattern, declType, diagnostics, ref hasErrors);
            }

N
Neal Gafter 已提交
446
            BindPatternDesignation(node, node.Designation, declType, typeSyntax, diagnostics, ref hasErrors, out Symbol variableSymbol, out BoundExpression variableAccess);
447 448 449 450 451 452 453 454
            return new BoundRecursivePattern(
                syntax: node, declaredType: boundDeclType, inputType: declType, deconstructMethodOpt: deconstructMethod,
                deconstruction: patterns.ToImmutableAndFree(), propertiesOpt: propertiesOpt, variable: variableSymbol, variableAccess: variableAccess, hasErrors: hasErrors);
        }

        private BoundPattern BindPropertyPattern(PropertyPatternSyntax node, TypeSymbol operandType, bool hasErrors, DiagnosticBag diagnostics)
        {
            var typeSyntax = node.Type;
N
Neal Gafter 已提交
455
            TypeSymbol declType = BindRecursivePatternType(typeSyntax, operandType, ref hasErrors, out BoundTypeExpression boundDeclType, diagnostics);
456
            ImmutableArray<(Symbol property, BoundPattern pattern)> propertiesOpt = BindPropertySubpattern(node.PropertySubpattern, declType, diagnostics, ref hasErrors);
N
Neal Gafter 已提交
457
            BindPatternDesignation(node, node.Designation, declType, typeSyntax, diagnostics, ref hasErrors, out Symbol variableSymbol, out BoundExpression variableAccess);
458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505
            return new BoundRecursivePattern(
                syntax: node, declaredType: boundDeclType, inputType: declType, deconstructMethodOpt: null,
                deconstruction: default, propertiesOpt: propertiesOpt, variable: variableSymbol, variableAccess: variableAccess, hasErrors: hasErrors);
        }

        ImmutableArray<(Symbol property, BoundPattern pattern)> BindPropertySubpattern(
            PropertySubpatternSyntax node,
            TypeSymbol inputType,
            DiagnosticBag diagnostics,
            ref bool hasErrors)
        {
            var builder = ArrayBuilder<(Symbol property, BoundPattern pattern)>.GetInstance();
            foreach (var p in node.SubPatterns)
            {
                var name = p.NameColon?.Name;
                var pattern = p.Pattern;
                Symbol property = null;
                TypeSymbol propertyType;
                if (name == null)
                {
                    if (!hasErrors)
                    {
                        diagnostics.Add(ErrorCode.ERR_PropertyPatternNameMissing, pattern.Location, pattern);
                    }

                    propertyType = CreateErrorType();
                    hasErrors = true;
                }
                else
                {
                    property = LookupPropertyForPattern(inputType, name, out propertyType, ref hasErrors, diagnostics);
                }

                var boundPattern = BindPattern(pattern, propertyType, hasErrors, diagnostics);
                builder.Add((property, boundPattern));
            }

            return builder.ToImmutableAndFree();
        }

        private Symbol LookupPropertyForPattern(TypeSymbol inputType, IdentifierNameSyntax name, out TypeSymbol propertyType, ref bool hasErrors, DiagnosticBag diagnostics)
        {
            var symbol = BindPropertyPatternMember(inputType, name, ref hasErrors, diagnostics);

            if (inputType.IsErrorType() || hasErrors)
            {
                propertyType = CreateErrorType();
                return null;
506
            }
507 508 509

            propertyType = symbol.GetTypeOrReturnType();
            return symbol;
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 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582
        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>),
                typeArguments: default(ImmutableArray<TypeSymbol>),
                invoked: false,
                diagnostics: diagnostics);

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

            LookupResultKind resultKind = boundMember.ResultKind;
            hasErrors |= boundMember.HasAnyErrors || implicitReceiver.HasAnyErrors;

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

                case BoundKind.IndexerAccess:
                case BoundKind.DynamicIndexerAccess:
                case BoundKind.EventAccess:
                    // PROTOTYPE(patterns2): we need to decide what kinds of members can be used in a property pattern.
                    // For now we support fields and readable non-indexed properties.
                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);
                                hasErrors = true;
                                break;
                        }
                    }
                    return null;
            }

            if (hasErrors || !CheckValueKind(memberName.Parent, boundMember, BindValueKind.RValue, false, diagnostics))
            {
                return null;
            }

            return boundMember.ExpressionSymbol;
        }
583 584
    }
}