diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Deconstruct.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Deconstruct.cs index 1626b4610e51a3d9ac4dc2cbed6b0f8c84793aa7..0833bc46821e1f3e7facaad499cee16672523f02 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Deconstruct.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Deconstruct.cs @@ -259,7 +259,7 @@ private BoundExpression FixTupleLiteral(ArrayBuilder che ImmutableArray outPlaceholders; var inputPlaceholder = new BoundDeconstructValuePlaceholder(syntax, this.LocalScopeDepth, type); var deconstructInvocation = MakeDeconstructInvocationExpression(variables.Count, - inputPlaceholder, rightSyntax, diagnostics, out outPlaceholders); + inputPlaceholder, rightSyntax, diagnostics, out outPlaceholders, requireTwoOrMoreElements: true); if (deconstructInvocation.HasAnyErrors) { @@ -589,10 +589,11 @@ private static string ExtractDeconstructResultElementName(BoundExpression expres /// private BoundExpression MakeDeconstructInvocationExpression( int numCheckedVariables, BoundExpression receiver, SyntaxNode rightSyntax, - DiagnosticBag diagnostics, out ImmutableArray outPlaceholders) + DiagnosticBag diagnostics, out ImmutableArray outPlaceholders, + bool requireTwoOrMoreElements) { var receiverSyntax = (CSharpSyntaxNode)receiver.Syntax; - if (numCheckedVariables < 2) + if (requireTwoOrMoreElements && numCheckedVariables < 2) { Error(diagnostics, ErrorCode.ERR_DeconstructTooFewElements, receiverSyntax); outPlaceholders = default(ImmutableArray); @@ -690,9 +691,12 @@ private static string ExtractDeconstructResultElementName(BoundExpression expres private BoundBadExpression MissingDeconstruct(BoundExpression receiver, SyntaxNode rightSyntax, int numParameters, DiagnosticBag diagnostics, out ImmutableArray outPlaceholders, BoundExpression childNode) { - Error(diagnostics, ErrorCode.ERR_MissingDeconstruct, rightSyntax, receiver.Type, numParameters); - outPlaceholders = default; + if (!receiver.Type.IsErrorType()) + { + Error(diagnostics, ErrorCode.ERR_MissingDeconstruct, rightSyntax, receiver.Type, numParameters); + } + outPlaceholders = default; return BadExpression(rightSyntax, childNode); } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs index 061c2aae9f5e5f6c660dda4a73394b7a0d50f993..1de502821461e6ce47898dd3f4495a9f6d3013d9 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs @@ -2682,7 +2682,7 @@ private BoundExpression BindIsOperator(BinaryExpressionSyntax node, DiagnosticBa } var boundConstantPattern = BindConstantPattern( - node.Right, operand.Type, node.Right, node.Right.HasErrors, isPatternDiagnostics, out wasExpression, wasSwitchCase: false); + node.Right, operand.Type, node.Right, node.Right.HasErrors, isPatternDiagnostics, out wasExpression); if (wasExpression) { isTypeDiagnostics.Free(); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs index 6c050c6e9b4a364fb8666dff97604659b4720cd4..3fe62b4a007fcd3dd4a631afe945c312677a8e2a 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs @@ -6,6 +6,8 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; +using System; +using Microsoft.CodeAnalysis.PooledObjects; namespace Microsoft.CodeAnalysis.CSharp { @@ -41,39 +43,44 @@ private BoundExpression BindIsPatternExpression(IsPatternExpressionSyntax node, PatternSyntax node, TypeSymbol operandType, bool hasErrors, - DiagnosticBag diagnostics, - bool wasSwitchCase = false) + DiagnosticBag diagnostics) { switch (node.Kind()) { + case SyntaxKind.DiscardPattern: + return BindDiscardPattern((DiscardPatternSyntax)node, operandType, hasErrors, diagnostics); + case SyntaxKind.DeclarationPattern: - return BindDeclarationPattern( - (DeclarationPatternSyntax)node, operandType, hasErrors, diagnostics); + return BindDeclarationPattern((DeclarationPatternSyntax)node, operandType, hasErrors, diagnostics); case SyntaxKind.ConstantPattern: - return BindConstantPattern( - (ConstantPatternSyntax)node, operandType, hasErrors, diagnostics, wasSwitchCase); + return BindConstantPattern((ConstantPatternSyntax)node, operandType, hasErrors, diagnostics); case SyntaxKind.DeconstructionPattern: + return BindDeconstructionPattern((DeconstructionPatternSyntax)node, operandType, hasErrors, diagnostics); + case SyntaxKind.PropertyPattern: - // PROTOTYPE(patterns2): binding not yet supported for recursive pattern forms. - diagnostics.Add(ErrorCode.ERR_BindToBogus, node.Location, "recursive pattern"); - return new BoundConstantPattern(node, BadExpression(node), ConstantValue.Null, hasErrors: true); + return BindPropertyPattern((PropertyPatternSyntax)node, operandType, hasErrors, diagnostics); default: throw ExceptionUtilities.UnexpectedValue(node.Kind()); } } + 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); + } + private BoundConstantPattern BindConstantPattern( ConstantPatternSyntax node, TypeSymbol operandType, bool hasErrors, - DiagnosticBag diagnostics, - bool wasSwitchCase) + DiagnosticBag diagnostics) { bool wasExpression; - return BindConstantPattern(node, operandType, node.Expression, hasErrors, diagnostics, out wasExpression, wasSwitchCase); + return BindConstantPattern(node, operandType, node.Expression, hasErrors, diagnostics, out wasExpression); } internal BoundConstantPattern BindConstantPattern( @@ -82,8 +89,7 @@ private BoundExpression BindIsPatternExpression(IsPatternExpressionSyntax node, ExpressionSyntax patternExpression, bool hasErrors, DiagnosticBag diagnostics, - out bool wasExpression, - bool wasSwitchCase) + out bool wasExpression) { var expression = BindValue(patternExpression, diagnostics, BindValueKind.RValue); ConstantValue constantValueOpt = null; @@ -239,11 +245,23 @@ internal BoundExpression ConvertPatternExpression(TypeSymbol inputType, CSharpSy bool hasErrors, DiagnosticBag diagnostics) { - Debug.Assert(operandType != (object)null); - var typeSyntax = node.Type; + var boundDeclType = BindPatternType(typeSyntax, operandType, ref hasErrors, out var isVar, diagnostics); + var declType = boundDeclType.Type; + + BindPatternDesignation(node, node.Designation, declType, typeSyntax, diagnostics, ref hasErrors, out var variableSymbol, out var variableAccess); + 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); - bool isVar; AliasSymbol aliasOpt; TypeSymbol declType = BindType(typeSyntax, diagnostics, out isVar, out aliasOpt); if (isVar) @@ -258,7 +276,7 @@ internal BoundExpression ConvertPatternExpression(TypeSymbol inputType, CSharpSy } var boundDeclType = new BoundTypeExpression(typeSyntax, aliasOpt, inferredType: isVar, type: declType); - if (IsOperatorErrors(node, operandType, boundDeclType, diagnostics)) + if (IsOperatorErrors(typeSyntax, operandType, boundDeclType, diagnostics)) { hasErrors = true; } @@ -268,52 +286,288 @@ internal BoundExpression ConvertPatternExpression(TypeSymbol inputType, CSharpSy isVar: isVar, patternTypeWasInSource: true, diagnostics: diagnostics); } - switch (node.Designation.Kind()) + 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) { - case SyntaxKind.SingleVariableDesignation: - break; - case SyntaxKind.DiscardDesignation: - return new BoundDeclarationPattern(node, null, boundDeclType, isVar, hasErrors); + 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; default: - throw ExceptionUtilities.UnexpectedValue(node.Designation.Kind()); + throw ExceptionUtilities.UnexpectedValue(designation.Kind()); } - var designation = (SingleVariableDesignationSyntax)node.Designation; - var identifier = designation.Identifier; - SourceLocalSymbol localSymbol = this.LookupLocal(identifier); + } - if (localSymbol != (object)null) + TypeSymbol BindRecursivePatternType(TypeSyntax typeSyntax, TypeSymbol operandType, ref bool hasErrors, out BoundTypeExpression boundDeclType, DiagnosticBag diagnostics) + { + if (typeSyntax != null) { - if ((InConstructorInitializer || InFieldInitializer) && ContainingMemberOrLambda.ContainingSymbol.Kind == SymbolKind.NamedType) + boundDeclType = BindPatternType(typeSyntax, operandType, ref hasErrors, out var isVar, diagnostics); + if (isVar) { - Error(diagnostics, ErrorCode.ERR_ExpressionVariableInConstructorOrFieldInitializer, node); + // 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); } - localSymbol.SetType(declType); + return boundDeclType.Type; + } + else + { + boundDeclType = null; + return operandType.StrippedType(); // remove the nullable part of the input's type + } + } - // Check for variable declaration errors. - hasErrors |= localSymbol.ScopeBinder.ValidateDeclarationNameConflictsInScope(localSymbol, diagnostics); + private BoundPattern BindDeconstructionPattern(DeconstructionPatternSyntax node, TypeSymbol operandType, bool hasErrors, DiagnosticBag diagnostics) + { + var typeSyntax = node.Type; + TypeSymbol declType = BindRecursivePatternType(typeSyntax, operandType, ref hasErrors, out var boundDeclType, diagnostics); - if (!hasErrors) + var patterns = ArrayBuilder.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) { - hasErrors = CheckRestrictedTypeInAsync(this.ContainingMemberOrLambda, declType, diagnostics, typeSyntax); + 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); } - - return new BoundDeclarationPattern(node, localSymbol, boundDeclType, isVar, hasErrors); } 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(designation); - DiagnosticBag tempDiagnostics = DiagnosticBag.GetInstance(); - expressionVariableField.SetType(declType, tempDiagnostics); - tempDiagnostics.Free(); - BoundExpression receiver = SynthesizeReceiver(node, expressionVariableField, diagnostics); - var variableAccess = new BoundFieldAccess(node, receiver, expressionVariableField, null, hasErrors); - return new BoundDeclarationPattern(node, expressionVariableField, variableAccess, boundDeclType, isVar, hasErrors); + // 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( + node.SubPatterns.Count, inputPlaceholder, node, diagnostics, out var outPlaceholders, requireTwoOrMoreElements: false); + 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); + } + + BindPatternDesignation(node, node.Designation, declType, typeSyntax, diagnostics, ref hasErrors, out var variableSymbol, out var variableAccess); + 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; + TypeSymbol declType = BindRecursivePatternType(typeSyntax, operandType, ref hasErrors, out var boundDeclType, diagnostics); + ImmutableArray<(Symbol property, BoundPattern pattern)> propertiesOpt = BindPropertySubpattern(node.PropertySubpattern, declType, diagnostics, ref hasErrors); + BindPatternDesignation(node, node.Designation, declType, typeSyntax, diagnostics, ref hasErrors, out var variableSymbol, out var variableAccess); + 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; } + + propertyType = symbol.GetTypeOrReturnType(); + return symbol; } + 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), + typeArguments: default(ImmutableArray), + 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; + } } } diff --git a/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder.cs b/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder.cs new file mode 100644 index 0000000000000000000000000000000000000000..e9561e6bd8fdf47c7d09e0999888932a2895c076 --- /dev/null +++ b/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder.cs @@ -0,0 +1,423 @@ +// 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 System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Text; +using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.PooledObjects; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CSharp +{ + internal class DecisionDagBuilder + { + private readonly Conversions Conversions; + private readonly TypeSymbol BooleanType; + private readonly TypeSymbol ObjectType; + + internal DecisionDagBuilder(CSharpCompilation compilation) + { + this.Conversions = compilation.Conversions; + this.BooleanType = compilation.GetSpecialType(SpecialType.System_Boolean); + this.ObjectType = compilation.GetSpecialType(SpecialType.System_Object); + } + + public BoundDecisionDag CreateDecisionDag(BoundPatternSwitchStatement statement) + { + ImmutableArray cases = MakeCases(statement); + BoundDecisionDag dag = MakeDecisionDag(cases); + return dag; + } + + public BoundDagTemp LowerPattern( + BoundExpression input, + BoundPattern pattern, + out ImmutableArray decisions, + out ImmutableArray<(BoundExpression, BoundDagTemp)> bindings) + { + var rootIdentifier = new BoundDagTemp(input.Syntax, input.Type, null, 0); + MakeAndSimplifyDecisionsAndBindings(rootIdentifier, pattern, out decisions, out bindings); + return rootIdentifier; + } + + private ImmutableArray MakeCases(BoundPatternSwitchStatement statement) + { + var rootIdentifier = new BoundDagTemp(statement.Expression.Syntax, statement.Expression.Type, null, 0); + int i = 0; + var builder = ArrayBuilder.GetInstance(); + foreach (var section in statement.SwitchSections) + { + foreach (var label in section.SwitchLabels) + { + builder.Add(MakePartialCaseDecision(++i, rootIdentifier, label)); + } + } + + return builder.ToImmutableAndFree(); + } + + private PartialCaseDecision MakePartialCaseDecision(int index, BoundDagTemp input, BoundPatternSwitchLabel label) + { + MakeAndSimplifyDecisionsAndBindings(input, label.Pattern, out var decisions, out var bindings); + return new PartialCaseDecision(index, decisions, bindings, label.Guard, label.Label); + } + + private void MakeAndSimplifyDecisionsAndBindings( + BoundDagTemp input, + BoundPattern pattern, + out ImmutableArray decisions, + out ImmutableArray<(BoundExpression, BoundDagTemp)> bindings) + { + var decisionsBuilder = ArrayBuilder.GetInstance(); + var bindingsBuilder = ArrayBuilder<(BoundExpression, BoundDagTemp)>.GetInstance(); + // use site diagnostics will have been produced during binding of the patterns, so can be discarded here + HashSet discardedUseSiteDiagnostics = null; + MakeDecisionsAndBindings(input, pattern, decisionsBuilder, bindingsBuilder, ref discardedUseSiteDiagnostics); + + // Now simplify the decisions and bindings. We don't need anything in decisions that does not + // contribute to the result. This will, for example, permit us to match `(2, 3) is (2, _)` without + // fetching `Item2` from the input. + var usedValues = PooledHashSet.GetInstance(); + foreach (var (_, temp) in bindingsBuilder) + { + if (temp.Source != (object)null) + { + usedValues.Add(temp.Source); + } + } + for (int i = decisionsBuilder.Count - 1; i >= 0; i--) + { + switch (decisionsBuilder[i]) + { + case BoundDagEvaluation e: + { + if (usedValues.Contains(e)) + { + if (e.Input.Source != (object)null) + { + usedValues.Add(e.Input.Source); + } + } + else + { + decisionsBuilder.RemoveAt(i); + } + } + break; + case BoundDagDecision d: + { + usedValues.Add(d.Input.Source); + } + break; + default: + throw ExceptionUtilities.UnexpectedValue(decisionsBuilder[i]); + } + } + + // We also do not need to compute any result more than once. This will permit us to fetch + // a property once even if it is used more than once, e.g. `o is { X: P1, X: P2 }` + usedValues.Clear(); + usedValues.Add(input.Source); + for (int i = 0; i < decisionsBuilder.Count; i++) + { + switch (decisionsBuilder[i]) + { + case BoundDagEvaluation e: + if (usedValues.Contains(e)) + { + decisionsBuilder.RemoveAt(i); + i--; + } + else + { + usedValues.Add(e); + } + break; + } + } + + usedValues.Free(); + decisions = decisionsBuilder.ToImmutableAndFree(); + bindings = bindingsBuilder.ToImmutableAndFree(); + } + + private void MakeDecisionsAndBindings( + BoundDagTemp input, + BoundPattern pattern, + ArrayBuilder decisions, + ArrayBuilder<(BoundExpression, BoundDagTemp)> bindings, + ref HashSet discardedUseSiteDiagnostics) + { + switch (pattern) + { + case BoundDeclarationPattern declaration: + MakeDecisionsAndBindings(input, declaration, decisions, bindings, ref discardedUseSiteDiagnostics); + break; + case BoundConstantPattern constant: + MakeDecisionsAndBindings(input, constant, decisions, bindings, ref discardedUseSiteDiagnostics); + break; + case BoundDiscardPattern wildcard: + // Nothing to do. It always matches. + break; + case BoundRecursivePattern recursive: + MakeDecisionsAndBindings(input, recursive, decisions, bindings, ref discardedUseSiteDiagnostics); + break; + default: + throw new NotImplementedException(pattern.Kind.ToString()); + } + } + + private void MakeDecisionsAndBindings( + BoundDagTemp input, + BoundDeclarationPattern declaration, + ArrayBuilder decisions, + ArrayBuilder<(BoundExpression, BoundDagTemp)> bindings, + ref HashSet discardedUseSiteDiagnostics) + { + var type = declaration.DeclaredType.Type; + var syntax = declaration.Syntax; + + // Add a null and type test if needed. + if (!declaration.IsVar) + { + NullCheck(input, declaration.Syntax, decisions); + input = ConvertToType(input, declaration.Syntax, type, decisions, ref discardedUseSiteDiagnostics); + } + + var left = declaration.VariableAccess; + if (left != null) + { + Debug.Assert(left.Type == input.Type); + bindings.Add((left, input)); + } + else + { + Debug.Assert(declaration.Variable == null); + } + } + + private void NullCheck( + BoundDagTemp input, + SyntaxNode syntax, + ArrayBuilder decisions) + { + if (input.Type.CanContainNull()) + { + // Add a null test + decisions.Add(new BoundNonNullDecision(syntax, input)); + } + } + + private BoundDagTemp ConvertToType( + BoundDagTemp input, + SyntaxNode syntax, + TypeSymbol type, + ArrayBuilder decisions, + ref HashSet discardedUseSiteDiagnostics) + { + if (input.Type != type) + { + var inputType = input.Type.StrippedType(); // since a null check has already been done + var conversion = Conversions.ClassifyBuiltInConversion(inputType, type, ref discardedUseSiteDiagnostics); + if (input.Type.IsDynamic() ? type.SpecialType == SpecialType.System_Object : conversion.IsImplicit) + { + // type test not needed, only the type cast + } + else + { + // both type test and cast needed + decisions.Add(new BoundTypeDecision(syntax, type, input)); + } + + var evaluation = new BoundDagTypeEvaluation(syntax, type, input); + input = new BoundDagTemp(syntax, type, evaluation, 0); + decisions.Add(evaluation); + } + + return input; + } + + private void MakeDecisionsAndBindings( + BoundDagTemp input, + BoundConstantPattern constant, + ArrayBuilder decisions, + ArrayBuilder<(BoundExpression, BoundDagTemp)> bindings, + ref HashSet discardedUseSiteDiagnostics) + { + input = ConvertToType(input, constant.Syntax, constant.Value.Type, decisions, ref discardedUseSiteDiagnostics); + decisions.Add(new BoundValueDecision(constant.Syntax, constant.ConstantValue, input)); + } + + private void MakeDecisionsAndBindings( + BoundDagTemp input, + BoundRecursivePattern recursive, + ArrayBuilder decisions, + ArrayBuilder<(BoundExpression, BoundDagTemp)> bindings, + ref HashSet discardedUseSiteDiagnostics) + { + Debug.Assert(input.Type == recursive.InputType); + NullCheck(input, recursive.Syntax, decisions); + if (recursive.DeclaredType != null && recursive.DeclaredType.Type != input.Type) + { + input = ConvertToType(input, recursive.Syntax, recursive.DeclaredType.Type, decisions, ref discardedUseSiteDiagnostics); + } + + if (!recursive.Deconstruction.IsDefault) + { + // we have a "deconstruction" form, which is either an invocation of a Deconstruct method, or a disassembly of a tuple + if (recursive.DeconstructMethodOpt != null) + { + var method = recursive.DeconstructMethodOpt; + var evaluation = new BoundDagDeconstructEvaluation(recursive.Syntax, method, input); + decisions.Add(evaluation); + int extensionExtra = method.IsStatic ? 1 : 0; + int count = Math.Min(method.ParameterCount - extensionExtra, recursive.Deconstruction.Length); + for (int i = 0; i < count; i++) + { + var pattern = recursive.Deconstruction[i]; + var syntax = pattern.Syntax; + var output = new BoundDagTemp(syntax, method.Parameters[i + extensionExtra].Type, evaluation, i); + MakeDecisionsAndBindings(output, pattern, decisions, bindings, ref discardedUseSiteDiagnostics); + } + } + else if (input.Type.IsTupleType) + { + var elements = input.Type.TupleElements; + var elementTypes = input.Type.TupleElementTypes; + int count = Math.Min(elementTypes.Length, recursive.Deconstruction.Length); + for (int i = 0; i < count; i++) + { + var pattern = recursive.Deconstruction[i]; + var syntax = pattern.Syntax; + var field = elements[i]; + var evaluation = new BoundDagFieldEvaluation(syntax, field, input); // fetch the ItemN field + decisions.Add(evaluation); + var output = new BoundDagTemp(syntax, field.Type, evaluation, 0); + MakeDecisionsAndBindings(output, pattern, decisions, bindings, ref discardedUseSiteDiagnostics); + } + } + else + { + // TODO(patterns2): This should not occur except in error cases. Perhaps this will be used to handle the ITuple case. + throw new NotImplementedException(); + } + } + + if (recursive.PropertiesOpt != null) + { + // we have a "property" form + for (int i = 0; i < recursive.PropertiesOpt.Length; i++) + { + var prop = recursive.PropertiesOpt[i]; + var symbol = prop.symbol; + BoundDagEvaluation evaluation; + switch (symbol) + { + case PropertySymbol property: + evaluation = new BoundDagPropertyEvaluation(prop.pattern.Syntax, property, input); + break; + case FieldSymbol field: + evaluation = new BoundDagFieldEvaluation(prop.pattern.Syntax, field, input); + break; + default: + throw ExceptionUtilities.UnexpectedValue(symbol.Kind); + } + decisions.Add(evaluation); + var output = new BoundDagTemp(prop.pattern.Syntax, prop.symbol.GetTypeOrReturnType(), evaluation, 0); + MakeDecisionsAndBindings(output, prop.pattern, decisions, bindings, ref discardedUseSiteDiagnostics); + } + } + + if (recursive.VariableAccess != null) + { + // we have a "variable" declaration + bindings.Add((recursive.VariableAccess, input)); + } + } + + private static BoundDecisionDag MakeDecisionDag(ImmutableArray cases) + { + throw new NotImplementedException(); + } + } + + internal class PartialCaseDecision + { + public readonly int Index; + public readonly ImmutableArray Decisions; + public readonly ImmutableArray<(BoundExpression, BoundDagTemp)> Bindings; + public readonly BoundExpression WhereClause; + public readonly LabelSymbol CaseLabel; + public PartialCaseDecision( + int Index, + ImmutableArray Decisions, + ImmutableArray<(BoundExpression, BoundDagTemp)> Bindings, + BoundExpression WhereClause, + LabelSymbol CaseLabel) + { + this.Index = Index; + this.Decisions = Decisions; + this.Bindings = Bindings; + this.WhereClause = WhereClause; + this.CaseLabel = CaseLabel; + } + } + + partial class BoundDagEvaluation + { + public override bool Equals(object obj) => obj is BoundDagEvaluation other && this.Equals(other); + public bool Equals(BoundDagEvaluation other) + { + return other != (object)null && this.Kind == other.Kind && this.Input.Equals(other.Input) && this.Symbol == other.Symbol; + } + private Symbol Symbol + { + get + { + switch (this) + { + case BoundDagFieldEvaluation e: return e.Field; + case BoundDagPropertyEvaluation e: return e.Property; + case BoundDagTypeEvaluation e: return e.Type; + case BoundDagDeconstructEvaluation e: return e.DeconstructMethod; + default: throw ExceptionUtilities.UnexpectedValue(this.Kind); + } + } + } + public override int GetHashCode() + { + return this.Input.GetHashCode() ^ (this.Symbol?.GetHashCode() ?? 0); + } + public static bool operator ==(BoundDagEvaluation left, BoundDagEvaluation right) + { + return (left == (object)null) ? right == (object)null : left.Equals(right); + } + public static bool operator !=(BoundDagEvaluation left, BoundDagEvaluation right) + { + return !left.Equals(right); + } + } + + partial class BoundDagTemp + { + public override bool Equals(object obj) => obj is BoundDagTemp other && this.Equals(other); + public bool Equals(BoundDagTemp other) + { + return other != (object)null && this.Type == other.Type && object.Equals(this.Source, other.Source) && this.Index == other.Index; + } + public override int GetHashCode() + { + return this.Type.GetHashCode() ^ (this.Source?.GetHashCode() ?? 0) ^ this.Index; + } + public static bool operator ==(BoundDagTemp left, BoundDagTemp right) + { + return left.Equals(right); + } + public static bool operator !=(BoundDagTemp left, BoundDagTemp right) + { + return !left.Equals(right); + } + } +} diff --git a/src/Compilers/CSharp/Portable/Binder/ExpressionVariableFinder.cs b/src/Compilers/CSharp/Portable/Binder/ExpressionVariableFinder.cs index f888828b62369bfd075c956b911271219c5a68b3..ec8c6f3f15b13ca58137385cfb2880addf6bdad0 100644 --- a/src/Compilers/CSharp/Portable/Binder/ExpressionVariableFinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/ExpressionVariableFinder.cs @@ -200,7 +200,31 @@ public override void VisitDeclarationPattern(DeclarationPatternSyntax node) base.VisitDeclarationPattern(node); } + public override void VisitDeconstructionPattern(DeconstructionPatternSyntax node) + { + var variable = MakePatternVariable(node, _nodeToBind); + if ((object)variable != null) + { + _variablesBuilder.Add(variable); + } + + base.VisitDeconstructionPattern(node); + } + + public override void VisitPropertyPattern(PropertyPatternSyntax node) + { + var variable = MakePatternVariable(node, _nodeToBind); + if ((object)variable != null) + { + _variablesBuilder.Add(variable); + } + + base.VisitPropertyPattern(node); + } + protected abstract TFieldOrLocalSymbol MakePatternVariable(DeclarationPatternSyntax node, SyntaxNode nodeToBind); + protected abstract TFieldOrLocalSymbol MakePatternVariable(DeconstructionPatternSyntax node, SyntaxNode nodeToBind); + protected abstract TFieldOrLocalSymbol MakePatternVariable(PropertyPatternSyntax node, SyntaxNode nodeToBind); public override void VisitParenthesizedLambdaExpression(ParenthesizedLambdaExpressionSyntax node) { } public override void VisitSimpleLambdaExpression(SimpleLambdaExpressionSyntax node) { } @@ -434,10 +458,25 @@ internal class ExpressionVariableFinder : ExpressionVariableFinder protected override LocalSymbol MakePatternVariable(DeclarationPatternSyntax node, SyntaxNode nodeToBind) { - var designation = node.Designation as SingleVariableDesignationSyntax; + return MakePatternVariable(node.Type, node.Designation, nodeToBind); + } + + protected override LocalSymbol MakePatternVariable(DeconstructionPatternSyntax node, SyntaxNode nodeToBind) + { + return MakePatternVariable(node.Type, node.Designation, nodeToBind); + } + + protected override LocalSymbol MakePatternVariable(PropertyPatternSyntax node, SyntaxNode nodeToBind) + { + return MakePatternVariable(node.Type, node.Designation, nodeToBind); + } + + private LocalSymbol MakePatternVariable(TypeSyntax type, VariableDesignationSyntax variableDesignation, SyntaxNode nodeToBind) + { + var designation = variableDesignation as SingleVariableDesignationSyntax; if (designation == null) { - Debug.Assert(node.Designation.Kind() == SyntaxKind.DiscardDesignation); + Debug.Assert(variableDesignation == null || variableDesignation.Kind() == SyntaxKind.DiscardDesignation); return null; } @@ -453,7 +492,7 @@ protected override LocalSymbol MakePatternVariable(DeclarationPatternSyntax node _scopeBinder.ContainingMemberOrLambda, scopeBinder: _scopeBinder, nodeBinder: _enclosingBinder, - typeSyntax: node.Type, + typeSyntax: type, identifierToken: designation.Identifier, kind: LocalDeclarationKind.PatternVariable, nodeToBind: nodeToBind, @@ -561,6 +600,34 @@ protected override Symbol MakePatternVariable(DeclarationPatternSyntax node, Syn _containingFieldOpt, nodeToBind); } + protected override Symbol MakePatternVariable(DeconstructionPatternSyntax node, SyntaxNode nodeToBind) + { + var designation = node.Designation as SingleVariableDesignationSyntax; + if (designation == null) + { + return null; + } + + return GlobalExpressionVariable.Create( + _containingType, _modifiers, node.Type, + designation.Identifier.ValueText, designation, designation.GetLocation(), + _containingFieldOpt, nodeToBind); + } + + protected override Symbol MakePatternVariable(PropertyPatternSyntax node, SyntaxNode nodeToBind) + { + var designation = node.Designation as SingleVariableDesignationSyntax; + if (designation == null) + { + return null; + } + + return GlobalExpressionVariable.Create( + _containingType, _modifiers, node.Type, + designation.Identifier.ValueText, designation, designation.GetLocation(), + _containingFieldOpt, nodeToBind); + } + protected override Symbol MakeDeclarationExpressionVariable(DeclarationExpressionSyntax node, SingleVariableDesignationSyntax designation, BaseArgumentListSyntax argumentListSyntaxOpt, SyntaxNode nodeToBind) { return GlobalExpressionVariable.Create( diff --git a/src/Compilers/CSharp/Portable/Binder/PatternSwitchBinder.cs b/src/Compilers/CSharp/Portable/Binder/PatternSwitchBinder.cs index 8726ca16c1546b95785ade5758646a18dbad3ea3..c662c88379c67534877b92a52e0a2fbc88b12039 100644 --- a/src/Compilers/CSharp/Portable/Binder/PatternSwitchBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/PatternSwitchBinder.cs @@ -54,9 +54,10 @@ internal override BoundStatement BindSwitchExpressionAndSections(SwitchStatement BindPatternSwitchSections(originalBinder, out defaultLabel, out isComplete, out var someCaseMatches, diagnostics); var locals = GetDeclaredLocalsForScope(node); var functions = GetDeclaredLocalFunctionsForScope(node); + BoundDecisionDag decisionDag = null; // not relevant to the C# 7 pattern binder return new BoundPatternSwitchStatement( node, boundSwitchExpression, someCaseMatches, - locals, functions, switchSections, defaultLabel, this.BreakLabel, this, isComplete); + locals, functions, switchSections, defaultLabel, this.BreakLabel, decisionDag, isComplete); } /// @@ -157,7 +158,7 @@ internal override void BindPatternSwitchLabelForInference(CasePatternSwitchLabel { patternMatches = null; } - else if (boundLabel.Pattern.Kind == BoundKind.WildcardPattern) + else if (boundLabel.Pattern.Kind == BoundKind.DiscardPattern) { // wildcard pattern matches anything patternMatches = true; @@ -211,7 +212,7 @@ internal override void BindPatternSwitchLabelForInference(CasePatternSwitchLabel var caseLabelSyntax = (CaseSwitchLabelSyntax)node; bool wasExpression; var pattern = sectionBinder.BindConstantPattern( - node, SwitchGoverningType, caseLabelSyntax.Value, node.HasErrors, diagnostics, out wasExpression, wasSwitchCase: true); + node, SwitchGoverningType, caseLabelSyntax.Value, node.HasErrors, diagnostics, out wasExpression); bool hasErrors = pattern.HasErrors; var constantValue = pattern.ConstantValue; if (!hasErrors && @@ -237,7 +238,7 @@ internal override void BindPatternSwitchLabelForInference(CasePatternSwitchLabel case SyntaxKind.DefaultSwitchLabel: { var defaultLabelSyntax = (DefaultSwitchLabelSyntax)node; - var pattern = new BoundWildcardPattern(node); + var pattern = new BoundDiscardPattern(node); bool hasErrors = pattern.HasErrors; if (defaultLabel != null) { @@ -258,7 +259,7 @@ internal override void BindPatternSwitchLabelForInference(CasePatternSwitchLabel { var matchLabelSyntax = (CasePatternSwitchLabelSyntax)node; var pattern = sectionBinder.BindPattern( - matchLabelSyntax.Pattern, SwitchGoverningType, node.HasErrors, diagnostics, wasSwitchCase: true); + matchLabelSyntax.Pattern, SwitchGoverningType, node.HasErrors, diagnostics); return new BoundPatternSwitchLabel(node, label, pattern, matchLabelSyntax.WhenClause != null ? sectionBinder.BindBooleanExpression(matchLabelSyntax.WhenClause.Condition, diagnostics) : null, true, node.HasErrors); diff --git a/src/Compilers/CSharp/Portable/Binder/PatternSwitchBinder2.cs b/src/Compilers/CSharp/Portable/Binder/PatternSwitchBinder2.cs new file mode 100644 index 0000000000000000000000000000000000000000..a71e3c7b8324b2bd5e6e0893539c0a47deed3a91 --- /dev/null +++ b/src/Compilers/CSharp/Portable/Binder/PatternSwitchBinder2.cs @@ -0,0 +1,234 @@ +// 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 System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.PooledObjects; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CSharp +{ + // We use a subclass of SwitchBinder for the pattern-matching switch statement until we have completed + // a totally compatible implementation of switch that also accepts pattern-matching constructs. + internal partial class PatternSwitchBinder2 : SwitchBinder + { + internal PatternSwitchBinder2(Binder next, SwitchStatementSyntax switchSyntax) : base(next, switchSyntax) + { + } + + /// + /// When pattern-matching is enabled, we use a completely different binder and binding + /// strategy for switch statements. Once we have confirmed that it is totally upward + /// compatible with the existing syntax and semantics, we will merge them completely. + /// However, until we have edit-and-continue working, we continue using the old binder + /// when we can. + /// + private bool UseV8SwitchBinder + { + get + { + var parseOptions = SwitchSyntax?.SyntaxTree?.Options as CSharpParseOptions; + return + parseOptions?.Features.ContainsKey("testV8SwitchBinder") == true || + HasPatternSwitchSyntax(SwitchSyntax) || + !SwitchGoverningType.IsValidV6SwitchGoverningType(); + } + } + + internal override BoundStatement BindSwitchExpressionAndSections(SwitchStatementSyntax node, Binder originalBinder, DiagnosticBag diagnostics) + { + // If it is a valid C# 6 switch statement, we use the old binder to bind it. + if (!UseV8SwitchBinder) return base.BindSwitchExpressionAndSections(node, originalBinder, diagnostics); + + Debug.Assert(SwitchSyntax.Equals(node)); + + // Bind switch expression and set the switch governing type. + var boundSwitchExpression = SwitchGoverningExpression; + diagnostics.AddRange(SwitchGoverningDiagnostics); + + BoundPatternSwitchLabel defaultLabel; + bool isComplete; + ImmutableArray switchSections = + BindPatternSwitchSections(originalBinder, out defaultLabel, out isComplete, out var someCaseMatches, diagnostics); + var locals = GetDeclaredLocalsForScope(node); + var functions = GetDeclaredLocalFunctionsForScope(node); + BoundDecisionDag decisionDag = null; // we'll compute it later + return new BoundPatternSwitchStatement( + syntax: node, + expression: boundSwitchExpression, + someLabelAlwaysMatches: someCaseMatches, + innerLocals: locals, + innerLocalFunctions: functions, + switchSections: switchSections, + defaultLabel: defaultLabel, + breakLabel: this.BreakLabel, + decisionDag: decisionDag, + isComplete: isComplete); + } + + /// + /// Bind a pattern switch label in order to force inference of the type of pattern variables. + /// + internal override void BindPatternSwitchLabelForInference(CasePatternSwitchLabelSyntax node, DiagnosticBag diagnostics) + { + // node should be a label of this switch statement. + Debug.Assert(this.SwitchSyntax == node.Parent.Parent); + + // This simulates enough of the normal binding path of a switch statement to cause + // the label's pattern variables to have their types inferred, if necessary. + // It also binds the when clause, and therefore any pattern and out variables there. + BoundPatternSwitchLabel defaultLabel = null; + BindPatternSwitchSectionLabel( + sectionBinder: GetBinder(node.Parent), + node: node, + label: LabelsByNode[node], + defaultLabel: ref defaultLabel, + diagnostics: diagnostics); + } + + /// + /// Bind the pattern switch labels, reporting in the process which cases are subsumed. The strategy + /// implemented with the help of , is to start with an empty + /// decision tree, and for each case we visit the decision tree to see if the case is subsumed. If it + /// is, we report an error. If it is not subsumed and there is no guard expression, we then add it to + /// the decision tree. + /// + private ImmutableArray BindPatternSwitchSections( + Binder originalBinder, + out BoundPatternSwitchLabel defaultLabel, + out bool isComplete, + out bool someCaseMatches, + DiagnosticBag diagnostics) + { + defaultLabel = null; + + // someCaseMatches will be set to true if some single case label would handle all inputs + someCaseMatches = false; + + // Bind match sections + var boundPatternSwitchSectionsBuilder = ArrayBuilder.GetInstance(); + SubsumptionDiagnosticBuilder subsumption = new SubsumptionDiagnosticBuilder(ContainingMemberOrLambda, SwitchSyntax, this.Conversions, SwitchGoverningType); + foreach (var sectionSyntax in SwitchSyntax.Sections) + { + var section = BindPatternSwitchSection(sectionSyntax, originalBinder, ref defaultLabel, ref someCaseMatches, subsumption, diagnostics); + boundPatternSwitchSectionsBuilder.Add(section); + } + + isComplete = defaultLabel != null || subsumption.IsComplete || someCaseMatches; + return boundPatternSwitchSectionsBuilder.ToImmutableAndFree(); + } + + /// + /// Bind the pattern switch section, producing subsumption diagnostics. + /// + /// + /// + /// If a default label is found in this section, assigned that label + /// If a case is found that would always match the input, set to true + /// A helper class that uses a decision tree to produce subsumption diagnostics. + /// + /// + private BoundPatternSwitchSection BindPatternSwitchSection( + SwitchSectionSyntax node, + Binder originalBinder, + ref BoundPatternSwitchLabel defaultLabel, + ref bool someCaseMatches, + SubsumptionDiagnosticBuilder subsumption, + DiagnosticBag diagnostics) + { + // Bind match section labels + var boundLabelsBuilder = ArrayBuilder.GetInstance(); + var sectionBinder = originalBinder.GetBinder(node); // this binder can bind pattern variables from the section. + Debug.Assert(sectionBinder != null); + var labelsByNode = LabelsByNode; + + foreach (var labelSyntax in node.Labels) + { + LabelSymbol label = labelsByNode[labelSyntax]; + BoundPatternSwitchLabel boundLabel = BindPatternSwitchSectionLabel(sectionBinder, labelSyntax, label, ref defaultLabel, diagnostics); + boundLabelsBuilder.Add(boundLabel); + } + + // Bind switch section statements + var boundStatementsBuilder = ArrayBuilder.GetInstance(); + foreach (var statement in node.Statements) + { + boundStatementsBuilder.Add(sectionBinder.BindStatement(statement, diagnostics)); + } + + return new BoundPatternSwitchSection(node, sectionBinder.GetDeclaredLocalsForScope(node), boundLabelsBuilder.ToImmutableAndFree(), boundStatementsBuilder.ToImmutableAndFree()); + } + + private BoundPatternSwitchLabel BindPatternSwitchSectionLabel( + Binder sectionBinder, + SwitchLabelSyntax node, + LabelSymbol label, + ref BoundPatternSwitchLabel defaultLabel, + DiagnosticBag diagnostics) + { + // Until we've determined whether or not the switch label is reachable, we assume it + // is. The caller updates isReachable after determining if the label is subsumed. + const bool isReachable = true; + + switch (node.Kind()) + { + case SyntaxKind.CaseSwitchLabel: + { + var caseLabelSyntax = (CaseSwitchLabelSyntax)node; + bool wasExpression; + var pattern = sectionBinder.BindConstantPattern( + node, SwitchGoverningType, caseLabelSyntax.Value, node.HasErrors, diagnostics, out wasExpression); + bool hasErrors = pattern.HasErrors; + var constantValue = pattern.ConstantValue; + if (!hasErrors && + (object)constantValue != null && + pattern.Value.Type == SwitchGoverningType && + this.FindMatchingSwitchCaseLabel(constantValue, caseLabelSyntax) != label) + { + diagnostics.Add(ErrorCode.ERR_DuplicateCaseLabel, node.Location, pattern.ConstantValue.GetValueToDisplay() ?? label.Name); + hasErrors = true; + } + + if (caseLabelSyntax.Value.Kind() == SyntaxKind.DefaultLiteralExpression) + { + diagnostics.Add(ErrorCode.WRN_DefaultInSwitch, caseLabelSyntax.Value.Location); + } + + return new BoundPatternSwitchLabel(node, label, pattern, null, isReachable, hasErrors); + } + + case SyntaxKind.DefaultSwitchLabel: + { + var defaultLabelSyntax = (DefaultSwitchLabelSyntax)node; + var pattern = new BoundDiscardPattern(node); + bool hasErrors = pattern.HasErrors; + if (defaultLabel != null) + { + diagnostics.Add(ErrorCode.ERR_DuplicateCaseLabel, node.Location, label.Name); + hasErrors = true; + } + + // Note that this is semantically last! The caller will place it in the decision tree + // in the final position. + defaultLabel = new BoundPatternSwitchLabel(node, label, pattern, null, isReachable, hasErrors); + return defaultLabel; + } + + case SyntaxKind.CasePatternSwitchLabel: + { + var matchLabelSyntax = (CasePatternSwitchLabelSyntax)node; + var pattern = sectionBinder.BindPattern( + matchLabelSyntax.Pattern, SwitchGoverningType, node.HasErrors, diagnostics); + return new BoundPatternSwitchLabel(node, label, pattern, + matchLabelSyntax.WhenClause != null ? sectionBinder.BindBooleanExpression(matchLabelSyntax.WhenClause.Condition, diagnostics) : null, + isReachable, node.HasErrors); + } + + default: + throw ExceptionUtilities.UnexpectedValue(node); + } + } + } +} diff --git a/src/Compilers/CSharp/Portable/Binder/SubsumptionDiagnosticBuilder.cs b/src/Compilers/CSharp/Portable/Binder/SubsumptionDiagnosticBuilder.cs index 3a837bd5f2ca3fb4f2ea258b2cf648fa02cbc9a5..fc870317ab9b8c8ed61f484c533c6a38cdc91225 100644 --- a/src/Compilers/CSharp/Portable/Binder/SubsumptionDiagnosticBuilder.cs +++ b/src/Compilers/CSharp/Portable/Binder/SubsumptionDiagnosticBuilder.cs @@ -27,7 +27,7 @@ internal sealed class SubsumptionDiagnosticBuilder : DecisionTreeBuilder { // For the purpose of computing subsumption, we ignore the input expression's constant // value. Therefore we create a fake expression here that doesn't contain the value. - var placeholderExpression = new BoundDup(syntax, RefKind.None, switchGoverningType); + var placeholderExpression = new BoundImplicitReceiver(syntax, switchGoverningType); _subsumptionTree = CreateEmptyDecisionTree(placeholderExpression); } @@ -271,7 +271,7 @@ private ErrorCode CheckSubsumed(BoundPattern pattern, DecisionTree decisionTree, throw ExceptionUtilities.UnexpectedValue(decisionTree.Kind); } } - case BoundKind.WildcardPattern: + case BoundKind.DiscardPattern: // because we always handle `default:` last, and that is the only way to get a wildcard pattern, // we should never need to see if it subsumes something else. throw ExceptionUtilities.UnexpectedValue(decisionTree.Kind); diff --git a/src/Compilers/CSharp/Portable/Binder/SwitchBinder.cs b/src/Compilers/CSharp/Portable/Binder/SwitchBinder.cs index 9f8f3116ff20d1b818455cf10278b2712b66b1c7..1f7afef1b75dd6b5c86830e3911cc1ee0e754839 100644 --- a/src/Compilers/CSharp/Portable/Binder/SwitchBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/SwitchBinder.cs @@ -40,10 +40,34 @@ internal static SwitchBinder Create(Binder next, SwitchStatementSyntax switchSyn (parseOptions?.IsFeatureEnabled(MessageID.IDS_FeaturePatternMatching) != false || parseOptions?.Features.ContainsKey("testV7SwitchBinder") != false || switchSyntax.HasErrors && HasPatternSwitchSyntax(switchSyntax)) - ? new PatternSwitchBinder(next, switchSyntax) + ? ( HasRecursivePatternSwitchSyntax(switchSyntax) + ? new PatternSwitchBinder2(next, switchSyntax) + : (SwitchBinder)new PatternSwitchBinder(next, switchSyntax)) : new SwitchBinder(next, switchSyntax); } + private static bool HasRecursivePatternSwitchSyntax(SwitchStatementSyntax switchSyntax) + { + foreach (var section in switchSyntax.Sections) + { + foreach (var label in section.Labels) + { + if (label is CasePatternSwitchLabelSyntax s) + { + switch (s.Pattern.Kind()) + { + case SyntaxKind.DeconstructionPattern: + case SyntaxKind.PropertyPattern: + case SyntaxKind.DiscardPattern: + return true; + } + } + } + } + + return false; + } + internal static bool HasPatternSwitchSyntax(SwitchStatementSyntax switchSyntax) { foreach (var section in switchSyntax.Sections) @@ -242,7 +266,7 @@ private void BuildSwitchLabels(SyntaxList labelsSyntax, Binde // bind the pattern, to cause its pattern variables to be inferred if necessary var matchLabel = (CasePatternSwitchLabelSyntax)labelSyntax; var pattern = sectionBinder.BindPattern( - matchLabel.Pattern, SwitchGoverningType, labelSyntax.HasErrors, tempDiagnosticBag, wasSwitchCase: true); + matchLabel.Pattern, SwitchGoverningType, labelSyntax.HasErrors, tempDiagnosticBag); break; default: diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml index 2f9a3867f138b997bda03d36e3379f6ee3bd3850..69d230e125fcbf77098b606ced46878feab3adc0 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml @@ -799,16 +799,77 @@ - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Input is the input to the decision point. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1588,6 +1649,14 @@ + + + + + + + + @@ -1599,17 +1668,28 @@ free. The necessity of this member is a consequence of a design issue documented in https://github.com/dotnet/roslyn/issues/13960 . When that is fixed this field can be removed. --> - + - - - - - - + + + + + + + + + + diff --git a/src/Compilers/CSharp/Portable/BoundTree/Constructors.cs b/src/Compilers/CSharp/Portable/BoundTree/Constructors.cs index fd05b72b55635d1b9ebda448726f253c42e8586d..977f5d1049224bded94085fc71211e7fd0b8b4df 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/Constructors.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/Constructors.cs @@ -555,12 +555,4 @@ public BoundTryStatement(SyntaxNode syntax, BoundBlock tryBlock, ImmutableArray< { } } - - internal partial class BoundDeclarationPattern - { - public BoundDeclarationPattern(SyntaxNode syntax, LocalSymbol localSymbol, BoundTypeExpression declaredType, bool isVar, bool hasErrors = false) - : this(syntax, localSymbol, localSymbol == null ? new BoundDiscardExpression(syntax, declaredType.Type) : (BoundExpression)new BoundLocal(syntax, localSymbol, null, declaredType.Type), declaredType, isVar, hasErrors) - { - } - } } diff --git a/src/Compilers/CSharp/Portable/BoundTree/DecisionTreeBuilder.cs b/src/Compilers/CSharp/Portable/BoundTree/DecisionTreeBuilder.cs index 1d480568cc39588526203d908f8044d5431dc304..3be773555d1a2d92e53643048b4b6696d88ef1c7 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/DecisionTreeBuilder.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/DecisionTreeBuilder.cs @@ -116,7 +116,12 @@ protected DecisionTree AddToDecisionTree(DecisionTree decisionTree, SyntaxNode s { var declarationPattern = (BoundDeclarationPattern)pattern; DecisionMaker maker = - (e, t) => new DecisionTree.Guarded(e, t, ImmutableArray.Create(new KeyValuePair(e, declarationPattern.VariableAccess)), sectionSyntax, guard, label); + (e, t) => new DecisionTree.Guarded( + e, t, + (declarationPattern.VariableAccess == null) + ? ImmutableArray>.Empty + : ImmutableArray.Create(new KeyValuePair(e, declarationPattern.VariableAccess)), + sectionSyntax, guard, label); if (declarationPattern.IsVar) { return Add(decisionTree, maker); @@ -126,7 +131,7 @@ protected DecisionTree AddToDecisionTree(DecisionTree decisionTree, SyntaxNode s return AddByType(decisionTree, declarationPattern.DeclaredType.Type, maker); } } - case BoundKind.WildcardPattern: + case BoundKind.DiscardPattern: // We do not yet support a wildcard pattern syntax. It is used exclusively // to model the "default:" case, which is handled specially in the caller. default: diff --git a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs index 7a23f77af3725a76075044e4e82b5378fcaf7ea6..d117aa7237db163d1a49a0ec81afacbb46e00644 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs +++ b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs @@ -5416,6 +5416,15 @@ internal class CSharpResources { } } + /// + /// Looks up a localized string similar to The type 'var' is not permitted in recursive patterns. If you want the type inferred, just omit it.. + /// + internal static string ERR_InferredRecursivePatternType { + get { + return ResourceManager.GetString("ERR_InferredRecursivePatternType", resourceCulture); + } + } + /// /// Looks up a localized string similar to Cannot initialize a by-reference variable with a value. /// @@ -7981,6 +7990,15 @@ internal class CSharpResources { } } + /// + /// Looks up a localized string similar to A property subpattern requires a reference to the property or field to be matched, e.g. '{{ Name: {0}}}'. + /// + internal static string ERR_PropertyPatternNameMissing { + get { + return ResourceManager.GetString("ERR_PropertyPatternNameMissing", resourceCulture); + } + } + /// /// Looks up a localized string similar to '{0}': property or indexer must have at least one accessor. /// @@ -9961,6 +9979,15 @@ internal class CSharpResources { } } + /// + /// Looks up a localized string similar to Matching the tuple type '{0}' requires '{1}' subpatterns, but '{2}' subpatterns are present.. + /// + internal static string ERR_WrongNumberOfSubpatterns { + get { + return ResourceManager.GetString("ERR_WrongNumberOfSubpatterns", resourceCulture); + } + } + /// /// Looks up a localized string similar to The yield statement cannot be used inside an anonymous method or lambda expression. /// diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index f3402013425ae0b28b8ba461a14b148a0a61404c..50064da12449be379362bffb99abfecb3aa7338b 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -5216,4 +5216,13 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ recursive patterns + + The type 'var' is not permitted in recursive patterns. If you want the type inferred, just omit it. + + + Matching the tuple type '{0}' requires '{1}' subpatterns, but '{2}' subpatterns are present. + + + A property subpattern requires a reference to the property or field to be matched, e.g. '{{ Name: {0}}}' + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index ae49ca7a27c651f209c44f6f37046bee94b06df4..25fe237e7de983e79fe5ebb44c0d3ceb8909418e 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1541,6 +1541,10 @@ internal enum ErrorCode #region diagnostics introduced for recursive patterns // PROTOTYPE(patterns2): renumber these before committing ERR_MissingPattern = 8500, + ERR_InferredRecursivePatternType = 8501, + ERR_WrongNumberOfSubpatterns = 8502, + ERR_PropertyPatternNameMissing = 8503, + //ERR_FeatureIsUnimplemented = 8504, #endregion diagnostics introduced for recursive patterns } diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/DataFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/DataFlowPass.cs index 034ba0a081736838ce84372db34a0013ac051500..516d06d7f9683bc9c62c73ca1848fa26d5400f86 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/DataFlowPass.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/DataFlowPass.cs @@ -1189,6 +1189,22 @@ protected virtual void AssignImpl(BoundNode node, BoundExpression value, RefKind break; } + case BoundKind.RecursivePattern: + { + var pattern = (BoundRecursivePattern)node; + var symbol = pattern.Variable as LocalSymbol; + if ((object)symbol != null) + { + // we do not track definite assignment for pattern variables when they are + // promoted to fields for top-level code in scripts and interactive + int slot = GetOrCreateSlot(symbol); + SetSlotState(slot, assigned: written || !this.State.Reachable); + } + + if (written) NoteWrite(pattern.VariableAccess, value, read); + break; + } + case BoundKind.LocalDeclaration: { var local = (BoundLocalDeclaration)node; @@ -1479,7 +1495,7 @@ private void AssignPatternVariables(BoundPattern pattern) Assign(pat, null, RefKind.None, false); break; } - case BoundKind.WildcardPattern: + case BoundKind.DiscardPattern: break; case BoundKind.ConstantPattern: { @@ -1487,6 +1503,26 @@ private void AssignPatternVariables(BoundPattern pattern) this.VisitRvalue(pat.Value); break; } + case BoundKind.RecursivePattern: + { + var pat = (BoundRecursivePattern)pattern; + if (!pat.Deconstruction.IsDefaultOrEmpty) + { + foreach (var subpat in pat.Deconstruction) + { + AssignPatternVariables(subpat); + } + } + if (!pat.PropertiesOpt.IsDefaultOrEmpty) + { + foreach (var (_, subpat) in pat.PropertiesOpt) + { + AssignPatternVariables(subpat); + } + } + Assign(pat, null, RefKind.None, false); + break; + } default: break; } diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/DataFlowsOutWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/DataFlowsOutWalker.cs index b25a5e2c1fa9ba9dec81d3b52c76f31f725728d4..a82df0ca1c76e784e3c389f83e6913b1aeed6a8c 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/DataFlowsOutWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/DataFlowsOutWalker.cs @@ -109,6 +109,11 @@ private Symbol GetNodeSymbol(BoundNode node) return ((BoundDeclarationPattern)node).Variable as LocalSymbol; } + case BoundKind.RecursivePattern: + { + return ((BoundRecursivePattern)node).Variable as LocalSymbol; + } + case BoundKind.FieldAccess: { var fieldAccess = (BoundFieldAccess)node; diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/VariablesDeclaredWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/VariablesDeclaredWalker.cs index 712528f87ba88c1a15469e83dfe368d7c8cae8a5..37e6099db85df6c5969eb5e8ac8713798acc7d32 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/VariablesDeclaredWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/VariablesDeclaredWalker.cs @@ -64,15 +64,29 @@ protected override void VisitPatternSwitchSection(BoundPatternSwitchSection node /// private void NoteDeclaredPatternVariables(BoundPattern pattern) { - if (IsInside && pattern.Kind == BoundKind.DeclarationPattern) + if (IsInside) { - var decl = (BoundDeclarationPattern)pattern; - // The variable may be null if it is a discard designation `_`. - if (decl.Variable?.Kind == SymbolKind.Local) + switch (pattern) { - // Because this API only returns local symbols and parameters, - // we exclude pattern variables that have become fields in scripts. - _variablesDeclared.Add(decl.Variable); + case BoundDeclarationPattern decl: + { + // The variable may be null if it is a discard designation `_`. + if (decl.Variable?.Kind == SymbolKind.Local) + { + // Because this API only returns local symbols and parameters, + // we exclude pattern variables that have become fields in scripts. + _variablesDeclared.Add(decl.Variable); + } + } + break; + case BoundRecursivePattern recur: + { + if (recur.Variable?.Kind == SymbolKind.Local) + { + _variablesDeclared.Add(recur.Variable); + } + } + break; } } } diff --git a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs index f3ae601600bd8972f39c8389a767542aefe35494..881a9a7b35278e0e4346845d38043ecc78f4381f 100644 --- a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs @@ -89,6 +89,18 @@ internal enum BoundKind: byte BreakStatement, ContinueStatement, PatternSwitchStatement, + EvaluationPoint, + DecisionPoint, + WhereClause, + Decision, + DagTemp, + NonNullDecision, + TypeDecision, + ValueDecision, + DagDeconstructEvaluation, + DagTypeEvaluation, + DagFieldEvaluation, + DagPropertyEvaluation, PatternSwitchSection, PatternSwitchLabel, IfStatement, @@ -162,9 +174,10 @@ internal enum BoundKind: byte InterpolatedString, StringInsert, IsPatternExpression, - DeclarationPattern, ConstantPattern, - WildcardPattern, + DiscardPattern, + DeclarationPattern, + RecursivePattern, DiscardExpression, ThrowExpression, OutVariablePendingInference, @@ -2809,8 +2822,8 @@ public BoundContinueStatement Update(GeneratedLabelSymbol label) internal sealed partial class BoundPatternSwitchStatement : BoundStatement { - public BoundPatternSwitchStatement(SyntaxNode syntax, BoundExpression expression, bool someLabelAlwaysMatches, ImmutableArray innerLocals, ImmutableArray innerLocalFunctions, ImmutableArray switchSections, BoundPatternSwitchLabel defaultLabel, GeneratedLabelSymbol breakLabel, PatternSwitchBinder binder, bool isComplete, bool hasErrors = false) - : base(BoundKind.PatternSwitchStatement, syntax, hasErrors || expression.HasErrors() || switchSections.HasErrors() || defaultLabel.HasErrors()) + public BoundPatternSwitchStatement(SyntaxNode syntax, BoundExpression expression, bool someLabelAlwaysMatches, ImmutableArray innerLocals, ImmutableArray innerLocalFunctions, ImmutableArray switchSections, BoundPatternSwitchLabel defaultLabel, GeneratedLabelSymbol breakLabel, BoundDecisionDag decisionDag, bool isComplete, bool hasErrors = false) + : base(BoundKind.PatternSwitchStatement, syntax, hasErrors || expression.HasErrors() || switchSections.HasErrors() || defaultLabel.HasErrors() || decisionDag.HasErrors()) { Debug.Assert(expression != null, "Field 'expression' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); @@ -2818,7 +2831,6 @@ public BoundPatternSwitchStatement(SyntaxNode syntax, BoundExpression expression Debug.Assert(!innerLocalFunctions.IsDefault, "Field 'innerLocalFunctions' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); Debug.Assert(!switchSections.IsDefault, "Field 'switchSections' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); Debug.Assert(breakLabel != null, "Field 'breakLabel' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); - Debug.Assert(binder != null, "Field 'binder' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); this.Expression = expression; this.SomeLabelAlwaysMatches = someLabelAlwaysMatches; @@ -2827,7 +2839,7 @@ public BoundPatternSwitchStatement(SyntaxNode syntax, BoundExpression expression this.SwitchSections = switchSections; this.DefaultLabel = defaultLabel; this.BreakLabel = breakLabel; - this.Binder = binder; + this.DecisionDag = decisionDag; this.IsComplete = isComplete; } @@ -2846,7 +2858,7 @@ public BoundPatternSwitchStatement(SyntaxNode syntax, BoundExpression expression public GeneratedLabelSymbol BreakLabel { get; } - public PatternSwitchBinder Binder { get; } + public BoundDecisionDag DecisionDag { get; } public bool IsComplete { get; } @@ -2855,11 +2867,473 @@ public override BoundNode Accept(BoundTreeVisitor visitor) return visitor.VisitPatternSwitchStatement(this); } - public BoundPatternSwitchStatement Update(BoundExpression expression, bool someLabelAlwaysMatches, ImmutableArray innerLocals, ImmutableArray innerLocalFunctions, ImmutableArray switchSections, BoundPatternSwitchLabel defaultLabel, GeneratedLabelSymbol breakLabel, PatternSwitchBinder binder, bool isComplete) + public BoundPatternSwitchStatement Update(BoundExpression expression, bool someLabelAlwaysMatches, ImmutableArray innerLocals, ImmutableArray innerLocalFunctions, ImmutableArray switchSections, BoundPatternSwitchLabel defaultLabel, GeneratedLabelSymbol breakLabel, BoundDecisionDag decisionDag, bool isComplete) + { + if (expression != this.Expression || someLabelAlwaysMatches != this.SomeLabelAlwaysMatches || innerLocals != this.InnerLocals || innerLocalFunctions != this.InnerLocalFunctions || switchSections != this.SwitchSections || defaultLabel != this.DefaultLabel || breakLabel != this.BreakLabel || decisionDag != this.DecisionDag || isComplete != this.IsComplete) + { + var result = new BoundPatternSwitchStatement(this.Syntax, expression, someLabelAlwaysMatches, innerLocals, innerLocalFunctions, switchSections, defaultLabel, breakLabel, decisionDag, isComplete, this.HasErrors); + result.WasCompilerGenerated = this.WasCompilerGenerated; + return result; + } + return this; + } + } + + internal abstract partial class BoundDecisionDag : BoundNode + { + protected BoundDecisionDag(BoundKind kind, SyntaxNode syntax, LabelSymbol label, bool hasErrors) + : base(kind, syntax, hasErrors) + { + + Debug.Assert(label != null, "Field 'label' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); + + this.Label = label; + } + + protected BoundDecisionDag(BoundKind kind, SyntaxNode syntax, LabelSymbol label) + : base(kind, syntax) + { + + Debug.Assert(label != null, "Field 'label' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); + + this.Label = label; + } + + + public LabelSymbol Label { get; } + } + + internal sealed partial class BoundEvaluationPoint : BoundDecisionDag + { + public BoundEvaluationPoint(SyntaxNode syntax, BoundDagEvaluation evaluation, BoundDecisionDag next, LabelSymbol label, bool hasErrors = false) + : base(BoundKind.EvaluationPoint, syntax, label, hasErrors || evaluation.HasErrors() || next.HasErrors()) + { + + Debug.Assert(evaluation != null, "Field 'evaluation' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); + Debug.Assert(next != null, "Field 'next' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); + Debug.Assert(label != null, "Field 'label' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); + + this.Evaluation = evaluation; + this.Next = next; + } + + + public BoundDagEvaluation Evaluation { get; } + + public BoundDecisionDag Next { get; } + + public override BoundNode Accept(BoundTreeVisitor visitor) + { + return visitor.VisitEvaluationPoint(this); + } + + public BoundEvaluationPoint Update(BoundDagEvaluation evaluation, BoundDecisionDag next, LabelSymbol label) + { + if (evaluation != this.Evaluation || next != this.Next || label != this.Label) + { + var result = new BoundEvaluationPoint(this.Syntax, evaluation, next, label, this.HasErrors); + result.WasCompilerGenerated = this.WasCompilerGenerated; + return result; + } + return this; + } + } + + internal sealed partial class BoundDecisionPoint : BoundDecisionDag + { + public BoundDecisionPoint(SyntaxNode syntax, BoundDagDecision decision, BoundDecisionDag whenTrue, BoundDecisionDag whenFalse, LabelSymbol label, bool hasErrors = false) + : base(BoundKind.DecisionPoint, syntax, label, hasErrors || decision.HasErrors() || whenTrue.HasErrors() || whenFalse.HasErrors()) + { + + Debug.Assert(decision != null, "Field 'decision' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); + Debug.Assert(whenTrue != null, "Field 'whenTrue' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); + Debug.Assert(whenFalse != null, "Field 'whenFalse' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); + Debug.Assert(label != null, "Field 'label' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); + + this.Decision = decision; + this.WhenTrue = whenTrue; + this.WhenFalse = whenFalse; + } + + + public BoundDagDecision Decision { get; } + + public BoundDecisionDag WhenTrue { get; } + + public BoundDecisionDag WhenFalse { get; } + + public override BoundNode Accept(BoundTreeVisitor visitor) + { + return visitor.VisitDecisionPoint(this); + } + + public BoundDecisionPoint Update(BoundDagDecision decision, BoundDecisionDag whenTrue, BoundDecisionDag whenFalse, LabelSymbol label) + { + if (decision != this.Decision || whenTrue != this.WhenTrue || whenFalse != this.WhenFalse || label != this.Label) + { + var result = new BoundDecisionPoint(this.Syntax, decision, whenTrue, whenFalse, label, this.HasErrors); + result.WasCompilerGenerated = this.WasCompilerGenerated; + return result; + } + return this; + } + } + + internal sealed partial class BoundWhereClause : BoundDecisionDag + { + public BoundWhereClause(SyntaxNode syntax, ImmutableArray<(BoundExpression,BoundDagTemp)> bindings, BoundExpression @where, BoundDecisionDag whenTrue, BoundDecisionDag whenFalse, LabelSymbol label, bool hasErrors = false) + : base(BoundKind.WhereClause, syntax, label, hasErrors || @where.HasErrors() || whenTrue.HasErrors() || whenFalse.HasErrors()) + { + + Debug.Assert(!bindings.IsDefault, "Field 'bindings' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); + Debug.Assert(whenTrue != null, "Field 'whenTrue' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); + Debug.Assert(label != null, "Field 'label' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); + + this.Bindings = bindings; + this.Where = @where; + this.WhenTrue = whenTrue; + this.WhenFalse = whenFalse; + } + + + public ImmutableArray<(BoundExpression,BoundDagTemp)> Bindings { get; } + + public BoundExpression Where { get; } + + public BoundDecisionDag WhenTrue { get; } + + public BoundDecisionDag WhenFalse { get; } + + public override BoundNode Accept(BoundTreeVisitor visitor) + { + return visitor.VisitWhereClause(this); + } + + public BoundWhereClause Update(ImmutableArray<(BoundExpression,BoundDagTemp)> bindings, BoundExpression @where, BoundDecisionDag whenTrue, BoundDecisionDag whenFalse, LabelSymbol label) + { + if (bindings != this.Bindings || @where != this.Where || whenTrue != this.WhenTrue || whenFalse != this.WhenFalse || label != this.Label) + { + var result = new BoundWhereClause(this.Syntax, bindings, @where, whenTrue, whenFalse, label, this.HasErrors); + result.WasCompilerGenerated = this.WasCompilerGenerated; + return result; + } + return this; + } + } + + internal sealed partial class BoundDecision : BoundDecisionDag + { + public BoundDecision(SyntaxNode syntax, LabelSymbol label, bool hasErrors) + : base(BoundKind.Decision, syntax, label, hasErrors) + { + + Debug.Assert(label != null, "Field 'label' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); + + } + + public BoundDecision(SyntaxNode syntax, LabelSymbol label) + : base(BoundKind.Decision, syntax, label) + { + + Debug.Assert(label != null, "Field 'label' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); + + } + + + public override BoundNode Accept(BoundTreeVisitor visitor) + { + return visitor.VisitDecision(this); + } + + public BoundDecision Update(LabelSymbol label) + { + if (label != this.Label) + { + var result = new BoundDecision(this.Syntax, label, this.HasErrors); + result.WasCompilerGenerated = this.WasCompilerGenerated; + return result; + } + return this; + } + } + + internal abstract partial class BoundDagDecision : BoundNode + { + protected BoundDagDecision(BoundKind kind, SyntaxNode syntax, BoundDagTemp input, bool hasErrors = false) + : base(kind, syntax, hasErrors) + { + + Debug.Assert(input != null, "Field 'input' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); + + this.Input = input; + } + + + public BoundDagTemp Input { get; } + } + + internal sealed partial class BoundDagTemp : BoundNode + { + public BoundDagTemp(SyntaxNode syntax, TypeSymbol type, BoundDagEvaluation source, int index, bool hasErrors = false) + : base(BoundKind.DagTemp, syntax, hasErrors || source.HasErrors()) + { + + Debug.Assert(type != null, "Field 'type' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); + + this.Type = type; + this.Source = source; + this.Index = index; + } + + + public TypeSymbol Type { get; } + + public BoundDagEvaluation Source { get; } + + public int Index { get; } + + public override BoundNode Accept(BoundTreeVisitor visitor) + { + return visitor.VisitDagTemp(this); + } + + public BoundDagTemp Update(TypeSymbol type, BoundDagEvaluation source, int index) + { + if (type != this.Type || source != this.Source || index != this.Index) + { + var result = new BoundDagTemp(this.Syntax, type, source, index, this.HasErrors); + result.WasCompilerGenerated = this.WasCompilerGenerated; + return result; + } + return this; + } + } + + internal sealed partial class BoundNonNullDecision : BoundDagDecision + { + public BoundNonNullDecision(SyntaxNode syntax, BoundDagTemp input, bool hasErrors = false) + : base(BoundKind.NonNullDecision, syntax, input, hasErrors || input.HasErrors()) + { + + Debug.Assert(input != null, "Field 'input' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); + + } + + + public override BoundNode Accept(BoundTreeVisitor visitor) + { + return visitor.VisitNonNullDecision(this); + } + + public BoundNonNullDecision Update(BoundDagTemp input) + { + if (input != this.Input) + { + var result = new BoundNonNullDecision(this.Syntax, input, this.HasErrors); + result.WasCompilerGenerated = this.WasCompilerGenerated; + return result; + } + return this; + } + } + + internal sealed partial class BoundTypeDecision : BoundDagDecision + { + public BoundTypeDecision(SyntaxNode syntax, TypeSymbol type, BoundDagTemp input, bool hasErrors = false) + : base(BoundKind.TypeDecision, syntax, input, hasErrors || input.HasErrors()) + { + + Debug.Assert(type != null, "Field 'type' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); + Debug.Assert(input != null, "Field 'input' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); + + this.Type = type; + } + + + public TypeSymbol Type { get; } + + public override BoundNode Accept(BoundTreeVisitor visitor) + { + return visitor.VisitTypeDecision(this); + } + + public BoundTypeDecision Update(TypeSymbol type, BoundDagTemp input) + { + if (type != this.Type || input != this.Input) + { + var result = new BoundTypeDecision(this.Syntax, type, input, this.HasErrors); + result.WasCompilerGenerated = this.WasCompilerGenerated; + return result; + } + return this; + } + } + + internal sealed partial class BoundValueDecision : BoundDagDecision + { + public BoundValueDecision(SyntaxNode syntax, ConstantValue value, BoundDagTemp input, bool hasErrors = false) + : base(BoundKind.ValueDecision, syntax, input, hasErrors || input.HasErrors()) + { + + Debug.Assert(value != null, "Field 'value' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); + Debug.Assert(input != null, "Field 'input' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); + + this.Value = value; + } + + + public ConstantValue Value { get; } + + public override BoundNode Accept(BoundTreeVisitor visitor) + { + return visitor.VisitValueDecision(this); + } + + public BoundValueDecision Update(ConstantValue value, BoundDagTemp input) + { + if (value != this.Value || input != this.Input) + { + var result = new BoundValueDecision(this.Syntax, value, input, this.HasErrors); + result.WasCompilerGenerated = this.WasCompilerGenerated; + return result; + } + return this; + } + } + + internal abstract partial class BoundDagEvaluation : BoundDagDecision + { + protected BoundDagEvaluation(BoundKind kind, SyntaxNode syntax, BoundDagTemp input, bool hasErrors = false) + : base(kind, syntax, input, hasErrors) + { + + Debug.Assert(input != null, "Field 'input' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); + + } + + } + + internal sealed partial class BoundDagDeconstructEvaluation : BoundDagEvaluation + { + public BoundDagDeconstructEvaluation(SyntaxNode syntax, MethodSymbol deconstructMethod, BoundDagTemp input, bool hasErrors = false) + : base(BoundKind.DagDeconstructEvaluation, syntax, input, hasErrors || input.HasErrors()) + { + + Debug.Assert(deconstructMethod != null, "Field 'deconstructMethod' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); + Debug.Assert(input != null, "Field 'input' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); + + this.DeconstructMethod = deconstructMethod; + } + + + public MethodSymbol DeconstructMethod { get; } + + public override BoundNode Accept(BoundTreeVisitor visitor) { - if (expression != this.Expression || someLabelAlwaysMatches != this.SomeLabelAlwaysMatches || innerLocals != this.InnerLocals || innerLocalFunctions != this.InnerLocalFunctions || switchSections != this.SwitchSections || defaultLabel != this.DefaultLabel || breakLabel != this.BreakLabel || binder != this.Binder || isComplete != this.IsComplete) + return visitor.VisitDagDeconstructEvaluation(this); + } + + public BoundDagDeconstructEvaluation Update(MethodSymbol deconstructMethod, BoundDagTemp input) + { + if (deconstructMethod != this.DeconstructMethod || input != this.Input) { - var result = new BoundPatternSwitchStatement(this.Syntax, expression, someLabelAlwaysMatches, innerLocals, innerLocalFunctions, switchSections, defaultLabel, breakLabel, binder, isComplete, this.HasErrors); + var result = new BoundDagDeconstructEvaluation(this.Syntax, deconstructMethod, input, this.HasErrors); + result.WasCompilerGenerated = this.WasCompilerGenerated; + return result; + } + return this; + } + } + + internal sealed partial class BoundDagTypeEvaluation : BoundDagEvaluation + { + public BoundDagTypeEvaluation(SyntaxNode syntax, TypeSymbol type, BoundDagTemp input, bool hasErrors = false) + : base(BoundKind.DagTypeEvaluation, syntax, input, hasErrors || input.HasErrors()) + { + + Debug.Assert(type != null, "Field 'type' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); + Debug.Assert(input != null, "Field 'input' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); + + this.Type = type; + } + + + public TypeSymbol Type { get; } + + public override BoundNode Accept(BoundTreeVisitor visitor) + { + return visitor.VisitDagTypeEvaluation(this); + } + + public BoundDagTypeEvaluation Update(TypeSymbol type, BoundDagTemp input) + { + if (type != this.Type || input != this.Input) + { + var result = new BoundDagTypeEvaluation(this.Syntax, type, input, this.HasErrors); + result.WasCompilerGenerated = this.WasCompilerGenerated; + return result; + } + return this; + } + } + + internal sealed partial class BoundDagFieldEvaluation : BoundDagEvaluation + { + public BoundDagFieldEvaluation(SyntaxNode syntax, FieldSymbol field, BoundDagTemp input, bool hasErrors = false) + : base(BoundKind.DagFieldEvaluation, syntax, input, hasErrors || input.HasErrors()) + { + + Debug.Assert(field != null, "Field 'field' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); + Debug.Assert(input != null, "Field 'input' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); + + this.Field = field; + } + + + public FieldSymbol Field { get; } + + public override BoundNode Accept(BoundTreeVisitor visitor) + { + return visitor.VisitDagFieldEvaluation(this); + } + + public BoundDagFieldEvaluation Update(FieldSymbol field, BoundDagTemp input) + { + if (field != this.Field || input != this.Input) + { + var result = new BoundDagFieldEvaluation(this.Syntax, field, input, this.HasErrors); + result.WasCompilerGenerated = this.WasCompilerGenerated; + return result; + } + return this; + } + } + + internal sealed partial class BoundDagPropertyEvaluation : BoundDagEvaluation + { + public BoundDagPropertyEvaluation(SyntaxNode syntax, PropertySymbol property, BoundDagTemp input, bool hasErrors = false) + : base(BoundKind.DagPropertyEvaluation, syntax, input, hasErrors || input.HasErrors()) + { + + Debug.Assert(property != null, "Field 'property' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); + Debug.Assert(input != null, "Field 'input' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); + + this.Property = property; + } + + + public PropertySymbol Property { get; } + + public override BoundNode Accept(BoundTreeVisitor visitor) + { + return visitor.VisitDagPropertyEvaluation(this); + } + + public BoundDagPropertyEvaluation Update(PropertySymbol property, BoundDagTemp input) + { + if (property != this.Property || input != this.Input) + { + var result = new BoundDagPropertyEvaluation(this.Syntax, property, input, this.HasErrors); result.WasCompilerGenerated = this.WasCompilerGenerated; return result; } @@ -5896,14 +6370,64 @@ protected BoundPattern(BoundKind kind, SyntaxNode syntax) } + internal sealed partial class BoundConstantPattern : BoundPattern + { + public BoundConstantPattern(SyntaxNode syntax, BoundExpression value, ConstantValue constantValue, bool hasErrors = false) + : base(BoundKind.ConstantPattern, syntax, hasErrors || value.HasErrors()) + { + + Debug.Assert(value != null, "Field 'value' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); + + this.Value = value; + this.ConstantValue = constantValue; + } + + + public BoundExpression Value { get; } + + public ConstantValue ConstantValue { get; } + + public override BoundNode Accept(BoundTreeVisitor visitor) + { + return visitor.VisitConstantPattern(this); + } + + public BoundConstantPattern Update(BoundExpression value, ConstantValue constantValue) + { + if (value != this.Value || constantValue != this.ConstantValue) + { + var result = new BoundConstantPattern(this.Syntax, value, constantValue, this.HasErrors); + result.WasCompilerGenerated = this.WasCompilerGenerated; + return result; + } + return this; + } + } + + internal sealed partial class BoundDiscardPattern : BoundPattern + { + public BoundDiscardPattern(SyntaxNode syntax, bool hasErrors) + : base(BoundKind.DiscardPattern, syntax, hasErrors) + { + } + + public BoundDiscardPattern(SyntaxNode syntax) + : base(BoundKind.DiscardPattern, syntax) + { + } + + + public override BoundNode Accept(BoundTreeVisitor visitor) + { + return visitor.VisitDiscardPattern(this); + } + } + internal sealed partial class BoundDeclarationPattern : BoundPattern { public BoundDeclarationPattern(SyntaxNode syntax, Symbol variable, BoundExpression variableAccess, BoundTypeExpression declaredType, bool isVar, bool hasErrors = false) : base(BoundKind.DeclarationPattern, syntax, hasErrors || variableAccess.HasErrors() || declaredType.HasErrors()) { - - Debug.Assert(variableAccess != null, "Field 'variableAccess' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); - this.Variable = variable; this.VariableAccess = variableAccess; this.DeclaredType = declaredType; @@ -5936,33 +6460,48 @@ public BoundDeclarationPattern Update(Symbol variable, BoundExpression variableA } } - internal sealed partial class BoundConstantPattern : BoundPattern + internal sealed partial class BoundRecursivePattern : BoundPattern { - public BoundConstantPattern(SyntaxNode syntax, BoundExpression value, ConstantValue constantValue, bool hasErrors = false) - : base(BoundKind.ConstantPattern, syntax, hasErrors || value.HasErrors()) + public BoundRecursivePattern(SyntaxNode syntax, BoundTypeExpression declaredType, TypeSymbol inputType, MethodSymbol deconstructMethodOpt, ImmutableArray deconstruction, ImmutableArray<(Symbol symbol, BoundPattern pattern)> propertiesOpt, Symbol variable, BoundExpression variableAccess, bool hasErrors = false) + : base(BoundKind.RecursivePattern, syntax, hasErrors || declaredType.HasErrors() || deconstruction.HasErrors() || variableAccess.HasErrors()) { - Debug.Assert(value != null, "Field 'value' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); + Debug.Assert(inputType != null, "Field 'inputType' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); - this.Value = value; - this.ConstantValue = constantValue; + this.DeclaredType = declaredType; + this.InputType = inputType; + this.DeconstructMethodOpt = deconstructMethodOpt; + this.Deconstruction = deconstruction; + this.PropertiesOpt = propertiesOpt; + this.Variable = variable; + this.VariableAccess = variableAccess; } - public BoundExpression Value { get; } + public BoundTypeExpression DeclaredType { get; } - public ConstantValue ConstantValue { get; } + public TypeSymbol InputType { get; } + + public MethodSymbol DeconstructMethodOpt { get; } + + public ImmutableArray Deconstruction { get; } + + public ImmutableArray<(Symbol symbol, BoundPattern pattern)> PropertiesOpt { get; } + + public Symbol Variable { get; } + + public BoundExpression VariableAccess { get; } public override BoundNode Accept(BoundTreeVisitor visitor) { - return visitor.VisitConstantPattern(this); + return visitor.VisitRecursivePattern(this); } - public BoundConstantPattern Update(BoundExpression value, ConstantValue constantValue) + public BoundRecursivePattern Update(BoundTypeExpression declaredType, TypeSymbol inputType, MethodSymbol deconstructMethodOpt, ImmutableArray deconstruction, ImmutableArray<(Symbol symbol, BoundPattern pattern)> propertiesOpt, Symbol variable, BoundExpression variableAccess) { - if (value != this.Value || constantValue != this.ConstantValue) + if (declaredType != this.DeclaredType || inputType != this.InputType || deconstructMethodOpt != this.DeconstructMethodOpt || deconstruction != this.Deconstruction || propertiesOpt != this.PropertiesOpt || variable != this.Variable || variableAccess != this.VariableAccess) { - var result = new BoundConstantPattern(this.Syntax, value, constantValue, this.HasErrors); + var result = new BoundRecursivePattern(this.Syntax, declaredType, inputType, deconstructMethodOpt, deconstruction, propertiesOpt, variable, variableAccess, this.HasErrors); result.WasCompilerGenerated = this.WasCompilerGenerated; return result; } @@ -5970,25 +6509,6 @@ public BoundConstantPattern Update(BoundExpression value, ConstantValue constant } } - internal sealed partial class BoundWildcardPattern : BoundPattern - { - public BoundWildcardPattern(SyntaxNode syntax, bool hasErrors) - : base(BoundKind.WildcardPattern, syntax, hasErrors) - { - } - - public BoundWildcardPattern(SyntaxNode syntax) - : base(BoundKind.WildcardPattern, syntax) - { - } - - - public override BoundNode Accept(BoundTreeVisitor visitor) - { - return visitor.VisitWildcardPattern(this); - } - } - internal sealed partial class BoundDiscardExpression : BoundExpression { public BoundDiscardExpression(SyntaxNode syntax, TypeSymbol type, bool hasErrors) @@ -6294,6 +6814,30 @@ internal R VisitInternal(BoundNode node, A arg) return VisitContinueStatement(node as BoundContinueStatement, arg); case BoundKind.PatternSwitchStatement: return VisitPatternSwitchStatement(node as BoundPatternSwitchStatement, arg); + case BoundKind.EvaluationPoint: + return VisitEvaluationPoint(node as BoundEvaluationPoint, arg); + case BoundKind.DecisionPoint: + return VisitDecisionPoint(node as BoundDecisionPoint, arg); + case BoundKind.WhereClause: + return VisitWhereClause(node as BoundWhereClause, arg); + case BoundKind.Decision: + return VisitDecision(node as BoundDecision, arg); + case BoundKind.DagTemp: + return VisitDagTemp(node as BoundDagTemp, arg); + case BoundKind.NonNullDecision: + return VisitNonNullDecision(node as BoundNonNullDecision, arg); + case BoundKind.TypeDecision: + return VisitTypeDecision(node as BoundTypeDecision, arg); + case BoundKind.ValueDecision: + return VisitValueDecision(node as BoundValueDecision, arg); + case BoundKind.DagDeconstructEvaluation: + return VisitDagDeconstructEvaluation(node as BoundDagDeconstructEvaluation, arg); + case BoundKind.DagTypeEvaluation: + return VisitDagTypeEvaluation(node as BoundDagTypeEvaluation, arg); + case BoundKind.DagFieldEvaluation: + return VisitDagFieldEvaluation(node as BoundDagFieldEvaluation, arg); + case BoundKind.DagPropertyEvaluation: + return VisitDagPropertyEvaluation(node as BoundDagPropertyEvaluation, arg); case BoundKind.PatternSwitchSection: return VisitPatternSwitchSection(node as BoundPatternSwitchSection, arg); case BoundKind.PatternSwitchLabel: @@ -6440,12 +6984,14 @@ internal R VisitInternal(BoundNode node, A arg) return VisitStringInsert(node as BoundStringInsert, arg); case BoundKind.IsPatternExpression: return VisitIsPatternExpression(node as BoundIsPatternExpression, arg); - case BoundKind.DeclarationPattern: - return VisitDeclarationPattern(node as BoundDeclarationPattern, arg); case BoundKind.ConstantPattern: return VisitConstantPattern(node as BoundConstantPattern, arg); - case BoundKind.WildcardPattern: - return VisitWildcardPattern(node as BoundWildcardPattern, arg); + case BoundKind.DiscardPattern: + return VisitDiscardPattern(node as BoundDiscardPattern, arg); + case BoundKind.DeclarationPattern: + return VisitDeclarationPattern(node as BoundDeclarationPattern, arg); + case BoundKind.RecursivePattern: + return VisitRecursivePattern(node as BoundRecursivePattern, arg); case BoundKind.DiscardExpression: return VisitDiscardExpression(node as BoundDiscardExpression, arg); case BoundKind.ThrowExpression: @@ -6672,71 +7218,119 @@ public virtual R VisitScope(BoundScope node, A arg) { return this.DefaultVisit(node, arg); } - public virtual R VisitStateMachineScope(BoundStateMachineScope node, A arg) + public virtual R VisitStateMachineScope(BoundStateMachineScope node, A arg) + { + return this.DefaultVisit(node, arg); + } + public virtual R VisitLocalDeclaration(BoundLocalDeclaration node, A arg) + { + return this.DefaultVisit(node, arg); + } + public virtual R VisitMultipleLocalDeclarations(BoundMultipleLocalDeclarations node, A arg) + { + return this.DefaultVisit(node, arg); + } + public virtual R VisitLocalFunctionStatement(BoundLocalFunctionStatement node, A arg) + { + return this.DefaultVisit(node, arg); + } + public virtual R VisitSequence(BoundSequence node, A arg) + { + return this.DefaultVisit(node, arg); + } + public virtual R VisitNoOpStatement(BoundNoOpStatement node, A arg) + { + return this.DefaultVisit(node, arg); + } + public virtual R VisitReturnStatement(BoundReturnStatement node, A arg) + { + return this.DefaultVisit(node, arg); + } + public virtual R VisitYieldReturnStatement(BoundYieldReturnStatement node, A arg) + { + return this.DefaultVisit(node, arg); + } + public virtual R VisitYieldBreakStatement(BoundYieldBreakStatement node, A arg) + { + return this.DefaultVisit(node, arg); + } + public virtual R VisitThrowStatement(BoundThrowStatement node, A arg) + { + return this.DefaultVisit(node, arg); + } + public virtual R VisitExpressionStatement(BoundExpressionStatement node, A arg) + { + return this.DefaultVisit(node, arg); + } + public virtual R VisitSwitchStatement(BoundSwitchStatement node, A arg) + { + return this.DefaultVisit(node, arg); + } + public virtual R VisitSwitchSection(BoundSwitchSection node, A arg) { return this.DefaultVisit(node, arg); } - public virtual R VisitLocalDeclaration(BoundLocalDeclaration node, A arg) + public virtual R VisitSwitchLabel(BoundSwitchLabel node, A arg) { return this.DefaultVisit(node, arg); } - public virtual R VisitMultipleLocalDeclarations(BoundMultipleLocalDeclarations node, A arg) + public virtual R VisitBreakStatement(BoundBreakStatement node, A arg) { return this.DefaultVisit(node, arg); } - public virtual R VisitLocalFunctionStatement(BoundLocalFunctionStatement node, A arg) + public virtual R VisitContinueStatement(BoundContinueStatement node, A arg) { return this.DefaultVisit(node, arg); } - public virtual R VisitSequence(BoundSequence node, A arg) + public virtual R VisitPatternSwitchStatement(BoundPatternSwitchStatement node, A arg) { return this.DefaultVisit(node, arg); } - public virtual R VisitNoOpStatement(BoundNoOpStatement node, A arg) + public virtual R VisitEvaluationPoint(BoundEvaluationPoint node, A arg) { return this.DefaultVisit(node, arg); } - public virtual R VisitReturnStatement(BoundReturnStatement node, A arg) + public virtual R VisitDecisionPoint(BoundDecisionPoint node, A arg) { return this.DefaultVisit(node, arg); } - public virtual R VisitYieldReturnStatement(BoundYieldReturnStatement node, A arg) + public virtual R VisitWhereClause(BoundWhereClause node, A arg) { return this.DefaultVisit(node, arg); } - public virtual R VisitYieldBreakStatement(BoundYieldBreakStatement node, A arg) + public virtual R VisitDecision(BoundDecision node, A arg) { return this.DefaultVisit(node, arg); } - public virtual R VisitThrowStatement(BoundThrowStatement node, A arg) + public virtual R VisitDagTemp(BoundDagTemp node, A arg) { return this.DefaultVisit(node, arg); } - public virtual R VisitExpressionStatement(BoundExpressionStatement node, A arg) + public virtual R VisitNonNullDecision(BoundNonNullDecision node, A arg) { return this.DefaultVisit(node, arg); } - public virtual R VisitSwitchStatement(BoundSwitchStatement node, A arg) + public virtual R VisitTypeDecision(BoundTypeDecision node, A arg) { return this.DefaultVisit(node, arg); } - public virtual R VisitSwitchSection(BoundSwitchSection node, A arg) + public virtual R VisitValueDecision(BoundValueDecision node, A arg) { return this.DefaultVisit(node, arg); } - public virtual R VisitSwitchLabel(BoundSwitchLabel node, A arg) + public virtual R VisitDagDeconstructEvaluation(BoundDagDeconstructEvaluation node, A arg) { return this.DefaultVisit(node, arg); } - public virtual R VisitBreakStatement(BoundBreakStatement node, A arg) + public virtual R VisitDagTypeEvaluation(BoundDagTypeEvaluation node, A arg) { return this.DefaultVisit(node, arg); } - public virtual R VisitContinueStatement(BoundContinueStatement node, A arg) + public virtual R VisitDagFieldEvaluation(BoundDagFieldEvaluation node, A arg) { return this.DefaultVisit(node, arg); } - public virtual R VisitPatternSwitchStatement(BoundPatternSwitchStatement node, A arg) + public virtual R VisitDagPropertyEvaluation(BoundDagPropertyEvaluation node, A arg) { return this.DefaultVisit(node, arg); } @@ -7032,15 +7626,19 @@ public virtual R VisitIsPatternExpression(BoundIsPatternExpression node, A arg) { return this.DefaultVisit(node, arg); } - public virtual R VisitDeclarationPattern(BoundDeclarationPattern node, A arg) + public virtual R VisitConstantPattern(BoundConstantPattern node, A arg) { return this.DefaultVisit(node, arg); } - public virtual R VisitConstantPattern(BoundConstantPattern node, A arg) + public virtual R VisitDiscardPattern(BoundDiscardPattern node, A arg) + { + return this.DefaultVisit(node, arg); + } + public virtual R VisitDeclarationPattern(BoundDeclarationPattern node, A arg) { return this.DefaultVisit(node, arg); } - public virtual R VisitWildcardPattern(BoundWildcardPattern node, A arg) + public virtual R VisitRecursivePattern(BoundRecursivePattern node, A arg) { return this.DefaultVisit(node, arg); } @@ -7344,6 +7942,54 @@ public virtual BoundNode VisitPatternSwitchStatement(BoundPatternSwitchStatement { return this.DefaultVisit(node); } + public virtual BoundNode VisitEvaluationPoint(BoundEvaluationPoint node) + { + return this.DefaultVisit(node); + } + public virtual BoundNode VisitDecisionPoint(BoundDecisionPoint node) + { + return this.DefaultVisit(node); + } + public virtual BoundNode VisitWhereClause(BoundWhereClause node) + { + return this.DefaultVisit(node); + } + public virtual BoundNode VisitDecision(BoundDecision node) + { + return this.DefaultVisit(node); + } + public virtual BoundNode VisitDagTemp(BoundDagTemp node) + { + return this.DefaultVisit(node); + } + public virtual BoundNode VisitNonNullDecision(BoundNonNullDecision node) + { + return this.DefaultVisit(node); + } + public virtual BoundNode VisitTypeDecision(BoundTypeDecision node) + { + return this.DefaultVisit(node); + } + public virtual BoundNode VisitValueDecision(BoundValueDecision node) + { + return this.DefaultVisit(node); + } + public virtual BoundNode VisitDagDeconstructEvaluation(BoundDagDeconstructEvaluation node) + { + return this.DefaultVisit(node); + } + public virtual BoundNode VisitDagTypeEvaluation(BoundDagTypeEvaluation node) + { + return this.DefaultVisit(node); + } + public virtual BoundNode VisitDagFieldEvaluation(BoundDagFieldEvaluation node) + { + return this.DefaultVisit(node); + } + public virtual BoundNode VisitDagPropertyEvaluation(BoundDagPropertyEvaluation node) + { + return this.DefaultVisit(node); + } public virtual BoundNode VisitPatternSwitchSection(BoundPatternSwitchSection node) { return this.DefaultVisit(node); @@ -7636,15 +8282,19 @@ public virtual BoundNode VisitIsPatternExpression(BoundIsPatternExpression node) { return this.DefaultVisit(node); } - public virtual BoundNode VisitDeclarationPattern(BoundDeclarationPattern node) + public virtual BoundNode VisitConstantPattern(BoundConstantPattern node) { return this.DefaultVisit(node); } - public virtual BoundNode VisitConstantPattern(BoundConstantPattern node) + public virtual BoundNode VisitDiscardPattern(BoundDiscardPattern node) + { + return this.DefaultVisit(node); + } + public virtual BoundNode VisitDeclarationPattern(BoundDeclarationPattern node) { return this.DefaultVisit(node); } - public virtual BoundNode VisitWildcardPattern(BoundWildcardPattern node) + public virtual BoundNode VisitRecursivePattern(BoundRecursivePattern node) { return this.DefaultVisit(node); } @@ -8017,6 +8667,71 @@ public override BoundNode VisitPatternSwitchStatement(BoundPatternSwitchStatemen this.Visit(node.Expression); this.VisitList(node.SwitchSections); this.Visit(node.DefaultLabel); + this.Visit(node.DecisionDag); + return null; + } + public override BoundNode VisitEvaluationPoint(BoundEvaluationPoint node) + { + this.Visit(node.Evaluation); + this.Visit(node.Next); + return null; + } + public override BoundNode VisitDecisionPoint(BoundDecisionPoint node) + { + this.Visit(node.Decision); + this.Visit(node.WhenTrue); + this.Visit(node.WhenFalse); + return null; + } + public override BoundNode VisitWhereClause(BoundWhereClause node) + { + this.Visit(node.Where); + this.Visit(node.WhenTrue); + this.Visit(node.WhenFalse); + return null; + } + public override BoundNode VisitDecision(BoundDecision node) + { + return null; + } + public override BoundNode VisitDagTemp(BoundDagTemp node) + { + this.Visit(node.Source); + return null; + } + public override BoundNode VisitNonNullDecision(BoundNonNullDecision node) + { + this.Visit(node.Input); + return null; + } + public override BoundNode VisitTypeDecision(BoundTypeDecision node) + { + this.Visit(node.Input); + return null; + } + public override BoundNode VisitValueDecision(BoundValueDecision node) + { + this.Visit(node.Input); + return null; + } + public override BoundNode VisitDagDeconstructEvaluation(BoundDagDeconstructEvaluation node) + { + this.Visit(node.Input); + return null; + } + public override BoundNode VisitDagTypeEvaluation(BoundDagTypeEvaluation node) + { + this.Visit(node.Input); + return null; + } + public override BoundNode VisitDagFieldEvaluation(BoundDagFieldEvaluation node) + { + this.Visit(node.Input); + return null; + } + public override BoundNode VisitDagPropertyEvaluation(BoundDagPropertyEvaluation node) + { + this.Visit(node.Input); return null; } public override BoundNode VisitPatternSwitchSection(BoundPatternSwitchSection node) @@ -8407,19 +9122,26 @@ public override BoundNode VisitIsPatternExpression(BoundIsPatternExpression node this.Visit(node.Pattern); return null; } - public override BoundNode VisitDeclarationPattern(BoundDeclarationPattern node) + public override BoundNode VisitConstantPattern(BoundConstantPattern node) { - this.Visit(node.VariableAccess); - this.Visit(node.DeclaredType); + this.Visit(node.Value); return null; } - public override BoundNode VisitConstantPattern(BoundConstantPattern node) + public override BoundNode VisitDiscardPattern(BoundDiscardPattern node) { - this.Visit(node.Value); return null; } - public override BoundNode VisitWildcardPattern(BoundWildcardPattern node) + public override BoundNode VisitDeclarationPattern(BoundDeclarationPattern node) + { + this.Visit(node.VariableAccess); + this.Visit(node.DeclaredType); + return null; + } + public override BoundNode VisitRecursivePattern(BoundRecursivePattern node) { + this.Visit(node.DeclaredType); + this.VisitList(node.Deconstruction); + this.Visit(node.VariableAccess); return null; } public override BoundNode VisitDiscardExpression(BoundDiscardExpression node) @@ -8838,7 +9560,75 @@ public override BoundNode VisitPatternSwitchStatement(BoundPatternSwitchStatemen BoundExpression expression = (BoundExpression)this.Visit(node.Expression); ImmutableArray switchSections = (ImmutableArray)this.VisitList(node.SwitchSections); BoundPatternSwitchLabel defaultLabel = (BoundPatternSwitchLabel)this.Visit(node.DefaultLabel); - return node.Update(expression, node.SomeLabelAlwaysMatches, node.InnerLocals, node.InnerLocalFunctions, switchSections, defaultLabel, node.BreakLabel, node.Binder, node.IsComplete); + BoundDecisionDag decisionDag = (BoundDecisionDag)this.Visit(node.DecisionDag); + return node.Update(expression, node.SomeLabelAlwaysMatches, node.InnerLocals, node.InnerLocalFunctions, switchSections, defaultLabel, node.BreakLabel, decisionDag, node.IsComplete); + } + public override BoundNode VisitEvaluationPoint(BoundEvaluationPoint node) + { + BoundDagEvaluation evaluation = (BoundDagEvaluation)this.Visit(node.Evaluation); + BoundDecisionDag next = (BoundDecisionDag)this.Visit(node.Next); + return node.Update(evaluation, next, node.Label); + } + public override BoundNode VisitDecisionPoint(BoundDecisionPoint node) + { + BoundDagDecision decision = (BoundDagDecision)this.Visit(node.Decision); + BoundDecisionDag whenTrue = (BoundDecisionDag)this.Visit(node.WhenTrue); + BoundDecisionDag whenFalse = (BoundDecisionDag)this.Visit(node.WhenFalse); + return node.Update(decision, whenTrue, whenFalse, node.Label); + } + public override BoundNode VisitWhereClause(BoundWhereClause node) + { + BoundExpression @where = (BoundExpression)this.Visit(node.Where); + BoundDecisionDag whenTrue = (BoundDecisionDag)this.Visit(node.WhenTrue); + BoundDecisionDag whenFalse = (BoundDecisionDag)this.Visit(node.WhenFalse); + return node.Update(node.Bindings, @where, whenTrue, whenFalse, node.Label); + } + public override BoundNode VisitDecision(BoundDecision node) + { + return node; + } + public override BoundNode VisitDagTemp(BoundDagTemp node) + { + BoundDagEvaluation source = (BoundDagEvaluation)this.Visit(node.Source); + TypeSymbol type = this.VisitType(node.Type); + return node.Update(type, source, node.Index); + } + public override BoundNode VisitNonNullDecision(BoundNonNullDecision node) + { + BoundDagTemp input = (BoundDagTemp)this.Visit(node.Input); + return node.Update(input); + } + public override BoundNode VisitTypeDecision(BoundTypeDecision node) + { + BoundDagTemp input = (BoundDagTemp)this.Visit(node.Input); + TypeSymbol type = this.VisitType(node.Type); + return node.Update(type, input); + } + public override BoundNode VisitValueDecision(BoundValueDecision node) + { + BoundDagTemp input = (BoundDagTemp)this.Visit(node.Input); + return node.Update(node.Value, input); + } + public override BoundNode VisitDagDeconstructEvaluation(BoundDagDeconstructEvaluation node) + { + BoundDagTemp input = (BoundDagTemp)this.Visit(node.Input); + return node.Update(node.DeconstructMethod, input); + } + public override BoundNode VisitDagTypeEvaluation(BoundDagTypeEvaluation node) + { + BoundDagTemp input = (BoundDagTemp)this.Visit(node.Input); + TypeSymbol type = this.VisitType(node.Type); + return node.Update(type, input); + } + public override BoundNode VisitDagFieldEvaluation(BoundDagFieldEvaluation node) + { + BoundDagTemp input = (BoundDagTemp)this.Visit(node.Input); + return node.Update(node.Field, input); + } + public override BoundNode VisitDagPropertyEvaluation(BoundDagPropertyEvaluation node) + { + BoundDagTemp input = (BoundDagTemp)this.Visit(node.Input); + return node.Update(node.Property, input); } public override BoundNode VisitPatternSwitchSection(BoundPatternSwitchSection node) { @@ -9287,21 +10077,29 @@ public override BoundNode VisitIsPatternExpression(BoundIsPatternExpression node TypeSymbol type = this.VisitType(node.Type); return node.Update(expression, pattern, type); } - public override BoundNode VisitDeclarationPattern(BoundDeclarationPattern node) - { - BoundExpression variableAccess = (BoundExpression)this.Visit(node.VariableAccess); - BoundTypeExpression declaredType = (BoundTypeExpression)this.Visit(node.DeclaredType); - return node.Update(node.Variable, variableAccess, declaredType, node.IsVar); - } public override BoundNode VisitConstantPattern(BoundConstantPattern node) { BoundExpression value = (BoundExpression)this.Visit(node.Value); return node.Update(value, node.ConstantValue); } - public override BoundNode VisitWildcardPattern(BoundWildcardPattern node) + public override BoundNode VisitDiscardPattern(BoundDiscardPattern node) { return node; } + public override BoundNode VisitDeclarationPattern(BoundDeclarationPattern node) + { + BoundExpression variableAccess = (BoundExpression)this.Visit(node.VariableAccess); + BoundTypeExpression declaredType = (BoundTypeExpression)this.Visit(node.DeclaredType); + return node.Update(node.Variable, variableAccess, declaredType, node.IsVar); + } + public override BoundNode VisitRecursivePattern(BoundRecursivePattern node) + { + BoundTypeExpression declaredType = (BoundTypeExpression)this.Visit(node.DeclaredType); + ImmutableArray deconstruction = (ImmutableArray)this.VisitList(node.Deconstruction); + BoundExpression variableAccess = (BoundExpression)this.Visit(node.VariableAccess); + TypeSymbol inputType = this.VisitType(node.InputType); + return node.Update(declaredType, inputType, node.DeconstructMethodOpt, deconstruction, node.PropertiesOpt, node.Variable, variableAccess); + } public override BoundNode VisitDiscardExpression(BoundDiscardExpression node) { TypeSymbol type = this.VisitType(node.Type); @@ -10021,11 +10819,124 @@ public override TreeDumperNode VisitPatternSwitchStatement(BoundPatternSwitchSta new TreeDumperNode("switchSections", null, from x in node.SwitchSections select Visit(x, null)), new TreeDumperNode("defaultLabel", null, new TreeDumperNode[] { Visit(node.DefaultLabel, null) }), new TreeDumperNode("breakLabel", node.BreakLabel, null), - new TreeDumperNode("binder", node.Binder, null), + new TreeDumperNode("decisionDag", null, new TreeDumperNode[] { Visit(node.DecisionDag, null) }), new TreeDumperNode("isComplete", node.IsComplete, null) } ); } + public override TreeDumperNode VisitEvaluationPoint(BoundEvaluationPoint node, object arg) + { + return new TreeDumperNode("evaluationPoint", null, new TreeDumperNode[] + { + new TreeDumperNode("evaluation", null, new TreeDumperNode[] { Visit(node.Evaluation, null) }), + new TreeDumperNode("next", null, new TreeDumperNode[] { Visit(node.Next, null) }), + new TreeDumperNode("label", node.Label, null) + } + ); + } + public override TreeDumperNode VisitDecisionPoint(BoundDecisionPoint node, object arg) + { + return new TreeDumperNode("decisionPoint", null, new TreeDumperNode[] + { + new TreeDumperNode("decision", null, new TreeDumperNode[] { Visit(node.Decision, null) }), + new TreeDumperNode("whenTrue", null, new TreeDumperNode[] { Visit(node.WhenTrue, null) }), + new TreeDumperNode("whenFalse", null, new TreeDumperNode[] { Visit(node.WhenFalse, null) }), + new TreeDumperNode("label", node.Label, null) + } + ); + } + public override TreeDumperNode VisitWhereClause(BoundWhereClause node, object arg) + { + return new TreeDumperNode("whereClause", null, new TreeDumperNode[] + { + new TreeDumperNode("bindings", node.Bindings, null), + new TreeDumperNode("@where", null, new TreeDumperNode[] { Visit(node.Where, null) }), + new TreeDumperNode("whenTrue", null, new TreeDumperNode[] { Visit(node.WhenTrue, null) }), + new TreeDumperNode("whenFalse", null, new TreeDumperNode[] { Visit(node.WhenFalse, null) }), + new TreeDumperNode("label", node.Label, null) + } + ); + } + public override TreeDumperNode VisitDecision(BoundDecision node, object arg) + { + return new TreeDumperNode("decision", null, new TreeDumperNode[] + { + new TreeDumperNode("label", node.Label, null) + } + ); + } + public override TreeDumperNode VisitDagTemp(BoundDagTemp node, object arg) + { + return new TreeDumperNode("dagTemp", null, new TreeDumperNode[] + { + new TreeDumperNode("type", node.Type, null), + new TreeDumperNode("source", null, new TreeDumperNode[] { Visit(node.Source, null) }), + new TreeDumperNode("index", node.Index, null) + } + ); + } + public override TreeDumperNode VisitNonNullDecision(BoundNonNullDecision node, object arg) + { + return new TreeDumperNode("nonNullDecision", null, new TreeDumperNode[] + { + new TreeDumperNode("input", null, new TreeDumperNode[] { Visit(node.Input, null) }) + } + ); + } + public override TreeDumperNode VisitTypeDecision(BoundTypeDecision node, object arg) + { + return new TreeDumperNode("typeDecision", null, new TreeDumperNode[] + { + new TreeDumperNode("type", node.Type, null), + new TreeDumperNode("input", null, new TreeDumperNode[] { Visit(node.Input, null) }) + } + ); + } + public override TreeDumperNode VisitValueDecision(BoundValueDecision node, object arg) + { + return new TreeDumperNode("valueDecision", null, new TreeDumperNode[] + { + new TreeDumperNode("value", node.Value, null), + new TreeDumperNode("input", null, new TreeDumperNode[] { Visit(node.Input, null) }) + } + ); + } + public override TreeDumperNode VisitDagDeconstructEvaluation(BoundDagDeconstructEvaluation node, object arg) + { + return new TreeDumperNode("dagDeconstructEvaluation", null, new TreeDumperNode[] + { + new TreeDumperNode("deconstructMethod", node.DeconstructMethod, null), + new TreeDumperNode("input", null, new TreeDumperNode[] { Visit(node.Input, null) }) + } + ); + } + public override TreeDumperNode VisitDagTypeEvaluation(BoundDagTypeEvaluation node, object arg) + { + return new TreeDumperNode("dagTypeEvaluation", null, new TreeDumperNode[] + { + new TreeDumperNode("type", node.Type, null), + new TreeDumperNode("input", null, new TreeDumperNode[] { Visit(node.Input, null) }) + } + ); + } + public override TreeDumperNode VisitDagFieldEvaluation(BoundDagFieldEvaluation node, object arg) + { + return new TreeDumperNode("dagFieldEvaluation", null, new TreeDumperNode[] + { + new TreeDumperNode("field", node.Field, null), + new TreeDumperNode("input", null, new TreeDumperNode[] { Visit(node.Input, null) }) + } + ); + } + public override TreeDumperNode VisitDagPropertyEvaluation(BoundDagPropertyEvaluation node, object arg) + { + return new TreeDumperNode("dagPropertyEvaluation", null, new TreeDumperNode[] + { + new TreeDumperNode("property", node.Property, null), + new TreeDumperNode("input", null, new TreeDumperNode[] { Visit(node.Input, null) }) + } + ); + } public override TreeDumperNode VisitPatternSwitchSection(BoundPatternSwitchSection node, object arg) { return new TreeDumperNode("patternSwitchSection", null, new TreeDumperNode[] @@ -10826,6 +11737,20 @@ public override TreeDumperNode VisitIsPatternExpression(BoundIsPatternExpression } ); } + public override TreeDumperNode VisitConstantPattern(BoundConstantPattern node, object arg) + { + return new TreeDumperNode("constantPattern", null, new TreeDumperNode[] + { + new TreeDumperNode("value", null, new TreeDumperNode[] { Visit(node.Value, null) }), + new TreeDumperNode("constantValue", node.ConstantValue, null) + } + ); + } + public override TreeDumperNode VisitDiscardPattern(BoundDiscardPattern node, object arg) + { + return new TreeDumperNode("discardPattern", null, Array.Empty() + ); + } public override TreeDumperNode VisitDeclarationPattern(BoundDeclarationPattern node, object arg) { return new TreeDumperNode("declarationPattern", null, new TreeDumperNode[] @@ -10837,20 +11762,20 @@ public override TreeDumperNode VisitDeclarationPattern(BoundDeclarationPattern n } ); } - public override TreeDumperNode VisitConstantPattern(BoundConstantPattern node, object arg) + public override TreeDumperNode VisitRecursivePattern(BoundRecursivePattern node, object arg) { - return new TreeDumperNode("constantPattern", null, new TreeDumperNode[] + return new TreeDumperNode("recursivePattern", null, new TreeDumperNode[] { - new TreeDumperNode("value", null, new TreeDumperNode[] { Visit(node.Value, null) }), - new TreeDumperNode("constantValue", node.ConstantValue, null) + new TreeDumperNode("declaredType", null, new TreeDumperNode[] { Visit(node.DeclaredType, null) }), + new TreeDumperNode("inputType", node.InputType, null), + new TreeDumperNode("deconstructMethodOpt", node.DeconstructMethodOpt, null), + new TreeDumperNode("deconstruction", null, node.Deconstruction.IsDefault ? Array.Empty() : from x in node.Deconstruction select Visit(x, null)), + new TreeDumperNode("propertiesOpt", node.PropertiesOpt, null), + new TreeDumperNode("variable", node.Variable, null), + new TreeDumperNode("variableAccess", null, new TreeDumperNode[] { Visit(node.VariableAccess, null) }) } ); } - public override TreeDumperNode VisitWildcardPattern(BoundWildcardPattern node, object arg) - { - return new TreeDumperNode("wildcardPattern", null, Array.Empty() - ); - } public override TreeDumperNode VisitDiscardExpression(BoundDiscardExpression node, object arg) { return new TreeDumperNode("discardExpression", null, new TreeDumperNode[] diff --git a/src/Compilers/CSharp/Portable/Generated/Syntax.xml.Internal.Generated.cs b/src/Compilers/CSharp/Portable/Generated/Syntax.xml.Internal.Generated.cs index e1166a7be25fdded936bbfcf62bddbccca464307..753010563d81f62967bad566978d66014b4fdec9 100644 --- a/src/Compilers/CSharp/Portable/Generated/Syntax.xml.Internal.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/Syntax.xml.Internal.Generated.cs @@ -10730,6 +10730,114 @@ protected PatternSyntax(ObjectReader reader) } } + internal sealed partial class DiscardPatternSyntax : PatternSyntax + { + internal readonly SyntaxToken underscoreToken; + + internal DiscardPatternSyntax(SyntaxKind kind, SyntaxToken underscoreToken, DiagnosticInfo[] diagnostics, SyntaxAnnotation[] annotations) + : base(kind, diagnostics, annotations) + { + this.SlotCount = 1; + this.AdjustFlagsAndWidth(underscoreToken); + this.underscoreToken = underscoreToken; + } + + + internal DiscardPatternSyntax(SyntaxKind kind, SyntaxToken underscoreToken, SyntaxFactoryContext context) + : base(kind) + { + this.SetFactoryContext(context); + this.SlotCount = 1; + this.AdjustFlagsAndWidth(underscoreToken); + this.underscoreToken = underscoreToken; + } + + + internal DiscardPatternSyntax(SyntaxKind kind, SyntaxToken underscoreToken) + : base(kind) + { + this.SlotCount = 1; + this.AdjustFlagsAndWidth(underscoreToken); + this.underscoreToken = underscoreToken; + } + + public SyntaxToken UnderscoreToken { get { return this.underscoreToken; } } + + internal override GreenNode GetSlot(int index) + { + switch (index) + { + case 0: return this.underscoreToken; + default: return null; + } + } + + internal override SyntaxNode CreateRed(SyntaxNode parent, int position) + { + return new CSharp.Syntax.DiscardPatternSyntax(this, parent, position); + } + + public override TResult Accept(CSharpSyntaxVisitor visitor) + { + return visitor.VisitDiscardPattern(this); + } + + public override void Accept(CSharpSyntaxVisitor visitor) + { + visitor.VisitDiscardPattern(this); + } + + public DiscardPatternSyntax Update(SyntaxToken underscoreToken) + { + if (underscoreToken != this.UnderscoreToken) + { + var newNode = SyntaxFactory.DiscardPattern(underscoreToken); + var diags = this.GetDiagnostics(); + if (diags != null && diags.Length > 0) + newNode = newNode.WithDiagnosticsGreen(diags); + var annotations = this.GetAnnotations(); + if (annotations != null && annotations.Length > 0) + newNode = newNode.WithAnnotationsGreen(annotations); + return newNode; + } + + return this; + } + + internal override GreenNode SetDiagnostics(DiagnosticInfo[] diagnostics) + { + return new DiscardPatternSyntax(this.Kind, this.underscoreToken, diagnostics, GetAnnotations()); + } + + internal override GreenNode SetAnnotations(SyntaxAnnotation[] annotations) + { + return new DiscardPatternSyntax(this.Kind, this.underscoreToken, GetDiagnostics(), annotations); + } + + internal DiscardPatternSyntax(ObjectReader reader) + : base(reader) + { + this.SlotCount = 1; + var underscoreToken = (SyntaxToken)reader.ReadValue(); + if (underscoreToken != null) + { + AdjustFlagsAndWidth(underscoreToken); + this.underscoreToken = underscoreToken; + } + } + + internal override void WriteTo(ObjectWriter writer) + { + base.WriteTo(writer); + writer.WriteValue(this.underscoreToken); + } + + static DiscardPatternSyntax() + { + ObjectBinder.RegisterTypeReader(typeof(DiscardPatternSyntax), r => new DiscardPatternSyntax(r)); + } + } + internal sealed partial class DeclarationPatternSyntax : PatternSyntax { internal readonly TypeSyntax type; @@ -15974,7 +16082,7 @@ internal ForEachVariableStatementSyntax(SyntaxKind kind, SyntaxToken forEachKeyw /// /// The variable(s) of the loop. In correct code this is a tuple /// literal, declaration expression with a tuple designator, or - /// a wildcard syntax in the form of a simple identifier. In broken + /// a discard syntax in the form of a simple identifier. In broken /// code it could be something else. /// public ExpressionSyntax Variable { get { return this.variable; } } @@ -34642,6 +34750,11 @@ public virtual TResult VisitWhenClause(WhenClauseSyntax node) return this.DefaultVisit(node); } + public virtual TResult VisitDiscardPattern(DiscardPatternSyntax node) + { + return this.DefaultVisit(node); + } + public virtual TResult VisitDeclarationPattern(DeclarationPatternSyntax node) { return this.DefaultVisit(node); @@ -35686,6 +35799,11 @@ public virtual void VisitWhenClause(WhenClauseSyntax node) this.DefaultVisit(node); } + public virtual void VisitDiscardPattern(DiscardPatternSyntax node) + { + this.DefaultVisit(node); + } + public virtual void VisitDeclarationPattern(DeclarationPatternSyntax node) { this.DefaultVisit(node); @@ -36936,6 +37054,12 @@ public override CSharpSyntaxNode VisitWhenClause(WhenClauseSyntax node) return node.Update(whenKeyword, condition); } + public override CSharpSyntaxNode VisitDiscardPattern(DiscardPatternSyntax node) + { + var underscoreToken = (SyntaxToken)this.Visit(node.UnderscoreToken); + return node.Update(underscoreToken); + } + public override CSharpSyntaxNode VisitDeclarationPattern(DeclarationPatternSyntax node) { var type = (TypeSyntax)this.Visit(node.Type); @@ -40606,6 +40730,33 @@ public WhenClauseSyntax WhenClause(SyntaxToken whenKeyword, ExpressionSyntax con return result; } + public DiscardPatternSyntax DiscardPattern(SyntaxToken underscoreToken) + { +#if DEBUG + if (underscoreToken == null) + throw new ArgumentNullException(nameof(underscoreToken)); + switch (underscoreToken.Kind) + { + case SyntaxKind.IdentifierToken: + break; + default: + throw new ArgumentException("underscoreToken"); + } +#endif + + int hash; + var cached = CSharpSyntaxNodeCache.TryGetNode((int)SyntaxKind.DiscardPattern, underscoreToken, this.context, out hash); + if (cached != null) return (DiscardPatternSyntax)cached; + + var result = new DiscardPatternSyntax(SyntaxKind.DiscardPattern, underscoreToken, this.context); + if (hash >= 0) + { + SyntaxNodeCache.AddNode(result, hash); + } + + return result; + } + public DeclarationPatternSyntax DeclarationPattern(TypeSyntax type, VariableDesignationSyntax designation) { #if DEBUG @@ -47633,6 +47784,33 @@ public static WhenClauseSyntax WhenClause(SyntaxToken whenKeyword, ExpressionSyn return result; } + public static DiscardPatternSyntax DiscardPattern(SyntaxToken underscoreToken) + { +#if DEBUG + if (underscoreToken == null) + throw new ArgumentNullException(nameof(underscoreToken)); + switch (underscoreToken.Kind) + { + case SyntaxKind.IdentifierToken: + break; + default: + throw new ArgumentException("underscoreToken"); + } +#endif + + int hash; + var cached = SyntaxNodeCache.TryGetNode((int)SyntaxKind.DiscardPattern, underscoreToken, out hash); + if (cached != null) return (DiscardPatternSyntax)cached; + + var result = new DiscardPatternSyntax(SyntaxKind.DiscardPattern, underscoreToken); + if (hash >= 0) + { + SyntaxNodeCache.AddNode(result, hash); + } + + return result; + } + public static DeclarationPatternSyntax DeclarationPattern(TypeSyntax type, VariableDesignationSyntax designation) { #if DEBUG @@ -52283,6 +52461,7 @@ internal static IEnumerable GetNodeTypes() typeof(IsPatternExpressionSyntax), typeof(ThrowExpressionSyntax), typeof(WhenClauseSyntax), + typeof(DiscardPatternSyntax), typeof(DeclarationPatternSyntax), typeof(DeconstructionPatternSyntax), typeof(SubpatternElementSyntax), diff --git a/src/Compilers/CSharp/Portable/Generated/Syntax.xml.Main.Generated.cs b/src/Compilers/CSharp/Portable/Generated/Syntax.xml.Main.Generated.cs index 6b70bca7751a91e43da9f61ce23557981507ac88..7c5e90a3e39bb3b991ca49333bece1f9576d2c7c 100644 --- a/src/Compilers/CSharp/Portable/Generated/Syntax.xml.Main.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/Syntax.xml.Main.Generated.cs @@ -454,6 +454,12 @@ public virtual TResult VisitWhenClause(WhenClauseSyntax node) return this.DefaultVisit(node); } + /// Called when the visitor visits a DiscardPatternSyntax node. + public virtual TResult VisitDiscardPattern(DiscardPatternSyntax node) + { + return this.DefaultVisit(node); + } + /// Called when the visitor visits a DeclarationPatternSyntax node. public virtual TResult VisitDeclarationPattern(DeclarationPatternSyntax node) { @@ -1705,6 +1711,12 @@ public virtual void VisitWhenClause(WhenClauseSyntax node) this.DefaultVisit(node); } + /// Called when the visitor visits a DiscardPatternSyntax node. + public virtual void VisitDiscardPattern(DiscardPatternSyntax node) + { + this.DefaultVisit(node); + } + /// Called when the visitor visits a DeclarationPatternSyntax node. public virtual void VisitDeclarationPattern(DeclarationPatternSyntax node) { @@ -3090,6 +3102,12 @@ public override SyntaxNode VisitWhenClause(WhenClauseSyntax node) return node.Update(whenKeyword, condition); } + public override SyntaxNode VisitDiscardPattern(DiscardPatternSyntax node) + { + var underscoreToken = this.VisitToken(node.UnderscoreToken); + return node.Update(underscoreToken); + } + public override SyntaxNode VisitDeclarationPattern(DeclarationPatternSyntax node) { var type = (TypeSyntax)this.Visit(node.Type); @@ -6609,6 +6627,20 @@ public static WhenClauseSyntax WhenClause(ExpressionSyntax condition) return SyntaxFactory.WhenClause(SyntaxFactory.Token(SyntaxKind.WhenKeyword), condition); } + /// Creates a new DiscardPatternSyntax instance. + public static DiscardPatternSyntax DiscardPattern(SyntaxToken underscoreToken) + { + switch (underscoreToken.Kind()) + { + case SyntaxKind.IdentifierToken: + break; + default: + throw new ArgumentException("underscoreToken"); + } + return (DiscardPatternSyntax)Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.SyntaxFactory.DiscardPattern((Syntax.InternalSyntax.SyntaxToken)underscoreToken.Node).CreateRed(); + } + + /// Creates a new DeclarationPatternSyntax instance. public static DeclarationPatternSyntax DeclarationPattern(TypeSyntax type, VariableDesignationSyntax designation) { diff --git a/src/Compilers/CSharp/Portable/Generated/Syntax.xml.Syntax.Generated.cs b/src/Compilers/CSharp/Portable/Generated/Syntax.xml.Syntax.Generated.cs index 624b0e9237320cbae4d19e4c583d119ffe0f7d80..21b189335c9f203cabb3839a09f1bddfe0314107 100644 --- a/src/Compilers/CSharp/Portable/Generated/Syntax.xml.Syntax.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/Syntax.xml.Syntax.Generated.cs @@ -6711,6 +6711,63 @@ internal PatternSyntax(Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.CShar } } + public sealed partial class DiscardPatternSyntax : PatternSyntax + { + internal DiscardPatternSyntax(Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.CSharpSyntaxNode green, SyntaxNode parent, int position) + : base(green, parent, position) + { + } + + public SyntaxToken UnderscoreToken + { + get { return new SyntaxToken(this, ((Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.DiscardPatternSyntax)this.Green).underscoreToken, this.Position, 0); } + } + + internal override SyntaxNode GetNodeSlot(int index) + { + switch (index) + { + default: return null; + } + } + internal override SyntaxNode GetCachedSlot(int index) + { + switch (index) + { + default: return null; + } + } + + public override TResult Accept(CSharpSyntaxVisitor visitor) + { + return visitor.VisitDiscardPattern(this); + } + + public override void Accept(CSharpSyntaxVisitor visitor) + { + visitor.VisitDiscardPattern(this); + } + + public DiscardPatternSyntax Update(SyntaxToken underscoreToken) + { + if (underscoreToken != this.UnderscoreToken) + { + var newNode = SyntaxFactory.DiscardPattern(underscoreToken); + var annotations = this.GetAnnotations(); + if (annotations != null && annotations.Length > 0) + return newNode.WithAnnotations(annotations); + return newNode; + } + + return this; + } + + public DiscardPatternSyntax WithUnderscoreToken(SyntaxToken underscoreToken) + { + return this.Update(underscoreToken); + } + } + public sealed partial class DeclarationPatternSyntax : PatternSyntax { private TypeSyntax type; @@ -9914,7 +9971,7 @@ public override SyntaxToken OpenParenToken /// /// The variable(s) of the loop. In correct code this is a tuple /// literal, declaration expression with a tuple designator, or - /// a wildcard syntax in the form of a simple identifier. In broken + /// a discard syntax in the form of a simple identifier. In broken /// code it could be something else. /// public ExpressionSyntax Variable diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Patterns.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Patterns.cs index 0dddd02d2af9764e46028db85af971ecce2a3fb3..c89ab311d3138461900775dab78d440514e0aebf 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Patterns.cs @@ -1,164 +1,581 @@ // 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 System; +using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; -using System.Collections.Immutable; -using System.Collections.Generic; namespace Microsoft.CodeAnalysis.CSharp { internal sealed partial class LocalRewriter { - public override BoundNode VisitIsPatternExpression(BoundIsPatternExpression node) + private struct IsPatternTranslator : IDisposable { - var loweredExpression = VisitExpression(node.Expression); - var loweredPattern = LowerPattern(node.Pattern); - return MakeIsPattern(loweredPattern, loweredExpression); - } + private readonly LocalRewriter _localRewriter; + private readonly SyntheticBoundNodeFactory _factory; - // Input must be used no more than once in the result. If it is needed repeatedly store its value in a temp and use the temp. - BoundExpression MakeIsPattern(BoundPattern loweredPattern, BoundExpression loweredInput) - { - var syntax = _factory.Syntax = loweredPattern.Syntax; - switch (loweredPattern.Kind) - { - case BoundKind.DeclarationPattern: - { - var declPattern = (BoundDeclarationPattern)loweredPattern; - return MakeIsDeclarationPattern(declPattern, loweredInput); - } - - case BoundKind.WildcardPattern: - return _factory.Literal(true); + private readonly ArrayBuilder _conjunctBuilder; + private readonly ArrayBuilder _sideEffectBuilder; + private readonly DagTempAllocator _tempAllocator; - case BoundKind.ConstantPattern: - { - var constantPattern = (BoundConstantPattern)loweredPattern; - return MakeIsConstantPattern(constantPattern, loweredInput); - } + private readonly BoundExpression _loweredInput; + private readonly BoundDagTemp _inputTemp; - default: - throw ExceptionUtilities.UnexpectedValue(loweredPattern.Kind); + public IsPatternTranslator(LocalRewriter localRewriter, BoundExpression loweredInput) + { + this._localRewriter = localRewriter; + this._factory = localRewriter._factory; + this._tempAllocator = new DagTempAllocator(localRewriter._factory); + this._loweredInput = loweredInput; + this._inputTemp = new BoundDagTemp(loweredInput.Syntax, loweredInput.Type, null, 0); + this._conjunctBuilder = ArrayBuilder.GetInstance(); + this._sideEffectBuilder = ArrayBuilder.GetInstance(); } - } - BoundPattern LowerPattern(BoundPattern pattern) - { - switch (pattern.Kind) + public void Dispose() { - case BoundKind.DeclarationPattern: - { - var declPattern = (BoundDeclarationPattern)pattern; - return declPattern.Update(declPattern.Variable, VisitExpression(declPattern.VariableAccess), declPattern.DeclaredType, declPattern.IsVar); - } - case BoundKind.ConstantPattern: - { - var constantPattern = (BoundConstantPattern)pattern; - return constantPattern.Update(VisitExpression(constantPattern.Value), constantPattern.ConstantValue); - } - default: - return pattern; + _conjunctBuilder.Free(); + _sideEffectBuilder.Free(); + _tempAllocator.Dispose(); } - } - private BoundExpression MakeIsConstantPattern(BoundConstantPattern loweredPattern, BoundExpression loweredInput) - { - return CompareWithConstant(loweredInput, loweredPattern.Value); - } + public class DagTempAllocator : IDisposable + { + private readonly SyntheticBoundNodeFactory _factory; + private readonly PooledDictionary _map = PooledDictionary.GetInstance(); + private readonly ArrayBuilder _temps = ArrayBuilder.GetInstance(); - private BoundExpression MakeIsDeclarationPattern(BoundDeclarationPattern loweredPattern, BoundExpression loweredInput) - { - Debug.Assert(((object)loweredPattern.Variable == null && loweredPattern.VariableAccess.Kind == BoundKind.DiscardExpression) || - loweredPattern.Variable.GetTypeOrReturnType() == loweredPattern.DeclaredType.Type); + public DagTempAllocator(SyntheticBoundNodeFactory factory) + { + this._factory = factory; + } - if (loweredPattern.IsVar) - { - var result = _factory.Literal(true); + public void Dispose() + { + _temps.Free(); + _map.Free(); + } - if (loweredPattern.VariableAccess.Kind == BoundKind.DiscardExpression) + public BoundExpression GetTemp(BoundDagTemp dagTemp) { + if (!_map.TryGetValue(dagTemp, out var result)) + { + // PROTOTYPE(patterns2): Not sure what temp kind should be used for `is pattern`. + var temp = _factory.SynthesizedLocal(dagTemp.Type, syntax: _factory.Syntax, kind: SynthesizedLocalKind.SwitchCasePatternMatching); + _map.Add(dagTemp, _factory.Local(temp)); + _temps.Add(temp); + result = _factory.Local(temp); + } + return result; } - Debug.Assert((object)loweredPattern.Variable != null && loweredInput.Type.Equals(loweredPattern.Variable.GetTypeOrReturnType(), TypeCompareKind.AllIgnoreOptions)); + public ImmutableArray AllTemps() + { + return _temps.ToImmutableArray(); + } - var assignment = _factory.AssignmentExpression(loweredPattern.VariableAccess, loweredInput); - return _factory.MakeSequence(assignment, result); + public void AssignTemp(BoundDagTemp dagTemp, BoundExpression value) + { + _map.Add(dagTemp, value); + } } - if (loweredPattern.VariableAccess.Kind == BoundKind.DiscardExpression) + private bool LowerDecision(BoundDagDecision decision) { - LocalSymbol temp; - BoundLocal discard = _factory.MakeTempForDiscard((BoundDiscardExpression)loweredPattern.VariableAccess, out temp); - - return _factory.Sequence(ImmutableArray.Create(temp), - sideEffects: ImmutableArray.Empty, - result: MakeIsDeclarationPattern(loweredPattern.Syntax, loweredInput, discard, requiresNullTest: loweredInput.Type.CanContainNull())); + var oldSyntax = _factory.Syntax; + _factory.Syntax = decision.Syntax; + var result = LowerDecisionCore(decision); + _factory.Syntax = oldSyntax; + return result; } - return MakeIsDeclarationPattern(loweredPattern.Syntax, loweredInput, loweredPattern.VariableAccess, requiresNullTest: loweredInput.Type.CanContainNull()); - } - - /// - /// Is the test, produced as a result of a pattern-matching operation, always true? - /// Knowing that enables us to construct slightly more efficient code. - /// - private bool IsIrrefutablePatternTest(BoundExpression test) - { - while (true) + private bool LowerDecisionCore(BoundDagDecision decision) { - switch (test.Kind) + void addConjunct(ref IsPatternTranslator self, BoundExpression expression) { - case BoundKind.Literal: + // PROTOTYPE(patterns2): could handle constant expressions more efficiently. + if (self._sideEffectBuilder.Count != 0) + { + expression = self._factory.Sequence(ImmutableArray.Empty, self._sideEffectBuilder.ToImmutable(), expression); + self._sideEffectBuilder.Clear(); + } + + self._conjunctBuilder.Add(expression); + } + + var input = _tempAllocator.GetTemp(decision.Input); + switch (decision) + { + case BoundNonNullDecision d: + // If the actual input is a constant, short-circuit this test + if (d.Input == _inputTemp && _loweredInput.ConstantValue != null) + { + var decisionResult = _loweredInput.ConstantValue != ConstantValue.Null; + if (!decisionResult) + { + _conjunctBuilder.Add(_factory.Literal(decisionResult)); + return true; + } + } + else + { + // PROTOTYPE(patterns2): combine null test and type test when possible for improved code + addConjunct(ref this, _localRewriter.MakeNullCheck(d.Syntax, input, input.Type.IsNullableType() ? BinaryOperatorKind.NullableNullNotEqual : BinaryOperatorKind.NotEqual)); + } + break; + case BoundTypeDecision d: + { + addConjunct(ref this, _factory.Is(input, d.Type)); + } + break; + case BoundValueDecision d: + // If the actual input is a constant, short-circuit this test + if (d.Input == _inputTemp && _loweredInput.ConstantValue != null) + { + var decisionResult = _loweredInput.ConstantValue == d.Value; + if (!decisionResult) + { + _conjunctBuilder.Add(_factory.Literal(decisionResult)); + return true; + } + } + else if (d.Value == ConstantValue.Null) + { + addConjunct(ref this, _localRewriter.MakeNullCheck(d.Syntax, input, input.Type.IsNullableType() ? BinaryOperatorKind.NullableNullEqual : BinaryOperatorKind.Equal)); + } + else + { + var systemObject = _factory.SpecialType(SpecialType.System_Object); + addConjunct(ref this, _localRewriter.MakeEqual(_factory.Literal(d.Value, input.Type), input)); + } + break; + case BoundDagFieldEvaluation f: + { + var field = f.Field; + var outputTemp = new BoundDagTemp(f.Syntax, field.Type, f, 0); + var output = _tempAllocator.GetTemp(outputTemp); + _sideEffectBuilder.Add(_factory.AssignmentExpression(output, _factory.Field(input, field))); + } + break; + case BoundDagPropertyEvaluation p: + { + var property = p.Property; + var outputTemp = new BoundDagTemp(p.Syntax, property.Type, p, 0); + var output = _tempAllocator.GetTemp(outputTemp); + _sideEffectBuilder.Add(_factory.AssignmentExpression(output, _factory.Property(input, property))); + } + break; + case BoundDagDeconstructEvaluation d: + { + var method = d.DeconstructMethod; + var refKindBuilder = ArrayBuilder.GetInstance(); + var argBuilder = ArrayBuilder.GetInstance(); + BoundExpression receiver; + void addArg(RefKind refKind, BoundExpression expression) + { + refKindBuilder.Add(refKind); + argBuilder.Add(expression); + } + Debug.Assert(method.Name == "Deconstruct"); + int extensionExtra; + if (method.IsStatic) + { + Debug.Assert(method.IsExtensionMethod); + receiver = _factory.Type(method.ContainingType); + addArg(RefKind.None, input); + extensionExtra = 1; + } + else + { + receiver = input; + extensionExtra = 0; + } + for (int i = extensionExtra; i < method.ParameterCount; i++) + { + var parameter = method.Parameters[i]; + Debug.Assert(parameter.RefKind == RefKind.Out); + var outputTemp = new BoundDagTemp(d.Syntax, parameter.Type, d, i - extensionExtra); + addArg(RefKind.Out, _tempAllocator.GetTemp(outputTemp)); + } + _sideEffectBuilder.Add(_factory.Call(receiver, method, refKindBuilder.ToImmutableAndFree(), argBuilder.ToImmutableAndFree())); + } + break; + case BoundDagTypeEvaluation t: { - var value = ((BoundLiteral)test).ConstantValue; - return value.IsBoolean && value.BooleanValue; + var inputType = input.Type; + if (inputType.IsDynamic()) + { + inputType = _factory.SpecialType(SpecialType.System_Object); + } + + var type = t.Type; + var outputTemp = new BoundDagTemp(t.Syntax, type, t, 0); + var output = _tempAllocator.GetTemp(outputTemp); + var conversion = _factory.Compilation.ClassifyConversion(inputType, output.Type); + if (conversion.Exists) + { + _sideEffectBuilder.Add(_factory.AssignmentExpression(output, _factory.Convert(type, input, conversion))); + } + else + { + _sideEffectBuilder.Add(_factory.AssignmentExpression(output, _factory.As(input, type))); + } } - case BoundKind.Sequence: - test = ((BoundSequence)test).Value; - continue; - default: - return false; + break; + } + + return false; + } + + public BoundExpression LowerIsPattern(BoundPattern pattern, CSharpCompilation compilation) + { + var decisionBuilder = new DecisionDagBuilder(compilation); + var inputTemp = decisionBuilder.LowerPattern(_loweredInput, pattern, out var decisions, out var bindings); + Debug.Assert(inputTemp == _inputTemp); + + // first, copy the input expression into the input temp + if (pattern.Kind != BoundKind.RecursivePattern && + (_loweredInput.Kind == BoundKind.Local || _loweredInput.Kind == BoundKind.Parameter || _loweredInput.ConstantValue != null)) + { + // Since non-recursive patterns cannot have side-effects on locals, we reuse an existing local + // if present. A recursive pattern, on the other hand, may mutate a local through a captured lambda + // when a `Deconstruct` method is invoked. + _tempAllocator.AssignTemp(inputTemp, _loweredInput); + } + else + { + _sideEffectBuilder.Add(_factory.AssignmentExpression(_tempAllocator.GetTemp(inputTemp), _loweredInput)); + } + + // then process all of the individual decisions + foreach (var decision in decisions) + { + if (LowerDecision(decision)) + { + break; + } + } + + if (_sideEffectBuilder.Count != 0) + { + _conjunctBuilder.Add(_factory.Sequence(ImmutableArray.Empty, _sideEffectBuilder.ToImmutable(), _factory.Literal(true))); + _sideEffectBuilder.Clear(); + } + BoundExpression result = null; + foreach (var conjunct in _conjunctBuilder) + { + result = (result == null) ? conjunct : _factory.LogicalAnd(result, conjunct); + } + + var bindingsBuilder = ArrayBuilder.GetInstance(); + foreach (var (left, right) in bindings) + { + bindingsBuilder.Add(_factory.AssignmentExpression(left, _tempAllocator.GetTemp(right))); + } + + if (bindingsBuilder.Count > 0) + { + var c = _factory.Sequence(ImmutableArray.Empty, bindingsBuilder.ToImmutableAndFree(), _factory.Literal(true)); + result = (result == null) ? c : (BoundExpression)_factory.LogicalAnd(result, c); + } + else if (result == null) + { + result = _factory.Literal(true); } + + return _factory.Sequence(_tempAllocator.AllTemps(), ImmutableArray.Empty, result); } } - private BoundExpression CompareWithConstant(BoundExpression input, BoundExpression boundConstant) + private BoundExpression MakeTypeTestAndAssignment(BoundExpression loweredTarget, BoundExpression loweredInput, TypeSymbol type) { - var systemObject = _factory.SpecialType(SpecialType.System_Object); - if (boundConstant.ConstantValue == ConstantValue.Null) + Debug.Assert(type == loweredTarget.Type); + + // If the match is impossible, we simply evaluate the input and yield false. + var matchConstantValue = MatchConstantValue(loweredInput, type, false); + if (matchConstantValue == false) + { + return _factory.MakeSequence(loweredInput, _factory.Literal(false)); + } + + // It is possible that the input value is already of the correct type, in which case the pattern + // is irrefutable, and we can just do the assignment and return true (or perform the null test). + if (matchConstantValue == true) { - if (input.Type.IsNonNullableValueType()) + BoundExpression convertedInput; + if (loweredInput.Type.IsNullableType()) { - var systemBoolean = _factory.SpecialType(SpecialType.System_Boolean); - return RewriteNullableNullEquality( - syntax: _factory.Syntax, - kind: BinaryOperatorKind.NullableNullEqual, - loweredLeft: input, - loweredRight: boundConstant, - returnType: systemBoolean); + var getValueOrDefault = _factory.SpecialMethod(SpecialMember.System_Nullable_T_GetValueOrDefault).AsMember((NamedTypeSymbol)loweredInput.Type); + convertedInput = _factory.Convert(type, _factory.Call(loweredInput, getValueOrDefault)); } else { - return _factory.ObjectEqual(_factory.Convert(systemObject, input), boundConstant); + convertedInput = _factory.Convert(type, loweredInput); } + var assignment = _factory.AssignmentExpression(loweredTarget, convertedInput); + return _factory.MakeSequence(assignment, _factory.Literal(true)); } - else if (input.Type.IsNullableType() && boundConstant.NullableNeverHasValue()) + + // a pattern match of the form "expression is Type identifier" is equivalent to + // an invocation of one of these helpers: + if (type.IsReferenceType) { - return _factory.Not(MakeNullableHasValue(_factory.Syntax, input)); + // bool Is(object e, out T t) where T : class // reference type + // { + // t = e as T; + // return t != null; + // } + + return _factory.ObjectNotEqual( + _factory.AssignmentExpression(loweredTarget, _factory.As(loweredInput, type)), + _factory.Null(type)); } - else + else // type parameter or value type { - return _factory.StaticCall( - systemObject, - "Equals", - _factory.Convert(systemObject, boundConstant), - _factory.Convert(systemObject, input) + // bool Is(this object i, out T o) + // { + // // inefficient because it performs the type test twice, and also because it boxes the input. + // bool s; + // o = (s = i is T) ? (T)i : default(T); + // return s; + // } + + // Because a cast involving a type parameter is not necessarily a valid conversion (or, if it is, it might not + // be of a kind appropriate for pattern-matching), we use `object` as an intermediate type for the input expression. + var objectType = _factory.SpecialType(SpecialType.System_Object); + var s = _factory.SynthesizedLocal(_factory.SpecialType(SpecialType.System_Boolean), loweredTarget.Syntax); + var i = _factory.SynthesizedLocal(objectType, loweredTarget.Syntax); // we copy the input to avoid double evaluation + return _factory.Sequence( + ImmutableArray.Create(s, i), + ImmutableArray.Create( + _factory.AssignmentExpression(_factory.Local(i), _factory.Convert(objectType, loweredInput)), + _factory.AssignmentExpression(loweredTarget, _factory.Conditional( + _factory.AssignmentExpression(_factory.Local(s), _factory.Is(_factory.Local(i), type)), + _factory.Convert(type, _factory.Local(i)), + _factory.Default(type), type)) + ), + _factory.Local(s) ); } } + private BoundExpression MakeEqual(BoundLiteral boundLiteral, BoundExpression input) + { + if (boundLiteral.Type.SpecialType == SpecialType.System_Double && Double.IsNaN(boundLiteral.ConstantValue.DoubleValue) || + boundLiteral.Type.SpecialType == SpecialType.System_Single && Single.IsNaN(boundLiteral.ConstantValue.SingleValue)) + { + // NaN must be treated specially, as operator== doesn't treat it as equal to anything, even itself. + Debug.Assert(boundLiteral.Type == input.Type); + return _factory.InstanceCall(boundLiteral, "Equals", input); + } + + var booleanType = _factory.SpecialType(SpecialType.System_Boolean); + var intType = _factory.SpecialType(SpecialType.System_Int32); + switch (boundLiteral.Type.SpecialType) + { + case SpecialType.System_Boolean: + return MakeBinaryOperator(_factory.Syntax, BinaryOperatorKind.BoolEqual, boundLiteral, input, booleanType, method: null); + case SpecialType.System_Byte: + case SpecialType.System_Char: + case SpecialType.System_Int16: + case SpecialType.System_SByte: + case SpecialType.System_UInt16: + // PROTOTYPE(patterns2): need to check that this produces efficient code + return MakeBinaryOperator(_factory.Syntax, BinaryOperatorKind.IntEqual, _factory.Convert(intType, boundLiteral), _factory.Convert(intType, input), booleanType, method: null); + case SpecialType.System_Decimal: + return MakeBinaryOperator(_factory.Syntax, BinaryOperatorKind.DecimalEqual, boundLiteral, input, booleanType, method: null); + case SpecialType.System_Double: + return MakeBinaryOperator(_factory.Syntax, BinaryOperatorKind.DoubleEqual, boundLiteral, input, booleanType, method: null); + case SpecialType.System_Int32: + return MakeBinaryOperator(_factory.Syntax, BinaryOperatorKind.IntEqual, boundLiteral, input, booleanType, method: null); + case SpecialType.System_Int64: + return MakeBinaryOperator(_factory.Syntax, BinaryOperatorKind.LongEqual, boundLiteral, input, booleanType, method: null); + case SpecialType.System_Single: + return MakeBinaryOperator(_factory.Syntax, BinaryOperatorKind.FloatEqual, boundLiteral, input, booleanType, method: null); + case SpecialType.System_String: + return MakeBinaryOperator(_factory.Syntax, BinaryOperatorKind.StringEqual, boundLiteral, input, booleanType, method: null); + case SpecialType.System_UInt32: + return MakeBinaryOperator(_factory.Syntax, BinaryOperatorKind.UIntEqual, boundLiteral, input, booleanType, method: null); + case SpecialType.System_UInt64: + return MakeBinaryOperator(_factory.Syntax, BinaryOperatorKind.ULongEqual, boundLiteral, input, booleanType, method: null); + default: + // PROTOTYPE(patterns2): need more efficient code for enum test, e.g. `color is Color.Red` + // This is the (correct but inefficient) fallback for any type that isn't yet implemented (e.g. enums) + var systemObject = _factory.SpecialType(SpecialType.System_Object); + return _factory.StaticCall( + systemObject, + "Equals", + _factory.Convert(systemObject, boundLiteral), + _factory.Convert(systemObject, input) + ); + } + } + + public override BoundNode VisitIsPatternExpression(BoundIsPatternExpression node) + { + using (var x = new IsPatternTranslator(this, VisitExpression(node.Expression))) + { + return x.LowerIsPattern(node.Pattern, this._compilation); + } + } + + //public override BoundNode VisitIsPatternExpression(BoundIsPatternExpression node) + //{ + // var loweredExpression = VisitExpression(node.Expression); + // var loweredPattern = LowerPattern(node.Pattern); + // return MakeIsPattern(loweredPattern, loweredExpression); + //} + + //// Input must be used no more than once in the result. If it is needed repeatedly store its value in a temp and use the temp. + //BoundExpression MakeIsPattern(BoundPattern loweredPattern, BoundExpression loweredInput) + //{ + // var syntax = _factory.Syntax = loweredPattern.Syntax; + // switch (loweredPattern.Kind) + // { + // case BoundKind.DeclarationPattern: + // { + // var declPattern = (BoundDeclarationPattern)loweredPattern; + // return MakeIsDeclarationPattern(declPattern, loweredInput); + // } + + // case BoundKind.WildcardPattern: + // return _factory.Literal(true); + + // case BoundKind.ConstantPattern: + // { + // var constantPattern = (BoundConstantPattern)loweredPattern; + // return MakeIsConstantPattern(constantPattern, loweredInput); + // } + + // default: + // throw ExceptionUtilities.UnexpectedValue(loweredPattern.Kind); + // } + //} + + BoundPattern LowerPattern(BoundPattern pattern) + { + switch (pattern.Kind) + { + case BoundKind.DeclarationPattern: + { + var declPattern = (BoundDeclarationPattern)pattern; + return declPattern.Update(declPattern.Variable, VisitExpression(declPattern.VariableAccess), declPattern.DeclaredType, declPattern.IsVar); + } + case BoundKind.RecursivePattern: + { + throw ExceptionUtilities.UnexpectedValue(pattern.Kind); + } + case BoundKind.ConstantPattern: + { + var constantPattern = (BoundConstantPattern)pattern; + return constantPattern.Update(VisitExpression(constantPattern.Value), constantPattern.ConstantValue); + } + default: + return pattern; + } + } + + //private BoundExpression MakeIsConstantPattern(BoundConstantPattern loweredPattern, BoundExpression loweredInput) + //{ + // return CompareWithConstant(loweredInput, loweredPattern.Value); + //} + + //private BoundExpression MakeIsDeclarationPattern(BoundDeclarationPattern loweredPattern, BoundExpression loweredInput) + //{ + // Debug.Assert(((object)loweredPattern.Variable == null && loweredPattern.VariableAccess.Kind == BoundKind.DiscardExpression) || + // loweredPattern.Variable.GetTypeOrReturnType() == loweredPattern.DeclaredType.Type); + + // if (loweredPattern.IsVar) + // { + // var result = _factory.Literal(true); + + // if (loweredPattern.VariableAccess.Kind == BoundKind.DiscardExpression) + // { + // return result; + // } + + // Debug.Assert((object)loweredPattern.Variable != null && loweredInput.Type.Equals(loweredPattern.Variable.GetTypeOrReturnType(), TypeCompareKind.AllIgnoreOptions)); + + // var assignment = _factory.AssignmentExpression(loweredPattern.VariableAccess, loweredInput); + // return _factory.MakeSequence(assignment, result); + // } + + // if (loweredPattern.VariableAccess.Kind == BoundKind.DiscardExpression) + // { + // LocalSymbol temp; + // BoundLocal discard = _factory.MakeTempForDiscard((BoundDiscardExpression)loweredPattern.VariableAccess, out temp); + + // return _factory.Sequence(ImmutableArray.Create(temp), + // sideEffects: ImmutableArray.Empty, + // result: MakeIsDeclarationPattern(loweredPattern.Syntax, loweredInput, discard, requiresNullTest: loweredInput.Type.CanContainNull())); + // } + + // return MakeIsDeclarationPattern(loweredPattern.Syntax, loweredInput, loweredPattern.VariableAccess, requiresNullTest: loweredInput.Type.CanContainNull()); + //} + + ///// + ///// Is the test, produced as a result of a pattern-matching operation, always true? + ///// Knowing that enables us to construct slightly more efficient code. + ///// + //private bool IsIrrefutablePatternTest(BoundExpression test) + //{ + // while (true) + // { + // switch (test.Kind) + // { + // case BoundKind.Literal: + // { + // var value = ((BoundLiteral)test).ConstantValue; + // return value.IsBoolean && value.BooleanValue; + // } + // case BoundKind.Sequence: + // test = ((BoundSequence)test).Value; + // continue; + // default: + // return false; + // } + // } + //} + + //private BoundExpression CompareWithConstant(BoundExpression input, BoundExpression boundConstant) + //{ + // var systemObject = _factory.SpecialType(SpecialType.System_Object); + // if (boundConstant.ConstantValue == ConstantValue.Null) + // { + // if (input.Type.IsNonNullableValueType()) + // { + // var systemBoolean = _factory.SpecialType(SpecialType.System_Boolean); + // return RewriteNullableNullEquality( + // syntax: _factory.Syntax, + // kind: BinaryOperatorKind.NullableNullEqual, + // loweredLeft: input, + // loweredRight: boundConstant, + // returnType: systemBoolean); + // } + // else + // { + // return _factory.ObjectEqual(_factory.Convert(systemObject, input), boundConstant); + // } + // } + // else if (input.Type.IsNullableType() && boundConstant.NullableNeverHasValue()) + // { + // return _factory.Not(MakeNullableHasValue(_factory.Syntax, input)); + // } + // else + // { + // return _factory.StaticCall( + // systemObject, + // "Equals", + // _factory.Convert(systemObject, boundConstant), + // _factory.Convert(systemObject, input) + // ); + // } + //} + private bool? MatchConstantValue(BoundExpression source, TypeSymbol targetType, bool requiredNullTest) { // use site diagnostics will already have been reported during binding. @@ -215,7 +632,6 @@ BoundExpression MakeIsDeclarationPattern(SyntaxNode syntax, BoundExpression lowe var assignment = _factory.AssignmentExpression(loweredTarget, convertedInput); return _factory.MakeSequence(assignment, _factory.Literal(true)); } - } else { diff --git a/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs b/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs index 896e5f0138342b466d541475901330b44c719d35..d306e4d3bad5b7ffcaacda73a2d9f34b50b45c84 100644 --- a/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs +++ b/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs @@ -381,6 +381,11 @@ public BoundAssignmentOperator AssignmentExpression(BoundExpression left, BoundE return new BoundAssignmentOperator(Syntax, left, right, left.Type, refKind: refKind) { WasCompilerGenerated = true }; } + public BoundAssignmentOperator AssignmentExpression(LocalSymbol left, BoundExpression right, RefKind refKind = RefKind.None) + { + return new BoundAssignmentOperator(Syntax, this.Local(left), right, left.Type, refKind: refKind) { WasCompilerGenerated = true }; + } + public BoundBlock Block() { return Block(ImmutableArray.Empty); @@ -553,6 +558,11 @@ public BoundLiteral Literal(uint value) return new BoundLiteral(Syntax, ConstantValue.Create(value), SpecialType(Microsoft.CodeAnalysis.SpecialType.System_UInt32)) { WasCompilerGenerated = true }; } + public BoundLiteral Literal(ConstantValue value, TypeSymbol type) + { + return new BoundLiteral(Syntax, value, type) { WasCompilerGenerated = true }; + } + public BoundObjectCreationExpression New(NamedTypeSymbol type, params BoundExpression[] args) { // TODO: add diagnostics for when things fall apart diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index 99b8e029324955679217df204f7b3ee8074718ff..9c8245d2f73335efae7d0dcd760b615b059d2abd 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -224,8 +224,10 @@ private IOperation CreateInternal(BoundNode boundNode) return CreateBoundConstantPatternOperation((BoundConstantPattern)boundNode); case BoundKind.DeclarationPattern: return CreateBoundDeclarationPatternOperation((BoundDeclarationPattern)boundNode); - case BoundKind.WildcardPattern: - throw ExceptionUtilities.Unreachable; + case BoundKind.RecursivePattern: + return CreateBoundRecursivePatternOperation((BoundRecursivePattern)boundNode); + case BoundKind.DiscardPattern: + return CreateBoundDiscardPatternOperation((BoundDiscardPattern)boundNode); case BoundKind.PatternSwitchStatement: return CreateBoundPatternSwitchStatementOperation((BoundPatternSwitchStatement)boundNode); case BoundKind.PatternSwitchLabel: @@ -1631,12 +1633,12 @@ private IInterpolatedStringText CreateBoundInterpolatedStringTextOperation(Bound private IConstantPattern CreateBoundConstantPatternOperation(BoundConstantPattern boundConstantPattern) { - Lazy value = new Lazy(() => Create(boundConstantPattern.Value)); - SyntaxNode syntax = boundConstantPattern.Syntax; - ITypeSymbol type = null; - Optional constantValue = default(Optional); - bool isImplicit = boundConstantPattern.WasCompilerGenerated; - return new LazyConstantPattern(value, _semanticModel, syntax, type, constantValue, isImplicit); + Lazy value = new Lazy(() => Create(boundConstantPattern.Value)); + SyntaxNode syntax = boundConstantPattern.Syntax; + ITypeSymbol type = null; + Optional constantValue = default(Optional); + bool isImplicit = boundConstantPattern.WasCompilerGenerated; + return new LazyConstantPattern(value, _semanticModel, syntax, type, constantValue, isImplicit); } private IDeclarationPattern CreateBoundDeclarationPatternOperation(BoundDeclarationPattern boundDeclarationPattern) @@ -1649,6 +1651,25 @@ private IDeclarationPattern CreateBoundDeclarationPatternOperation(BoundDeclarat return new DeclarationPattern(variable, _semanticModel, syntax, type, constantValue, isImplicit); } + private IDiscardPattern CreateBoundDiscardPatternOperation(BoundDiscardPattern boundDiscardPattern) + { + SyntaxNode syntax = boundDiscardPattern.Syntax; + ITypeSymbol type = null; + Optional constantValue = default(Optional); + bool isImplicit = boundDiscardPattern.WasCompilerGenerated; + return new DiscardPattern(_semanticModel, syntax, type, constantValue, isImplicit); + } + + private IRecursivePattern CreateBoundRecursivePatternOperation(BoundRecursivePattern boundRecursivePattern) + { + ISymbol variable = boundRecursivePattern.Variable; + SyntaxNode syntax = boundRecursivePattern.Syntax; + ITypeSymbol type = null; + Optional constantValue = default(Optional); + bool isImplicit = boundRecursivePattern.WasCompilerGenerated; + return new RecursivePattern(variable, _semanticModel, syntax, type, constantValue, isImplicit); + } + private ISwitchStatement CreateBoundPatternSwitchStatementOperation(BoundPatternSwitchStatement boundPatternSwitchStatement) { Lazy value = new Lazy(() => Create(boundPatternSwitchStatement.Expression)); @@ -1667,7 +1688,7 @@ private ICaseClause CreateBoundPatternSwitchLabelOperation(BoundPatternSwitchLab Optional constantValue = default(Optional); bool isImplicit = boundPatternSwitchLabel.WasCompilerGenerated; - if (boundPatternSwitchLabel.Pattern.Kind == BoundKind.WildcardPattern) + if (boundPatternSwitchLabel.Pattern.Kind == BoundKind.DiscardPattern) { // Default switch label in pattern switch statement is represented as a default case clause. return new DefaultCaseClause(_semanticModel, syntax, type, constantValue, isImplicit); diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index c894f9fca87c10373c2f74d68184032120ca1102..9625cdc1a41ff19c8b3422dbd118c19ea5dea10e 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -5146,25 +5146,6 @@ private ScanTypeArgumentListKind ScanTypeArgumentList(NameOptions options) // then assume it is unless we see specific tokens following it. switch (this.CurrentToken.Kind) { - case SyntaxKind.IdentifierToken: - // C#7: In certain contexts, we treat *identifier* as a disambiguating token. Those - // contexts are where the sequence of tokens being disambiguated is immediately preceded by one - // of the keywords is, case, or out, or arises while parsing the first element of a tuple literal - // (in which case the tokens are preceded by `(` and the identifier is followed by a `,`) or a - // subsequent element of a tuple literal (in which case the tokens are preceded by `,` and the - // identifier is followed by a `,` or `)`). - // Note that we treat query contextual keywords (which appear here as identifiers) as disambiguating tokens as well. - if ((options & (NameOptions.AfterIs | NameOptions.DefinitePattern | NameOptions.AfterOut)) != 0 || - (options & NameOptions.AfterTupleComma) != 0 && (this.PeekToken(1).Kind == SyntaxKind.CommaToken || this.PeekToken(1).Kind == SyntaxKind.CloseParenToken) || - (options & NameOptions.FirstElementOfPossibleTupleLiteral) != 0 && this.PeekToken(1).Kind == SyntaxKind.CommaToken - ) - { - // we allow 'G x' as a pattern-matching operation and a declaration expression in a tuple. - return ScanTypeArgumentListKind.DefiniteTypeArgumentList; - } - - return ScanTypeArgumentListKind.PossibleTypeArgumentList; - case SyntaxKind.OpenParenToken: case SyntaxKind.CloseParenToken: case SyntaxKind.CloseBracketToken: @@ -5203,6 +5184,27 @@ private ScanTypeArgumentListKind ScanTypeArgumentList(NameOptions options) // `A>C` elsewhere is parsed as `A < (B >> C)` return ScanTypeArgumentListKind.DefiniteTypeArgumentList; + case SyntaxKind.IdentifierToken: + // C#7: In certain contexts, we treat *identifier* as a disambiguating token. Those + // contexts are where the sequence of tokens being disambiguated is immediately preceded by one + // of the keywords is, case, or out, or arises while parsing the first element of a tuple literal + // (in which case the tokens are preceded by `(` and the identifier is followed by a `,`) or a + // subsequent element of a tuple literal (in which case the tokens are preceded by `,` and the + // identifier is followed by a `,` or `)`). + // In C#8 (or whenever recursive patterns are introduced) we also treat an identifier as a + // disambiguating token if we're parsing the type of a pattern. + // Note that we treat query contextual keywords (which appear here as identifiers) as disambiguating tokens as well. + if ((options & (NameOptions.AfterIs | NameOptions.DefinitePattern | NameOptions.AfterOut)) != 0 || + (options & NameOptions.AfterTupleComma) != 0 && (this.PeekToken(1).Kind == SyntaxKind.CommaToken || this.PeekToken(1).Kind == SyntaxKind.CloseParenToken) || + (options & NameOptions.FirstElementOfPossibleTupleLiteral) != 0 && this.PeekToken(1).Kind == SyntaxKind.CommaToken + ) + { + // we allow 'G x' as a pattern-matching operation and a declaration expression in a tuple. + return ScanTypeArgumentListKind.DefiniteTypeArgumentList; + } + + return ScanTypeArgumentListKind.PossibleTypeArgumentList; + case SyntaxKind.EndOfFileToken: // e.g. `e is A` // This is useful for parsing expressions in isolation return ScanTypeArgumentListKind.DefiniteTypeArgumentList; diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser_Patterns.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser_Patterns.cs index 75268343546437b62c79b5dc629ea4e082681ecd..7aa9af164ea4039c7fd9590134316d2d7c70e843 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser_Patterns.cs @@ -18,6 +18,12 @@ private CSharpSyntaxNode ParseTypeOrPatternForIsOperator() var tk = this.CurrentToken.Kind; Precedence precedence = GetPrecedence(SyntaxKind.IsPatternExpression); + // We will parse a shift-expression ONLY (nothing looser) - i.e. not a relational expression + // So x is y < z should be parsed as (x is y) < z + // But x is y << z should be parsed as x is (y << z) + Debug.Assert(Precedence.Shift == precedence + 1); + + // For totally broken syntax, parse a type for error recovery purposes switch (tk) { case SyntaxKind.CloseParenToken: @@ -32,6 +38,17 @@ private CSharpSyntaxNode ParseTypeOrPatternForIsOperator() break; } + if (tk == SyntaxKind.IdentifierToken && this.CurrentToken.Text == "_") + { + // In a pattern, we reserve `_` as a wildcard. It cannot be used (with that spelling) as the + // type of a declaration or recursive pattern, nor as a type in an in-type expression starting + // in C# 7. The binder will give a diagnostic if + // there is a usable symbol in scope by that name. You can always escape it, using `@_`. + // TODO(patterns2): Should we use the "contextual keyword" infrastructure for this? + var node = _syntaxFactory.DiscardPattern(this.EatToken(SyntaxKind.IdentifierToken)); + return this.CheckFeatureAvailability(node, MessageID.IDS_FeatureRecursivePatterns); + } + // If it starts with 'nameof(', skip the 'if' and parse as a constant pattern. if (SyntaxFacts.IsPredefinedType(tk) || (tk == SyntaxKind.IdentifierToken && @@ -42,16 +59,19 @@ private CSharpSyntaxNode ParseTypeOrPatternForIsOperator() { TypeSyntax type = this.ParseType(ParseTypeMode.AfterIs); - if (!type.IsMissing && this.IsTrueIdentifier()) + if (!type.IsMissing) { - var designation = ParseSimpleDesignation(); - return _syntaxFactory.DeclarationPattern(type, designation); + PatternSyntax p = ParsePatternContinued(type, false); + if (p != null) + { + return p; + } } tk = this.CurrentToken.ContextualKind; if ((!IsExpectedBinaryOperator(tk) || GetPrecedence(SyntaxFacts.GetBinaryExpression(tk)) <= precedence) && // member selection is not formally a binary operator but has higher precedence than relational - tk != SyntaxKind.DotToken) + tk != SyntaxKind.DotToken) { // it is a typical "is Type" operator. // Note that we don't bother checking for primary expressions such as X[e], X(e), X++, and X-- @@ -66,11 +86,26 @@ private CSharpSyntaxNode ParseTypeOrPatternForIsOperator() this.Release(ref resetPoint); } } + // check to see if it looks like a recursive pattern. + else if (tk == SyntaxKind.OpenParenToken || tk == SyntaxKind.OpenBraceToken) + { + var resetPoint = this.GetResetPoint(); + try + { + PatternSyntax p = ParsePatternContinued(null, false); + if (p != null) + { + return p; + } - // We parse a shift-expression ONLY (nothing looser) - i.e. not a relational expression - // So x is y < z should be parsed as (x is y) < z - // But x is y << z should be parsed as x is (y << z) - Debug.Assert(Precedence.Shift == precedence + 1); + // this can occur when we encounter a misplaced lambda expression. + this.Reset(ref resetPoint); + } + finally + { + this.Release(ref resetPoint); + } + } // In places where a pattern is supported, we do not support tuple types // due to both syntactic and semantic ambiguities between tuple types and positional patterns. @@ -221,14 +256,56 @@ private bool ScanDesignation(bool permitTuple) } } - // Priority is the ExpressionSyntax. It might return ExpressionSyntax which might be a constant pattern such as 'case 3:' - // All constant expressions are converted to the constant pattern in the switch binder if it is a match statement. - // It is used for parsing patterns in the switch cases. It never returns constant pattern, because for a `case` we - // need to use a pre-pattern-matching syntax node for a constant case. + /// + /// Here is the grammar being parsed: + /// ``` antlr + /// pattern + /// : declaration_pattern + /// | constant_pattern + /// | deconstruction_pattern + /// | property_pattern + /// | discard_pattern + /// ; + /// declaration_pattern + /// : type identifier + /// ; + /// constant_pattern + /// : expression + /// ; + /// deconstruction_pattern + /// : type? '(' subpatterns? ')' property_subpattern? identifier? + /// ; + /// subpatterns + /// : subpattern + /// | subpattern ',' subpatterns + /// ; + /// subpattern + /// : pattern + /// | identifier ':' pattern + /// ; + /// property_subpattern + /// : '{' subpatterns? '}' + /// ; + /// property_pattern + /// : property_subpattern identifier? + /// ; + /// discard_pattern + /// : '_' + /// ; + /// ``` + /// + /// Priority is the ExpressionSyntax. It might return ExpressionSyntax which might be a constant pattern such as 'case 3:' + /// All constant expressions are converted to the constant pattern in the switch binder if it is a match statement. + /// It is used for parsing patterns in the switch cases. It never returns constant pattern, because for a `case` we + /// need to use a pre-pattern-matching syntax node for a constant case. + /// + /// prevents the use of "when" for the identifier + /// private CSharpSyntaxNode ParseExpressionOrPattern(bool forCase) { // handle common error recovery situations during typing - switch (this.CurrentToken.Kind) + var tk = this.CurrentToken.Kind; + switch (tk) { case SyntaxKind.CommaToken: case SyntaxKind.SemicolonToken: @@ -239,11 +316,21 @@ private CSharpSyntaxNode ParseExpressionOrPattern(bool forCase) return this.ParseIdentifierName(ErrorCode.ERR_MissingArgument); } + if (tk == SyntaxKind.IdentifierToken && this.CurrentToken.Text == "_") + { + // In a pattern, we reserve `_` as a wildcard. It cannot be used (with that spelling) as the + // type of a declaration or recursive pattern, nor as a type in an in-type expression starting + // in C# 7. The binder will give a diagnostic if + // there is a usable symbol in scope by that name. You can always escape it, using `@_`. + // TODO(patterns2): Should we use the "contextual keyword" infrastructure for this? + var node = _syntaxFactory.DiscardPattern(this.EatToken(SyntaxKind.IdentifierToken)); + return this.CheckFeatureAvailability(node, MessageID.IDS_FeatureRecursivePatterns); + } + var resetPoint = this.GetResetPoint(); try { TypeSyntax type = null; - var tk = this.CurrentToken.Kind; if ((SyntaxFacts.IsPredefinedType(tk) || tk == SyntaxKind.IdentifierToken) && // If it is a nameof, skip the 'if' and parse as an expression. (this.CurrentToken.ContextualKind != SyntaxKind.NameOfKeyword || this.PeekToken(1).Kind != SyntaxKind.OpenParenToken)) @@ -257,97 +344,108 @@ private CSharpSyntaxNode ParseExpressionOrPattern(bool forCase) } } - PropertySubpatternSyntax propertySubpattern = null; - bool parsePropertySubpattern() + PatternSyntax p = ParsePatternContinued(type, forCase); + if (p != null) { - if (this.CurrentToken.Kind == SyntaxKind.OpenBraceToken) - { - propertySubpattern = ParsePropertySubpattern(); - return true; - } - - return false; + return (forCase && p is ConstantPatternSyntax c) ? c.expression : (CSharpSyntaxNode)p; } - VariableDesignationSyntax designation = null; - bool parseDesignation() - { - if (this.IsTrueIdentifier() && (!forCase || this.CurrentToken.ContextualKind != SyntaxKind.WhenKeyword)) - { - designation = ParseSimpleDesignation(); - return true; - } - - return false; - } + this.Reset(ref resetPoint); + return this.ParseSubExpression(Precedence.Expression); + } + finally + { + this.Release(ref resetPoint); + } + } - bool looksLikeCast() + private PatternSyntax ParsePatternContinued(TypeSyntax type, bool forCase) + { + PropertySubpatternSyntax propertySubpattern = null; + bool parsePropertySubpattern() + { + if (this.CurrentToken.Kind == SyntaxKind.OpenBraceToken) { - var resetPoint2 = this.GetResetPoint(); - try - { - return this.ScanCast(forPattern: true); - } - finally - { - this.Reset(ref resetPoint2); - this.Release(ref resetPoint2); - } + propertySubpattern = ParsePropertySubpattern(); + return true; } - if (this.CurrentToken.Kind == SyntaxKind.OpenParenToken && (type != null || !looksLikeCast())) - { - // It is possible this is a parenthesized (constant) expression. - // We normalize later. - ParseSubpatternList(out var openParenToken, out var subPatterns, out var closeParenToken, SyntaxKind.OpenParenToken, SyntaxKind.CloseParenToken); - if (this.CurrentToken.Kind == SyntaxKind.EqualsGreaterThanToken) - { - // Testers do the darndest things, in this case putting a lambda expression where a pattern is expected. - this.Reset(ref resetPoint); - return this.ParseSubExpression(Precedence.Expression); - } + return false; + } - parsePropertySubpattern(); - parseDesignation(); + VariableDesignationSyntax designation = null; + bool parseDesignation() + { + if (this.IsTrueIdentifier() && (!forCase || this.CurrentToken.ContextualKind != SyntaxKind.WhenKeyword)) + { + designation = ParseSimpleDesignation(); + return true; + } - if (type == null && - propertySubpattern == null && - designation == null && - subPatterns.Count == 1 && - subPatterns[0].NameColon == null && - subPatterns[0].Pattern is ConstantPatternSyntax cp) - { - // There is an ambiguity between a deconstruction pattern `(` pattern `)` - // and a constant expression pattern than happens to be parenthesized. - // We normalize to the parenthesized expression, and semantic analysis - // will notice the parens and treat it whichever way is semantically sensible, - // giving priority to its treatment as a constant expression. - return _syntaxFactory.ParenthesizedExpression(openParenToken, cp.Expression, closeParenToken); - } + return false; + } - var node = _syntaxFactory.DeconstructionPattern(type, openParenToken, subPatterns, closeParenToken, propertySubpattern, designation); - return this.CheckFeatureAvailability(node, MessageID.IDS_FeatureRecursivePatterns); + bool looksLikeCast() + { + var resetPoint2 = this.GetResetPoint(); + try + { + return this.ScanCast(forPattern: true); } - - if (parsePropertySubpattern()) + finally { - parseDesignation(); - var node = _syntaxFactory.PropertyPattern(type, propertySubpattern, designation); - return this.CheckFeatureAvailability(node, MessageID.IDS_FeatureRecursivePatterns); + this.Reset(ref resetPoint2); + this.Release(ref resetPoint2); } + } - if (type != null && parseDesignation()) + if (this.CurrentToken.Kind == SyntaxKind.OpenParenToken && (type != null || !looksLikeCast())) + { + // It is possible this is a parenthesized (constant) expression. + // We normalize later. + ParseSubpatternList(out var openParenToken, out var subPatterns, out var closeParenToken, SyntaxKind.OpenParenToken, SyntaxKind.CloseParenToken); + //if (this.CurrentToken.Kind == SyntaxKind.EqualsGreaterThanToken) + //{ + // // Testers do the darndest things, in this case putting a lambda expression where a pattern is expected. + // return null; + //} + + parsePropertySubpattern(); + parseDesignation(); + + if (type == null && + propertySubpattern == null && + designation == null && + subPatterns.Count == 1 && + subPatterns[0].NameColon == null && + subPatterns[0].Pattern is ConstantPatternSyntax cp) { - return _syntaxFactory.DeclarationPattern(type, designation); + // There is an ambiguity between a deconstruction pattern `(` pattern `)` + // and a constant expression pattern than happens to be parenthesized. + // We normalize to the parenthesized expression, and semantic analysis + // will notice the parens and treat it whichever way is semantically sensible, + // giving priority to its treatment as a constant expression. + return _syntaxFactory.ConstantPattern(_syntaxFactory.ParenthesizedExpression(openParenToken, cp.Expression, closeParenToken)); } - this.Reset(ref resetPoint); - return this.ParseSubExpression(Precedence.Expression); + var node = _syntaxFactory.DeconstructionPattern(type, openParenToken, subPatterns, closeParenToken, propertySubpattern, designation); + return this.CheckFeatureAvailability(node, MessageID.IDS_FeatureRecursivePatterns); } - finally + + if (parsePropertySubpattern()) { - this.Release(ref resetPoint); + parseDesignation(); + var node = _syntaxFactory.PropertyPattern(type, propertySubpattern, designation); + return this.CheckFeatureAvailability(node, MessageID.IDS_FeatureRecursivePatterns); } + + if (type != null && parseDesignation()) + { + return _syntaxFactory.DeclarationPattern(type, designation); + } + + // let the caller fall back to its default (expression or type) + return null; } private PatternSyntax ParsePattern() diff --git a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt index 1f4ab66bbc7a5c434542faa2be8c52501064b993..d8b472b5d5d826f0d25dd14b77c68a26885f184b 100644 --- a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt @@ -17,6 +17,10 @@ Microsoft.CodeAnalysis.CSharp.Syntax.DeconstructionPatternSyntax.WithOpenParenTo Microsoft.CodeAnalysis.CSharp.Syntax.DeconstructionPatternSyntax.WithPropertySubpattern(Microsoft.CodeAnalysis.CSharp.Syntax.PropertySubpatternSyntax propertySubpattern) -> Microsoft.CodeAnalysis.CSharp.Syntax.DeconstructionPatternSyntax Microsoft.CodeAnalysis.CSharp.Syntax.DeconstructionPatternSyntax.WithSubPatterns(Microsoft.CodeAnalysis.SeparatedSyntaxList subPatterns) -> Microsoft.CodeAnalysis.CSharp.Syntax.DeconstructionPatternSyntax Microsoft.CodeAnalysis.CSharp.Syntax.DeconstructionPatternSyntax.WithType(Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax type) -> Microsoft.CodeAnalysis.CSharp.Syntax.DeconstructionPatternSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.DiscardPatternSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.DiscardPatternSyntax.UnderscoreToken.get -> Microsoft.CodeAnalysis.SyntaxToken +Microsoft.CodeAnalysis.CSharp.Syntax.DiscardPatternSyntax.Update(Microsoft.CodeAnalysis.SyntaxToken underscoreToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.DiscardPatternSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.DiscardPatternSyntax.WithUnderscoreToken(Microsoft.CodeAnalysis.SyntaxToken underscoreToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.DiscardPatternSyntax Microsoft.CodeAnalysis.CSharp.Syntax.PropertyPatternSyntax Microsoft.CodeAnalysis.CSharp.Syntax.PropertyPatternSyntax.AddPropertySubpatternSubPatterns(params Microsoft.CodeAnalysis.CSharp.Syntax.SubpatternElementSyntax[] items) -> Microsoft.CodeAnalysis.CSharp.Syntax.PropertyPatternSyntax Microsoft.CodeAnalysis.CSharp.Syntax.PropertyPatternSyntax.Designation.get -> Microsoft.CodeAnalysis.CSharp.Syntax.VariableDesignationSyntax @@ -45,15 +49,19 @@ Microsoft.CodeAnalysis.CSharp.Syntax.SubpatternElementSyntax.Update(Microsoft.Co Microsoft.CodeAnalysis.CSharp.Syntax.SubpatternElementSyntax.WithNameColon(Microsoft.CodeAnalysis.CSharp.Syntax.NameColonSyntax nameColon) -> Microsoft.CodeAnalysis.CSharp.Syntax.SubpatternElementSyntax Microsoft.CodeAnalysis.CSharp.Syntax.SubpatternElementSyntax.WithPattern(Microsoft.CodeAnalysis.CSharp.Syntax.PatternSyntax pattern) -> Microsoft.CodeAnalysis.CSharp.Syntax.SubpatternElementSyntax Microsoft.CodeAnalysis.CSharp.SyntaxKind.DeconstructionPattern = 9023 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind +Microsoft.CodeAnalysis.CSharp.SyntaxKind.DiscardPattern = 9024 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind Microsoft.CodeAnalysis.CSharp.SyntaxKind.PropertyPattern = 9020 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind Microsoft.CodeAnalysis.CSharp.SyntaxKind.PropertySubpattern = 9021 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind Microsoft.CodeAnalysis.CSharp.SyntaxKind.SubpatternElement = 9022 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitDeconstructionPattern(Microsoft.CodeAnalysis.CSharp.Syntax.DeconstructionPatternSyntax node) -> Microsoft.CodeAnalysis.SyntaxNode +override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitDiscardPattern(Microsoft.CodeAnalysis.CSharp.Syntax.DiscardPatternSyntax node) -> Microsoft.CodeAnalysis.SyntaxNode override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitPropertyPattern(Microsoft.CodeAnalysis.CSharp.Syntax.PropertyPatternSyntax node) -> Microsoft.CodeAnalysis.SyntaxNode override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitPropertySubpattern(Microsoft.CodeAnalysis.CSharp.Syntax.PropertySubpatternSyntax node) -> Microsoft.CodeAnalysis.SyntaxNode override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitSubpatternElement(Microsoft.CodeAnalysis.CSharp.Syntax.SubpatternElementSyntax node) -> Microsoft.CodeAnalysis.SyntaxNode override Microsoft.CodeAnalysis.CSharp.Syntax.DeconstructionPatternSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> void override Microsoft.CodeAnalysis.CSharp.Syntax.DeconstructionPatternSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> TResult +override Microsoft.CodeAnalysis.CSharp.Syntax.DiscardPatternSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> void +override Microsoft.CodeAnalysis.CSharp.Syntax.DiscardPatternSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> TResult override Microsoft.CodeAnalysis.CSharp.Syntax.PropertyPatternSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> void override Microsoft.CodeAnalysis.CSharp.Syntax.PropertyPatternSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> TResult override Microsoft.CodeAnalysis.CSharp.Syntax.PropertySubpatternSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> void @@ -64,6 +72,7 @@ static Microsoft.CodeAnalysis.CSharp.CSharpExtensions.GetConversion(this Microso static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.DeconstructionPattern(Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax type, Microsoft.CodeAnalysis.SeparatedSyntaxList subPatterns, Microsoft.CodeAnalysis.CSharp.Syntax.PropertySubpatternSyntax propertySubpattern, Microsoft.CodeAnalysis.CSharp.Syntax.VariableDesignationSyntax designation) -> Microsoft.CodeAnalysis.CSharp.Syntax.DeconstructionPatternSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.DeconstructionPattern(Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax type, Microsoft.CodeAnalysis.SyntaxToken openParenToken, Microsoft.CodeAnalysis.SeparatedSyntaxList subPatterns, Microsoft.CodeAnalysis.SyntaxToken closeParenToken, Microsoft.CodeAnalysis.CSharp.Syntax.PropertySubpatternSyntax propertySubpattern, Microsoft.CodeAnalysis.CSharp.Syntax.VariableDesignationSyntax designation) -> Microsoft.CodeAnalysis.CSharp.Syntax.DeconstructionPatternSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.DeconstructionPattern(Microsoft.CodeAnalysis.SeparatedSyntaxList subPatterns = default(Microsoft.CodeAnalysis.SeparatedSyntaxList)) -> Microsoft.CodeAnalysis.CSharp.Syntax.DeconstructionPatternSyntax +static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.DiscardPattern(Microsoft.CodeAnalysis.SyntaxToken underscoreToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.DiscardPatternSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.PropertyPattern() -> Microsoft.CodeAnalysis.CSharp.Syntax.PropertyPatternSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.PropertyPattern(Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax type, Microsoft.CodeAnalysis.CSharp.Syntax.PropertySubpatternSyntax propertySubpattern, Microsoft.CodeAnalysis.CSharp.Syntax.VariableDesignationSyntax designation) -> Microsoft.CodeAnalysis.CSharp.Syntax.PropertyPatternSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.PropertySubpattern(Microsoft.CodeAnalysis.SeparatedSyntaxList subPatterns = default(Microsoft.CodeAnalysis.SeparatedSyntaxList)) -> Microsoft.CodeAnalysis.CSharp.Syntax.PropertySubpatternSyntax @@ -72,10 +81,12 @@ static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.RefType(Microsoft.CodeAnalysi static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.SubpatternElement(Microsoft.CodeAnalysis.CSharp.Syntax.NameColonSyntax nameColon, Microsoft.CodeAnalysis.CSharp.Syntax.PatternSyntax pattern) -> Microsoft.CodeAnalysis.CSharp.Syntax.SubpatternElementSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.SubpatternElement(Microsoft.CodeAnalysis.CSharp.Syntax.PatternSyntax pattern) -> Microsoft.CodeAnalysis.CSharp.Syntax.SubpatternElementSyntax virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitDeconstructionPattern(Microsoft.CodeAnalysis.CSharp.Syntax.DeconstructionPatternSyntax node) -> void +virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitDiscardPattern(Microsoft.CodeAnalysis.CSharp.Syntax.DiscardPatternSyntax node) -> void virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitPropertyPattern(Microsoft.CodeAnalysis.CSharp.Syntax.PropertyPatternSyntax node) -> void virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitPropertySubpattern(Microsoft.CodeAnalysis.CSharp.Syntax.PropertySubpatternSyntax node) -> void virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitSubpatternElement(Microsoft.CodeAnalysis.CSharp.Syntax.SubpatternElementSyntax node) -> void virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitDeconstructionPattern(Microsoft.CodeAnalysis.CSharp.Syntax.DeconstructionPatternSyntax node) -> TResult +virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitDiscardPattern(Microsoft.CodeAnalysis.CSharp.Syntax.DiscardPatternSyntax node) -> TResult virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitPropertyPattern(Microsoft.CodeAnalysis.CSharp.Syntax.PropertyPatternSyntax node) -> TResult virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitPropertySubpattern(Microsoft.CodeAnalysis.CSharp.Syntax.PropertySubpatternSyntax node) -> TResult virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitSubpatternElement(Microsoft.CodeAnalysis.CSharp.Syntax.SubpatternElementSyntax node) -> TResult \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/GlobalExpressionVariable.cs b/src/Compilers/CSharp/Portable/Symbols/Source/GlobalExpressionVariable.cs index 526396520b9c9b7c8539b117830783f22bb948a2..d8d40813bcb554c4f6d4b5f59518228a26fe6de7 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/GlobalExpressionVariable.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/GlobalExpressionVariable.cs @@ -27,7 +27,7 @@ internal class GlobalExpressionVariable : SourceMemberFieldSymbol : base(containingType, modifiers, name, syntax, location) { Debug.Assert(DeclaredAccessibility == Accessibility.Private); - _typeSyntax = typeSyntax.GetReference(); + _typeSyntax = typeSyntax?.GetReference(); } internal static GlobalExpressionVariable Create( @@ -43,14 +43,14 @@ internal class GlobalExpressionVariable : SourceMemberFieldSymbol Debug.Assert(nodeToBind.Kind() == SyntaxKind.VariableDeclarator || nodeToBind is ExpressionSyntax); var syntaxReference = syntax.GetReference(); - return typeSyntax.IsVar + return (typeSyntax == null || typeSyntax.IsVar) ? new InferrableGlobalExpressionVariable(containingType, modifiers, typeSyntax, name, syntaxReference, location, containingFieldOpt, nodeToBind) : new GlobalExpressionVariable(containingType, modifiers, typeSyntax, name, syntaxReference, location); } protected override SyntaxList AttributeDeclarationSyntaxList => default(SyntaxList); - protected override TypeSyntax TypeSyntax => (TypeSyntax)_typeSyntax.GetSyntax(); + protected override TypeSyntax TypeSyntax => (TypeSyntax)_typeSyntax?.GetSyntax(); protected override SyntaxTokenList ModifiersTokenList => default(SyntaxTokenList); public override bool HasInitializer => false; protected override ConstantValue MakeConstantValue( @@ -73,12 +73,21 @@ internal override TypeSymbol GetFieldType(ConsList fieldsBeingBound var diagnostics = DiagnosticBag.GetInstance(); TypeSymbol type; + bool isVar; var binderFactory = compilation.GetBinderFactory(SyntaxTree); - var binder = binderFactory.GetBinder(typeSyntax); + var binder = binderFactory.GetBinder(typeSyntax ?? SyntaxNode); - bool isVar; - type = binder.BindType(typeSyntax, diagnostics, out isVar); + if (typeSyntax != null) + { + type = binder.BindType(typeSyntax, diagnostics, out isVar); + } + else + { + // Recursive patterns may omit the type syntax + isVar = true; + type = null; + } Debug.Assert((object)type != null || isVar); @@ -124,6 +133,7 @@ private TypeSymbol SetType(CSharpCompilation compilation, DiagnosticBag diagnost compilation.DeclarationDiagnostics.AddRange(diagnostics); state.NotePartComplete(CompletionPart.Type); } + return _lazyType; } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceLocalSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceLocalSymbol.cs index 77dac53792fdee345582ea34ae99e8eca7fc65d6..add70caa5deb60aea6e4ef5247dd4762899b4cfb 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceLocalSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceLocalSymbol.cs @@ -56,7 +56,7 @@ internal class SourceLocalSymbol : LocalSymbol this._scopeBinder = scopeBinder; this._containingSymbol = containingSymbol; this._identifierToken = identifierToken; - this._typeSyntax = allowRefKind ? typeSyntax.SkipRef(out this._refKind) : typeSyntax; + this._typeSyntax = allowRefKind ? typeSyntax?.SkipRef(out this._refKind) : typeSyntax; this._declarationKind = declarationKind; // create this eagerly as it will always be needed for the EnsureSingleDefinition @@ -170,7 +170,7 @@ internal new string GetDebuggerDisplay() new[] { SyntaxKind.LocalDeclarationStatement, SyntaxKind.ForStatement, SyntaxKind.UsingStatement, SyntaxKind.FixedStatement }. Contains(nodeToBind.Ancestors().OfType().First().Kind()) || nodeToBind is ExpressionSyntax); - return typeSyntax.IsVar && kind != LocalDeclarationKind.DeclarationExpressionVariable + return typeSyntax?.IsVar != false && kind != LocalDeclarationKind.DeclarationExpressionVariable ? new LocalSymbolWithEnclosingContext(containingSymbol, scopeBinder, nodeBinder, typeSyntax, identifierToken, kind, nodeToBind, forbiddenZone) : new SourceLocalSymbol(containingSymbol, scopeBinder, false, typeSyntax, identifierToken, kind); } @@ -303,14 +303,13 @@ public bool IsVar { if (_typeSyntax == null) { - // in "let x = 1;" there is no syntax corresponding to the type. + // in "e is {} x" there is no syntax corresponding to the type. return true; } if (_typeSyntax.IsVar) { - bool isVar; - TypeSymbol declType = this.TypeSyntaxBinder.BindType(_typeSyntax, new DiagnosticBag(), out isVar); + TypeSymbol declType = this.TypeSyntaxBinder.BindType(_typeSyntax, new DiagnosticBag(), out var isVar); return isVar; } @@ -325,8 +324,16 @@ private TypeSymbol GetTypeSymbol() Binder typeBinder = this.TypeSyntaxBinder; bool isVar; - RefKind refKind; - TypeSymbol declType = typeBinder.BindType(_typeSyntax.SkipRef(out refKind), diagnostics, out isVar); + TypeSymbol declType; + if (_typeSyntax == null) // In recursive patterns the type may be omitted. + { + isVar = true; + declType = null; + } + else + { + declType = typeBinder.BindType(_typeSyntax.SkipRef(out var refKind), diagnostics, out isVar); + } if (isVar) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs index d283ea77968e4c1c3f66d2598641b06c26e83643..ffe56c0e148e08b060f00d2703dbddeb843e1769 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs @@ -49,15 +49,15 @@ protected void TypeChecks(TypeSymbol type, DiagnosticBag diagnostics) } else if (type.SpecialType == SpecialType.System_Void) { - diagnostics.Add(ErrorCode.ERR_FieldCantHaveVoidType, TypeSyntax.Location); + diagnostics.Add(ErrorCode.ERR_FieldCantHaveVoidType, TypeSyntax?.Location ?? this.Locations[0]); } else if (type.IsRestrictedType(ignoreSpanLikeTypes: true)) { - diagnostics.Add(ErrorCode.ERR_FieldCantBeRefAny, TypeSyntax.Location, type); + diagnostics.Add(ErrorCode.ERR_FieldCantBeRefAny, TypeSyntax?.Location ?? this.Locations[0], type); } else if (type.IsByRefLikeType && (this.IsStatic || !containingType.IsByRefLikeType)) { - diagnostics.Add(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, TypeSyntax.Location, type); + diagnostics.Add(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, TypeSyntax?.Location ?? this.Locations[0], type); } else if (IsConst && !type.CanBeConst()) { diff --git a/src/Compilers/CSharp/Portable/Syntax/Syntax.xml b/src/Compilers/CSharp/Portable/Syntax/Syntax.xml index c88494eae39836178df23f3d92a14e242287a2d0..9e3d26544fc84161e08c6d28dabf5a5f90df99f3 100644 --- a/src/Compilers/CSharp/Portable/Syntax/Syntax.xml +++ b/src/Compilers/CSharp/Portable/Syntax/Syntax.xml @@ -1718,6 +1718,12 @@ + + + + + + @@ -2169,7 +2175,7 @@ The variable(s) of the loop. In correct code this is a tuple literal, declaration expression with a tuple designator, or - a wildcard syntax in the form of a simple identifier. In broken + a discard syntax in the form of a simple identifier. In broken code it could be something else. diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs index 67091cb6b5ab7520263f280735fc2209abf9b138..8a0705aa101e06289fea840470124ff087baed1e 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs @@ -560,6 +560,7 @@ public enum SyntaxKind : ushort PropertySubpattern = 9021, SubpatternElement = 9022, DeconstructionPattern = 9023, + DiscardPattern = 9024, // Kinds between 9000 and 9039 are "reserved" for pattern matching. // Please start with 9040 if you add more kinds below. diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenDeconstructTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenDeconstructTests.cs index f4b87265c05256b06001503164e1d2061c3f16ec..81ec4d799e33649cb4424d046d3bfba8bba92da4 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenDeconstructTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenDeconstructTests.cs @@ -5906,6 +5906,12 @@ static void M(out int x) // (8,18): error CS8059: Feature 'pattern matching' is not available in C# 6. Please use language version 7.0 or greater. // bool b = 3 is int _; Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "3 is int _").WithArguments("pattern matching", "7.0").WithLocation(8, 18), + // (11,13): error CS8059: Feature 'pattern matching' is not available in C# 6. Please use language version 7.0 or greater. + // case _: // not a discard + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "case _:").WithArguments("pattern matching", "7.0").WithLocation(11, 13), + // (11,18): error CS8058: Feature 'recursive patterns' is experimental and unsupported; use '/features:patterns2' to enable. + // case _: // not a discard + Diagnostic(ErrorCode.ERR_FeatureIsExperimental, "_").WithArguments("recursive patterns", "patterns2").WithLocation(11, 18), // (16,13): error CS8059: Feature 'pattern matching' is not available in C# 6. Please use language version 7.0 or greater. // case int _: Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "case int _:").WithArguments("pattern matching", "7.0").WithLocation(16, 13), @@ -5918,15 +5924,9 @@ static void M(out int x) // (6,10): error CS8059: Feature 'tuples' is not available in C# 6. Please use language version 7.0 or greater. // (_, var _, int _) = (1, 2, 3); Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "_").WithArguments("tuples", "7.0").WithLocation(6, 10), - // (11,18): error CS0103: The name '_' does not exist in the current context - // case _: // not a discard - Diagnostic(ErrorCode.ERR_NameNotInContext, "_").WithArguments("_").WithLocation(11, 18), // (21,15): error CS8059: Feature 'tuples' is not available in C# 6. Please use language version 7.0 or greater. // M(out _); - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "_").WithArguments("tuples", "7.0").WithLocation(21, 15), - // (12,17): warning CS0162: Unreachable code detected - // break; - Diagnostic(ErrorCode.WRN_UnreachableCode, "break").WithLocation(12, 17) + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "_").WithArguments("tuples", "7.0").WithLocation(21, 15) ); } diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenOperators.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenOperators.cs index d7f042839d2b4ba737c751eae2fc0848a2b8c8a3..ffd328b4693be566fd48e1eb058fb76e24e35f7c 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenOperators.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenOperators.cs @@ -11,9 +11,8 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen { - public class CodeGenOperatorTests : CSharpTestBase + public class CodeGenOperators : CSharpTestBase { - [Fact] public void TestIsNullPattern() { @@ -258,40 +257,34 @@ public static void Main() // Release var compilation = CompileAndVerify(source, expectedOutput: "True Branch taken", options: TestOptions.ReleaseExe); compilation.VerifyIL("C.Main", @"{ - // Code size 20 (0x14) - .maxstack 2 - IL_0000: ldnull - IL_0001: ldnull - IL_0002: ceq - IL_0004: call ""void System.Console.Write(bool)"" - IL_0009: ldstr "" Branch taken"" - IL_000e: call ""void System.Console.Write(string)"" - IL_0013: ret + // Code size 17 (0x11) + .maxstack 1 + IL_0000: ldc.i4.1 + IL_0001: call ""void System.Console.Write(bool)"" + IL_0006: ldstr "" Branch taken"" + IL_000b: call ""void System.Console.Write(string)"" + IL_0010: ret }"); // Debug compilation = CompileAndVerify(source, expectedOutput: "True Branch taken", options: TestOptions.DebugExe); compilation.VerifyIL("C.Main", @"{ - // Code size 33 (0x21) - .maxstack 2 - .locals init (bool V_0) - IL_0000: nop - IL_0001: ldnull - IL_0002: ldnull - IL_0003: ceq - IL_0005: call ""void System.Console.Write(bool)"" - IL_000a: nop - IL_000b: ldnull - IL_000c: ldnull - IL_000d: ceq - IL_000f: stloc.0 - IL_0010: ldloc.0 - IL_0011: brfalse.s IL_0020 - IL_0013: nop - IL_0014: ldstr "" Branch taken"" - IL_0019: call ""void System.Console.Write(string)"" - IL_001e: nop - IL_001f: nop - IL_0020: ret + // Code size 27 (0x1b) + .maxstack 1 + .locals init (bool V_0) + IL_0000: nop + IL_0001: ldc.i4.1 + IL_0002: call ""void System.Console.Write(bool)"" + IL_0007: nop + IL_0008: ldc.i4.1 + IL_0009: stloc.0 + IL_000a: ldloc.0 + IL_000b: brfalse.s IL_001a + IL_000d: nop + IL_000e: ldstr "" Branch taken"" + IL_0013: call ""void System.Console.Write(string)"" + IL_0018: nop + IL_0019: nop + IL_001a: ret }"); } @@ -316,40 +309,34 @@ public static void Main() // Release var compilation = CompileAndVerify(source, expectedOutput: "True Branch taken", options: TestOptions.ReleaseExe); compilation.VerifyIL("C.Main", @"{ - // Code size 20 (0x14) - .maxstack 2 - IL_0000: ldnull - IL_0001: ldnull - IL_0002: ceq - IL_0004: call ""void System.Console.Write(bool)"" - IL_0009: ldstr "" Branch taken"" - IL_000e: call ""void System.Console.Write(string)"" - IL_0013: ret + // Code size 17 (0x11) + .maxstack 1 + IL_0000: ldc.i4.1 + IL_0001: call ""void System.Console.Write(bool)"" + IL_0006: ldstr "" Branch taken"" + IL_000b: call ""void System.Console.Write(string)"" + IL_0010: ret }"); // Debug compilation = CompileAndVerify(source, expectedOutput: "True Branch taken", options: TestOptions.DebugExe); compilation.VerifyIL("C.Main", @"{ - // Code size 33 (0x21) - .maxstack 2 - .locals init (bool V_0) - IL_0000: nop - IL_0001: ldnull - IL_0002: ldnull - IL_0003: ceq - IL_0005: call ""void System.Console.Write(bool)"" - IL_000a: nop - IL_000b: ldnull - IL_000c: ldnull - IL_000d: ceq - IL_000f: stloc.0 - IL_0010: ldloc.0 - IL_0011: brfalse.s IL_0020 - IL_0013: nop - IL_0014: ldstr "" Branch taken"" - IL_0019: call ""void System.Console.Write(string)"" - IL_001e: nop - IL_001f: nop - IL_0020: ret + // Code size 27 (0x1b) + .maxstack 1 + .locals init (bool V_0) + IL_0000: nop + IL_0001: ldc.i4.1 + IL_0002: call ""void System.Console.Write(bool)"" + IL_0007: nop + IL_0008: ldc.i4.1 + IL_0009: stloc.0 + IL_000a: ldloc.0 + IL_000b: brfalse.s IL_001a + IL_000d: nop + IL_000e: ldstr "" Branch taken"" + IL_0013: call ""void System.Console.Write(string)"" + IL_0018: nop + IL_0019: nop + IL_001a: ret }"); } diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs index 0512f9bdfded89fc0afee0da18239115a48c07a3..d4a83b80e6cc1a7a5678e3abc15561a1b0745627 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs @@ -14866,27 +14866,24 @@ void Match(object o) var comp = CreateStandardCompilation(source); comp.VerifyDiagnostics( + // (6,18): error CS8058: Feature 'recursive patterns' is experimental and unsupported; use '/features:patterns2' to enable. + // if (o is (int, int) t) { } + Diagnostic(ErrorCode.ERR_FeatureIsExperimental, "(int, int) t").WithArguments("recursive patterns", "patterns2").WithLocation(6, 18), // (6,19): error CS1525: Invalid expression term 'int' // if (o is (int, int) t) { } Diagnostic(ErrorCode.ERR_InvalidExprTerm, "int").WithArguments("int").WithLocation(6, 19), // (6,24): error CS1525: Invalid expression term 'int' // if (o is (int, int) t) { } Diagnostic(ErrorCode.ERR_InvalidExprTerm, "int").WithArguments("int").WithLocation(6, 24), - // (6,29): error CS1026: ) expected - // if (o is (int, int) t) { } - Diagnostic(ErrorCode.ERR_CloseParenExpected, "t").WithLocation(6, 29), - // (6,30): error CS1002: ; expected - // if (o is (int, int) t) { } - Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(6, 30), - // (6,30): error CS1513: } expected + // (6,18): error CS8129: No suitable Deconstruct instance or extension method was found for type 'object', with 2 out parameters and a void return type. // if (o is (int, int) t) { } - Diagnostic(ErrorCode.ERR_RbraceExpected, ")").WithLocation(6, 30), - // (6,18): error CS0150: A constant value is expected + Diagnostic(ErrorCode.ERR_MissingDeconstruct, "(int, int) t").WithArguments("object", "2").WithLocation(6, 18), + // (6,19): error CS0150: A constant value is expected // if (o is (int, int) t) { } - Diagnostic(ErrorCode.ERR_ConstantExpected, "(int, int)").WithLocation(6, 18), - // (6,29): error CS0103: The name 't' does not exist in the current context + Diagnostic(ErrorCode.ERR_ConstantExpected, "int").WithLocation(6, 19), + // (6,24): error CS0150: A constant value is expected // if (o is (int, int) t) { } - Diagnostic(ErrorCode.ERR_NameNotInContext, "t").WithArguments("t").WithLocation(6, 29) + Diagnostic(ErrorCode.ERR_ConstantExpected, "int").WithLocation(6, 24) ); } @@ -14927,21 +14924,12 @@ void Match(object o) var comp = CreateStandardCompilation(source); comp.VerifyDiagnostics( - // (6,19): error CS8185: A declaration is not allowed in this context. - // if (o is (int a, int b)) { } - Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int a").WithLocation(6, 19), - // (6,26): error CS8185: A declaration is not allowed in this context. - // if (o is (int a, int b)) { } - Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int b").WithLocation(6, 26), - // (6,18): error CS0150: A constant value is expected - // if (o is (int a, int b)) { } - Diagnostic(ErrorCode.ERR_ConstantExpected, "(int a, int b)").WithLocation(6, 18), - // (6,19): error CS0165: Use of unassigned local variable 'a' + // (6,18): error CS8058: Feature 'recursive patterns' is experimental and unsupported; use '/features:patterns2' to enable. // if (o is (int a, int b)) { } - Diagnostic(ErrorCode.ERR_UseDefViolation, "int a").WithArguments("a").WithLocation(6, 19), - // (6,26): error CS0165: Use of unassigned local variable 'b' + Diagnostic(ErrorCode.ERR_FeatureIsExperimental, "(int a, int b)").WithArguments("recursive patterns", "patterns2").WithLocation(6, 18), + // (6,18): error CS8129: No suitable Deconstruct instance or extension method was found for type 'object', with 2 out parameters and a void return type. // if (o is (int a, int b)) { } - Diagnostic(ErrorCode.ERR_UseDefViolation, "int b").WithArguments("b").WithLocation(6, 26) + Diagnostic(ErrorCode.ERR_MissingDeconstruct, "(int a, int b)").WithArguments("object", "2").WithLocation(6, 18) ); } @@ -14960,9 +14948,12 @@ void Match(object o) var comp = CreateStandardCompilation(source); comp.VerifyDiagnostics( - // (6,18): error CS0150: A constant value is expected - // if (o is (1, 2)) - Diagnostic(ErrorCode.ERR_ConstantExpected, "(1, 2)").WithLocation(6, 18) + // (6,18): error CS8058: Feature 'recursive patterns' is experimental and unsupported; use '/features:patterns2' to enable. + // if (o is (1, 2)) { } + Diagnostic(ErrorCode.ERR_FeatureIsExperimental, "(1, 2)").WithArguments("recursive patterns", "patterns2").WithLocation(6, 18), + // (6,18): error CS8129: No suitable Deconstruct instance or extension method was found for type 'object', with 2 out parameters and a void return type. + // if (o is (1, 2)) { } + Diagnostic(ErrorCode.ERR_MissingDeconstruct, "(1, 2)").WithArguments("object", "2").WithLocation(6, 18) ); } @@ -14992,12 +14983,15 @@ void Match(object o) // (7,24): error CS1525: Invalid expression term 'int' // case (int, int) tuple: return; Diagnostic(ErrorCode.ERR_InvalidExprTerm, "int").WithArguments("int").WithLocation(7, 24), - // (7,18): error CS0570: 'recursive pattern' is not supported by the language + // (7,18): error CS8129: No suitable Deconstruct instance or extension method was found for type 'object', with 2 out parameters and a void return type. // case (int, int) tuple: return; - Diagnostic(ErrorCode.ERR_BindToBogus, "(int, int) tuple").WithArguments("recursive pattern").WithLocation(7, 18), - // (7,36): warning CS0162: Unreachable code detected + Diagnostic(ErrorCode.ERR_MissingDeconstruct, "(int, int) tuple").WithArguments("object", "2").WithLocation(7, 18), + // (7,19): error CS0150: A constant value is expected // case (int, int) tuple: return; - Diagnostic(ErrorCode.WRN_UnreachableCode, "return").WithLocation(7, 36) + Diagnostic(ErrorCode.ERR_ConstantExpected, "int").WithLocation(7, 19), + // (7,24): error CS0150: A constant value is expected + // case (int, int) tuple: return; + Diagnostic(ErrorCode.ERR_ConstantExpected, "int").WithLocation(7, 24) ); } @@ -15021,12 +15015,9 @@ void Match(object o) // (7,18): error CS8058: Feature 'recursive patterns' is experimental and unsupported; use '/features:patterns2' to enable. // case (1, 1): return; Diagnostic(ErrorCode.ERR_FeatureIsExperimental, "(1, 1)").WithArguments("recursive patterns", "patterns2").WithLocation(7, 18), - // (7,18): error CS0570: 'recursive pattern' is not supported by the language - // case (1, 1): return; - Diagnostic(ErrorCode.ERR_BindToBogus, "(1, 1)").WithArguments("recursive pattern").WithLocation(7, 18), - // (7,26): warning CS0162: Unreachable code detected + // (7,18): error CS8129: No suitable Deconstruct instance or extension method was found for type 'object', with 2 out parameters and a void return type. // case (1, 1): return; - Diagnostic(ErrorCode.WRN_UnreachableCode, "return").WithLocation(7, 26) + Diagnostic(ErrorCode.ERR_MissingDeconstruct, "(1, 1)").WithArguments("object", "2").WithLocation(7, 18) ); } @@ -15050,12 +15041,9 @@ void Match(object o) // (7,18): error CS8058: Feature 'recursive patterns' is experimental and unsupported; use '/features:patterns2' to enable. // case (1, 1) t: return; Diagnostic(ErrorCode.ERR_FeatureIsExperimental, "(1, 1) t").WithArguments("recursive patterns", "patterns2").WithLocation(7, 18), - // (7,18): error CS0570: 'recursive pattern' is not supported by the language - // case (1, 1) t: return; - Diagnostic(ErrorCode.ERR_BindToBogus, "(1, 1) t").WithArguments("recursive pattern").WithLocation(7, 18), - // (7,28): warning CS0162: Unreachable code detected + // (7,18): error CS8129: No suitable Deconstruct instance or extension method was found for type 'object', with 2 out parameters and a void return type. // case (1, 1) t: return; - Diagnostic(ErrorCode.WRN_UnreachableCode, "return").WithLocation(7, 28) + Diagnostic(ErrorCode.ERR_MissingDeconstruct, "(1, 1) t").WithArguments("object", "2").WithLocation(7, 18) ); } @@ -23443,6 +23431,9 @@ static void M() }"; var comp = CreateStandardCompilation( source, references: new[] { ValueTupleRef, SystemRuntimeFacadeRef }); comp.VerifyDiagnostics( + // (6,28): error CS8058: Feature 'recursive patterns' is experimental and unsupported; use '/features:patterns2' to enable. + // var x1 = (1, 1) is (int, int a)?; + Diagnostic(ErrorCode.ERR_FeatureIsExperimental, "(int, int a)").WithArguments("recursive patterns", "patterns2").WithLocation(6, 28), // (6,29): error CS1525: Invalid expression term 'int' // var x1 = (1, 1) is (int, int a)?; Diagnostic(ErrorCode.ERR_InvalidExprTerm, "int").WithArguments("int").WithLocation(6, 29), @@ -23455,15 +23446,9 @@ static void M() // (6,41): error CS1525: Invalid expression term ';' // var x1 = (1, 1) is (int, int a)?; Diagnostic(ErrorCode.ERR_InvalidExprTerm, ";").WithArguments(";").WithLocation(6, 41), - // (6,34): error CS8185: A declaration is not allowed in this context. - // var x1 = (1, 1) is (int, int a)?; - Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int a").WithLocation(6, 34), - // (6,28): error CS0150: A constant value is expected - // var x1 = (1, 1) is (int, int a)?; - Diagnostic(ErrorCode.ERR_ConstantExpected, "(int, int a)").WithLocation(6, 28), - // (6,34): error CS0165: Use of unassigned local variable 'a' + // (6,29): error CS0150: A constant value is expected // var x1 = (1, 1) is (int, int a)?; - Diagnostic(ErrorCode.ERR_UseDefViolation, "int a").WithArguments("a").WithLocation(6, 34) + Diagnostic(ErrorCode.ERR_ConstantExpected, "int").WithLocation(6, 29) ); } diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/PatternTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/PatternTests.cs index 498f79f2d6be5c118a66c7d8d37bccd74fd0ef61..48bdad99495da416aa126fd076e465b5c1e4f10f 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/PatternTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/PatternTests.cs @@ -97,9 +97,12 @@ static void M1(int? x) { case int i: break; }").WithArguments("System.Nullable`1", "GetValueOrDefault").WithLocation(12, 9), - // (17,36): error CS0656: Missing compiler required member 'System.Nullable`1.GetValueOrDefault' + // (17,36): error CS0656: Missing compiler required member 'System.Nullable`1.get_HasValue' + // static bool M2(int? x) => x is int i; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "int i").WithArguments("System.Nullable`1", "get_HasValue").WithLocation(17, 36), + // (17,36): error CS0656: Missing compiler required member 'System.Nullable`1.get_Value' // static bool M2(int? x) => x is int i; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "int i").WithArguments("System.Nullable`1", "GetValueOrDefault").WithLocation(17, 36) + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "int i").WithArguments("System.Nullable`1", "get_Value").WithLocation(17, 36) ); } @@ -138,11 +141,14 @@ static void M1(int? x) }").WithArguments("System.Nullable`1", "get_HasValue").WithLocation(12, 9), // (17,36): error CS0656: Missing compiler required member 'System.Nullable`1.get_HasValue' // static bool M2(int? x) => x is int i; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "int i").WithArguments("System.Nullable`1", "get_HasValue").WithLocation(17, 36) + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "int i").WithArguments("System.Nullable`1", "get_HasValue").WithLocation(17, 36), + // (17,36): error CS0656: Missing compiler required member 'System.Nullable`1.get_Value' + // static bool M2(int? x) => x is int i; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "int i").WithArguments("System.Nullable`1", "get_Value").WithLocation(17, 36) ); } - [Fact, WorkItem(17266, "https://github.com/dotnet/roslyn/issues/17266")] + [Fact(Skip = "PROTOTYPE(patterns2): code quality"), WorkItem(17266, "https://github.com/dotnet/roslyn/issues/17266")] public void DoubleEvaluation01() { var source = @@ -194,7 +200,7 @@ .maxstack 1 }"); } - [Fact, WorkItem(19122, "https://github.com/dotnet/roslyn/issues/19122")] + [Fact(Skip = "PROTOTYPE(patterns2): code quality"), WorkItem(19122, "https://github.com/dotnet/roslyn/issues/19122")] public void PatternCrash_01() { var source = @"using System; diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/SwitchTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/SwitchTests.cs index 8c8b5d307adaa8e00de673ba892a07af76a4bf77..5a240b6900351a4d51e3988f1c81725351d11817 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/SwitchTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/SwitchTests.cs @@ -8370,7 +8370,7 @@ .maxstack 2 ); } - [Fact, WorkItem(18859, "https://github.com/dotnet/roslyn/issues/18859")] + [Fact(Skip = "PROTOTYPE(patterns2): code quality"), WorkItem(18859, "https://github.com/dotnet/roslyn/issues/18859")] public void BoxInPatternIf_02() { var source = @"using System; @@ -8428,7 +8428,7 @@ .maxstack 1 ); } - [Fact, WorkItem(16195, "https://github.com/dotnet/roslyn/issues/16195")] + [Fact(Skip = "PROTOTYPE(patterns2): code quality"), WorkItem(16195, "https://github.com/dotnet/roslyn/issues/16195")] public void TestMatchWithTypeParameter_01() { var source = @@ -8631,7 +8631,7 @@ .maxstack 2 ); } - [Fact, WorkItem(16195, "https://github.com/dotnet/roslyn/issues/16195")] + [Fact(Skip = "PROTOTYPE(patterns2): code quality"), WorkItem(16195, "https://github.com/dotnet/roslyn/issues/16195")] public void TestMatchWithTypeParameter_02() { var source = @@ -9250,7 +9250,7 @@ .maxstack 2 ); } - [Fact, WorkItem(16129, "https://github.com/dotnet/roslyn/issues/16129")] + [Fact(Skip = "PROTOTYPE(patterns2): code quality"), WorkItem(16129, "https://github.com/dotnet/roslyn/issues/16129")] public void ExactPatternMatch() { var source = diff --git a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs index 2d67592e6bb25c7781e8c326e056907db82753d0..4b6c6892527381fa15e07f463c11d6691410f89a 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs @@ -7583,7 +7583,7 @@ .maxstack 2 "); } - [Fact] + [Fact(Skip = "PROTOTYPE(patterns2): code quality")] public void PatternVariable_TypeChange() { var source0 = MarkedSource(@" @@ -7750,7 +7750,7 @@ .maxstack 2 }"); } - [Fact] + [Fact(Skip = "PROTOTYPE(patterns2): code quality")] public void PatternVariable_DeleteInsert() { var source0 = MarkedSource(@" diff --git a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/LocalSlotMappingTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/LocalSlotMappingTests.cs index 10d0dd4bece584feb9e007dda49e42379e88fd81..f0399e8a64ae3383f56bd1885d802e66d22fe392 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/LocalSlotMappingTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/LocalSlotMappingTests.cs @@ -3642,7 +3642,7 @@ .maxstack 2 ", methodToken: diff1.UpdatedMethods.Single()); } - [Fact] + [Fact(Skip = "PROTOTYPE(patterns2): code quality")] public void PatternVariable() { var source = @" @@ -3811,7 +3811,7 @@ .maxstack 2 ", methodToken: diff1.UpdatedMethods.Single()); } - [Fact] + [Fact(Skip = "PROTOTYPE(patterns2): code quality")] public void PatternMatching_Variable() { var source = @" @@ -3873,7 +3873,7 @@ .maxstack 2 }", methodToken: diff1.UpdatedMethods.Single()); } - [Fact] + [Fact(Skip = "PROTOTYPE(patterns2): code quality")] public void PatternMatching_NoVariable() { var source = @" diff --git a/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs b/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs index 9fd04e97f295b4aaee327f4adf7a541797c25cb3..b84b16d19955262c702564b4af83dd6da8da0fb1 100644 --- a/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs +++ b/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs @@ -7186,7 +7186,7 @@ static void M() #region Patterns - [Fact] + [Fact(Skip = "PROTOTYPE(patterns2): pdb details")] public void SyntaxOffset_Pattern() { var source = @"class C { bool F(object o) => o is int i && o is 3 && o is bool; }"; diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests.cs index ec362dc446a2c895c959831337cf2f1a8e51044c..6bf47b7cbb2a8194f99235fa1cae1f9522772b78 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests.cs @@ -3614,12 +3614,12 @@ static void M(object o) } }"; CreateCompilationWithMscorlib45(source).VerifyDiagnostics( - // (7,29): error CS1525: Invalid expression term ')' + // (7,28): error CS8058: Feature 'recursive patterns' is experimental and unsupported; use '/features:patterns2' to enable. // if (o.Equals is()) {} - Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(7, 29), - // (8,34): error CS1525: Invalid expression term ')' + Diagnostic(ErrorCode.ERR_FeatureIsExperimental, "()").WithArguments("recursive patterns", "patterns2").WithLocation(7, 28), + // (8,33): error CS8058: Feature 'recursive patterns' is experimental and unsupported; use '/features:patterns2' to enable. // if (object.Equals is()) {} - Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(8, 34), + Diagnostic(ErrorCode.ERR_FeatureIsExperimental, "()").WithArguments("recursive patterns", "patterns2").WithLocation(8, 33), // (7,17): error CS0837: The first operand of an 'is' or 'as' operator may not be a lambda expression, anonymous method, or method group. // if (o.Equals is()) {} Diagnostic(ErrorCode.ERR_LambdaInIsAs, "o.Equals is()").WithLocation(7, 17), @@ -3645,16 +3645,16 @@ static void M(object o) } }"; CreateCompilationWithMscorlib45(source).VerifyDiagnostics( - // (7,25): error CS1525: Invalid expression term ')' + // (7,24): error CS8058: Feature 'recursive patterns' is experimental and unsupported; use '/features:patterns2' to enable. // if (null is()) {} - Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(7, 25), - // (8,39): error CS1525: Invalid expression term ')' + Diagnostic(ErrorCode.ERR_FeatureIsExperimental, "()").WithArguments("recursive patterns", "patterns2").WithLocation(7, 24), + // (8,38): error CS8058: Feature 'recursive patterns' is experimental and unsupported; use '/features:patterns2' to enable. // if ((1, object.Equals) is()) {} - Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(8, 39), + Diagnostic(ErrorCode.ERR_FeatureIsExperimental, "()").WithArguments("recursive patterns", "patterns2").WithLocation(8, 38), // (7,17): error CS8117: Invalid operand for pattern match; value required, but found ''. // if (null is()) {} Diagnostic(ErrorCode.ERR_BadIsPatternExpression, "null").WithArguments("").WithLocation(7, 17), - // (8,17): error CS8179: Predefined type 'System.ValueTuple`2' is not defined or imported + // (8,17): error CS8179: Predefined type 'System.ValueTuple`2' is not defined or imported, or is declared in multiple referenced assemblies // if ((1, object.Equals) is()) {} Diagnostic(ErrorCode.ERR_PredefinedValueTupleTypeNotFound, "(1, object.Equals)").WithArguments("System.ValueTuple`2").WithLocation(8, 17), // (8,17): error CS0023: Operator 'is' cannot be applied to operand of type '(int, method group)' @@ -4379,15 +4379,12 @@ public static void Main() "; var compilation = CreateCompilationWithMscorlibAndSystemCore(source, options: TestOptions.DebugDll); compilation.VerifyDiagnostics( - // (8,29): error CS0246: The type or namespace name '_' could not be found (are you missing a using directive or an assembly reference?) + // (8,29): error CS8058: Feature 'recursive patterns' is experimental and unsupported; use '/features:patterns2' to enable. // Write($"is _: {i is _}, "); - Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "_").WithArguments("_").WithLocation(8, 29), - // (11,18): error CS0103: The name '_' does not exist in the current context + Diagnostic(ErrorCode.ERR_FeatureIsExperimental, "_").WithArguments("recursive patterns", "patterns2").WithLocation(8, 29), + // (11,18): error CS8058: Feature 'recursive patterns' is experimental and unsupported; use '/features:patterns2' to enable. // case _: - Diagnostic(ErrorCode.ERR_NameNotInContext, "_").WithArguments("_").WithLocation(11, 18), - // (12,17): warning CS0162: Unreachable code detected - // Write("case _"); - Diagnostic(ErrorCode.WRN_UnreachableCode, "Write").WithLocation(12, 17) + Diagnostic(ErrorCode.ERR_FeatureIsExperimental, "_").WithArguments("recursive patterns", "patterns2").WithLocation(11, 18) ); } @@ -4415,15 +4412,15 @@ public static void Main() "; var compilation = CreateCompilationWithMscorlibAndSystemCore(source, options: TestOptions.DebugDll); compilation.VerifyDiagnostics( - // (9,29): error CS0150: A constant value is expected + // (9,29): error CS8058: Feature 'recursive patterns' is experimental and unsupported; use '/features:patterns2' to enable. // Write($"is _: {i is _}, "); - Diagnostic(ErrorCode.ERR_ConstantExpected, "_").WithLocation(9, 29), - // (12,18): error CS0150: A constant value is expected + Diagnostic(ErrorCode.ERR_FeatureIsExperimental, "_").WithArguments("recursive patterns", "patterns2").WithLocation(9, 29), + // (12,18): error CS8058: Feature 'recursive patterns' is experimental and unsupported; use '/features:patterns2' to enable. // case _: - Diagnostic(ErrorCode.ERR_ConstantExpected, "_").WithLocation(12, 18), - // (13,17): warning CS0162: Unreachable code detected - // Write("case _"); - Diagnostic(ErrorCode.WRN_UnreachableCode, "Write").WithLocation(13, 17) + Diagnostic(ErrorCode.ERR_FeatureIsExperimental, "_").WithArguments("recursive patterns", "patterns2").WithLocation(12, 18), + // (8,13): warning CS0219: The variable '_' is assigned but its value is never used + // int _ = 4; + Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "_").WithArguments("_").WithLocation(8, 13) ); } @@ -6119,7 +6116,7 @@ static bool M(TypedReference x, int* p, ref int z) Diagnostic(ErrorCode.ERR_PatternWrongType, "object").WithArguments("System.TypedReference", "object").WithLocation(9, 23), // (10,23): error CS0244: Neither 'is' nor 'as' is valid on pointer types // var b2 = p is object o2; // not allowed 2 - Diagnostic(ErrorCode.ERR_PointerInAsOrIs, "object o2").WithLocation(10, 23), + Diagnostic(ErrorCode.ERR_PointerInAsOrIs, "object").WithLocation(10, 23), // (7,31): warning CS0168: The variable 'z0' is declared but never used // var r1 = z is ref int z0; // syntax error 2 Diagnostic(ErrorCode.WRN_UnreferencedVar, "z0").WithArguments("z0").WithLocation(7, 31) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests2.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests2.cs new file mode 100644 index 0000000000000000000000000000000000000000..51c03e71ff5066b2a4d11c53db897d3247185ec4 --- /dev/null +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests2.cs @@ -0,0 +1,205 @@ +// 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 System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests +{ + [CompilerTrait(CompilerFeature.Patterns)] + public class PatternMatchingTests2 : PatternMatchingTestBase + { + [Fact] + public void Patterns2_00() + { + var source = +@" +using System; +class Program +{ + public static void Main() + { + Console.WriteLine(1 is int {} x ? x : -1); + } +} +"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularWithRecursivePatterns); + compilation.VerifyDiagnostics( + ); + var comp = CompileAndVerify(compilation, expectedOutput: @"1"); + } + + [Fact] + public void Patterns2_01() + { + var source = +@" +using System; +class Program +{ + public static void Main() + { + Point p = new Point(); + Check(true, p is Point(3, 4) { Length: 5 } q1 && Check(p, q1)); + Check(false, p is Point(1, 4) { Length: 5 }); + Check(false, p is Point(3, 1) { Length: 5 }); + Check(false, p is Point(3, 4) { Length: 1 }); + Check(true, p is (3, 4) { Length: 5 } q2 && Check(p, q2)); + Check(false, p is (1, 4) { Length: 5 }); + Check(false, p is (3, 1) { Length: 5 }); + Check(false, p is (3, 4) { Length: 1 }); + } + private static bool Check(T expected, T actual) + { + if (!object.Equals(expected, actual)) throw new Exception($""expected: {expected}; actual: {actual}""); + return true; + } +} +public class Point +{ + public void Deconstruct(out int X, out int Y) + { + X = 3; + Y = 4; + } + public int Length => 5; +} +"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularWithRecursivePatterns); + compilation.VerifyDiagnostics( + ); + var comp = CompileAndVerify(compilation, expectedOutput: ""); + } + + [Fact] + public void Patterns2_02() + { + var source = +@" +using System; +class Program +{ + public static void Main() + { + Point p = new Point(); + Check(true, p is Point(3, 4) { Length: 5 } q1 && Check(p, q1)); + Check(false, p is Point(1, 4) { Length: 5 }); + Check(false, p is Point(3, 1) { Length: 5 }); + Check(false, p is Point(3, 4) { Length: 1 }); + Check(true, p is (3, 4) { Length: 5 } q2 && Check(p, q2)); + Check(false, p is (1, 4) { Length: 5 }); + Check(false, p is (3, 1) { Length: 5 }); + Check(false, p is (3, 4) { Length: 1 }); + } + private static bool Check(T expected, T actual) + { + if (!object.Equals(expected, actual)) throw new Exception($""expected: {expected}; actual: {actual}""); + return true; + } +} +public class Point +{ + public int Length => 5; +} +public static class PointExtensions +{ + public static void Deconstruct(this Point p, out int X, out int Y) + { + X = 3; + Y = 4; + } +} +"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularWithRecursivePatterns); + compilation.VerifyDiagnostics( + ); + var comp = CompileAndVerify(compilation, expectedOutput: ""); + } + + [Fact] + public void Patterns2_DiscardPattern_01() + { + var source = +@" +using System; +class Program +{ + public static void Main() + { + Point p = new Point(); + Check(true, p is Point(_, _) { Length: _ } q1 && Check(p, q1)); + Check(false, p is Point(1, _) { Length: _ }); + Check(false, p is Point(_, 1) { Length: _ }); + Check(false, p is Point(_, _) { Length: 1 }); + Check(true, p is (_, _) { Length: _ } q2 && Check(p, q2)); + Check(false, p is (1, _) { Length: _ }); + Check(false, p is (_, 1) { Length: _ }); + Check(false, p is (_, _) { Length: 1 }); + } + private static bool Check(T expected, T actual) + { + if (!object.Equals(expected, actual)) throw new Exception($""expected: {expected}; actual: {actual}""); + return true; + } +} +public class Point +{ + public void Deconstruct(out int X, out int Y) + { + X = 3; + Y = 4; + } + public int Length => 5; +} +"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularWithRecursivePatterns); + compilation.VerifyDiagnostics( + ); + var comp = CompileAndVerify(compilation, expectedOutput: ""); + } + + [Fact(Skip = "Pattern-based switch with recursive patterns is not yet implemented")] + public void Patterns2_Switch_Later() + { + var source = +@" +class Program +{ + public static void Main() + { + Point p = new Point(); + switch (p) + { + case Point(3, 4) { Length: 5 }: + System.Console.WriteLine(true); + break; + default: + System.Console.WriteLine(false); + break; + } + } +} +public class Point +{ + public void Deconstruct(out int X, out int Y) + { + X = 3; + Y = 4; + } + public int Length => 5; +} +"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularWithRecursivePatterns); + compilation.VerifyDiagnostics( + ); + var comp = CompileAndVerify(compilation, expectedOutput: "True"); + } + } +} diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternSwitchTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternSwitchTests.cs index fdaa7bc06203173fd6c5352c94f0c446a34af6b3..f2c6830e751a382bcfbde21b08601026d6655983 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternSwitchTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternSwitchTests.cs @@ -1569,186 +1569,177 @@ static void M(object o) // (39,18): error CS8058: Feature 'recursive patterns' is experimental and unsupported; use '/features:patterns2' to enable. // case (System.Int64, System.Int64) d: Diagnostic(ErrorCode.ERR_FeatureIsExperimental, "(System.Int64, System.Int64) d").WithArguments("recursive patterns", "patterns2").WithLocation(39, 18), + // (43,22): error CS8058: Feature 'recursive patterns' is experimental and unsupported; use '/features:patterns2' to enable. + // if (o is (int, int)) {} + Diagnostic(ErrorCode.ERR_FeatureIsExperimental, "(int, int)").WithArguments("recursive patterns", "patterns2").WithLocation(43, 22), // (43,23): error CS1525: Invalid expression term 'int' // if (o is (int, int)) {} Diagnostic(ErrorCode.ERR_InvalidExprTerm, "int").WithArguments("int").WithLocation(43, 23), // (43,28): error CS1525: Invalid expression term 'int' // if (o is (int, int)) {} Diagnostic(ErrorCode.ERR_InvalidExprTerm, "int").WithArguments("int").WithLocation(43, 28), + // (44,22): error CS8058: Feature 'recursive patterns' is experimental and unsupported; use '/features:patterns2' to enable. + // if (o is (int x, int y)) {} + Diagnostic(ErrorCode.ERR_FeatureIsExperimental, "(int x, int y)").WithArguments("recursive patterns", "patterns2").WithLocation(44, 22), + // (45,22): error CS8058: Feature 'recursive patterns' is experimental and unsupported; use '/features:patterns2' to enable. + // if (o is (int, int) z)) {} + Diagnostic(ErrorCode.ERR_FeatureIsExperimental, "(int, int) z").WithArguments("recursive patterns", "patterns2").WithLocation(45, 22), // (45,23): error CS1525: Invalid expression term 'int' // if (o is (int, int) z)) {} Diagnostic(ErrorCode.ERR_InvalidExprTerm, "int").WithArguments("int").WithLocation(45, 23), // (45,28): error CS1525: Invalid expression term 'int' // if (o is (int, int) z)) {} Diagnostic(ErrorCode.ERR_InvalidExprTerm, "int").WithArguments("int").WithLocation(45, 28), - // (45,33): error CS1026: ) expected + // (45,35): error CS1525: Invalid expression term ')' // if (o is (int, int) z)) {} - Diagnostic(ErrorCode.ERR_CloseParenExpected, "z").WithLocation(45, 33), - // (45,34): error CS1002: ; expected + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(45, 35), + // (45,35): error CS1002: ; expected // if (o is (int, int) z)) {} - Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(45, 34), - // (45,34): error CS1513: } expected + Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(45, 35), + // (45,35): error CS1513: } expected // if (o is (int, int) z)) {} - Diagnostic(ErrorCode.ERR_RbraceExpected, ")").WithLocation(45, 34), - // (46,37): error CS1026: ) expected - // if (o is (int a, int b) c) {} - Diagnostic(ErrorCode.ERR_CloseParenExpected, "c").WithLocation(46, 37), - // (46,38): error CS1002: ; expected + Diagnostic(ErrorCode.ERR_RbraceExpected, ")").WithLocation(45, 35), + // (46,22): error CS8058: Feature 'recursive patterns' is experimental and unsupported; use '/features:patterns2' to enable. // if (o is (int a, int b) c) {} - Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(46, 38), - // (46,38): error CS1513: } expected - // if (o is (int a, int b) c) {} - Diagnostic(ErrorCode.ERR_RbraceExpected, ")").WithLocation(46, 38), - // (51,51): error CS1026: ) expected + Diagnostic(ErrorCode.ERR_FeatureIsExperimental, "(int a, int b) c").WithArguments("recursive patterns", "patterns2").WithLocation(46, 22), + // (49,22): error CS8058: Feature 'recursive patterns' is experimental and unsupported; use '/features:patterns2' to enable. + // if (o is (System.Int32, System.Int32)) {} + Diagnostic(ErrorCode.ERR_FeatureIsExperimental, "(System.Int32, System.Int32)").WithArguments("recursive patterns", "patterns2").WithLocation(49, 22), + // (50,22): error CS8058: Feature 'recursive patterns' is experimental and unsupported; use '/features:patterns2' to enable. + // if (o is (System.Int32 x, System.Int32 y)) {} + Diagnostic(ErrorCode.ERR_FeatureIsExperimental, "(System.Int32 x, System.Int32 y)").WithArguments("recursive patterns", "patterns2").WithLocation(50, 22), + // (51,22): error CS8058: Feature 'recursive patterns' is experimental and unsupported; use '/features:patterns2' to enable. // if (o is (System.Int32, System.Int32) z)) {} - Diagnostic(ErrorCode.ERR_CloseParenExpected, "z").WithLocation(51, 51), - // (51,52): error CS1002: ; expected + Diagnostic(ErrorCode.ERR_FeatureIsExperimental, "(System.Int32, System.Int32) z").WithArguments("recursive patterns", "patterns2").WithLocation(51, 22), + // (51,53): error CS1525: Invalid expression term ')' // if (o is (System.Int32, System.Int32) z)) {} - Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(51, 52), - // (51,52): error CS1513: } expected + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(51, 53), + // (51,53): error CS1002: ; expected // if (o is (System.Int32, System.Int32) z)) {} - Diagnostic(ErrorCode.ERR_RbraceExpected, ")").WithLocation(51, 52), - // (52,55): error CS1026: ) expected - // if (o is (System.Int32 a, System.Int32 b) c) {} - Diagnostic(ErrorCode.ERR_CloseParenExpected, "c").WithLocation(52, 55), - // (52,56): error CS1002: ; expected - // if (o is (System.Int32 a, System.Int32 b) c) {} - Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(52, 56), - // (52,56): error CS1513: } expected + Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(51, 53), + // (51,53): error CS1513: } expected + // if (o is (System.Int32, System.Int32) z)) {} + Diagnostic(ErrorCode.ERR_RbraceExpected, ")").WithLocation(51, 53), + // (52,22): error CS8058: Feature 'recursive patterns' is experimental and unsupported; use '/features:patterns2' to enable. // if (o is (System.Int32 a, System.Int32 b) c) {} - Diagnostic(ErrorCode.ERR_RbraceExpected, ")").WithLocation(52, 56), - // (21,18): error CS0570: 'recursive pattern' is not supported by the language + Diagnostic(ErrorCode.ERR_FeatureIsExperimental, "(System.Int32 a, System.Int32 b) c").WithArguments("recursive patterns", "patterns2").WithLocation(52, 22), + // (21,18): error CS8129: No suitable Deconstruct instance or extension method was found for type 'object', with 2 out parameters and a void return type. + // case (int, int): + Diagnostic(ErrorCode.ERR_MissingDeconstruct, "(int, int)").WithArguments("object", "2").WithLocation(21, 18), + // (21,19): error CS0150: A constant value is expected // case (int, int): - Diagnostic(ErrorCode.ERR_BindToBogus, "(int, int)").WithArguments("recursive pattern").WithLocation(21, 18), - // (22,18): error CS0570: 'recursive pattern' is not supported by the language + Diagnostic(ErrorCode.ERR_ConstantExpected, "int").WithLocation(21, 19), + // (21,24): error CS0150: A constant value is expected + // case (int, int): + Diagnostic(ErrorCode.ERR_ConstantExpected, "int").WithLocation(21, 24), + // (22,18): error CS8129: No suitable Deconstruct instance or extension method was found for type 'object', with 2 out parameters and a void return type. // case (int x, int y): - Diagnostic(ErrorCode.ERR_BindToBogus, "(int x, int y)").WithArguments("recursive pattern").WithLocation(22, 18), - // (23,18): error CS0570: 'recursive pattern' is not supported by the language + Diagnostic(ErrorCode.ERR_MissingDeconstruct, "(int x, int y)").WithArguments("object", "2").WithLocation(22, 18), + // (23,18): error CS8129: No suitable Deconstruct instance or extension method was found for type 'object', with 2 out parameters and a void return type. + // case (int, int) z: + Diagnostic(ErrorCode.ERR_MissingDeconstruct, "(int, int) z").WithArguments("object", "2").WithLocation(23, 18), + // (23,19): error CS0150: A constant value is expected + // case (int, int) z: + Diagnostic(ErrorCode.ERR_ConstantExpected, "int").WithLocation(23, 19), + // (23,24): error CS0150: A constant value is expected // case (int, int) z: - Diagnostic(ErrorCode.ERR_BindToBogus, "(int, int) z").WithArguments("recursive pattern").WithLocation(23, 18), - // (24,18): error CS0570: 'recursive pattern' is not supported by the language + Diagnostic(ErrorCode.ERR_ConstantExpected, "int").WithLocation(23, 24), + // (24,18): error CS8129: No suitable Deconstruct instance or extension method was found for type 'object', with 2 out parameters and a void return type. // case (int a, int b) c: - Diagnostic(ErrorCode.ERR_BindToBogus, "(int a, int b) c").WithArguments("recursive pattern").WithLocation(24, 18), - // (25,18): error CS0570: 'recursive pattern' is not supported by the language + Diagnostic(ErrorCode.ERR_MissingDeconstruct, "(int a, int b) c").WithArguments("object", "2").WithLocation(24, 18), + // (25,18): error CS8129: No suitable Deconstruct instance or extension method was found for type 'object', with 2 out parameters and a void return type. // case (long, long) d: - Diagnostic(ErrorCode.ERR_BindToBogus, "(long, long) d").WithArguments("recursive pattern").WithLocation(25, 18), - // (30,18): error CS0570: 'recursive pattern' is not supported by the language + Diagnostic(ErrorCode.ERR_MissingDeconstruct, "(long, long) d").WithArguments("object", "2").WithLocation(25, 18), + // (25,19): error CS0150: A constant value is expected + // case (long, long) d: + Diagnostic(ErrorCode.ERR_ConstantExpected, "long").WithLocation(25, 19), + // (25,25): error CS0150: A constant value is expected + // case (long, long) d: + Diagnostic(ErrorCode.ERR_ConstantExpected, "long").WithLocation(25, 25), + // (30,18): error CS8129: No suitable Deconstruct instance or extension method was found for type 'object', with 2 out parameters and a void return type. + // case (int, int) z: + Diagnostic(ErrorCode.ERR_MissingDeconstruct, "(int, int) z").WithArguments("object", "2").WithLocation(30, 18), + // (30,19): error CS0150: A constant value is expected + // case (int, int) z: + Diagnostic(ErrorCode.ERR_ConstantExpected, "int").WithLocation(30, 19), + // (30,24): error CS0150: A constant value is expected // case (int, int) z: - Diagnostic(ErrorCode.ERR_BindToBogus, "(int, int) z").WithArguments("recursive pattern").WithLocation(30, 18), - // (32,18): error CS0570: 'recursive pattern' is not supported by the language + Diagnostic(ErrorCode.ERR_ConstantExpected, "int").WithLocation(30, 24), + // (32,18): error CS8129: No suitable Deconstruct instance or extension method was found for type 'object', with 2 out parameters and a void return type. // case (long, long) d: - Diagnostic(ErrorCode.ERR_BindToBogus, "(long, long) d").WithArguments("recursive pattern").WithLocation(32, 18), - // (37,18): error CS0570: 'recursive pattern' is not supported by the language + Diagnostic(ErrorCode.ERR_MissingDeconstruct, "(long, long) d").WithArguments("object", "2").WithLocation(32, 18), + // (32,19): error CS0150: A constant value is expected + // case (long, long) d: + Diagnostic(ErrorCode.ERR_ConstantExpected, "long").WithLocation(32, 19), + // (32,25): error CS0150: A constant value is expected + // case (long, long) d: + Diagnostic(ErrorCode.ERR_ConstantExpected, "long").WithLocation(32, 25), + // (37,18): error CS8129: No suitable Deconstruct instance or extension method was found for type 'object', with 2 out parameters and a void return type. + // case (System.Int32, System.Int32) z: + Diagnostic(ErrorCode.ERR_MissingDeconstruct, "(System.Int32, System.Int32) z").WithArguments("object", "2").WithLocation(37, 18), + // (37,19): error CS0119: 'int' is a type, which is not valid in the given context // case (System.Int32, System.Int32) z: - Diagnostic(ErrorCode.ERR_BindToBogus, "(System.Int32, System.Int32) z").WithArguments("recursive pattern").WithLocation(37, 18), - // (39,18): error CS0570: 'recursive pattern' is not supported by the language + Diagnostic(ErrorCode.ERR_BadSKunknown, "System.Int32").WithArguments("int", "type").WithLocation(37, 19), + // (37,33): error CS0119: 'int' is a type, which is not valid in the given context + // case (System.Int32, System.Int32) z: + Diagnostic(ErrorCode.ERR_BadSKunknown, "System.Int32").WithArguments("int", "type").WithLocation(37, 33), + // (39,18): error CS8129: No suitable Deconstruct instance or extension method was found for type 'object', with 2 out parameters and a void return type. + // case (System.Int64, System.Int64) d: + Diagnostic(ErrorCode.ERR_MissingDeconstruct, "(System.Int64, System.Int64) d").WithArguments("object", "2").WithLocation(39, 18), + // (39,19): error CS0119: 'long' is a type, which is not valid in the given context + // case (System.Int64, System.Int64) d: + Diagnostic(ErrorCode.ERR_BadSKunknown, "System.Int64").WithArguments("long", "type").WithLocation(39, 19), + // (39,33): error CS0119: 'long' is a type, which is not valid in the given context // case (System.Int64, System.Int64) d: - Diagnostic(ErrorCode.ERR_BindToBogus, "(System.Int64, System.Int64) d").WithArguments("recursive pattern").WithLocation(39, 18), - // (43,22): error CS0150: A constant value is expected + Diagnostic(ErrorCode.ERR_BadSKunknown, "System.Int64").WithArguments("long", "type").WithLocation(39, 33), + // (43,22): error CS8129: No suitable Deconstruct instance or extension method was found for type 'object', with 2 out parameters and a void return type. // if (o is (int, int)) {} - Diagnostic(ErrorCode.ERR_ConstantExpected, "(int, int)").WithLocation(43, 22), - // (44,23): error CS8185: A declaration is not allowed in this context. - // if (o is (int x, int y)) {} - Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int x").WithLocation(44, 23), - // (44,30): error CS8185: A declaration is not allowed in this context. - // if (o is (int x, int y)) {} - Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int y").WithLocation(44, 30), - // (44,22): error CS0150: A constant value is expected + Diagnostic(ErrorCode.ERR_MissingDeconstruct, "(int, int)").WithArguments("object", "2").WithLocation(43, 22), + // (43,23): error CS0150: A constant value is expected + // if (o is (int, int)) {} + Diagnostic(ErrorCode.ERR_ConstantExpected, "int").WithLocation(43, 23), + // (43,28): error CS0150: A constant value is expected + // if (o is (int, int)) {} + Diagnostic(ErrorCode.ERR_ConstantExpected, "int").WithLocation(43, 28), + // (44,22): error CS8129: No suitable Deconstruct instance or extension method was found for type 'object', with 2 out parameters and a void return type. // if (o is (int x, int y)) {} - Diagnostic(ErrorCode.ERR_ConstantExpected, "(int x, int y)").WithLocation(44, 22), - // (45,22): error CS0150: A constant value is expected + Diagnostic(ErrorCode.ERR_MissingDeconstruct, "(int x, int y)").WithArguments("object", "2").WithLocation(44, 22), + // (45,22): error CS8129: No suitable Deconstruct instance or extension method was found for type 'object', with 2 out parameters and a void return type. // if (o is (int, int) z)) {} - Diagnostic(ErrorCode.ERR_ConstantExpected, "(int, int)").WithLocation(45, 22), - // (45,33): error CS0103: The name 'z' does not exist in the current context + Diagnostic(ErrorCode.ERR_MissingDeconstruct, "(int, int) z").WithArguments("object", "2").WithLocation(45, 22), + // (45,23): error CS0150: A constant value is expected // if (o is (int, int) z)) {} - Diagnostic(ErrorCode.ERR_NameNotInContext, "z").WithArguments("z").WithLocation(45, 33), - // (46,23): error CS8185: A declaration is not allowed in this context. - // if (o is (int a, int b) c) {} - Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int a").WithLocation(46, 23), - // (46,30): error CS8185: A declaration is not allowed in this context. - // if (o is (int a, int b) c) {} - Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int b").WithLocation(46, 30), - // (46,22): error CS0150: A constant value is expected - // if (o is (int a, int b) c) {} - Diagnostic(ErrorCode.ERR_ConstantExpected, "(int a, int b)").WithLocation(46, 22), - // (46,37): error CS0103: The name 'c' does not exist in the current context + Diagnostic(ErrorCode.ERR_ConstantExpected, "int").WithLocation(45, 23), + // (45,28): error CS0150: A constant value is expected + // if (o is (int, int) z)) {} + Diagnostic(ErrorCode.ERR_ConstantExpected, "int").WithLocation(45, 28), + // (46,22): error CS8129: No suitable Deconstruct instance or extension method was found for type 'object', with 2 out parameters and a void return type. // if (o is (int a, int b) c) {} - Diagnostic(ErrorCode.ERR_NameNotInContext, "c").WithArguments("c").WithLocation(46, 37), + Diagnostic(ErrorCode.ERR_MissingDeconstruct, "(int a, int b) c").WithArguments("object", "2").WithLocation(46, 22), + // (49,22): error CS8129: No suitable Deconstruct instance or extension method was found for type 'object', with 2 out parameters and a void return type. + // if (o is (System.Int32, System.Int32)) {} + Diagnostic(ErrorCode.ERR_MissingDeconstruct, "(System.Int32, System.Int32)").WithArguments("object", "2").WithLocation(49, 22), // (49,23): error CS0119: 'int' is a type, which is not valid in the given context // if (o is (System.Int32, System.Int32)) {} Diagnostic(ErrorCode.ERR_BadSKunknown, "System.Int32").WithArguments("int", "type").WithLocation(49, 23), // (49,37): error CS0119: 'int' is a type, which is not valid in the given context // if (o is (System.Int32, System.Int32)) {} Diagnostic(ErrorCode.ERR_BadSKunknown, "System.Int32").WithArguments("int", "type").WithLocation(49, 37), - // (50,23): error CS8185: A declaration is not allowed in this context. - // if (o is (System.Int32 x, System.Int32 y)) {} - Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "System.Int32 x").WithLocation(50, 23), - // (50,39): error CS8185: A declaration is not allowed in this context. + // (50,22): error CS8129: No suitable Deconstruct instance or extension method was found for type 'object', with 2 out parameters and a void return type. // if (o is (System.Int32 x, System.Int32 y)) {} - Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "System.Int32 y").WithLocation(50, 39), - // (50,22): error CS0150: A constant value is expected - // if (o is (System.Int32 x, System.Int32 y)) {} - Diagnostic(ErrorCode.ERR_ConstantExpected, "(System.Int32 x, System.Int32 y)").WithLocation(50, 22), + Diagnostic(ErrorCode.ERR_MissingDeconstruct, "(System.Int32 x, System.Int32 y)").WithArguments("object", "2").WithLocation(50, 22), + // (51,22): error CS8129: No suitable Deconstruct instance or extension method was found for type 'object', with 2 out parameters and a void return type. + // if (o is (System.Int32, System.Int32) z)) {} + Diagnostic(ErrorCode.ERR_MissingDeconstruct, "(System.Int32, System.Int32) z").WithArguments("object", "2").WithLocation(51, 22), // (51,23): error CS0119: 'int' is a type, which is not valid in the given context // if (o is (System.Int32, System.Int32) z)) {} Diagnostic(ErrorCode.ERR_BadSKunknown, "System.Int32").WithArguments("int", "type").WithLocation(51, 23), // (51,37): error CS0119: 'int' is a type, which is not valid in the given context // if (o is (System.Int32, System.Int32) z)) {} Diagnostic(ErrorCode.ERR_BadSKunknown, "System.Int32").WithArguments("int", "type").WithLocation(51, 37), - // (51,51): error CS0103: The name 'z' does not exist in the current context - // if (o is (System.Int32, System.Int32) z)) {} - Diagnostic(ErrorCode.ERR_NameNotInContext, "z").WithArguments("z").WithLocation(51, 51), - // (52,23): error CS8185: A declaration is not allowed in this context. - // if (o is (System.Int32 a, System.Int32 b) c) {} - Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "System.Int32 a").WithLocation(52, 23), - // (52,39): error CS8185: A declaration is not allowed in this context. - // if (o is (System.Int32 a, System.Int32 b) c) {} - Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "System.Int32 b").WithLocation(52, 39), - // (52,22): error CS0150: A constant value is expected - // if (o is (System.Int32 a, System.Int32 b) c) {} - Diagnostic(ErrorCode.ERR_ConstantExpected, "(System.Int32 a, System.Int32 b)").WithLocation(52, 22), - // (52,55): error CS0103: The name 'c' does not exist in the current context - // if (o is (System.Int32 a, System.Int32 b) c) {} - Diagnostic(ErrorCode.ERR_NameNotInContext, "c").WithArguments("c").WithLocation(52, 55), - // (26,17): warning CS0162: Unreachable code detected - // break; - Diagnostic(ErrorCode.WRN_UnreachableCode, "break").WithLocation(26, 17), - // (31,17): warning CS0162: Unreachable code detected - // break; - Diagnostic(ErrorCode.WRN_UnreachableCode, "break").WithLocation(31, 17), - // (33,17): warning CS0162: Unreachable code detected - // break; - Diagnostic(ErrorCode.WRN_UnreachableCode, "break").WithLocation(33, 17), - // (38,17): warning CS0162: Unreachable code detected - // break; - Diagnostic(ErrorCode.WRN_UnreachableCode, "break").WithLocation(38, 17), - // (40,17): warning CS0162: Unreachable code detected - // break; - Diagnostic(ErrorCode.WRN_UnreachableCode, "break").WithLocation(40, 17), - // (44,23): error CS0165: Use of unassigned local variable 'x' - // if (o is (int x, int y)) {} - Diagnostic(ErrorCode.ERR_UseDefViolation, "int x").WithArguments("x").WithLocation(44, 23), - // (44,30): error CS0165: Use of unassigned local variable 'y' - // if (o is (int x, int y)) {} - Diagnostic(ErrorCode.ERR_UseDefViolation, "int y").WithArguments("y").WithLocation(44, 30), - // (46,23): error CS0165: Use of unassigned local variable 'a' - // if (o is (int a, int b) c) {} - Diagnostic(ErrorCode.ERR_UseDefViolation, "int a").WithArguments("a").WithLocation(46, 23), - // (46,30): error CS0165: Use of unassigned local variable 'b' - // if (o is (int a, int b) c) {} - Diagnostic(ErrorCode.ERR_UseDefViolation, "int b").WithArguments("b").WithLocation(46, 30), - // (50,23): error CS0165: Use of unassigned local variable 'x' - // if (o is (System.Int32 x, System.Int32 y)) {} - Diagnostic(ErrorCode.ERR_UseDefViolation, "System.Int32 x").WithArguments("x").WithLocation(50, 23), - // (50,39): error CS0165: Use of unassigned local variable 'y' - // if (o is (System.Int32 x, System.Int32 y)) {} - Diagnostic(ErrorCode.ERR_UseDefViolation, "System.Int32 y").WithArguments("y").WithLocation(50, 39), - // (52,23): error CS0165: Use of unassigned local variable 'a' - // if (o is (System.Int32 a, System.Int32 b) c) {} - Diagnostic(ErrorCode.ERR_UseDefViolation, "System.Int32 a").WithArguments("a").WithLocation(52, 23), - // (52,39): error CS0165: Use of unassigned local variable 'b' + // (52,22): error CS8129: No suitable Deconstruct instance or extension method was found for type 'object', with 2 out parameters and a void return type. // if (o is (System.Int32 a, System.Int32 b) c) {} - Diagnostic(ErrorCode.ERR_UseDefViolation, "System.Int32 b").WithArguments("b").WithLocation(52, 39) + Diagnostic(ErrorCode.ERR_MissingDeconstruct, "(System.Int32 a, System.Int32 b) c").WithArguments("object", "2").WithLocation(52, 22) ); } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/SwitchTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/SwitchTests.cs index 364fb814d9835e91318fd5763ab3d59718cd1add..85a02b9de402bd809df7c613f66f45daecdaf289 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/SwitchTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/SwitchTests.cs @@ -1181,18 +1181,18 @@ static object F(int i) // (6,17): error CS0151: A switch expression or case label must be a bool, char, string, integral, enum, or corresponding nullable type in C# 6 and earlier. // switch (o) Diagnostic(ErrorCode.ERR_V6SwitchGoverningTypeValueExpected, "o").WithLocation(6, 17), - // (8,18): error CS0570: 'recursive pattern' is not supported by the language + // (8,18): error CS8129: No suitable Deconstruct instance or extension method was found for type 'object', with 1 out parameters and a void return type. // case ((o.GetType().Name.Length)): - Diagnostic(ErrorCode.ERR_BindToBogus, "((o.GetType().Name.Length))").WithArguments("recursive pattern").WithLocation(8, 18), + Diagnostic(ErrorCode.ERR_MissingDeconstruct, "((o.GetType().Name.Length))").WithArguments("object", "1").WithLocation(8, 18), + // (8,20): error CS0118: 'o' is a variable but is used like a type + // case ((o.GetType().Name.Length)): + Diagnostic(ErrorCode.ERR_BadSKknown, "o").WithArguments("o", "variable", "type").WithLocation(8, 20), + // (8,32): error CS0103: The name 'Name' does not exist in the current context + // case ((o.GetType().Name.Length)): + Diagnostic(ErrorCode.ERR_NameNotInContext, "Name").WithArguments("Name").WithLocation(8, 32), // (9,17): error CS7036: There is no argument given that corresponds to the required formal parameter 'o' of 'C.M(object)' // M(); - Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "M").WithArguments("o", "C.M(object)").WithLocation(9, 17), - // (12,13): error CS8120: The switch case has already been handled by a previous case. - // case 0: - Diagnostic(ErrorCode.ERR_PatternIsSubsumed, "case 0:").WithLocation(12, 13), - // (9,17): warning CS0162: Unreachable code detected - // M(); - Diagnostic(ErrorCode.WRN_UnreachableCode, "M").WithLocation(9, 17) + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "M").WithArguments("o", "C.M(object)").WithLocation(9, 17) ); CreateStandardCompilation(text, parseOptions: TestOptions.Regular6WithV7SwitchBinder).VerifyDiagnostics( // (8,13): error CS8059: Feature 'pattern matching' is not available in C# 6. Please use language version 7.0 or greater. @@ -1216,18 +1216,18 @@ static object F(int i) // (6,17): error CS0151: A switch expression or case label must be a bool, char, string, integral, enum, or corresponding nullable type in C# 6 and earlier. // switch (o) Diagnostic(ErrorCode.ERR_V6SwitchGoverningTypeValueExpected, "o").WithLocation(6, 17), - // (8,18): error CS0570: 'recursive pattern' is not supported by the language + // (8,18): error CS8129: No suitable Deconstruct instance or extension method was found for type 'object', with 1 out parameters and a void return type. + // case ((o.GetType().Name.Length)): + Diagnostic(ErrorCode.ERR_MissingDeconstruct, "((o.GetType().Name.Length))").WithArguments("object", "1").WithLocation(8, 18), + // (8,20): error CS0118: 'o' is a variable but is used like a type + // case ((o.GetType().Name.Length)): + Diagnostic(ErrorCode.ERR_BadSKknown, "o").WithArguments("o", "variable", "type").WithLocation(8, 20), + // (8,32): error CS0103: The name 'Name' does not exist in the current context // case ((o.GetType().Name.Length)): - Diagnostic(ErrorCode.ERR_BindToBogus, "((o.GetType().Name.Length))").WithArguments("recursive pattern").WithLocation(8, 18), + Diagnostic(ErrorCode.ERR_NameNotInContext, "Name").WithArguments("Name").WithLocation(8, 32), // (9,17): error CS7036: There is no argument given that corresponds to the required formal parameter 'o' of 'C.M(object)' // M(); - Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "M").WithArguments("o", "C.M(object)").WithLocation(9, 17), - // (12,13): error CS8120: The switch case has already been handled by a previous case. - // case 0: - Diagnostic(ErrorCode.ERR_PatternIsSubsumed, "case 0:").WithLocation(12, 13), - // (9,17): warning CS0162: Unreachable code detected - // M(); - Diagnostic(ErrorCode.WRN_UnreachableCode, "M").WithLocation(9, 17) + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "M").WithArguments("o", "C.M(object)").WithLocation(9, 17) ); CreateStandardCompilation(text).VerifyDiagnostics( // (8,18): error CS8058: Feature 'recursive patterns' is experimental and unsupported; use '/features:patterns2' to enable. @@ -1245,18 +1245,18 @@ static object F(int i) // (8,32): error CS1003: Syntax error, ',' expected // case ((o.GetType().Name.Length)): Diagnostic(ErrorCode.ERR_SyntaxError, "Name").WithArguments(",", "").WithLocation(8, 32), - // (8,18): error CS0570: 'recursive pattern' is not supported by the language + // (8,18): error CS8129: No suitable Deconstruct instance or extension method was found for type 'object', with 1 out parameters and a void return type. + // case ((o.GetType().Name.Length)): + Diagnostic(ErrorCode.ERR_MissingDeconstruct, "((o.GetType().Name.Length))").WithArguments("object", "1").WithLocation(8, 18), + // (8,20): error CS0118: 'o' is a variable but is used like a type // case ((o.GetType().Name.Length)): - Diagnostic(ErrorCode.ERR_BindToBogus, "((o.GetType().Name.Length))").WithArguments("recursive pattern").WithLocation(8, 18), + Diagnostic(ErrorCode.ERR_BadSKknown, "o").WithArguments("o", "variable", "type").WithLocation(8, 20), + // (8,32): error CS0103: The name 'Name' does not exist in the current context + // case ((o.GetType().Name.Length)): + Diagnostic(ErrorCode.ERR_NameNotInContext, "Name").WithArguments("Name").WithLocation(8, 32), // (9,17): error CS7036: There is no argument given that corresponds to the required formal parameter 'o' of 'C.M(object)' // M(); - Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "M").WithArguments("o", "C.M(object)").WithLocation(9, 17), - // (12,13): error CS8120: The switch case has already been handled by a previous case. - // case 0: - Diagnostic(ErrorCode.ERR_PatternIsSubsumed, "case 0:").WithLocation(12, 13), - // (9,17): warning CS0162: Unreachable code detected - // M(); - Diagnostic(ErrorCode.WRN_UnreachableCode, "M").WithLocation(9, 17) + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "M").WithArguments("o", "C.M(object)").WithLocation(9, 17) ); } diff --git a/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelGetSemanticInfoTests.cs b/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelGetSemanticInfoTests.cs index fe891676ed04d1a1d5b4cc180eb2aae5037939e4..b019d789bf8c9f3c1a7e283e27cead0b2e7c2b14 100644 --- a/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelGetSemanticInfoTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelGetSemanticInfoTests.cs @@ -8873,20 +8873,43 @@ public static int Main(string[] args) } } "; - var semanticInfo = GetSemanticInfoForTest(sourceCode); + CreateStandardCompilation(sourceCode).VerifyDiagnostics( + // (12,28): error CS8058: Feature 'recursive patterns' is experimental and unsupported; use '/features:patterns2' to enable. + // case /**/()=>3/**/: + Diagnostic(ErrorCode.ERR_FeatureIsExperimental, "()").WithArguments("recursive patterns", "patterns2").WithLocation(12, 28), + // (12,30): error CS1003: Syntax error, ':' expected + // case /**/()=>3/**/: + Diagnostic(ErrorCode.ERR_SyntaxError, "=>").WithArguments(":", "=>").WithLocation(12, 30), + // (12,30): error CS1513: } expected + // case /**/()=>3/**/: + Diagnostic(ErrorCode.ERR_RbraceExpected, "=>").WithLocation(12, 30), + // (12,44): error CS1002: ; expected + // case /**/()=>3/**/: + Diagnostic(ErrorCode.ERR_SemicolonExpected, ":").WithLocation(12, 44), + // (12,44): error CS1513: } expected + // case /**/()=>3/**/: + Diagnostic(ErrorCode.ERR_RbraceExpected, ":").WithLocation(12, 44), + // (12,28): error CS8129: No suitable Deconstruct instance or extension method was found for type 'string', with 0 out parameters and a void return type. + // case /**/()=>3/**/: + Diagnostic(ErrorCode.ERR_MissingDeconstruct, "()").WithArguments("string", "0").WithLocation(12, 28) + ); - Assert.Null(semanticInfo.Type); - Assert.Equal("System.String", semanticInfo.ConvertedType.ToTestDisplayString()); - Assert.Equal(TypeKind.Class, semanticInfo.ConvertedType.TypeKind); - Assert.Equal(ConversionKind.NoConversion, semanticInfo.ImplicitConversion.Kind); + // Due to language changes to support recursive patterns, the parser will no longer treat this syntax as a lambda. - Assert.Equal("lambda expression", semanticInfo.Symbol.ToTestDisplayString()); - Assert.Equal(SymbolKind.Method, semanticInfo.Symbol.Kind); - Assert.Equal(0, semanticInfo.CandidateSymbols.Length); + //var semanticInfo = GetSemanticInfoForTest(sourceCode); - Assert.Equal(0, semanticInfo.MethodGroup.Length); + //Assert.Null(semanticInfo.Type); + //Assert.Equal("System.String", semanticInfo.ConvertedType.ToTestDisplayString()); + //Assert.Equal(TypeKind.Class, semanticInfo.ConvertedType.TypeKind); + //Assert.Equal(ConversionKind.NoConversion, semanticInfo.ImplicitConversion.Kind); - Assert.False(semanticInfo.IsCompileTimeConstant); + //Assert.Equal("lambda expression", semanticInfo.Symbol.ToTestDisplayString()); + //Assert.Equal(SymbolKind.Method, semanticInfo.Symbol.Kind); + //Assert.Equal(0, semanticInfo.CandidateSymbols.Length); + + //Assert.Equal(0, semanticInfo.MethodGroup.Length); + + //Assert.False(semanticInfo.IsCompileTimeConstant); } [Fact] @@ -8914,20 +8937,36 @@ public static int Main(string[] args) } } "; - var semanticInfo = GetSemanticInfoForTest(sourceCode); + CreateStandardCompilation(sourceCode).VerifyDiagnostics( + // (13,28): error CS8058: Feature 'recursive patterns' is experimental and unsupported; use '/features:patterns2' to enable. + // case /**/()=>/**/: + Diagnostic(ErrorCode.ERR_FeatureIsExperimental, "()").WithArguments("recursive patterns", "patterns2").WithLocation(13, 28), + // (13,30): error CS1003: Syntax error, ':' expected + // case /**/()=>/**/: + Diagnostic(ErrorCode.ERR_SyntaxError, "=>").WithArguments(":", "=>").WithLocation(13, 30), + // (13,30): error CS1513: } expected + // case /**/()=>/**/: + Diagnostic(ErrorCode.ERR_RbraceExpected, "=>").WithLocation(13, 30), + // (13,28): error CS8129: No suitable Deconstruct instance or extension method was found for type 'string', with 0 out parameters and a void return type. + // case /**/()=>/**/: + Diagnostic(ErrorCode.ERR_MissingDeconstruct, "()").WithArguments("string", "0").WithLocation(13, 28) + ); - Assert.Null(semanticInfo.Type); - Assert.Equal("System.String", semanticInfo.ConvertedType.ToTestDisplayString()); - Assert.Equal(TypeKind.Class, semanticInfo.ConvertedType.TypeKind); - Assert.Equal(ConversionKind.NoConversion, semanticInfo.ImplicitConversion.Kind); + // Due to language changes to support recursive patterns, the parser will no longer treat this syntax as a lambda. + //var semanticInfo = GetSemanticInfoForTest(sourceCode); - Assert.Equal("lambda expression", semanticInfo.Symbol.ToTestDisplayString()); - Assert.Equal(SymbolKind.Method, semanticInfo.Symbol.Kind); - Assert.Equal(0, semanticInfo.CandidateSymbols.Length); + //Assert.Null(semanticInfo.Type); + //Assert.Equal("System.String", semanticInfo.ConvertedType.ToTestDisplayString()); + //Assert.Equal(TypeKind.Class, semanticInfo.ConvertedType.TypeKind); + //Assert.Equal(ConversionKind.NoConversion, semanticInfo.ImplicitConversion.Kind); - Assert.Equal(0, semanticInfo.MethodGroup.Length); + //Assert.Equal("lambda expression", semanticInfo.Symbol.ToTestDisplayString()); + //Assert.Equal(SymbolKind.Method, semanticInfo.Symbol.Kind); + //Assert.Equal(0, semanticInfo.CandidateSymbols.Length); - Assert.False(semanticInfo.IsCompileTimeConstant); + //Assert.Equal(0, semanticInfo.MethodGroup.Length); + + //Assert.False(semanticInfo.IsCompileTimeConstant); } [Fact] diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs index 49335b9a45e9c05d3f0fc8f644b52c85970af48d..591612ac47bdf91ef79cdc07b3be48b333810a63 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs @@ -1982,7 +1982,7 @@ static void Where() ); } - [Fact] + [Fact(Skip = "PROTOTYPE(patterns2): when do we detect this lowering error?")] public void System_Nullable_T_GetValueOrDefault_10() { var source = diff --git a/src/Compilers/CSharp/Test/Syntax/Generated/Syntax.Test.xml.Generated.cs b/src/Compilers/CSharp/Test/Syntax/Generated/Syntax.Test.xml.Generated.cs index 39012a971c0854dba60b1048cebb30fd454587cf..5c54bdf55780116c30673a45f351481293e3f723 100644 --- a/src/Compilers/CSharp/Test/Syntax/Generated/Syntax.Test.xml.Generated.cs +++ b/src/Compilers/CSharp/Test/Syntax/Generated/Syntax.Test.xml.Generated.cs @@ -361,7 +361,7 @@ private static Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.InterpolatedS private static Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.IsPatternExpressionSyntax GenerateIsPatternExpression() { - return Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.SyntaxFactory.IsPatternExpression(GenerateIdentifierName(), Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.SyntaxFactory.Token(SyntaxKind.IsKeyword), GenerateDeclarationPattern()); + return Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.SyntaxFactory.IsPatternExpression(GenerateIdentifierName(), Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.SyntaxFactory.Token(SyntaxKind.IsKeyword), GenerateDiscardPattern()); } private static Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.ThrowExpressionSyntax GenerateThrowExpression() @@ -374,6 +374,11 @@ private static Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.WhenClauseSyn return Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.SyntaxFactory.WhenClause(Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.SyntaxFactory.Token(SyntaxKind.WhenKeyword), GenerateIdentifierName()); } + private static Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.DiscardPatternSyntax GenerateDiscardPattern() + { + return Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.SyntaxFactory.DiscardPattern(Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.SyntaxFactory.Identifier("UnderscoreToken")); + } + private static Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.DeclarationPatternSyntax GenerateDeclarationPattern() { return Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.SyntaxFactory.DeclarationPattern(GenerateIdentifierName(), GenerateSingleVariableDesignation()); @@ -386,7 +391,7 @@ private static Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.Deconstructio private static Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.SubpatternElementSyntax GenerateSubpatternElement() { - return Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.SyntaxFactory.SubpatternElement(null, GenerateDeclarationPattern()); + return Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.SyntaxFactory.SubpatternElement(null, GenerateDiscardPattern()); } private static Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.PropertyPatternSyntax GeneratePropertyPattern() @@ -591,7 +596,7 @@ private static Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.SwitchSection private static Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.CasePatternSwitchLabelSyntax GenerateCasePatternSwitchLabel() { - return Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.SyntaxFactory.CasePatternSwitchLabel(Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.SyntaxFactory.Token(SyntaxKind.CaseKeyword), GenerateDeclarationPattern(), null, Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.SyntaxFactory.Identifier("ColonToken")); + return Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.SyntaxFactory.CasePatternSwitchLabel(Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.SyntaxFactory.Token(SyntaxKind.CaseKeyword), GenerateDiscardPattern(), null, Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.SyntaxFactory.Identifier("ColonToken")); } private static Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.CaseSwitchLabelSyntax GenerateCaseSwitchLabel() @@ -1915,6 +1920,16 @@ public void TestWhenClauseFactoryAndProperties() AttachAndCheckDiagnostics(node); } + [Fact] + public void TestDiscardPatternFactoryAndProperties() + { + var node = GenerateDiscardPattern(); + + Assert.Equal(SyntaxKind.IdentifierToken, node.UnderscoreToken.Kind); + + AttachAndCheckDiagnostics(node); + } + [Fact] public void TestDeclarationPatternFactoryAndProperties() { @@ -5587,6 +5602,32 @@ public void TestWhenClauseIdentityRewriter() Assert.Same(oldNode, newNode); } + [Fact] + public void TestDiscardPatternTokenDeleteRewriter() + { + var oldNode = GenerateDiscardPattern(); + var rewriter = new TokenDeleteRewriter(); + var newNode = rewriter.Visit(oldNode); + + if(!oldNode.IsMissing) + { + Assert.NotEqual(oldNode, newNode); + } + + Assert.NotNull(newNode); + Assert.True(newNode.IsMissing, "No tokens => missing"); + } + + [Fact] + public void TestDiscardPatternIdentityRewriter() + { + var oldNode = GenerateDiscardPattern(); + var rewriter = new IdentityRewriter(); + var newNode = rewriter.Visit(oldNode); + + Assert.Same(oldNode, newNode); + } + [Fact] public void TestDeclarationPatternTokenDeleteRewriter() { @@ -9454,7 +9495,7 @@ private static InterpolatedStringExpressionSyntax GenerateInterpolatedStringExpr private static IsPatternExpressionSyntax GenerateIsPatternExpression() { - return SyntaxFactory.IsPatternExpression(GenerateIdentifierName(), SyntaxFactory.Token(SyntaxKind.IsKeyword), GenerateDeclarationPattern()); + return SyntaxFactory.IsPatternExpression(GenerateIdentifierName(), SyntaxFactory.Token(SyntaxKind.IsKeyword), GenerateDiscardPattern()); } private static ThrowExpressionSyntax GenerateThrowExpression() @@ -9467,6 +9508,11 @@ private static WhenClauseSyntax GenerateWhenClause() return SyntaxFactory.WhenClause(SyntaxFactory.Token(SyntaxKind.WhenKeyword), GenerateIdentifierName()); } + private static DiscardPatternSyntax GenerateDiscardPattern() + { + return SyntaxFactory.DiscardPattern(SyntaxFactory.Identifier("UnderscoreToken")); + } + private static DeclarationPatternSyntax GenerateDeclarationPattern() { return SyntaxFactory.DeclarationPattern(GenerateIdentifierName(), GenerateSingleVariableDesignation()); @@ -9479,7 +9525,7 @@ private static DeconstructionPatternSyntax GenerateDeconstructionPattern() private static SubpatternElementSyntax GenerateSubpatternElement() { - return SyntaxFactory.SubpatternElement(default(NameColonSyntax), GenerateDeclarationPattern()); + return SyntaxFactory.SubpatternElement(default(NameColonSyntax), GenerateDiscardPattern()); } private static PropertyPatternSyntax GeneratePropertyPattern() @@ -9684,7 +9730,7 @@ private static SwitchSectionSyntax GenerateSwitchSection() private static CasePatternSwitchLabelSyntax GenerateCasePatternSwitchLabel() { - return SyntaxFactory.CasePatternSwitchLabel(SyntaxFactory.Token(SyntaxKind.CaseKeyword), GenerateDeclarationPattern(), default(WhenClauseSyntax), SyntaxFactory.Identifier("ColonToken")); + return SyntaxFactory.CasePatternSwitchLabel(SyntaxFactory.Token(SyntaxKind.CaseKeyword), GenerateDiscardPattern(), default(WhenClauseSyntax), SyntaxFactory.Identifier("ColonToken")); } private static CaseSwitchLabelSyntax GenerateCaseSwitchLabel() @@ -11008,6 +11054,16 @@ public void TestWhenClauseFactoryAndProperties() Assert.Equal(node, newNode); } + [Fact] + public void TestDiscardPatternFactoryAndProperties() + { + var node = GenerateDiscardPattern(); + + Assert.Equal(SyntaxKind.IdentifierToken, node.UnderscoreToken.Kind()); + var newNode = node.WithUnderscoreToken(node.UnderscoreToken); + Assert.Equal(node, newNode); + } + [Fact] public void TestDeclarationPatternFactoryAndProperties() { @@ -14680,6 +14736,32 @@ public void TestWhenClauseIdentityRewriter() Assert.Same(oldNode, newNode); } + [Fact] + public void TestDiscardPatternTokenDeleteRewriter() + { + var oldNode = GenerateDiscardPattern(); + var rewriter = new TokenDeleteRewriter(); + var newNode = rewriter.Visit(oldNode); + + if(!oldNode.IsMissing) + { + Assert.NotEqual(oldNode, newNode); + } + + Assert.NotNull(newNode); + Assert.True(newNode.IsMissing, "No tokens => missing"); + } + + [Fact] + public void TestDiscardPatternIdentityRewriter() + { + var oldNode = GenerateDiscardPattern(); + var rewriter = new IdentityRewriter(); + var newNode = rewriter.Visit(oldNode); + + Assert.Same(oldNode, newNode); + } + [Fact] public void TestDeclarationPatternTokenDeleteRewriter() { diff --git a/src/Compilers/Core/Portable/Generated/Operations.xml.Generated.cs b/src/Compilers/Core/Portable/Generated/Operations.xml.Generated.cs index cc9fca46a57de8f1ea50fdb91ce2fed300ff4b6e..9025429aa5501bb2d7831f6d4fecb2b1b742dab4 100644 --- a/src/Compilers/Core/Portable/Generated/Operations.xml.Generated.cs +++ b/src/Compilers/Core/Portable/Generated/Operations.xml.Generated.cs @@ -1,5 +1,7 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Despite its name, this file IS NOT (yet) automatically generated. + using System; using System.Collections.Generic; using System.Collections.Immutable; @@ -5785,6 +5787,64 @@ public override void Accept(OperationVisitor visitor) } } + /// + /// Represents a C# discard pattern. + /// + internal sealed partial class DiscardPattern : Operation, IDiscardPattern + { + public DiscardPattern(SemanticModel semanticModel, SyntaxNode syntax, ITypeSymbol type, Optional constantValue, bool isImplicit) : + base(OperationKind.DeclarationPattern, semanticModel, syntax, type, constantValue, isImplicit) + { + } + public override IEnumerable Children + { + get + { + yield break; + } + } + public override void Accept(OperationVisitor visitor) + { + visitor.VisitDiscardPattern(this); + } + public override TResult Accept(OperationVisitor visitor, TArgument argument) + { + return visitor.VisitDiscardPattern(this, argument); + } + } + + /// + /// Represents a C# declaration pattern. + /// + internal sealed partial class RecursivePattern : Operation, IRecursivePattern + { + public RecursivePattern(ISymbol declaredSymbol, SemanticModel semanticModel, SyntaxNode syntax, ITypeSymbol type, Optional constantValue, bool isImplicit) : + base(OperationKind.DeclarationPattern, semanticModel, syntax, type, constantValue, isImplicit) + { + DeclaredSymbol = declaredSymbol; + } + /// + /// Symbol declared by the pattern. + /// + public ISymbol DeclaredSymbol { get; } + public override IEnumerable Children + { + // PROTOTYPE(patterns2): Need to define what else is needed to support the IOperation framework + get + { + yield break; + } + } + public override void Accept(OperationVisitor visitor) + { + visitor.VisitRecursivePattern(this); + } + public override TResult Accept(OperationVisitor visitor, TArgument argument) + { + return visitor.VisitRecursivePattern(this, argument); + } + } + /// /// Represents a C# pattern case clause. /// diff --git a/src/Compilers/Core/Portable/Operations/IDiscardPattern.cs b/src/Compilers/Core/Portable/Operations/IDiscardPattern.cs new file mode 100644 index 0000000000000000000000000000000000000000..f1002135878f7253a208845093e22bfe7669ad0a --- /dev/null +++ b/src/Compilers/Core/Portable/Operations/IDiscardPattern.cs @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.CodeAnalysis.Semantics +{ + /// + /// Represents a C# discard pattern. + /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// + public interface IDiscardPattern : IPattern + { + } +} + diff --git a/src/Compilers/Core/Portable/Operations/IRecursivePattern.cs b/src/Compilers/Core/Portable/Operations/IRecursivePattern.cs new file mode 100644 index 0000000000000000000000000000000000000000..44ce41ff2f02142badade044250a4239159fd6f6 --- /dev/null +++ b/src/Compilers/Core/Portable/Operations/IRecursivePattern.cs @@ -0,0 +1,24 @@ +// 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 System.Collections.Immutable; + +namespace Microsoft.CodeAnalysis.Semantics +{ + /// + /// Represents a C# recursive pattern. + /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// + public interface IRecursivePattern : IPattern + { + /// + /// Symbol declared by the pattern. + /// + ISymbol DeclaredSymbol { get; } + + // PROTOTYPE(patterns2): Need to define what else is needed for IOperation. + } +} + diff --git a/src/Compilers/Core/Portable/Operations/OperationCloner.cs b/src/Compilers/Core/Portable/Operations/OperationCloner.cs index 0366e4dec8f85ff471444561b507c975b1a41a16..0fc7edb075b4035c4cae16989e6e40de5305412e 100644 --- a/src/Compilers/Core/Portable/Operations/OperationCloner.cs +++ b/src/Compilers/Core/Portable/Operations/OperationCloner.cs @@ -486,6 +486,16 @@ public override IOperation VisitDeclarationPattern(IDeclarationPattern operation return new DeclarationPattern(operation.DeclaredSymbol, ((Operation)operation).SemanticModel, operation.Syntax, operation.Type, operation.ConstantValue, operation.IsImplicit); } + public override IOperation VisitDiscardPattern(IDiscardPattern operation, object argument) + { + return new DiscardPattern(((Operation)operation).SemanticModel, operation.Syntax, operation.Type, operation.ConstantValue, operation.IsImplicit); + } + + public override IOperation VisitRecursivePattern(IRecursivePattern operation, object argument) + { + return new RecursivePattern(operation.DeclaredSymbol, ((Operation)operation).SemanticModel, operation.Syntax, operation.Type, operation.ConstantValue, operation.IsImplicit); + } + public override IOperation VisitPatternCaseClause(IPatternCaseClause operation, object argument) { return new PatternCaseClause(operation.Label, Visit(operation.Pattern), Visit(operation.GuardExpression), ((Operation)operation).SemanticModel, operation.Syntax, operation.Type, operation.ConstantValue, operation.IsImplicit); diff --git a/src/Compilers/Core/Portable/Operations/OperationVisitor.cs b/src/Compilers/Core/Portable/Operations/OperationVisitor.cs index ae1e8a1bbfe95c8a4d008555307ffa7c76419cc2..7247e3092fdf1a9d066340aaefd105d9dc6abe7b 100644 --- a/src/Compilers/Core/Portable/Operations/OperationVisitor.cs +++ b/src/Compilers/Core/Portable/Operations/OperationVisitor.cs @@ -490,6 +490,16 @@ public virtual void VisitDeclarationPattern(IDeclarationPattern operation) DefaultVisit(operation); } + public virtual void VisitDiscardPattern(IDiscardPattern operation) + { + DefaultVisit(operation); + } + + public virtual void VisitRecursivePattern(IRecursivePattern operation) + { + DefaultVisit(operation); + } + public virtual void VisitPatternCaseClause(IPatternCaseClause operation) { DefaultVisit(operation); @@ -1005,6 +1015,16 @@ public virtual TResult VisitDeclarationPattern(IDeclarationPattern operation, TA return DefaultVisit(operation, argument); } + public virtual TResult VisitDiscardPattern(IDiscardPattern operation, TArgument argument) + { + return DefaultVisit(operation, argument); + } + + public virtual TResult VisitRecursivePattern(IRecursivePattern operation, TArgument argument) + { + return DefaultVisit(operation, argument); + } + public virtual TResult VisitPatternCaseClause(IPatternCaseClause operation, TArgument argument) { return DefaultVisit(operation, argument); diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index 2771619b84a567d4fc5d0814a297ad1ba114c40f..0aaa8b3eaba22cf789726021739dc557b5d02408 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -283,6 +283,7 @@ Microsoft.CodeAnalysis.Semantics.IDefaultCaseClause Microsoft.CodeAnalysis.Semantics.IDefaultValueExpression Microsoft.CodeAnalysis.Semantics.IDelegateCreationExpression Microsoft.CodeAnalysis.Semantics.IDelegateCreationExpression.Target.get -> Microsoft.CodeAnalysis.IOperation +Microsoft.CodeAnalysis.Semantics.IDiscardPattern Microsoft.CodeAnalysis.Semantics.IDoLoopStatement Microsoft.CodeAnalysis.Semantics.IDoLoopStatement.Condition.get -> Microsoft.CodeAnalysis.IOperation Microsoft.CodeAnalysis.Semantics.IDoLoopStatement.DoLoopKind.get -> Microsoft.CodeAnalysis.Semantics.DoLoopKind @@ -421,6 +422,8 @@ Microsoft.CodeAnalysis.Semantics.IRaiseEventStatement.EventReference.get -> Micr Microsoft.CodeAnalysis.Semantics.IRangeCaseClause Microsoft.CodeAnalysis.Semantics.IRangeCaseClause.MaximumValue.get -> Microsoft.CodeAnalysis.IOperation Microsoft.CodeAnalysis.Semantics.IRangeCaseClause.MinimumValue.get -> Microsoft.CodeAnalysis.IOperation +Microsoft.CodeAnalysis.Semantics.IRecursivePattern +Microsoft.CodeAnalysis.Semantics.IRecursivePattern.DeclaredSymbol.get -> Microsoft.CodeAnalysis.ISymbol Microsoft.CodeAnalysis.Semantics.IRelationalCaseClause Microsoft.CodeAnalysis.Semantics.IRelationalCaseClause.Relation.get -> Microsoft.CodeAnalysis.Semantics.BinaryOperatorKind Microsoft.CodeAnalysis.Semantics.IRelationalCaseClause.Value.get -> Microsoft.CodeAnalysis.IOperation @@ -554,6 +557,7 @@ virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitDeconstructionAss virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitDefaultCaseClause(Microsoft.CodeAnalysis.Semantics.IDefaultCaseClause operation) -> void virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitDefaultValueExpression(Microsoft.CodeAnalysis.Semantics.IDefaultValueExpression operation) -> void virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitDelegateCreationExpression(Microsoft.CodeAnalysis.Semantics.IDelegateCreationExpression operation) -> void +virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitDiscardPattern(Microsoft.CodeAnalysis.Semantics.IDiscardPattern operation) -> void virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitDoLoopStatement(Microsoft.CodeAnalysis.Semantics.IDoLoopStatement operation) -> void virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitDynamicIndexerAccessExpression(Microsoft.CodeAnalysis.Semantics.IDynamicIndexerAccessExpression operation) -> void virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitDynamicInvocationExpression(Microsoft.CodeAnalysis.Semantics.IDynamicInvocationExpression operation) -> void @@ -599,6 +603,7 @@ virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitPropertyInitializ virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitPropertyReferenceExpression(Microsoft.CodeAnalysis.Semantics.IPropertyReferenceExpression operation) -> void virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitRaiseEventStatement(Microsoft.CodeAnalysis.Semantics.IRaiseEventStatement operation) -> void virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitRangeCaseClause(Microsoft.CodeAnalysis.Semantics.IRangeCaseClause operation) -> void +virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitRecursivePattern(Microsoft.CodeAnalysis.Semantics.IRecursivePattern operation) -> void virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitRelationalCaseClause(Microsoft.CodeAnalysis.Semantics.IRelationalCaseClause operation) -> void virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitReturnStatement(Microsoft.CodeAnalysis.Semantics.IReturnStatement operation) -> void virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitSimpleAssignmentExpression(Microsoft.CodeAnalysis.Semantics.ISimpleAssignmentExpression operation) -> void @@ -648,6 +653,7 @@ virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.Vi virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitDefaultCaseClause(Microsoft.CodeAnalysis.Semantics.IDefaultCaseClause operation, TArgument argument) -> TResult virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitDefaultValueExpression(Microsoft.CodeAnalysis.Semantics.IDefaultValueExpression operation, TArgument argument) -> TResult virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitDelegateCreationExpression(Microsoft.CodeAnalysis.Semantics.IDelegateCreationExpression operation, TArgument argument) -> TResult +virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitDiscardPattern(Microsoft.CodeAnalysis.Semantics.IDiscardPattern operation, TArgument argument) -> TResult virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitDoLoopStatement(Microsoft.CodeAnalysis.Semantics.IDoLoopStatement operation, TArgument argument) -> TResult virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitDynamicIndexerAccessExpression(Microsoft.CodeAnalysis.Semantics.IDynamicIndexerAccessExpression operation, TArgument argument) -> TResult virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitDynamicInvocationExpression(Microsoft.CodeAnalysis.Semantics.IDynamicInvocationExpression operation, TArgument argument) -> TResult @@ -693,6 +699,7 @@ virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.Vi virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitPropertyReferenceExpression(Microsoft.CodeAnalysis.Semantics.IPropertyReferenceExpression operation, TArgument argument) -> TResult virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitRaiseEventStatement(Microsoft.CodeAnalysis.Semantics.IRaiseEventStatement operation, TArgument argument) -> TResult virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitRangeCaseClause(Microsoft.CodeAnalysis.Semantics.IRangeCaseClause operation, TArgument argument) -> TResult +virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitRecursivePattern(Microsoft.CodeAnalysis.Semantics.IRecursivePattern operation, TArgument argument) -> TResult virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitRelationalCaseClause(Microsoft.CodeAnalysis.Semantics.IRelationalCaseClause operation, TArgument argument) -> TResult virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitReturnStatement(Microsoft.CodeAnalysis.Semantics.IReturnStatement operation, TArgument argument) -> TResult virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitSimpleAssignmentExpression(Microsoft.CodeAnalysis.Semantics.ISimpleAssignmentExpression operation, TArgument argument) -> TResult diff --git a/src/Compilers/Test/Utilities/CSharp/TestOptions.cs b/src/Compilers/Test/Utilities/CSharp/TestOptions.cs index bace46be72fab99fed05b3160c0cd5946d13e7f1..7da99584a07dfde72048e67b23e07c3cc706f8c2 100644 --- a/src/Compilers/Test/Utilities/CSharp/TestOptions.cs +++ b/src/Compilers/Test/Utilities/CSharp/TestOptions.cs @@ -29,7 +29,9 @@ public static class TestOptions public static readonly CSharpParseOptions Regular6WithV7SwitchBinder = Regular6.WithFeatures(new Dictionary() { { "testV7SwitchBinder", "true" } }); public static readonly CSharpParseOptions RegularWithIOperationFeature = Regular.WithIOperationsFeature(); - + + public static readonly CSharpParseOptions RegularWithRecursivePatterns = Regular.WithRecursivePatterns(); + public static readonly CSharpCompilationOptions ReleaseDll = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, optimizationLevel: OptimizationLevel.Release); public static readonly CSharpCompilationOptions ReleaseExe = new CSharpCompilationOptions(OutputKind.ConsoleApplication, optimizationLevel: OptimizationLevel.Release); @@ -111,6 +113,11 @@ public static CSharpParseOptions WithIOperationsFeature(this CSharpParseOptions { return options.WithFeatures(options.Features.Concat(new[] { new KeyValuePair("IOperation", "true") })); } + + public static CSharpParseOptions WithRecursivePatterns(this CSharpParseOptions options) + { + return options.WithFeatures(options.Features.Concat(new[] { new KeyValuePair("patterns2", "true") })); + } } } diff --git a/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterEnterOnTokenTests.cs b/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterEnterOnTokenTests.cs index 5fae1ac2b5b0e5e51dd97ea1772867359172131c..5eea078c14ba6e37effe984fb1998b5bf2611c3a 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterEnterOnTokenTests.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterEnterOnTokenTests.cs @@ -1299,7 +1299,7 @@ static void Main(string[] args) expectedIndentation: 8); } - [WpfFact] + [WpfFact(Skip = "PROTOTYPE(patterns2): need to implement indentation for recursive patterns")] [Trait(Traits.Feature, Traits.Features.SmartIndent)] public async Task IndentPatternPropertyFirst() { diff --git a/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterTests.cs b/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterTests.cs index 9611625ca85bc67ada33530b08b9545fdcd353af..051a61b091bad46a7a2c1182e04fc29bfd1a391c 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterTests.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterTests.cs @@ -2591,7 +2591,7 @@ static void M() expectedIndentation: 8); } - [WpfFact, Trait(Traits.Feature, Traits.Features.SmartIndent)] + [WpfFact(Skip = "PROTOTYPE(patterns2): need to implement indentation for recursive patterns"), Trait(Traits.Feature, Traits.Features.SmartIndent)] public void PatternPropertyIndentFirst() { var code = @" @@ -2634,7 +2634,7 @@ void M(object o) expectedIndentation: 12); } - [WpfFact, Trait(Traits.Feature, Traits.Features.SmartIndent)] + [WpfFact(Skip = "PROTOTYPE(patterns2): need to implement indentation for recursive patterns"), Trait(Traits.Feature, Traits.Features.SmartIndent)] public void PatternPropertyIndentNestedFirst() { var code = @" diff --git a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/DeclarationTests.cs b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/DeclarationTests.cs index deb0180f7d84a731a08888622a38cd61de574581..f235f053f0109378f337caf220b66b0db2c6a8fa 100644 --- a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/DeclarationTests.cs +++ b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/DeclarationTests.cs @@ -309,14 +309,13 @@ static void Test(bool x) Assert.Equal(flags, DkmClrCompilationResultFlags.PotentialSideEffect | DkmClrCompilationResultFlags.ReadOnlyResult); testData.GetMethodData("<>x.<>m0").VerifyIL( @"{ - // Code size 77 (0x4d) + // Code size 72 (0x48) .maxstack 4 .locals init (object V_0, //y bool V_1, object V_2, System.Guid V_3, - bool V_4, - object V_5) + int V_4) IL_0000: ldtoken ""int"" IL_0005: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" IL_000a: ldstr ""z"" @@ -326,24 +325,22 @@ .maxstack 4 IL_0018: ldnull IL_0019: call ""void Microsoft.VisualStudio.Debugger.Clr.IntrinsicMethods.CreateVariable(System.Type, string, System.Guid, byte[])"" IL_001e: ldarg.0 - IL_001f: stloc.s V_5 - IL_0021: ldstr ""z"" - IL_0026: call ""int Microsoft.VisualStudio.Debugger.Clr.IntrinsicMethods.GetVariableAddress(string)"" - IL_002b: ldloc.s V_5 - IL_002d: isinst ""int"" - IL_0032: ldnull - IL_0033: cgt.un - IL_0035: dup - IL_0036: stloc.s V_4 - IL_0038: brtrue.s IL_003d - IL_003a: ldc.i4.0 - IL_003b: br.s IL_0044 - IL_003d: ldloc.s V_5 - IL_003f: unbox.any ""int"" - IL_0044: stind.i4 - IL_0045: ldloc.s V_4 - IL_0047: call ""void C.Test(bool)"" - IL_004c: ret + IL_001f: brfalse.s IL_0041 + IL_0021: ldarg.0 + IL_0022: isinst ""int"" + IL_0027: brfalse.s IL_0041 + IL_0029: ldarg.0 + IL_002a: unbox.any ""int"" + IL_002f: stloc.s V_4 + IL_0031: ldstr ""z"" + IL_0036: call ""int Microsoft.VisualStudio.Debugger.Clr.IntrinsicMethods.GetVariableAddress(string)"" + IL_003b: ldloc.s V_4 + IL_003d: stind.i4 + IL_003e: ldc.i4.1 + IL_003f: br.s IL_0042 + IL_0041: ldc.i4.0 + IL_0042: call ""void C.Test(bool)"" + IL_0047: ret }"); }); } @@ -1778,14 +1775,13 @@ static object Test(bool x) context.CompileAssignment("x", "Test(x is int i)", out error, testData); testData.GetMethodData("<>x.<>m0").VerifyIL( @"{ - // Code size 79 (0x4f) + // Code size 74 (0x4a) .maxstack 4 .locals init (object V_0, //y bool V_1, object V_2, System.Guid V_3, - bool V_4, - object V_5) + int V_4) IL_0000: ldtoken ""int"" IL_0005: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" IL_000a: ldstr ""i"" @@ -1795,25 +1791,23 @@ .maxstack 4 IL_0018: ldnull IL_0019: call ""void Microsoft.VisualStudio.Debugger.Clr.IntrinsicMethods.CreateVariable(System.Type, string, System.Guid, byte[])"" IL_001e: ldarg.0 - IL_001f: stloc.s V_5 - IL_0021: ldstr ""i"" - IL_0026: call ""int Microsoft.VisualStudio.Debugger.Clr.IntrinsicMethods.GetVariableAddress(string)"" - IL_002b: ldloc.s V_5 - IL_002d: isinst ""int"" - IL_0032: ldnull - IL_0033: cgt.un - IL_0035: dup - IL_0036: stloc.s V_4 - IL_0038: brtrue.s IL_003d - IL_003a: ldc.i4.0 - IL_003b: br.s IL_0044 - IL_003d: ldloc.s V_5 - IL_003f: unbox.any ""int"" - IL_0044: stind.i4 - IL_0045: ldloc.s V_4 - IL_0047: call ""object C.Test(bool)"" - IL_004c: starg.s V_0 - IL_004e: ret + IL_001f: brfalse.s IL_0041 + IL_0021: ldarg.0 + IL_0022: isinst ""int"" + IL_0027: brfalse.s IL_0041 + IL_0029: ldarg.0 + IL_002a: unbox.any ""int"" + IL_002f: stloc.s V_4 + IL_0031: ldstr ""i"" + IL_0036: call ""int Microsoft.VisualStudio.Debugger.Clr.IntrinsicMethods.GetVariableAddress(string)"" + IL_003b: ldloc.s V_4 + IL_003d: stind.i4 + IL_003e: ldc.i4.1 + IL_003f: br.s IL_0042 + IL_0041: ldc.i4.0 + IL_0042: call ""object C.Test(bool)"" + IL_0047: starg.s V_0 + IL_0049: ret }"); }); } @@ -1851,7 +1845,7 @@ static object Test(bool x) context.CompileAssignment("x", "Test(x is string i)", out error, testData); testData.GetMethodData("<>x.<>m0").VerifyIL( @"{ - // Code size 63 (0x3f) + // Code size 74 (0x4a) .maxstack 4 .locals init (object V_0, //y bool V_1, @@ -1866,19 +1860,24 @@ .maxstack 4 IL_0017: ldloc.3 IL_0018: ldnull IL_0019: call ""void Microsoft.VisualStudio.Debugger.Clr.IntrinsicMethods.CreateVariable(System.Type, string, System.Guid, byte[])"" - IL_001e: ldstr ""i"" - IL_0023: call ""string Microsoft.VisualStudio.Debugger.Clr.IntrinsicMethods.GetVariableAddress(string)"" - IL_0028: ldarg.0 - IL_0029: isinst ""string"" - IL_002e: dup + IL_001e: ldarg.0 + IL_001f: brfalse.s IL_0041 + IL_0021: ldarg.0 + IL_0022: isinst ""string"" + IL_0027: brfalse.s IL_0041 + IL_0029: ldarg.0 + IL_002a: castclass ""string"" IL_002f: stloc.s V_4 - IL_0031: stind.ref - IL_0032: ldloc.s V_4 - IL_0034: ldnull - IL_0035: cgt.un - IL_0037: call ""object C.Test(bool)"" - IL_003c: starg.s V_0 - IL_003e: ret + IL_0031: ldstr ""i"" + IL_0036: call ""string Microsoft.VisualStudio.Debugger.Clr.IntrinsicMethods.GetVariableAddress(string)"" + IL_003b: ldloc.s V_4 + IL_003d: stind.ref + IL_003e: ldc.i4.1 + IL_003f: br.s IL_0042 + IL_0041: ldc.i4.0 + IL_0042: call ""object C.Test(bool)"" + IL_0047: starg.s V_0 + IL_0049: ret }"); }); } @@ -1916,13 +1915,12 @@ static object Test(bool x) context.CompileAssignment("x", "Test(x is object i)", out error, testData); testData.GetMethodData("<>x.<>m0").VerifyIL( @"{ - // Code size 58 (0x3a) + // Code size 57 (0x39) .maxstack 4 .locals init (object V_0, //y bool V_1, object V_2, - System.Guid V_3, - object V_4) + System.Guid V_3) IL_0000: ldtoken ""object"" IL_0005: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" IL_000a: ldstr ""i"" @@ -1931,18 +1929,18 @@ .maxstack 4 IL_0017: ldloc.3 IL_0018: ldnull IL_0019: call ""void Microsoft.VisualStudio.Debugger.Clr.IntrinsicMethods.CreateVariable(System.Type, string, System.Guid, byte[])"" - IL_001e: ldstr ""i"" - IL_0023: call ""object Microsoft.VisualStudio.Debugger.Clr.IntrinsicMethods.GetVariableAddress(string)"" - IL_0028: ldarg.0 - IL_0029: dup - IL_002a: stloc.s V_4 + IL_001e: ldarg.0 + IL_001f: brfalse.s IL_0030 + IL_0021: ldstr ""i"" + IL_0026: call ""object Microsoft.VisualStudio.Debugger.Clr.IntrinsicMethods.GetVariableAddress(string)"" + IL_002b: ldarg.0 IL_002c: stind.ref - IL_002d: ldloc.s V_4 - IL_002f: ldnull - IL_0030: cgt.un - IL_0032: call ""object C.Test(bool)"" - IL_0037: starg.s V_0 - IL_0039: ret + IL_002d: ldc.i4.1 + IL_002e: br.s IL_0031 + IL_0030: ldc.i4.0 + IL_0031: call ""object C.Test(bool)"" + IL_0036: starg.s V_0 + IL_0038: ret }"); }); } @@ -2039,7 +2037,7 @@ static void M(int? x) context.CompileAssignment("x", "Test(x is int i)", out error, testData); testData.GetMethodData("<>x.<>m0").VerifyIL( @"{ - // Code size 67 (0x43) + // Code size 74 (0x4a) .maxstack 4 .locals init (object V_0, //y bool V_1, @@ -2047,7 +2045,7 @@ .maxstack 4 int V_3, object V_4, System.Guid V_5, - int? V_6) + int V_6) IL_0000: ldtoken ""int"" IL_0005: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" IL_000a: ldstr ""i"" @@ -2056,18 +2054,22 @@ .maxstack 4 IL_0017: ldloc.s V_5 IL_0019: ldnull IL_001a: call ""void Microsoft.VisualStudio.Debugger.Clr.IntrinsicMethods.CreateVariable(System.Type, string, System.Guid, byte[])"" - IL_001f: ldarg.0 - IL_0020: stloc.s V_6 - IL_0022: ldstr ""i"" - IL_0027: call ""int Microsoft.VisualStudio.Debugger.Clr.IntrinsicMethods.GetVariableAddress(string)"" - IL_002c: ldloca.s V_6 - IL_002e: call ""int int?.GetValueOrDefault()"" - IL_0033: stind.i4 - IL_0034: ldloca.s V_6 - IL_0036: call ""bool int?.HasValue.get"" - IL_003b: call ""int? C.Test(bool)"" - IL_0040: starg.s V_0 - IL_0042: ret + IL_001f: ldarga.s V_0 + IL_0021: call ""bool int?.HasValue.get"" + IL_0026: brfalse.s IL_0041 + IL_0028: ldarga.s V_0 + IL_002a: call ""int int?.Value.get"" + IL_002f: stloc.s V_6 + IL_0031: ldstr ""i"" + IL_0036: call ""int Microsoft.VisualStudio.Debugger.Clr.IntrinsicMethods.GetVariableAddress(string)"" + IL_003b: ldloc.s V_6 + IL_003d: stind.i4 + IL_003e: ldc.i4.1 + IL_003f: br.s IL_0042 + IL_0041: ldc.i4.0 + IL_0042: call ""int? C.Test(bool)"" + IL_0047: starg.s V_0 + IL_0049: ret }"); }); } @@ -2105,14 +2107,13 @@ static int Test(bool y) Assert.Equal(flags, DkmClrCompilationResultFlags.PotentialSideEffect | DkmClrCompilationResultFlags.ReadOnlyResult); testData.GetMethodData("<>x.<>m0").VerifyIL( @"{ - // Code size 118 (0x76) + // Code size 113 (0x71) .maxstack 4 .locals init (object V_0, //y bool V_1, object V_2, System.Guid V_3, - bool V_4, - object V_5) + int V_4) IL_0000: ldtoken ""int"" IL_0005: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" IL_000a: ldstr ""z"" @@ -2132,25 +2133,23 @@ .maxstack 4 IL_003c: ldstr ""z"" IL_0041: call ""int Microsoft.VisualStudio.Debugger.Clr.IntrinsicMethods.GetVariableAddress(string)"" IL_0046: ldarg.0 - IL_0047: stloc.s V_5 - IL_0049: ldstr ""i"" - IL_004e: call ""int Microsoft.VisualStudio.Debugger.Clr.IntrinsicMethods.GetVariableAddress(string)"" - IL_0053: ldloc.s V_5 - IL_0055: isinst ""int"" - IL_005a: ldnull - IL_005b: cgt.un - IL_005d: dup - IL_005e: stloc.s V_4 - IL_0060: brtrue.s IL_0065 - IL_0062: ldc.i4.0 - IL_0063: br.s IL_006c - IL_0065: ldloc.s V_5 - IL_0067: unbox.any ""int"" - IL_006c: stind.i4 - IL_006d: ldloc.s V_4 - IL_006f: call ""int C.Test(bool)"" - IL_0074: stind.i4 - IL_0075: ret + IL_0047: brfalse.s IL_0069 + IL_0049: ldarg.0 + IL_004a: isinst ""int"" + IL_004f: brfalse.s IL_0069 + IL_0051: ldarg.0 + IL_0052: unbox.any ""int"" + IL_0057: stloc.s V_4 + IL_0059: ldstr ""i"" + IL_005e: call ""int Microsoft.VisualStudio.Debugger.Clr.IntrinsicMethods.GetVariableAddress(string)"" + IL_0063: ldloc.s V_4 + IL_0065: stind.i4 + IL_0066: ldc.i4.1 + IL_0067: br.s IL_006a + IL_0069: ldc.i4.0 + IL_006a: call ""int C.Test(bool)"" + IL_006f: stind.i4 + IL_0070: ret }"); }); } diff --git a/src/Test/Utilities/Portable/TestResource.resx b/src/Test/Utilities/Portable/TestResource.resx index fd8efa6623b2afd0ce3b8314230c40f72852cd53..b693b8086b4b1c5c9c70a33af6ffc157ef566a89 100644 --- a/src/Test/Utilities/Portable/TestResource.resx +++ b/src/Test/Utilities/Portable/TestResource.resx @@ -324,7 +324,7 @@ namespace My break; case int i: break; - case Type (int x, 3) { A: 5, B: 7 } identifier: + case Type (int x, 3) { A: _, B: 7 } identifier: break; default: {