private BoundExpression ApplyConvertedTypes(BoundExpression expr, TupleBinaryOperatorInfo @operator, bool isRight, DiagnosticBag diagnostics) { TypeSymbol convertedType = isRight ? @operator.RightConvertedTypeOpt : @operator.LeftConvertedTypeOpt; if (convertedType is null) { if (@operator.InfoKind == TupleBinaryOperatorInfoKind.Multiple && expr.Kind == BoundKind.TupleLiteral) { // Although the tuple will remain typeless, we'll give elements converted types as possible var multiple = (TupleBinaryOperatorInfo.Multiple)@operator; if (multiple.Operators.Length == 0) { return(expr); } var tuple = (BoundTupleLiteral)expr; ImmutableArray <BoundExpression> arguments = tuple.Arguments; int length = arguments.Length; Debug.Assert(length == multiple.Operators.Length); var builder = ArrayBuilder <BoundExpression> .GetInstance(length); for (int i = 0; i < length; i++) { builder.Add(ApplyConvertedTypes(arguments[i], multiple.Operators[i], isRight, diagnostics)); } return(tuple.Update(argumentNamesOpt: default, inferredNamesOpt: default, builder.ToImmutableAndFree(), tuple.Type));
private TupleBinaryOperatorInfo BindTupleDynamicBinaryOperatorSingleInfo( BinaryExpressionSyntax node, BinaryOperatorKind kind, BoundExpression left, BoundExpression right, BindingDiagnosticBag diagnostics ) { // This method binds binary == and != operators where one or both of the operands are dynamic. Debug.Assert( (object)left.Type != null && left.Type.IsDynamic() || (object)right.Type != null && right.Type.IsDynamic() ); bool hasError = false; if (!IsLegalDynamicOperand(left) || !IsLegalDynamicOperand(right)) { // Operator '{0}' cannot be applied to operands of type '{1}' and '{2}' Error( diagnostics, ErrorCode.ERR_BadBinaryOps, node, node.OperatorToken.Text, left.Display, right.Display ); hasError = true; } BinaryOperatorKind elementOperatorKind = hasError ? kind : kind.WithType(BinaryOperatorKind.Dynamic); TypeSymbol dynamicType = hasError ? CreateErrorType() : Compilation.DynamicType; // We'll want to dynamically invoke operators op_true (/op_false) for equality (/inequality) comparison, but we don't need // to prepare either a conversion or a truth operator. Those can just be synthesized during lowering. return(new TupleBinaryOperatorInfo.Single( dynamicType, dynamicType, elementOperatorKind, methodSymbolOpt: null, conversionForBool: Conversion.Identity, boolOperator: default )); }
private BoundExpression RewriteTupleOperator(TupleBinaryOperatorInfo @operator, BoundExpression left, BoundExpression right, TypeSymbol boolType, ArrayBuilder <LocalSymbol> temps, BinaryOperatorKind operatorKind) { switch (@operator.InfoKind) { case TupleBinaryOperatorInfoKind.Multiple: return(RewriteTupleNestedOperators((TupleBinaryOperatorInfo.Multiple)@operator, left, right, boolType, temps, operatorKind)); case TupleBinaryOperatorInfoKind.Single: return(RewriteTupleSingleOperator((TupleBinaryOperatorInfo.Single)@operator, left, right, boolType, operatorKind)); case TupleBinaryOperatorInfoKind.NullNull: var nullnull = (TupleBinaryOperatorInfo.NullNull)@operator; return(new BoundLiteral(left.Syntax, ConstantValue.Create(nullnull.Kind == BinaryOperatorKind.Equal), boolType)); default: throw ExceptionUtilities.UnexpectedValue(@operator.InfoKind); } }
private BoundExpression ApplyConvertedTypes(BoundExpression expr, TupleBinaryOperatorInfo @operator, bool isRight, DiagnosticBag diagnostics) { TypeSymbol convertedType = isRight ? @operator.RightConvertedTypeOpt : @operator.LeftConvertedTypeOpt; if (convertedType is null) { // Note: issues with default will already have been reported by BindSimpleBinaryOperator (ie. we couldn't find a suitable element-wise operator) if (@operator.InfoKind == TupleBinaryOperatorInfoKind.Multiple && expr is BoundTupleLiteral tuple) { // Although the tuple will remain typeless, we'll give elements converted types as possible var multiple = (TupleBinaryOperatorInfo.Multiple)@operator; if (multiple.Operators.Length == 0) { return(BindToNaturalType(expr, diagnostics, reportNoTargetType: false)); } ImmutableArray <BoundExpression> arguments = tuple.Arguments; int length = arguments.Length; Debug.Assert(length == multiple.Operators.Length); var builder = ArrayBuilder <BoundExpression> .GetInstance(length); for (int i = 0; i < length; i++) { builder.Add(ApplyConvertedTypes(arguments[i], multiple.Operators[i], isRight, diagnostics)); } return(new BoundConvertedTupleLiteral( tuple.Syntax, tuple, wasTargetTyped: false, builder.ToImmutableAndFree(), tuple.ArgumentNamesOpt, tuple.InferredNamesOpt, tuple.Type, tuple.HasErrors)); } // This element isn't getting a converted type return(BindToNaturalType(expr, diagnostics, reportNoTargetType: false)); } // We were able to determine a converted type (for this tuple literal or element), we can just convert to it return(GenerateConversionForAssignment(convertedType, expr, diagnostics)); }
/// <summary> /// Walk down tuple literals and replace all the side-effecting elements that need saving with temps. /// Expressions that are not tuple literals need saving, and tuple literals that are involved in a simple comparison rather than a tuple comparison. /// </summary> private BoundExpression ReplaceTerminalElementsWithTemps(BoundExpression expr, TupleBinaryOperatorInfo operators, ArrayBuilder <BoundExpression> initEffects, ArrayBuilder <LocalSymbol> temps) { if (operators.InfoKind == TupleBinaryOperatorInfoKind.Multiple) { // Example: // in `(expr1, expr2) == (..., ...)` we need to save `expr1` and `expr2` if (expr.Kind == BoundKind.TupleLiteral) { var tuple = (BoundTupleLiteral)expr; var multiple = (TupleBinaryOperatorInfo.Multiple)operators; var builder = ArrayBuilder <BoundExpression> .GetInstance(tuple.Arguments.Length); for (int i = 0; i < tuple.Arguments.Length; i++) { var argument = tuple.Arguments[i]; var newArgument = ReplaceTerminalElementsWithTemps(argument, multiple.Operators[i], initEffects, temps); builder.Add(newArgument); } return(new BoundTupleLiteral(tuple.Syntax, tuple.ArgumentNamesOpt, tuple.InferredNamesOpt, builder.ToImmutableAndFree(), tuple.Type, tuple.HasErrors)); } } // Examples: // in `expr == (..., ...)` we need to save `expr` because it's not a tuple literal // in `(..., expr) == (..., (..., ...))` we need to save `expr` because it is used in a simple comparison return(EvaluateSideEffectingArgumentToTemp(VisitExpression(expr), initEffects, ref temps)); }
/// <summary> /// Walk down tuple literals and replace all the side-effecting elements that need saving with temps. /// Expressions that are not tuple literals need saving, and tuple literals that are involved in a simple comparison rather than a tuple comparison. /// </summary> private BoundExpression ReplaceTerminalElementsWithTemps(BoundExpression expr, TupleBinaryOperatorInfo operators, ArrayBuilder <BoundExpression> initEffects, ArrayBuilder <LocalSymbol> temps) { if (operators.InfoKind == TupleBinaryOperatorInfoKind.Multiple) { // Example: // in `(expr1, expr2) == (..., ...)` we need to save `expr1` and `expr2` if (expr.Kind == BoundKind.TupleLiteral) { var tuple = (BoundTupleLiteral)expr; var multiple = (TupleBinaryOperatorInfo.Multiple)operators; var builder = ArrayBuilder <BoundExpression> .GetInstance(tuple.Arguments.Length); for (int i = 0; i < tuple.Arguments.Length; i++) { var argument = tuple.Arguments[i]; var newArgument = ReplaceTerminalElementsWithTemps(argument, multiple.Operators[i], initEffects, temps); builder.Add(newArgument); } return(new BoundTupleLiteral(tuple.Syntax, tuple.ArgumentNamesOpt, tuple.InferredNamesOpt, builder.ToImmutableAndFree(), tuple.Type, tuple.HasErrors)); } } // Examples: // in `expr == (..., ...)` we need to save `expr` because it's not a tuple literal // in `(..., expr) == (..., (..., ...))` we need to save `expr` because it is used in a simple comparison BoundExpression loweredExpr = VisitExpression(expr); if ((object)loweredExpr.Type != null) { BoundExpression value = NullableAlwaysHasValue(loweredExpr); if (value != null) { // Optimization: if the nullable expression always has a value, we'll replace that value // with a temp saving that value BoundExpression savedValue = EvaluateSideEffectingArgumentToTemp(value, initEffects, temps); var objectCreation = (BoundObjectCreationExpression)loweredExpr; return(objectCreation.UpdateArgumentsAndInitializer(ImmutableArray.Create(savedValue), objectCreation.ArgumentRefKindsOpt, objectCreation.InitializerExpressionOpt)); } } // Note: lowered nullable expressions that never have a value also don't have side-effects return(EvaluateSideEffectingArgumentToTemp(loweredExpr, initEffects, temps)); }