提交 29a1b9af 编写于 作者: V VSadov

CR feedback on tuple type implicit conversions

上级 7a7a1833
......@@ -4,6 +4,7 @@
using System.Diagnostics;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Roslyn.Utilities;
using System.Collections.Immutable;
namespace Microsoft.CodeAnalysis.CSharp
{
......@@ -92,7 +93,7 @@ internal partial class Binder
if (conversion.IsTupleLiteral ||
(conversion.Kind == ConversionKind.ImplicitNullable && source.Kind == BoundKind.TupleLiteral))
{
return CreateTupleConversion(syntax, (BoundTupleLiteral)source, conversion, isCast, destination, diagnostics);
return CreateTupleLiteralConversion(syntax, (BoundTupleLiteral)source, conversion, isCast, destination, diagnostics);
}
if (conversion.IsUserDefined)
......@@ -330,7 +331,7 @@ private BoundExpression CreateMethodGroupConversion(CSharpSyntaxNode syntax, Bou
return new BoundConversion(syntax, group, conversion, @checked: false, explicitCastInCode: isCast, constantValueOpt: ConstantValue.NotAvailable, type: destination, hasErrors: hasErrors) { WasCompilerGenerated = source.WasCompilerGenerated };
}
private BoundExpression CreateTupleConversion(CSharpSyntaxNode syntax, BoundTupleLiteral sourceTuple, Conversion conversion, bool isCast, TypeSymbol destination, DiagnosticBag diagnostics)
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.
......@@ -355,10 +356,9 @@ private BoundExpression CreateTupleConversion(CSharpSyntaxNode syntax, BoundTupl
var arguments = sourceTuple.Arguments;
var convertedArguments = ArrayBuilder<BoundExpression>.GetInstance(arguments.Length);
var targetElementTypes = ArrayBuilder<TypeSymbol>.GetInstance(arguments.Length);
TupleTypeSymbol.AddElementTypes(targetType, targetElementTypes);
Debug.Assert(targetElementTypes.Count == arguments.Length, "converting a tuple literal to incompatible type?");
ImmutableArray<TypeSymbol> targetElementTypes = targetType.GetElementTypesIfTupleOrCompatible();
Debug.Assert(targetElementTypes.Length == arguments.Length, "converting a tuple literal to incompatible type?");
for (int i = 0; i < arguments.Length; i++)
{
......@@ -367,8 +367,6 @@ private BoundExpression CreateTupleConversion(CSharpSyntaxNode syntax, BoundTupl
convertedArguments.Add(CreateConversion(argument, destType, diagnostics));
}
targetElementTypes.Free();
BoundExpression result = new BoundConvertedTupleLiteral(
sourceTuple.Syntax,
sourceTuple.Type,
......
......@@ -47,7 +47,7 @@ public struct Conversion : IEquatable<Conversion>
private readonly UserDefinedConversionResult _conversionResult; //no effect on Equals/GetHashCode
internal readonly ConversionKind _kind;
private readonly ConversionKind _kind;
private readonly byte _flags;
private const byte IsExtensionMethodMask = 1 << 0;
......
......@@ -715,29 +715,21 @@ protected override bool HasImplicitTupleLiteralConversion(BoundExpression source
return false;
}
var targetElementTypes = ArrayBuilder<TypeSymbol>.GetInstance(arguments.Length);
TupleTypeSymbol.AddElementTypes((NamedTypeSymbol)destination, targetElementTypes);
Debug.Assert(arguments.Length == targetElementTypes.Count);
ImmutableArray<TypeSymbol> targetElementTypes = destination.GetElementTypesIfTupleOrCompatible();
Debug.Assert(arguments.Length == targetElementTypes.Length);
try
// check arguments against flattened list of target element types
for (int i = 0; i < arguments.Length; i++)
{
// check arguments against flattened list of target element types
for (int i = 0; i < arguments.Length; i++)
var argument = arguments[i];
var result = ClassifyImplicitConversionFromExpression(argument, targetElementTypes[i], ref useSiteDiagnostics);
if (!result.Exists)
{
var argument = arguments[i];
var result = ClassifyImplicitConversionFromExpression(argument, targetElementTypes[i], ref useSiteDiagnostics);
if (!result.Exists)
{
return false;
}
return false;
}
return true;
}
finally
{
targetElementTypes.Free();
}
return true;
}
protected override Conversion GetInterpolatedStringConversion(BoundInterpolatedString source, TypeSymbol destination, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
......
......@@ -7,6 +7,7 @@
using Microsoft.CodeAnalysis.Text;
using System.Collections.Generic;
using Roslyn.Utilities;
using System.Collections.Immutable;
namespace Microsoft.CodeAnalysis.CSharp
{
......@@ -923,15 +924,12 @@ private bool HasImplicitNullableConversion(TypeSymbol source, TypeSymbol destina
private bool HasImplicitTupleConversion(TypeSymbol source, TypeSymbol destination, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
{
if (!source.IsTupleType || !destination.IsTupleType)
{
return false;
}
var sourceTypes = source.TupleElementTypes;
var destTypes = destination.TupleElementTypes;
ImmutableArray<TypeSymbol> sourceTypes;
ImmutableArray<TypeSymbol> destTypes;
if (sourceTypes.Length != destTypes.Length)
if (!source.TryGetElementTypesIfTupleOrCompatible(out sourceTypes) ||
!destination.TryGetElementTypesIfTupleOrCompatible(out destTypes) ||
sourceTypes.Length != destTypes.Length)
{
return false;
}
......
......@@ -659,30 +659,18 @@ private void MakeExplicitParameterTypeInferences(Binder binder, BoundTupleLitera
return;
}
var destTypes = ArrayBuilder<TypeSymbol>.GetInstance(sourceArguments.Length);
TupleTypeSymbol.AddElementTypes(destination, destTypes);
var destTypes = destination.GetElementTypesIfTupleOrCompatible();
Debug.Assert(sourceArguments.Length == destTypes.Length);
try
{
if (sourceArguments.Length != destTypes.Count)
{
return;
}
// 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
// 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);
}
}
finally
for (int i = 0; i < sourceArguments.Length; i++)
{
destTypes.Free();
var sourceArgument = sourceArguments[i];
var destType = destTypes[i];
MakeExplicitParameterTypeInferences(binder, sourceArgument, destType, isExactInference, ref useSiteDiagnostics);
}
}
......@@ -842,26 +830,14 @@ private void MakeOutputTypeInferences(Binder binder, BoundTupleLiteral argument,
return;
}
var destTypes = ArrayBuilder<TypeSymbol>.GetInstance(sourceArguments.Length);
TupleTypeSymbol.AddElementTypes(destination, destTypes);
var destTypes = destination.GetElementTypesIfTupleOrCompatible();
Debug.Assert(sourceArguments.Length == destTypes.Length);
try
for (int i = 0; i < sourceArguments.Length; i++)
{
if (sourceArguments.Length != destTypes.Count)
{
return;
}
for (int i = 0; i < sourceArguments.Length; i++)
{
var sourceArgument = sourceArguments[i];
var destType = destTypes[i];
MakeOutputTypeInferences(binder, sourceArgument, destType, ref useSiteDiagnostics);
}
}
finally
{
destTypes.Free();
var sourceArgument = sourceArguments[i];
var destType = destTypes[i];
MakeOutputTypeInferences(binder, sourceArgument, destType, ref useSiteDiagnostics);
}
}
......@@ -1604,11 +1580,11 @@ private bool ExactTupleInference(TypeSymbol source, TypeSymbol target, ref HashS
// that is ok, because we are inferring type parameters used in the matching elements,
// This is not the situation where entire tuple type used to infer a single type param
var sourceTypes = source.TupleElementTypes;
var targetTypes = target.TupleElementTypes;
ImmutableArray<TypeSymbol> sourceTypes;
ImmutableArray<TypeSymbol> targetTypes;
if (sourceTypes.IsDefaultOrEmpty ||
targetTypes.IsDefaultOrEmpty ||
if (!source.TryGetElementTypesIfTupleOrCompatible(out sourceTypes) ||
!target.TryGetElementTypesIfTupleOrCompatible(out targetTypes) ||
sourceTypes.Length != targetTypes.Length)
{
return false;
......@@ -1872,11 +1848,11 @@ private bool LowerBoundTupleInference(TypeSymbol source, TypeSymbol target, ref
// that is ok, because we are inferring type parameters used in the matching elements,
// This is not the situation where entire tuple type used to infer a single type param
var sourceTypes = source.TupleElementTypes;
var targetTypes = target.TupleElementTypes;
ImmutableArray<TypeSymbol> sourceTypes;
ImmutableArray<TypeSymbol> targetTypes;
if (sourceTypes.IsDefaultOrEmpty ||
targetTypes.IsDefaultOrEmpty ||
if (!source.TryGetElementTypesIfTupleOrCompatible(out sourceTypes) ||
!target.TryGetElementTypesIfTupleOrCompatible(out targetTypes) ||
sourceTypes.Length != targetTypes.Length)
{
return false;
......@@ -2133,6 +2109,10 @@ private void UpperBoundInference(TypeSymbol source, TypeSymbol target, ref HashS
// SPEC: then an exact inference is made from U1 to V1.
Debug.Assert(source.IsReferenceType);
// NOTE: spec would ask us to do the following checks, but since the value types
// are trivially handled as exact inference in the callers, we do not have to.
//if (ExactNullableInference(source, target, ref useSiteDiagnostics))
//{
// return;
......
......@@ -1952,30 +1952,18 @@ private bool ExpressionMatchExactly(BoundTupleLiteral tupleSource, TypeSymbol ta
return false;
}
var destTypes = ArrayBuilder<TypeSymbol>.GetInstance(sourceArguments.Length);
TupleTypeSymbol.AddElementTypes(destination, destTypes);
var destTypes = destination.GetElementTypesIfTupleOrCompatible();
Debug.Assert(sourceArguments.Length == destTypes.Length);
try
for (int i = 0; i < sourceArguments.Length; i++)
{
if (sourceArguments.Length != destTypes.Count)
if (!ExpressionMatchExactly(sourceArguments[i], destTypes[i], ref useSiteDiagnostics))
{
return false;
}
for (int i = 0; i < sourceArguments.Length; i++)
{
if (!ExpressionMatchExactly(sourceArguments[i], destTypes[i], ref useSiteDiagnostics))
{
return false;
}
}
return true;
}
finally
{
destTypes.Free();
}
return true;
}
private class ReturnStatements : BoundTreeWalker
......
......@@ -332,7 +332,7 @@ private static bool IsFloatPointExpressionOfUnknownPrecision(BoundExpression rew
conversionKind: conversionKind,
@checked: @checked,
explicitCastInCode: explicitCastInCode,
rewrittenType: (TupleTypeSymbol)rewrittenType);
rewrittenType: (NamedTypeSymbol)rewrittenType);
default:
break;
......@@ -634,14 +634,29 @@ private BoundExpression MakeImplicitConversion(BoundExpression rewrittenOperand,
ConversionKind conversionKind,
bool @checked,
bool explicitCastInCode,
TupleTypeSymbol rewrittenType)
NamedTypeSymbol rewrittenType)
{
var destElementTypes = rewrittenType.TupleElementTypes;
var destElementTypes = rewrittenType.GetElementTypesIfTupleOrCompatible();
var numElements = destElementTypes.Length;
var srcType = (TupleTypeSymbol)rewrittenOperand.Type;
var srcEleemntTypes = srcType.TupleElementTypes;
var srcElementFields = srcType.TupleElementFields;
ImmutableArray<FieldSymbol> srcElementFields;
TypeSymbol srcType = rewrittenOperand.Type;
if (srcType.IsTupleType)
{
srcElementFields = ((TupleTypeSymbol)srcType).TupleElementFields;
}
else
{
// The following codepath should be very uncommon (if reachable at all)
// we should generally not see tuple compatible types in bound trees and
// see actual tuple types instead.
Debug.Assert(srcType.IsTupleCompatible());
// PERF: if allocations here become nuisance, consider caching the TupleTypeSymbol
// in the type symbols that can actually be tuple compatible
srcElementFields = TupleTypeSymbol.Create((NamedTypeSymbol)srcType).TupleElementFields;
}
var fieldAccessorsBuilder = ArrayBuilder<BoundExpression>.GetInstance(numElements);
......@@ -657,7 +672,7 @@ private BoundExpression MakeImplicitConversion(BoundExpression rewrittenOperand,
{
Symbol.ReportUseSiteDiagnostic(useSiteInfo, _diagnostics, syntax.Location);
}
var fieldAccess = MakeTupleFieldAccess(syntax, field, savedTuple, null, LookupResultKind.Empty, srcEleemntTypes[i]);
var fieldAccess = MakeTupleFieldAccess(syntax, field, savedTuple, null, LookupResultKind.Empty);
var convertedFieldAccess = MakeConversion(fieldAccess, destElementTypes[i], @checked);
fieldAccessorsBuilder.Add(convertedFieldAccess);
}
......
......@@ -25,7 +25,7 @@ public override BoundNode VisitFieldAccess(BoundFieldAccess node)
if (fieldSymbol.IsTupleField)
{
return MakeTupleFieldAccess(syntax, fieldSymbol, rewrittenReceiver, constantValueOpt, resultKind, type);
return MakeTupleFieldAccess(syntax, fieldSymbol, rewrittenReceiver, constantValueOpt, resultKind);
}
BoundExpression result = oldNodeOpt != null ?
......@@ -54,8 +54,7 @@ public override BoundNode VisitFieldAccess(BoundFieldAccess node)
FieldSymbol tupleField,
BoundExpression rewrittenReceiver,
ConstantValue constantValueOpt,
LookupResultKind resultKind,
TypeSymbol type)
LookupResultKind resultKind)
{
var tupleType = tupleField.ContainingType;
......@@ -65,7 +64,7 @@ public override BoundNode VisitFieldAccess(BoundFieldAccess node)
if ((object)underlyingField == null)
{
// Use-site error must have been reported elsewhere.
return new BoundFieldAccess(syntax, rewrittenReceiver, tupleField, constantValueOpt, resultKind, type, hasErrors: true);
return _factory.BadExpression(tupleField.Type);
}
if (underlyingField.ContainingType != currentLinkType)
......@@ -76,22 +75,22 @@ public override BoundNode VisitFieldAccess(BoundFieldAccess node)
if ((object)tupleRestField == null)
{
// error tolerance for cases when Rest is missing
return new BoundFieldAccess(syntax, rewrittenReceiver, tupleField, constantValueOpt, resultKind, type, hasErrors: true);
return _factory.BadExpression(tupleField.Type);
}
// make nested field accesses to Rest
do
{
FieldSymbol nestedFieldSymbol = tupleRestField.AsMember(currentLinkType);
rewrittenReceiver = _factory.Field(rewrittenReceiver, nestedFieldSymbol);
currentLinkType = currentLinkType.TypeArgumentsNoUseSiteDiagnostics[TupleTypeSymbol.RestPosition - 1].TupleUnderlyingType;
rewrittenReceiver = new BoundFieldAccess(syntax, rewrittenReceiver, nestedFieldSymbol, ConstantValue.NotAvailable, LookupResultKind.Viable, currentLinkType);
}
while (underlyingField.ContainingType != currentLinkType);
}
// make a field access for the most local access
return new BoundFieldAccess(syntax, rewrittenReceiver, underlyingField, constantValueOpt, resultKind, type);
return _factory.Field(rewrittenReceiver, underlyingField);
}
}
}
......@@ -2,6 +2,7 @@
using Microsoft.CodeAnalysis.CSharp.Symbols;
using System.Collections.Immutable;
using System.Diagnostics;
namespace Microsoft.CodeAnalysis.CSharp
{
......@@ -31,12 +32,13 @@ private BoundNode VisitTupleExpression(BoundTupleExpression node)
/// </summary>
private BoundExpression RewriteTupleCreationExpression(BoundTupleExpression node, ImmutableArray<BoundExpression> rewrittenArguments)
{
return MakeTupleCreationExpression(node.Syntax, (TupleTypeSymbol)node.Type, rewrittenArguments) ?? node;
return MakeTupleCreationExpression(node.Syntax, (NamedTypeSymbol)node.Type, rewrittenArguments);
}
private BoundExpression MakeTupleCreationExpression(CSharpSyntaxNode syntax, TupleTypeSymbol type, ImmutableArray<BoundExpression> rewrittenArguments)
private BoundExpression MakeTupleCreationExpression(CSharpSyntaxNode syntax, NamedTypeSymbol type, ImmutableArray<BoundExpression> rewrittenArguments)
{
NamedTypeSymbol underlyingTupleType = type.TupleUnderlyingType;
NamedTypeSymbol underlyingTupleType = type.TupleUnderlyingType ?? type;
Debug.Assert(underlyingTupleType.IsTupleCompatible());
ArrayBuilder<NamedTypeSymbol> underlyingTupleTypeChain = ArrayBuilder<NamedTypeSymbol>.GetInstance();
TupleTypeSymbol.GetUnderlyingTypeChain(underlyingTupleType, underlyingTupleTypeChain);
......@@ -54,7 +56,7 @@ private BoundExpression MakeTupleCreationExpression(CSharpSyntaxNode syntax, Tup
syntax);
if ((object)smallestCtor == null)
{
return null;
return _factory.BadExpression(type);
}
MethodSymbol smallestConstructor = smallestCtor.AsMember(smallestType);
......@@ -69,7 +71,7 @@ private BoundExpression MakeTupleCreationExpression(CSharpSyntaxNode syntax, Tup
syntax);
if ((object)tuple8Ctor == null)
{
return null;
return _factory.BadExpression(type);
}
// make successively larger creation expressions containing the previous one
......
......@@ -251,6 +251,11 @@ public BoundBaseReference Base()
return new BoundBaseReference(Syntax, CurrentMethod.ThisParameter.Type.BaseTypeNoUseSiteDiagnostics) { WasCompilerGenerated = true };
}
public BoundBadExpression BadExpression(TypeSymbol type)
{
return new BoundBadExpression(Syntax, LookupResultKind.Empty, ImmutableArray<Symbol>.Empty, ImmutableArray<BoundNode>.Empty, type, hasErrors: true);
}
public BoundParameter Parameter(ParameterSymbol p)
{
return new BoundParameter(Syntax, p, p.Type) { WasCompilerGenerated = true };
......
......@@ -714,12 +714,13 @@ private ImmutableArray<FieldSymbol> CollectTupleElementFields()
continue;
}
var field = (TupleFieldSymbol)member;
int index = field.TupleFieldId;
int index = (member as TupleFieldSymbol)?.TupleFieldId ??
((TupleErrorFieldSymbol)member).TupleFieldId;
if (index >= 0)
{
Debug.Assert((object)builder[index] == null);
builder[index] = field;
builder[index] = (FieldSymbol)member;
}
}
......
......@@ -360,6 +360,60 @@ public static ImmutableArray<ParameterSymbol> DelegateParameters(this TypeSymbol
return type.DelegateInvokeMethod().Parameters;
}
public static bool TryGetElementTypesIfTupleOrCompatible(this TypeSymbol type, out ImmutableArray<TypeSymbol> elementTypes)
{
if (type.IsTupleType)
{
elementTypes = ((TupleTypeSymbol)type).TupleElementTypes;
return true;
}
// The following codepath should be very uncommon since it would be rare
// to see a tuple underlying type not represented as a tuple.
// It still might happen since tuple underlying types are creatable via public APIs
// and it is also possible that they would be passed in.
// PERF: if allocations here become nuisance, consider caching the results
// in the type symbols that can actually be tuple compatible
int cardinality;
if (!type.IsTupleCompatible(out cardinality))
{
// source not a tuple or compatible
elementTypes = default(ImmutableArray<TypeSymbol>);
return false;
}
var elementTypesBuilder = ArrayBuilder<TypeSymbol>.GetInstance(cardinality);
TupleTypeSymbol.AddElementTypes((NamedTypeSymbol)type, elementTypesBuilder);
Debug.Assert(elementTypesBuilder.Count == cardinality);
elementTypes = elementTypesBuilder.ToImmutableAndFree();
return true;
}
public static ImmutableArray<TypeSymbol> GetElementTypesIfTupleOrCompatible(this TypeSymbol type)
{
if (type.IsTupleType)
{
return ((TupleTypeSymbol)type).TupleElementTypes;
}
// The following codepath should be very uncommon since it would be rare
// to see a tuple underlying type not represented as a tuple.
// It still might happen since tuple underlying types are creatable via public APIs
// and it is also possible that they would be passed in.
Debug.Assert(type.IsTupleCompatible());
// PERF: if allocations here become nuisance, consider caching the results
// in the type symbols that can actually be tuple compatible
var elementTypesBuilder = ArrayBuilder<TypeSymbol>.GetInstance();
TupleTypeSymbol.AddElementTypes((NamedTypeSymbol)type, elementTypesBuilder);
return elementTypesBuilder.ToImmutableAndFree();
}
public static MethodSymbol DelegateInvokeMethod(this TypeSymbol type)
{
Debug.Assert((object)type != null);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册