private BoundTupleLiteral DeconstructVariablesAsTuple(SyntaxNode syntax, ArrayBuilder <DeconstructionVariable> variables) { int count = variables.Count; var valuesBuilder = ArrayBuilder <BoundExpression> .GetInstance(count); var typesBuilder = ArrayBuilder <TypeSymbol> .GetInstance(count); var locationsBuilder = ArrayBuilder <Location> .GetInstance(count); foreach (var variable in variables) { if (variable.HasNestedVariables) { var nestedTuple = DeconstructVariablesAsTuple(variable.Syntax, variable.NestedVariables); valuesBuilder.Add(nestedTuple); typesBuilder.Add(nestedTuple.Type); } else { var single = variable.Single; valuesBuilder.Add(single); typesBuilder.Add(single.Type); } locationsBuilder.Add(variable.Syntax.Location); } // MakeDeconstructionConstructionStep constructs the final tuple type and checks constraints already. var type = TupleTypeSymbol.Create(syntax.Location, typesBuilder.ToImmutableAndFree(), locationsBuilder.ToImmutableAndFree(), elementNames: default(ImmutableArray <string>), compilation: this.Compilation, shouldCheckConstraints: false); return(new BoundTupleLiteral(syntax: syntax, argumentNamesOpt: default(ImmutableArray <string>), arguments: valuesBuilder.ToImmutableAndFree(), type: type)); }
private void AccessTupleFields(BoundDeconstructionAssignmentOperator node, BoundDeconstructionDeconstructStep deconstruction, ArrayBuilder <LocalSymbol> temps, ArrayBuilder <BoundExpression> stores, ArrayBuilder <BoundValuePlaceholderBase> placeholders) { var target = PlaceholderReplacement(deconstruction.InputPlaceholder); var tupleType = target.Type.IsTupleType ? target.Type : TupleTypeSymbol.Create((NamedTypeSymbol)target.Type); var tupleElementTypes = tupleType.TupleElementTypes; var numElements = tupleElementTypes.Length; SyntaxNode syntax = node.Syntax; // save the target as we need to access it multiple times BoundAssignmentOperator assignmentToTemp; BoundLocal savedTuple = _factory.StoreToTemp(target, out assignmentToTemp); stores.Add(assignmentToTemp); temps.Add(savedTuple.LocalSymbol); // list the tuple fields accessors var fields = tupleType.TupleElements; for (int i = 0; i < numElements; i++) { var field = fields[i]; DiagnosticInfo useSiteInfo = field.GetUseSiteDiagnostic(); if ((object)useSiteInfo != null && useSiteInfo.Severity == DiagnosticSeverity.Error) { Symbol.ReportUseSiteDiagnostic(useSiteInfo, _diagnostics, syntax.Location); } var fieldAccess = MakeTupleFieldAccess(syntax, field, savedTuple, null, LookupResultKind.Empty); AddPlaceholderReplacement(deconstruction.OutputPlaceholders[i], fieldAccess); placeholders.Add(deconstruction.OutputPlaceholders[i]); } }
private BoundTupleLiteral DeconstructionVariablesAsTuple(CSharpSyntaxNode syntax, ArrayBuilder <DeconstructionVariable> variables, DiagnosticBag diagnostics, bool hasErrors) { int count = variables.Count; var valuesBuilder = ArrayBuilder <BoundExpression> .GetInstance(count); var typesBuilder = ArrayBuilder <TypeSymbol> .GetInstance(count); var locationsBuilder = ArrayBuilder <Location> .GetInstance(count); foreach (var variable in variables) { if (variable.HasNestedVariables) { var nestedTuple = DeconstructionVariablesAsTuple(variable.Syntax, variable.NestedVariables, diagnostics, hasErrors); valuesBuilder.Add(nestedTuple); typesBuilder.Add(nestedTuple.Type); } else { var single = variable.Single; valuesBuilder.Add(single); typesBuilder.Add(single.Type); } locationsBuilder.Add(variable.Syntax.Location); } var type = TupleTypeSymbol.Create(syntax.Location, typesBuilder.ToImmutableAndFree(), locationsBuilder.ToImmutableAndFree(), elementNames: default(ImmutableArray <string>), compilation: this.Compilation, shouldCheckConstraints: !hasErrors, syntax: syntax, diagnostics: hasErrors ? null : diagnostics); return(new BoundTupleLiteral(syntax: syntax, argumentNamesOpt: default(ImmutableArray <string>), arguments: valuesBuilder.ToImmutableAndFree(), type: type)); }
private NamedTypeSymbol DecodeNamedType(NamedTypeSymbol type) { // First decode the type arguments var typeArgs = type.TypeArgumentsNoUseSiteDiagnostics; var decodedArgs = DecodeTypeArguments(typeArgs); NamedTypeSymbol decodedType = type; // Now check the container NamedTypeSymbol containingType = type.ContainingType; NamedTypeSymbol decodedContainingType; if ((object)containingType != null && 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) { var newTypeArgs = type.HasTypeArgumentsCustomModifiers ? decodedArgs.SelectAsArray((t, i, m) => new TypeWithModifiers(t, m.GetTypeArgumentCustomModifiers(i)), type) : decodedArgs.SelectAsArray(TypeMap.TypeSymbolAsTypeWithModifiers); 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(newTypeArgs)); } decodedType = type.ConstructedFrom.Construct(newTypeArgs, unbound: false); } // Now decode into a tuple, if it is one int tupleCardinality; if (decodedType.IsTupleCompatible(out tupleCardinality)) { var elementNames = EatElementNamesIfAvailable(tupleCardinality); Debug.Assert(elementNames.IsDefault || elementNames.Length == tupleCardinality); decodedType = TupleTypeSymbol.Create(decodedType, elementNames); } return(decodedType); }
/// <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)); }
private NamedTypeSymbol TransformTupleType(NamedTypeSymbol tupleType, bool isContaining) { Debug.Assert(tupleType.IsTupleType); var underlying = tupleType.TupleUnderlyingType; var transformedUnderlying = TransformNamedType(underlying, isContaining); return(TupleTypeSymbol.Create(transformedUnderlying, tupleType.TupleElementNames)); }
private BoundTupleLiteral DeconstructionVariablesAsTuple(CSharpSyntaxNode syntax, ArrayBuilder <DeconstructionVariable> variables, DiagnosticBag diagnostics, bool hasErrors) { int count = variables.Count; var valuesBuilder = ArrayBuilder <BoundExpression> .GetInstance(count); var typesBuilder = ArrayBuilder <TypeSymbol> .GetInstance(count); var locationsBuilder = ArrayBuilder <Location> .GetInstance(count); var namesBuilder = ArrayBuilder <string> .GetInstance(count); foreach (var variable in variables) { if (variable.HasNestedVariables) { var nestedTuple = DeconstructionVariablesAsTuple(variable.Syntax, variable.NestedVariables, diagnostics, hasErrors); valuesBuilder.Add(nestedTuple); typesBuilder.Add(nestedTuple.Type); namesBuilder.Add(null); } else { var single = variable.Single; valuesBuilder.Add(single); typesBuilder.Add(single.Type); namesBuilder.Add(ExtractDeconstructResultElementName(single)); } locationsBuilder.Add(variable.Syntax.Location); } var uniqueFieldNames = PooledHashSet <string> .GetInstance(); RemoveDuplicateInferredTupleNames(namesBuilder, uniqueFieldNames); uniqueFieldNames.Free(); ImmutableArray <string> tupleNames = namesBuilder.ToImmutableAndFree(); bool disallowInferredNames = this.Compilation.LanguageVersion.DisallowInferredTupleElementNames(); var inferredPositions = tupleNames.SelectAsArray(n => n != null); var type = TupleTypeSymbol.Create(syntax.Location, typesBuilder.ToImmutableAndFree(), locationsBuilder.ToImmutableAndFree(), tupleNames, this.Compilation, shouldCheckConstraints: !hasErrors, errorPositions: disallowInferredNames ? inferredPositions : default(ImmutableArray <bool>), syntax: syntax, diagnostics: hasErrors ? null : diagnostics); // Always track the inferred positions in the bound node, so that conversions don't produce a warning // for "dropped names" on tuple literal when the name was inferred. return(new BoundTupleLiteral(syntax, tupleNames, inferredPositions, arguments: valuesBuilder.ToImmutableAndFree(), type: type)); }
/// <summary> /// This method recurses through leftTargets, right and conversion at the same time. /// As it does, it collects side-effects into the proper buckets (init, deconstructions, conversions, assignments). /// /// The side-effects from the right initially go into the init bucket. But once we started drilling into a Deconstruct /// invocation, subsequent side-effects from the right go into the deconstructions bucket (otherwise they would /// be evaluated out of order). /// </summary> private BoundTupleLiteral ApplyDeconstructionConversion(ArrayBuilder <Binder.DeconstructionVariable> leftTargets, BoundExpression right, Conversion conversion, ArrayBuilder <LocalSymbol> temps, DeconstructionSideEffects effects, bool inInit) { Debug.Assert(conversion.Kind == ConversionKind.Deconstruction); ImmutableArray <BoundExpression> rightParts = GetRightParts(right, conversion, ref temps, effects, ref inInit); ImmutableArray <Conversion> underlyingConversions = conversion.UnderlyingConversions; Debug.Assert(!underlyingConversions.IsDefault); Debug.Assert(leftTargets.Count == rightParts.Length && leftTargets.Count == conversion.UnderlyingConversions.Length); var builder = ArrayBuilder <BoundExpression> .GetInstance(leftTargets.Count); for (int i = 0; i < leftTargets.Count; i++) { BoundExpression resultPart; if (leftTargets[i].HasNestedVariables) { resultPart = ApplyDeconstructionConversion(leftTargets[i].NestedVariables, rightParts[i], underlyingConversions[i], temps, effects, inInit); } else { var rightPart = rightParts[i]; if (inInit) { rightPart = EvaluateSideEffectingArgumentToTemp(VisitExpression(rightPart), inInit ? effects.init : effects.deconstructions, ref temps); } BoundExpression leftTarget = leftTargets[i].Single; resultPart = EvaluateConversionToTemp(rightPart, underlyingConversions[i], leftTarget.Type, temps, effects.conversions); if (leftTarget.Kind != BoundKind.DiscardExpression) { effects.assignments.Add(MakeAssignmentOperator(resultPart.Syntax, leftTarget, resultPart, leftTarget.Type, used: true, isChecked: false, isCompoundAssignment: false)); } } builder.Add(resultPart); } var tupleType = TupleTypeSymbol.Create(locationOpt: null, elementTypes: builder.SelectAsArray(e => e.Type), elementLocations: default(ImmutableArray <Location>), elementNames: default(ImmutableArray <string>), compilation: _compilation, shouldCheckConstraints: false); return(new BoundTupleLiteral(right.Syntax, default(ImmutableArray <string>), builder.ToImmutableAndFree(), tupleType)); }
private NamedTypeSymbol TransformTupleType(NamedTypeSymbol tupleType, bool isContaining) { Debug.Assert(tupleType.IsTupleType); var underlying = tupleType.TupleUnderlyingType; var transformedUnderlying = TransformNamedType(underlying, isContaining); if ((object)transformedUnderlying == null) { // Bail, something is wrong with the flags. // the dynamic transformation should be ignored. return(null); } return(TupleTypeSymbol.Create(transformedUnderlying, tupleType.TupleElementNames)); }
private BoundTupleLiteral DeconstructionVariablesAsTuple(CSharpSyntaxNode syntax, ArrayBuilder <DeconstructionVariable> variables, DiagnosticBag diagnostics, bool ignoreDiagnosticsFromTuple) { int count = variables.Count; var valuesBuilder = ArrayBuilder <BoundExpression> .GetInstance(count); var typesBuilder = ArrayBuilder <TypeSymbol> .GetInstance(count); var locationsBuilder = ArrayBuilder <Location> .GetInstance(count); var namesBuilder = ArrayBuilder <string> .GetInstance(count); foreach (var variable in variables) { if (variable.HasNestedVariables) { var nestedTuple = DeconstructionVariablesAsTuple(variable.Syntax, variable.NestedVariables, diagnostics, ignoreDiagnosticsFromTuple); valuesBuilder.Add(nestedTuple); typesBuilder.Add(nestedTuple.Type); namesBuilder.Add(null); } else { var single = variable.Single; valuesBuilder.Add(single); typesBuilder.Add(single.Type); namesBuilder.Add(ExtractDeconstructResultElementName(single)); } locationsBuilder.Add(variable.Syntax.Location); } var uniqueFieldNames = PooledHashSet <string> .GetInstance(); RemoveDuplicateInferredTupleNames(namesBuilder, uniqueFieldNames); uniqueFieldNames.Free(); ImmutableArray <string> tupleNames = namesBuilder.ToImmutableAndFree(); bool disallowInferredNames = this.Compilation.LanguageVersion.DisallowInferredTupleElementNames(); var inferredPositions = tupleNames.SelectAsArray(n => n != null); var type = TupleTypeSymbol.Create(syntax.Location, typesBuilder.ToImmutableAndFree(), locationsBuilder.ToImmutableAndFree(), tupleNames, this.Compilation, shouldCheckConstraints: !ignoreDiagnosticsFromTuple, errorPositions: disallowInferredNames ? inferredPositions : default,
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); 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 Symbol VisitNamedType(NamedTypeSymbol sourceType) { var originalDef = sourceType.OriginalDefinition; if ((object)originalDef != (object)sourceType) { HashSet <DiagnosticInfo> useSiteDiagnostics = null; var typeArguments = sourceType.GetAllTypeArguments(ref useSiteDiagnostics); var otherDef = (NamedTypeSymbol)this.Visit(originalDef); if ((object)otherDef == null) { return(null); } var otherTypeParameters = otherDef.GetAllTypeParameters(); bool translationFailed = false; var otherTypeArguments = typeArguments.SelectAsArray((t, v) => { var newType = (TypeSymbol)v.Visit(t.Type); if ((object)newType == null) { // For a newly added type, there is no match in the previous generation, so it could be null. translationFailed = true; newType = t.Type; } return(new TypeWithModifiers(newType, v.VisitCustomModifiers(t.CustomModifiers))); }, this); if (translationFailed) { // For a newly added type, there is no match in the previous generation, so it could be null. return(null); } // TODO: LambdaFrame has alpha renamed type parameters, should we rather fix that? var typeMap = new TypeMap(otherTypeParameters, otherTypeArguments, allowAlpha: true); return(typeMap.SubstituteNamedType(otherDef)); } else if (sourceType.IsTupleType) { var otherDef = (NamedTypeSymbol)this.Visit(sourceType.TupleUnderlyingType); if ((object)otherDef == null || !otherDef.IsTupleOrCompatibleWithTupleOfCardinality(sourceType.TupleElementTypes.Length)) { return(null); } return(TupleTypeSymbol.Create(otherDef, sourceType.TupleElementNames)); } Debug.Assert(sourceType.IsDefinition); var otherContainer = this.Visit(sourceType.ContainingSymbol); // Containing type will be missing from other assembly // if the type was added in the (newer) source assembly. if ((object)otherContainer == null) { return(null); } switch (otherContainer.Kind) { case SymbolKind.Namespace: if (AnonymousTypeManager.IsAnonymousTypeTemplate(sourceType)) { Debug.Assert((object)otherContainer == (object)_otherAssembly.GlobalNamespace); AnonymousTypeValue value; this.TryFindAnonymousType(sourceType, out value); return((NamedTypeSymbol)value.Type); } else if (sourceType.IsAnonymousType) { return(this.Visit(AnonymousTypeManager.TranslateAnonymousTypeSymbol(sourceType))); } else { return(FindMatchingNamespaceMember((NamespaceSymbol)otherContainer, sourceType, AreNamedTypesEqual)); } case SymbolKind.NamedType: return(FindMatchingNamedTypeMember((NamedTypeSymbol)otherContainer, sourceType, AreNamedTypesEqual)); default: throw ExceptionUtilities.UnexpectedValue(otherContainer.Kind); } }