private Expr GenerateUserDefinedConversion(ExprUserDefinedConversion pExpr, Expr pArgument) { Expr pCastCall = pExpr.UserDefinedCall; Expr pCastArgument = pExpr.Argument; Expr pConversionSource; if (!isEnumToDecimalConversion(pArgument.Type, pExpr.Type) && IsNullableValueAccess(pCastArgument, pArgument)) { // We have an implicit conversion of nullable CType to the value CType, generate a convert node for it. pConversionSource = GenerateValueAccessConversion(pArgument); } else { ExprCall call = pCastCall as ExprCall; Expr pUDConversion = call?.PConversions; if (pUDConversion != null) { if (pUDConversion is ExprCall convCall) { Expr pUDConversionArgument = convCall.OptionalArguments; if (IsNullableValueAccess(pUDConversionArgument, pArgument)) { pConversionSource = GenerateValueAccessConversion(pArgument); } else { pConversionSource = Visit(pUDConversionArgument); } return(GenerateConversionWithSource(pConversionSource, pCastCall.Type, call.isChecked())); } // This can happen if we have a UD conversion from C to, say, int, // and we have an explicit cast to decimal?. The conversion should // then be bound as two chained user-defined conversions. Debug.Assert(pUDConversion is ExprUserDefinedConversion); // Just recurse. return(GenerateUserDefinedConversion((ExprUserDefinedConversion)pUDConversion, pArgument)); } pConversionSource = Visit(pCastArgument); } return(GenerateUserDefinedConversion(pCastArgument, pExpr.Type, pConversionSource, pExpr.UserDefinedCallMethod)); }
protected override Expr VisitCALL(ExprCall expr) { Debug.Assert(expr != null); switch (expr.NullableCallLiftKind) { default: break; case NullableCallLiftKind.NullableIntermediateConversion: case NullableCallLiftKind.NullableConversion: case NullableCallLiftKind.NullableConversionConstructor: return(GenerateConversion(expr.OptionalArguments, expr.Type, expr.isChecked())); case NullableCallLiftKind.NotLiftedIntermediateConversion: case NullableCallLiftKind.UserDefinedConversion: return(GenerateUserDefinedConversion(expr.OptionalArguments, expr.Type, expr.MethWithInst)); } if (expr.MethWithInst.Meth().IsConstructor()) { return(GenerateConstructor(expr)); } ExprMemberGroup memberGroup = expr.MemberGroup; if (memberGroup.IsDelegate) { return(GenerateDelegateInvoke(expr)); } Expr pObject; if (expr.MethWithInst.Meth().isStatic || expr.MemberGroup.OptionalObject == null) { pObject = GetExprFactory().CreateNull(); } else { pObject = expr.MemberGroup.OptionalObject; // If we have, say, an int? which is the object of a call to ToString // then we do NOT want to generate ((object)i).ToString() because that // will convert a null-valued int? to a null object. Rather what we want // to do is box it to a ValueType and call ValueType.ToString. // // To implement this we say that if the object of the call is an implicit boxing cast // then just generate the object, not the cast. If the cast is explicit in the // source code then it will be an EXPLICITCAST and we will visit it normally. // // It might be better to rewrite the expression tree API so that it // can handle in the general case all implicit boxing conversions. Right now it // requires that all arguments to a call that need to be boxed be explicitly boxed. if (pObject != null && pObject is ExprCast cast && cast.IsBoxingCast) { pObject = cast.Argument; } pObject = Visit(pObject); } Expr methodInfo = GetExprFactory().CreateMethodInfo(expr.MethWithInst); Expr args = GenerateArgsList(expr.OptionalArguments); Expr Params = GenerateParamsArray(args, PredefinedType.PT_EXPRESSION); PREDEFMETH pdm = PREDEFMETH.PM_EXPRESSION_CALL; Debug.Assert(!expr.MethWithInst.Meth().isVirtual || expr.MemberGroup.OptionalObject != null); return(GenerateCall(pdm, pObject, methodInfo, Params)); }