diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs
index ab79a8b8e3c72..6d67cc7457713 100644
--- a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs
+++ b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs
@@ -26,7 +26,7 @@ internal BoundExpression CreateConversion(
var conversion = Conversions.ClassifyConversionFromExpression(source, destination, isChecked: CheckOverflowAtRuntime, ref useSiteInfo);
diagnostics.Add(source.Syntax, useSiteInfo);
- return CreateConversion(source.Syntax, source, conversion, isCast: false, conversionGroupOpt: null, destination: destination, diagnostics: diagnostics);
+ return CreateConversion(source.Syntax, source, conversion, isCast: false, conversionGroupOpt: null, InConversionGroupFlags.Unspecified, destination: destination, diagnostics: diagnostics);
}
internal BoundExpression CreateConversion(
@@ -35,7 +35,7 @@ internal BoundExpression CreateConversion(
TypeSymbol destination,
BindingDiagnosticBag diagnostics)
{
- return CreateConversion(source.Syntax, source, conversion, isCast: false, conversionGroupOpt: null, destination: destination, diagnostics: diagnostics);
+ return CreateConversion(source.Syntax, source, conversion, isCast: false, conversionGroupOpt: null, InConversionGroupFlags.Unspecified, destination: destination, diagnostics: diagnostics);
}
internal BoundExpression CreateConversion(
@@ -44,10 +44,11 @@ internal BoundExpression CreateConversion(
Conversion conversion,
bool isCast,
ConversionGroup? conversionGroupOpt,
+ InConversionGroupFlags inConversionGroupFlags,
TypeSymbol destination,
BindingDiagnosticBag diagnostics)
{
- return CreateConversion(syntax, source, conversion, isCast: isCast, conversionGroupOpt, source.WasCompilerGenerated, destination, diagnostics);
+ return CreateConversion(syntax, source, conversion, isCast: isCast, conversionGroupOpt, inConversionGroupFlags, source.WasCompilerGenerated, destination, diagnostics);
}
protected BoundExpression CreateConversion(
@@ -56,13 +57,14 @@ protected BoundExpression CreateConversion(
Conversion conversion,
bool isCast,
ConversionGroup? conversionGroupOpt,
+ InConversionGroupFlags inConversionGroupFlags,
bool wasCompilerGenerated,
TypeSymbol destination,
BindingDiagnosticBag diagnostics,
bool hasErrors = false)
{
- var result = createConversion(syntax, source, conversion, isCast, conversionGroupOpt, wasCompilerGenerated, destination, diagnostics, hasErrors);
+ var result = createConversion(syntax, source, conversion, isCast, conversionGroupOpt, inConversionGroupFlags, wasCompilerGenerated, destination, diagnostics, hasErrors);
Debug.Assert(result is BoundConversion || (conversion.IsIdentity && ((object)result == source) || source.NeedsToBeConverted()) || hasErrors);
@@ -75,7 +77,7 @@ protected BoundExpression CreateConversion(
else if (source.Type is not null && filterConversion(conversion))
{
var placeholder2 = new BoundValuePlaceholder(source.Syntax, source.Type);
- var result2 = createConversion(syntax, placeholder2, conversion, isCast, conversionGroupOpt, wasCompilerGenerated, destination, BindingDiagnosticBag.Discarded, hasErrors);
+ var result2 = createConversion(syntax, placeholder2, conversion, isCast, conversionGroupOpt: new ConversionGroup(conversion), InConversionGroupFlags.Unspecified, wasCompilerGenerated, destination, BindingDiagnosticBag.Discarded, hasErrors);
Debug.Assert(BoundNode.GetConversion(result2, placeholder2) == conversion);
}
@@ -98,6 +100,7 @@ BoundExpression createConversion(
Conversion conversion,
bool isCast,
ConversionGroup? conversionGroupOpt,
+ InConversionGroupFlags inConversionGroupFlags,
bool wasCompilerGenerated,
TypeSymbol destination,
BindingDiagnosticBag diagnostics,
@@ -129,7 +132,7 @@ BoundExpression createConversion(
if (conversion.IsMethodGroup)
{
- return CreateMethodGroupConversion(syntax, source, conversion, isCast: isCast, conversionGroupOpt, destination, diagnostics);
+ return CreateMethodGroupConversion(syntax, source, conversion, isCast: isCast, conversionGroupOpt, inConversionGroupFlags, destination, diagnostics);
}
// Obsolete diagnostics for method group are reported as part of creating the method group conversion.
@@ -137,23 +140,23 @@ BoundExpression createConversion(
if (conversion.IsAnonymousFunction && source.Kind == BoundKind.UnboundLambda)
{
- return CreateAnonymousFunctionConversion(syntax, source, conversion, isCast: isCast, conversionGroupOpt, destination, diagnostics);
+ return CreateAnonymousFunctionConversion(syntax, source, conversion, isCast: isCast, conversionGroupOpt, inConversionGroupFlags, destination, diagnostics);
}
if (conversion.Kind == ConversionKind.FunctionType)
{
- return CreateFunctionTypeConversion(syntax, source, conversion, isCast: isCast, conversionGroupOpt, destination, diagnostics);
+ return CreateFunctionTypeConversion(syntax, source, conversion, isCast: isCast, conversionGroupOpt, inConversionGroupFlags, destination, diagnostics);
}
if (conversion.IsStackAlloc)
{
- return CreateStackAllocConversion(syntax, source, conversion, isCast, conversionGroupOpt, destination, diagnostics);
+ return CreateStackAllocConversion(syntax, source, conversion, isCast, conversionGroupOpt, inConversionGroupFlags, destination, diagnostics);
}
if (conversion.IsTupleLiteralConversion ||
(conversion.IsNullable && conversion.UnderlyingConversions[0].IsTupleLiteralConversion))
{
- return CreateTupleLiteralConversion(syntax, (BoundTupleLiteral)source, conversion, isCast: isCast, conversionGroupOpt, destination, diagnostics);
+ return CreateTupleLiteralConversion(syntax, (BoundTupleLiteral)source, conversion, isCast: isCast, conversionGroupOpt, inConversionGroupFlags, destination, diagnostics);
}
if (conversion.Kind == ConversionKind.SwitchExpression)
@@ -166,6 +169,7 @@ BoundExpression createConversion(
CheckOverflowAtRuntime,
explicitCastInCode: isCast && !wasCompilerGenerated,
conversionGroupOpt,
+ inConversionGroupFlags,
convertedSwitch.ConstantValueOpt,
destination,
hasErrors);
@@ -181,6 +185,7 @@ BoundExpression createConversion(
CheckOverflowAtRuntime,
explicitCastInCode: isCast && !wasCompilerGenerated,
conversionGroupOpt,
+ inConversionGroupFlags,
convertedConditional.ConstantValueOpt,
destination,
hasErrors);
@@ -202,6 +207,7 @@ BoundExpression createConversion(
@checked: CheckOverflowAtRuntime,
explicitCastInCode: isCast && !wasCompilerGenerated,
conversionGroupOpt,
+ inConversionGroupFlags,
constantValueOpt: null,
destination);
}
@@ -225,7 +231,7 @@ BoundExpression createConversion(
if (conversion.IsObjectCreation)
{
- return ConvertObjectCreationExpression(syntax, (BoundUnconvertedObjectCreationExpression)source, conversion, isCast, destination, conversionGroupOpt, wasCompilerGenerated, diagnostics);
+ return ConvertObjectCreationExpression(syntax, (BoundUnconvertedObjectCreationExpression)source, conversion, isCast, destination, conversionGroupOpt, inConversionGroupFlags, wasCompilerGenerated, diagnostics);
}
if (source.Kind == BoundKind.UnconvertedCollectionExpression)
@@ -246,6 +252,7 @@ BoundExpression createConversion(
@checked: CheckOverflowAtRuntime,
explicitCastInCode: isCast && !wasCompilerGenerated,
conversionGroupOpt,
+ inConversionGroupFlags,
constantValueOpt: null,
type: destination);
}
@@ -263,6 +270,7 @@ BoundExpression createConversion(
{
// User-defined conversions are likely to be represented as multiple
// BoundConversion instances so a ConversionGroup is necessary.
+ Debug.Assert(inConversionGroupFlags == InConversionGroupFlags.Unspecified);
return CreateUserDefinedConversion(syntax, source, conversion, isCast: isCast, conversionGroupOpt ?? new ConversionGroup(conversion), destination, diagnostics, hasErrors);
}
@@ -298,6 +306,7 @@ source.Type is { } sourceType &&
@checked: CheckOverflowAtRuntime,
explicitCastInCode: isCast && !wasCompilerGenerated,
conversionGroupOpt,
+ inConversionGroupFlags,
constantValueOpt: constantValue,
type: destination,
hasErrors: hasErrors)
@@ -334,6 +343,7 @@ void ensureAllUnderlyingConversionsChecked(SyntaxNode syntax, BoundExpression so
conversion.UnderlyingConversions[0],
isCast: false,
conversionGroupOpt: null,
+ InConversionGroupFlags.Unspecified,
wasCompilerGenerated,
destination.GetNullableUnderlyingType(),
diagnostics);
@@ -346,6 +356,7 @@ void ensureAllUnderlyingConversionsChecked(SyntaxNode syntax, BoundExpression so
conversion.UnderlyingConversions[0],
isCast: false,
conversionGroupOpt: null,
+ InConversionGroupFlags.Unspecified,
wasCompilerGenerated,
destination.GetNullableUnderlyingType(),
diagnostics);
@@ -363,6 +374,7 @@ void ensureAllUnderlyingConversionsChecked(SyntaxNode syntax, BoundExpression so
conversion.UnderlyingConversions[0],
isCast: false,
conversionGroupOpt: null,
+ InConversionGroupFlags.Unspecified,
wasCompilerGenerated,
destination,
diagnostics);
@@ -391,6 +403,7 @@ void ensureAllUnderlyingConversionsChecked(SyntaxNode syntax, BoundExpression so
elementConversions[i],
isCast: false,
conversionGroupOpt: null,
+ InConversionGroupFlags.Unspecified,
wasCompilerGenerated,
destTypes[i].Type,
diagnostics);
@@ -746,7 +759,7 @@ private static void CheckInlineArrayTypeIsSupported(SyntaxNode syntax, TypeSymbo
private static BoundExpression ConvertObjectCreationExpression(
SyntaxNode syntax, BoundUnconvertedObjectCreationExpression node, Conversion conversion, bool isCast, TypeSymbol destination,
- ConversionGroup? conversionGroupOpt, bool wasCompilerGenerated, BindingDiagnosticBag diagnostics)
+ ConversionGroup? conversionGroupOpt, InConversionGroupFlags inConversionGroupFlags, bool wasCompilerGenerated, BindingDiagnosticBag diagnostics)
{
var arguments = AnalyzedArguments.GetInstance(node.Arguments, node.ArgumentRefKindsOpt, node.ArgumentNamesOpt);
BoundExpression expr = bindObjectCreationExpression(node.Syntax, node.InitializerOpt, node.Binder, destination.StrippedType(), arguments, diagnostics);
@@ -771,6 +784,7 @@ private static BoundExpression ConvertObjectCreationExpression(
node.Binder.CheckOverflowAtRuntime,
explicitCastInCode: isCast && !wasCompilerGenerated,
conversionGroupOpt,
+ inConversionGroupFlags,
expr.ConstantValueOpt,
destination)
{ WasCompilerGenerated = wasCompilerGenerated };
@@ -968,6 +982,7 @@ private BoundCollectionExpression ConvertCollectionExpression(
elementConversion,
isCast: false,
conversionGroupOpt: null,
+ InConversionGroupFlags.Unspecified,
destination: elementType,
diagnostics);
builder.Add(convertedElement!);
@@ -1004,6 +1019,7 @@ BoundNode bindSpreadElement(BoundCollectionExpressionSpreadElement element, Type
elementConversion,
isCast: false,
conversionGroupOpt: null,
+ InConversionGroupFlags.Unspecified,
destination: elementType,
diagnostics);
return element.Update(
@@ -1941,11 +1957,11 @@ private BoundExpression ConvertConditionalExpression(
var trueExpr =
targetTyped
- ? CreateConversion(source.Consequence.Syntax, source.Consequence, underlyingConversions[0], isCast: false, conversionGroupOpt: null, destination, diagnostics)
+ ? CreateConversion(source.Consequence.Syntax, source.Consequence, underlyingConversions[0], isCast: false, conversionGroupOpt: null, InConversionGroupFlags.Unspecified, destination, diagnostics)
: GenerateConversionForAssignment(destination, source.Consequence, diagnostics);
var falseExpr =
targetTyped
- ? CreateConversion(source.Alternative.Syntax, source.Alternative, underlyingConversions[1], isCast: false, conversionGroupOpt: null, destination, diagnostics)
+ ? CreateConversion(source.Alternative.Syntax, source.Alternative, underlyingConversions[1], isCast: false, conversionGroupOpt: null, InConversionGroupFlags.Unspecified, destination, diagnostics)
: GenerateConversionForAssignment(destination, source.Alternative, diagnostics);
conversion.MarkUnderlyingConversionsChecked();
var constantValue = FoldConditionalOperator(condition, trueExpr, falseExpr);
@@ -1981,7 +1997,7 @@ private BoundExpression ConvertSwitchExpression(BoundUnconvertedSwitchExpression
var oldValue = oldCase.Value;
var newValue =
targetTyped
- ? CreateConversion(oldValue.Syntax, oldValue, underlyingConversions[i], isCast: false, conversionGroupOpt: null, destination, diagnostics)
+ ? CreateConversion(oldValue.Syntax, oldValue, underlyingConversions[i], isCast: false, conversionGroupOpt: null, InConversionGroupFlags.Unspecified, destination, diagnostics)
: GenerateConversionForAssignment(destination, oldValue, diagnostics);
var newCase = (oldValue == newValue) ? oldCase :
new BoundSwitchExpressionArm(oldCase.Syntax, oldCase.Locals, oldCase.Pattern, oldCase.WhenClause, newValue, oldCase.Label, oldCase.HasErrors);
@@ -2006,6 +2022,7 @@ private BoundExpression CreateUserDefinedConversion(
bool hasErrors)
{
Debug.Assert(conversionGroup != null);
+ Debug.Assert(conversionGroup.Conversion == conversion);
Debug.Assert(conversion.IsUserDefined);
conversion.MarkUnderlyingConversionsChecked();
@@ -2021,6 +2038,7 @@ private BoundExpression CreateUserDefinedConversion(
CheckOverflowAtRuntime,
explicitCastInCode: isCast,
conversionGroup,
+ InConversionGroupFlags.UserDefinedOperator | InConversionGroupFlags.UserDefinedErroneous,
constantValueOpt: ConstantValue.NotAvailable,
type: destination,
hasErrors: true)
@@ -2063,6 +2081,7 @@ private BoundExpression CreateUserDefinedConversion(
conversion: conversion.UserDefinedFromConversion,
isCast: false,
conversionGroupOpt: conversionGroup,
+ InConversionGroupFlags.UserDefinedFromConversion,
wasCompilerGenerated: false,
destination: conversion.BestUserDefinedConversionAnalysis.FromType,
diagnostics: diagnostics);
@@ -2074,12 +2093,18 @@ private BoundExpression CreateUserDefinedConversion(
!TypeSymbol.Equals(conversion.BestUserDefinedConversionAnalysis.FromType, conversionParameterType, TypeCompareKind.ConsiderEverything2))
{
// Conversion's "from" type --> conversion method's parameter type.
+ Conversion toParameterTypeConversion = Conversions.ClassifyStandardConversion(convertedOperand.Type, conversionParameterType, ref useSiteInfo);
+ Debug.Assert(toParameterTypeConversion.IsNullable);
+ Debug.Assert(toParameterTypeConversion.IsExplicit);
+ Debug.Assert(toParameterTypeConversion.UnderlyingConversions[0].IsIdentity);
+
convertedOperand = CreateConversion(
syntax: syntax,
source: convertedOperand,
- conversion: Conversions.ClassifyStandardConversion(convertedOperand.Type, conversionParameterType, ref useSiteInfo),
+ conversion: toParameterTypeConversion,
isCast: false,
conversionGroupOpt: conversionGroup,
+ InConversionGroupFlags.UserDefinedFromConversionAdjustment,
wasCompilerGenerated: true,
destination: conversionParameterType,
diagnostics: diagnostics);
@@ -2103,6 +2128,7 @@ private BoundExpression CreateUserDefinedConversion(
@checked: CheckOverflowAtRuntime,
explicitCastInCode: isCast,
conversionGroup,
+ InConversionGroupFlags.UserDefinedOperator,
constantValueOpt: ConstantValue.NotAvailable,
type: conversionReturnType)
{ WasCompilerGenerated = true };
@@ -2119,12 +2145,18 @@ private BoundExpression CreateUserDefinedConversion(
else
{
// Conversion method's return type --> conversion's "to" type
+ Conversion fromReturnTypeConversion = Conversions.ClassifyStandardConversion(conversionReturnType, conversionToType, ref useSiteInfo);
+ Debug.Assert(fromReturnTypeConversion.IsNullable);
+ Debug.Assert(fromReturnTypeConversion.IsImplicit);
+ Debug.Assert(!fromReturnTypeConversion.UnderlyingConversions[0].IsIdentity);
+
userDefinedConversion = CreateConversion(
syntax: syntax,
source: userDefinedConversion,
- conversion: Conversions.ClassifyStandardConversion(conversionReturnType, conversionToType, ref useSiteInfo),
+ conversion: fromReturnTypeConversion,
isCast: false,
conversionGroupOpt: conversionGroup,
+ InConversionGroupFlags.UserDefinedReturnTypeAdjustment,
wasCompilerGenerated: true,
destination: conversionToType,
diagnostics: diagnostics);
@@ -2141,6 +2173,7 @@ private BoundExpression CreateUserDefinedConversion(
@checked: CheckOverflowAtRuntime,
explicitCastInCode: isCast,
conversionGroup,
+ InConversionGroupFlags.UserDefinedOperator,
constantValueOpt: ConstantValue.NotAvailable,
type: conversionToType)
{ WasCompilerGenerated = true };
@@ -2155,6 +2188,7 @@ private BoundExpression CreateUserDefinedConversion(
conversion: toConversion,
isCast: false,
conversionGroupOpt: conversionGroup,
+ InConversionGroupFlags.UserDefinedFinal,
wasCompilerGenerated: true, // NOTE: doesn't necessarily set flag on resulting bound expression.
destination: destination,
diagnostics: diagnostics);
@@ -2166,7 +2200,10 @@ private BoundExpression CreateUserDefinedConversion(
return finalConversion;
}
- private BoundExpression CreateFunctionTypeConversion(SyntaxNode syntax, BoundExpression source, Conversion conversion, bool isCast, ConversionGroup? conversionGroup, TypeSymbol destination, BindingDiagnosticBag diagnostics)
+ private BoundExpression CreateFunctionTypeConversion(
+ SyntaxNode syntax, BoundExpression source, Conversion conversion, bool isCast,
+ ConversionGroup? conversionGroup, InConversionGroupFlags inConversionGroupFlags,
+ TypeSymbol destination, BindingDiagnosticBag diagnostics)
{
Debug.Assert(conversion.Kind == ConversionKind.FunctionType);
Debug.Assert(source.Kind is BoundKind.MethodGroup or BoundKind.UnboundLambda);
@@ -2189,15 +2226,24 @@ private BoundExpression CreateFunctionTypeConversion(SyntaxNode syntax, BoundExp
!isCast &&
conversion.Exists &&
destination.SpecialType == SpecialType.System_Object;
+
+ Debug.Assert((inConversionGroupFlags & InConversionGroupFlags.FunctionTypeDelegate) == 0);
BoundExpression expr;
if (!conversion.Exists)
{
GenerateImplicitConversionError(diagnostics, syntax, conversion, source, delegateType);
- expr = new BoundConversion(syntax, source, conversion, @checked: false, explicitCastInCode: isCast, conversionGroup, constantValueOpt: ConstantValue.NotAvailable, type: delegateType, hasErrors: true) { WasCompilerGenerated = source.WasCompilerGenerated };
+ expr = new BoundConversion(
+ syntax, source, conversion, @checked: false, explicitCastInCode: isCast,
+ conversionGroup, inConversionGroupFlags | InConversionGroupFlags.FunctionTypeDelegate,
+ constantValueOpt: ConstantValue.NotAvailable, type: delegateType, hasErrors: true)
+ { WasCompilerGenerated = source.WasCompilerGenerated };
}
else
{
- expr = CreateConversion(syntax, source, conversion, isCast, conversionGroup, delegateType, diagnostics);
+ expr = CreateConversion(
+ syntax, source, conversion, isCast,
+ conversionGroup, inConversionGroupFlags | InConversionGroupFlags.FunctionTypeDelegate,
+ delegateType, diagnostics);
}
conversion = Conversions.ClassifyConversionFromExpression(expr, destination, isChecked: CheckOverflowAtRuntime, ref useSiteInfo);
@@ -2211,10 +2257,14 @@ private BoundExpression CreateFunctionTypeConversion(SyntaxNode syntax, BoundExp
}
diagnostics.Add(syntax, useSiteInfo);
- return CreateConversion(syntax, expr, conversion, isCast, conversionGroup, destination, diagnostics);
+ Debug.Assert((inConversionGroupFlags & InConversionGroupFlags.FunctionTypeDelegateToTarget) == 0);
+ return CreateConversion(syntax, expr, conversion, isCast, conversionGroup, inConversionGroupFlags | InConversionGroupFlags.FunctionTypeDelegateToTarget, destination, diagnostics);
}
- private BoundExpression CreateAnonymousFunctionConversion(SyntaxNode syntax, BoundExpression source, Conversion conversion, bool isCast, ConversionGroup? conversionGroup, TypeSymbol destination, BindingDiagnosticBag diagnostics)
+ private BoundExpression CreateAnonymousFunctionConversion(
+ SyntaxNode syntax, BoundExpression source, Conversion conversion, bool isCast,
+ ConversionGroup? conversionGroup, InConversionGroupFlags inConversionGroupFlags,
+ TypeSymbol destination, BindingDiagnosticBag diagnostics)
{
// We have a successful anonymous function conversion; rather than producing a node
// which is a conversion on top of an unbound lambda, replace it with the bound
@@ -2236,12 +2286,16 @@ private BoundExpression CreateAnonymousFunctionConversion(SyntaxNode syntax, Bou
@checked: false,
explicitCastInCode: isCast,
conversionGroup,
+ inConversionGroupFlags,
constantValueOpt: ConstantValue.NotAvailable,
type: destination)
{ WasCompilerGenerated = source.WasCompilerGenerated };
}
- private BoundExpression CreateMethodGroupConversion(SyntaxNode syntax, BoundExpression source, Conversion conversion, bool isCast, ConversionGroup? conversionGroup, TypeSymbol destination, BindingDiagnosticBag diagnostics)
+ private BoundExpression CreateMethodGroupConversion(
+ SyntaxNode syntax, BoundExpression source, Conversion conversion, bool isCast,
+ ConversionGroup? conversionGroup, InConversionGroupFlags inConversionGroupFlags,
+ TypeSymbol destination, BindingDiagnosticBag diagnostics)
{
var (originalGroup, isAddressOf) = source switch
{
@@ -2260,7 +2314,7 @@ private BoundExpression CreateMethodGroupConversion(SyntaxNode syntax, BoundExpr
Debug.Assert(conversion.UnderlyingConversions.IsDefault);
conversion.MarkUnderlyingConversionsChecked();
- return new BoundConversion(syntax, group, conversion, @checked: false, explicitCastInCode: isCast, conversionGroup, constantValueOpt: ConstantValue.NotAvailable, type: destination, hasErrors: hasErrors) { WasCompilerGenerated = group.WasCompilerGenerated };
+ return new BoundConversion(syntax, group, conversion, @checked: false, explicitCastInCode: isCast, conversionGroup, inConversionGroupFlags, constantValueOpt: ConstantValue.NotAvailable, type: destination, hasErrors: hasErrors) { WasCompilerGenerated = group.WasCompilerGenerated };
}
private static void CheckParameterModifierMismatchMethodConversion(SyntaxNode syntax, MethodSymbol lambdaOrMethod, TypeSymbol targetType, bool invokedAsExtensionMethod, BindingDiagnosticBag diagnostics)
@@ -2399,7 +2453,10 @@ static void reportUseSiteDiagnosticForSynthesizedAttribute(
}
}
- private BoundExpression CreateStackAllocConversion(SyntaxNode syntax, BoundExpression source, Conversion conversion, bool isCast, ConversionGroup? conversionGroup, TypeSymbol destination, BindingDiagnosticBag diagnostics)
+ private BoundExpression CreateStackAllocConversion(
+ SyntaxNode syntax, BoundExpression source, Conversion conversion, bool isCast,
+ ConversionGroup? conversionGroup, InConversionGroupFlags inConversionGroupFlags,
+ TypeSymbol destination, BindingDiagnosticBag diagnostics)
{
Debug.Assert(conversion.IsStackAlloc);
@@ -2424,11 +2481,17 @@ private BoundExpression CreateStackAllocConversion(SyntaxNode syntax, BoundExpre
var convertedNode = new BoundConvertedStackAllocExpression(syntax, elementType, boundStackAlloc.Count, boundStackAlloc.InitializerOpt, stackAllocType, boundStackAlloc.HasErrors);
var underlyingConversion = conversion.UnderlyingConversions.Single();
- return CreateConversion(syntax, convertedNode, underlyingConversion, isCast: isCast, conversionGroup, destination, diagnostics);
+ return CreateConversion(syntax, convertedNode, underlyingConversion, isCast: isCast, conversionGroup, inConversionGroupFlags, destination, diagnostics);
}
- private BoundExpression CreateTupleLiteralConversion(SyntaxNode syntax, BoundTupleLiteral sourceTuple, Conversion conversion, bool isCast, ConversionGroup? conversionGroup, TypeSymbol destination, BindingDiagnosticBag diagnostics)
+ private BoundExpression CreateTupleLiteralConversion(
+ SyntaxNode syntax, BoundTupleLiteral sourceTuple, Conversion conversion, bool isCast,
+ ConversionGroup? conversionGroup, InConversionGroupFlags inConversionGroupFlags,
+ TypeSymbol destination, BindingDiagnosticBag diagnostics)
{
+ Debug.Assert(conversion.IsTupleLiteralConversion ||
+ (conversion.IsNullable && conversion.UnderlyingConversions[0].IsTupleLiteralConversion));
+
// 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.IsNullable == destination.IsNullableType());
@@ -2491,7 +2554,7 @@ private BoundExpression CreateTupleLiteralConversion(SyntaxNode syntax, BoundTup
var destType = targetElementTypes[i];
var elementConversion = underlyingConversions[i];
var elementConversionGroup = isCast ? new ConversionGroup(elementConversion, destType) : null;
- convertedArguments.Add(CreateConversion(argument.Syntax, argument, elementConversion, isCast: isCast, elementConversionGroup, destType.Type, diagnostics));
+ convertedArguments.Add(CreateConversion(argument.Syntax, argument, elementConversion, isCast: isCast, elementConversionGroup, InConversionGroupFlags.Unspecified, destType.Type, diagnostics));
}
BoundExpression result = new BoundConvertedTupleLiteral(
@@ -2503,6 +2566,7 @@ private BoundExpression CreateTupleLiteralConversion(SyntaxNode syntax, BoundTup
sourceTuple.InferredNamesOpt,
targetType).WithSuppression(sourceTuple.IsSuppressed);
+ Debug.Assert((inConversionGroupFlags & InConversionGroupFlags.TupleLiteral) == 0);
if (!TypeSymbol.Equals(sourceTuple.Type, destination, TypeCompareKind.ConsiderEverything2))
{
// literal cast is applied to the literal
@@ -2513,12 +2577,14 @@ private BoundExpression CreateTupleLiteralConversion(SyntaxNode syntax, BoundTup
@checked: false,
explicitCastInCode: isCast,
conversionGroup,
+ inConversionGroupFlags | InConversionGroupFlags.TupleLiteral,
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.
+ Debug.Assert((inConversionGroupFlags & InConversionGroupFlags.TupleLiteralExplicitIdentity) == 0);
if (isCast)
{
result = new BoundConversion(
@@ -2528,6 +2594,7 @@ private BoundExpression CreateTupleLiteralConversion(SyntaxNode syntax, BoundTup
@checked: false,
explicitCastInCode: isCast,
conversionGroup,
+ inConversionGroupFlags | InConversionGroupFlags.TupleLiteralExplicitIdentity,
constantValueOpt: ConstantValue.NotAvailable,
type: destination);
}
diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Deconstruct.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Deconstruct.cs
index dcf0f30ab831c..579baee37d641 100644
--- a/src/Compilers/CSharp/Portable/Binder/Binder_Deconstruct.cs
+++ b/src/Compilers/CSharp/Portable/Binder/Binder_Deconstruct.cs
@@ -129,8 +129,8 @@ private BoundDeconstructionAssignmentOperator BindDeconstructionAssignment(
return new BoundDeconstructionAssignmentOperator(
node,
DeconstructionVariablesAsTuple(left, checkedVariables, diagnostics, ignoreDiagnosticsFromTuple: true),
- new BoundConversion(boundRHS.Syntax, boundRHS, Conversion.Deconstruction, @checked: false, explicitCastInCode: false, conversionGroupOpt: null,
- constantValueOpt: null, type: type, hasErrors: true),
+ new BoundConversion(boundRHS.Syntax, boundRHS, Conversion.Deconstruction, @checked: false, explicitCastInCode: false,
+ conversionGroupOpt: null, InConversionGroupFlags.Unspecified, constantValueOpt: null, type: type, hasErrors: true),
resultIsUsed,
voidType,
hasErrors: true);
@@ -165,6 +165,7 @@ private BoundDeconstructionAssignmentOperator BindDeconstructionAssignment(
@checked: false,
explicitCastInCode: false,
conversionGroupOpt: null,
+ InConversionGroupFlags.Unspecified,
constantValueOpt: null,
type: returnType,
hasErrors: hasErrors)
@@ -309,7 +310,8 @@ private bool MakeDeconstructionConversion(
var operandPlaceholder = new BoundValuePlaceholder(syntax, ErrorTypeSymbol.UnknownResultType).MakeCompilerGenerated();
nestedConversions.Add((operandPlaceholder, new BoundConversion(syntax, operandPlaceholder, nestedConversion,
@checked: false, explicitCastInCode: false,
- conversionGroupOpt: null, constantValueOpt: null,
+ conversionGroupOpt: null, InConversionGroupFlags.Unspecified,
+ constantValueOpt: null,
#pragma warning disable format
type: ErrorTypeSymbol.UnknownResultType) { WasCompilerGenerated = true }));
#pragma warning restore format
@@ -333,7 +335,7 @@ private bool MakeDeconstructionConversion(
{
var operandPlaceholder = new BoundValuePlaceholder(syntax, tupleOrDeconstructedTypes[i]).MakeCompilerGenerated();
nestedConversions.Add((operandPlaceholder, CreateConversion(syntax, operandPlaceholder,
- nestedConversion, isCast: false, conversionGroupOpt: null, single.Type, diagnostics)));
+ nestedConversion, isCast: false, conversionGroupOpt: null, InConversionGroupFlags.Unspecified, single.Type, diagnostics)));
}
}
}
diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
index 8324d4f1a0396..4b593df90dbdf 100644
--- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
+++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
@@ -2833,7 +2833,7 @@ private BoundExpression BindCastCore(ExpressionSyntax node, BoundExpression oper
GenerateExplicitConversionErrors(diagnostics, node, conversion, operand, targetType);
}
- return CreateConversion(node, operand, conversion, isCast: true, conversionGroupOpt: conversionGroup, wasCompilerGenerated: wasCompilerGenerated, destination: targetType, diagnostics: diagnostics, hasErrors: hasErrors | suppressErrors);
+ return CreateConversion(node, operand, conversion, isCast: true, conversionGroupOpt: conversionGroup, InConversionGroupFlags.Unspecified, wasCompilerGenerated: wasCompilerGenerated, destination: targetType, diagnostics: diagnostics, hasErrors: hasErrors | suppressErrors);
}
private void GenerateExplicitConversionErrors(
@@ -3579,7 +3579,7 @@ BoundExpression coerceArgument(
{
reportUnsafeIfNeeded(methodResult, diagnostics, argument, parameterTypeWithAnnotations);
- coercedArgument = CreateConversion(argument.Syntax, argument, kind, isCast: false, conversionGroupOpt: null, parameterTypeWithAnnotations.Type, diagnostics);
+ coercedArgument = CreateConversion(argument.Syntax, argument, kind, isCast: false, conversionGroupOpt: null, InConversionGroupFlags.Unspecified, parameterTypeWithAnnotations.Type, diagnostics);
}
else if (argument.Kind == BoundKind.OutVariablePendingInference)
{
@@ -3600,7 +3600,7 @@ BoundExpression coerceArgument(
if (argument is BoundTupleLiteral)
{
// CreateConversion reports tuple literal name mismatches, and constructs the expected pattern of bound nodes.
- coercedArgument = CreateConversion(argument.Syntax, argument, kind, isCast: false, conversionGroupOpt: null, parameterTypeWithAnnotations.Type, diagnostics);
+ coercedArgument = CreateConversion(argument.Syntax, argument, kind, isCast: false, conversionGroupOpt: null, InConversionGroupFlags.Unspecified, parameterTypeWithAnnotations.Type, diagnostics);
}
else
{
@@ -3765,6 +3765,7 @@ BoundExpression bindInterpolatedStringHandlerInMemberCall(
interpolatedStringConversion,
isCast: false,
conversionGroupOpt: null,
+ InConversionGroupFlags.Unspecified,
handlerType,
diagnostics);
}
@@ -3781,6 +3782,7 @@ BoundExpression bindInterpolatedStringHandlerInMemberCall(
interpolatedStringConversion,
isCast: false,
conversionGroupOpt: null,
+ InConversionGroupFlags.Unspecified,
wasCompilerGenerated: false,
handlerType,
diagnostics,
@@ -3796,6 +3798,7 @@ BoundExpression bindInterpolatedStringHandlerInMemberCall(
interpolatedStringConversion,
isCast: false,
conversionGroupOpt: null,
+ InConversionGroupFlags.Unspecified,
handlerType,
diagnostics);
}
@@ -3963,6 +3966,7 @@ BoundExpression bindInterpolatedStringHandlerInMemberCall(
@checked: CheckOverflowAtRuntime,
explicitCastInCode: false,
conversionGroupOpt: null,
+ InConversionGroupFlags.Unspecified,
constantValueOpt: null,
handlerType,
hasErrors || interpolatedString.HasErrors);
@@ -9116,6 +9120,7 @@ protected BoundExpression BindFieldAccess(
@checked: true,
explicitCastInCode: false,
conversionGroupOpt: null,
+ InConversionGroupFlags.Unspecified,
constantValueOpt: expr.ConstantValueOpt,
type: underlyingType);
}
@@ -9953,7 +9958,7 @@ private BoundExpression ConvertToArrayIndex(BoundExpression index, BindingDiagno
GenerateImplicitConversionError(diagnostics, node, failedConversion, index, int32);
// Suppress any additional diagnostics
- return CreateConversion(node, index, failedConversion, isCast: false, conversionGroupOpt: null, destination: int32, diagnostics: BindingDiagnosticBag.Discarded);
+ return CreateConversion(node, index, failedConversion, isCast: false, conversionGroupOpt: null, InConversionGroupFlags.Unspecified, destination: int32, diagnostics: BindingDiagnosticBag.Discarded);
}
return result;
@@ -10015,7 +10020,7 @@ private BoundExpression TryImplicitConversionToArrayIndex(BoundExpression expr,
conversion = conversion.SetArrayIndexConversionForDynamic();
}
- BoundExpression result = CreateConversion(expr.Syntax, expr, conversion, isCast: false, conversionGroupOpt: null, destination: targetType, diagnostics); // UNDONE: was cast?
+ BoundExpression result = CreateConversion(expr.Syntax, expr, conversion, isCast: false, conversionGroupOpt: null, InConversionGroupFlags.Unspecified, destination: targetType, diagnostics); // UNDONE: was cast?
Debug.Assert(result != null); // If this ever fails (it shouldn't), then put a null-check around the diagnostics update.
return result;
diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs
index 1d7d6c1ff173d..e781e0a44ed34 100644
--- a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs
+++ b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs
@@ -1766,6 +1766,7 @@ BoundExpression bindDefaultArgument(SyntaxNode syntax, ParameterSymbol parameter
conversion,
isCast,
isCast ? new ConversionGroup(conversion, parameter.TypeWithAnnotations) : null,
+ InConversionGroupFlags.Unspecified,
parameterType,
diagnostics);
}
@@ -1880,6 +1881,7 @@ private BoundExpression CreateParamsCollection(SyntaxNode node, ParameterSymbol
@checked: CheckOverflowAtRuntime,
explicitCastInCode: false,
conversionGroupOpt: null,
+ InConversionGroupFlags.Unspecified,
constantValueOpt: null,
type: collectionType)
{ WasCompilerGenerated = true, IsParamsArrayOrCollection = true };
diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs
index 1d8f1dd77ca1e..1b1ddf07bced1 100644
--- a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs
+++ b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs
@@ -75,10 +75,10 @@ BoundExpression bindCompoundAssignment(AssignmentExpressionSyntax node, ref Oper
var placeholder = new BoundValuePlaceholder(right.Syntax, left.HasDynamicType() ? left.Type : right.Type).MakeCompilerGenerated();
var finalDynamicConversion = this.Compilation.Conversions.ClassifyConversionFromExpression(placeholder, left.Type, isChecked: CheckOverflowAtRuntime, ref useSiteInfo);
diagnostics.Add(node, useSiteInfo);
- var conversion = (BoundConversion)CreateConversion(node, placeholder, finalDynamicConversion, isCast: true, conversionGroupOpt: null, left.Type, diagnostics);
+ var conversion = (BoundConversion)CreateConversion(node, placeholder, finalDynamicConversion, isCast: true, conversionGroupOpt: null, InConversionGroupFlags.Unspecified, left.Type, diagnostics);
conversion = conversion.Update(conversion.Operand, conversion.Conversion, conversion.IsBaseConversion, conversion.Checked,
- explicitCastInCode: true, conversion.ConstantValueOpt, conversion.ConversionGroupOpt, conversion.Type);
+ explicitCastInCode: true, conversion.ConstantValueOpt, conversion.ConversionGroupOpt, conversion.InConversionGroupFlags, conversion.Type);
return new BoundCompoundAssignmentOperator(
node,
@@ -316,7 +316,7 @@ BoundExpression bindCompoundAssignment(AssignmentExpressionSyntax node, ref Oper
Debug.Assert(left.Kind != BoundKind.EventAccess || hasError);
var leftPlaceholder = new BoundValuePlaceholder(left.Syntax, leftType).MakeCompilerGenerated();
- var leftConversion = CreateConversion(node.Left, leftPlaceholder, best.LeftConversion, isCast: false, conversionGroupOpt: null, best.Signature.LeftType, diagnostics);
+ var leftConversion = CreateConversion(node.Left, leftPlaceholder, best.LeftConversion, isCast: false, conversionGroupOpt: null, InConversionGroupFlags.Unspecified, best.Signature.LeftType, diagnostics);
return new BoundCompoundAssignmentOperator(node, bestSignature, left, rightConverted,
leftPlaceholder, leftConversion, finalPlaceholder, finalConversion, resultKind, originalUserDefinedOperators, leftType, hasError);
@@ -506,7 +506,7 @@ bool shouldTryUserDefinedInstanceOperator(AssignmentExpressionSyntax node, bool
Debug.Assert(leftType.IsReferenceType);
leftPlaceholder = new BoundValuePlaceholder(left.Syntax, leftType).MakeCompilerGenerated();
- leftConversion = CreateConversion(node, leftPlaceholder, conversion, isCast: false, conversionGroupOpt: null, method.ContainingType.ExtensionParameter.Type, diagnostics);
+ leftConversion = CreateConversion(node, leftPlaceholder, conversion, isCast: false, conversionGroupOpt: null, InConversionGroupFlags.Unspecified, method.ContainingType.ExtensionParameter.Type, diagnostics);
}
}
@@ -1551,6 +1551,7 @@ BoundExpression bindConditionalLogicalOperator(BinaryExpressionSyntax node, Boun
trueFalseOperator.Conversion,
isCast: false,
conversionGroupOpt: null,
+ InConversionGroupFlags.Unspecified,
trueFalseOperator.Signature.OperandType,
diagnostics);
@@ -1617,7 +1618,7 @@ private bool IsValidDynamicCondition(BoundExpression left, bool isNegative, Bind
{
if (left.Type is not null)
{
- CreateConversion(left.Syntax, new BoundValuePlaceholder(left.Syntax, left.Type).MakeCompilerGenerated(), implicitConversion, isCast: false, conversionGroupOpt: null, booleanType, diagnostics);
+ CreateConversion(left.Syntax, new BoundValuePlaceholder(left.Syntax, left.Type).MakeCompilerGenerated(), implicitConversion, isCast: false, conversionGroupOpt: null, InConversionGroupFlags.Unspecified, booleanType, diagnostics);
}
else
{
@@ -1656,7 +1657,7 @@ private bool IsValidDynamicCondition(BoundExpression left, bool isNegative, Bind
userDefinedOperator = result.Signature.Method;
TypeSymbol parameterType = userDefinedOperator.Parameters[0].Type;
- CreateConversion(left.Syntax, operandPlaceholder, result.Conversion, isCast: false, conversionGroupOpt: null, parameterType, diagnostics);
+ CreateConversion(left.Syntax, operandPlaceholder, result.Conversion, isCast: false, conversionGroupOpt: null, InConversionGroupFlags.Unspecified, parameterType, diagnostics);
return true;
}
@@ -3407,7 +3408,7 @@ BoundExpression bindIncrementOperator(ExpressionSyntax node, ExpressionSyntax op
}
var operandPlaceholder = new BoundValuePlaceholder(operand.Syntax, operandType).MakeCompilerGenerated();
- var operandConversion = CreateConversion(node, operandPlaceholder, best.Conversion, isCast: false, conversionGroupOpt: null, best.Signature.OperandType, diagnostics);
+ var operandConversion = CreateConversion(node, operandPlaceholder, best.Conversion, isCast: false, conversionGroupOpt: null, InConversionGroupFlags.Unspecified, best.Signature.OperandType, diagnostics);
return new BoundIncrementOperator(
node,
@@ -3593,7 +3594,7 @@ InstanceUserDefinedIncrementUsageMode getInstanceUserDefinedIncrementUsageMode(
Debug.Assert(operandType.IsReferenceType);
operandPlaceholder = new BoundValuePlaceholder(operand.Syntax, operandType).MakeCompilerGenerated();
- operandConversion = CreateConversion(node, operandPlaceholder, conversion, isCast: false, conversionGroupOpt: null, method.ContainingType.ExtensionParameter.Type, diagnostics);
+ operandConversion = CreateConversion(node, operandPlaceholder, conversion, isCast: false, conversionGroupOpt: null, InConversionGroupFlags.Unspecified, method.ContainingType.ExtensionParameter.Type, diagnostics);
}
}
@@ -4388,7 +4389,7 @@ private BoundExpression BindUnaryOperatorCore(CSharpSyntaxNode node, string oper
operatorResolutionForReporting.Free();
var signature = best.Signature;
- var resultOperand = CreateConversion(operand.Syntax, operand, best.Conversion, isCast: false, conversionGroupOpt: null, signature.OperandType, diagnostics);
+ var resultOperand = CreateConversion(operand.Syntax, operand, best.Conversion, isCast: false, conversionGroupOpt: null, InConversionGroupFlags.Unspecified, signature.OperandType, diagnostics);
var resultType = signature.ReturnType;
UnaryOperatorKind resultOperatorKind = signature.Kind;
var resultConstant = FoldUnaryOperator(node, resultOperatorKind, resultOperand, resultType, diagnostics);
@@ -5386,7 +5387,7 @@ private BoundExpression BindAsOperator(BinaryExpressionSyntax node, BindingDiagn
operandPlaceholder = new BoundValuePlaceholder(operand.Syntax, operand.Type).MakeCompilerGenerated();
operandConversion = CreateConversion(node, operandPlaceholder,
Conversion.NullLiteral,
- isCast: false, conversionGroupOpt: null, resultType, diagnostics);
+ isCast: false, conversionGroupOpt: null, InConversionGroupFlags.Unspecified, resultType, diagnostics);
return new BoundAsOperator(node, operand, typeExpression, operandPlaceholder, operandConversion, resultType);
}
@@ -5433,7 +5434,7 @@ private BoundExpression BindAsOperator(BinaryExpressionSyntax node, BindingDiagn
operandPlaceholder = new BoundValuePlaceholder(operand.Syntax, operand.Type).MakeCompilerGenerated();
operandConversion = CreateConversion(node, operandPlaceholder,
conversion,
- isCast: false, conversionGroupOpt: null, resultType, diagnostics);
+ isCast: false, conversionGroupOpt: null, InConversionGroupFlags.Unspecified, resultType, diagnostics);
}
else
{
@@ -5652,7 +5653,7 @@ private BoundExpression BindNullCoalescingOperator(BinaryExpressionSyntax node,
var objectType = GetSpecialType(SpecialType.System_Object, diagnostics, node);
var leftConversion = CreateConversion(node, leftPlaceholder,
Conversions.ClassifyConversionFromExpression(leftOperand, objectType, isChecked: CheckOverflowAtRuntime, ref useSiteInfo),
- isCast: false, conversionGroupOpt: null, objectType, diagnostics);
+ isCast: false, conversionGroupOpt: null, InConversionGroupFlags.Unspecified, objectType, diagnostics);
rightOperand = BindToNaturalType(rightOperand, diagnostics);
diagnostics.Add(node, useSiteInfo);
@@ -5740,7 +5741,7 @@ private BoundExpression BindNullCoalescingOperator(BinaryExpressionSyntax node,
if (leftConversionClassification.Exists)
{
var leftPlaceholder = new BoundValuePlaceholder(leftOperand.Syntax, optLeftType0).MakeCompilerGenerated();
- var leftConversion = CreateConversion(node, leftPlaceholder, leftConversionClassification, isCast: false, conversionGroupOpt: null, optRightType, diagnostics);
+ var leftConversion = CreateConversion(node, leftPlaceholder, leftConversionClassification, isCast: false, conversionGroupOpt: null, InConversionGroupFlags.Unspecified, optRightType, diagnostics);
diagnostics.Add(node, useSiteInfo);
return new BoundNullCoalescingOperator(node, leftOperand, rightOperand, leftPlaceholder, leftConversion, resultKind, @checked: CheckOverflowAtRuntime, optRightType);
@@ -5754,7 +5755,7 @@ private BoundExpression BindNullCoalescingOperator(BinaryExpressionSyntax node,
if (leftConversionClassification.Exists)
{
var leftPlaceholder = new BoundValuePlaceholder(leftOperand.Syntax, optLeftType).MakeCompilerGenerated();
- var leftConversion = CreateConversion(node, leftPlaceholder, leftConversionClassification, isCast: false, conversionGroupOpt: null, optRightType, diagnostics);
+ var leftConversion = CreateConversion(node, leftPlaceholder, leftConversionClassification, isCast: false, conversionGroupOpt: null, InConversionGroupFlags.Unspecified, optRightType, diagnostics);
diagnostics.Add(node, useSiteInfo);
return new BoundNullCoalescingOperator(node, leftOperand, rightOperand, leftPlaceholder, leftConversion, resultKind, @checked: CheckOverflowAtRuntime, optRightType);
diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs
index 8551f6ab5ae13..af94974d4c273 100644
--- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs
+++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs
@@ -1384,7 +1384,8 @@ private BoundExpression GetFixedLocalCollectionInitializer(
if (elementConversionClassification.IsValid)
{
elementPlaceholder = new BoundValuePlaceholder(initializerSyntax, pointerType).MakeCompilerGenerated();
- elementConversion = CreateConversion(initializerSyntax, elementPlaceholder, elementConversionClassification, isCast: false, conversionGroupOpt: null, declType,
+ elementConversion = CreateConversion(initializerSyntax, elementPlaceholder, elementConversionClassification, isCast: false,
+ conversionGroupOpt: null, InConversionGroupFlags.Unspecified, declType,
elementConversionClassification.IsImplicit ? diagnostics : BindingDiagnosticBag.Discarded);
}
else
@@ -2028,7 +2029,7 @@ internal BoundExpression GenerateConversionForAssignment(TypeSymbol targetType,
diagnostics = BindingDiagnosticBag.Discarded;
}
- return CreateConversion(expression.Syntax, expression, conversion, isCast: false, conversionGroupOpt: null, targetType, diagnostics);
+ return CreateConversion(expression.Syntax, expression, conversion, isCast: false, conversionGroupOpt: null, InConversionGroupFlags.Unspecified, targetType, diagnostics);
}
#nullable enable
@@ -2689,7 +2690,7 @@ internal BoundExpression BindBooleanExpression(ExpressionSyntax node, BindingDia
// The expression could not be bound. Insert a fake conversion
// around it to bool and keep on going.
// NOTE: no user-defined conversion candidates.
- return BoundConversion.Synthesized(node, BindToTypeForErrorRecovery(expr), Conversion.NoConversion, false, explicitCastInCode: false, conversionGroupOpt: null, ConstantValue.NotAvailable, boolean, hasErrors: true);
+ return BoundConversion.Synthesized(node, BindToTypeForErrorRecovery(expr), Conversion.NoConversion, false, explicitCastInCode: false, conversionGroupOpt: null, InConversionGroupFlags.Unspecified, ConstantValue.NotAvailable, boolean, hasErrors: true);
}
// Oddly enough, "if(dyn)" is bound not as a dynamic conversion to bool, but as a dynamic
@@ -2741,6 +2742,7 @@ internal BoundExpression BindBooleanExpression(ExpressionSyntax node, BindingDia
conversion: conversion,
isCast: false,
conversionGroupOpt: null,
+ InConversionGroupFlags.Unspecified,
wasCompilerGenerated: true,
destination: boolean,
diagnostics: diagnostics);
@@ -2756,7 +2758,7 @@ internal BoundExpression BindBooleanExpression(ExpressionSyntax node, BindingDia
{
// No. Give a "not convertible to bool" error.
GenerateImplicitConversionError(diagnostics, node, conversion, expr, boolean);
- return BoundConversion.Synthesized(node, expr, Conversion.NoConversion, false, explicitCastInCode: false, conversionGroupOpt: null, ConstantValue.NotAvailable, boolean, hasErrors: true);
+ return BoundConversion.Synthesized(node, expr, Conversion.NoConversion, false, explicitCastInCode: false, conversionGroupOpt: null, InConversionGroupFlags.Unspecified, ConstantValue.NotAvailable, boolean, hasErrors: true);
}
UnaryOperatorSignature signature = best.Signature;
@@ -2767,6 +2769,7 @@ internal BoundExpression BindBooleanExpression(ExpressionSyntax node, BindingDia
best.Conversion,
isCast: false,
conversionGroupOpt: null,
+ InConversionGroupFlags.Unspecified,
destination: best.Signature.OperandType,
diagnostics: diagnostics);
@@ -3240,7 +3243,7 @@ internal BoundExpression CreateReturnConversion(
}
}
- return CreateConversion(argument.Syntax, argument, conversion, isCast: false, conversionGroupOpt: null, returnType, diagnostics);
+ return CreateConversion(argument.Syntax, argument, conversion, isCast: false, conversionGroupOpt: null, InConversionGroupFlags.Unspecified, returnType, diagnostics);
}
private BoundTryStatement BindTryStatement(TryStatementSyntax node, BindingDiagnosticBag diagnostics)
diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_TupleOperators.cs b/src/Compilers/CSharp/Portable/Binder/Binder_TupleOperators.cs
index 48f03491a0303..493d3af9cd91f 100644
--- a/src/Compilers/CSharp/Portable/Binder/Binder_TupleOperators.cs
+++ b/src/Compilers/CSharp/Portable/Binder/Binder_TupleOperators.cs
@@ -136,7 +136,7 @@ private void PrepareBoolConversionAndTruthOperator(TypeSymbol type, BinaryExpres
if (conversion.IsImplicit)
{
conversionForBoolPlaceholder = new BoundValuePlaceholder(node, type).MakeCompilerGenerated();
- conversionForBool = CreateConversion(node, conversionForBoolPlaceholder, conversion, isCast: false, conversionGroupOpt: null, boolean, diagnostics);
+ conversionForBool = CreateConversion(node, conversionForBoolPlaceholder, conversion, isCast: false, conversionGroupOpt: null, InConversionGroupFlags.Unspecified, boolean, diagnostics);
boolOperator = default;
return;
}
@@ -166,7 +166,7 @@ private void PrepareBoolConversionAndTruthOperator(TypeSymbol type, BinaryExpres
if (best.HasValue)
{
conversionForBoolPlaceholder = new BoundValuePlaceholder(node, type).MakeCompilerGenerated();
- conversionForBool = CreateConversion(node, conversionForBoolPlaceholder, best.Conversion, isCast: false, conversionGroupOpt: null, best.Signature.OperandType, diagnostics);
+ conversionForBool = CreateConversion(node, conversionForBoolPlaceholder, best.Conversion, isCast: false, conversionGroupOpt: null, InConversionGroupFlags.Unspecified, best.Signature.OperandType, diagnostics);
boolOperator = best.Signature;
return;
}
diff --git a/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs b/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs
index 32393bbad9ace..2d6af3dcfaf77 100644
--- a/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs
+++ b/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs
@@ -511,7 +511,7 @@ private BoundForEachStatement BindForEachPartsWorker(BindingDiagnosticBag diagno
createConversionDiagnostics = BindingDiagnosticBag.GetInstance(diagnostics);
}
- BoundExpression elementConversion = CreateConversion(_syntax, elementPlaceholder, elementConversionClassification, isCast: false, conversionGroupOpt: null, iterationVariableType.Type, createConversionDiagnostics);
+ BoundExpression elementConversion = CreateConversion(_syntax, elementPlaceholder, elementConversionClassification, isCast: false, conversionGroupOpt: null, InConversionGroupFlags.Unspecified, iterationVariableType.Type, createConversionDiagnostics);
if (createConversionDiagnostics.AccumulatesDiagnostics && !createConversionDiagnostics.DiagnosticBag.IsEmptyWithoutResolution)
{
@@ -567,7 +567,7 @@ private BoundForEachStatement BindForEachPartsWorker(BindingDiagnosticBag diagno
if (currentConversionClassification.IsValid)
{
builder.CurrentPlaceholder = new BoundValuePlaceholder(_syntax, builder.CurrentPropertyGetter.ReturnType).MakeCompilerGenerated();
- builder.CurrentConversion = CreateConversion(_syntax, builder.CurrentPlaceholder, currentConversionClassification, isCast: false, conversionGroupOpt: null, builder.ElementType, diagnostics);
+ builder.CurrentConversion = CreateConversion(_syntax, builder.CurrentPlaceholder, currentConversionClassification, isCast: false, conversionGroupOpt: null, InConversionGroupFlags.Unspecified, builder.ElementType, diagnostics);
}
if (IsAsync)
@@ -651,6 +651,7 @@ protected BoundExpression ConvertForEachCollection(
collectionConversionClassification,
isCast: false,
conversionGroupOpt: null,
+ InConversionGroupFlags.Unspecified,
collectionType,
diagnostics);
@@ -667,6 +668,7 @@ protected BoundExpression ConvertForEachCollection(
@checked: CheckOverflowAtRuntime,
explicitCastInCode: false,
conversionGroupOpt: null,
+ InConversionGroupFlags.Unspecified,
ConstantValue.NotAvailable,
collectionType);
}
@@ -1561,6 +1563,7 @@ private MethodArgumentInfo FindForEachPatternMethodViaExtension(SyntaxNode synta
@checked: CheckOverflowAtRuntime,
explicitCastInCode: false,
conversionGroupOpt: null,
+ InConversionGroupFlags.Unspecified,
ConstantValue.NotAvailable,
result.Parameters[0].Type);
diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs
index 731dcf784e037..fdb7132fba85e 100644
--- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs
+++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs
@@ -1180,7 +1180,7 @@ public override bool Equals(object? obj)
/// true if the specified object is equal to the current object; otherwise, false.
public bool Equals(Conversion other)
{
- return this.Kind == other.Kind && this.Method == other.Method;
+ return this.Kind == other.Kind && Equals(this._uncommonData, other._uncommonData);
}
///
@@ -1189,7 +1189,7 @@ public bool Equals(Conversion other)
/// A hash code for the current object.
public override int GetHashCode()
{
- return Hash.Combine(this.Method, (int)this.Kind);
+ return Hash.Combine(this._uncommonData, (int)this.Kind);
}
///
diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedImplicitConversions.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedImplicitConversions.cs
index dfbc2e991b3fa..92e44f962385d 100644
--- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedImplicitConversions.cs
+++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedImplicitConversions.cs
@@ -611,7 +611,7 @@ private Conversion EncompassingImplicitConversion(TypeSymbol a, TypeSymbol b, re
return EncompassingImplicitConversion(aExpr: null, a, b, ref useSiteInfo);
}
- private static bool IsEncompassingImplicitConversionKind(ConversionKind kind)
+ internal static bool IsEncompassingImplicitConversionKind(ConversionKind kind)
{
switch (kind)
{
diff --git a/src/Compilers/CSharp/Portable/Binder/SwitchBinder.cs b/src/Compilers/CSharp/Portable/Binder/SwitchBinder.cs
index 13b7e5c6fadbc..ec238a709c8b0 100644
--- a/src/Compilers/CSharp/Portable/Binder/SwitchBinder.cs
+++ b/src/Compilers/CSharp/Portable/Binder/SwitchBinder.cs
@@ -433,7 +433,7 @@ private BoundExpression BindSwitchGoverningExpression(BindingDiagnosticBag diagn
Debug.Assert(conversion.Method.IsUserDefinedConversion());
Debug.Assert(conversion.UserDefinedToConversion.IsIdentity);
Debug.Assert(resultantGoverningType.IsValidV6SwitchGoverningType(isTargetTypeOfUserDefinedOp: true));
- return binder.CreateConversion(node, switchGoverningExpression, conversion, isCast: false, conversionGroupOpt: null, resultantGoverningType, diagnostics);
+ return binder.CreateConversion(node, switchGoverningExpression, conversion, isCast: false, conversionGroupOpt: null, InConversionGroupFlags.Unspecified, resultantGoverningType, diagnostics);
}
else if (!switchGoverningType.IsVoidType())
{
diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundConversion.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundConversion.cs
index 3ea0d97417d22..d3a95d3be08a3 100644
--- a/src/Compilers/CSharp/Portable/BoundTree/BoundConversion.cs
+++ b/src/Compilers/CSharp/Portable/BoundTree/BoundConversion.cs
@@ -12,6 +12,140 @@ private partial void Validate()
{
Debug.Assert(!Binder.IsTypeOrValueExpression(Operand));
Debug.Assert(!Binder.IsMethodGroupWithTypeOrValueReceiver(Operand));
+
+ if (Conversion.IsTupleLiteralConversion ||
+ (Conversion.IsNullable && Conversion.UnderlyingConversions[0].IsTupleLiteralConversion))
+ {
+ Debug.Assert((InConversionGroupFlags & InConversionGroupFlags.TupleLiteral) != 0);
+ }
+
+ if ((InConversionGroupFlags & InConversionGroupFlags.TupleLiteral) != 0)
+ {
+ Debug.Assert(Conversion.IsTupleLiteralConversion ||
+ (Conversion.IsNullable && Conversion.UnderlyingConversions[0].IsTupleLiteralConversion));
+ Debug.Assert(Operand is BoundConvertedTupleLiteral);
+ }
+
+ if ((InConversionGroupFlags & InConversionGroupFlags.TupleLiteralExplicitIdentity) != 0)
+ {
+ Debug.Assert(Conversion.IsIdentity);
+ Debug.Assert(Operand is BoundConvertedTupleLiteral ||
+ (Operand is BoundConversion operandAsConversion &&
+ operandAsConversion.ConversionGroupOpt == ConversionGroupOpt &&
+ (operandAsConversion.InConversionGroupFlags & InConversionGroupFlags.TupleLiteral) != 0));
+ }
+
+ // Assert the shape of the conversion tree for user-defined conversions.
+ if (Conversion.IsUserDefined)
+ {
+ if (InConversionGroupFlags is InConversionGroupFlags.LoweredFormOfUserDefinedConversionForExpressionTree or InConversionGroupFlags.TupleBinaryOperatorPendingLowering)
+ {
+ Debug.Assert(ConversionGroupOpt is null);
+ }
+ else
+ {
+ Debug.Assert(ConversionGroupOpt?.Conversion.IsUserDefined == true);
+ }
+ }
+
+ if (ConversionGroupOpt?.Conversion.IsUserDefined == true)
+ {
+ if (Conversion.IsUserDefined)
+ {
+ Debug.Assert(Conversion == ConversionGroupOpt.Conversion);
+
+ if (!ConversionGroupOpt.Conversion.IsValid)
+ {
+ Debug.Assert(InConversionGroupFlags == (InConversionGroupFlags.UserDefinedOperator | InConversionGroupFlags.UserDefinedErroneous));
+ Debug.Assert(Operand is not BoundConversion operandAsConversion || operandAsConversion.ConversionGroupOpt != ConversionGroupOpt);
+ }
+ else
+ {
+ Debug.Assert(InConversionGroupFlags == InConversionGroupFlags.UserDefinedOperator);
+
+ if (Operand is BoundConversion operandAsConversion && operandAsConversion.ConversionGroupOpt == ConversionGroupOpt)
+ {
+ Debug.Assert((operandAsConversion.InConversionGroupFlags & (InConversionGroupFlags.UserDefinedFromConversion | InConversionGroupFlags.UserDefinedFromConversionAdjustment)) != 0);
+ }
+ else
+ {
+ Debug.Assert(Conversion.UserDefinedFromConversion.IsIdentity ||
+ (Conversion.UserDefinedFromConversion.IsTupleLiteralConversion &&
+ Operand is BoundConvertedTupleLiteral));
+ }
+ }
+ }
+ else
+ {
+ Debug.Assert(!ExplicitCastInCode);
+ Debug.Assert(ConversionGroupOpt.Conversion.IsValid);
+
+ if (ConversionGroupOpt.Conversion.IsImplicit)
+ {
+ Debug.Assert(ConversionsBase.IsEncompassingImplicitConversionKind(Conversion.Kind) ||
+ (Conversion.IsExplicit && Conversion.IsNullable &&
+ (InConversionGroupFlags & InConversionGroupFlags.UserDefinedFromConversionAdjustment) != 0));
+ }
+
+ const InConversionGroupFlags all =
+ InConversionGroupFlags.UserDefinedOperator |
+ InConversionGroupFlags.UserDefinedFromConversion |
+ InConversionGroupFlags.UserDefinedFromConversionAdjustment |
+ InConversionGroupFlags.UserDefinedReturnTypeAdjustment |
+ InConversionGroupFlags.UserDefinedFinal |
+ InConversionGroupFlags.UserDefinedErroneous;
+
+ if ((InConversionGroupFlags & InConversionGroupFlags.UserDefinedFromConversion) != 0)
+ {
+ Debug.Assert((InConversionGroupFlags & all) == InConversionGroupFlags.UserDefinedFromConversion);
+ Debug.Assert(Operand is not BoundConversion operandAsConversion ||
+ operandAsConversion.ConversionGroupOpt != ConversionGroupOpt);
+ Debug.Assert(Conversion == ConversionGroupOpt.Conversion.UserDefinedFromConversion);
+ }
+ else if ((InConversionGroupFlags & InConversionGroupFlags.UserDefinedFromConversionAdjustment) != 0)
+ {
+ Debug.Assert((InConversionGroupFlags & all) == InConversionGroupFlags.UserDefinedFromConversionAdjustment);
+ Debug.Assert(Conversion.IsNullable);
+ Debug.Assert(Conversion.IsExplicit);
+ Debug.Assert(Conversion.UnderlyingConversions[0].IsIdentity);
+
+ if (Operand is BoundConversion operandAsConversion && operandAsConversion.ConversionGroupOpt == ConversionGroupOpt)
+ {
+ Debug.Assert((operandAsConversion.InConversionGroupFlags & InConversionGroupFlags.UserDefinedFromConversion) != 0);
+ }
+ else
+ {
+ Debug.Assert(ConversionGroupOpt.Conversion.UserDefinedFromConversion.IsIdentity ||
+ (ConversionGroupOpt.Conversion.UserDefinedFromConversion.IsTupleLiteralConversion &&
+ Operand is BoundConvertedTupleLiteral));
+ }
+ }
+ else if ((InConversionGroupFlags & InConversionGroupFlags.UserDefinedReturnTypeAdjustment) != 0)
+ {
+ Debug.Assert((InConversionGroupFlags & all) == InConversionGroupFlags.UserDefinedReturnTypeAdjustment);
+ Debug.Assert(Conversion.IsNullable);
+ Debug.Assert(Conversion.IsImplicit);
+ Debug.Assert(!Conversion.UnderlyingConversions[0].IsIdentity);
+ Debug.Assert(Operand is BoundConversion operandAsConversion &&
+ operandAsConversion.ConversionGroupOpt == ConversionGroupOpt &&
+ operandAsConversion.Conversion.IsUserDefined);
+ }
+ else if ((InConversionGroupFlags & InConversionGroupFlags.UserDefinedFinal) != 0)
+ {
+ Debug.Assert((InConversionGroupFlags & all) == InConversionGroupFlags.UserDefinedFinal);
+
+ Debug.Assert(Operand is BoundConversion operandAsConversion &&
+ operandAsConversion.ConversionGroupOpt == ConversionGroupOpt &&
+ (operandAsConversion.Conversion.IsUserDefined ||
+ (operandAsConversion.InConversionGroupFlags & InConversionGroupFlags.UserDefinedReturnTypeAdjustment) != 0));
+
+ }
+ else
+ {
+ ExceptionUtilities.UnexpectedValue(InConversionGroupFlags);
+ }
+ }
+ }
}
}
}
diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundExpression.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundExpression.cs
index baaf9fb3386cb..27f56a28831e5 100644
--- a/src/Compilers/CSharp/Portable/BoundTree/BoundExpression.cs
+++ b/src/Compilers/CSharp/Portable/BoundTree/BoundExpression.cs
@@ -485,7 +485,7 @@ public override bool SuppressVirtualCalls
public BoundConversion UpdateOperand(BoundExpression operand)
{
- return this.Update(operand: operand, this.Conversion, this.IsBaseConversion, this.Checked, this.ExplicitCastInCode, this.ConstantValueOpt, this.ConversionGroupOpt, this.Type);
+ return this.Update(operand: operand, this.Conversion, this.IsBaseConversion, this.Checked, this.ExplicitCastInCode, this.ConstantValueOpt, this.ConversionGroupOpt, this.InConversionGroupFlags, this.Type);
}
///
diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml
index 6e07066f211f2..4c7b1e3a72fbd 100644
--- a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml
+++ b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml
@@ -39,6 +39,7 @@
+
@@ -909,6 +910,7 @@
Conversion is represented by a single BoundConversion.
-->
+