/* See if standard reference equality applies. Make sure not to return true if another == operator may be applicable and better (or ambiguous)! This also handles == on System.Delegate, since it has special rules as well. */ protected bool GetRefEqualSigs(List<BinOpFullSig> prgbofs, BinOpArgInfo info) { if (info.mask != BinOpMask.Equal) { return false; } if (info.type1 != info.typeRaw1 || info.type2 != info.typeRaw2) { return false; } bool fRet = false; CType type1 = info.type1; CType type2 = info.type2; CType typeObj = GetReqPDT(PredefinedType.PT_OBJECT); CType typeCls = null; if (type1.IsNullType() && type2.IsNullType()) { typeCls = typeObj; fRet = true; goto LRecord; } // Check for: operator ==(System.Delegate, System.Delegate). CType typeDel; typeDel = GetReqPDT(PredefinedType.PT_DELEGATE); if (canConvert(info.arg1, typeDel) && canConvert(info.arg2, typeDel) && !type1.isDelegateType() && !type2.isDelegateType()) { prgbofs.Add(new BinOpFullSig(typeDel, typeDel, BindDelBinOp, OpSigFlags.Convert, LiftFlags.None, BinOpFuncKind.DelBinOp)); } // The reference type equality operators only handle reference types. FUNDTYPE ft1; ft1 = type1.fundType(); FUNDTYPE ft2; ft2 = type2.fundType(); switch (ft1) { default: return false; case FUNDTYPE.FT_REF: break; case FUNDTYPE.FT_VAR: if (type1.AsTypeParameterType().IsValueType() || (!type1.AsTypeParameterType().IsReferenceType() && !type2.IsNullType())) return false; type1 = type1.AsTypeParameterType().GetEffectiveBaseClass(); break; } if (type2.IsNullType()) { fRet = true; // We don't need to determine the actual best type since we're // returning true - indicating that we've found the best operator. typeCls = typeObj; goto LRecord; } switch (ft2) { default: return false; case FUNDTYPE.FT_REF: break; case FUNDTYPE.FT_VAR: if (type2.AsTypeParameterType().IsValueType() || (!type2.AsTypeParameterType().IsReferenceType() && !type1.IsNullType())) return false; type2 = type2.AsTypeParameterType().GetEffectiveBaseClass(); break; } if (type1.IsNullType()) { fRet = true; // We don't need to determine the actual best type since we're // returning true - indicating that we've found the best operator. typeCls = typeObj; goto LRecord; } if (!canCast(type1, type2, CONVERTTYPE.NOUDC) && !canCast(type2, type1, CONVERTTYPE.NOUDC)) return false; if (type1.isInterfaceType() || type1.isPredefType(PredefinedType.PT_STRING) || GetSymbolLoader().HasBaseConversion(type1, typeDel)) type1 = typeObj; else if (type1.IsArrayType()) type1 = GetReqPDT(PredefinedType.PT_ARRAY); else if (!type1.isClassType()) return false; if (type2.isInterfaceType() || type2.isPredefType(PredefinedType.PT_STRING) || GetSymbolLoader().HasBaseConversion(type2, typeDel)) type2 = typeObj; else if (type2.IsArrayType()) type2 = GetReqPDT(PredefinedType.PT_ARRAY); else if (!type2.isClassType()) return false; Debug.Assert(type1.isClassType() && !type1.isPredefType(PredefinedType.PT_STRING) && !type1.isPredefType(PredefinedType.PT_DELEGATE)); Debug.Assert(type2.isClassType() && !type2.isPredefType(PredefinedType.PT_STRING) && !type2.isPredefType(PredefinedType.PT_DELEGATE)); if (GetSymbolLoader().HasBaseConversion(type2, type1)) typeCls = type1; else if (GetSymbolLoader().HasBaseConversion(type1, type2)) typeCls = type2; LRecord: prgbofs.Add(new BinOpFullSig(typeCls, typeCls, BindRefCmpOp, OpSigFlags.None, LiftFlags.None, BinOpFuncKind.RefCmpOp)); return fRet; }
/* Get the special signatures when at least one of the args is a pointer. Since pointers can't be type arguments, a nullable pointer is illegal, so no sense trying to lift any of these. NOTE: We don't filter out bad operators on void pointers since BindPtrBinOp gives better error messages than the operator overload resolution does. */ protected bool GetPtrBinOpSigs(List<BinOpFullSig> prgbofs, BinOpArgInfo info) { if (!info.type1.IsPointerType() && !info.type2.IsPointerType()) { return false; } // (ptr, ptr) : - // (ptr, int) : + - // (ptr, uint) : + - // (ptr, long) : + - // (ptr, ulong) : + - // (int, ptr) : + // (uint, ptr) : + // (long, ptr) : + // (ulong, ptr) : + // (void, void) : == != < > <= >= // Check the common case first. if (info.type1.IsPointerType() && info.type2.IsPointerType()) { if (info.ValidForVoidPointer()) { prgbofs.Add(new BinOpFullSig(info.type1, info.type2, BindPtrCmpOp, OpSigFlags.None, LiftFlags.None, BinOpFuncKind.PtrCmpOp)); return true; } if (info.type1 == info.type2 && info.ValidForPointer()) { prgbofs.Add(new BinOpFullSig(info.type1, info.type2, BindPtrBinOp, OpSigFlags.None, LiftFlags.None, BinOpFuncKind.PtrBinOp)); return true; } return false; } CType typeT; if (info.type1.IsPointerType()) { if (info.type2.IsNullType()) { if (!info.ValidForVoidPointer()) { return false; } prgbofs.Add(new BinOpFullSig(info.type1, info.type1, BindPtrCmpOp, OpSigFlags.Convert, LiftFlags.None, BinOpFuncKind.PtrCmpOp)); return true; } if (!info.ValidForPointerAndNumber()) { return false; } for (uint i = 0; i < s_rgptIntOp.Length; i++) { if (canConvert(info.arg2, typeT = GetReqPDT(s_rgptIntOp[i]))) { prgbofs.Add(new BinOpFullSig(info.type1, typeT, BindPtrBinOp, OpSigFlags.Convert, LiftFlags.None, BinOpFuncKind.PtrBinOp)); return true; } } return false; } Debug.Assert(info.type2.IsPointerType()); if (info.type1.IsNullType()) { if (!info.ValidForVoidPointer()) { return false; } prgbofs.Add(new BinOpFullSig(info.type2, info.type2, BindPtrCmpOp, OpSigFlags.Convert, LiftFlags.None, BinOpFuncKind.PtrCmpOp)); return true; } if (!info.ValidForNumberAndPointer()) { return false; } for (uint i = 0; i < s_rgptIntOp.Length; i++) { if (canConvert(info.arg1, typeT = GetReqPDT(s_rgptIntOp[i]))) { prgbofs.Add(new BinOpFullSig(typeT, info.type2, BindPtrBinOp, OpSigFlags.Convert, LiftFlags.None, BinOpFuncKind.PtrBinOp)); return true; } } return false; }
protected EXPR bindUserDefinedBinOp(ExpressionKind ek, BinOpArgInfo info) { MethPropWithInst pmpwi = null; if (info.pt1 <= PredefinedType.PT_ULONG && info.pt2 <= PredefinedType.PT_ULONG) { return null; } EXPR expr = null; switch (info.binopKind) { case BinOpKind.Logical: { // Logical operators cannot be overloaded, but use the bitwise overloads. EXPRCALL call = BindUDBinop((ExpressionKind)(ek - ExpressionKind.EK_LOGAND + ExpressionKind.EK_BITAND), info.arg1, info.arg2, true, out pmpwi); if (call != null) { if (call.isOK()) { expr = BindUserBoolOp(ek, call); } else { expr = call; } } break; } default: expr = BindUDBinop(ek, info.arg1, info.arg2, false, out pmpwi); break; } if (expr == null) { return null; } return GetExprFactory().CreateUserDefinedBinop(ek, expr.type, info.arg1, info.arg2, expr, pmpwi); }
/* Record the appropriate binary operator full signature from the given BinOpArgInfo. This assumes that any NullableType valued args should be lifted. */ private void RecordBinOpSigFromArgs(List<BinOpFullSig> prgbofs, BinOpArgInfo info) { LiftFlags grflt = LiftFlags.None; CType typeSig1; CType typeSig2; if (info.type1 != info.typeRaw1) { Debug.Assert(info.type1.IsNullableType()); grflt = grflt | LiftFlags.Lift1; typeSig1 = GetSymbolLoader().GetTypeManager().GetNullable(info.typeRaw1); } else typeSig1 = info.typeRaw1; if (info.type2 != info.typeRaw2) { Debug.Assert(info.type2.IsNullableType()); grflt = grflt | LiftFlags.Lift2; typeSig2 = GetSymbolLoader().GetTypeManager().GetNullable(info.typeRaw2); } else typeSig2 = info.typeRaw2; prgbofs.Add(new BinOpFullSig(typeSig1, typeSig2, BindEnumBinOp, OpSigFlags.Value, grflt, BinOpFuncKind.EnumBinOp)); }
/* Get the special signatures when at least one of the args is an enum. Return true if we find an exact match. */ protected bool GetEnumBinOpSigs(List<BinOpFullSig> prgbofs, BinOpArgInfo info) { if (!info.typeRaw1.isEnumType() && !info.typeRaw2.isEnumType()) { return false; } // (enum, enum) : - == != < > <= >=&| ^ // (enum, under) : + - // (under, enum) : + CType typeSig1 = null; CType typeSig2 = null; LiftFlags grflt = LiftFlags.None; // Look for the no conversions cases. Still need to determine the lifting. These are the common case. if (info.typeRaw1 == info.typeRaw2) { if (!info.ValidForEnum()) { return false; } RecordBinOpSigFromArgs(prgbofs, info); return true; } bool isValidForEnum; if (info.typeRaw1.isEnumType()) { isValidForEnum = (info.typeRaw2 == info.typeRaw1.underlyingEnumType() && info.ValidForEnumAndUnderlyingType()); } else { isValidForEnum = (info.typeRaw1 == info.typeRaw2.underlyingEnumType() && info.ValidForUnderlyingTypeAndEnum()); } if (isValidForEnum) { RecordBinOpSigFromArgs(prgbofs, info); return true; } // Now deal with the conversion cases. Since there are no conversions from enum types to other // enum types we never need to do both cases. if (info.typeRaw1.isEnumType()) { isValidForEnum = info.ValidForEnum() && CanConvertArg2(info, info.typeRaw1, out grflt, out typeSig1, out typeSig2) || info.ValidForEnumAndUnderlyingType() && CanConvertArg2(info, info.typeRaw1.underlyingEnumType(), out grflt, out typeSig1, out typeSig2); } else { isValidForEnum = info.ValidForEnum() && CanConvertArg1(info, info.typeRaw2, out grflt, out typeSig1, out typeSig2) || info.ValidForEnumAndUnderlyingType() && CanConvertArg1(info, info.typeRaw2.underlyingEnumType(), out grflt, out typeSig1, out typeSig2); } if (isValidForEnum) { prgbofs.Add(new BinOpFullSig(typeSig1, typeSig2, BindEnumBinOp, OpSigFlags.Value, grflt, BinOpFuncKind.EnumBinOp)); } return false; }
/* Get the special signatures when at least one of the args is a delegate instance. Returns true iff an exact signature match is found. */ protected bool GetDelBinOpSigs(List<BinOpFullSig> prgbofs, BinOpArgInfo info) { if (!info.ValidForDelegate()) { return false; } if (!info.type1.isDelegateType() && !info.type2.isDelegateType()) { return false; } // Don't allow comparison with an anonymous method or lambda. It's just too weird. if (((info.mask & BinOpMask.Equal) != 0) && (info.type1.IsBoundLambdaType() || info.type2.IsBoundLambdaType())) return false; // No conversions needed. Determine the lifting. This is the common case. if (info.type1 == info.type2) { prgbofs.Add(new BinOpFullSig(info.type1, info.type2, BindDelBinOp, OpSigFlags.Reference, LiftFlags.None, BinOpFuncKind.DelBinOp)); return true; } // Now, for each delegate type, if both arguments convert to that delegate type, that is a candidate // for this binary operator. It's possible that we add two candidates, in which case they will compete // in overload resolution. Or we could add no candidates. bool t1tot2 = info.type2.isDelegateType() && canConvert(info.arg1, info.type2); bool t2tot1 = info.type1.isDelegateType() && canConvert(info.arg2, info.type1); if (t1tot2) { prgbofs.Add(new BinOpFullSig(info.type2, info.type2, BindDelBinOp, OpSigFlags.Reference, LiftFlags.None, BinOpFuncKind.DelBinOp)); } if (t2tot1) { prgbofs.Add(new BinOpFullSig(info.type1, info.type1, BindDelBinOp, OpSigFlags.Reference, LiftFlags.None, BinOpFuncKind.DelBinOp)); } // Might be ambiguous so return false. return false; }
/* Same as CanConvertArg1 but with the indices interchanged! */ private bool CanConvertArg2(BinOpArgInfo info, CType typeDst, out LiftFlags pgrflt, out CType ptypeSig1, out CType ptypeSig2) { Debug.Assert(!typeDst.IsNullableType()); ptypeSig1 = null; ptypeSig2 = null; if (canConvert(info.arg2, typeDst)) pgrflt = LiftFlags.None; else { pgrflt = LiftFlags.None; if (!GetSymbolLoader().FCanLift()) return false; typeDst = GetSymbolLoader().GetTypeManager().GetNullable(typeDst); if (!canConvert(info.arg2, typeDst)) return false; pgrflt = LiftFlags.Convert2; } ptypeSig2 = typeDst; if (info.type1.IsNullableType()) { pgrflt = pgrflt | LiftFlags.Lift1; ptypeSig1 = GetSymbolLoader().GetTypeManager().GetNullable(info.typeRaw1); } else ptypeSig1 = info.typeRaw1; return true; }
protected EXPR BindStandardBinopCore(BinOpArgInfo info, BinOpFullSig bofs, ExpressionKind ek, EXPRFLAG flags) { if (bofs.pfn == null) { return BadOperatorTypesError(ek, info.arg1, info.arg2); } if (!bofs.isLifted() || !bofs.AutoLift()) { EXPR expr1 = info.arg1; EXPR expr2 = info.arg2; if (bofs.ConvertOperandsBeforeBinding()) { expr1 = mustConvert(expr1, bofs.Type1()); expr2 = mustConvert(expr2, bofs.Type2()); } if (bofs.fnkind == BinOpFuncKind.BoolBitwiseOp) { return BindBoolBitwiseOp(ek, flags, expr1, expr2, bofs); } return bofs.pfn(ek, flags, expr1, expr2); } Debug.Assert(bofs.fnkind != BinOpFuncKind.BoolBitwiseOp); return BindLiftedStandardBinOp(info, bofs, ek, flags); }
private EXPR BindLiftedStandardBinOp(BinOpArgInfo info, BinOpFullSig bofs, ExpressionKind ek, EXPRFLAG flags) { Debug.Assert(bofs.Type1().IsNullableType() || bofs.Type2().IsNullableType()); EXPR arg1 = info.arg1; EXPR arg2 = info.arg2; // We want to get the base types of the arguments and attempt to bind the non-lifted form of the // method so that we error report (ie divide by zero etc), and then we store in the resulting // binop that we have a lifted operator. EXPR pArgument1 = null; EXPR pArgument2 = null; EXPR nonLiftedArg1 = null; EXPR nonLiftedArg2 = null; EXPR nonLiftedResult = null; CType resultType = null; LiftArgument(arg1, bofs.Type1(), bofs.ConvertFirst(), out pArgument1, out nonLiftedArg1); LiftArgument(arg2, bofs.Type2(), bofs.ConvertSecond(), out pArgument2, out nonLiftedArg2); // Now call the non-lifted method to generate errors, and stash the result. if (!nonLiftedArg1.isNull() && !nonLiftedArg2.isNull()) { // Only compute the method if theres no nulls. If there are, we'll special case it // later, since operations with a null operand are null. nonLiftedResult = bofs.pfn(ek, flags, nonLiftedArg1, nonLiftedArg2); } // Check if we have a comparison. If so, set the result type to bool. if (info.binopKind == BinOpKind.Compare || info.binopKind == BinOpKind.Equal) { resultType = GetReqPDT(PredefinedType.PT_BOOL); } else { if (bofs.fnkind == BinOpFuncKind.EnumBinOp) { AggregateType enumType; resultType = GetEnumBinOpType(ek, nonLiftedArg1.type, nonLiftedArg2.type, out enumType); } else { resultType = pArgument1.type; } resultType = resultType.IsNullableType() ? resultType : GetSymbolLoader().GetTypeManager().GetNullable(resultType); } EXPRBINOP exprRes = GetExprFactory().CreateBinop(ek, resultType, pArgument1, pArgument2); mustCast(nonLiftedResult, resultType, 0); exprRes.isLifted = true; exprRes.flags |= flags; Debug.Assert((exprRes.flags & EXPRFLAG.EXF_LVALUE) == 0); return exprRes; }
/* This handles binding binary operators by first checking for user defined operators, then applying overload resolution to the predefined operators. It handles lifting over nullable. */ public EXPR BindStandardBinop(ExpressionKind ek, EXPR arg1, EXPR arg2) { Debug.Assert(arg1 != null); Debug.Assert(arg2 != null); EXPRFLAG flags = 0; BinOpArgInfo info = new BinOpArgInfo(arg1, arg2); if (!GetBinopKindAndFlags(ek, out info.binopKind, out flags)) { // If we don't get the BinopKind and the flags, then we must have had some bad operator types. return BadOperatorTypesError(ek, arg1, arg2); } info.mask = (BinOpMask)(1 << (int)info.binopKind); List<BinOpFullSig> binopSignatures = new List<BinOpFullSig>(); int bestBinopSignature = -1; // First check if this is a user defined binop. If it is, return it. EXPR exprUD = bindUserDefinedBinOp(ek, info); if (exprUD != null) { return exprUD; } // Get the special binop signatures. If successful, the special binop signature will be // the last item in the array of signatures that we give it. bool exactMatch = GetSpecialBinopSignatures(binopSignatures, info); if (!exactMatch) { // No match, try to get standard and lifted binop signatures. exactMatch = GetStandardAndLiftedBinopSignatures(binopSignatures, info); } // If we have an exact match in either the special binop signatures or the standard/lifted binop // signatures, then we set our best match. Otherwise, we check if we had any signatures at all. // If we didn't, then its possible where we have x == null, where x is nullable, so try to bind // the null equality comparison. Otherwise, we had some ambiguity - we have a match, but its not exact. if (exactMatch) { Debug.Assert(binopSignatures.Count > 0); bestBinopSignature = binopSignatures.Count - 1; } else if (binopSignatures.Count == 0) { // If we got no matches then it's possible that we're in the case // x == null, where x is nullable. return bindNullEqualityComparison(ek, info); } else { // We had some matches, try to find the best one. FindBestSignatureInList returns < 0 if // we don't have a best one, otherwise it returns the index of the best one in our list that // we give it. bestBinopSignature = FindBestSignatureInList(binopSignatures, info); if (bestBinopSignature < 0) { // Ambiguous. return ambiguousOperatorError(ek, arg1, arg2); } } // If we're here, we should have a binop signature that exactly matches. Debug.Assert(bestBinopSignature < binopSignatures.Count); // We've found the one to use, so lets go and bind it. return BindStandardBinopCore(info, binopSignatures[bestBinopSignature], ek, flags); }
protected EXPRBINOP bindNullEqualityComparison(ExpressionKind ek, BinOpArgInfo info) { EXPR arg1 = info.arg1; EXPR arg2 = info.arg2; if (info.binopKind == BinOpKind.Equal) { CType typeBool = GetReqPDT(PredefinedType.PT_BOOL); EXPRBINOP exprRes = null; if (info.type1.IsNullableType() && info.type2.IsNullType()) { arg2 = GetExprFactory().CreateZeroInit(info.type1); exprRes = GetExprFactory().CreateBinop(ek, typeBool, arg1, arg2); } if (info.type1.IsNullType() && info.type2.IsNullableType()) { arg1 = GetExprFactory().CreateZeroInit(info.type2); exprRes = GetExprFactory().CreateBinop(ek, typeBool, arg1, arg2); } if (exprRes != null) { exprRes.isLifted = true; return exprRes; } } EXPR pExpr = BadOperatorTypesError(ek, info.arg1, info.arg2, GetTypes().GetErrorSym()); Debug.Assert(pExpr.isBIN()); return pExpr.asBIN(); }
// Returns the index of the best match, or -1 if there is no best match. protected int FindBestSignatureInList( List<BinOpFullSig> binopSignatures, BinOpArgInfo info) { Debug.Assert(binopSignatures != null); if (binopSignatures.Count == 1) { return 0; } int bestSignature = 0; int index; // Try to find a candidate for the best. for (index = 1; index < binopSignatures.Count; index++) { if (bestSignature < 0) { bestSignature = index; } else { int nT = WhichBofsIsBetter(binopSignatures[bestSignature], binopSignatures[index], info.type1, info.type2); if (nT == 0) { bestSignature = -1; } else if (nT > 0) { bestSignature = index; } } } if (bestSignature == -1) { return -1; } // Verify that the candidate really is not worse than all others. // Do we need to loop over the whole list here, or just // from 0 . bestSignature - 1? for (index = 0; index < binopSignatures.Count; index++) { if (index == bestSignature) { continue; } if (WhichBofsIsBetter(binopSignatures[bestSignature], binopSignatures[index], info.type1, info.type2) >= 0) { return -1; } } return bestSignature; }
// Adds standard and lifted signatures to the candidate list. If we find an exact match // then it will be the last item on the list and we return true. protected bool GetStandardAndLiftedBinopSignatures(List<BinOpFullSig> rgbofs, BinOpArgInfo info) { Debug.Assert(rgbofs != null); int ibos; int ibosMinLift; ibosMinLift = GetSymbolLoader().FCanLift() ? 0 : g_binopSignatures.Length; for (ibos = 0; ibos < g_binopSignatures.Length; ibos++) { BinOpSig bos = g_binopSignatures[ibos]; if ((bos.mask & info.mask) == 0) { continue; } CType typeSig1 = GetOptPDT(bos.pt1, PredefinedTypes.isRequired(bos.pt1)); CType typeSig2 = GetOptPDT(bos.pt2, PredefinedTypes.isRequired(bos.pt2)); if (typeSig1 == null || typeSig2 == null) continue; ConvKind cv1 = GetConvKind(info.pt1, bos.pt1); ConvKind cv2 = GetConvKind(info.pt2, bos.pt2); LiftFlags grflt = LiftFlags.None; switch (cv1) { default: VSFAIL("Shouldn't happen!"); continue; case ConvKind.None: continue; case ConvKind.Explicit: if (!info.arg1.isCONSTANT_OK()) { continue; } // Need to try to convert. if (canConvert(info.arg1, typeSig1)) { break; } if (ibos < ibosMinLift || !bos.CanLift()) { continue; } Debug.Assert(typeSig1.IsValType()); typeSig1 = GetSymbolLoader().GetTypeManager().GetNullable(typeSig1); if (!canConvert(info.arg1, typeSig1)) { continue; } switch (GetConvKind(info.ptRaw1, bos.pt1)) { default: grflt = grflt | LiftFlags.Convert1; break; case ConvKind.Implicit: case ConvKind.Identity: grflt = grflt | LiftFlags.Lift1; break; } break; case ConvKind.Unknown: if (canConvert(info.arg1, typeSig1)) { break; } if (ibos < ibosMinLift || !bos.CanLift()) { continue; } Debug.Assert(typeSig1.IsValType()); typeSig1 = GetSymbolLoader().GetTypeManager().GetNullable(typeSig1); if (!canConvert(info.arg1, typeSig1)) { continue; } switch (GetConvKind(info.ptRaw1, bos.pt1)) { default: grflt = grflt | LiftFlags.Convert1; break; case ConvKind.Implicit: case ConvKind.Identity: grflt = grflt | LiftFlags.Lift1; break; } break; case ConvKind.Implicit: break; case ConvKind.Identity: if (cv2 == ConvKind.Identity) { BinOpFullSig newsig = new BinOpFullSig(this, bos); if (newsig.Type1() != null && newsig.Type2() != null) { // Exact match. rgbofs.Add(newsig); return true; } } break; } switch (cv2) { default: VSFAIL("Shouldn't happen!"); continue; case ConvKind.None: continue; case ConvKind.Explicit: if (!info.arg2.isCONSTANT_OK()) { continue; } // Need to try to convert. if (canConvert(info.arg2, typeSig2)) { break; } if (ibos < ibosMinLift || !bos.CanLift()) { continue; } Debug.Assert(typeSig2.IsValType()); typeSig2 = GetSymbolLoader().GetTypeManager().GetNullable(typeSig2); if (!canConvert(info.arg2, typeSig2)) { continue; } switch (GetConvKind(info.ptRaw2, bos.pt2)) { default: grflt = grflt | LiftFlags.Convert2; break; case ConvKind.Implicit: case ConvKind.Identity: grflt = grflt | LiftFlags.Lift2; break; } break; case ConvKind.Unknown: if (canConvert(info.arg2, typeSig2)) { break; } if (ibos < ibosMinLift || !bos.CanLift()) { continue; } Debug.Assert(typeSig2.IsValType()); typeSig2 = GetSymbolLoader().GetTypeManager().GetNullable(typeSig2); if (!canConvert(info.arg2, typeSig2)) { continue; } switch (GetConvKind(info.ptRaw2, bos.pt2)) { default: grflt = grflt | LiftFlags.Convert2; break; case ConvKind.Implicit: case ConvKind.Identity: grflt = grflt | LiftFlags.Lift2; break; } break; case ConvKind.Identity: case ConvKind.Implicit: break; } if (grflt != LiftFlags.None) { // We have a lifted signature. rgbofs.Add(new BinOpFullSig(typeSig1, typeSig2, bos.pfn, bos.grfos, grflt, bos.fnkind)); // NOTE: Can't skip any if we use a 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.... ibosMinLift = ibos + bos.cbosSkip + 1; } else { // Record it as applicable and skip accordingly. rgbofs.Add(new BinOpFullSig(this, bos)); ibos += bos.cbosSkip; } } return false; }
// Adds special signatures to the candidate list. If we find an exact match // then it will be the last item on the list and we return true. protected bool GetSpecialBinopSignatures(List<BinOpFullSig> prgbofs, BinOpArgInfo info) { Debug.Assert(prgbofs != null); if (info.pt1 <= PredefinedType.PT_ULONG && info.pt2 <= PredefinedType.PT_ULONG) { return false; } return GetDelBinOpSigs(prgbofs, info) || GetEnumBinOpSigs(prgbofs, info) || GetPtrBinOpSigs(prgbofs, info) || GetRefEqualSigs(prgbofs, info); }