public BoundDeconstructValuePlaceholder SetInferredType(TypeSymbol type, bool success) { Debug.Assert((object)Placeholder == null); Placeholder = new BoundDeconstructValuePlaceholder(this.Syntax, type, hasErrors: this.HasErrors || !success); return Placeholder; }
/// <summary> /// There are two kinds of deconstruction-assignments which this binding handles: tuple and non-tuple. /// /// Returns a BoundDeconstructionAssignmentOperator with a list of deconstruction steps and assignment steps. /// Deconstruct steps for tuples have no invocation to Deconstruct, but steps for non-tuples do. /// The caller is responsible for releasing all the ArrayBuilders in checkedVariables. /// </summary> private BoundDeconstructionAssignmentOperator BindDeconstructionAssignment( CSharpSyntaxNode node, ExpressionSyntax right, ArrayBuilder <DeconstructionVariable> checkedVariables, DiagnosticBag diagnostics, BoundDeconstructValuePlaceholder rhsPlaceholder = null) { // receiver for first Deconstruct step var boundRHS = rhsPlaceholder ?? BindValue(right, diagnostics, BindValueKind.RValue); boundRHS = FixTupleLiteral(checkedVariables, boundRHS, node, diagnostics); if ((object)boundRHS.Type == null || boundRHS.Type.IsErrorType()) { // we could still not infer a type for the RHS FailRemainingInferences(checkedVariables, diagnostics); return(new BoundDeconstructionAssignmentOperator( node, FlattenDeconstructVariables(checkedVariables), boundRHS, ImmutableArray <BoundDeconstructionDeconstructStep> .Empty, ImmutableArray <BoundDeconstructionAssignmentStep> .Empty, ImmutableArray <BoundDeconstructionAssignmentStep> .Empty, ImmutableArray <BoundDeconstructionConstructionStep> .Empty, GetSpecialType(SpecialType.System_Void, diagnostics, node), hasErrors: true)); } var deconstructionSteps = ArrayBuilder <BoundDeconstructionDeconstructStep> .GetInstance(1); var conversionSteps = ArrayBuilder <BoundDeconstructionAssignmentStep> .GetInstance(1); var assignmentSteps = ArrayBuilder <BoundDeconstructionAssignmentStep> .GetInstance(1); var constructionStepsOpt = ArrayBuilder <BoundDeconstructionConstructionStep> .GetInstance(1); bool hasErrors = !DeconstructIntoSteps( new BoundDeconstructValuePlaceholder(boundRHS.Syntax, boundRHS.Type), node, diagnostics, checkedVariables, deconstructionSteps, conversionSteps, assignmentSteps, constructionStepsOpt); TypeSymbol returnType = hasErrors ? CreateErrorType() : constructionStepsOpt.Last().OutputPlaceholder.Type; var deconstructions = deconstructionSteps.ToImmutableAndFree(); var conversions = conversionSteps.ToImmutableAndFree(); var assignments = assignmentSteps.ToImmutableAndFree(); var constructions = constructionStepsOpt.ToImmutableAndFree(); FailRemainingInferences(checkedVariables, diagnostics); return(new BoundDeconstructionAssignmentOperator( node, FlattenDeconstructVariables(checkedVariables), boundRHS, deconstructions, conversions, assignments, constructions, returnType, hasErrors: hasErrors)); }
/// <summary> /// There are two kinds of deconstruction-assignments which this binding handles: tuple and non-tuple. /// /// Returns a BoundDeconstructionAssignmentOperator with a list of deconstruction steps and assignment steps. /// Deconstruct steps for tuples have no invocation to Deconstruct, but steps for non-tuples do. /// The caller is responsible for releasing all the ArrayBuilders in checkedVariables. /// </summary> private BoundDeconstructionAssignmentOperator BindDeconstructionAssignment( CSharpSyntaxNode node, ExpressionSyntax right, ArrayBuilder<DeconstructionVariable> checkedVariables, DiagnosticBag diagnostics, bool isDeclaration, BoundDeconstructValuePlaceholder rhsPlaceholder = null) { // receiver for first Deconstruct step var boundRHS = rhsPlaceholder ?? BindValue(right, diagnostics, BindValueKind.RValue); boundRHS = FixTupleLiteral(checkedVariables, boundRHS, node, diagnostics); if ((object)boundRHS.Type == null) { // we could still not infer a type for the RHS FailRemainingInferences(checkedVariables, diagnostics); return new BoundDeconstructionAssignmentOperator( node, isDeclaration, FlattenDeconstructVariables(checkedVariables), boundRHS, ImmutableArray<BoundDeconstructionDeconstructStep>.Empty, ImmutableArray<BoundDeconstructionAssignmentStep>.Empty, ImmutableArray<BoundDeconstructionAssignmentStep>.Empty, ImmutableArray<BoundDeconstructionConstructionStep>.Empty, GetSpecialType(SpecialType.System_Void, diagnostics, node), hasErrors: true); } var deconstructionSteps = ArrayBuilder<BoundDeconstructionDeconstructStep>.GetInstance(1); var conversionSteps = ArrayBuilder<BoundDeconstructionAssignmentStep>.GetInstance(1); var assignmentSteps = ArrayBuilder<BoundDeconstructionAssignmentStep>.GetInstance(1); var constructionStepsOpt = isDeclaration ? null : ArrayBuilder<BoundDeconstructionConstructionStep>.GetInstance(1); bool hasErrors = !DeconstructIntoSteps( new BoundDeconstructValuePlaceholder(boundRHS.Syntax, boundRHS.Type), node, diagnostics, checkedVariables, deconstructionSteps, conversionSteps, assignmentSteps, constructionStepsOpt); TypeSymbol returnType = isDeclaration ? GetSpecialType(SpecialType.System_Void, diagnostics, node) : hasErrors ? CreateErrorType() : constructionStepsOpt.Last().OutputPlaceholder.Type; var deconstructions = deconstructionSteps.ToImmutableAndFree(); var conversions = conversionSteps.ToImmutableAndFree(); var assignments = assignmentSteps.ToImmutableAndFree(); var constructions = isDeclaration ? default(ImmutableArray<BoundDeconstructionConstructionStep>) : constructionStepsOpt.ToImmutableAndFree(); FailRemainingInferences(checkedVariables, diagnostics); return new BoundDeconstructionAssignmentOperator( node, isDeclaration, FlattenDeconstructVariables(checkedVariables), boundRHS, deconstructions, conversions, assignments, constructions, returnType, hasErrors: hasErrors); }
/// <summary> /// Whether the target is a tuple or a type that requires Deconstruction, this will generate and stack appropriate deconstruction and assignment steps. /// Note that the variables may either be plain or nested variables. /// The variables may be updated with inferred types if they didn't have types initially. /// Returns false if there was an error. /// </summary> private bool DeconstructIntoSteps( BoundDeconstructValuePlaceholder targetPlaceholder, CSharpSyntaxNode syntax, DiagnosticBag diagnostics, ArrayBuilder <DeconstructionVariable> variables, ArrayBuilder <BoundDeconstructionDeconstructStep> deconstructionSteps, ArrayBuilder <BoundDeconstructionAssignmentStep> assignmentSteps) { Debug.Assert(targetPlaceholder.Type != null); BoundDeconstructionDeconstructStep step; if (targetPlaceholder.Type.IsTupleType) { // tuple literal such as `(1, 2)`, `(null, null)`, `(x.P, y.M())` step = MakeTupleDeconstructStep(targetPlaceholder, syntax, diagnostics, variables, deconstructionSteps, assignmentSteps); } else { step = MakeNonTupleDeconstructStep(targetPlaceholder, syntax, diagnostics, variables, deconstructionSteps, assignmentSteps); } if (step == null) { return(false); } deconstructionSteps.Add(step); // outputs will either need a conversion step and assignment step, or if they are nested variables, they will need further deconstruction return(DeconstructOrAssignOutputs(step, variables, syntax, diagnostics, deconstructionSteps, assignmentSteps)); }
public BoundDeconstructValuePlaceholder SetInferredType(TypeSymbol type, bool success) { Debug.Assert((object)Placeholder == null); Placeholder = new BoundDeconstructValuePlaceholder(this.Syntax, type, hasErrors: this.HasErrors || !success); return(Placeholder); }
/// <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); }
public BoundDeconstructValuePlaceholder SetInferredType(TypeSymbolWithAnnotations type, Binder binder, bool success) { Debug.Assert(Placeholder is null); // The val escape scope for this placeholder won't be used, so defaulting to narrowest scope Placeholder = new BoundDeconstructValuePlaceholder(this.Syntax, binder.LocalScopeDepth, type.TypeSymbol, hasErrors: this.HasErrors || !success); return(Placeholder); }
internal DeconstructMethodInfo( BoundExpression invocation, BoundDeconstructValuePlaceholder inputPlaceholder, ImmutableArray <BoundDeconstructValuePlaceholder> outputPlaceholders ) { (Invocation, InputPlaceholder, OutputPlaceholders) = ( invocation, inputPlaceholder, outputPlaceholders ); }
internal BoundDeconstructionAssignmentOperator BindDeconstruction( CSharpSyntaxNode node, ExpressionSyntax left, ExpressionSyntax right, DiagnosticBag diagnostics, bool isDeclaration, BoundDeconstructValuePlaceholder rightPlaceholder = null) { DeconstructionVariable locals = BindDeconstructionVariables(left, isDeclaration, diagnostics); Debug.Assert(locals.HasNestedVariables); var result = BindDeconstructionAssignment(node, right, locals.NestedVariables, diagnostics, rhsPlaceholder: rightPlaceholder); FreeDeconstructionVariables(locals.NestedVariables); return result; }
/// <summary> /// Figures out how to assign from sourceType into receivingVariable and bundles the information (leaving holes for the actual source and receiver) into an AssignmentInfo. /// </summary> private BoundDeconstructionAssignmentStep MakeDeconstructionAssignmentStep( BoundExpression receivingVariable, TypeSymbol sourceType, BoundDeconstructValuePlaceholder inputPlaceholder, CSharpSyntaxNode node, DiagnosticBag diagnostics) { var outputPlaceholder = new BoundDeconstructValuePlaceholder(receivingVariable.Syntax, receivingVariable.Type) { WasCompilerGenerated = true }; // each assignment has a placeholder for a receiver and another for the source BoundAssignmentOperator op = BindAssignment(receivingVariable.Syntax, outputPlaceholder, inputPlaceholder, diagnostics); return(new BoundDeconstructionAssignmentStep(node, op, inputPlaceholder, outputPlaceholder)); }
internal BoundDeconstructionAssignmentOperator BindDeconstruction( CSharpSyntaxNode node, ExpressionSyntax left, ExpressionSyntax right, DiagnosticBag diagnostics, bool isDeclaration, BoundDeconstructValuePlaceholder rightPlaceholder = null) { DeconstructionVariable locals = BindDeconstructionVariables(left, isDeclaration, diagnostics); Debug.Assert(locals.HasNestedVariables); var result = BindDeconstructionAssignment(node, right, locals.NestedVariables, diagnostics, rhsPlaceholder: rightPlaceholder); FreeDeconstructionVariables(locals.NestedVariables); return(result); }
/// <summary> /// There are two kinds of deconstruction-assignments which this binding handles: tuple and non-tuple. /// /// Returns a BoundDeconstructionAssignmentOperator with a list of deconstruction steps and assignment steps. /// Deconstruct steps for tuples have no invocation to Deconstruct, but steps for non-tuples do. /// The caller is responsible for releasing all the ArrayBuilders in checkedVariables. /// </summary> private BoundDeconstructionAssignmentOperator BindDeconstructionAssignment(CSharpSyntaxNode node, ExpressionSyntax right, ArrayBuilder<DeconstructionVariable> checkedVariables, DiagnosticBag diagnostics, bool isDeclaration, BoundDeconstructValuePlaceholder rhsPlaceholder = null) { TypeSymbol voidType = GetSpecialType(SpecialType.System_Void, diagnostics, node); // receiver for first Deconstruct step var boundRHS = rhsPlaceholder ?? BindValue(right, diagnostics, BindValueKind.RValue); if ((object)boundRHS.Type == null) { if (boundRHS.Kind == BoundKind.TupleLiteral) { // Let's fix the literal up by figuring out its type // For declarations, that means merging type information from the LHS and RHS // For assignments, only the LHS side matters since it is necessarily typed TypeSymbol lhsAsTuple = MakeMergedTupleType(checkedVariables, (BoundTupleLiteral)boundRHS, node, Compilation, diagnostics); if ((object)lhsAsTuple != null) { boundRHS = GenerateConversionForAssignment(lhsAsTuple, boundRHS, diagnostics); } } else { Error(diagnostics, ErrorCode.ERR_DeconstructRequiresExpression, right); } if ((object)boundRHS.Type == null) { // we could still not infer a type for the RHS FailRemainingInferences(checkedVariables, diagnostics); return new BoundDeconstructionAssignmentOperator( node, isDeclaration, FlattenDeconstructVariables(checkedVariables), boundRHS, ImmutableArray<BoundDeconstructionDeconstructStep>.Empty, ImmutableArray<BoundDeconstructionAssignmentStep>.Empty, voidType, hasErrors: true); } } var deconstructionSteps = ArrayBuilder<BoundDeconstructionDeconstructStep>.GetInstance(1); var assignmentSteps = ArrayBuilder<BoundDeconstructionAssignmentStep>.GetInstance(1); bool hasErrors = !DeconstructIntoSteps(new BoundDeconstructValuePlaceholder(boundRHS.Syntax, boundRHS.Type), node, diagnostics, checkedVariables, deconstructionSteps, assignmentSteps); var deconstructions = deconstructionSteps.ToImmutableAndFree(); var assignments = assignmentSteps.ToImmutableAndFree(); FailRemainingInferences(checkedVariables, diagnostics); return new BoundDeconstructionAssignmentOperator(node, isDeclaration, FlattenDeconstructVariables(checkedVariables), boundRHS, deconstructions, assignments, voidType, hasErrors: hasErrors); }
/// <summary> /// This will generate and stack appropriate deconstruction and assignment steps for a non-tuple type. /// Returns null if there was an error (if a suitable Deconstruct method was not found). /// </summary> private BoundDeconstructionDeconstructStep MakeNonTupleDeconstructStep( BoundDeconstructValuePlaceholder targetPlaceholder, CSharpSyntaxNode syntax, DiagnosticBag diagnostics, ArrayBuilder <DeconstructionVariable> variables) { // symbol and parameters for Deconstruct ImmutableArray <BoundDeconstructValuePlaceholder> outPlaceholders; var deconstructInvocation = MakeDeconstructInvocationExpression(variables.Count, targetPlaceholder, syntax, diagnostics, out outPlaceholders); if (deconstructInvocation.HasAnyErrors) { return(null); } SetInferredTypes(variables, outPlaceholders.SelectAsArray(p => p.Type), diagnostics); return(new BoundDeconstructionDeconstructStep(syntax, deconstructInvocation, targetPlaceholder, outPlaceholders)); }
/// <summary> /// The produces a deconstruction step with no Deconstruct method since the tuple already has distinct elements. /// </summary> private BoundDeconstructionDeconstructStep MakeTupleDeconstructStep( BoundDeconstructValuePlaceholder targetPlaceholder, CSharpSyntaxNode syntax, DiagnosticBag diagnostics, ArrayBuilder <DeconstructionVariable> variables) { Debug.Assert(targetPlaceholder.Type.IsTupleType); var tupleTypes = targetPlaceholder.Type.TupleElementTypes; SetInferredTypes(variables, tupleTypes, diagnostics); if (variables.Count != tupleTypes.Length) { Error(diagnostics, ErrorCode.ERR_DeconstructWrongCardinality, syntax, tupleTypes.Length, variables.Count); return(null); } return(new BoundDeconstructionDeconstructStep(syntax, null, targetPlaceholder, tupleTypes.SelectAsArray((t, i, v) => new BoundDeconstructValuePlaceholder(v[i].Syntax, t), variables))); }
private BoundDeconstructionConstructionStep MakeDeconstructionConstructionStep(CSharpSyntaxNode node, DiagnosticBag diagnostics, ImmutableArray <BoundDeconstructValuePlaceholder> constructionInputs) { var tuple = TupleTypeSymbol.Create(locationOpt: null, elementTypes: constructionInputs.SelectAsArray(e => e.Type), elementLocations: default(ImmutableArray <Location>), elementNames: default(ImmutableArray <string>), compilation: Compilation, diagnostics: diagnostics, syntax: node); var outputPlaceholder = new BoundDeconstructValuePlaceholder(node, tuple) { WasCompilerGenerated = true }; BoundExpression construction = new BoundTupleLiteral(node, default(ImmutableArray <string>), constructionInputs.CastArray <BoundExpression>(), tuple); return(new BoundDeconstructionConstructionStep(node, construction, outputPlaceholder)); }
/// <summary> /// There are two kinds of deconstruction-assignments which this binding handles: tuple and non-tuple. /// /// Returns a BoundDeconstructionAssignmentOperator with a list of deconstruction steps and assignment steps. /// Deconstruct steps for tuples have no invocation to Deconstruct, but steps for non-tuples do. /// The caller is responsible for releasing all the ArrayBuilders in checkedVariables. /// </summary> private BoundDeconstructionAssignmentOperator BindDeconstructionAssignment(CSharpSyntaxNode node, ExpressionSyntax right, ArrayBuilder<DeconstructionVariable> checkedVariables, DiagnosticBag diagnostics, bool isDeclaration, BoundDeconstructValuePlaceholder rhsPlaceholder = null) { TypeSymbol voidType = GetSpecialType(SpecialType.System_Void, diagnostics, node); // receiver for first Deconstruct step var boundRHS = rhsPlaceholder ?? BindValue(right, diagnostics, BindValueKind.RValue); if ((object)boundRHS.Type == null) { if (boundRHS.Kind == BoundKind.TupleLiteral && !isDeclaration) { // tuple literal without type such as `(null, null)`, let's fix it up by peeking at the LHS TypeSymbol lhsAsTuple = MakeTupleTypeFromDeconstructionLHS(checkedVariables, diagnostics, Compilation); boundRHS = GenerateConversionForAssignment(lhsAsTuple, boundRHS, diagnostics); } else { // expression without type such as `null` Error(diagnostics, ErrorCode.ERR_DeconstructRequiresExpression, right); FailRemainingInferences(checkedVariables, diagnostics); return new BoundDeconstructionAssignmentOperator( node, isDeclaration, FlattenDeconstructVariables(checkedVariables), boundRHS, ImmutableArray<BoundDeconstructionDeconstructStep>.Empty, ImmutableArray<BoundDeconstructionAssignmentStep>.Empty, voidType, hasErrors: true); } } var deconstructionSteps = ArrayBuilder<BoundDeconstructionDeconstructStep>.GetInstance(1); var assignmentSteps = ArrayBuilder<BoundDeconstructionAssignmentStep>.GetInstance(1); bool hasErrors = !DeconstructIntoSteps(new BoundDeconstructValuePlaceholder(boundRHS.Syntax, boundRHS.Type), node, diagnostics, checkedVariables, deconstructionSteps, assignmentSteps); var deconstructions = deconstructionSteps.ToImmutableAndFree(); var assignments = assignmentSteps.ToImmutableAndFree(); FailRemainingInferences(checkedVariables, diagnostics); return new BoundDeconstructionAssignmentOperator(node, isDeclaration, FlattenDeconstructVariables(checkedVariables), boundRHS, deconstructions, assignments, voidType, hasErrors: hasErrors); }
/// <summary> /// This will generate and stack appropriate deconstruction and assignment steps for a non-tuple type. /// Returns null if there was an error (if a suitable Deconstruct method was not found). /// </summary> private BoundDeconstructionDeconstructStep MakeNonTupleDeconstructStep( BoundDeconstructValuePlaceholder targetPlaceholder, CSharpSyntaxNode syntax, DiagnosticBag diagnostics, ArrayBuilder<DeconstructionVariable> variables) { // symbol and parameters for Deconstruct ImmutableArray<BoundDeconstructValuePlaceholder> outPlaceholders; var deconstructInvocation = MakeDeconstructInvocationExpression(variables.Count, targetPlaceholder, syntax, diagnostics, out outPlaceholders); if (deconstructInvocation.HasAnyErrors) { return null; } SetInferredTypes(variables, outPlaceholders.SelectAsArray(p => p.Type), diagnostics); return new BoundDeconstructionDeconstructStep(syntax, deconstructInvocation, targetPlaceholder, outPlaceholders); }
/// <summary> /// The produces a deconstruction step with no Deconstruct method since the tuple already has distinct elements. /// </summary> private BoundDeconstructionDeconstructStep MakeTupleDeconstructStep( BoundDeconstructValuePlaceholder targetPlaceholder, CSharpSyntaxNode syntax, DiagnosticBag diagnostics, ArrayBuilder<DeconstructionVariable> variables) { Debug.Assert(targetPlaceholder.Type.IsTupleType); var tupleTypes = targetPlaceholder.Type.TupleElementTypes; SetInferredTypes(variables, tupleTypes, diagnostics); if (variables.Count != tupleTypes.Length) { Error(diagnostics, ErrorCode.ERR_DeconstructWrongCardinality, syntax, tupleTypes.Length, variables.Count); return null; } return new BoundDeconstructionDeconstructStep(syntax, null, targetPlaceholder, tupleTypes.SelectAsArray((t, i, v) => new BoundDeconstructValuePlaceholder(v[i].Syntax, t), variables)); }
/// <summary> /// Whether the target is a tuple or a type that requires Deconstruction, this will generate and stack appropriate deconstruction and assignment steps. /// Note that the variables may either be plain or nested variables. /// The variables may be updated with inferred types if they didn't have types initially. /// Returns false if there was an error. /// Pass in constructionStepsOpt as null if construction steps should not be computed. /// </summary> private bool DeconstructIntoSteps( BoundDeconstructValuePlaceholder targetPlaceholder, CSharpSyntaxNode syntax, DiagnosticBag diagnostics, ArrayBuilder<DeconstructionVariable> variables, ArrayBuilder<BoundDeconstructionDeconstructStep> deconstructionSteps, ArrayBuilder<BoundDeconstructionAssignmentStep> conversionSteps, ArrayBuilder<BoundDeconstructionAssignmentStep> assignmentSteps, ArrayBuilder<BoundDeconstructionConstructionStep> constructionStepsOpt) { Debug.Assert(targetPlaceholder.Type != null); BoundDeconstructionDeconstructStep step; if (targetPlaceholder.Type.IsTupleType) { // tuple literal such as `(1, 2)`, `(null, null)`, `(x.P, y.M())` step = MakeTupleDeconstructStep(targetPlaceholder, syntax, diagnostics, variables); } else { step = MakeNonTupleDeconstructStep(targetPlaceholder, syntax, diagnostics, variables); } if (step == null) { return false; } deconstructionSteps.Add(step); // outputs will either need a conversion step and assignment step, or if they are nested variables, they will need further deconstruction return DeconstructOrAssignOutputs(step, variables, syntax, diagnostics, deconstructionSteps, conversionSteps, assignmentSteps, constructionStepsOpt); }
internal BoundDeconstructionAssignmentOperator BindDeconstructionDeclaration(CSharpSyntaxNode node, VariableDeclarationSyntax declaration, ExpressionSyntax right, DiagnosticBag diagnostics, BoundDeconstructValuePlaceholder rightPlaceholder = null) { ArrayBuilder <DeconstructionVariable> locals = BindDeconstructionDeclarationLocals(declaration, declaration.Type, diagnostics); var result = BindDeconstructionAssignment(node, right, locals, diagnostics, isDeclaration: true, rhsPlaceholder: rightPlaceholder); FreeDeconstructionVariables(locals); return(result); }
/// <summary> /// There are two kinds of deconstruction-assignments which this binding handles: tuple and non-tuple. /// /// Returns a BoundDeconstructionAssignmentOperator with a list of deconstruction steps and assignment steps. /// Deconstruct steps for tuples have no invocation to Deconstruct, but steps for non-tuples do. /// The caller is responsible for releasing all the ArrayBuilders in checkedVariables. /// </summary> private BoundDeconstructionAssignmentOperator BindDeconstructionAssignment(CSharpSyntaxNode node, ExpressionSyntax right, ArrayBuilder <DeconstructionVariable> checkedVariables, DiagnosticBag diagnostics, bool isDeclaration, BoundDeconstructValuePlaceholder rhsPlaceholder = null) { TypeSymbol voidType = GetSpecialType(SpecialType.System_Void, diagnostics, node); // receiver for first Deconstruct step var boundRHS = rhsPlaceholder ?? BindValue(right, diagnostics, BindValueKind.RValue); if ((object)boundRHS.Type == null) { if (boundRHS.Kind == BoundKind.TupleLiteral) { // Let's fix the literal up by figuring out its type // For declarations, that means merging type information from the LHS and RHS // For assignments, only the LHS side matters since it is necessarily typed TypeSymbol lhsAsTuple = MakeMergedTupleType(checkedVariables, (BoundTupleLiteral)boundRHS, node, Compilation, diagnostics); if ((object)lhsAsTuple != null) { boundRHS = GenerateConversionForAssignment(lhsAsTuple, boundRHS, diagnostics); } } else { Error(diagnostics, ErrorCode.ERR_DeconstructRequiresExpression, right); } if ((object)boundRHS.Type == null) { // we could still not infer a type for the RHS FailRemainingInferences(checkedVariables, diagnostics); return(new BoundDeconstructionAssignmentOperator( node, isDeclaration, FlattenDeconstructVariables(checkedVariables), boundRHS, ImmutableArray <BoundDeconstructionDeconstructStep> .Empty, ImmutableArray <BoundDeconstructionAssignmentStep> .Empty, voidType, hasErrors: true)); } } var deconstructionSteps = ArrayBuilder <BoundDeconstructionDeconstructStep> .GetInstance(1); var assignmentSteps = ArrayBuilder <BoundDeconstructionAssignmentStep> .GetInstance(1); bool hasErrors = !DeconstructIntoSteps(new BoundDeconstructValuePlaceholder(boundRHS.Syntax, boundRHS.Type), node, diagnostics, checkedVariables, deconstructionSteps, assignmentSteps); var deconstructions = deconstructionSteps.ToImmutableAndFree(); var assignments = assignmentSteps.ToImmutableAndFree(); FailRemainingInferences(checkedVariables, diagnostics); return(new BoundDeconstructionAssignmentOperator(node, isDeclaration, FlattenDeconstructVariables(checkedVariables), boundRHS, deconstructions, assignments, voidType, hasErrors: hasErrors)); }
/// <summary> /// Like BindForEachParts, but only bind the deconstruction part of the foreach, for purpose of inferring the types of the declared locals. /// </summary> internal override BoundStatement BindForEachDeconstruction(DiagnosticBag diagnostics, Binder originalBinder) { // Use the right binder to avoid seeing iteration variable BoundExpression collectionExpr = originalBinder.GetBinder(_syntax.Expression).BindValue(_syntax.Expression, diagnostics, BindValueKind.RValue); ForEachEnumeratorInfo.Builder builder = new ForEachEnumeratorInfo.Builder(); TypeSymbol inferredType; bool hasErrors = !GetEnumeratorInfoAndInferCollectionElementType(ref builder, ref collectionExpr, diagnostics, out inferredType); VariableComponentSyntax variables = ((ForEachComponentStatementSyntax)_syntax).VariableComponent; var valuePlaceholder = new BoundDeconstructValuePlaceholder(_syntax.Expression, inferredType ?? CreateErrorType("var")); BoundDeconstructionAssignmentOperator deconstruction = BindDeconstructionDeclaration( variables, variables, right: null, diagnostics: diagnostics, rightPlaceholder: valuePlaceholder); return new BoundExpressionStatement(_syntax, deconstruction); }
/// <summary> /// Recursively builds a Conversion object with Kind=Deconstruction including information about any necessary /// Deconstruct method and any element-wise conversion. /// /// Note that the variables may either be plain or nested variables. /// The variables may be updated with inferred types if they didn't have types initially. /// Returns false if there was an error. /// </summary> private bool MakeDeconstructionConversion( TypeSymbol type, SyntaxNode syntax, SyntaxNode rightSyntax, DiagnosticBag diagnostics, ArrayBuilder <DeconstructionVariable> variables, out Conversion conversion) { Debug.Assert(type != null); ImmutableArray <TypeSymbol> tupleOrDeconstructedTypes; conversion = Conversion.Deconstruction; // Figure out the deconstruct method (if one is required) and determine the types we get from the RHS at this level var deconstructMethod = default(DeconstructMethodInfo); if (type.IsTupleType) { // tuple literal such as `(1, 2)`, `(null, null)`, `(x.P, y.M())` tupleOrDeconstructedTypes = type.TupleElementTypes; SetInferredTypes(variables, tupleOrDeconstructedTypes, diagnostics); if (variables.Count != tupleOrDeconstructedTypes.Length) { Error(diagnostics, ErrorCode.ERR_DeconstructWrongCardinality, syntax, tupleOrDeconstructedTypes.Length, variables.Count); return(false); } } else { ImmutableArray <BoundDeconstructValuePlaceholder> outPlaceholders; var inputPlaceholder = new BoundDeconstructValuePlaceholder(syntax, this.LocalScopeDepth, type); var deconstructInvocation = MakeDeconstructInvocationExpression(variables.Count, inputPlaceholder, rightSyntax, diagnostics, out outPlaceholders); if (deconstructInvocation.HasAnyErrors) { return(false); } deconstructMethod = new DeconstructMethodInfo(deconstructInvocation, inputPlaceholder, outPlaceholders); tupleOrDeconstructedTypes = outPlaceholders.SelectAsArray(p => p.Type); SetInferredTypes(variables, tupleOrDeconstructedTypes, diagnostics); } // Figure out whether those types will need conversions, including further deconstructions bool hasErrors = false; int count = variables.Count; var nestedConversions = ArrayBuilder <Conversion> .GetInstance(count); for (int i = 0; i < count; i++) { var variable = variables[i]; Conversion nestedConversion; if (variable.HasNestedVariables) { var elementSyntax = syntax.Kind() == SyntaxKind.TupleExpression ? ((TupleExpressionSyntax)syntax).Arguments[i] : syntax; hasErrors |= !MakeDeconstructionConversion(tupleOrDeconstructedTypes[i], syntax, rightSyntax, diagnostics, variable.NestedVariables, out nestedConversion); } else { var single = variable.Single; HashSet <DiagnosticInfo> useSiteDiagnostics = null; nestedConversion = this.Conversions.ClassifyConversionFromType(tupleOrDeconstructedTypes[i], single.Type, ref useSiteDiagnostics); diagnostics.Add(single.Syntax, useSiteDiagnostics); if (!nestedConversion.IsImplicit) { hasErrors = true; GenerateImplicitConversionError(diagnostics, Compilation, single.Syntax, nestedConversion, tupleOrDeconstructedTypes[i], single.Type); } } nestedConversions.Add(nestedConversion); } conversion = new Conversion(ConversionKind.Deconstruction, deconstructMethod, nestedConversions.ToImmutableAndFree()); return(!hasErrors); }
private BoundDeconstructionConstructionStep MakeDeconstructionConstructionStep(CSharpSyntaxNode node, DiagnosticBag diagnostics, ImmutableArray<BoundDeconstructValuePlaceholder> constructionInputs) { var tuple = TupleTypeSymbol.Create(locationOpt: null, elementTypes: constructionInputs.SelectAsArray(e => e.Type), elementLocations: default(ImmutableArray<Location>), elementNames: default(ImmutableArray<string>), compilation: Compilation, diagnostics: diagnostics, syntax: node); var outputPlaceholder = new BoundDeconstructValuePlaceholder(node, tuple) { WasCompilerGenerated = true }; BoundExpression construction = new BoundTupleLiteral(node, default(ImmutableArray<string>), constructionInputs.CastArray<BoundExpression>(), tuple); return new BoundDeconstructionConstructionStep(node, construction, outputPlaceholder); }
public override BoundNode VisitDeconstructValuePlaceholder(BoundDeconstructValuePlaceholder node) { Fail(node); return(null); }
private BoundForEachStatement BindForEachPartsWorker(DiagnosticBag diagnostics, Binder originalBinder) { // Use the right binder to avoid seeing iteration variable BoundExpression collectionExpr = originalBinder.GetBinder(_syntax.Expression).BindValue(_syntax.Expression, diagnostics, BindValueKind.RValue); ForEachEnumeratorInfo.Builder builder = new ForEachEnumeratorInfo.Builder(); TypeSymbol inferredType; bool hasErrors = !GetEnumeratorInfoAndInferCollectionElementType(ref builder, ref collectionExpr, diagnostics, out inferredType); // These should only occur when special types are missing or malformed. hasErrors = hasErrors || (object)builder.GetEnumeratorMethod == null || (object)builder.MoveNextMethod == null || (object)builder.CurrentPropertyGetter == null; TypeSymbol iterationVariableType; BoundTypeExpression boundIterationVariableType; bool hasNameConflicts = false; BoundForEachDeconstructStep deconstructStep = null; switch (_syntax.Kind()) { case SyntaxKind.ForEachStatement: { var node = (ForEachStatementSyntax)_syntax; // Check for local variable conflicts in the *enclosing* binder; obviously the *current* // binder has a local that matches! hasNameConflicts = originalBinder.ValidateDeclarationNameConflictsInScope(IterationVariable, diagnostics); // If the type in syntax is "var", then the type should be set explicitly so that the // Type property doesn't fail. TypeSyntax typeSyntax = node.Type; bool isVar; AliasSymbol alias; TypeSymbol declType = BindType(typeSyntax, diagnostics, out isVar, out alias); if (isVar) { iterationVariableType = inferredType ?? CreateErrorType("var"); } else { Debug.Assert((object)declType != null); iterationVariableType = declType; } boundIterationVariableType = new BoundTypeExpression(typeSyntax, alias, iterationVariableType); this.IterationVariable.SetTypeSymbol(iterationVariableType); break; } case SyntaxKind.ForEachComponentStatement: { var node = (ForEachComponentStatementSyntax)_syntax; iterationVariableType = inferredType ?? CreateErrorType("var"); var variables = node.VariableComponent; var valuePlaceholder = new BoundDeconstructValuePlaceholder(_syntax.Expression, iterationVariableType); BoundDeconstructionAssignmentOperator deconstruction = BindDeconstructionDeclaration( variables, variables, right: null, diagnostics: diagnostics, rightPlaceholder: valuePlaceholder); deconstructStep = new BoundForEachDeconstructStep(variables, deconstruction, valuePlaceholder); boundIterationVariableType = new BoundTypeExpression(variables, aliasOpt: null, type: iterationVariableType); break; } default: throw ExceptionUtilities.UnexpectedValue(_syntax.Kind()); } BoundStatement body = originalBinder.BindPossibleEmbeddedStatement(_syntax.Statement, diagnostics); hasErrors = hasErrors || iterationVariableType.IsErrorType(); // Skip the conversion checks and array/enumerator differentiation if we know we have an error (except local name conflicts). if (hasErrors) { return new BoundForEachStatement( _syntax, null, // can't be sure that it's complete default(Conversion), boundIterationVariableType, this.IterationVariable, collectionExpr, deconstructStep, body, CheckOverflowAtRuntime, this.BreakLabel, this.ContinueLabel, hasErrors); } hasErrors |= hasNameConflicts; var foreachKeyword = _syntax.ForEachKeyword; ReportDiagnosticsIfObsolete(diagnostics, builder.GetEnumeratorMethod, foreachKeyword, hasBaseReceiver: false); ReportDiagnosticsIfObsolete(diagnostics, builder.MoveNextMethod, foreachKeyword, hasBaseReceiver: false); ReportDiagnosticsIfObsolete(diagnostics, builder.CurrentPropertyGetter, foreachKeyword, hasBaseReceiver: false); ReportDiagnosticsIfObsolete(diagnostics, builder.CurrentPropertyGetter.AssociatedSymbol, foreachKeyword, hasBaseReceiver: false); // We want to convert from inferredType in the array/string case and builder.ElementType in the enumerator case, // but it turns out that these are equivalent (when both are available). HashSet<DiagnosticInfo> useSiteDiagnostics = null; Conversion elementConversion = this.Conversions.ClassifyConversionFromType(inferredType, iterationVariableType, ref useSiteDiagnostics, forCast: true); if (!elementConversion.IsValid) { ImmutableArray<MethodSymbol> originalUserDefinedConversions = elementConversion.OriginalUserDefinedConversions; if (originalUserDefinedConversions.Length > 1) { diagnostics.Add(ErrorCode.ERR_AmbigUDConv, _syntax.ForEachKeyword.GetLocation(), originalUserDefinedConversions[0], originalUserDefinedConversions[1], inferredType, iterationVariableType); } else { SymbolDistinguisher distinguisher = new SymbolDistinguisher(this.Compilation, inferredType, iterationVariableType); diagnostics.Add(ErrorCode.ERR_NoExplicitConv, _syntax.ForEachKeyword.GetLocation(), distinguisher.First, distinguisher.Second); } hasErrors = true; } else { ReportDiagnosticsIfObsolete(diagnostics, elementConversion, _syntax.ForEachKeyword, hasBaseReceiver: false); } // Spec (§8.8.4): // If the type X of expression is dynamic then there is an implicit conversion from >>expression<< (not the type of the expression) // to the System.Collections.IEnumerable interface (§6.1.8). builder.CollectionConversion = this.Conversions.ClassifyConversionFromExpression(collectionExpr, builder.CollectionType, ref useSiteDiagnostics); builder.CurrentConversion = this.Conversions.ClassifyConversionFromType(builder.CurrentPropertyGetter.ReturnType, builder.ElementType, ref useSiteDiagnostics); builder.EnumeratorConversion = this.Conversions.ClassifyConversionFromType(builder.GetEnumeratorMethod.ReturnType, GetSpecialType(SpecialType.System_Object, diagnostics, _syntax), ref useSiteDiagnostics); diagnostics.Add(_syntax.ForEachKeyword.GetLocation(), useSiteDiagnostics); // Due to the way we extracted the various types, these conversions should always be possible. // CAVEAT: if we're iterating over an array of pointers, the current conversion will fail since we // can't convert from object to a pointer type. Similarly, if we're iterating over an array of // Nullable<Error>, the current conversion will fail because we don't know if an ErrorType is a // value type. This doesn't matter in practice, since we won't actually use the enumerator pattern // when we lower the loop. Debug.Assert(builder.CollectionConversion.IsValid); Debug.Assert(builder.CurrentConversion.IsValid || (builder.ElementType.IsPointerType() && collectionExpr.Type.IsArray()) || (builder.ElementType.IsNullableType() && builder.ElementType.GetMemberTypeArgumentsNoUseSiteDiagnostics().Single().IsErrorType() && collectionExpr.Type.IsArray())); Debug.Assert(builder.EnumeratorConversion.IsValid || this.Compilation.GetSpecialType(SpecialType.System_Object).TypeKind == TypeKind.Error || !useSiteDiagnostics.IsNullOrEmpty(), "Conversions to object succeed unless there's a problem with the object type or the source type"); // If user-defined conversions could occur here, we would need to check for ObsoleteAttribute. Debug.Assert((object)builder.CollectionConversion.Method == null, "Conversion from collection expression to collection type should not be user-defined"); Debug.Assert((object)builder.CurrentConversion.Method == null, "Conversion from Current property type to element type should not be user-defined"); Debug.Assert((object)builder.EnumeratorConversion.Method == null, "Conversion from GetEnumerator return type to System.Object should not be user-defined"); // We're wrapping the collection expression in a (non-synthesized) conversion so that its converted // type (i.e. builder.CollectionType) will be available in the binding API. BoundConversion convertedCollectionExpression = new BoundConversion( collectionExpr.Syntax, collectionExpr, builder.CollectionConversion, CheckOverflowAtRuntime, false, ConstantValue.NotAvailable, builder.CollectionType); return new BoundForEachStatement( _syntax, builder.Build(this.Flags), elementConversion, boundIterationVariableType, this.IterationVariable, convertedCollectionExpression, deconstructStep, body, CheckOverflowAtRuntime, this.BreakLabel, this.ContinueLabel, hasErrors); }
/// <summary> /// Figures out how to assign from inputPlaceholder into receivingVariable and bundles the information (leaving holes for the actual source and receiver) into an AssignmentInfo. /// </summary> private BoundDeconstructionAssignmentStep MakeDeconstructionAssignmentStep( BoundExpression receivingVariable, BoundDeconstructValuePlaceholder inputPlaceholder, CSharpSyntaxNode node, DiagnosticBag diagnostics) { var outputPlaceholder = new BoundDeconstructValuePlaceholder(receivingVariable.Syntax, receivingVariable.Type) { WasCompilerGenerated = true }; // each assignment has a placeholder for a receiver and another for the source BoundAssignmentOperator op = BindAssignment(receivingVariable.Syntax, outputPlaceholder, inputPlaceholder, diagnostics); return new BoundDeconstructionAssignmentStep(node, op, outputPlaceholder); }
public override BoundNode VisitDeconstructValuePlaceholder(BoundDeconstructValuePlaceholder node) { return(PlaceholderReplacement(node)); }
internal BoundDeconstructionAssignmentOperator BindDeconstructionDeclaration(CSharpSyntaxNode node, VariableDeclarationSyntax declaration, ExpressionSyntax right, DiagnosticBag diagnostics, BoundDeconstructValuePlaceholder rightPlaceholder = null) { ArrayBuilder<DeconstructionVariable> locals = BindDeconstructionDeclarationLocals(declaration, declaration.Type, diagnostics); var result = BindDeconstructionAssignment(node, right, locals, diagnostics, isDeclaration: true, rhsPlaceholder: rightPlaceholder); FreeDeconstructionVariables(locals); return result; }
/// <summary> /// Recursively builds a Conversion object with Kind=Deconstruction including information about any necessary /// Deconstruct method and any element-wise conversion. /// /// Note that the variables may either be plain or nested variables. /// The variables may be updated with inferred types if they didn't have types initially. /// Returns false if there was an error. /// </summary> private bool MakeDeconstructionConversion( TypeSymbol type, SyntaxNode syntax, SyntaxNode rightSyntax, BindingDiagnosticBag diagnostics, ArrayBuilder <DeconstructionVariable> variables, out Conversion conversion) { Debug.Assert((object)type != null); ImmutableArray <TypeSymbol> tupleOrDeconstructedTypes; conversion = Conversion.Deconstruction; // Figure out the deconstruct method (if one is required) and determine the types we get from the RHS at this level var deconstructMethod = default(DeconstructMethodInfo); if (type.IsTupleType) { // tuple literal such as `(1, 2)`, `(null, null)`, `(x.P, y.M())` tupleOrDeconstructedTypes = type.TupleElementTypesWithAnnotations.SelectAsArray(TypeMap.AsTypeSymbol); SetInferredTypes(variables, tupleOrDeconstructedTypes, diagnostics); if (variables.Count != tupleOrDeconstructedTypes.Length) { Error(diagnostics, ErrorCode.ERR_DeconstructWrongCardinality, syntax, tupleOrDeconstructedTypes.Length, variables.Count); return(false); } } else { if (variables.Count < 2) { Error(diagnostics, ErrorCode.ERR_DeconstructTooFewElements, syntax); return(false); } var inputPlaceholder = new BoundDeconstructValuePlaceholder(syntax, this.LocalScopeDepth, type); BoundExpression deconstructInvocation = MakeDeconstructInvocationExpression(variables.Count, inputPlaceholder, rightSyntax, diagnostics, outPlaceholders: out ImmutableArray <BoundDeconstructValuePlaceholder> outPlaceholders, out _); if (deconstructInvocation.HasAnyErrors) { return(false); } deconstructMethod = new DeconstructMethodInfo(deconstructInvocation, inputPlaceholder, outPlaceholders); tupleOrDeconstructedTypes = outPlaceholders.SelectAsArray(p => p.Type); SetInferredTypes(variables, tupleOrDeconstructedTypes, diagnostics); } // Figure out whether those types will need conversions, including further deconstructions bool hasErrors = false; int count = variables.Count; var nestedConversions = ArrayBuilder <(BoundValuePlaceholder?, BoundExpression?)> .GetInstance(count); for (int i = 0; i < count; i++) { var variable = variables[i]; Conversion nestedConversion; if (variable.NestedVariables is object) { var elementSyntax = syntax.Kind() == SyntaxKind.TupleExpression ? ((TupleExpressionSyntax)syntax).Arguments[i] : syntax; hasErrors |= !MakeDeconstructionConversion(tupleOrDeconstructedTypes[i], elementSyntax, rightSyntax, diagnostics, variable.NestedVariables, out nestedConversion); Debug.Assert(nestedConversion.Kind == ConversionKind.Deconstruction); var operandPlaceholder = new BoundValuePlaceholder(syntax, ErrorTypeSymbol.UnknownResultType).MakeCompilerGenerated(); nestedConversions.Add((operandPlaceholder, new BoundConversion(syntax, operandPlaceholder, nestedConversion, @checked: false, explicitCastInCode: false, conversionGroupOpt: null, constantValueOpt: null, #pragma warning disable format type: ErrorTypeSymbol.UnknownResultType) { WasCompilerGenerated = true }));