private NamedTypeSymbol DecodeNamedType(NamedTypeSymbol type) { // First decode the type arguments var typeArgs = type.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics; var decodedArgs = DecodeTypeArguments(typeArgs); NamedTypeSymbol decodedType = type; // Now check the container NamedTypeSymbol containingType = type.ContainingType; NamedTypeSymbol?decodedContainingType; if (containingType is object && containingType.IsGenericType) { decodedContainingType = DecodeNamedType(containingType); Debug.Assert(decodedContainingType.IsGenericType); } else { decodedContainingType = containingType; } // Replace the type if necessary var containerChanged = !ReferenceEquals(decodedContainingType, containingType); var typeArgsChanged = typeArgs != decodedArgs; if (typeArgsChanged || containerChanged) { if (containerChanged) { decodedType = decodedType.OriginalDefinition.AsMember(decodedContainingType); // If the type is nested, e.g. Outer<T>.Inner<V>, then Inner is definitely // not a tuple, since we know all tuple-compatible types (System.ValueTuple) // are not nested types. Thus, it is safe to return without checking if // Inner is a tuple. return(decodedType.ConstructIfGeneric(decodedArgs)); } decodedType = type.ConstructedFrom.Construct(decodedArgs, unbound: false); } // Now decode into a tuple, if it is one if (decodedType.IsTupleType) { int tupleCardinality = decodedType.TupleElementTypesWithAnnotations.Length; if (tupleCardinality > 0) { var elementNames = EatElementNamesIfAvailable(tupleCardinality); Debug.Assert(elementNames.IsDefault || elementNames.Length == tupleCardinality); decodedType = NamedTypeSymbol.CreateTuple(decodedType, elementNames); } } return(decodedType); }
private BoundTupleExpression DeconstructionVariablesAsTuple(CSharpSyntaxNode syntax, ArrayBuilder <DeconstructionVariable> variables, DiagnosticBag diagnostics, bool ignoreDiagnosticsFromTuple) { int count = variables.Count; var valuesBuilder = ArrayBuilder <BoundExpression> .GetInstance(count); var typesWithAnnotationsBuilder = ArrayBuilder <TypeWithAnnotations> .GetInstance(count); var locationsBuilder = ArrayBuilder <Location> .GetInstance(count); var namesBuilder = ArrayBuilder <string> .GetInstance(count); foreach (var variable in variables) { BoundExpression value; if (variable.HasNestedVariables) { value = DeconstructionVariablesAsTuple(variable.Syntax, variable.NestedVariables, diagnostics, ignoreDiagnosticsFromTuple); namesBuilder.Add(null); } else { value = variable.Single; namesBuilder.Add(ExtractDeconstructResultElementName(value)); } valuesBuilder.Add(value); typesWithAnnotationsBuilder.Add(TypeWithAnnotations.Create(value.Type)); locationsBuilder.Add(variable.Syntax.Location); } ImmutableArray <BoundExpression> arguments = valuesBuilder.ToImmutableAndFree(); var uniqueFieldNames = PooledHashSet <string> .GetInstance(); RemoveDuplicateInferredTupleNamesAndFreeIfEmptied(ref namesBuilder, uniqueFieldNames); uniqueFieldNames.Free(); ImmutableArray <string> tupleNames = namesBuilder is null ? default : namesBuilder.ToImmutableAndFree(); ImmutableArray <bool> inferredPositions = tupleNames.IsDefault ? default : tupleNames.SelectAsArray(n => n != null); bool disallowInferredNames = this.Compilation.LanguageVersion.DisallowInferredTupleElementNames(); var type = NamedTypeSymbol.CreateTuple( syntax.Location, typesWithAnnotationsBuilder.ToImmutableAndFree(), locationsBuilder.ToImmutableAndFree(), tupleNames, this.Compilation, shouldCheckConstraints: !ignoreDiagnosticsFromTuple, includeNullability: false, errorPositions: disallowInferredNames ? inferredPositions : default,
/// <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 typesWithAnnotationsBuilder = ArrayBuilder <TypeWithAnnotations> .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); } } typesWithAnnotationsBuilder.Add(TypeWithAnnotations.Create(mergedType)); locationsBuilder.Add(element.Syntax.Location); } if (typesWithAnnotationsBuilder.Any(t => !t.HasType)) { typesWithAnnotationsBuilder.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(NamedTypeSymbol.CreateTuple( locationOpt: null, elementTypesWithAnnotations: typesWithAnnotationsBuilder.ToImmutableAndFree(), elementLocations: locationsBuilder.ToImmutableAndFree(), elementNames: default(ImmutableArray <string>), compilation: compilation, diagnostics: diagnostics, shouldCheckConstraints: true, includeNullability: false, errorPositions: default(ImmutableArray <bool>), syntax: syntax)); }