Skip to content

Commit

Permalink
Allow deconstruction of 'default' literal
Browse files Browse the repository at this point in the history
  • Loading branch information
jcouv committed Mar 18, 2018
1 parent 56502b6 commit 08eb608
Show file tree
Hide file tree
Showing 18 changed files with 386 additions and 15 deletions.
56 changes: 41 additions & 15 deletions src/Compilers/CSharp/Portable/Binder/Binder_Deconstruct.cs
Original file line number Diff line number Diff line change
Expand Up @@ -198,28 +198,48 @@ private static bool IsDeconstructionResultUsed(ExpressionSyntax left)
/// <summary>When boundRHS is a tuple literal, fix it up by inferring its types.</summary>
private BoundExpression FixTupleLiteral(ArrayBuilder<DeconstructionVariable> checkedVariables, BoundExpression boundRHS, CSharpSyntaxNode syntax, DiagnosticBag diagnostics)
{
if (boundRHS.Kind == BoundKind.TupleLiteral)
if (DeconstructionTypeIsFixable(boundRHS, diagnostics))
{
// Let's fix the literal up by figuring out its type
// Let's fix the tuple (or `default`) 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

// If we already have diagnostics at this point, it is not worth collecting likely duplicate diagnostics from making the merged type
bool hadErrors = diagnostics.HasAnyErrors();
TypeSymbol mergedTupleType = MakeMergedTupleType(checkedVariables, (BoundTupleLiteral)boundRHS, syntax, Compilation, hadErrors ? null : diagnostics);
TypeSymbol mergedTupleType = MakeMergedTupleType(checkedVariables, boundRHS, syntax, Compilation, hadErrors ? null : diagnostics);
if ((object)mergedTupleType != null)
{
boundRHS = GenerateConversionForAssignment(mergedTupleType, boundRHS, diagnostics);
}
}
else if ((object)boundRHS.Type == null)
else if (boundRHS.Type is null)
{
Error(diagnostics, ErrorCode.ERR_DeconstructRequiresExpression, boundRHS.Syntax);
}

return boundRHS;
}

private static bool DeconstructionTypeIsFixable(BoundExpression boundRHS, DiagnosticBag diagnostics)
{
if (boundRHS.Kind == BoundKind.TupleLiteral)
{
return true;
}

if (boundRHS.IsLiteralDefault())
{
if (diagnostics != null)
{
CheckFeatureAvailability(boundRHS.Syntax, MessageID.IDS_FeatureDeconstructDefault, diagnostics);
}

return true;
}

return false;
}

/// <summary>
/// Recursively builds a Conversion object with Kind=Deconstruction including information about any necessary
/// Deconstruct method and any element-wise conversion.
Expand Down Expand Up @@ -449,31 +469,37 @@ private string GetDebuggerDisplay()
/// <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.
/// For cases where the RHS is a `default` literal, the type information from the LHS determines the merged 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)
private static TypeSymbol MakeMergedTupleType(ArrayBuilder<DeconstructionVariable> lhsVariables, BoundExpression rhs, CSharpSyntaxNode syntax, CSharpCompilation compilation, DiagnosticBag diagnostics)
{
Debug.Assert(rhs.Kind == BoundKind.TupleLiteral || rhs.IsLiteralDefault());

bool rhsIsDefault = rhs.IsLiteralDefault();
BoundTupleLiteral rhsAsTupleLiteral = rhsIsDefault ? null : (BoundTupleLiteral)rhs;
int leftLength = lhsVariables.Count;
int rightLength = rhsLiteral.Arguments.Length;
int rightLength = rhsIsDefault ? leftLength : rhsAsTupleLiteral.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];
// In the `default` literal case, we'll pretend that it is a tuple literal with all elements also `default`.
BoundExpression element = rhsIsDefault ? rhs : rhsAsTupleLiteral.Arguments[i];
TypeSymbol mergedType = element.Type;

if (i < leftLength)
{
var variable = lhsVariables[i];
if (variable.HasNestedVariables)
{
if (element.Kind == BoundKind.TupleLiteral)
if (DeconstructionTypeIsFixable(element, diagnostics))
{
// (variables) on the left and (elements) on the right
mergedType = MakeMergedTupleType(variable.NestedVariables, (BoundTupleLiteral)element, syntax, compilation, diagnostics);
// (variables) on the left and (elements) or `default` on the right
mergedType = MakeMergedTupleType(variable.NestedVariables, element, syntax, compilation, diagnostics);
}
else if ((object)mergedType == null)
else if (mergedType is null)
{
// (variables) on the left and null on the right
Error(diagnostics, ErrorCode.ERR_DeconstructRequiresExpression, element.Syntax);
Expand All @@ -490,7 +516,7 @@ private static TypeSymbol MakeMergedTupleType(ArrayBuilder<DeconstructionVariabl
}
else
{
if ((object)mergedType == null)
if (mergedType is null)
{
// a typeless element on the right, matching no variable on the left
Error(diagnostics, ErrorCode.ERR_DeconstructRequiresExpression, element.Syntax);
Expand All @@ -501,7 +527,7 @@ private static TypeSymbol MakeMergedTupleType(ArrayBuilder<DeconstructionVariabl
locationsBuilder.Add(element.Syntax.Location);
}

if (typesBuilder.Any(t => t == null))
if (typesBuilder.Any(t => t is null))
{
typesBuilder.Free();
locationsBuilder.Free();
Expand All @@ -515,11 +541,11 @@ private static TypeSymbol MakeMergedTupleType(ArrayBuilder<DeconstructionVariabl
locationOpt: null,
elementTypes: typesBuilder.ToImmutableAndFree(),
elementLocations: locationsBuilder.ToImmutableAndFree(),
elementNames: default(ImmutableArray<string>),
elementNames: default,
compilation: compilation,
diagnostics: diagnostics,
shouldCheckConstraints: true,
errorPositions: default(ImmutableArray<bool>),
errorPositions: default,
syntax: syntax);
}

Expand Down
9 changes: 9 additions & 0 deletions src/Compilers/CSharp/Portable/CSharpResources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/Compilers/CSharp/Portable/CSharpResources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,9 @@
<data name="IDS_FeatureTupleEquality" xml:space="preserve">
<value>tuple equality</value>
</data>
<data name="IDS_FeatureDeconstructDefault" xml:space="preserve">
<value>deconstruction on default</value>
</data>
<data name="IDS_FeatureNullable" xml:space="preserve">
<value>nullable types</value>
</data>
Expand Down
2 changes: 2 additions & 0 deletions src/Compilers/CSharp/Portable/Errors/MessageID.cs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ internal enum MessageID
IDS_FeatureStackAllocInitializer = MessageBase + 12740,
IDS_FeatureTupleEquality = MessageBase + 12741,
IDS_FeatureExpressionVariablesInQueriesAndInitializers = MessageBase + 12742,
IDS_FeatureDeconstructDefault = MessageBase + 12743,
}

// Message IDs may refer to strings that need to be localized.
Expand Down Expand Up @@ -201,6 +202,7 @@ internal static LanguageVersion RequiredVersion(this MessageID feature)
case MessageID.IDS_FeatureAttributesOnBackingFields: // semantic check
case MessageID.IDS_FeatureImprovedOverloadCandidates: // semantic check
case MessageID.IDS_FeatureTupleEquality: // semantic check
case MessageID.IDS_FeatureDeconstructDefault: // semantic check
case MessageID.IDS_FeatureRefReassignment:
case MessageID.IDS_FeatureRefFor:
case MessageID.IDS_FeatureRefForEach:
Expand Down
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -8715,6 +8715,11 @@ Pokud chcete odstranit toto varování, můžete místo toho použít /reference
<target state="new">declaration of expression variables in member initializers and queries</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureDeconstructDefault">
<source>deconstruction on default</source>
<target state="new">deconstruction on default</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -8715,6 +8715,11 @@ Um die Warnung zu beheben, können Sie stattdessen /reference verwenden (Einbett
<target state="new">declaration of expression variables in member initializers and queries</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureDeconstructDefault">
<source>deconstruction on default</source>
<target state="new">deconstruction on default</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -8715,6 +8715,11 @@ Para eliminar la advertencia puede usar /reference (establezca la propiedad Embe
<target state="new">declaration of expression variables in member initializers and queries</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureDeconstructDefault">
<source>deconstruction on default</source>
<target state="new">deconstruction on default</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -8715,6 +8715,11 @@ Pour supprimer l'avertissement, vous pouvez utiliser la commande /reference (dé
<target state="new">declaration of expression variables in member initializers and queries</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureDeconstructDefault">
<source>deconstruction on default</source>
<target state="new">deconstruction on default</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -8715,6 +8715,11 @@ Per rimuovere l'avviso, è invece possibile usare /reference (impostare la propr
<target state="new">declaration of expression variables in member initializers and queries</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureDeconstructDefault">
<source>deconstruction on default</source>
<target state="new">deconstruction on default</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -8715,6 +8715,11 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<target state="new">declaration of expression variables in member initializers and queries</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureDeconstructDefault">
<source>deconstruction on default</source>
<target state="new">deconstruction on default</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -8715,6 +8715,11 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<target state="new">declaration of expression variables in member initializers and queries</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureDeconstructDefault">
<source>deconstruction on default</source>
<target state="new">deconstruction on default</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -8715,6 +8715,11 @@ Aby usunąć ostrzeżenie, możesz zamiast tego użyć opcji /reference (ustaw w
<target state="new">declaration of expression variables in member initializers and queries</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureDeconstructDefault">
<source>deconstruction on default</source>
<target state="new">deconstruction on default</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -8715,6 +8715,11 @@ Para incorporar informações de tipo de interoperabilidade para os dois assembl
<target state="new">declaration of expression variables in member initializers and queries</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureDeconstructDefault">
<source>deconstruction on default</source>
<target state="new">deconstruction on default</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -8715,6 +8715,11 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<target state="new">declaration of expression variables in member initializers and queries</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureDeconstructDefault">
<source>deconstruction on default</source>
<target state="new">deconstruction on default</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -8715,6 +8715,11 @@ Uyarıyı kaldırmak için, /reference kullanabilirsiniz (Birlikte Çalışma T
<target state="new">declaration of expression variables in member initializers and queries</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureDeconstructDefault">
<source>deconstruction on default</source>
<target state="new">deconstruction on default</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -8715,6 +8715,11 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<target state="new">declaration of expression variables in member initializers and queries</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureDeconstructDefault">
<source>deconstruction on default</source>
<target state="new">deconstruction on default</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -8715,6 +8715,11 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<target state="new">declaration of expression variables in member initializers and queries</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureDeconstructDefault">
<source>deconstruction on default</source>
<target state="new">deconstruction on default</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
Loading

0 comments on commit 08eb608

Please sign in to comment.