private void UnaryOperatorEasyOut(UnaryOperatorKind kind, BoundExpression operand, UnaryOperatorOverloadResolutionResult result) { var operandType = operand.Type; if ((object)operandType == null) { return; } var easyOut = UnopEasyOut.OpKind(kind, operandType); if (easyOut == UnaryOperatorKind.Error) { return; } UnaryOperatorSignature signature = this.Compilation.builtInOperators.GetSignature(easyOut); Conversion?conversion = Conversions.FastClassifyConversion(operandType, signature.OperandType); Debug.Assert(conversion.HasValue && conversion.Value.IsImplicit); result.Results.Add(UnaryOperatorAnalysisResult.Applicable(signature, conversion.Value)); }
private static UnaryOperatorSignature?GetPointerOperation(UnaryOperatorKind kind, BoundExpression operand) { Debug.Assert(operand != null); var pointerType = operand.Type as PointerTypeSymbol; if ((object)pointerType == null) { return(null); } UnaryOperatorSignature?op = null; switch (kind) { case UnaryOperatorKind.PostfixIncrement: case UnaryOperatorKind.PostfixDecrement: case UnaryOperatorKind.PrefixIncrement: case UnaryOperatorKind.PrefixDecrement: op = new UnaryOperatorSignature(kind | UnaryOperatorKind.Pointer, pointerType, pointerType); break; } return(op); }
public static UnaryOperatorAnalysisResult Inapplicable(UnaryOperatorSignature signature, Conversion conversion) { return(new UnaryOperatorAnalysisResult(OperatorAnalysisResultKind.Inapplicable, signature, conversion)); }
private UnaryOperatorAnalysisResult(OperatorAnalysisResultKind kind, UnaryOperatorSignature signature, Conversion conversion) { this.Kind = kind; this.Signature = signature; this.Conversion = conversion; }
/// <summary> /// If an element-wise binary operator returns a non-bool type, we will either: /// - prepare a conversion to bool if one exists /// - prepare a truth operator: op_false in the case of an equality (<c>a == b</c> will be lowered to <c>!((a == b).op_false)</c>) or op_true in the case of inequality, /// with the conversion being used for its input. /// </summary> private void PrepareBoolConversionAndTruthOperator(TypeSymbol type, BinaryExpressionSyntax node, BinaryOperatorKind binaryOperator, DiagnosticBag diagnostics, out Conversion conversionForBool, out UnaryOperatorSignature boolOperator) { // Is the operand implicitly convertible to bool? HashSet <DiagnosticInfo> useSiteDiagnostics = null; TypeSymbol boolean = GetSpecialType(SpecialType.System_Boolean, diagnostics, node); Conversion conversion = this.Conversions.ClassifyImplicitConversionFromType(type, boolean, ref useSiteDiagnostics); diagnostics.Add(node, useSiteDiagnostics); if (conversion.IsImplicit) { ReportDiagnosticsIfObsolete(diagnostics, conversion, node, hasBaseReceiver: false); conversionForBool = conversion; boolOperator = default; return; } // It was not. Does it implement operator true (or false)? UnaryOperatorKind boolOpKind; switch (binaryOperator) { case BinaryOperatorKind.Equal: boolOpKind = UnaryOperatorKind.False; break; case BinaryOperatorKind.NotEqual: boolOpKind = UnaryOperatorKind.True; break; default: throw ExceptionUtilities.UnexpectedValue(binaryOperator); } LookupResultKind resultKind; ImmutableArray <MethodSymbol> originalUserDefinedOperators; BoundExpression comparisonResult = new BoundTupleOperandPlaceholder(node, type); UnaryOperatorAnalysisResult best = this.UnaryOperatorOverloadResolution(boolOpKind, comparisonResult, node, diagnostics, out resultKind, out originalUserDefinedOperators); if (best.HasValue) { conversionForBool = best.Conversion; boolOperator = best.Signature; return; } // It did not. Give a "not convertible to bool" error. GenerateImplicitConversionError(diagnostics, node, conversion, comparisonResult, boolean); conversionForBool = Conversion.NoConversion; boolOperator = default; return; }
private BetterResult BetterOperator( UnaryOperatorSignature op1, UnaryOperatorSignature op2, BoundExpression operand, ref CompoundUseSiteInfo <AssemblySymbol> useSiteInfo ) { // First we see if the conversion from the operand to one operand type is better than // the conversion to the other. BetterResult better = BetterConversionFromExpression( operand, op1.OperandType, op2.OperandType, ref useSiteInfo ); if (better == BetterResult.Left || better == BetterResult.Right) { return(better); } // There was no better member on the basis of conversions. Go to the tiebreaking round. // SPEC: In case the parameter type sequences P1, P2 and Q1, Q2 are equivalent -- that is, every Pi // SPEC: has an identity conversion to the corresponding Qi -- the following tie-breaking rules // SPEC: are applied: if (Conversions.HasIdentityConversion(op1.OperandType, op2.OperandType)) { // SPEC: If Mp has more specific parameter types than Mq then Mp is better than Mq. // Under what circumstances can two unary operators with identical signatures be "more specific" // than another? With a binary operator you could have C<T>.op+(C<T>, T) and C<T>.op+(C<T>, int). // When doing overload resolution on C<int> + int, the latter is more specific. But with a unary // operator, the sole operand *must* be the containing type or its nullable type. Therefore // if there is an identity conversion, then the parameters really were identical. We therefore // skip checking for specificity. // SPEC: If one member is a non-lifted operator and the other is a lifted operator, // SPEC: the non-lifted one is better. bool lifted1 = op1.Kind.IsLifted(); bool lifted2 = op2.Kind.IsLifted(); if (lifted1 && !lifted2) { return(BetterResult.Right); } else if (!lifted1 && lifted2) { return(BetterResult.Left); } } // Always prefer operators with val parameters over operators with in parameters: if (op1.RefKind == RefKind.None && op2.RefKind == RefKind.In) { return(BetterResult.Left); } else if (op2.RefKind == RefKind.None && op1.RefKind == RefKind.In) { return(BetterResult.Right); } return(BetterResult.Neither); }
private static UnaryOperatorSignature? GetPointerOperation(UnaryOperatorKind kind, BoundExpression operand) { Debug.Assert(operand != null); var pointerType = operand.Type as PointerTypeSymbol; if ((object)pointerType == null) { return null; } UnaryOperatorSignature? op = null; switch (kind) { case UnaryOperatorKind.PostfixIncrement: case UnaryOperatorKind.PostfixDecrement: case UnaryOperatorKind.PrefixIncrement: case UnaryOperatorKind.PrefixDecrement: op = new UnaryOperatorSignature(kind | UnaryOperatorKind.Pointer, pointerType, pointerType); break; } return op; }
private BetterResult BetterOperator(UnaryOperatorSignature op1, UnaryOperatorSignature op2, BoundExpression operand, ref HashSet<DiagnosticInfo> useSiteDiagnostics) { // First we see if the conversion from the operand to one operand type is better than // the conversion to the other. BetterResult better = BetterConversionFromExpression(operand, op1.OperandType, op2.OperandType, ref useSiteDiagnostics); if (better == BetterResult.Left || better == BetterResult.Right) { return better; } // There was no better member on the basis of conversions. Go to the tiebreaking round. // SPEC: In case the parameter type sequences P1, P2 and Q1, Q2 are equivalent -- that is, every Pi // SPEC: has an identity conversion to the corresponding Qi -- the following tie-breaking rules // SPEC: are applied: if (Conversions.HasIdentityConversion(op1.OperandType, op2.OperandType)) { // SPEC: If Mp has more specific parameter types than Mq then Mp is better than Mq. // Under what circumstances can two unary operators with identical signatures be "more specific" // than another? With a binary operator you could have C<T>.op+(C<T>, T) and C<T>.op+(C<T>, int). // When doing overload resolution on C<int> + int, the latter is more specific. But with a unary // operator, the sole operand *must* be the containing type or its nullable type. Therefore // if there is an identity conversion, then the parameters really were identical. We therefore // skip checking for specificity. // SPEC: If one member is a non-lifted operator and the other is a lifted operator, // SPEC: the non-lifted one is better. bool lifted1 = op1.Kind.IsLifted(); bool lifted2 = op2.Kind.IsLifted(); if (lifted1 && !lifted2) { return BetterResult.Right; } else if (!lifted1 && lifted2) { return BetterResult.Left; } } return BetterResult.Neither; }
private void PrepareBoolConversionAndTruthOperator(TypeSymbol type, BinaryExpressionSyntax node, BinaryOperatorKind binaryOperator, BindingDiagnosticBag diagnostics, out BoundExpression conversionForBool, out BoundValuePlaceholder conversionForBoolPlaceholder, out UnaryOperatorSignature boolOperator) { // Is the operand implicitly convertible to bool? CompoundUseSiteInfo <AssemblySymbol> useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); TypeSymbol boolean = GetSpecialType(SpecialType.System_Boolean, diagnostics, node); Conversion conversion = this.Conversions.ClassifyImplicitConversionFromType(type, boolean, ref useSiteInfo); diagnostics.Add(node, useSiteInfo); if (conversion.IsImplicit) { conversionForBoolPlaceholder = new BoundValuePlaceholder(node, type).MakeCompilerGenerated(); conversionForBool = CreateConversion(node, conversionForBoolPlaceholder, conversion, isCast: false, conversionGroupOpt: null, boolean, diagnostics); boolOperator = default; return; } // It was not. Does it implement operator true (or false)? UnaryOperatorKind boolOpKind; switch (binaryOperator) { case BinaryOperatorKind.Equal: boolOpKind = UnaryOperatorKind.False; break; case BinaryOperatorKind.NotEqual: boolOpKind = UnaryOperatorKind.True; break; default: throw ExceptionUtilities.UnexpectedValue(binaryOperator); } LookupResultKind resultKind; ImmutableArray <MethodSymbol> originalUserDefinedOperators; BoundExpression comparisonResult = new BoundTupleOperandPlaceholder(node, type); UnaryOperatorAnalysisResult best = this.UnaryOperatorOverloadResolution(boolOpKind, comparisonResult, node, diagnostics, out resultKind, out originalUserDefinedOperators); if (best.HasValue) { conversionForBoolPlaceholder = new BoundValuePlaceholder(node, type).MakeCompilerGenerated(); conversionForBool = CreateConversion(node, conversionForBoolPlaceholder, best.Conversion, isCast: false, conversionGroupOpt: null, best.Signature.OperandType, diagnostics); boolOperator = best.Signature; return; } // It did not. Give a "not convertible to bool" error. GenerateImplicitConversionError(diagnostics, node, conversion, comparisonResult, boolean); conversionForBoolPlaceholder = null; conversionForBool = null; boolOperator = default; return; }
/// <summary> /// Produce an element-wise comparison and logic to ensure the result is a bool type. /// /// If an element-wise comparison doesn't return bool, then: /// - if it is dynamic, we'll do `!(comparisonResult.false)` or `comparisonResult.true` /// - if it implicitly converts to bool, we'll just do the conversion /// - otherwise, we'll do `!(comparisonResult.false)` or `comparisonResult.true` (as we'd do for `if` or `while`) /// </summary> private BoundExpression RewriteTupleSingleOperator(TupleBinaryOperatorInfo.Single single, BoundExpression left, BoundExpression right, TypeSymbol boolType, BinaryOperatorKind operatorKind) { if (single.Kind.IsDynamic()) { // Produce // !((left == right).op_false) // (left != right).op_true BoundExpression dynamicResult = _dynamicFactory.MakeDynamicBinaryOperator(single.Kind, left, right, isCompoundAssignment: false, _compilation.DynamicType).ToExpression(); if (operatorKind == BinaryOperatorKind.Equal) { return(_factory.Not(MakeUnaryOperator(UnaryOperatorKind.DynamicFalse, left.Syntax, method: null, dynamicResult, boolType))); } else { return(MakeUnaryOperator(UnaryOperatorKind.DynamicTrue, left.Syntax, method: null, dynamicResult, boolType)); } } if (left.IsLiteralNull() && right.IsLiteralNull()) { // For `null == null` this is special-cased during initial binding return(new BoundLiteral(left.Syntax, ConstantValue.Create(operatorKind == BinaryOperatorKind.Equal), boolType)); } // We leave both operands in nullable-null conversions unconverted, MakeBinaryOperator has special for null-literal bool isNullableNullConversion = single.Kind.OperandTypes() == BinaryOperatorKind.NullableNull; BoundExpression convertedLeft = isNullableNullConversion ? left : MakeConversionNode(left.Syntax, left, single.LeftConversion, single.LeftConvertedTypeOpt, @checked: false); BoundExpression convertedRight = isNullableNullConversion ? right : MakeConversionNode(right.Syntax, right, single.RightConversion, single.RightConvertedTypeOpt, @checked: false); BoundExpression binary = MakeBinaryOperator(_factory.Syntax, single.Kind, convertedLeft, convertedRight, single.MethodSymbolOpt?.ReturnType ?? boolType, single.MethodSymbolOpt); UnaryOperatorSignature boolOperator = single.BoolOperator; Conversion boolConversion = single.ConversionForBool; BoundExpression result; if (boolOperator.Kind != UnaryOperatorKind.Error) { // Produce // !((left == right).op_false) // (left != right).op_true BoundExpression convertedBinary = MakeConversionNode(_factory.Syntax, binary, boolConversion, boolOperator.OperandType, @checked: false); Debug.Assert(boolOperator.ReturnType.SpecialType == SpecialType.System_Boolean); result = MakeUnaryOperator(boolOperator.Kind, binary.Syntax, boolOperator.Method, convertedBinary, boolType); if (operatorKind == BinaryOperatorKind.Equal) { result = _factory.Not(result); } } else if (!boolConversion.IsIdentity) { // Produce // (bool)(left == right) // (bool)(left != right) result = MakeConversionNode(_factory.Syntax, binary, boolConversion, boolType, @checked: false); } else { result = binary; } return(result); }
public static UnaryOperatorAnalysisResult Inapplicable(UnaryOperatorSignature signature, Conversion conversion) { return new UnaryOperatorAnalysisResult(OperatorAnalysisResultKind.Inapplicable, signature, conversion); }