//------------------------------------------------------------ // FUNCBREC.BindNubNew // /// <summary> /// Create an expr for new T?(exprSrc) where T is exprSrc->type. /// </summary> /// <param name="treeNode"></param> /// <param name="srcExpr"></param> /// <returns></returns> //------------------------------------------------------------ private EXPR BindNubNew(BASENODE treeNode, EXPR srcExpr) { DebugUtil.Assert(srcExpr != null); // Create a NUBSYM instance whose base bype is represented by srcExpr.TypeSym. NUBSYM nubSym = Compiler.MainSymbolManager.GetNubType(srcExpr.TypeSym); // Get a TYPESYM instance representing Nullable<> for nubSym. AGGTYPESYM aggTypeSym = nubSym.GetAggTypeSym(); if (aggTypeSym == null) { return(NewError(treeNode, nubSym)); } Compiler.EnsureState(aggTypeSym, AggStateEnum.Prepared); METHSYM methSym = Compiler.MainSymbolManager.NullableCtorMethodSym; if (methSym == null) { string name = Compiler.NameManager.GetPredefinedName(PREDEFNAME.CTOR); for (SYM sym = Compiler.MainSymbolManager.LookupAggMember(name, aggTypeSym.GetAggregate(), SYMBMASK.ALL); ; sym = sym.NextSameNameSym) { if (sym == null) { Compiler.Error(treeNode, CSCERRID.ERR_MissingPredefinedMember, new ErrArg(aggTypeSym), new ErrArg(name)); return(NewError(treeNode, nubSym)); } if (sym.IsMETHSYM) { methSym = sym as METHSYM; if (methSym.ParameterTypes.Count == 1 && methSym.ParameterTypes[0].IsTYVARSYM && methSym.Access == ACCESS.PUBLIC) { break; } } } Compiler.MainSymbolManager.NullableCtorMethodSym = methSym; } EXPRCALL resExpr = NewExpr(treeNode, EXPRKIND.CALL, nubSym) as EXPRCALL; resExpr.MethodWithInst.Set(methSym, aggTypeSym, BSYMMGR.EmptyTypeArray); resExpr.ArgumentsExpr = srcExpr; resExpr.ObjectExpr = null; resExpr.Flags |= EXPRFLAG.NEWOBJCALL | EXPRFLAG.CANTBENULL; return(resExpr); }
//------------------------------------------------------------ // FUNCBREC.BindNubOpRes (1) // /// <summary> /// <para>Combine the condition and value.</para> /// <para>(In sscli, warOnNull has the default value false.)</para> /// </summary> /// <param name="treeNode"></param> /// <param name="nubSym"></param> /// <param name="dstTypeSym"></param> /// <param name="valueExpr"></param> /// <param name="nubInfo"></param> /// <param name="warnOnNull"></param> /// <returns></returns> //------------------------------------------------------------ private EXPR BindNubOpRes( BASENODE treeNode, NUBSYM nubSym, TYPESYM dstTypeSym, EXPR valueExpr, ref NubInfo nubInfo, bool warnOnNull) // = false { if (nubInfo.FAlwaysNull() && warnOnNull) { Compiler.Error(treeNode, CSCERRID.WRN_AlwaysNull, new ErrArg(nubSym)); } return(BindNubOpRes( treeNode, dstTypeSym, valueExpr, NewExprZero(treeNode, dstTypeSym.IsNUBSYM ? dstTypeSym : nubSym), ref nubInfo)); }
//------------------------------------------------------------ // FUNCBREC.BindNubConversion // // 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. // /// <summary></summary> /// <param name="treeNode"></param> /// <param name="srcExpr"></param> /// <param name="srcTypeSym"></param> /// <param name="dstNubSym"></param> /// <param name="dstExpr"></param> /// <param name="flags"></param> /// <returns></returns> //------------------------------------------------------------ private bool BindNubConversion( BASENODE treeNode, EXPR srcExpr, TYPESYM srcTypeSym, NUBSYM dstNubSym, ref EXPR dstExpr, ConvertTypeEnum flags) { // This code assumes that STANDARD and ISEXPLICIT are never both set. // bindUserDefinedConversion should ensure this! DebugUtil.Assert((~flags & (ConvertTypeEnum.STANDARD | ConvertTypeEnum.ISEXPLICIT)) != 0); DebugUtil.Assert(srcExpr == null || srcExpr.TypeSym == srcTypeSym); DebugUtil.Assert(dstExpr == null || srcExpr != null); DebugUtil.Assert(srcTypeSym != dstNubSym); // bindImplicitConversion should have taken care of this already. AGGTYPESYM dstAggTypeSym = dstNubSym.GetAggTypeSym(); if (dstAggTypeSym == null) { return(false); } // Check for the unboxing conversion. This takes precedence over the wrapping conversions. if (Compiler.IsBaseType(dstNubSym.BaseTypeSym, srcTypeSym) && !FWrappingConv(srcTypeSym, dstNubSym)) { // These should be different! Fix the caller if srcTypeSym is an AGGTYPESYM of Nullable. DebugUtil.Assert(dstAggTypeSym != srcTypeSym); // srcTypeSym is a base type of the destination nullable type so there is an explicit // unboxing conversion. if ((flags & ConvertTypeEnum.ISEXPLICIT) == 0) { return(false); } return(BindSimpleCast(treeNode, srcExpr, dstNubSym, ref dstExpr, EXPRFLAG.UNBOX)); } int dstNubStripCount; int srcNubStripCount; TYPESYM dstBaseTypeSym = dstNubSym.StripNubs(out dstNubStripCount); TYPESYM srcBaseTypeSym = srcTypeSym.StripNubs(out srcNubStripCount); bindNubConversion_Convert fnConvert; EXPR expr = null; // temp if ((flags & ConvertTypeEnum.ISEXPLICIT) != 0) { fnConvert = new bindNubConversion_Convert(BindExplicitConversion); } else { fnConvert = new bindNubConversion_Convert(BindImplicitConversion); } //bool (FUNCBREC::*pfn)(BASENODE *, EXPR *, TYPESYM *, TYPESYM *, EXPR **, uint) = // (flags & ISEXPLICIT) ? &FUNCBREC::bindExplicitConversion : &FUNCBREC::bindImplicitConversion; if (srcNubStripCount == 0) { DebugUtil.Assert(srcTypeSym == srcBaseTypeSym); // The null type can be implicitly converted to T? as the default value. if (srcTypeSym.IsNULLSYM) { dstExpr = AddSideEffects(treeNode, NewExprZero(treeNode, dstNubSym), srcExpr, true, true); return(true); } EXPR tempExpr = srcExpr; // If there is an implicit/explicit S => T then there is an implicit/explicit S => T? if (srcTypeSym == dstBaseTypeSym || fnConvert( treeNode, srcExpr, srcTypeSym, dstBaseTypeSym, ref tempExpr, flags | ConvertTypeEnum.NOUDC)) { // srcTypeSym is not nullable so just wrap the required number of times. for (int i = 0; i < dstNubStripCount; i++) { tempExpr = BindNubNew(treeNode, tempExpr); } DebugUtil.Assert(tempExpr.TypeSym == dstNubSym); dstExpr = tempExpr; return(true); } // No builtin conversion. Maybe there is a user defined conversion.... return( (flags & ConvertTypeEnum.NOUDC) == 0 && BindUserDefinedConversion( treeNode, srcExpr, srcTypeSym, dstNubSym, ref dstExpr, (flags & ConvertTypeEnum.ISEXPLICIT) == 0)); } // 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 (srcBaseTypeSym != dstBaseTypeSym && !fnConvert(treeNode, null, srcBaseTypeSym, dstBaseTypeSym, ref expr, flags | ConvertTypeEnum.NOUDC)) { // No builtin conversion. Maybe there is a user defined conversion.... return( (flags & ConvertTypeEnum.NOUDC) == 0 && BindUserDefinedConversion( treeNode, srcExpr, srcTypeSym, dstNubSym, ref dstExpr, (flags & ConvertTypeEnum.ISEXPLICIT) == 0)); } // We need to go all the way down to the base types, do the conversion, then come all the way back up. EXPR valExpr; NubInfo nubInfo = new NubInfo(); BindNubCondValBin( treeNode, srcExpr, null, ref nubInfo, LiftFlagsEnum.LiftBoth); valExpr = nubInfo.Val(0); DebugUtil.Assert(valExpr.TypeSym == srcBaseTypeSym); if (!fnConvert( treeNode, valExpr, valExpr.TypeSym, dstBaseTypeSym, ref valExpr, flags | ConvertTypeEnum.NOUDC)) { DebugUtil.Assert(false, "bind(Im|Ex)plicitConversion failed unexpectedly"); return(false); } for (int i = 0; i < dstNubStripCount; i++) { valExpr = BindNubNew(treeNode, valExpr); } DebugUtil.Assert(valExpr.TypeSym == dstNubSym); dstExpr = BindNubOpRes(treeNode, dstNubSym, dstNubSym, valExpr, ref nubInfo, false); return(true); }