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. --> +