internal void Parse(BoundAsOperator boundAsOperator) { base.Parse(boundAsOperator); this.ConversionKind = boundAsOperator.Conversion.Kind; this.TargetType = Deserialize(boundAsOperator.TargetType) as TypeExpression; this.Operand = Deserialize(boundAsOperator.Operand) as Expression; }
public static bool IsImplicitConversion(this ConversionKind kind) { switch (kind) { case ConversionKind.AnonymousFunction: case ConversionKind.Boxing: case ConversionKind.Dynamic: case ConversionKind.Identity: case ConversionKind.ImplicitConstant: case ConversionKind.ImplicitEnumeration: case ConversionKind.ImplicitNullable: case ConversionKind.ImplicitNumeric: case ConversionKind.ImplicitReference: case ConversionKind.ImplicitUserDefined: case ConversionKind.MethodGroup: case ConversionKind.NullLiteral: return(true); case ConversionKind.ExplicitEnumeration: case ConversionKind.ExplicitNullable: case ConversionKind.ExplicitNumeric: case ConversionKind.ExplicitReference: case ConversionKind.ExplicitUserDefined: case ConversionKind.NoConversion: case ConversionKind.Unboxing: return(false); default: Debug.Fail("Bad conversion kind!"); return(false); } }
internal void Parse(BoundConversion boundConversion) { base.Parse(boundConversion); this.TypeSource = boundConversion.Operand.Type; this.Operand = Deserialize(boundConversion.Operand) as Expression; this.ConversionKind = boundConversion.ConversionKind; this.Checked = boundConversion.Checked; var methodGroupOperand = this.Operand as MethodGroup; if (methodGroupOperand != null && methodGroupOperand.Method == null) { var boundMethodGroup = boundConversion.Operand as BoundMethodGroup; if (boundMethodGroup != null) { var methodGroup = new MethodGroup { ReceiverOpt = Deserialize(boundMethodGroup.ReceiverOpt) as Expression, Method = boundConversion.ExpressionSymbol as IMethodSymbol }; this.Operand = methodGroup; } } }
private BoundExpression MakeIsOperator( BoundIsOperator oldNode, SyntaxNode syntax, BoundExpression rewrittenOperand, BoundTypeExpression rewrittenTargetType, ConversionKind conversionKind, TypeSymbol rewrittenType) { if (rewrittenOperand.Kind == BoundKind.MethodGroup) { var methodGroup = (BoundMethodGroup)rewrittenOperand; BoundExpression?receiver = methodGroup.ReceiverOpt; if (receiver != null && receiver.Kind != BoundKind.ThisReference) { // possible side-effect return(RewriteConstantIsOperator(receiver.Syntax, receiver, ConstantValue.False, rewrittenType)); } else { return(MakeLiteral(syntax, ConstantValue.False, rewrittenType)); } } var operandType = rewrittenOperand.Type; var targetType = rewrittenTargetType.Type; Debug.Assert(operandType is { } || rewrittenOperand.ConstantValue !.IsNull);
/// <summary> /// Emits expression and converts it to required type. /// </summary> public void EmitConvert(BoundExpression expr, TypeSymbol to, ConversionKind conversion = ConversionKind.Implicit, bool notNull = false) { Debug.Assert(expr != null); Debug.Assert(to != null); // pop effectively if (to.IsVoid()) { expr.Access = BoundAccess.None; if (!expr.IsConstant()) { // POP LOAD <expr> EmitPop(Emit(expr)); } return; } // bind target expression type expr.Access = expr.Access.WithRead(to); if (!expr.Access.IsReadRef) { // constants if (expr.ConstantValue.HasValue && to != null) { // TODO: ConversionKind.Strict ? EmitConvert(EmitLoadConstant(expr.ConstantValue.Value, to, notNull), 0, to); return; } // loads value from place most effectively without runtime type checking var place = PlaceOrNull(expr); if (place != null && place.Type != to) { var type = TryEmitVariableSpecialize(place, expr.TypeRefMask); if (type != null) { EmitConvert(type, 0, to); return; } } // avoiding of load of full value if (place != null && place.HasAddress && place.Type != null && place.Type.IsValueType) { var conv = DeclaringCompilation.Conversions.ClassifyConversion(place.Type, to, conversion); if (conv.Exists && conv.IsUserDefined && !conv.MethodSymbol.IsStatic) { // (ADDR expr).Method() this.EmitImplicitConversion(EmitCall(ILOpCode.Call, (MethodSymbol)conv.MethodSymbol, expr, ImmutableArray <BoundArgument> .Empty), to, @checked: true); return; } } } // EmitConvert(expr.Emit(this), expr.TypeRefMask, to, conversion); }
private Conversion( ConversionKind kind, UncommonData uncommonData) { _kind = kind; _uncommonData = uncommonData; }
internal Conversion(ConversionKind kind, DeconstructionInfo deconstructionInfo, ImmutableArray <Conversion> nestedConversions) { Debug.Assert(kind == ConversionKind.Deconstruction); this._kind = kind; _uncommonData = new DeconstructionUncommonData(deconstructionInfo, nestedConversions); }
internal void Parse(BoundNullCoalescingOperator boundNullCoalescingOperator) { base.Parse(boundNullCoalescingOperator); this.LeftOperand = Deserialize(boundNullCoalescingOperator.LeftOperand) as Expression; this.RightOperand = Deserialize(boundNullCoalescingOperator.RightOperand) as Expression; this.ConversionKind = boundNullCoalescingOperator.LeftConversion.Kind; }
private static void AssertTrivialConversion(ConversionKind kind) { bool isTrivial; switch (kind) { case ConversionKind.NoConversion: case ConversionKind.Identity: case ConversionKind.ImplicitConstant: case ConversionKind.ImplicitNumeric: case ConversionKind.ImplicitReference: case ConversionKind.ImplicitEnumeration: case ConversionKind.ImplicitThrow: case ConversionKind.AnonymousFunction: case ConversionKind.DefaultOrNullLiteral: case ConversionKind.NullToPointer: case ConversionKind.PointerToVoid: case ConversionKind.PointerToPointer: case ConversionKind.PointerToInteger: case ConversionKind.IntegerToPointer: case ConversionKind.ExplicitReference: case ConversionKind.IntPtr: case ConversionKind.ExplicitEnumeration: case ConversionKind.ExplicitNumeric: case ConversionKind.InterpolatedString: isTrivial = true; break; default: isTrivial = false; break; } Debug.Assert(isTrivial, "this conversion needs additional data: " + kind); }
private static bool IsEncompassingImplicitConversionKind(ConversionKind kind) { switch (kind) { // Doesn't even exist. case ConversionKind.NoConversion: // These are conversions from expression and do not apply. // Specifically disallowed because there would be subtle consequences for the overload betterness rules. case ConversionKind.ImplicitDynamic: case ConversionKind.MethodGroup: case ConversionKind.AnonymousFunction: case ConversionKind.InterpolatedString: case ConversionKind.SwitchExpression: case ConversionKind.ImplicitEnumeration: case ConversionKind.StackAllocToPointerType: case ConversionKind.StackAllocToSpanType: // Not "standard". case ConversionKind.ImplicitUserDefined: case ConversionKind.ExplicitUserDefined: // Not implicit. case ConversionKind.ExplicitNumeric: case ConversionKind.ExplicitEnumeration: case ConversionKind.ExplicitNullable: case ConversionKind.ExplicitReference: case ConversionKind.Unboxing: case ConversionKind.ExplicitDynamic: case ConversionKind.PointerToPointer: case ConversionKind.PointerToInteger: case ConversionKind.IntegerToPointer: case ConversionKind.IntPtr: case ConversionKind.ExplicitTupleLiteral: case ConversionKind.ExplicitTuple: return(false); // Spec'd in C# 4. case ConversionKind.Identity: case ConversionKind.ImplicitNumeric: case ConversionKind.ImplicitNullable: case ConversionKind.ImplicitReference: case ConversionKind.Boxing: case ConversionKind.ImplicitConstant: case ConversionKind.PointerToVoid: // Added to spec in Roslyn timeframe. case ConversionKind.DefaultOrNullLiteral: // updated to include "default" in C# 7.1 case ConversionKind.NullToPointer: // Added for C# 7. case ConversionKind.ImplicitTupleLiteral: case ConversionKind.ImplicitTuple: case ConversionKind.ImplicitThrow: return(true); default: throw ExceptionUtilities.UnexpectedValue(kind); } }
internal Conversion(UserDefinedConversionResult conversionResult, bool isImplicit) : this() { this.Kind = conversionResult.Kind == UserDefinedConversionResultKind.NoApplicableOperators ? ConversionKind.NoConversion : isImplicit?ConversionKind.ImplicitUserDefined : ConversionKind.ExplicitUserDefined; this.conversionResult = conversionResult; }
private static bool IsEncompassingImplicitConversionKind(ConversionKind kind) { switch (kind) { // Doesn't even exist. case ConversionKind.NoConversion: // Specifically disallowed because there would be subtle // consequences for the overload betterness rules. case ConversionKind.MethodGroup: case ConversionKind.AnonymousFunction: case ConversionKind.ImplicitDynamic: case ConversionKind.InterpolatedString: // DELIBERATE SPEC VIOLATION: // We do not support an encompassing implicit conversion from a zero constant // to an enum type, because the native compiler did not. It would be a breaking // change. case ConversionKind.ImplicitEnumeration: // Not built in. case ConversionKind.ImplicitUserDefined: case ConversionKind.ExplicitUserDefined: // Not implicit. case ConversionKind.ExplicitNumeric: case ConversionKind.ExplicitEnumeration: case ConversionKind.ExplicitNullable: case ConversionKind.ExplicitReference: case ConversionKind.Unboxing: case ConversionKind.ExplicitDynamic: case ConversionKind.PointerToPointer: case ConversionKind.PointerToInteger: case ConversionKind.IntegerToPointer: case ConversionKind.IntPtr: return(false); // Spec'd in C# 4. case ConversionKind.Identity: case ConversionKind.ImplicitNumeric: case ConversionKind.ImplicitNullable: case ConversionKind.ImplicitReference: case ConversionKind.Boxing: case ConversionKind.ImplicitConstant: case ConversionKind.PointerToVoid: // Added to spec in Roslyn timeframe. case ConversionKind.NullLiteral: case ConversionKind.NullToPointer: // Added for C# 7. case ConversionKind.ImplicitTuple: return(true); default: throw ExceptionUtilities.UnexpectedValue(kind); } }
// Is the particular conversion an implicit conversion? public static bool IsImplicitConversion(this ConversionKind conversionKind) { switch (conversionKind) { case NoConversion: case UnsetConversionKind: return(false); case Identity: case ImplicitNumeric: case ImplicitTupleLiteral: case ImplicitTuple: case ImplicitEnumeration: case ImplicitThrow: case ImplicitNullable: case NullLiteral: case DefaultLiteral: case ImplicitReference: case Boxing: case ImplicitDynamic: case ImplicitConstant: case ImplicitUtf8StringLiteral: case ImplicitUserDefined: case AnonymousFunction: case ConversionKind.MethodGroup: case ConversionKind.FunctionType: case ImplicitPointerToVoid: case ImplicitNullToPointer: case InterpolatedString: case InterpolatedStringHandler: case SwitchExpression: case ConditionalExpression: case Deconstruction: case StackAllocToPointerType: case StackAllocToSpanType: case ImplicitPointer: case ObjectCreation: return(true); case ExplicitNumeric: case ExplicitTuple: case ExplicitTupleLiteral: case ExplicitEnumeration: case ExplicitNullable: case ExplicitReference: case Unboxing: case ExplicitDynamic: case ExplicitUserDefined: case ExplicitPointerToPointer: case ExplicitPointerToInteger: case ExplicitIntegerToPointer: case IntPtr: return(false); default: throw ExceptionUtilities.UnexpectedValue(conversionKind); } }
internal CommonConversion(bool exists, bool isIdentity, bool isNumeric, bool isReference, bool isImplicit, IMethodSymbol methodSymbol) { _conversionKind = (exists ? ConversionKind.Exists : ConversionKind.None) | (isIdentity ? ConversionKind.IsIdentity : ConversionKind.None) | (isNumeric ? ConversionKind.IsNumeric : ConversionKind.None) | (isReference ? ConversionKind.IsReference : ConversionKind.None) | (isImplicit ? ConversionKind.IsImplicit : ConversionKind.None); MethodSymbol = methodSymbol; }
public Conv(ILInstruction argument, PrimitiveType targetType, bool checkForOverflow, Sign inputSign) : base(OpCode.Conv, argument) { this.TargetType = targetType; this.CheckForOverflow = checkForOverflow; this.InputSign = inputSign; Debug.Assert((inputSign != Sign.None) == (checkForOverflow || targetType == PrimitiveType.R4 || targetType == PrimitiveType.R8)); this.Kind = GetConversionKind(targetType, argument.ResultType, inputSign); Debug.Assert(this.Kind != ConversionKind.Invalid); }
// Is the particular conversion an implicit conversion? public static bool IsImplicitConversion(this ConversionKind conversionKind) { switch (conversionKind) { case ConversionKind.NoConversion: return(false); case ConversionKind.Identity: case ConversionKind.ImplicitNumeric: case ConversionKind.ImplicitTupleLiteral: case ConversionKind.ImplicitTuple: case ConversionKind.ImplicitEnumeration: case ConversionKind.ImplicitThrow: case ConversionKind.ImplicitNullable: case ConversionKind.DefaultOrNullLiteral: case ConversionKind.ImplicitReference: case ConversionKind.Boxing: case ConversionKind.ImplicitDynamic: case ConversionKind.ImplicitConstant: case ConversionKind.ImplicitUserDefined: case ConversionKind.AnonymousFunction: case ConversionKind.MethodGroup: case ConversionKind.PointerToVoid: case ConversionKind.NullToPointer: case ConversionKind.InterpolatedString: case ConversionKind.Deconstruction: case ConversionKind.StackAllocToPointerType: case ConversionKind.StackAllocToSpanType: #if XSHARP case ConversionKind.IntegerToPointer: case ConversionKind.IntPtr: #endif return(true); case ConversionKind.ExplicitNumeric: case ConversionKind.ExplicitTuple: case ConversionKind.ExplicitTupleLiteral: case ConversionKind.ExplicitEnumeration: case ConversionKind.ExplicitNullable: case ConversionKind.ExplicitReference: case ConversionKind.Unboxing: case ConversionKind.ExplicitDynamic: case ConversionKind.ExplicitUserDefined: case ConversionKind.PointerToPointer: case ConversionKind.PointerToInteger: #if !XSHARP case ConversionKind.IntegerToPointer: case ConversionKind.IntPtr: #endif return(false); default: throw ExceptionUtilities.UnexpectedValue(conversionKind); } }
internal Conversion(ConversionKind kind, ImmutableArray <Conversion> nestedConversions) { this._kind = kind; _uncommonData = new UncommonData( isExtensionMethod: false, isArrayIndex: false, conversionResult: default(UserDefinedConversionResult), conversionMethod: null, nestedConversions: nestedConversions); }
// For the method group, lambda and anonymous method conversions internal Conversion(ConversionKind kind, MethodSymbol methodGroupConversionMethod, bool isExtensionMethod) : this() { this.Kind = kind; this.methodGroupConversionMethod = methodGroupConversionMethod; if (isExtensionMethod) { this.flags = IsExtensionMethodMask; } }
// For the method group, lambda and anonymous method conversions internal Conversion(ConversionKind kind, MethodSymbol conversionMethod, bool isExtensionMethod) { this._kind = kind; _uncommonData = new UncommonData( isExtensionMethod: isExtensionMethod, isArrayIndex: false, conversionResult: default(UserDefinedConversionResult), conversionMethod: conversionMethod, nestedConversions: default(ImmutableArray <Conversion>)); }
public override ILInstruction UnwrapConv(ConversionKind kind) { if (this.Kind == kind) { return(Argument.UnwrapConv(kind)); } else { return(this); } }
internal CommonConversion(bool exists, bool isIdentity, bool isNumeric, bool isReference, bool isImplicit, bool isNullable, IMethodSymbol?methodSymbol, ITypeSymbol?constrainedToType) { _conversionKind = (exists ? ConversionKind.Exists : ConversionKind.None) | (isIdentity ? ConversionKind.IsIdentity : ConversionKind.None) | (isNumeric ? ConversionKind.IsNumeric : ConversionKind.None) | (isReference ? ConversionKind.IsReference : ConversionKind.None) | (isImplicit ? ConversionKind.IsImplicit : ConversionKind.None) | (isNullable ? ConversionKind.IsNullable : ConversionKind.None); MethodSymbol = methodSymbol; ConstrainedToType = constrainedToType; }
internal Conversion(UserDefinedConversionResult conversionResult, bool isImplicit) { _kind = conversionResult.Kind == UserDefinedConversionResultKind.NoApplicableOperators ? ConversionKind.NoConversion : isImplicit ? ConversionKind.ImplicitUserDefined : ConversionKind.ExplicitUserDefined; _uncommonData = new UncommonData( isExtensionMethod: false, isArrayIndex: false, conversionResult: conversionResult, conversionMethod: null, nestedConversions: default);
public OptionalValue <Series <K, R> > TryAs <R>(ConversionKind conversionKind) { OptionalValue <IVector <R> > optionalValue1 = VectorHelpers.tryConvertType <R>(conversionKind, (IVector)this.vector); FSharpFunc <IVector <R>, Series <K, R> > fsharpFunc = (FSharpFunc <IVector <R>, Series <K, R> >) new \u0024Series.TryAs\u00401255 <K, R> (this); OptionalValue <IVector <R> > optionalValue2 = optionalValue1; if (optionalValue2.HasValue) { return(new OptionalValue <Series <K, R> >(fsharpFunc.Invoke(optionalValue2.Value))); } return(OptionalValue <Series <K, R> > .Missing); }
public OptionalValue <R> TryGetAs <R>(K column, ConversionKind conversionKind) { var optionalValue1 = this.TryGet(column); Func <object, R> fsharpFunc = (FSharpFunc <object, R>) new \u0024Series.TryGetAs\u00401248\u002D1 <R> (conversionKind); var optionalValue2 = optionalValue1; if (optionalValue2.HasValue) { return(new OptionalValue <R>(fsharpFunc.Invoke(optionalValue2.Value))); } return(OptionalValue <R> .Missing); }
// Is the particular conversion a used-defined conversion? public static bool IsUserDefinedConversion(this ConversionKind conversionKind) { switch (conversionKind) { case ImplicitUserDefined: case ExplicitUserDefined: return(true); default: return(false); } }
private Conversion(ConversionKind kind, bool isExtensionMethod, bool isArrayIndex, UserDefinedConversionResult conversionResult, MethodSymbol methodGroupConversionMethod) { this.Kind = kind; this.conversionResult = conversionResult; this.methodGroupConversionMethod = methodGroupConversionMethod; this.flags = isExtensionMethod ? IsExtensionMethodMask : (byte)0; if (isArrayIndex) { flags |= IsArrayIndexMask; } }
public Conv(ILInstruction argument, StackType inputType, Sign inputSign, PrimitiveType targetType, bool checkForOverflow, bool isLifted = false) : base(OpCode.Conv, argument) { bool needsSign = checkForOverflow || targetType == PrimitiveType.R4 || targetType == PrimitiveType.R8; Debug.Assert(!(needsSign && inputSign == Sign.None)); this.InputType = inputType; this.InputSign = needsSign ? inputSign : Sign.None; this.TargetType = targetType; this.CheckForOverflow = checkForOverflow; this.Kind = GetConversionKind(targetType, this.InputType, this.InputSign); this.IsLifted = isLifted; }
internal void Parse(BoundConversion boundConversion) { base.Parse(boundConversion); this.TypeSource = boundConversion.Operand.Type; this.Operand = Deserialize(boundConversion.Operand) as Expression; this.ConversionKind = boundConversion.ConversionKind; ////var castExpression = boundConversion.Syntax.Green as CastExpressionSyntax; ////if (castExpression != null) ////{ //// // TODO: finish it ////} }
public Conv(ILInstruction argument, StackType inputType, Sign inputSign, PrimitiveType targetType, bool checkForOverflow, bool isLifted = false) : base(OpCode.Conv, argument) { bool needsSign = checkForOverflow || (!inputType.IsFloatType() && (targetType == PrimitiveType.R4 || targetType == PrimitiveType.R8)); Debug.Assert(!(needsSign && inputSign == Sign.None)); this.InputSign = needsSign ? inputSign : Sign.None; this.InputType = inputType; this.TargetType = targetType; this.CheckForOverflow = checkForOverflow; this.Kind = GetConversionKind(targetType, this.InputType, this.InputSign); // Debug.Assert(Kind != ConversionKind.Invalid); // invalid conversion can happen with invalid IL/missing references this.IsLifted = isLifted; }
public static bool IsPointerConversion(this ConversionKind kind) { switch (kind) { case ConversionKind.PointerToVoid: case ConversionKind.PointerToPointer: case ConversionKind.PointerToInteger: case ConversionKind.IntegerToPointer: case ConversionKind.NullToPointer: return(true); default: return(false); } }
private void TestClassifyConversionBuiltInNumeric(string from, string to, ConversionKind ck) { const string template = @" class C {{ static void Foo({1} v) {{ }} static void Main() {{ {0} v = default({0}); Foo({2}v); }} }} "; var isExplicitConversion = ck == ConversionKind.ExplicitNumeric; var source = string.Format(template, from, to, isExplicitConversion ? "(" + to + ")" : ""); var tree = Parse(source); var comp = CreateCompilationWithMscorlib(tree); comp.VerifyDiagnostics(); var model = comp.GetSemanticModel(tree); var c = (TypeDeclarationSyntax)tree.GetCompilationUnitRoot().Members[0]; var main = (MethodDeclarationSyntax)c.Members[1]; var call = (InvocationExpressionSyntax)((ExpressionStatementSyntax)main.Body.Statements[1]).Expression; var arg = call.ArgumentList.Arguments[0].Expression; if (isExplicitConversion) { ConversionTestHelper(model, ((CastExpressionSyntax)arg).Expression, model.GetTypeInfo(arg).ConvertedType, ck); } else { ConversionTestHelper(model, arg, ck, ck); } }
public BoundExpression Convert(TypeSymbol type, BoundExpression arg, ConversionKind conversionKind, bool isChecked = false) { return new BoundConversion( Syntax, arg, conversionKind, LookupResultKind.Viable, isBaseConversion: false, symbolOpt: null, @checked: isChecked, explicitCastInCode: true, isExtensionMethod: false, isArrayIndex: false, constantValueOpt: null, type: type) { WasCompilerGenerated = true }; }
public void ClassifyConversionBuiltInNumeric() { const ConversionKind ID = ConversionKind.Identity; const ConversionKind IN = ConversionKind.ImplicitNumeric; const ConversionKind XN = ConversionKind.ExplicitNumeric; var types = new[] { "sbyte", "byte", "short", "ushort", "int", "uint", "long", "ulong", "char", "float", "double", "decimal" }; var conversions = new ConversionKind[,] { // to sb b s us i ui l ul c f d m // from /* sb */ { ID, XN, IN, XN, IN, XN, IN, XN, XN, IN, IN, IN }, /* b */ { XN, ID, IN, IN, IN, IN, IN, IN, XN, IN, IN, IN }, /* s */ { XN, XN, ID, XN, IN, XN, IN, XN, XN, IN, IN, IN }, /* us */ { XN, XN, XN, ID, IN, IN, IN, IN, XN, IN, IN, IN }, /* i */ { XN, XN, XN, XN, ID, XN, IN, XN, XN, IN, IN, IN }, /* ui */ { XN, XN, XN, XN, XN, ID, IN, IN, XN, IN, IN, IN }, /* l */ { XN, XN, XN, XN, XN, XN, ID, XN, XN, IN, IN, IN }, /* ul */ { XN, XN, XN, XN, XN, XN, XN, ID, XN, IN, IN, IN }, /* c */ { XN, XN, XN, IN, IN, IN, IN, IN, ID, IN, IN, IN }, /* f */ { XN, XN, XN, XN, XN, XN, XN, XN, XN, ID, IN, XN }, /* d */ { XN, XN, XN, XN, XN, XN, XN, XN, XN, XN, ID, XN }, /* m */ { XN, XN, XN, XN, XN, XN, XN, XN, XN, XN, XN, ID } }; for (var from = 0; from < types.Length; from++) { for (var to = 0; to < types.Length; to++) { TestClassifyConversionBuiltInNumeric(types[from], types[to], conversions[from, to]); } } }
internal static ConstantValue GetAsOperatorConstantResult(TypeSymbol operandType, TypeSymbol targetType, ConversionKind conversionKind, ConstantValue operandConstantValue) { // NOTE: Even though BoundIsOperator and BoundAsOperator will always have no ConstantValue // NOTE: (they are non-constant expressions according to Section 7.19 of the specification), // NOTE: we want to perform constant analysis of is/as expressions during binding to generate warnings (always true/false/null) // NOTE: and during rewriting for optimized codegen. ConstantValue isOperatorConstantResult = GetIsOperatorConstantResult(operandType, targetType, conversionKind, operandConstantValue); if (isOperatorConstantResult != null && !isOperatorConstantResult.BooleanValue) { return ConstantValue.Null; } return null; }
private BoundExpression RewriteNullableConversion( CSharpSyntaxNode syntax, BoundExpression rewrittenOperand, ConversionKind conversionKind, bool @checked, bool explicitCastInCode, TypeSymbol rewrittenType) { Debug.Assert((object)rewrittenType != null); if (inExpressionLambda) { return RewriteLiftedConversionInExpressionTree(syntax, rewrittenOperand, conversionKind, @checked, explicitCastInCode, rewrittenType); } TypeSymbol rewrittenOperandType = rewrittenOperand.Type; Debug.Assert(rewrittenType.IsNullableType() || rewrittenOperandType.IsNullableType()); if (rewrittenOperandType.IsNullableType() && rewrittenType.IsNullableType()) { return RewriteFullyLiftedBuiltInConversion(syntax, rewrittenOperand, conversionKind, @checked, rewrittenType); } else if (rewrittenType.IsNullableType()) { // SPEC: If the nullable conversion is from S to T?, the conversion is // SPEC: evaluated as the underlying conversion from S to T followed // SPEC: by a wrapping from T to T?. BoundExpression rewrittenConversion = MakeConversion(rewrittenOperand, rewrittenType.GetNullableUnderlyingType(), @checked); MethodSymbol ctor = GetNullableMethod(syntax, rewrittenType, SpecialMember.System_Nullable_T__ctor); return new BoundObjectCreationExpression(syntax, ctor, rewrittenConversion); } else { // SPEC: if the nullable conversion is from S? to T, the conversion is // SPEC: evaluated as an unwrapping from S? to S followed by the underlying // SPEC: conversion from S to T. // We can do a simple optimization here if we know that the source is never null: BoundExpression nonNullValue = NullableAlwaysHasValue(rewrittenOperand); if (nonNullValue != null) { return MakeConversion(nonNullValue, rewrittenType, @checked); } // (If the source is known to be null then we need to keep the call to get Value // in place so that it throws at runtime.) MethodSymbol get_Value = GetNullableMethod(syntax, rewrittenOperandType, SpecialMember.System_Nullable_T_get_Value); return MakeConversion(BoundCall.Synthesized(syntax, rewrittenOperand, get_Value), rewrittenType, @checked); } }
internal static ConstantValue GetAsOperatorConstantResult(TypeSymbol operandType, TypeSymbol targetType, ConversionKind conversionKind, ConstantValue operandConstantValue) { // NOTE: Even though BoundIsOperator and BoundAsOperator will always have no ConstantValue // NOTE: (they are non-constant expressions according to Section 7.19 of the specification), // NOTE: we want to perform constant analysis of is/as expressions during binding to generate warnings (always true/false/null) // NOTE: and during rewriting for optimized codegen. // Native compiler port: // // check for case we know is always false // if (arg->isNull() || !canCast(arg, type2, NOUDC) && type1->IsValType() && type2->isClassType() && (!type1->IsTypeParameterType() || !type2->isPredefType(PT_ENUM))) // { // GetErrorContext()->Error(tree, WRN_AlwaysNull, type2); // return rval; // } if (operandConstantValue == ConstantValue.Null || (conversionKind == ConversionKind.NoConversion && (operandType.IsValueType && targetType.IsClassType() && (!operandType.IsTypeParameter() || targetType.SpecialType != SpecialType.System_Enum)))) { return ConstantValue.Null; } else { return null; } }
private BoundExpression RewriteFullyLiftedBuiltInConversion( CSharpSyntaxNode syntax, BoundExpression operand, ConversionKind kind, bool @checked, TypeSymbol type) { // SPEC: If the nullable conversion is from S? to T?: // SPEC: * If the source HasValue property is false the result // SPEC: is a null value of type T?. // SPEC: * Otherwise the conversion is evaluated as an unwrapping // SPEC: from S? to S, followed by the underlying conversion from // SPEC: S to T, followed by a wrapping from T to T? BoundExpression optimized = OptimizeLiftedBuiltInConversion(syntax, operand, kind, @checked, type); if (optimized != null) { return optimized; } // We are unable to optimize the conversion. "(T?)s" is generated as: // S? temp = s; // temp.HasValue ? new T?((T)temp.GetValueOrDefault()) : default(T?) BoundAssignmentOperator tempAssignment; var boundTemp = factory.StoreToTemp(operand, out tempAssignment); MethodSymbol get_HasValue = GetNullableMethod(syntax, boundTemp.Type, SpecialMember.System_Nullable_T_get_HasValue); MethodSymbol getValueOrDefault = GetNullableMethod(syntax, boundTemp.Type, SpecialMember.System_Nullable_T_GetValueOrDefault); BoundExpression condition = BoundCall.Synthesized(syntax, boundTemp, get_HasValue); BoundExpression consequence = new BoundObjectCreationExpression( syntax, GetNullableMethod(syntax, type, SpecialMember.System_Nullable_T__ctor), MakeConversion( BoundCall.Synthesized(syntax, boundTemp, getValueOrDefault), type.GetNullableUnderlyingType(), @checked)); BoundExpression alternative = new BoundDefaultOperator(syntax, null, type); BoundExpression conditionalExpression = RewriteConditionalOperator( syntax: syntax, rewrittenCondition: condition, rewrittenConsequence: consequence, rewrittenAlternative: alternative, constantValueOpt: null, rewrittenType: type); return new BoundSequence( syntax: syntax, locals: ImmutableArray.Create(boundTemp.LocalSymbol), sideEffects: ImmutableArray.Create<BoundExpression>(tempAssignment), value: conditionalExpression, type: type); }
/// <summary> /// Helper method to generate a lowered conversion. /// </summary> private BoundExpression MakeConversion( BoundConversion oldNode, CSharpSyntaxNode syntax, BoundExpression rewrittenOperand, ConversionKind conversionKind, MethodSymbol symbolOpt, bool @checked, bool explicitCastInCode, bool isExtensionMethod, bool isArrayIndex, ConstantValue constantValueOpt, TypeSymbol rewrittenType) { Debug.Assert(oldNode == null || oldNode.Syntax == syntax); Debug.Assert((object)rewrittenType != null); @checked = @checked && (inExpressionLambda && (explicitCastInCode || DistinctSpecialTypes(rewrittenOperand.Type, rewrittenType)) || NeedsChecked(rewrittenOperand.Type, rewrittenType)); switch (conversionKind) { case ConversionKind.Identity: // Spec 6.1.1: // An identity conversion converts from any type to the same type. // This conversion exists such that an entity that already has a required type can be said to be convertible to that type. // Because object and dynamic are considered equivalent there is an identity conversion between object and dynamic, // and between constructed types that are the same when replacing all occurrences of dynamic with object. // Why ignoreDynamic: false? // Lowering phase treats object and dynamic as equivalent types. So we don't need to produce any conversion here, // but we need to change the Type property on the resulting BoundExpression to match the rewrittenType. // This is necessary so that subsequent lowering transformations see that the expression is dynamic. if (inExpressionLambda || !rewrittenOperand.Type.Equals(rewrittenType, ignoreCustomModifiers: false, ignoreDynamic: false)) { break; } // 4.1.6 C# spec: To force a value of a floating point type to the exact precision of its type, an explicit cast can be used. // If this is not an identity conversion of a float with unknown precision, strip away the identity conversion. if (!(explicitCastInCode && IsFloatPointExpressionOfUnknownPrecision(rewrittenOperand))) { return rewrittenOperand; } break; case ConversionKind.ExplicitUserDefined: case ConversionKind.ImplicitUserDefined: return RewriteUserDefinedConversion( syntax: syntax, rewrittenOperand: rewrittenOperand, method: symbolOpt, rewrittenType: rewrittenType, conversionKind: conversionKind); case ConversionKind.IntPtr: return RewriteIntPtrConversion(oldNode, syntax, rewrittenOperand, conversionKind, symbolOpt, @checked, explicitCastInCode, isExtensionMethod, isArrayIndex, constantValueOpt, rewrittenType); case ConversionKind.ImplicitNullable: case ConversionKind.ExplicitNullable: return RewriteNullableConversion( syntax: syntax, rewrittenOperand: rewrittenOperand, conversionKind: conversionKind, @checked: @checked, explicitCastInCode: explicitCastInCode, rewrittenType: rewrittenType); case ConversionKind.Boxing: if (!inExpressionLambda) { // We can perform some optimizations if we have a nullable value type // as the operand and we know its nullability: // * (object)new int?() is the same as (object)null // * (object)new int?(123) is the same as (object)123 if (NullableNeverHasValue(rewrittenOperand)) { return new BoundDefaultOperator(syntax, rewrittenType); } BoundExpression nullableValue = NullableAlwaysHasValue(rewrittenOperand); if (nullableValue != null) { // Recurse, eliminating the unnecessary ctor. return MakeConversion(oldNode, syntax, nullableValue, conversionKind, symbolOpt, @checked, explicitCastInCode, isExtensionMethod, isArrayIndex, constantValueOpt, rewrittenType); } } break; case ConversionKind.NullLiteral: if (!inExpressionLambda || !explicitCastInCode) { return new BoundDefaultOperator(syntax, rewrittenType); } break; case ConversionKind.ImplicitReference: case ConversionKind.ExplicitReference: if (rewrittenOperand.IsDefaultValue() && (!inExpressionLambda || !explicitCastInCode)) { return new BoundDefaultOperator(syntax, rewrittenType); } break; case ConversionKind.ImplicitNumeric: case ConversionKind.ExplicitNumeric: if (rewrittenOperand.IsDefaultValue() && (!inExpressionLambda || !explicitCastInCode)) { return new BoundDefaultOperator(syntax, rewrittenType); } if (rewrittenType.SpecialType == SpecialType.System_Decimal || rewrittenOperand.Type.SpecialType == SpecialType.System_Decimal) { return RewriteDecimalConversion(oldNode, syntax, rewrittenOperand, rewrittenOperand.Type, rewrittenType); } break; case ConversionKind.ImplicitEnumeration: // A conversion from constant zero to nullable is actually classified as an // implicit enumeration conversion, not an implicit nullable conversion. // Lower it to (E?)(E)0. if (rewrittenType.IsNullableType()) { var operand = MakeConversion( oldNode, syntax, rewrittenOperand, ConversionKind.ImplicitEnumeration, symbolOpt, @checked, explicitCastInCode, isExtensionMethod, false, constantValueOpt, rewrittenType.GetNullableUnderlyingType()); return MakeConversion( oldNode, syntax, operand, ConversionKind.ImplicitNullable, symbolOpt, @checked, explicitCastInCode, isExtensionMethod, isArrayIndex, constantValueOpt, rewrittenType); } goto case ConversionKind.ExplicitEnumeration; case ConversionKind.ExplicitEnumeration: if (!rewrittenType.IsNullableType() && rewrittenOperand.IsDefaultValue() && (!inExpressionLambda || !explicitCastInCode)) { return new BoundDefaultOperator(syntax, rewrittenType); } if (rewrittenType.SpecialType == SpecialType.System_Decimal) { Debug.Assert(rewrittenOperand.Type.IsEnumType()); var underlyingTypeFrom = rewrittenOperand.Type.GetEnumUnderlyingType(); rewrittenOperand = MakeConversion(rewrittenOperand, underlyingTypeFrom, false); return RewriteDecimalConversion(oldNode, syntax, rewrittenOperand, underlyingTypeFrom, rewrittenType); } else if (rewrittenOperand.Type.SpecialType == SpecialType.System_Decimal) { // This is where we handle conversion from Decimal to Enum: e.g., E e = (E) d; // where 'e' is of type Enum E and 'd' is of type Decimal. // Conversion can be simply done by applying its underlying numeric type to RewriteDecimalConversion(). Debug.Assert(rewrittenType.IsEnumType()); var underlyingTypeTo = rewrittenType.GetEnumUnderlyingType(); var rewrittenNode = RewriteDecimalConversion(oldNode, syntax, rewrittenOperand, rewrittenOperand.Type, underlyingTypeTo); // However, the type of the rewritten node becomes underlying numeric type, not Enum type, // which violates the overall constraint saying the type cannot be changed during rewriting (see LocalRewriter.cs). // Instead of loosening this constraint, we return BoundConversion from underlying numeric type to Enum type, // which will be eliminated during emitting (see EmitEnumConversion): e.g., E e = (E)(int) d; return new BoundConversion( syntax, rewrittenNode, conversionKind, LookupResultKind.Viable, isBaseConversion: false, symbolOpt: symbolOpt, @checked: false, explicitCastInCode: explicitCastInCode, isExtensionMethod: isExtensionMethod, isArrayIndex: false, constantValueOpt: constantValueOpt, type: rewrittenType); } break; case ConversionKind.ImplicitDynamic: case ConversionKind.ExplicitDynamic: Debug.Assert((object)symbolOpt == null); Debug.Assert(!isExtensionMethod); Debug.Assert(constantValueOpt == null); return dynamicFactory.MakeDynamicConversion(rewrittenOperand, explicitCastInCode || conversionKind == ConversionKind.ExplicitDynamic, isArrayIndex, @checked, rewrittenType).ToExpression(); default: break; } return oldNode != null ? oldNode.Update( rewrittenOperand, conversionKind, oldNode.ResultKind, isBaseConversion: oldNode.IsBaseConversion, symbolOpt: symbolOpt, @checked: @checked, explicitCastInCode: explicitCastInCode, isExtensionMethod: isExtensionMethod, isArrayIndex: isArrayIndex, constantValueOpt: constantValueOpt, type: rewrittenType) : new BoundConversion( syntax, rewrittenOperand, conversionKind, LookupResultKind.Viable, isBaseConversion: false, symbolOpt: symbolOpt, @checked: @checked, explicitCastInCode: explicitCastInCode, isExtensionMethod: isExtensionMethod, isArrayIndex: isArrayIndex, constantValueOpt: constantValueOpt, type: rewrittenType); }
private BoundExpression RewriteIntPtrConversion( BoundConversion oldNode, CSharpSyntaxNode syntax, BoundExpression rewrittenOperand, ConversionKind conversionKind, MethodSymbol symbolOpt, bool @checked, bool explicitCastInCode, bool isExtensionMethod, bool isArrayIndex, ConstantValue constantValueOpt, TypeSymbol rewrittenType) { Debug.Assert(rewrittenOperand != null); Debug.Assert((object)rewrittenType != null); TypeSymbol source = rewrittenOperand.Type; TypeSymbol target = rewrittenType; TypeSymbol t0 = target.StrippedType(); TypeSymbol s0 = source.StrippedType(); if (t0 != target && s0 != source) { // UNDONE: RewriteLiftedIntPtrConversion return oldNode != null ? oldNode.Update( rewrittenOperand, conversionKind, oldNode.ResultKind, isBaseConversion: oldNode.IsBaseConversion, symbolOpt: symbolOpt, @checked: @checked, explicitCastInCode: explicitCastInCode, isExtensionMethod: isExtensionMethod, isArrayIndex: isArrayIndex, constantValueOpt: constantValueOpt, type: rewrittenType) : new BoundConversion( syntax, rewrittenOperand, conversionKind, LookupResultKind.Viable, isBaseConversion: false, symbolOpt: symbolOpt, @checked: @checked, explicitCastInCode: explicitCastInCode, isExtensionMethod: isExtensionMethod, isArrayIndex: isArrayIndex, constantValueOpt: constantValueOpt, type: rewrittenType); } SpecialMember member = GetIntPtrConversionMethod(source: rewrittenOperand.Type, target: rewrittenType); MethodSymbol method = GetSpecialTypeMethod(syntax, member); Debug.Assert(!method.ReturnsVoid); Debug.Assert(method.ParameterCount == 1); rewrittenOperand = MakeConversion(rewrittenOperand, method.ParameterTypes[0], @checked); var returnType = method.ReturnType; Debug.Assert((object)returnType != null); if (inExpressionLambda) { return BoundConversion.Synthesized(syntax, rewrittenOperand, new Conversion(conversionKind, method, false), @checked, explicitCastInCode, constantValueOpt, rewrittenType); } var rewrittenCall = inExpressionLambda && oldNode != null ? new BoundConversion(syntax, rewrittenOperand, new Conversion(conversionKind, method, false), @checked, explicitCastInCode, constantValueOpt, returnType) : MakeCall( syntax: syntax, rewrittenReceiver: null, method: method, rewrittenArguments: ImmutableArray.Create<BoundExpression>(rewrittenOperand), type: returnType); return MakeConversion(rewrittenCall, rewrittenType, @checked); }
private BoundExpression RewriteUserDefinedConversion( CSharpSyntaxNode syntax, BoundExpression rewrittenOperand, MethodSymbol method, TypeSymbol rewrittenType, ConversionKind conversionKind) { Debug.Assert((object)method != null && !method.ReturnsVoid && method.ParameterCount == 1); if (rewrittenOperand.Type != method.ParameterTypes[0]) { Debug.Assert(rewrittenOperand.Type.IsNullableType()); Debug.Assert(rewrittenOperand.Type.GetNullableUnderlyingType() == method.ParameterTypes[0]); return RewriteLiftedUserDefinedConversion(syntax, rewrittenOperand, method, rewrittenType, conversionKind); } var conv = new Conversion(ConversionKind.ExplicitUserDefined, method, false); Debug.Assert(conv.IsValid); BoundExpression result = inExpressionLambda ? BoundConversion.Synthesized(syntax, rewrittenOperand, MakeConversion(syntax, conv, rewrittenOperand.Type, rewrittenType), false, true, default(ConstantValue), rewrittenType) : (BoundExpression)BoundCall.Synthesized(syntax, null, method, rewrittenOperand); Debug.Assert(result.Type == rewrittenType); return result; }
private BoundExpression RewriteLiftedUserDefinedConversion( CSharpSyntaxNode syntax, BoundExpression rewrittenOperand, MethodSymbol method, TypeSymbol rewrittenType, ConversionKind conversionKind) { if (inExpressionLambda) { Conversion conv = MakeConversion(syntax, new Conversion(conversionKind, method, false), rewrittenOperand.Type, rewrittenType); return BoundConversion.Synthesized(syntax, rewrittenOperand, conv, false, true, default(ConstantValue), rewrittenType); } // DELIBERATE SPEC VIOLATION: // The native compiler allows for a "lifted" conversion even when the return type of the conversion // not a non-nullable value type. For example, if we have a conversion from struct S to string, // then a "lifted" conversion from S? to string is considered by the native compiler to exist, // with the semantics of "s.HasValue ? (string)s.Value : (string)null". The Roslyn compiler // perpetuates this error for the sake of backwards compatibility. Debug.Assert((object)rewrittenType != null); Debug.Assert(rewrittenOperand.Type.IsNullableType()); BoundExpression optimized = OptimizeLiftedUserDefinedConversion(syntax, rewrittenOperand, conversionKind, method, rewrittenType); if (optimized != null) { return optimized; } // We have no optimizations we can perform. If the return type of the // conversion method is a non-nullable value type R then we lower this as: // // temp = operand // temp.HasValue ? new R?(op_Whatever(temp.GetValueOrDefault())) : default(R?) // // Otherwise, if the return type of the conversion is a nullable value type, reference type // or pointer type P, then we lower this as: // // temp = operand // temp.HasValue ? op_Whatever(temp.GetValueOrDefault()) : default(P) BoundAssignmentOperator tempAssignment; BoundLocal boundTemp = factory.StoreToTemp(rewrittenOperand, out tempAssignment); MethodSymbol get_HasValue = GetNullableMethod(syntax, boundTemp.Type, SpecialMember.System_Nullable_T_get_HasValue); MethodSymbol getValueOrDefault = GetNullableMethod(syntax, boundTemp.Type, SpecialMember.System_Nullable_T_GetValueOrDefault); // temp.HasValue BoundExpression condition = BoundCall.Synthesized(syntax, boundTemp, get_HasValue); // temp.GetValueOrDefault() BoundCall callGetValueOrDefault = BoundCall.Synthesized(syntax, boundTemp, getValueOrDefault); // op_Whatever(temp.GetValueOrDefault()) BoundCall userDefinedCall = BoundCall.Synthesized(syntax, null, method, callGetValueOrDefault); // new R?(op_Whatever(temp.GetValueOrDefault()) BoundExpression consequence = MakeLiftedUserDefinedConversionConsequence(userDefinedCall, rewrittenType); // default(R?) BoundExpression alternative = new BoundDefaultOperator(syntax, rewrittenType); // temp.HasValue ? new R?(op_Whatever(temp.GetValueOrDefault())) : default(R?) BoundExpression conditionalExpression = RewriteConditionalOperator( syntax: syntax, rewrittenCondition: condition, rewrittenConsequence: consequence, rewrittenAlternative: alternative, constantValueOpt: null, rewrittenType: rewrittenType); // temp = operand // temp.HasValue ? new R?(op_Whatever(temp.GetValueOrDefault())) : default(R?) return new BoundSequence( syntax: syntax, locals: ImmutableArray.Create(boundTemp.LocalSymbol), sideEffects: ImmutableArray.Create<BoundExpression>(tempAssignment), value: conditionalExpression, type: rewrittenType); }
private BoundExpression DistributeLiftedConversionIntoLiftedOperand( CSharpSyntaxNode syntax, BoundExpression operand, ConversionKind kind, bool @checked, MethodSymbol method, TypeSymbol type) { // Third, an even trickier optimization. Suppose we have a lifted conversion on top of // a lifted operation. Say, "decimal? d = M() + N()" where M() and N() return nullable ints. // We can codegen this naively as: // // int? m = M(); // int? n = N(); // int? r = m.HasValue && n.HasValue ? new int?(m.Value + n.Value) : new int?(); // decimal? d = r.HasValue ? new decimal?((decimal)r.Value) : new decimal?(); // // However, we also observe that we could do the conversion on both branches of the conditional: // // int? m = M(); // int? n = N(); // decimal? d = m.HasValue && n.HasValue ? (decimal?)(new int?(m.Value + n.Value)) : (decimal?)(new int?()); // // And we already optimize those, above! So we could reduce this to: // // int? m = M(); // int? n = N(); // decimal? d = m.HasValue && n.HasValue ? new decimal?((decimal)(m.Value + n.Value)) : new decimal?()); // // which avoids entirely the creation of the unnecessary nullable int! if (operand.Kind == BoundKind.Sequence) { BoundSequence seq = (BoundSequence)operand; if (seq.Value.Kind == BoundKind.ConditionalOperator) { BoundConditionalOperator conditional = (BoundConditionalOperator)seq.Value; Debug.Assert(seq.Type == conditional.Type); Debug.Assert(conditional.Type == conditional.Consequence.Type); Debug.Assert(conditional.Type == conditional.Alternative.Type); if (NullableAlwaysHasValue(conditional.Consequence) != null && NullableNeverHasValue(conditional.Alternative)) { return new BoundSequence( seq.Syntax, seq.Locals, seq.SideEffects, RewriteConditionalOperator( conditional.Syntax, conditional.Condition, MakeConversion(null, syntax, conditional.Consequence, kind, method, @checked, false, false, false, ConstantValue.NotAvailable, type), MakeConversion(null, syntax, conditional.Alternative, kind, method, @checked, false, false, false, ConstantValue.NotAvailable, type), ConstantValue.NotAvailable, type), type); } } } return null; }
private BoundExpression OptimizeLiftedBuiltInConversion( CSharpSyntaxNode syntax, BoundExpression operand, ConversionKind kind, bool @checked, TypeSymbol type) { Debug.Assert(operand != null); Debug.Assert((object)type != null); // First, an optimization. If the source is known to always be null then // we can simply return the alternative. if (NullableNeverHasValue(operand)) { return new BoundDefaultOperator(syntax, null, type); } // Second, a trickier optimization. If the conversion is "(T?)(new S?(x))" then // we generate "new T?((T)x)" BoundExpression nonNullValue = NullableAlwaysHasValue(operand); if (nonNullValue != null) { return new BoundObjectCreationExpression( syntax, GetNullableMethod(syntax, type, SpecialMember.System_Nullable_T__ctor), MakeConversion( nonNullValue, type.GetNullableUnderlyingType(), @checked)); } // Third, a very tricky optimization. return DistributeLiftedConversionIntoLiftedOperand(syntax, operand, kind, @checked, null, type); }
private BoundExpression OptimizeLiftedUserDefinedConversion( CSharpSyntaxNode syntax, BoundExpression operand, ConversionKind kind, MethodSymbol method, TypeSymbol type) { // We begin with some optimizations: if the converted expression is known to always be null // then we can skip the whole thing and simply return the alternative: if (NullableNeverHasValue(operand)) { return new BoundDefaultOperator(syntax, type); } // If the converted expression is known to never be null then we can return // new R?(op_Whatever(nonNullableValue)) BoundExpression nonNullValue = NullableAlwaysHasValue(operand); if (nonNullValue != null) { return MakeLiftedUserDefinedConversionConsequence(BoundCall.Synthesized(syntax, null, method, nonNullValue), type); } return DistributeLiftedConversionIntoLiftedOperand(syntax, operand, kind, false, method, type); }
private BoundExpression RewriteTupleConversion( CSharpSyntaxNode syntax, BoundExpression rewrittenOperand, ConversionKind conversionKind, bool @checked, bool explicitCastInCode, NamedTypeSymbol rewrittenType) { var destElementTypes = rewrittenType.GetElementTypesOfTupleOrCompatible(); var numElements = destElementTypes.Length; ImmutableArray<FieldSymbol> srcElementFields; TypeSymbol srcType = rewrittenOperand.Type; if (srcType.IsTupleType) { srcElementFields = ((TupleTypeSymbol)srcType).TupleElementFields; } else { // The following codepath should be very uncommon (if reachable at all) // we should generally not see tuple compatible types in bound trees and // see actual tuple types instead. Debug.Assert(srcType.IsTupleCompatible()); // PERF: if allocations here become nuisance, consider caching the TupleTypeSymbol // in the type symbols that can actually be tuple compatible srcElementFields = TupleTypeSymbol.Create((NamedTypeSymbol)srcType).TupleElementFields; } var fieldAccessorsBuilder = ArrayBuilder<BoundExpression>.GetInstance(numElements); BoundAssignmentOperator assignmentToTemp; var savedTuple = _factory.StoreToTemp(rewrittenOperand, out assignmentToTemp); for (int i = 0; i < numElements; i++) { var field = srcElementFields[i]; DiagnosticInfo useSiteInfo = field.GetUseSiteDiagnostic(); if ((object)useSiteInfo != null && useSiteInfo.Severity == DiagnosticSeverity.Error) { Symbol.ReportUseSiteDiagnostic(useSiteInfo, _diagnostics, syntax.Location); } var fieldAccess = MakeTupleFieldAccess(syntax, field, savedTuple, null, LookupResultKind.Empty); var convertedFieldAccess = MakeConversion(fieldAccess, destElementTypes[i], @checked); fieldAccessorsBuilder.Add(convertedFieldAccess); } var result = MakeTupleCreationExpression(syntax, rewrittenType, fieldAccessorsBuilder.ToImmutableAndFree()); return _factory.Sequence(savedTuple.LocalSymbol, assignmentToTemp, result); }
public BoundExpression Convert(TypeSymbol type, BoundExpression arg, ConversionKind conversionKind, bool isChecked = false) { return new BoundConversion(Syntax, arg, conversionKind, null, isChecked, true, false, false, null, LookupResultKind.Viable, type) { WasCompilerGenerated = true }; }
private BoundExpression RewriteIntPtrConversion( BoundConversion oldNode, CSharpSyntaxNode syntax, BoundExpression rewrittenOperand, ConversionKind conversionKind, MethodSymbol symbolOpt, bool @checked, bool explicitCastInCode, bool isExtensionMethod, bool isArrayIndex, ConstantValue constantValueOpt, TypeSymbol rewrittenType) { Debug.Assert(rewrittenOperand != null); Debug.Assert((object)rewrittenType != null); TypeSymbol source = rewrittenOperand.Type; TypeSymbol target = rewrittenType; SpecialMember member = GetIntPtrConversionMethod(source: rewrittenOperand.Type, target: rewrittenType); MethodSymbol method = GetSpecialTypeMethod(syntax, member); Debug.Assert(!method.ReturnsVoid); Debug.Assert(method.ParameterCount == 1); if (source.IsNullableType() && target.IsNullableType()) { Debug.Assert(target.IsNullableType()); return RewriteLiftedUserDefinedConversion(syntax, rewrittenOperand, method, rewrittenType, ConversionKind.IntPtr); } else if (source.IsNullableType()) { rewrittenOperand = MakeConversion(rewrittenOperand, source.StrippedType(), @checked); } rewrittenOperand = MakeConversion(rewrittenOperand, method.ParameterTypes[0], @checked); var returnType = method.ReturnType; Debug.Assert((object)returnType != null); if (_inExpressionLambda) { return BoundConversion.Synthesized(syntax, rewrittenOperand, new Conversion(conversionKind, method, false), @checked, explicitCastInCode, constantValueOpt, rewrittenType); } var rewrittenCall = MakeCall( syntax: syntax, rewrittenReceiver: null, method: method, rewrittenArguments: ImmutableArray.Create(rewrittenOperand), type: returnType); return MakeConversion(rewrittenCall, rewrittenType, @checked); }
private static bool ReportAsOperatorConversionDiagnostics( CSharpSyntaxNode node, DiagnosticBag diagnostics, Compilation compilation, TypeSymbol operandType, TypeSymbol targetType, ConversionKind conversionKind, ConstantValue operandConstantValue) { // SPEC: In an operation of the form E as T, E must be an expression and T must be a reference type, // SPEC: a type parameter known to be a reference type, or a nullable type. // SPEC: Furthermore, at least one of the following must be true, or otherwise a compile-time error occurs: // SPEC: • An identity (§6.1.1), implicit nullable (§6.1.4), implicit reference (§6.1.6), boxing (§6.1.7), // SPEC: explicit nullable (§6.2.3), explicit reference (§6.2.4), or unboxing (§6.2.5) conversion exists // SPEC: from E to T. // SPEC: • The type of E or T is an open type. // SPEC: • E is the null literal. // SPEC VIOLATION: The specification contains an error in the list of legal conversions above. // SPEC VIOLATION: If we have "class C<T, U> where T : U where U : class" then there is // SPEC VIOLATION: an implicit conversion from T to U, but it is not an identity, reference or // SPEC VIOLATION: boxing conversion. It will be one of those at runtime, but at compile time // SPEC VIOLATION: we do not know which, and therefore cannot classify it as any of those. // SPEC VIOLATION: See Microsoft.CodeAnalysis.CSharp.UnitTests.SyntaxBinderTests.TestAsOperator_SpecErrorCase() test for an example. // SPEC VIOLATION: The specification also unintentionally allows the case where requirement 2 above: // SPEC VIOLATION: "The type of E or T is an open type" is true, but type of E is void type, i.e. T is an open type. // SPEC VIOLATION: Dev10 compiler correctly generates an error for this case and we will maintain compatibility. bool hasErrors = false; switch (conversionKind) { case ConversionKind.ImplicitReference: case ConversionKind.Boxing: case ConversionKind.ImplicitNullable: case ConversionKind.Identity: case ConversionKind.ExplicitNullable: case ConversionKind.ExplicitReference: case ConversionKind.Unboxing: break; default: // Generate an error if there is no possible legal conversion and both the operandType // and the targetType are closed types OR operandType is void type, otherwise we need a runtime check if (!operandType.ContainsTypeParameter() && !targetType.ContainsTypeParameter() || operandType.SpecialType == SpecialType.System_Void) { SymbolDistinguisher distinguisher = new SymbolDistinguisher(compilation, operandType, targetType); Error(diagnostics, ErrorCode.ERR_NoExplicitBuiltinConv, node, distinguisher.First, distinguisher.Second); hasErrors = true; } break; } if (!hasErrors) { ReportAsOperatorConstantWarnings(node, diagnostics, operandType, targetType, conversionKind, operandConstantValue); } return hasErrors; }
/// <summary> /// /// </summary> /// <param name="binding"></param> /// <param name="expr"></param> /// <param name="ept1">expr -> TypeInParent</param> /// <param name="ept2">Type(expr) -> TypeInParent</param> private void ConversionTestHelper(SemanticModel semanticModel, ExpressionSyntax expr, ConversionKind ept1, ConversionKind ept2) { var info = semanticModel.GetTypeInfo(expr); Assert.NotNull(info); Assert.NotNull(info.ConvertedType); var conv = semanticModel.GetConversion(expr); // NOT expect NoConversion Conversion act1 = semanticModel.ClassifyConversion(expr, (TypeSymbol)info.ConvertedType); Assert.Equal(ept1, act1.Kind); ValidateConversion(act1, ept1); ValidateConversion(act1, conv.Kind); if (ept2 == ConversionKind.NoConversion) { Assert.Null(info.Type); } else { Assert.NotNull(info.Type); var act2 = semanticModel.Compilation.ClassifyConversion(info.Type, info.ConvertedType); Assert.Equal(ept2, act2.Kind); ValidateConversion(act2, ept2); } }
private BoundExpression RewriteLiftedConversionInExpressionTree( CSharpSyntaxNode syntax, BoundExpression rewrittenOperand, ConversionKind conversionKind, bool @checked, bool explicitCastInCode, TypeSymbol rewrittenType) { Debug.Assert((object)rewrittenType != null); TypeSymbol rewrittenOperandType = rewrittenOperand.Type; Debug.Assert(rewrittenType.IsNullableType() || rewrittenOperandType.IsNullableType()); TypeSymbol typeFrom = rewrittenOperandType.StrippedType(); TypeSymbol typeTo = rewrittenType.StrippedType(); if (typeFrom != typeTo && (typeFrom.SpecialType == SpecialType.System_Decimal || typeTo.SpecialType == SpecialType.System_Decimal)) { // take special care if the underlying conversion is a decimal conversion TypeSymbol typeFromUnderlying = typeFrom; TypeSymbol typeToUnderlying = typeTo; // They can't both be enums, since one of them is decimal. if (typeFrom.IsEnumType()) { typeFromUnderlying = typeFrom.GetEnumUnderlyingType(); // NOTE: Dev10 converts enum? to underlying?, rather than directly to underlying. rewrittenOperandType = rewrittenOperandType.IsNullableType() ? ((NamedTypeSymbol)rewrittenOperandType.OriginalDefinition).Construct(typeFromUnderlying) : typeFromUnderlying; rewrittenOperand = BoundConversion.SynthesizedNonUserDefined(syntax, rewrittenOperand, ConversionKind.ImplicitEnumeration, rewrittenOperandType); } else if (typeTo.IsEnumType()) { typeToUnderlying = typeTo.GetEnumUnderlyingType(); } var method = (MethodSymbol)this.compilation.Assembly.GetSpecialTypeMember(DecimalConversionMethod(typeFromUnderlying, typeToUnderlying)); conversionKind = conversionKind.IsImplicitConversion() ? ConversionKind.ImplicitUserDefined : ConversionKind.ExplicitUserDefined; var result = new BoundConversion(syntax, rewrittenOperand, new Conversion(conversionKind, method, false), @checked, explicitCastInCode, default(ConstantValue), rewrittenType); return result; } else { return new BoundConversion(syntax, rewrittenOperand, new Conversion(conversionKind), @checked, explicitCastInCode, default(ConstantValue), rewrittenType); } }
internal static ConstantValue GetIsOperatorConstantResult(TypeSymbol operandType, TypeSymbol targetType, ConversionKind conversionKind, ConstantValue operandConstantValue) { Debug.Assert((object)targetType != null); // SPEC: The result of the operation depends on D and T as follows: // SPEC: 1) If T is a reference type, the result is true if D and T are the same type, if D is a reference type and // SPEC: an implicit reference conversion from D to T exists, or if D is a value type and a boxing conversion from D to T exists. // SPEC: 2) If T is a nullable type, the result is true if D is the underlying type of T. // SPEC: 3) If T is a non-nullable value type, the result is true if D and T are the same type. // SPEC: 4) Otherwise, the result is false. // NOTE: The language specification talks about the runtime evaluation of the is operation. // NOTE: However, we are interested in computing the compile time constant value for the expression. // NOTE: Even though BoundIsOperator and BoundAsOperator will always have no ConstantValue // NOTE: (they are non-constant expressions according to Section 7.19 of the specification), // NOTE: we want to perform constant analysis of is/as expressions during binding to generate warnings (always true/false/null) // NOTE: and during rewriting for optimized codegen. // NOTE: // NOTE: Because the heuristic presented here is used to change codegen, it must be conservative. It is acceptable // NOTE: for us to fail to report a warning in cases where humans could logically deduce that the operator will // NOTE: always return false. It is not acceptable to inaccurately warn that the operator will always return false // NOTE: if there are cases where it might succeed. // // To begin our heuristic: if the operand is literal null then we automatically return that the // result is false. You might think that we can simply check to see if the conversion is // ConversionKind.NullConversion, but "null is T" for a type parameter T is actually classified // as an implicit reference conversion if T is constrained to reference types. Rather // than deal with all those special cases we can simply bail out here. if (operandConstantValue == ConstantValue.Null) { return ConstantValue.False; } Debug.Assert((object)operandType != null); switch (conversionKind) { case ConversionKind.NoConversion: // Oddly enough, "x is T" can be true even if there is no conversion from x to T! // // Scenario 1: Type parameter compared to System.Enum. // // bool M1<X>(X x) where X : struct { return x is Enum; } // // There is no conversion from X to Enum, not even an explicit conversion. But // nevertheless, X could be constructed as an enumerated type. // However, we can sometimes know that the result will be false. // // Scenario 2: Constrained type parameter compared to reference type. // // bool M2<X>(X x) where X : struct { return x is string; } // // We know that X, constrained to struct, will never be string. // // Scenario 3: Value type compared to type parameter. // // bool M3<T>(int x) { return x is T; } // // There is no conversion from int to T, but T could nevertheless be int. // // Scenario 4: Constructed type compared to open type // // bool M4<T>(C<int> x) { return x is C<T>; } // // There is no conversion from C<int> to C<T>, but nevertheless, T might be int. // // Scenario 5: Open type compared to constructed type: // // bool M5<X>(C<X> x) { return x is C<int>); // // Again, X could be int. // // We could then go on to get more complicated. For example, // // bool M6<X>(C<X> x) where X : struct { return x is C<string>; } // // We know that C<X> is never convertible to C<string> no matter what // X is. Or: // // bool M7<T>(Dictionary<int, int> x) { return x is List<T>; } // // We know that no matter what T is, the conversion will never succeed. // // As noted above, we must be conservative. We follow the lead of the native compiler, // which uses the following algorithm: // // * If neither type is open and there is no conversion then the result is always false: if (!operandType.ContainsTypeParameter() && !targetType.ContainsTypeParameter()) { return ConstantValue.False; } // * Otherwise, at least one of them is of an open type. If the operand is of value type // and the target is a class type other than System.Enum, then we are in scenario 2, // not scenario 1, and can correctly deduce that the result is false. if (operandType.IsValueType && targetType.IsClassType() && targetType.SpecialType != SpecialType.System_Enum) { return ConstantValue.False; } // * Otherwise, we give up. Though there are other situations in which we can deduce that // the result will always be false, such as scenarios 6 and 7, but we do not attempt // to deduce this. // CONSIDER: we could use TypeUnification.CanUnify to do additional compile-time checking. return null; case ConversionKind.ImplicitNumeric: case ConversionKind.ExplicitNumeric: case ConversionKind.ImplicitEnumeration: // case ConversionKind.ExplicitEnumeration: // Handled separately below. case ConversionKind.ImplicitConstant: case ConversionKind.ImplicitUserDefined: case ConversionKind.ExplicitUserDefined: case ConversionKind.IntPtr: // Consider all the cases where we know that "x is T" must be false just from // the conversion classification. // // If we have "x is T" and the conversion from x to T is numeric or enum then the result must be false. // // If we have "null is T" then obviously that must be false. // // If we have "1 is long" then that must be false. (If we have "1 is int" then it is an identity conversion, // not an implicit constant conversion. // // User-defined and IntPtr conversions are always false for "is". return ConstantValue.False; case ConversionKind.ExplicitEnumeration: // Enum-to-enum conversions should be treated the same as unsuccessful struct-to-struct // conversions (i.e. make allowances for type unification, etc) if (operandType.IsEnumType() && targetType.IsEnumType()) { goto case ConversionKind.NoConversion; } return ConstantValue.False; case ConversionKind.ExplicitNullable: // An explicit nullable conversion is a conversion of one of the following forms: // // 1) X? --> Y?, where X --> Y is an explicit conversion. (If X --> Y is an implicit // conversion then X? --> Y? is an implicit nullable conversion.) In this case we // know that "X? is Y?" must be false because either X? is null, or we have an // explicit conversion from struct type X to struct type Y, and so X is never of type Y.) // // 2) X --> Y?, where again, X --> Y is an explicit conversion. By the same reasoning // as in case 1, this must be false. if (targetType.IsNullableType()) { return ConstantValue.False; } Debug.Assert(operandType.IsNullableType()); // 3) X? --> X. In this case, this is just a different way of writing "x != null". // We do not know what the result will be. // CONSIDER: If we know statically that the operand is going to be null or non-null // CONSIDER: then we could give a better result here. if (Conversions.HasIdentityConversion(operandType.GetNullableUnderlyingType(), targetType)) { return null; } // 4) X? --> Y where the conversion X --> Y is an implicit or explicit value type conversion. // "X? is Y" again must be false. return ConstantValue.False; case ConversionKind.ImplicitReference: case ConversionKind.ExplicitReference: case ConversionKind.Unboxing: // In these three cases, the expression type must be a reference type. Therefore, // the result cannot be determined. The expression could be null, resulting // in false, or it could be a non-null reference to the appropriate type, // resulting in true. return null; case ConversionKind.Identity: // The result of "x is T" can be statically determined to be true if x is an expression // of non-nullable value type T. If x is of reference or nullable value type then // we cannot know, because again, the expression value could be null or it could be good. // If it is of pointer type then we have already given an error. return (operandType.IsValueType && !operandType.IsNullableType()) ? ConstantValue.True : null; case ConversionKind.Boxing: // A boxing conversion might be a conversion: // // * From a non-nullable value type to a reference type // * From a nullable value type to a reference type // * From a type parameter that *could* be a value type under construction // to a reference type // // In the first case we know that the conversion will always succeed and that the // operand is never null, and therefore "is" will always result in true. // // In the second two cases we do not know; either the nullable value type could be // null, or the type parameter could be constructed with a reference type, and it // could be null. return operandType.IsValueType && !operandType.IsNullableType() ? ConstantValue.True : null; case ConversionKind.ImplicitNullable: // We have "x is T" in one of the following situations: // 1) x is of type X and T is X?. The value is always true. // 2) x is of type X and T is Y? where X is convertible to Y via an implicit numeric conversion. Eg, // x is of type int and T is decimal?. The value is always false. // 3) x is of type X? and T is Y? where X is convertible to Y via an implicit numeric conversion. // The value is always false. Debug.Assert(targetType.IsNullableType()); return (operandType == targetType.GetNullableUnderlyingType()) ? ConstantValue.True : ConstantValue.False; default: case ConversionKind.ImplicitDynamic: case ConversionKind.ExplicitDynamic: case ConversionKind.PointerToInteger: case ConversionKind.PointerToPointer: case ConversionKind.PointerToVoid: case ConversionKind.IntegerToPointer: case ConversionKind.NullToPointer: case ConversionKind.AnonymousFunction: case ConversionKind.NullLiteral: case ConversionKind.MethodGroup: // We've either replaced Dynamic with Object, or already bailed out with an error. throw ExceptionUtilities.UnexpectedValue(conversionKind); } }
private void ValidateConversion(Conversion conv, ConversionKind kind) { Assert.Equal(conv.Kind, kind); switch (kind) { case ConversionKind.NoConversion: Assert.False(conv.Exists); Assert.False(conv.IsImplicit); Assert.False(conv.IsExplicit); break; case ConversionKind.Identity: Assert.True(conv.Exists); Assert.True(conv.IsImplicit); Assert.False(conv.IsExplicit); Assert.True(conv.IsIdentity); break; case ConversionKind.ImplicitNumeric: Assert.True(conv.Exists); Assert.True(conv.IsImplicit); Assert.False(conv.IsExplicit); Assert.True(conv.IsNumeric); break; case ConversionKind.ImplicitEnumeration: Assert.True(conv.Exists); Assert.True(conv.IsImplicit); Assert.False(conv.IsExplicit); Assert.True(conv.IsEnumeration); break; case ConversionKind.ImplicitNullable: Assert.True(conv.Exists); Assert.True(conv.IsImplicit); Assert.False(conv.IsExplicit); Assert.True(conv.IsNullable); break; case ConversionKind.NullLiteral: Assert.True(conv.Exists); Assert.True(conv.IsImplicit); Assert.False(conv.IsExplicit); Assert.True(conv.IsNullLiteral); break; case ConversionKind.ImplicitReference: Assert.True(conv.Exists); Assert.True(conv.IsImplicit); Assert.False(conv.IsExplicit); Assert.True(conv.IsReference); break; case ConversionKind.Boxing: Assert.True(conv.Exists); Assert.True(conv.IsImplicit); Assert.False(conv.IsExplicit); Assert.True(conv.IsBoxing); break; case ConversionKind.ImplicitDynamic: Assert.True(conv.Exists); Assert.True(conv.IsImplicit); Assert.False(conv.IsExplicit); Assert.True(conv.IsDynamic); break; case ConversionKind.ExplicitDynamic: Assert.True(conv.Exists); Assert.True(conv.IsExplicit); Assert.False(conv.IsImplicit); Assert.True(conv.IsDynamic); break; case ConversionKind.ImplicitConstant: Assert.True(conv.Exists); Assert.True(conv.IsImplicit); Assert.False(conv.IsExplicit); Assert.True(conv.IsConstantExpression); break; case ConversionKind.ImplicitUserDefined: Assert.True(conv.Exists); Assert.True(conv.IsImplicit); Assert.False(conv.IsExplicit); Assert.True(conv.IsUserDefined); break; case ConversionKind.AnonymousFunction: Assert.True(conv.Exists); Assert.True(conv.IsImplicit); Assert.False(conv.IsExplicit); Assert.True(conv.IsAnonymousFunction); break; case ConversionKind.MethodGroup: Assert.True(conv.Exists); Assert.True(conv.IsImplicit); Assert.False(conv.IsExplicit); Assert.True(conv.IsMethodGroup); break; case ConversionKind.ExplicitNumeric: Assert.True(conv.Exists); Assert.False(conv.IsImplicit); Assert.True(conv.IsExplicit); Assert.True(conv.IsNumeric); break; case ConversionKind.ExplicitEnumeration: Assert.True(conv.Exists); Assert.False(conv.IsImplicit); Assert.True(conv.IsExplicit); Assert.True(conv.IsEnumeration); break; case ConversionKind.ExplicitNullable: Assert.True(conv.Exists); Assert.False(conv.IsImplicit); Assert.True(conv.IsExplicit); Assert.True(conv.IsNullable); break; case ConversionKind.ExplicitReference: Assert.True(conv.Exists); Assert.False(conv.IsImplicit); Assert.True(conv.IsExplicit); Assert.True(conv.IsReference); break; case ConversionKind.Unboxing: Assert.True(conv.Exists); Assert.False(conv.IsImplicit); Assert.True(conv.IsExplicit); Assert.True(conv.IsUnboxing); break; case ConversionKind.ExplicitUserDefined: Assert.True(conv.Exists); Assert.False(conv.IsImplicit); Assert.True(conv.IsExplicit); Assert.True(conv.IsUserDefined); break; case ConversionKind.NullToPointer: Assert.True(conv.Exists); Assert.True(conv.IsImplicit); Assert.False(conv.IsExplicit); Assert.False(conv.IsUserDefined); Assert.True(conv.IsPointer); break; case ConversionKind.PointerToVoid: Assert.True(conv.Exists); Assert.True(conv.IsImplicit); Assert.False(conv.IsExplicit); Assert.False(conv.IsUserDefined); Assert.True(conv.IsPointer); break; case ConversionKind.PointerToPointer: Assert.True(conv.Exists); Assert.False(conv.IsImplicit); Assert.True(conv.IsExplicit); Assert.False(conv.IsUserDefined); Assert.True(conv.IsPointer); break; case ConversionKind.IntegerToPointer: Assert.True(conv.Exists); Assert.False(conv.IsImplicit); Assert.True(conv.IsExplicit); Assert.False(conv.IsUserDefined); Assert.True(conv.IsPointer); break; case ConversionKind.PointerToInteger: Assert.True(conv.Exists); Assert.False(conv.IsImplicit); Assert.True(conv.IsExplicit); Assert.False(conv.IsUserDefined); Assert.True(conv.IsPointer); break; case ConversionKind.IntPtr: Assert.True(conv.Exists); Assert.False(conv.IsImplicit); Assert.True(conv.IsExplicit); Assert.False(conv.IsUserDefined); Assert.False(conv.IsPointer); Assert.True(conv.IsIntPtr); break; } }
private static void ReportAsOperatorConstantWarnings( CSharpSyntaxNode node, DiagnosticBag diagnostics, TypeSymbol operandType, TypeSymbol targetType, ConversionKind conversionKind, ConstantValue operandConstantValue) { // NOTE: Even though BoundIsOperator and BoundAsOperator will always have no ConstantValue // NOTE: (they are non-constant expressions according to Section 7.19 of the specification), // NOTE: we want to perform constant analysis of is/as expressions to generate warnings if the // NOTE: expression will always be true/false/null. ConstantValue constantValue = GetAsOperatorConstantResult(operandType, targetType, conversionKind, operandConstantValue); if (constantValue != null) { Debug.Assert(constantValue.IsNull); Error(diagnostics, ErrorCode.WRN_AlwaysNull, node, targetType); } }
/// <remarks> /// This method is intended for passes other than the LocalRewriter. /// Use MakeConversion helper method in the LocalRewriter instead, /// it generates a synthesized conversion in its lowered form. /// </remarks> public static BoundConversion SynthesizedNonUserDefined(CSharpSyntaxNode syntax, BoundExpression operand, ConversionKind kind, TypeSymbol type, ConstantValue constantValueOpt = null) { // We need more information than just the conversion kind for creating a synthesized user defined conversion. Debug.Assert(!kind.IsUserDefinedConversion(), "Use the BoundConversion.Synthesized overload that takes a 'Conversion' parameter for generating synthesized user defined conversions."); return new BoundConversion( syntax, operand, kind, resultKind: LookupResultKind.Viable, //not used isBaseConversion: false, symbolOpt: null, @checked: false, explicitCastInCode: false, isExtensionMethod: false, isArrayIndex: false, constantValueOpt: constantValueOpt, type: type) { WasCompilerGenerated = true }; }
/// <summary> /// Helper method to generate a lowered conversion from the given rewrittenOperand to the given rewrittenType with the given conversion kind. /// </summary> /// <remarks> /// Conversion kind must not be a user defined conversion, use the other overload which takes a 'Conversion' parameter for generating synthesized user defined conversions. /// </remarks> private BoundExpression MakeConversion( CSharpSyntaxNode syntax, BoundExpression rewrittenOperand, ConversionKind conversionKind, TypeSymbol rewrittenType, bool @checked, ConstantValue constantValueOpt = null) { // We need more information than just the conversion kind for creating a synthesized user defined conversion. Debug.Assert(!conversionKind.IsUserDefinedConversion(), "Use the other overload which takes a 'Conversion' parameter for generating synthesized user defined conversions."); return MakeConversion(syntax, rewrittenOperand, new Conversion(conversionKind), rewrittenType, @checked, explicitCastInCode: false, constantValueOpt: constantValueOpt); }
private void ConversionTestHelper(SemanticModel semanticModel, ExpressionSyntax expr, ITypeSymbol expsym, ConversionKind expkind) { var info = semanticModel.GetTypeInfo(expr); Assert.NotNull(info); Assert.NotNull(info.ConvertedType); // NOT expect NoConversion Conversion act1 = semanticModel.ClassifyConversion(expr, expsym); Assert.Equal(expkind, act1.Kind); ValidateConversion(act1, expkind); }