public ExprUserLogicalOp CreateUserLogOpError(CType pType, Expr pCallTF, ExprCall pCallOp) { ExprUserLogicalOp rval = CreateUserLogOp(pType, pCallTF, pCallOp); rval.SetError(); return(rval); }
///////////////////////////////////////////////////////////////////////////////// // Expression types. private ExprBinOp VisitBoundLambda(ExprBoundLambda anonmeth) { Debug.Assert(anonmeth != null); MethodSymbol lambdaMethod = GetPreDefMethod(PREDEFMETH.PM_EXPRESSION_LAMBDA); AggregateType delegateType = anonmeth.DelegateType; TypeArray lambdaTypeParams = GetSymbolLoader().getBSymmgr().AllocParams(1, new CType[] { delegateType }); AggregateType expressionType = GetSymbolLoader().GetPredefindType(PredefinedType.PT_EXPRESSION); MethWithInst mwi = new MethWithInst(lambdaMethod, expressionType, lambdaTypeParams); Expr createParameters = CreateWraps(anonmeth); Debug.Assert(createParameters != null); Debug.Assert(anonmeth.Expression != null); Expr body = Visit(anonmeth.Expression); Debug.Assert(anonmeth.ArgumentScope.nextChild == null); Expr parameters = GenerateParamsArray(null, PredefinedType.PT_PARAMETEREXPRESSION); Expr args = GetExprFactory().CreateList(body, parameters); CType typeRet = GetSymbolLoader().GetTypeManager().SubstType(mwi.Meth().RetType, mwi.GetType(), mwi.TypeArgs); ExprMemberGroup pMemGroup = GetExprFactory().CreateMemGroup(null, mwi); ExprCall call = GetExprFactory().CreateCall(0, typeRet, args, pMemGroup, mwi); call.PredefinedMethod = PREDEFMETH.PM_EXPRESSION_LAMBDA; return(GetExprFactory().CreateSequence(createParameters, call)); }
public ExprUserLogicalOp CreateUserLogOp(CType pType, Expr pCallTF, ExprCall pCallOp) { Debug.Assert(pCallTF != null); Debug.Assert(pCallOp != null); Debug.Assert(pCallOp.OptionalArguments != null); Debug.Assert(pCallOp.OptionalArguments.isLIST()); Debug.Assert(pCallOp.OptionalArguments.asLIST().OptionalElement != null); ExprUserLogicalOp rval = new ExprUserLogicalOp(); Expr leftChild = pCallOp.OptionalArguments.asLIST().OptionalElement; Debug.Assert(leftChild != null); if (leftChild.isWRAP()) { // In the EE case, we don't create WRAPEXPRs. leftChild = leftChild.asWRAP().OptionalExpression; Debug.Assert(leftChild != null); } rval.Kind = ExpressionKind.EK_USERLOGOP; rval.Type = pType; rval.Flags = EXPRFLAG.EXF_ASSGOP; rval.TrueFalseCall = pCallTF; rval.OperatorCall = pCallOp; rval.FirstOperandToExamine = leftChild; Debug.Assert(rval != null); return(rval); }
private Expr GenerateConstructor(ExprCall expr) { Debug.Assert(expr != null); Debug.Assert(expr.MethWithInst.Meth().IsConstructor()); Expr constructorInfo = GetExprFactory().CreateMethodInfo(expr.MethWithInst); Expr args = GenerateArgsList(expr.OptionalArguments); Expr Params = GenerateParamsArray(args, PredefinedType.PT_EXPRESSION); return(GenerateCall(PREDEFMETH.PM_EXPRESSION_NEW, constructorInfo, Params)); }
private Expr GenerateUserDefinedUnaryOperator(ExprUnaryOp expr) { Debug.Assert(expr != null); PREDEFMETH pdm; Expr arg = expr.Child; ExprCall call = (ExprCall)expr.OptionalUserDefinedCall; if (call != null) { // Use the actual argument of the call; it may contain user-defined // conversions or be a bound lambda, and that will not be in the original // argument stashed away in the left child of the operator. arg = call.OptionalArguments; } Debug.Assert(arg != null && arg.Kind != ExpressionKind.List); switch (expr.Kind) { case ExpressionKind.True: case ExpressionKind.False: return(Visit(call)); case ExpressionKind.UnaryPlus: pdm = PREDEFMETH.PM_EXPRESSION_UNARYPLUS_USER_DEFINED; break; case ExpressionKind.BitwiseNot: pdm = PREDEFMETH.PM_EXPRESSION_NOT_USER_DEFINED; break; case ExpressionKind.LogicalNot: pdm = PREDEFMETH.PM_EXPRESSION_NOT_USER_DEFINED; break; case ExpressionKind.DecimalNegate: case ExpressionKind.Negate: pdm = expr.isChecked() ? PREDEFMETH.PM_EXPRESSION_NEGATECHECKED_USER_DEFINED : PREDEFMETH.PM_EXPRESSION_NEGATE_USER_DEFINED; break; case ExpressionKind.Inc: case ExpressionKind.Dec: case ExpressionKind.DecimalInc: case ExpressionKind.DecimalDec: pdm = PREDEFMETH.PM_EXPRESSION_CALL; break; default: throw Error.InternalCompilerError(); } Expr op = Visit(arg); Expr methodInfo = GetExprFactory().CreateMethodInfo(expr.UserDefinedCallMethod); if (expr.Kind == ExpressionKind.Inc || expr.Kind == ExpressionKind.Dec || expr.Kind == ExpressionKind.DecimalInc || expr.Kind == ExpressionKind.DecimalDec) { return(GenerateCall(pdm, null, methodInfo, GenerateParamsArray(op, PredefinedType.PT_EXPRESSION))); } return(GenerateCall(pdm, op, methodInfo)); }
public ExprUserLogicalOp(CType type, Expr trueFalseCall, ExprCall operatorCall) : base(ExpressionKind.UserLogicalOp, type) { Debug.Assert(trueFalseCall != null); Debug.Assert((operatorCall?.OptionalArguments as ExprList)?.OptionalElement != null); Flags = EXPRFLAG.EXF_ASSGOP; TrueFalseCall = trueFalseCall; OperatorCall = operatorCall; Expr leftChild = ((ExprList)operatorCall.OptionalArguments).OptionalElement; // In the EE case, we don't create WRAPEXPRs. FirstOperandToExamine = leftChild is ExprWrap wrap ? wrap.OptionalExpression : leftChild; Debug.Assert(FirstOperandToExamine != null); }
private static bool IsNullableConstructor(Expr expr, out ExprCall call) { Debug.Assert(expr != null); if (expr is ExprCall pCall && pCall.MemberGroup.OptionalObject == null && (pCall.MethWithInst?.Meth().IsNullableConstructor() ?? false)) { call = pCall; return(true); } call = null; return(false); }
private Expr GenerateUserDefinedComparisonOperator(ExprBinOp expr) { Debug.Assert(expr != null); PREDEFMETH pdm; switch (expr.Kind) { case ExpressionKind.StringEq: pdm = PREDEFMETH.PM_EXPRESSION_EQUAL_USER_DEFINED; break; case ExpressionKind.StringNotEq: pdm = PREDEFMETH.PM_EXPRESSION_NOTEQUAL_USER_DEFINED; break; case ExpressionKind.DelegateEq: pdm = PREDEFMETH.PM_EXPRESSION_EQUAL_USER_DEFINED; break; case ExpressionKind.DelegateNotEq: pdm = PREDEFMETH.PM_EXPRESSION_NOTEQUAL_USER_DEFINED; break; case ExpressionKind.Eq: pdm = PREDEFMETH.PM_EXPRESSION_EQUAL_USER_DEFINED; break; case ExpressionKind.NotEq: pdm = PREDEFMETH.PM_EXPRESSION_NOTEQUAL_USER_DEFINED; break; case ExpressionKind.LessThanOrEqual: pdm = PREDEFMETH.PM_EXPRESSION_LESSTHANOREQUAL_USER_DEFINED; break; case ExpressionKind.LessThan: pdm = PREDEFMETH.PM_EXPRESSION_LESSTHAN_USER_DEFINED; break; case ExpressionKind.GreaterThanOrEqual: pdm = PREDEFMETH.PM_EXPRESSION_GREATERTHANOREQUAL_USER_DEFINED; break; case ExpressionKind.GreaterThan: pdm = PREDEFMETH.PM_EXPRESSION_GREATERTHAN_USER_DEFINED; break; default: throw Error.InternalCompilerError(); } Expr p1 = expr.OptionalLeftChild; Expr p2 = expr.OptionalRightChild; if (expr.OptionalUserDefinedCall != null) { ExprCall udcall = (ExprCall)expr.OptionalUserDefinedCall; ExprList args = (ExprList)udcall.OptionalArguments; Debug.Assert(args.OptionalNextListNode.Kind != ExpressionKind.List); p1 = args.OptionalElement; p2 = args.OptionalNextListNode; } p1 = Visit(p1); p2 = Visit(p2); FixLiftedUserDefinedBinaryOperators(expr, ref p1, ref p2); Expr lift = GetExprFactory().CreateBoolConstant(false); // We never lift to null in C#. Expr methodInfo = GetExprFactory().CreateMethodInfo(expr.UserDefinedCallMethod); return(GenerateCall(pdm, p1, p2, lift, methodInfo)); }
private Expr GenerateDelegateInvoke(ExprCall expr) { Debug.Assert(expr != null); ExprMemberGroup memberGroup = expr.MemberGroup; Debug.Assert(memberGroup.IsDelegate); Expr oldObject = memberGroup.OptionalObject; Debug.Assert(oldObject != null); Expr pObject = Visit(oldObject); Expr args = GenerateArgsList(expr.OptionalArguments); Expr Params = GenerateParamsArray(args, PredefinedType.PT_EXPRESSION); return(GenerateCall(PREDEFMETH.PM_EXPRESSION_INVOKE, pObject, Params)); }
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)); }
private ExprCall GenerateCall(PREDEFMETH pdm, Expr arg1, Expr arg2, Expr arg3, Expr arg4) { MethodSymbol method = GetPreDefMethod(pdm); if (method == null) { return(null); } AggregateType expressionType = GetSymbolLoader().GetPredefindType(PredefinedType.PT_EXPRESSION); Expr args = GetExprFactory().CreateList(arg1, arg2, arg3, arg4); MethWithInst mwi = new MethWithInst(method, expressionType); ExprMemberGroup pMemGroup = GetExprFactory().CreateMemGroup(null, mwi); ExprCall call = GetExprFactory().CreateCall(0, mwi.Meth().RetType, args, pMemGroup, mwi); call.PredefinedMethod = pdm; return(call); }
private static bool IsNullableConstructor(Expr expr, out ExprCall call) { Debug.Assert(expr != null); if (expr is ExprCall pCall && pCall.MemberGroup.OptionalObject == null) { MethodSymbol meth = pCall.MethWithInst.Meth(); if (meth != null && meth.IsNullableConstructor()) { call = pCall; return(true); } } call = null; return(false); }
private ExprCall GenerateCall(PREDEFMETH pdm, Expr arg1) { MethodSymbol method = GetPreDefMethod(pdm); // this should be enforced in an earlier pass and the transform pass should not // be handling this error if (method == null) { return(null); } AggregateType expressionType = GetSymbolLoader().GetPredefindType(PredefinedType.PT_EXPRESSION); MethWithInst mwi = new MethWithInst(method, expressionType); ExprMemberGroup pMemGroup = GetExprFactory().CreateMemGroup(null, mwi); ExprCall call = GetExprFactory().CreateCall(0, mwi.Meth().RetType, arg1, pMemGroup, mwi); call.PredefinedMethod = pdm; return(call); }
public ExprCall CreateCall(EXPRFLAG nFlags, CType pType, Expr pOptionalArguments, ExprMemberGroup pMemberGroup, MethWithInst MWI) { Debug.Assert(0 == (nFlags & ~( EXPRFLAG.EXF_NEWOBJCALL | EXPRFLAG.EXF_CONSTRAINED | EXPRFLAG.EXF_BASECALL | EXPRFLAG.EXF_NEWSTRUCTASSG | EXPRFLAG.EXF_IMPLICITSTRUCTASSG | EXPRFLAG.EXF_MASK_ANY ) )); ExprCall rval = new ExprCall(pType); rval.Flags = nFlags; rval.OptionalArguments = pOptionalArguments; rval.MemberGroup = pMemberGroup; rval.NullableCallLiftKind = NullableCallLiftKind.NotLifted; rval.MethWithInst = MWI; return(rval); }
public ExprUserLogicalOp CreateUserLogOp(CType pType, Expr pCallTF, ExprCall pCallOp) { Debug.Assert(pCallTF != null); Debug.Assert((pCallOp?.OptionalArguments as ExprList)?.OptionalElement != null); ExprUserLogicalOp rval = new ExprUserLogicalOp(pType); Expr leftChild = ((ExprList)pCallOp.OptionalArguments).OptionalElement; Debug.Assert(leftChild != null); if (leftChild is ExprWrap wrap) { // In the EE case, we don't create WRAPEXPRs. leftChild = wrap.OptionalExpression; Debug.Assert(leftChild != null); } rval.Flags = EXPRFLAG.EXF_ASSGOP; rval.TrueFalseCall = pCallTF; rval.OperatorCall = pCallOp; rval.FirstOperandToExamine = leftChild; return(rval); }
private static bool IsNullableConstructor(Expr expr) { Debug.Assert(expr != null); if (!expr.isCALL()) { return(false); } ExprCall pCall = expr.asCALL(); if (pCall.MemberGroup.OptionalObject != null) { return(false); } MethodSymbol meth = pCall.MethWithInst.Meth(); if (meth == null) { return(false); } return(meth.IsNullableConstructor()); }
public ExprCall BindNew(Expr pExprSrc) { Debug.Assert(pExprSrc != null); NullableType pNubSourceType = GetSymbolLoader().GetTypeManager().GetNullable(pExprSrc.Type); AggregateType pSourceType = pNubSourceType.GetAts(GetErrorContext()); if (pSourceType == null) { MethWithInst mwi = new MethWithInst(null, null); ExprMemberGroup pMemGroup = GetExprFactory().CreateMemGroup(pExprSrc, mwi); ExprCall rval = GetExprFactory().CreateCall(0, pNubSourceType, null, pMemGroup, null); rval.SetError(); return(rval); } MethodSymbol meth = GetSymbolLoader().getBSymmgr().methNubCtor; if (meth == null) { meth = GetSymbolLoader().getPredefinedMembers().GetMethod(PREDEFMETH.PM_G_OPTIONAL_CTOR); GetSymbolLoader().getBSymmgr().methNubCtor = meth; } MethWithInst methwithinst = new MethWithInst(meth, pSourceType, BSYMMGR.EmptyTypeArray()); ExprMemberGroup memgroup = GetExprFactory().CreateMemGroup(null, methwithinst); ExprCall pExprRes = GetExprFactory().CreateCall(EXPRFLAG.EXF_NEWOBJCALL | EXPRFLAG.EXF_CANTBENULL, pNubSourceType, pExprSrc, memgroup, methwithinst); if (meth == null) { pExprRes.SetError(); } return(pExprRes); }
public static ExprUserLogicalOp CreateUserLogOp(CType type, Expr trueFalseCall, ExprCall operatorCall) => new ExprUserLogicalOp(type, trueFalseCall, operatorCall);
public ExprUnaryOp CreateUserDefinedUnaryOperator(ExpressionKind exprKind, CType pType, Expr pOperand, ExprCall call, MethPropWithInst pmpwi) { Debug.Assert(pType != null); Debug.Assert(pOperand != null); Debug.Assert(call != null); Debug.Assert(pmpwi != null); ExprUnaryOp rval = new ExprUnaryOp(exprKind, pType); rval.Child = pOperand; // The call may be lifted, but we do not mark the outer binop as lifted. rval.OptionalUserDefinedCall = call; rval.UserDefinedCallMethod = pmpwi; if (call.HasError) { rval.SetError(); } return(rval); }
public ExprUserLogicalOp CreateUserLogOpError(CType type, Expr trueFalseCall, ExprCall operatorCall) { ExprUserLogicalOp rval = CreateUserLogOp(type, trueFalseCall, operatorCall); rval.SetError(); return(rval); }
// The call may be lifted, but we do not mark the outer binop as lifted. public static ExprUnaryOp CreateUserDefinedUnaryOperator(ExpressionKind exprKind, CType type, Expr operand, ExprCall call, MethPropWithInst userMethod) => new ExprUnaryOp(exprKind, type, operand, call, userMethod);
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)); }
protected virtual Expr VisitCALL(ExprCall pExpr) { return(VisitEXPR(pExpr)); }
/*************************************************************************************************** * Called by BindImplicitConversion when the destination type is Nullable<T>. The following * conversions are handled by this method: * * For S in { object, ValueType, interfaces implemented by underlying type} there is an explicit * unboxing conversion S => T? * System.Enum => T? there is an unboxing conversion if T is an enum type * null => T? implemented as default(T?) * * Implicit T?* => T?+ implemented by either wrapping or calling GetValueOrDefault the * appropriate number of times. * If imp/exp S => T then imp/exp S => T?+ implemented by converting to T then wrapping the * appropriate number of times. * If imp/exp S => T then imp/exp S?+ => T?+ implemented by calling GetValueOrDefault (m-1) times * then calling HasValue, producing a null if it returns false, otherwise calling Value, * converting to T then wrapping the appropriate number of times. * * The 3 rules above can be summarized with the following recursive rules: * * If imp/exp S => T? then imp/exp S? => T? implemented as * qs.HasValue ? (T?)(qs.Value) : default(T?) * If imp/exp S => T then imp/exp S => T? implemented as new T?((T)s) * * This method also handles calling bindUserDefinedConverion. This method does NOT handle * the following conversions: * * Implicit boxing conversion from S? to { object, ValueType, Enum, ifaces implemented by S }. (Handled by BindImplicitConversion.) * If imp/exp S => T then explicit S?+ => T implemented by calling Value the appropriate number * of times. (Handled by BindExplicitConversion.) * * The recursive equivalent is: * * If imp/exp S => T and T is not nullable then explicit S? => T implemented as qs.Value * * Some nullable conversion are NOT standard conversions. In particular, if S => T is implicit * then S? => T is not standard. Similarly if S => T is not implicit then S => T? is not standard. ***************************************************************************************************/ private bool BindNubConversion(NullableType nubDst) { // This code assumes that STANDARD and ISEXPLICIT are never both set. // bindUserDefinedConversion should ensure this! Debug.Assert(0 != (~_flags & (CONVERTTYPE.STANDARD | CONVERTTYPE.ISEXPLICIT))); Debug.Assert(_exprSrc == null || _exprSrc.Type == _typeSrc); Debug.Assert(!_needsExprDest || _exprSrc != null); Debug.Assert(_typeSrc != nubDst); // BindImplicitConversion should have taken care of this already. AggregateType atsDst = nubDst.GetAts(); // Check for the unboxing conversion. This takes precedence over the wrapping conversions. if (GetSymbolLoader().HasBaseConversion(nubDst.GetUnderlyingType(), _typeSrc) && !CConversions.FWrappingConv(_typeSrc, nubDst)) { // These should be different! Fix the caller if typeSrc is an AggregateType of Nullable. Debug.Assert(atsDst != _typeSrc); // typeSrc is a base type of the destination nullable type so there is an explicit // unboxing conversion. if (0 == (_flags & CONVERTTYPE.ISEXPLICIT)) { return(false); } if (_needsExprDest) { _binder.bindSimpleCast(_exprSrc, _typeDest, out _exprDest, EXPRFLAG.EXF_UNBOX); } return(true); } bool dstWasNullable; bool srcWasNullable; CType typeDstBase = nubDst.StripNubs(out dstWasNullable); CType typeSrcBase = _typeSrc.StripNubs(out srcWasNullable); ConversionFunc pfn = (_flags & CONVERTTYPE.ISEXPLICIT) != 0 ? (ConversionFunc)_binder.BindExplicitConversion : (ConversionFunc)_binder.BindImplicitConversion; if (!srcWasNullable) { Debug.Assert(_typeSrc == typeSrcBase); // The null type can be implicitly converted to T? as the default value. if (_typeSrc is NullType) { // If we have the constant null, generate it as a default value of T?. If we have // some crazy expression which has been determined to be always null, like (null??null) // keep it in its expression form and transform it in the nullable rewrite pass. if (_needsExprDest) { if (_exprSrc.isCONSTANT_OK()) { _exprDest = GetExprFactory().CreateZeroInit(nubDst); } else { _exprDest = GetExprFactory().CreateCast(_typeDest, _exprSrc); } } return(true); } Expr exprTmp = _exprSrc; // If there is an implicit/explicit S => T then there is an implicit/explicit S => T? if (_typeSrc == typeDstBase || pfn(_exprSrc, _typeSrc, typeDstBase, _needsExprDest, out exprTmp, _flags | CONVERTTYPE.NOUDC)) { if (_needsExprDest) { ExprUserDefinedConversion exprUDC = exprTmp as ExprUserDefinedConversion; if (exprUDC != null) { exprTmp = exprUDC.UserDefinedCall; } if (dstWasNullable) { ExprCall call = _binder.BindNubNew(exprTmp); exprTmp = call; call.NullableCallLiftKind = NullableCallLiftKind.NullableConversionConstructor; } if (exprUDC != null) { exprUDC.UserDefinedCall = exprTmp; exprTmp = exprUDC; } Debug.Assert(exprTmp.Type == nubDst); _exprDest = exprTmp; } return(true); } // No builtin conversion. Maybe there is a user defined conversion.... return(0 == (_flags & CONVERTTYPE.NOUDC) && _binder.bindUserDefinedConversion(_exprSrc, _typeSrc, nubDst, _needsExprDest, out _exprDest, 0 == (_flags & CONVERTTYPE.ISEXPLICIT))); } // Both are Nullable so there is only a conversion if there is a conversion between the base types. // That is, if there is an implicit/explicit S => T then there is an implicit/explicit S?+ => T?+. if (typeSrcBase != typeDstBase && !pfn(null, typeSrcBase, typeDstBase, false, out _exprDest, _flags | CONVERTTYPE.NOUDC)) { // No builtin conversion. Maybe there is a user defined conversion.... return(0 == (_flags & CONVERTTYPE.NOUDC) && _binder.bindUserDefinedConversion(_exprSrc, _typeSrc, nubDst, _needsExprDest, out _exprDest, 0 == (_flags & CONVERTTYPE.ISEXPLICIT))); } if (_needsExprDest) { MethWithInst mwi = new MethWithInst(null, null); ExprMemberGroup pMemGroup = GetExprFactory().CreateMemGroup(null, mwi); ExprCall exprDst = GetExprFactory().CreateCall(0, nubDst, _exprSrc, pMemGroup, null); // Here we want to first check whether or not the conversions work on the base types. Expr arg1 = _binder.mustCast(_exprSrc, typeSrcBase); bool convertible = (_flags & CONVERTTYPE.ISEXPLICIT) != 0 ? _binder.BindExplicitConversion( arg1, arg1.Type, typeDstBase, out arg1, _flags | CONVERTTYPE.NOUDC) : _binder.BindImplicitConversion( arg1, arg1.Type, typeDstBase, out arg1, _flags | CONVERTTYPE.NOUDC); if (!convertible) { Debug.Fail("bind(Im|Ex)plicitConversion failed unexpectedly"); return(false); } exprDst.CastOfNonLiftedResultToLiftedType = _binder.mustCast(arg1, nubDst, 0); exprDst.NullableCallLiftKind = NullableCallLiftKind.NullableConversion; exprDst.PConversions = exprDst.CastOfNonLiftedResultToLiftedType; _exprDest = exprDst; } return(true); }