/// <summary> /// Find any deconstruction locals that are still pending inference and fail their inference. /// </summary> private void FailRemainingInferences(ArrayBuilder <DeconstructionVariable> variables, DiagnosticBag diagnostics) { int count = variables.Count; for (int i = 0; i < count; i++) { var variable = variables[i]; if (variable.HasNestedVariables) { FailRemainingInferences(variable.NestedVariables, diagnostics); } else { switch (variable.Single.Kind) { case BoundKind.DeconstructionVariablePendingInference: BoundExpression local = ((DeconstructionVariablePendingInference)variable.Single).FailInference(this, diagnostics); variables[i] = new DeconstructionVariable(local, local.Syntax); break; case BoundKind.DiscardExpression: var pending = (BoundDiscardExpression)variable.Single; if ((object)pending.Type == null) { Error(diagnostics, ErrorCode.ERR_TypeInferenceFailedForImplicitlyTypedDeconstructionVariable, pending.Syntax, "_"); variables[i] = new DeconstructionVariable(pending.FailInference(this, diagnostics), pending.Syntax); } break; } // at this point we expect to have a type for every lvalue Debug.Assert((object)variables[i].Single.Type != null); } } }
/// <summary> /// Prepares locals corresponding to the variables of the declaration. /// The locals are kept in a tree which captures the nesting of variables. /// Each local is either a simple local (when its type is known) or a deconstruction local pending inference. /// The caller is responsible for releasing the nested ArrayBuilders. /// </summary> private ArrayBuilder <DeconstructionVariable> BindDeconstructionDeclarationLocals(VariableDeclarationSyntax node, TypeSyntax closestTypeSyntax, DiagnosticBag diagnostics) { Debug.Assert(node.IsDeconstructionDeclaration); SeparatedSyntaxList <VariableDeclarationSyntax> variables = node.Deconstruction.Variables; // There are four cases for VariableDeclaration: // - type and declarators are set, but deconstruction is null. This could represent `int x`, which is a single variable. // - type is null, declarators are set, but deconstruction is null. This could represent `x`, which is a single variable. // - type is set to 'var', declarators are null, and deconstruction is set. This could represent `var (...)` // - type and declarators are null, but deconstruction is set. This could represent `(int x, ...)` var localsBuilder = ArrayBuilder <DeconstructionVariable> .GetInstance(variables.Count); foreach (var variable in variables) { TypeSyntax typeSyntax = variable.Type ?? closestTypeSyntax; DeconstructionVariable local; if (variable.IsDeconstructionDeclaration) { local = new DeconstructionVariable(BindDeconstructionDeclarationLocals(variable, typeSyntax, diagnostics), variable.Deconstruction); } else { local = new DeconstructionVariable(BindDeconstructionDeclarationLocal(variable, typeSyntax, diagnostics), variable); } localsBuilder.Add(local); } return(localsBuilder); }
/// <summary> /// Inform the variables about found types. /// </summary> private void SetInferredTypes(ArrayBuilder <DeconstructionVariable> variables, ImmutableArray <TypeSymbol> foundTypes, DiagnosticBag diagnostics) { var matchCount = Math.Min(variables.Count, foundTypes.Length); for (int i = 0; i < matchCount; i++) { var variable = variables[i]; if (!variable.HasNestedVariables) { switch (variable.Single.Kind) { case BoundKind.DeconstructionVariablePendingInference: { var pending = (DeconstructionVariablePendingInference)variable.Single; BoundExpression local = pending.SetInferredType(foundTypes[i], this, diagnostics); variables[i] = new DeconstructionVariable(local, local.Syntax); } break; case BoundKind.DiscardedExpression: { var pending = (BoundDiscardedExpression)variable.Single; if ((object)pending.Type == null) { variables[i] = new DeconstructionVariable(pending.SetInferredType(foundTypes[i]), pending.Syntax); } } break; } } } }
/// <summary> /// Bind a deconstruction assignment. /// </summary> /// <param name="deconstruction">The deconstruction operation</param> /// <param name="left">The left (tuple) operand</param> /// <param name="right">The right (deconstructable) operand</param> /// <param name="diagnostics">Where to report diagnostics</param> /// <param name="declaration">A variable set to the first variable declaration found in the left</param> /// <param name="expression">A variable set to the first expression in the left that isn't a declaration or discard</param> /// <param name="resultIsUsedOverride">The expression evaluator needs to bind deconstructions (both assignments and declarations) as expression-statements /// and still access the returned value</param> /// <param name="rightPlaceholder"></param> /// <returns></returns> internal BoundDeconstructionAssignmentOperator BindDeconstruction( CSharpSyntaxNode deconstruction, ExpressionSyntax left, ExpressionSyntax right, DiagnosticBag diagnostics, ref DeclarationExpressionSyntax declaration, ref ExpressionSyntax expression, bool resultIsUsedOverride = false, BoundDeconstructValuePlaceholder rightPlaceholder = null) { DeconstructionVariable locals = BindDeconstructionVariables(left, diagnostics, ref declaration, ref expression); Debug.Assert(locals.HasNestedVariables); var deconstructionDiagnostics = new DiagnosticBag(); BoundExpression boundRight = rightPlaceholder ?? BindValue(right, deconstructionDiagnostics, BindValueKind.RValue); boundRight = FixTupleLiteral(locals.NestedVariables, boundRight, deconstruction, deconstructionDiagnostics); bool resultIsUsed = resultIsUsedOverride || IsDeconstructionResultUsed(left); var assignment = BindDeconstructionAssignment(deconstruction, left, boundRight, locals.NestedVariables, resultIsUsed, deconstructionDiagnostics); DeconstructionVariable.FreeDeconstructionVariables(locals.NestedVariables); diagnostics.AddRange(deconstructionDiagnostics); return(assignment); }
internal BoundDeconstructionAssignmentOperator BindDeconstructionDeclaration(CSharpSyntaxNode node, VariableComponentSyntax declaration, ExpressionSyntax right, DiagnosticBag diagnostics, BoundDeconstructValuePlaceholder rightPlaceholder = null) { DeconstructionVariable locals = BindDeconstructionDeclarationLocals(declaration, diagnostics); Debug.Assert(locals.HasNestedVariables); var result = BindDeconstructionAssignment(node, right, locals.NestedVariables, diagnostics, isDeclaration: true, rhsPlaceholder: rightPlaceholder); FreeDeconstructionVariables(locals.NestedVariables); return(result); }
/// <summary> /// Inform the variables about found types. /// </summary> private static void SetInferredTypes(ArrayBuilder <DeconstructionVariable> variables, ImmutableArray <TypeSymbol> foundTypes) { var matchCount = Math.Min(variables.Count, foundTypes.Length); for (int i = 0; i < matchCount; i++) { var variable = variables[i]; if (!variable.HasNestedVariables && variable.Single.Kind == BoundKind.DeconstructionLocalPendingInference) { BoundLocal local = ((DeconstructionLocalPendingInference)variable.Single).SetInferredType(foundTypes[i], success: true); variables[i] = new DeconstructionVariable(local, local.Syntax); } } }
/// <summary> /// Inform the variables about found types. /// </summary> private void SetInferredTypes(ArrayBuilder <DeconstructionVariable> variables, ImmutableArray <TypeSymbol> foundTypes, DiagnosticBag diagnostics) { var matchCount = Math.Min(variables.Count, foundTypes.Length); for (int i = 0; i < matchCount; i++) { var variable = variables[i]; if (!variable.HasNestedVariables && variable.Single.Kind == BoundKind.DeconstructionVariablePendingInference) { BoundExpression local = ((DeconstructionVariablePendingInference)variable.Single).SetInferredType(foundTypes[i], this, diagnostics); variables[i] = new DeconstructionVariable(local, local.Syntax); } } }
/// <summary> /// Bind a deconstruction assignment. /// </summary> /// <param name="deconstruction">The deconstruction operation</param> /// <param name="left">The left (tuple) operand</param> /// <param name="right">The right (deconstrucible) operand</param> /// <param name="diagnostics">Where to report diagnostics</param> /// <param name="declaration">A variable set to the first variable declaration found in the left</param> /// <param name="expression">A variable set to the first expression in the left that isn't a declaration or discard</param> /// <param name="rightPlaceholder"></param> /// <returns></returns> internal BoundDeconstructionAssignmentOperator BindDeconstruction( CSharpSyntaxNode deconstruction, ExpressionSyntax left, ExpressionSyntax right, DiagnosticBag diagnostics, ref DeclarationExpressionSyntax declaration, ref ExpressionSyntax expression, BoundDeconstructValuePlaceholder rightPlaceholder = null) { DeconstructionVariable locals = BindDeconstructionVariables(left, diagnostics, ref declaration, ref expression); Debug.Assert(locals.HasNestedVariables); var result = BindDeconstructionAssignment(deconstruction, left, right, locals.NestedVariables, diagnostics, rhsPlaceholder: rightPlaceholder); FreeDeconstructionVariables(locals.NestedVariables); return(result); }
private void FailRemainingInferencesAndSetValEscape(ArrayBuilder <DeconstructionVariable> variables, DiagnosticBag diagnostics, uint rhsValEscape) { int count = variables.Count; for (int i = 0; i < count; i++) { var variable = variables[i]; if (variable.NestedVariables is object) { FailRemainingInferencesAndSetValEscape(variable.NestedVariables, diagnostics, rhsValEscape); } else { Debug.Assert(variable.Single is object); switch (variable.Single.Kind) { case BoundKind.Local: var local = (BoundLocal)variable.Single; if (local.DeclarationKind != BoundLocalDeclarationKind.None) { ((SourceLocalSymbol)local.LocalSymbol).SetValEscape(rhsValEscape); } break; case BoundKind.DeconstructionVariablePendingInference: BoundExpression errorLocal = ((DeconstructionVariablePendingInference)variable.Single).FailInference(this, diagnostics); variables[i] = new DeconstructionVariable(errorLocal, errorLocal.Syntax); break; case BoundKind.DiscardExpression: var pending = (BoundDiscardExpression)variable.Single; if ((object?)pending.Type == null) { Error(diagnostics, ErrorCode.ERR_TypeInferenceFailedForImplicitlyTypedDeconstructionVariable, pending.Syntax, "_"); variables[i] = new DeconstructionVariable(pending.FailInference(this, diagnostics), pending.Syntax); } break; } // at this point we expect to have a type for every lvalue Debug.Assert((object?)variables[i].Single !.Type != null); } } }
private void SetInferredTypes(ArrayBuilder <DeconstructionVariable> variables, ImmutableArray <TypeSymbol> foundTypes, DiagnosticBag diagnostics) { var matchCount = Math.Min(variables.Count, foundTypes.Length); for (int i = 0; i < matchCount; i++) { var variable = variables[i]; if (variable.Single is { } pending) { if ((object?)pending.Type != null) { continue; } variables[i] = new DeconstructionVariable(SetInferredType(pending, foundTypes[i], diagnostics), variable.Syntax); } } }
/// <summary> /// Find any deconstruction locals that are still pending inference and fail their inference. /// </summary> private void FailRemainingInferences(ArrayBuilder <DeconstructionVariable> variables, DiagnosticBag diagnostics) { var count = variables.Count; for (int i = 0; i < count; i++) { var variable = variables[i]; if (variable.HasNestedVariables) { FailRemainingInferences(variable.NestedVariables, diagnostics); } else { if (variable.Single.Kind == BoundKind.DeconstructionLocalPendingInference) { var local = ((DeconstructionLocalPendingInference)variable.Single).FailInference(this); variables[i] = new DeconstructionVariable(local, local.Syntax); } } } }
/// <summary> /// Inform the variables about found types. /// </summary> private void SetInferredTypes(ArrayBuilder<DeconstructionVariable> variables, ImmutableArray<TypeSymbol> foundTypes, DiagnosticBag diagnostics) { var matchCount = Math.Min(variables.Count, foundTypes.Length); for (int i = 0; i < matchCount; i++) { var variable = variables[i]; if (!variable.HasNestedVariables) { var pending = variable.Single; if ((object)pending.Type != null) { continue; } variables[i] = new DeconstructionVariable(SetInferredType(pending, foundTypes[i], diagnostics), pending.Syntax); } } }
/// <summary> /// Find any deconstruction locals that are still pending inference and fail their inference. /// </summary> private void FailRemainingInferences(ArrayBuilder<DeconstructionVariable> variables, DiagnosticBag diagnostics) { var count = variables.Count; for (int i = 0; i < count; i++) { var variable = variables[i]; if (variable.HasNestedVariables) { FailRemainingInferences(variable.NestedVariables, diagnostics); } else { if (variable.Single.Kind == BoundKind.DeconstructionLocalPendingInference) { var local = ((DeconstructionLocalPendingInference)variable.Single).FailInference(this); variables[i] = new DeconstructionVariable(local, local.Syntax); } } } }
/// <summary> /// Prepares locals corresponding to the variables of the declaration. /// The locals are kept in a tree which captures the nesting of variables. /// Each local is either a simple local (when its type is known) or a deconstruction local pending inference. /// The caller is responsible for releasing the nested ArrayBuilders. /// </summary> private ArrayBuilder<DeconstructionVariable> BindDeconstructionDeclarationLocals(VariableDeclarationSyntax node, TypeSyntax closestTypeSyntax, DiagnosticBag diagnostics) { Debug.Assert(node.IsDeconstructionDeclaration); SeparatedSyntaxList<VariableDeclarationSyntax> variables = node.Deconstruction.Variables; // There are four cases for VariableDeclaration: // - type and declarators are set, but deconstruction is null. This could represent `int x`, which is a single variable. // - type is null, declarators are set, but deconstruction is null. This could represent `x`, which is a single variable. // - type is set to 'var', declarators are null, and deconstruction is set. This could represent `var (...)` // - type and declarators are null, but deconstruction is set. This could represent `(int x, ...)` var localsBuilder = ArrayBuilder<DeconstructionVariable>.GetInstance(variables.Count); foreach (var variable in variables) { TypeSyntax typeSyntax = variable.Type ?? closestTypeSyntax; DeconstructionVariable local; if (variable.IsDeconstructionDeclaration) { local = new DeconstructionVariable(BindDeconstructionDeclarationLocals(variable, typeSyntax, diagnostics), node.Deconstruction); } else { local = new DeconstructionVariable(BindDeconstructionDeclarationLocal(variable, typeSyntax, diagnostics)); } localsBuilder.Add(local); } return localsBuilder; }
/// <summary> /// Inform the variables about found types. /// </summary> private void SetInferredTypes(ArrayBuilder<DeconstructionVariable> variables, ImmutableArray<TypeSymbol> foundTypes, DiagnosticBag diagnostics) { var matchCount = Math.Min(variables.Count, foundTypes.Length); for (int i = 0; i < matchCount; i++) { var variable = variables[i]; if (!variable.HasNestedVariables && variable.Single.Kind == BoundKind.DeconstructionVariablePendingInference) { BoundExpression local = ((DeconstructionVariablePendingInference)variable.Single).SetInferredType(foundTypes[i], this, diagnostics); variables[i] = new DeconstructionVariable(local, local.Syntax); } } }
/// <summary> /// Find any deconstruction locals that are still pending inference and fail their inference. /// </summary> private void FailRemainingInferences(ArrayBuilder<DeconstructionVariable> variables, DiagnosticBag diagnostics) { int count = variables.Count; for (int i = 0; i < count; i++) { var variable = variables[i]; if (variable.HasNestedVariables) { FailRemainingInferences(variable.NestedVariables, diagnostics); } else { switch (variable.Single.Kind) { case BoundKind.DeconstructionVariablePendingInference: BoundExpression local = ((DeconstructionVariablePendingInference)variable.Single).FailInference(this, diagnostics); variables[i] = new DeconstructionVariable(local, local.Syntax); break; case BoundKind.DiscardExpression: var pending = (BoundDiscardExpression)variable.Single; if ((object)pending.Type == null) { Error(diagnostics, ErrorCode.ERR_TypeInferenceFailedForImplicitlyTypedDeconstructionVariable, pending.Syntax, "_"); variables[i] = new DeconstructionVariable(pending.FailInference(this, diagnostics), pending.Syntax); } break; } // at this point we expect to have a type for every lvalue Debug.Assert((object)variables[i].Single.Type != null); } } }
/// <summary> /// Inform the variables about found types. /// </summary> private static void SetInferredTypes(ArrayBuilder<DeconstructionVariable> variables, ImmutableArray<TypeSymbol> foundTypes) { var matchCount = Math.Min(variables.Count, foundTypes.Length); for (int i = 0; i < matchCount; i++) { var variable = variables[i]; if (!variable.HasNestedVariables && variable.Single.Kind == BoundKind.DeconstructionLocalPendingInference) { BoundLocal local = ((DeconstructionLocalPendingInference)variable.Single).SetInferredType(foundTypes[i], success: true); variables[i] = new DeconstructionVariable(local, local.Syntax); } } }
/// <summary> /// Inform the variables about found types. /// </summary> private void SetInferredTypes(ArrayBuilder<DeconstructionVariable> variables, ImmutableArray<TypeSymbol> foundTypes, DiagnosticBag diagnostics) { var matchCount = Math.Min(variables.Count, foundTypes.Length); for (int i = 0; i < matchCount; i++) { var variable = variables[i]; if (!variable.HasNestedVariables) { switch (variable.Single.Kind) { case BoundKind.DeconstructionVariablePendingInference: { var pending = (DeconstructionVariablePendingInference)variable.Single; BoundExpression local = pending.SetInferredType(foundTypes[i], this, diagnostics); variables[i] = new DeconstructionVariable(local, local.Syntax); } break; case BoundKind.DiscardedExpression: { var pending = (BoundDiscardedExpression)variable.Single; if ((object)pending.Type == null) { variables[i] = new DeconstructionVariable(pending.SetInferredType(foundTypes[i]), pending.Syntax); } } break; } } } }