Binder_Patterns.cs 42.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 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
            BoundPattern pattern = BindPattern(node.Pattern, expression.Type, hasErrors, diagnostics);
            hasErrors |= pattern.HasErrors;
            return MakeIsPatternExpression(
                node, expression, pattern, GetSpecialType(SpecialType.System_Boolean, diagnostics, node),
                hasErrors, diagnostics);
        }

        private BoundExpression MakeIsPatternExpression(SyntaxNode node, BoundExpression expression, BoundPattern pattern, TypeSymbol boolType, bool hasErrors, DiagnosticBag diagnostics)
        {
            // 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;
51 52
            }

53
            if (expression.ConstantValue != null)
54
            {
55
                decisionDag = decisionDag.SimplifyDecisionDagIfConstantInput(expression);
56 57 58 59 60 61 62 63 64 65 66
                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);
                    }
                }
67 68
            }

69
            return new BoundIsPatternExpression(node, expression, pattern, decisionDag, whenTrueLabel: whenTrueLabel, whenFalseLabel: whenFalseLabel, boolType, hasErrors);
70 71
        }

72 73
        private BoundExpression BindSwitchExpression(SwitchExpressionSyntax node, DiagnosticBag diagnostics)
        {
74 75 76 77 78 79 80 81
            Debug.Assert(node != null);
            Binder switchBinder = this.GetBinder(node);
            return switchBinder.BindSwitchExpressionCore(node, switchBinder, diagnostics);
        }

        internal virtual BoundExpression BindSwitchExpressionCore(SwitchExpressionSyntax node, Binder originalBinder, DiagnosticBag diagnostics)
        {
            return this.Next.BindSwitchExpressionCore(node, originalBinder, diagnostics);
82 83
        }

84 85
        internal BoundPattern BindPattern(
            PatternSyntax node,
86
            TypeSymbol inputType,
87
            bool hasErrors,
88
            DiagnosticBag diagnostics)
89 90 91
        {
            switch (node.Kind())
            {
92
                case SyntaxKind.DiscardPattern:
93
                    return BindDiscardPattern((DiscardPatternSyntax)node, inputType, hasErrors, diagnostics);
94

95
                case SyntaxKind.DeclarationPattern:
96
                    return BindDeclarationPattern((DeclarationPatternSyntax)node, inputType, hasErrors, diagnostics);
97

98
                case SyntaxKind.ConstantPattern:
99
                    return BindConstantPattern((ConstantPatternSyntax)node, inputType, hasErrors, diagnostics);
100

101 102
                case SyntaxKind.RecursivePattern:
                    return BindRecursivePattern((RecursivePatternSyntax)node, inputType, hasErrors, diagnostics);
103

104
                case SyntaxKind.VarPattern:
105
                    return BindVarPattern((VarPatternSyntax)node, inputType, hasErrors, diagnostics);
106

107
                default:
108
                    throw ExceptionUtilities.UnexpectedValue(node.Kind());
109 110
            }
        }
111

112
        private BoundPattern BindDiscardPattern(DiscardPatternSyntax node, TypeSymbol inputType, bool hasErrors, DiagnosticBag diagnostics)
113
        {
114 115 116 117
            // give an error if there is a bindable `_` in scope.
            var lookupResult = LookupResult.GetInstance();
            var name = node.UnderscoreToken.ValueText;
            HashSet<DiagnosticInfo> useSiteDiagnostics = null;
118 119 120
            this.LookupSymbolsInternal(
                lookupResult, name, arity: 0, basesBeingResolved: null,
                options: LookupOptions.AllMethodsOnArityZero, diagnose: false, ref useSiteDiagnostics);
121 122 123
            diagnostics.Add(node, useSiteDiagnostics);
            if (lookupResult.IsMultiViable)
            {
124
                diagnostics.Add(ErrorCode.ERR_UnderscoreDeclaredAndDiscardPattern, node.Location, lookupResult.Symbols[0]);
125 126
            }

N
Neal Gafter 已提交
127
            lookupResult.Free();
128
            return new BoundDiscardPattern(node, inputType);
129 130
        }

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

144
            return BindConstantPattern(node, innerExpression, inputType, node.Expression, hasErrors, diagnostics, out _);
145 146
        }

147
        internal BoundConstantPattern BindConstantPattern(
148
            CSharpSyntaxNode node,
149
            TypeSymbol inputType,
150
            ExpressionSyntax patternExpression,
151 152
            bool hasErrors,
            DiagnosticBag diagnostics,
153
            out bool wasExpression)
154
        {
155
            return BindConstantPattern(node, patternExpression.SkipParens(), inputType, patternExpression, hasErrors, diagnostics, out wasExpression);
156 157 158 159 160
        }

        internal BoundConstantPattern BindConstantPattern(
            CSharpSyntaxNode node,
            SyntaxNode innerExpression,
161
            TypeSymbol inputType,
162 163 164 165 166 167
            ExpressionSyntax patternExpression,
            bool hasErrors,
            DiagnosticBag diagnostics,
            out bool wasExpression)
        {
            if (innerExpression.Kind() == SyntaxKind.IdentifierName &&
168
                ((IdentifierNameSyntax)innerExpression).Identifier.Text == "_")
169 170 171 172 173
            {
                diagnostics.Add(ErrorCode.ERR_ConstantPatternNamedUnderscore, innerExpression.Location);
                hasErrors = true;
            }

174
            BoundExpression expression = BindValue(patternExpression, diagnostics, BindValueKind.RValue);
175
            ConstantValue constantValueOpt = null;
176
            BoundExpression convertedExpression = ConvertPatternExpression(inputType, patternExpression, expression, out constantValueOpt, diagnostics);
177
            wasExpression = expression.Type?.IsErrorType() != true;
178
            if (!convertedExpression.HasErrors && constantValueOpt == null)
179
            {
180
                diagnostics.Add(ErrorCode.ERR_ConstantExpected, patternExpression.Location);
N
Neal Gafter 已提交
181 182
                hasErrors = true;
            }
183

184 185 186 187 188 189 190 191 192
            if (convertedExpression.Type == null && constantValueOpt != ConstantValue.Null)
            {
                Debug.Assert(hasErrors);
                convertedExpression = new BoundConversion(
                    convertedExpression.Syntax, convertedExpression, Conversion.NoConversion, @checked: false,
                    explicitCastInCode: false, constantValueOpt: constantValueOpt, CreateErrorType(), hasErrors: true)
                    { WasCompilerGenerated = true };
            }

193
            return new BoundConstantPattern(node, convertedExpression, constantValueOpt ?? ConstantValue.Bad, inputType, hasErrors);
194 195
        }

196
        internal BoundExpression ConvertPatternExpression(TypeSymbol inputType, CSharpSyntaxNode node, BoundExpression expression, out ConstantValue constantValue, DiagnosticBag diagnostics)
197
        {
198
            BoundExpression convertedExpression;
199 200 201

            // 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`.
202 203
            bool inputContainsTypeParameter = inputType.ContainsTypeParameter();
            if (inputContainsTypeParameter)
204
            {
205
                convertedExpression = expression;
206
                HashSet<DiagnosticInfo> useSiteDiagnostics = null;
207 208 209 210 211 212 213 214 215 216 217 218 219 220
                if (expression.ConstantValue == ConstantValue.Null)
                {
                    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);
                }

                diagnostics.Add(node, useSiteDiagnostics);
221 222 223
            }
            else
            {
224 225 226
                // 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.
227 228 229
                convertedExpression = GenerateConversionForAssignment(inputType, expression, diagnostics);

                if (convertedExpression.Kind == BoundKind.Conversion)
230
                {
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
                    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;
                    }
                    else if (conversion.ConversionKind == ConversionKind.NoConversion && convertedExpression.Type?.IsErrorType() == true)
                    {
                        convertedExpression = operand;
                    }
254
                }
255 256
            }

G
gafter 已提交
257 258
            constantValue = convertedExpression.ConstantValue;
            return convertedExpression;
259 260
        }

261 262 263
        /// <summary>
        /// Check that the pattern type is valid for the operand. Return true if an error was reported.
        /// </summary>
264 265
        private bool CheckValidPatternType(
            CSharpSyntaxNode typeSyntax,
266
            TypeSymbol inputType,
267
            TypeSymbol patternType,
268
            bool patternTypeWasInSource,
269 270
            bool isVar,
            DiagnosticBag diagnostics)
271
        {
272
            Debug.Assert((object)inputType != null);
273
            Debug.Assert((object)patternType != null);
274

275
            if (inputType.IsErrorType() || patternType.IsErrorType())
276 277 278
            {
                return false;
            }
279 280 281 282 283 284
            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;
            }
285
            else if (patternType.IsNullableType() && !isVar && patternTypeWasInSource)
286 287
            {
                // It is an error to use pattern-matching with a nullable type, because you'll never get null. Use the underlying type.
288 289
                Error(diagnostics, ErrorCode.ERR_PatternNullableType, typeSyntax, patternType, patternType.GetNullableUnderlyingType());
                return true;
290
            }
291 292 293 294 295
            else if (patternType.IsStatic)
            {
                Error(diagnostics, ErrorCode.ERR_VarDeclIsStaticClass, typeSyntax, patternType);
                return true;
            }
296 297
            else if (!isVar)
            {
298 299 300 301 302 303
                if (patternType.IsDynamic())
                {
                    Error(diagnostics, ErrorCode.ERR_PatternDynamicType, typeSyntax);
                    return true;
                }

304
                HashSet<DiagnosticInfo> useSiteDiagnostics = null;
305
                bool? matchPossible = ExpressionOfTypeMatchesPatternType(Conversions, inputType, patternType, ref useSiteDiagnostics, out Conversion conversion, operandConstantValue: null, operandCouldBeNull: true);
306
                diagnostics.Add(typeSyntax, useSiteDiagnostics);
307
                if (matchPossible != false)
308
                {
309
                    if (!conversion.Exists && (inputType.ContainsTypeParameter() || patternType.ContainsTypeParameter()))
310 311 312 313
                    {
                        // 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)
314
                        {
315
                            Error(diagnostics, ErrorCode.ERR_PatternWrongGenericTypeInVersion, typeSyntax,
316
                                inputType, patternType,
317 318
                                Compilation.LanguageVersion.ToDisplayString(),
                                new CSharpRequiredLanguageVersion(requiredVersion));
319 320
                            return true;
                        }
321 322 323 324
                    }
                }
                else
                {
325
                    Error(diagnostics, ErrorCode.ERR_PatternWrongType, typeSyntax, inputType, patternType);
326
                    return true;
327 328
                }
            }
N
Neal Gafter 已提交
329

330 331 332
            return false;
        }

333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354
        /// <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);
            }

355
            conversion = conversions.ClassifyBuiltInConversion(expressionType, patternType, ref useSiteDiagnostics);
356
            ConstantValue result = Binder.GetIsOperatorConstantResult(expressionType, patternType, conversion.Kind, operandConstantValue, operandCouldBeNull);
357 358 359 360 361 362 363
            return
                (result == null) ? (bool?)null :
                (result == ConstantValue.True) ? true :
                (result == ConstantValue.False) ? false :
                throw ExceptionUtilities.UnexpectedValue(result);
        }

364
        private BoundPattern BindDeclarationPattern(
365
            DeclarationPatternSyntax node,
366
            TypeSymbol inputType,
367 368
            bool hasErrors,
            DiagnosticBag diagnostics)
369
        {
370
            TypeSyntax typeSyntax = node.Type;
371
            BoundTypeExpression boundDeclType = BindPatternType(typeSyntax, inputType, diagnostics, ref hasErrors, out bool isVar);
372
            if (typeSyntax.IsVar && !isVar)
373
            {
374
                // For compatibility, we parse the var pattern with a simple designator as a declaration pattern.
375 376 377 378 379 380
                // So we implement the semantics of the var pattern here, forbidding "var" to bind to a user-declared type.
                if (!hasErrors)
                {
                    diagnostics.Add(ErrorCode.ERR_VarMayNotBindToType, typeSyntax.Location, (boundDeclType.AliasOpt ?? (Symbol)boundDeclType.Type).ToDisplayString());
                }

381
                boundDeclType = new BoundTypeExpression(
382
                    syntax: typeSyntax, aliasOpt: null, inferredType: true, type: inputType, hasErrors: true);
383
            }
384

385
            TypeSymbol declType = boundDeclType.Type;
N
Neal Gafter 已提交
386
            BindPatternDesignation(node, node.Designation, declType, typeSyntax, diagnostics, ref hasErrors, out Symbol variableSymbol, out BoundExpression variableAccess);
387
            return new BoundDeclarationPattern(node, variableSymbol, variableAccess, boundDeclType, isVar, inputType, hasErrors);
388 389 390 391
        }

        private BoundTypeExpression BindPatternType(
            TypeSyntax typeSyntax,
392
            TypeSymbol inputType,
393
            DiagnosticBag diagnostics,
394
            ref bool hasErrors,
395
            out bool isVar)
396
        {
397
            Debug.Assert(inputType != (object)null);
398 399

            AliasSymbol aliasOpt;
400
            TypeSymbol declType = BindTypeOrVarKeyword(typeSyntax, diagnostics, out isVar, out aliasOpt);
401
            if (isVar)
402
            {
403
                declType = inputType;
404 405
            }

406 407 408
            if (declType == (object)null)
            {
                Debug.Assert(hasErrors);
409
                declType = this.CreateErrorType("var");
410 411
            }

412
            BoundTypeExpression boundDeclType = new BoundTypeExpression(typeSyntax, aliasOpt, inferredType: isVar, type: declType);
413 414
            hasErrors |= CheckValidPatternType(typeSyntax, inputType, declType,
                                               isVar: isVar, patternTypeWasInSource: true, diagnostics: diagnostics);
415 416 417 418 419 420 421 422 423 424 425 426 427 428
            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)
429
            {
430
                case SingleVariableDesignationSyntax singleVariableDesignation:
431
                    SyntaxToken identifier = singleVariableDesignation.Identifier;
432 433
                    SourceLocalSymbol localSymbol = this.LookupLocal(identifier);

434 435 436 437 438 439
                    if (localSymbol != (object)null)
                    {
                        if ((InConstructorInitializer || InFieldInitializer) && ContainingMemberOrLambda.ContainingSymbol.Kind == SymbolKind.NamedType)
                        {
                            CheckFeatureAvailability(node, MessageID.IDS_FeatureExpressionVariablesInQueriesAndInitializers, diagnostics);
                        }
440 441

                        localSymbol.SetType(declType);
442 443 444
                        // https://github.com/dotnet/roslyn/issues/28633: need to preserve/compute the val escape. The following line
                        // from master does not work because we don't have a source expression at this point.
                        //         localSymbol.SetValEscape(GetValEscape(sourceExpression, LocalScopeDepth));
445 446 447 448 449 450 451 452 453 454

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

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

                        variableSymbol = localSymbol;
455 456
                        variableAccess = new BoundLocal(
                            syntax: node, localSymbol: localSymbol, constantValueOpt: null, type: declType);
457 458 459 460 461 462 463 464 465 466 467 468 469
                        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;
470 471
                        variableAccess = new BoundFieldAccess(
                            syntax: node, receiver: receiver, fieldSymbol: expressionVariableField, constantValueOpt: null, hasErrors: hasErrors);
472 473 474 475 476 477 478
                        return;
                    }
                case DiscardDesignationSyntax _:
                case null:
                    variableSymbol = null;
                    variableAccess = null;
                    return;
479
                default:
480
                    throw ExceptionUtilities.UnexpectedValue(designation.Kind());
481 482
            }

483
        }
484

485
        TypeSymbol BindRecursivePatternType(TypeSyntax typeSyntax, TypeSymbol inputType, DiagnosticBag diagnostics, ref bool hasErrors, out BoundTypeExpression boundDeclType)
486 487
        {
            if (typeSyntax != null)
488
            {
489
                boundDeclType = BindPatternType(typeSyntax, inputType, diagnostics, ref hasErrors, out bool isVar);
490
                if (isVar)
491
                {
492 493 494 495
                    // 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);
N
Neal Gafter 已提交
496
                        hasErrors = true;
497 498
                    }

499
                    boundDeclType = new BoundTypeExpression(typeSyntax, aliasOpt: null, inferredType: true, type: inputType.StrippedType(), hasErrors: hasErrors);
500
                }
501

502 503 504 505 506
                return boundDeclType.Type;
            }
            else
            {
                boundDeclType = null;
507
                return inputType.StrippedType(); // remove the nullable part of the input's type
508 509
            }
        }
N
Neal Gafter 已提交
510

511
        private BoundPattern BindRecursivePattern(RecursivePatternSyntax node, TypeSymbol inputType, bool hasErrors, DiagnosticBag diagnostics)
512
        {
513
            TypeSyntax typeSyntax = node.Type;
514
            TypeSymbol declType = BindRecursivePatternType(typeSyntax, inputType, diagnostics, ref hasErrors, out BoundTypeExpression boundDeclType);
515

516
            MethodSymbol deconstructMethod = null;
517
            ImmutableArray<BoundSubpattern> deconstructionSubpatterns = default;
518
            if (node.DeconstructionPatternClause != null)
519
            {
520
                deconstructionSubpatterns = BindDeconstructionPatternClause(node.DeconstructionPatternClause, declType, diagnostics, out deconstructMethod, ref hasErrors);
521 522
            }

523 524
            ImmutableArray<BoundSubpattern> properties = default;
            if (node.PropertyPatternClause != null)
525
            {
526
                properties = BindPropertyPatternClause(node.PropertyPatternClause, declType, diagnostics, ref hasErrors);
527 528 529 530
            }

            BindPatternDesignation(node, node.Designation, declType, typeSyntax, diagnostics, ref hasErrors, out Symbol variableSymbol, out BoundExpression variableAccess);
            return new BoundRecursivePattern(
531 532
                syntax: node, declaredType: boundDeclType, inputType: inputType, deconstructMethod: deconstructMethod,
                deconstruction: deconstructionSubpatterns, properties: properties, variable: variableSymbol, variableAccess: variableAccess, hasErrors: hasErrors);
533 534
        }

535 536
        private ImmutableArray<BoundSubpattern> BindDeconstructionPatternClause(
            DeconstructionPatternClauseSyntax node,
537 538 539 540 541 542
            TypeSymbol declType,
            DiagnosticBag diagnostics,
            out MethodSymbol deconstructMethod,
            ref bool hasErrors)
        {
            deconstructMethod = null;
543
            var patterns = ArrayBuilder<BoundSubpattern>.GetInstance(node.Subpatterns.Count);
544 545 546
            if (declType.IsTupleType)
            {
                // It is a tuple type. Work according to its elements
547
                ImmutableArray<TypeSymbol> elementTypes = declType.TupleElementTypes;
548
                if (elementTypes.Length != node.Subpatterns.Count && !hasErrors)
549
                {
550
                    var location = new SourceLocation(node.SyntaxTree, new Text.TextSpan(node.OpenParenToken.SpanStart, node.CloseParenToken.Span.End - node.OpenParenToken.SpanStart));
551
                    diagnostics.Add(ErrorCode.ERR_WrongNumberOfSubpatterns, location, declType, elementTypes.Length, node.Subpatterns.Count);
552 553
                    hasErrors = true;
                }
554
                for (int i = 0; i < node.Subpatterns.Count; i++)
555
                {
556
                    var subpatternSyntax = node.Subpatterns[i];
557
                    bool isError = i >= elementTypes.Length;
558
                    TypeSymbol elementType = isError ? CreateErrorType() : elementTypes[i];
559
                    FieldSymbol foundField = null;
560
                    if (subpatternSyntax.NameColon != null)
561
                    {
562 563
                        string name = subpatternSyntax.NameColon.Name.Identifier.ValueText;
                        foundField = CheckIsTupleElement(subpatternSyntax.NameColon.Name, (NamedTypeSymbol)declType, name, i, diagnostics);
564
                    }
N
Neal Gafter 已提交
565

566
                    BoundSubpattern boundSubpattern = new BoundSubpattern(
567
                        subpatternSyntax,
568
                        foundField,
569
                        BindPattern(subpatternSyntax.Pattern, elementType, isError, diagnostics));
570
                    patterns.Add(boundSubpattern);
571 572 573
                }
            }
            else
574
            {
575 576
                // 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
577
                BoundExpression deconstruct = MakeDeconstructInvocationExpression(
578
                    node.Subpatterns.Count, inputPlaceholder, node, diagnostics, outPlaceholders: out ImmutableArray<BoundDeconstructValuePlaceholder> outPlaceholders);
579
                deconstructMethod = deconstruct.ExpressionSymbol as MethodSymbol;
580 581 582 583 584
                if (deconstructMethod is null)
                {
                    hasErrors = true;
                }

585
                int skippedExtensionParameters = deconstructMethod?.IsExtensionMethod == true ? 1 : 0;
586
                for (int i = 0; i < node.Subpatterns.Count; i++)
587
                {
588
                    var subPattern = node.Subpatterns[i];
589
                    bool isError = outPlaceholders.IsDefaultOrEmpty || i >= outPlaceholders.Length;
590
                    TypeSymbol elementType = isError ? CreateErrorType() : outPlaceholders[i].Type;
591
                    ParameterSymbol parameter = null;
592 593 594 595 596
                    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;
597
                        if (parameterIndex < deconstructMethod.ParameterCount)
598
                        {
599 600 601 602 603 604 605 606 607 608
                            parameter = deconstructMethod.Parameters[parameterIndex];
                            string parameterName = parameter.Name;
                            if (name != parameterName)
                            {
                                diagnostics.Add(ErrorCode.ERR_DeconstructParameterNameMismatch, subPattern.NameColon.Name.Location, name, parameterName);
                            }
                        }
                        else
                        {
                            Debug.Assert(deconstructMethod is ErrorMethodSymbol);
609 610
                        }
                    }
611 612 613 614 615
                    BoundSubpattern boundSubpattern = new BoundSubpattern(
                        subPattern,
                        parameter,
                        BindPattern(subPattern.Pattern, elementType, isError, diagnostics)
                        );
616 617 618 619
                    patterns.Add(boundSubpattern);
                }
            }

620
            return patterns.ToImmutableAndFree();
621 622
        }

623 624 625
        /// <summary>
        /// Check that the given name designates a tuple element at the given index, and return that element.
        /// </summary>
626
        private static FieldSymbol CheckIsTupleElement(SyntaxNode node, NamedTypeSymbol tupleType, string name, int tupleIndex, DiagnosticBag diagnostics)
627 628 629 630 631 632 633 634 635 636 637
        {
            FieldSymbol foundElement = null;
            foreach (var symbol in tupleType.GetMembers(name))
            {
                if (symbol is FieldSymbol field && field.IsTupleElement())
                {
                    foundElement = field;
                    break;
                }
            }

N
Neal Gafter 已提交
638
            if (foundElement is null || foundElement.TupleElementIndex != tupleIndex)
639
            {
N
Neal Gafter 已提交
640
                diagnostics.Add(ErrorCode.ERR_TupleElementNameMismatch, node.Location, name, $"Item{tupleIndex+1}");
641 642 643 644 645
            }

            return foundElement;
        }

646
        private BoundPattern BindVarPattern(VarPatternSyntax node, TypeSymbol inputType, bool hasErrors, DiagnosticBag diagnostics)
647
        {
648
            TypeSymbol declType = inputType;
649
            Symbol foundSymbol = BindTypeOrAliasOrKeyword(node.VarKeyword, node, diagnostics, out bool isVar);
650 651 652
            if (!isVar)
            {
                // Give an error if there is a bindable type "var" in scope
653
                diagnostics.Add(ErrorCode.ERR_VarMayNotBindToType, node.VarKeyword.GetLocation(), foundSymbol.ToDisplayString());
654 655 656
                hasErrors = true;
            }

657
            return BindVarDesignation(node, node.Designation, inputType, hasErrors, diagnostics);
658 659
        }

660
        private BoundPattern BindVarDesignation(VarPatternSyntax node, VariableDesignationSyntax designation, TypeSymbol inputType, bool hasErrors, DiagnosticBag diagnostics)
661 662 663 664 665
        {
            switch (designation.Kind())
            {
                case SyntaxKind.DiscardDesignation:
                    {
666
                        return new BoundDiscardPattern(designation, inputType);
667 668 669
                    }
                case SyntaxKind.SingleVariableDesignation:
                    {
670
                        BindPatternDesignation(
671
                            node: node, designation: designation, declType: inputType, typeSyntax: null, diagnostics: diagnostics,
672
                            hasErrors: ref hasErrors, variableSymbol: out Symbol variableSymbol, variableAccess: out BoundExpression variableAccess);
673 674
                        var boundOperandType = new BoundTypeExpression(syntax: node, aliasOpt: null, type: inputType); // fake a type expression for the variable's type
                        return new BoundDeclarationPattern(designation, variableSymbol, variableAccess, boundOperandType, isVar: true, inputType: inputType, hasErrors: hasErrors);
675 676 677 678
                    }
                case SyntaxKind.ParenthesizedVariableDesignation:
                    {
                        var tupleDesignation = (ParenthesizedVariableDesignationSyntax)designation;
679
                        var subPatterns = ArrayBuilder<BoundSubpattern>.GetInstance(tupleDesignation.Variables.Count);
680
                        MethodSymbol deconstructMethod = null;
681
                        if (inputType.IsTupleType)
682 683
                        {
                            // It is a tuple type. Work according to its elements
684
                            ImmutableArray<TypeSymbol> elementTypes = inputType.TupleElementTypes;
685 686
                            if (elementTypes.Length != tupleDesignation.Variables.Count && !hasErrors)
                            {
687 688
                                var location = new SourceLocation(node.SyntaxTree, 
                                    new Text.TextSpan(tupleDesignation.OpenParenToken.SpanStart, tupleDesignation.CloseParenToken.Span.End - tupleDesignation.OpenParenToken.SpanStart));
689
                                diagnostics.Add(ErrorCode.ERR_WrongNumberOfSubpatterns, location, inputType.TupleElementTypes, elementTypes.Length, tupleDesignation.Variables.Count);
690 691 692 693
                                hasErrors = true;
                            }
                            for (int i = 0; i < tupleDesignation.Variables.Count; i++)
                            {
694
                                var variable = tupleDesignation.Variables[i];
695
                                bool isError = i >= elementTypes.Length;
696
                                TypeSymbol elementType = isError ? CreateErrorType() : elementTypes[i];
697 698
                                BoundPattern pattern = BindVarDesignation(node, variable, elementType, isError, diagnostics);
                                subPatterns.Add(new BoundSubpattern(variable, symbol: null, pattern));
699 700 701 702 703
                            }
                        }
                        else
                        {
                            // It is not a tuple type. Seek an appropriate Deconstruct method.
704
                            var inputPlaceholder = new BoundImplicitReceiver(node, inputType); // A fake receiver expression to permit us to reuse binding logic
705
                            BoundExpression deconstruct = MakeDeconstructInvocationExpression(
706
                                tupleDesignation.Variables.Count, inputPlaceholder, node, diagnostics, outPlaceholders: out ImmutableArray<BoundDeconstructValuePlaceholder> outPlaceholders);
707 708 709
                            deconstructMethod = deconstruct.ExpressionSymbol as MethodSymbol;
                            for (int i = 0; i < tupleDesignation.Variables.Count; i++)
                            {
710
                                var variable = tupleDesignation.Variables[i];
711
                                bool isError = outPlaceholders.IsDefaultOrEmpty || i >= outPlaceholders.Length;
712
                                TypeSymbol elementType = isError ? CreateErrorType() : outPlaceholders[i].Type;
713 714
                                BoundPattern pattern = BindVarDesignation(node, variable, elementType, isError, diagnostics);
                                subPatterns.Add(new BoundSubpattern(variable, symbol: null, pattern));
715 716 717 718
                            }
                        }

                        return new BoundRecursivePattern(
719 720
                            syntax: node, declaredType: null, inputType: inputType, deconstructMethod: deconstructMethod,
                            deconstruction: subPatterns.ToImmutableAndFree(), properties: default, variable: null, variableAccess: null, hasErrors: hasErrors);
721 722 723 724 725 726 727 728
                    }
                default:
                    {
                        throw ExceptionUtilities.UnexpectedValue(designation.Kind());
                    }
            }
        }

729 730
        ImmutableArray<BoundSubpattern> BindPropertyPatternClause(
            PropertyPatternClauseSyntax node,
731 732 733 734
            TypeSymbol inputType,
            DiagnosticBag diagnostics,
            ref bool hasErrors)
        {
735
            var builder = ArrayBuilder<BoundSubpattern>.GetInstance(node.Subpatterns.Count);
736
            foreach (SubpatternSyntax p in node.Subpatterns)
737
            {
738 739
                IdentifierNameSyntax name = p.NameColon?.Name;
                PatternSyntax pattern = p.Pattern;
N
Neal Gafter 已提交
740 741
                Symbol member = null;
                TypeSymbol memberType;
742 743 744 745 746 747 748
                if (name == null)
                {
                    if (!hasErrors)
                    {
                        diagnostics.Add(ErrorCode.ERR_PropertyPatternNameMissing, pattern.Location, pattern);
                    }

N
Neal Gafter 已提交
749
                    memberType = CreateErrorType();
750 751 752 753
                    hasErrors = true;
                }
                else
                {
754
                    member = LookupMemberForPropertyPattern(inputType, name, diagnostics, ref hasErrors, out memberType);
755 756
                }

N
Neal Gafter 已提交
757
                BoundPattern boundPattern = BindPattern(pattern, memberType, hasErrors, diagnostics);
758
                builder.Add(new BoundSubpattern(p, member, boundPattern));
759 760 761 762 763
            }

            return builder.ToImmutableAndFree();
        }

764 765
        private Symbol LookupMemberForPropertyPattern(
            TypeSymbol inputType, IdentifierNameSyntax name, DiagnosticBag diagnostics, ref bool hasErrors, out TypeSymbol memberType)
766
        {
767
            Symbol symbol = BindPropertyPatternMember(inputType, name, ref hasErrors, diagnostics);
768

769
            if (inputType.IsErrorType() || hasErrors || symbol == (object)null)
770
            {
N
Neal Gafter 已提交
771
                memberType = CreateErrorType();
772
            }
773 774 775 776
            else
            {
                memberType = symbol.GetTypeOrReturnType();
            }
777 778

            return symbol;
779
        }
780

781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799
        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,
800
                indexed: false,
801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838
                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;
                        }
                    }
839 840

                    hasErrors = true;
841
                    return boundMember.ExpressionSymbol;
842 843
            }

844 845
            if (hasErrors || !CheckValueKind(node: memberName.Parent, expr: boundMember, valueKind: BindValueKind.RValue,
                                             checkingReceiver: false, diagnostics: diagnostics))
846
            {
847
                hasErrors = true;
848 849 850 851
            }

            return boundMember.ExpressionSymbol;
        }
852 853
    }
}