protected override Conversion GetExplicitTupleLiteralConversion(BoundTupleLiteral source, TypeSymbol destination, ref HashSet <DiagnosticInfo> useSiteDiagnostics, bool forCast) { var arguments = source.Arguments; // check if the type is actually compatible type for a tuple of given cardinality if (!destination.IsTupleOrCompatibleWithTupleOfCardinality(arguments.Length)) { return(Conversion.NoConversion); } ImmutableArray <TypeSymbol> targetElementTypes = destination.GetElementTypesOfTupleOrCompatible(); Debug.Assert(arguments.Length == targetElementTypes.Length); // check arguments against flattened list of target element types var argumentConversions = ArrayBuilder <Conversion> .GetInstance(arguments.Length); for (int i = 0; i < arguments.Length; i++) { var result = ClassifyConversionFromExpression(arguments[i], targetElementTypes[i], ref useSiteDiagnostics, forCast); if (!result.Exists) { argumentConversions.Free(); return(Conversion.NoConversion); } argumentConversions.Add(result); } return(new Conversion(ConversionKind.ExplicitTupleLiteral, argumentConversions.ToImmutableAndFree())); }
protected override Conversion GetImplicitTupleLiteralConversion(BoundTupleLiteral source, TypeSymbol destination, ref HashSet<DiagnosticInfo> useSiteDiagnostics) { var arguments = source.Arguments; // check if the type is actually compatible type for a tuple of given cardinality if (!destination.IsTupleOrCompatibleWithTupleOfCardinality(arguments.Length)) { return Conversion.NoConversion; } ImmutableArray<TypeSymbol> targetElementTypes = destination.GetElementTypesOfTupleOrCompatible(); Debug.Assert(arguments.Length == targetElementTypes.Length); // check arguments against flattened list of target element types var argumentConversions = ArrayBuilder<Conversion>.GetInstance(arguments.Length); for (int i = 0; i < arguments.Length; i++) { var argument = arguments[i]; var result = ClassifyImplicitConversionFromExpression(argument, targetElementTypes[i], ref useSiteDiagnostics); if (!result.Exists) { argumentConversions.Free(); return Conversion.NoConversion; } argumentConversions.Add(result); } return new Conversion(ConversionKind.ImplicitTupleLiteral, argumentConversions.ToImmutableAndFree()); }
public override BoundNode VisitTupleLiteral(BoundTupleLiteral node) { if (_inExpressionLambda) { Error(ErrorCode.ERR_ExpressionTreeContainsTupleLiteral, node); } return(base.VisitTupleLiteral(node)); }
private void CheckForDeconstructionAssignmentToSelf(BoundTupleLiteral leftTuple, BoundExpression right) { while (right.Kind == BoundKind.Conversion) { var conversion = (BoundConversion)right; switch (conversion.ConversionKind) { case ConversionKind.Deconstruction: case ConversionKind.ImplicitTupleLiteral: case ConversionKind.Identity: right = conversion.Operand; break; default: return; } } if (right.Kind != BoundKind.ConvertedTupleLiteral && right.Kind != BoundKind.TupleLiteral) { return; } var rightTuple = (BoundTupleExpression)right; var leftArguments = leftTuple.Arguments; int length = leftArguments.Length; Debug.Assert(length == rightTuple.Arguments.Length); for (int i = 0; i < length; i++) { var leftArgument = leftArguments[i]; var rightArgument = rightTuple.Arguments[i]; if (leftArgument.Kind == BoundKind.TupleLiteral) { CheckForDeconstructionAssignmentToSelf((BoundTupleLiteral)leftArgument, rightArgument); } else if (IsSameLocalOrField(leftArgument, rightArgument)) { Error(ErrorCode.WRN_AssignmentToSelf, leftArgument); } } }
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> /// For cases where the RHS of a deconstruction-declaration is a tuple literal, we merge type information from both the LHS and RHS. /// For cases where the RHS of a deconstruction-assignment is a tuple literal, the type information from the LHS determines the merged type, since all variables have a type. /// Returns null if a merged tuple type could not be fabricated. /// </summary> private static TypeSymbol MakeMergedTupleType(ArrayBuilder <DeconstructionVariable> lhsVariables, BoundTupleLiteral rhsLiteral, CSharpSyntaxNode syntax, CSharpCompilation compilation, DiagnosticBag diagnostics) { int leftLength = lhsVariables.Count; int rightLength = rhsLiteral.Arguments.Length; var typesBuilder = ArrayBuilder <TypeSymbol> .GetInstance(leftLength); for (int i = 0; i < rightLength; i++) { BoundExpression element = rhsLiteral.Arguments[i]; TypeSymbol mergedType = element.Type; if (i < leftLength) { var variable = lhsVariables[i]; if (variable.HasNestedVariables) { if (element.Kind == BoundKind.TupleLiteral) { // (variables) on the left and (elements) on the right mergedType = MakeMergedTupleType(variable.NestedVariables, (BoundTupleLiteral)element, syntax, compilation, diagnostics); } else if ((object)mergedType == null) { // (variables) on the left and null on the right Error(diagnostics, ErrorCode.ERR_DeconstructRequiresExpression, element.Syntax); } } else { if ((object)variable.Single.Type != null) { // typed-variable on the left mergedType = variable.Single.Type; } else if ((object)mergedType == null) { // typeless-variable on the left and typeless-element on the right Error(diagnostics, ErrorCode.ERR_DeconstructCouldNotInferMergedType, syntax, variable.Syntax, element.Syntax); } } } else { if ((object)mergedType == null) { // a typeless element on the right, matching no variable on the left Error(diagnostics, ErrorCode.ERR_DeconstructRequiresExpression, element.Syntax); } } typesBuilder.Add(mergedType); } if (typesBuilder.Any(t => t == null)) { typesBuilder.Free(); return(null); } return(TupleTypeSymbol.Create(locationOpt: null, elementTypes: typesBuilder.ToImmutableAndFree(), elementLocations: default(ImmutableArray <Location>), elementNames: default(ImmutableArray <string>), compilation: compilation, diagnostics: diagnostics)); }
public override BoundNode VisitTupleLiteral(BoundTupleLiteral node) { return(VisitTupleExpression(node)); }
protected override Conversion GetExplicitTupleLiteralConversion(BoundTupleLiteral source, TypeSymbol destination, ref HashSet<DiagnosticInfo> useSiteDiagnostics, bool forCast) { // Tuple conversions require a Binder, recursively throw ExceptionUtilities.Unreachable; }
protected override Conversion GetExplicitTupleLiteralConversion(BoundTupleLiteral source, TypeSymbol destination, ref HashSet <DiagnosticInfo> useSiteDiagnostics, bool forCast) { // Tuple conversions require a Binder, recursively throw ExceptionUtilities.Unreachable; }
public override BoundNode VisitTupleLiteral(BoundTupleLiteral node) { return VisitTupleExpression(node); }
private void MakeExplicitParameterTypeInferences(Binder binder, BoundTupleLiteral argument, TypeSymbol target, bool isExactInference, ref HashSet<DiagnosticInfo> useSiteDiagnostics) { // if tuple has a type we will try to match the type as a single input // Example: // if "(a: 1, b: 2)" is passed as T arg // then T becomes (int a, int b) var source = argument.Type; if (IsReallyAType(source)) { if (isExactInference) { // SPEC: * If V is one of the unfixed Xi then U is added to the set of // SPEC: exact bounds for Xi. if (ExactTypeParameterInference(source, target)) { return; } } else { // SPEC: A lower-bound inference from a type U to a type V is made as follows: // SPEC: * If V is one of the unfixed Xi then U is added to the set of // SPEC: lower bounds for Xi. if (LowerBoundTypeParameterInference(source, target)) { return; } } } // try match up element-wise to the destination tuple (or underlying type) // Example: // if "(a: 1, b: "qq")" is passed as (T, U) arg // then T becomes int and U becomes string if (target.Kind != SymbolKind.NamedType) { // tuples can only match to tuples or tuple underlying types. return; } var destination = (NamedTypeSymbol)target; var sourceArguments = argument.Arguments; // check if the type is actually compatible type for a tuple of given cardinality if (!destination.IsTupleOrCompatibleWithTupleOfCardinality(sourceArguments.Length)) { // target is not a tuple of appropriate shape return; } var destTypes = destination.GetElementTypesOfTupleOrCompatible(); Debug.Assert(sourceArguments.Length == destTypes.Length); // NOTE: we are losing tuple element names when recursing into argument expressions. // that is ok, because we are inferring type parameters used in the matching elements, // This is not the situation where entire tuple literal is used to infer a single type param for (int i = 0; i < sourceArguments.Length; i++) { var sourceArgument = sourceArguments[i]; var destType = destTypes[i]; MakeExplicitParameterTypeInferences(binder, sourceArgument, destType, isExactInference, ref useSiteDiagnostics); } }
private BoundExpression CreateTupleLiteralConversion(CSharpSyntaxNode syntax, BoundTupleLiteral sourceTuple, Conversion conversion, bool isCast, TypeSymbol destination, DiagnosticBag diagnostics) { // We have a successful tuple conversion; rather than producing a separate conversion node // which is a conversion on top of a tuple literal, tuple conversion is an element-wise conversion of arguments. Debug.Assert(conversion.Kind == ConversionKind.ImplicitTupleLiteral || conversion.Kind == ConversionKind.ImplicitNullable); Debug.Assert((conversion.Kind == ConversionKind.ImplicitNullable) == destination.IsNullableType()); TypeSymbol destinationWithoutNullable = conversion.Kind == ConversionKind.ImplicitNullable ? destinationWithoutNullable = destination.GetNullableUnderlyingType() : destination; NamedTypeSymbol targetType = (NamedTypeSymbol)destinationWithoutNullable; if (targetType.IsTupleType) { var destTupleType = (TupleTypeSymbol)targetType; // do not lose the original element names in the literal if different from names in the target // Come back to this, what about locations? (https://github.com/dotnet/roslyn/issues/11013) targetType = destTupleType.WithElementNames(sourceTuple.ArgumentNamesOpt); } var arguments = sourceTuple.Arguments; var convertedArguments = ArrayBuilder<BoundExpression>.GetInstance(arguments.Length); ImmutableArray<TypeSymbol> targetElementTypes = targetType.GetElementTypesOfTupleOrCompatible(); Debug.Assert(targetElementTypes.Length == arguments.Length, "converting a tuple literal to incompatible type?"); for (int i = 0; i < arguments.Length; i++) { var argument = arguments[i]; var destType = targetElementTypes[i]; HashSet<DiagnosticInfo> useSiteDiagnostics = null; Conversion elementConversion; if (isCast) { elementConversion = this.Conversions.ClassifyConversionForCast(argument, destType, ref useSiteDiagnostics); } else { elementConversion = this.Conversions.ClassifyConversionFromExpression(argument, destType, ref useSiteDiagnostics); } diagnostics.Add(syntax, useSiteDiagnostics); convertedArguments.Add(CreateConversion(argument.Syntax, argument, elementConversion, isCast, destType, diagnostics)); } BoundExpression result = new BoundConvertedTupleLiteral( sourceTuple.Syntax, sourceTuple.Type, convertedArguments.ToImmutableAndFree(), targetType); // We need to preserve any conversion that changes the type (even identity conversions), // or that was explicitly written in code (so that GetSemanticInfo can find the syntax in the bound tree). if (!isCast && targetType == destination) { return result; } // if we have a nullable cast combined with a name/dynamic cast // name/dynamic cast must happen before converting to nullable if (conversion.Kind == ConversionKind.ImplicitNullable && destinationWithoutNullable != targetType) { Debug.Assert(destinationWithoutNullable.Equals(targetType, ignoreDynamic: true)); result = new BoundConversion( syntax, result, Conversion.Identity, @checked: false, explicitCastInCode: isCast, constantValueOpt: ConstantValue.NotAvailable, type: destinationWithoutNullable) { WasCompilerGenerated = sourceTuple.WasCompilerGenerated }; } return new BoundConversion( syntax, result, conversion, @checked: false, explicitCastInCode: isCast, constantValueOpt: ConstantValue.NotAvailable, type: destination) { WasCompilerGenerated = sourceTuple.WasCompilerGenerated }; }
private BoundExpression CreateTupleLiteralConversion(SyntaxNode syntax, BoundTupleLiteral sourceTuple, Conversion conversion, bool isCast, TypeSymbol destination, DiagnosticBag diagnostics) { // We have a successful tuple conversion; rather than producing a separate conversion node // which is a conversion on top of a tuple literal, tuple conversion is an element-wise conversion of arguments. Debug.Assert((conversion.Kind == ConversionKind.ImplicitNullable) == destination.IsNullableType()); var destinationWithoutNullable = destination; var conversionWithoutNullable = conversion; if (conversion.Kind == ConversionKind.ImplicitNullable) { destinationWithoutNullable = destination.GetNullableUnderlyingType(); conversionWithoutNullable = conversion.UnderlyingConversions[0]; } Debug.Assert(conversionWithoutNullable.IsTupleLiteralConversion); NamedTypeSymbol targetType = (NamedTypeSymbol)destinationWithoutNullable; if (targetType.IsTupleType) { var destTupleType = (TupleTypeSymbol)targetType; // do not lose the original element names in the literal if different from names in the target TupleTypeSymbol.ReportNamesMismatchesIfAny(targetType, sourceTuple, diagnostics); // Come back to this, what about locations? (https://github.com/dotnet/roslyn/issues/11013) targetType = destTupleType.WithElementNames(sourceTuple.ArgumentNamesOpt); } var arguments = sourceTuple.Arguments; var convertedArguments = ArrayBuilder<BoundExpression>.GetInstance(arguments.Length); ImmutableArray<TypeSymbol> targetElementTypes = targetType.GetElementTypesOfTupleOrCompatible(); Debug.Assert(targetElementTypes.Length == arguments.Length, "converting a tuple literal to incompatible type?"); var underlyingConversions = conversionWithoutNullable.UnderlyingConversions; for (int i = 0; i < arguments.Length; i++) { var argument = arguments[i]; var destType = targetElementTypes[i]; var elementConversion = underlyingConversions[i]; convertedArguments.Add(CreateConversion(argument.Syntax, argument, elementConversion, isCast, destType, diagnostics)); } BoundExpression result = new BoundConvertedTupleLiteral( sourceTuple.Syntax, sourceTuple.Type, convertedArguments.ToImmutableAndFree(), targetType); if (sourceTuple.Type != destination) { // literal cast is applied to the literal result = new BoundConversion( sourceTuple.Syntax, result, conversion, @checked: false, explicitCastInCode: isCast, constantValueOpt: ConstantValue.NotAvailable, type: destination); } // If we had a cast in the code, keep conversion in the tree. // even though the literal is already converted to the target type. if (isCast) { result = new BoundConversion( syntax, result, Conversion.Identity, @checked: false, explicitCastInCode: isCast, constantValueOpt: ConstantValue.NotAvailable, type: destination); } return result; }
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); }
private void MakeOutputTypeInferences(Binder binder, BoundTupleLiteral argument, TypeSymbol formalType, ref HashSet<DiagnosticInfo> useSiteDiagnostics) { if (formalType.Kind != SymbolKind.NamedType) { // tuples can only match to tuples or tuple underlying types. return; } var destination = (NamedTypeSymbol)formalType; Debug.Assert(argument.Type == null, "should not need to dig into elements if tuple has natural type"); var sourceArguments = argument.Arguments; // check if the type is actually compatible type for a tuple of given cardinality if (!destination.IsTupleOrCompatibleWithTupleOfCardinality(sourceArguments.Length)) { return; } var destTypes = destination.GetElementTypesOfTupleOrCompatible(); Debug.Assert(sourceArguments.Length == destTypes.Length); for (int i = 0; i < sourceArguments.Length; i++) { var sourceArgument = sourceArguments[i]; var destType = destTypes[i]; MakeOutputTypeInferences(binder, sourceArgument, destType, ref useSiteDiagnostics); } }
/// <summary> /// For cases where the RHS of a deconstruction-declaration is a tuple literal, we merge type information from both the LHS and RHS. /// For cases where the RHS of a deconstruction-assignment is a tuple literal, the type information from the LHS determines the merged type, since all variables have a type. /// Returns null if a merged tuple type could not be fabricated. /// </summary> private static TypeSymbol MakeMergedTupleType(ArrayBuilder<DeconstructionVariable> lhsVariables, BoundTupleLiteral rhsLiteral, CSharpSyntaxNode syntax, CSharpCompilation compilation, DiagnosticBag diagnostics) { int leftLength = lhsVariables.Count; int rightLength = rhsLiteral.Arguments.Length; var typesBuilder = ArrayBuilder<TypeSymbol>.GetInstance(leftLength); for (int i = 0; i < rightLength; i++) { BoundExpression element = rhsLiteral.Arguments[i]; TypeSymbol mergedType = element.Type; if (i < leftLength) { var variable = lhsVariables[i]; if (variable.HasNestedVariables) { if (element.Kind == BoundKind.TupleLiteral) { // (variables) on the left and (elements) on the right mergedType = MakeMergedTupleType(variable.NestedVariables, (BoundTupleLiteral)element, syntax, compilation, diagnostics); } else if ((object)mergedType == null) { // (variables) on the left and null on the right Error(diagnostics, ErrorCode.ERR_DeconstructRequiresExpression, element.Syntax); } } else { if ((object)variable.Single.Type != null) { // typed-variable on the left mergedType = variable.Single.Type; } } } else { if ((object)mergedType == null) { // a typeless element on the right, matching no variable on the left Error(diagnostics, ErrorCode.ERR_DeconstructRequiresExpression, element.Syntax); } } typesBuilder.Add(mergedType); } if (typesBuilder.Any(t => t == null)) { typesBuilder.Free(); return null; } return TupleTypeSymbol.Create(locationOpt: null, elementTypes: typesBuilder.ToImmutableAndFree(), elementLocations: default(ImmutableArray<Location>), elementNames: default(ImmutableArray<string>), compilation: compilation, diagnostics: diagnostics); }
public override BoundNode VisitTupleLiteral(BoundTupleLiteral node) { throw ExceptionUtilities.Unreachable; }
/// <summary> /// For cases where the RHS of a deconstruction-declaration is a tuple literal, we merge type information from both the LHS and RHS. /// For cases where the RHS of a deconstruction-assignment is a tuple literal, the type information from the LHS determines the merged type, since all variables have a type. /// Returns null if a merged tuple type could not be fabricated. /// </summary> private static TypeSymbol MakeMergedTupleType(ArrayBuilder <DeconstructionVariable> lhsVariables, BoundTupleLiteral rhsLiteral, CSharpSyntaxNode syntax, CSharpCompilation compilation, DiagnosticBag diagnostics) { int leftLength = lhsVariables.Count; int rightLength = rhsLiteral.Arguments.Length; var typesBuilder = ArrayBuilder <TypeSymbol> .GetInstance(leftLength); var locationsBuilder = ArrayBuilder <Location> .GetInstance(leftLength); for (int i = 0; i < rightLength; i++) { BoundExpression element = rhsLiteral.Arguments[i]; TypeSymbol mergedType = element.Type; if (i < leftLength) { var variable = lhsVariables[i]; if (variable.HasNestedVariables) { if (element.Kind == BoundKind.TupleLiteral) { // (variables) on the left and (elements) on the right mergedType = MakeMergedTupleType(variable.NestedVariables, (BoundTupleLiteral)element, syntax, compilation, diagnostics); } else if ((object)mergedType == null) { // (variables) on the left and null on the right Error(diagnostics, ErrorCode.ERR_DeconstructRequiresExpression, element.Syntax); } } else { if ((object)variable.Single.Type != null) { // typed-variable on the left mergedType = variable.Single.Type; } } } else { if ((object)mergedType == null) { // a typeless element on the right, matching no variable on the left Error(diagnostics, ErrorCode.ERR_DeconstructRequiresExpression, element.Syntax); } } typesBuilder.Add(mergedType); locationsBuilder.Add(element.Syntax.Location); } if (typesBuilder.Any(t => t == null)) { typesBuilder.Free(); locationsBuilder.Free(); return(null); } // The tuple created here is not identical to the one created by // DeconstructionVariablesAsTuple. It represents a smaller // tree of types used for figuring out natural types in tuple literal. return(TupleTypeSymbol.Create( locationOpt: null, elementTypes: typesBuilder.ToImmutableAndFree(), elementLocations: locationsBuilder.ToImmutableAndFree(), elementNames: default(ImmutableArray <string>), compilation: compilation, diagnostics: diagnostics, shouldCheckConstraints: true, errorPositions: default(ImmutableArray <bool>), syntax: syntax)); }
public override BoundNode VisitTupleLiteral(BoundTupleLiteral node) { if (_inExpressionLambda) { Error(ErrorCode.ERR_ExpressionTreeContainsTupleLiteral, node); } return base.VisitTupleLiteral(node); }