/*************************************************************************************************** * Determine whether this expr has a constant value (EK_CONSTANT or EK_ZEROINIT), possibly with * side effects (via EK_SEQUENCE or EK_SEQREV). Returns NULL if not, or the constant expr if so. * The returned EXPR will always be an EK_CONSTANT or EK_ZEROINIT. ***************************************************************************************************/ public static EXPR GetConst(this EXPR expr) { EXPR exprVal = expr.GetSeqVal(); if (null == exprVal || !exprVal.isCONSTANT_OK() && exprVal.kind != ExpressionKind.EK_ZEROINIT) { return(null); } return(exprVal); }
/*************************************************************************************************** * 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(GetErrorContext()); if (atsDst == null) { return(false); } // 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, exprTypeDest, out exprDest, EXPRFLAG.EXF_UNBOX); } return(true); } int cnubDst; int cnubSrc; CType typeDstBase = nubDst.StripNubs(out cnubDst); EXPRCLASS exprTypeDstBase = GetExprFactory().MakeClass(typeDstBase); CType typeSrcBase = typeSrc.StripNubs(out cnubSrc); ConversionFunc pfn = (flags & CONVERTTYPE.ISEXPLICIT) != 0 ? (ConversionFunc)binder.BindExplicitConversion : (ConversionFunc)binder.BindImplicitConversion; if (cnubSrc == 0) { Debug.Assert(typeSrc == typeSrcBase); // The null type can be implicitly converted to T? as the default value. if (typeSrc.IsNullType()) { // 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(0x00, 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, exprTypeDstBase, nubDst, needsExprDest, out exprTmp, flags | CONVERTTYPE.NOUDC)) { if (needsExprDest) { // UNDONE: This is a premature realization of the nullable conversion as // UNDONE: a constructor. Rather than flagging this, can we simply emit it // UNDONE: as a cast node and have the operator rewrite pass turn it into // UNDONE: a constructor call? EXPRUSERDEFINEDCONVERSION exprUDC = exprTmp.kind == ExpressionKind.EK_USERDEFINEDCONVERSION ? exprTmp.asUSERDEFINEDCONVERSION() : null; if (exprUDC != null) { exprTmp = exprUDC.UserDefinedCall; } // This logic is left over from the days when T?? was legal. However there are error/LAF cases that necessitates the loop. // typeSrc is not nullable so just wrap the required number of times. For legal code (cnubDst <= 0). for (int i = 0; i < cnubDst; i++) { exprTmp = binder.BindNubNew(exprTmp); exprTmp.asCALL().nubLiftKind = NullableCallLiftKind.NullableConversionConstructor; } if (exprUDC != null) { exprUDC.UserDefinedCall = exprTmp; exprUDC.setType((CType)exprTmp.type); 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, exprTypeDstBase, nubDst, 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); EXPRMEMGRP 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); EXPRCLASS arg2 = GetExprFactory().MakeClass(typeDstBase); bool convertible; if (0 != (flags & CONVERTTYPE.ISEXPLICIT)) { convertible = binder.BindExplicitConversion(arg1, arg1.type, arg2, typeDstBase, out arg1, flags | CONVERTTYPE.NOUDC); } else { convertible = binder.BindImplicitConversion(arg1, arg1.type, arg2, typeDstBase, out arg1, flags | CONVERTTYPE.NOUDC); } if (!convertible) { VSFAIL("bind(Im|Ex)plicitConversion failed unexpectedly"); return(false); } exprDst.castOfNonLiftedResultToLiftedType = binder.mustCast(arg1, nubDst, 0); exprDst.nubLiftKind = NullableCallLiftKind.NullableConversion; exprDst.pConversions = exprDst.castOfNonLiftedResultToLiftedType; exprDest = exprDst; } return(true); }
public static bool isNull(this EXPR expr) { return(expr.isCONSTANT_OK() && (expr.type.fundType() == FUNDTYPE.FT_REF) && expr.asCONSTANT().Val.IsNullRef); }
public static bool isZero(this EXPR expr) { return((expr.isCONSTANT_OK()) && (expr.asCONSTANT().IsZero)); }
///////////////////////////////////////////////////////////////////////////////// private bool FindApplicableSignatures( EXPR pArgument, UnaOpMask unaryOpMask, List<UnaOpFullSig> pSignatures) { // All callers should already assert this to be the case. Debug.Assert(pArgument != null); Debug.Assert(pArgument.type != null); long iuosMinLift = GetSymbolLoader().FCanLift() ? 0 : g_rguos.Length; CType pArgumentType = pArgument.type; CType pRawType = pArgumentType.StripNubs(); PredefinedType pt = pArgumentType.isPredefined() ? pArgumentType.getPredefType() : PredefinedType.PT_COUNT; PredefinedType ptRaw = pRawType.isPredefined() ? pRawType.getPredefType() : PredefinedType.PT_COUNT; for (int index = 0; index < g_rguos.Length; index++) { UnaOpSig uos = g_rguos[index]; if ((uos.grfuom & unaryOpMask) == 0) { continue; } ConvKind cv = GetConvKind(pt, g_rguos[index].pt); CType typeSig = null; switch (cv) { default: VSFAIL("Shouldn't happen!"); continue; case ConvKind.None: continue; case ConvKind.Explicit: if (!pArgument.isCONSTANT_OK()) { continue; } if (canConvert(pArgument, typeSig = GetOptPDT(uos.pt))) { break; } if (index < iuosMinLift) { continue; } typeSig = GetSymbolLoader().GetTypeManager().GetNullable(typeSig); if (!canConvert(pArgument, typeSig)) { continue; } break; case ConvKind.Unknown: if (canConvert(pArgument, typeSig = GetOptPDT(uos.pt))) { break; } if (index < iuosMinLift) { continue; } typeSig = GetSymbolLoader().GetTypeManager().GetNullable(typeSig); if (!canConvert(pArgument, typeSig)) { continue; } break; case ConvKind.Implicit: break; case ConvKind.Identity: { UnaOpFullSig result = new UnaOpFullSig(this, uos); if (result.GetType() != null) { pSignatures.Add(result); return true; } } break; } if (typeSig != null && typeSig.IsNullableType()) { // Need to use a lifted signature. LiftFlags grflt = LiftFlags.None; switch (GetConvKind(ptRaw, uos.pt)) { default: grflt = grflt | LiftFlags.Convert1; break; case ConvKind.Implicit: case ConvKind.Identity: grflt = grflt | LiftFlags.Lift1; break; } pSignatures.Add(new UnaOpFullSig(typeSig, uos.pfn, grflt, uos.fnkind)); // NOTE: Can't skip any if we use the lifted signature because the // type might convert to int? and to long (but not to int) in which // case we should get an ambiguity. But we can skip the lifted ones.... iuosMinLift = index + uos.cuosSkip + 1; } else { // Record it as applicable and skip accordingly. UnaOpFullSig newResult = new UnaOpFullSig(this, uos); if (newResult.GetType() != null) { pSignatures.Add(newResult); } index += uos.cuosSkip; } } return false; }