/* * BindImplicitConversion * * This is a complex routine with complex parameters. Generally, this should * be called through one of the helper methods that insulates you * from the complexity of the interface. This routine handles all the logic * associated with implicit conversions. * * exprSrc - the expression being converted. Can be null if only type conversion * info is being supplied. * typeSrc - type of the source * typeDest - type of the destination * exprDest - returns an expression of the src converted to the dest. If null, we * only care about whether the conversion can be attempted, not the * expression tree. * flags - flags possibly customizing the conversions allowed. E.g., can suppress * user-defined conversions. * * returns true if the conversion can be made, false if not. */ public bool Bind() { // 13.1 Implicit conversions // // The following conversions are classified as implicit conversions: // // * Identity conversions // * Implicit numeric conversions // * Implicit enumeration conversions // * Implicit reference conversions // * Boxing conversions // * Implicit type parameter conversions // * Implicit constant expression conversions // * User-defined implicit conversions // * Implicit conversions from an anonymous method expression to a compatible delegate type // * Implicit conversion from a method group to a compatible delegate type // * Conversions from the null type (11.2.7) to any nullable type // * Implicit nullable conversions // * Lifted user-defined implicit conversions // // Implicit conversions can occur in a variety of situations, including function member invocations // (14.4.3), cast expressions (14.6.6), and assignments (14.14). // Can't convert to or from the error type. if (typeSrc == null || typeDest == null || typeDest.IsNeverSameType()) { return(false); } Debug.Assert(typeSrc != null && typeDest != null); // types must be supplied. Debug.Assert(exprSrc == null || typeSrc == exprSrc.type); // type of source should be correct if source supplied Debug.Assert(!needsExprDest || exprSrc != null); // need source expr to create dest expr switch (typeDest.GetTypeKind()) { case TypeKind.TK_ErrorType: Debug.Assert(typeDest.AsErrorType().HasTypeParent() || typeDest.AsErrorType().HasNSParent()); if (typeSrc != typeDest) { return(false); } if (needsExprDest) { exprDest = exprSrc; } return(true); case TypeKind.TK_NullType: // Can only convert to the null type if src is null. if (!typeSrc.IsNullType()) { return(false); } if (needsExprDest) { exprDest = exprSrc; } return(true); case TypeKind.TK_MethodGroupType: VSFAIL("Something is wrong with Type.IsNeverSameType()"); return(false); case TypeKind.TK_NaturalIntegerType: case TypeKind.TK_ArgumentListType: return(typeSrc == typeDest); case TypeKind.TK_VoidType: return(false); default: break; } if (typeSrc.IsErrorType()) { Debug.Assert(!typeDest.IsErrorType()); return(false); } // 13.1.1 Identity conversion // // An identity conversion converts from any type to the same type. This conversion exists only // such that an entity that already has a required type can be said to be convertible to that type. if (typeSrc == typeDest && ((flags & CONVERTTYPE.ISEXPLICIT) == 0 || (!typeSrc.isPredefType(PredefinedType.PT_FLOAT) && !typeSrc.isPredefType(PredefinedType.PT_DOUBLE)))) { if (needsExprDest) { exprDest = exprSrc; } return(true); } if (typeDest.IsNullableType()) { return(BindNubConversion(typeDest.AsNullableType())); } if (typeSrc.IsNullableType()) { return(bindImplicitConversionFromNullable(typeSrc.AsNullableType())); } if ((flags & CONVERTTYPE.ISEXPLICIT) != 0) { flags |= CONVERTTYPE.NOUDC; } // Get the fundamental types of destination. FUNDTYPE ftDest = typeDest.fundType(); Debug.Assert(ftDest != FUNDTYPE.FT_NONE || typeDest.IsParameterModifierType()); switch (typeSrc.GetTypeKind()) { default: VSFAIL("Bad type symbol kind"); break; case TypeKind.TK_MethodGroupType: if (exprSrc.isMEMGRP()) { EXPRCALL outExpr; bool retVal = binder.BindGrpConversion(exprSrc.asMEMGRP(), typeDest, needsExprDest, out outExpr, false); exprDest = outExpr; return(retVal); } return(false); case TypeKind.TK_VoidType: case TypeKind.TK_ErrorType: case TypeKind.TK_ParameterModifierType: case TypeKind.TK_ArgumentListType: return(false); case TypeKind.TK_NullType: if (bindImplicitConversionFromNull()) { return(true); } // If not, try user defined implicit conversions. break; case TypeKind.TK_ArrayType: if (bindImplicitConversionFromArray()) { return(true); } // If not, try user defined implicit conversions. break; case TypeKind.TK_PointerType: if (bindImplicitConversionFromPointer()) { return(true); } // If not, try user defined implicit conversions. break; case TypeKind.TK_TypeParameterType: if (bindImplicitConversionFromTypeVar(typeSrc.AsTypeParameterType())) { return(true); } // If not, try user defined implicit conversions. break; case TypeKind.TK_AggregateType: // TypeReference and ArgIterator can't be boxed (or converted to anything else) if (typeSrc.isSpecialByRefType()) { return(false); } if (bindImplicitConversionFromAgg(typeSrc.AsAggregateType())) { return(true); } // If not, try user defined implicit conversions. break; } // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // RUNTIME BINDER ONLY CHANGE // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // // Every incoming dynamic operand should be implicitly convertible // to any type that it is an instance of. if (exprSrc != null && exprSrc.RuntimeObject != null && typeDest.AssociatedSystemType.IsInstanceOfType(exprSrc.RuntimeObject) && binder.GetSemanticChecker().CheckTypeAccess(typeDest, binder.Context.ContextForMemberLookup())) { if (needsExprDest) { binder.bindSimpleCast(exprSrc, exprTypeDest, out exprDest, exprSrc.flags & EXPRFLAG.EXF_CANTBENULL); } return(true); } // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // END RUNTIME BINDER ONLY CHANGE // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // 13.1.8 User-defined implicit conversions // // A user-defined implicit conversion consists of an optional standard implicit conversion, // followed by execution of a user-defined implicit conversion operator, followed by another // optional standard implicit conversion. The exact rules for evaluating user-defined // conversions are described in 13.4.3. if (0 == (flags & CONVERTTYPE.NOUDC)) { return(binder.bindUserDefinedConversion(exprSrc, typeSrc, typeDest, needsExprDest, out exprDest, true)); } // No conversion was found. return(false); }
//////////////////////////////////////////////////////////////////////////////// private bool MethodGroupReturnTypeInference(EXPR pSource, CType pType) { // SPEC: Otherwise, if E is a method group and T is a delegate CType or // SPEC: expression tree CType with parameter types T1...Tk and return // SPEC: CType Tb and overload resolution of E with the types T1...Tk // SPEC: yields a single method with return CType U then a lower-bound // SPEC: inference is made from U to Tb. if (!pSource.isMEMGRP()) { return false; } pType = pType.GetDelegateTypeOfPossibleExpression(); if (!pType.isDelegateType()) { return false; } AggregateType pDelegateType = pType.AsAggregateType(); CType pDelegateReturnType = pDelegateType.GetDelegateReturnType(GetSymbolLoader()); if (pDelegateReturnType == null) { return false; } if (pDelegateReturnType.IsVoidType()) { return false; } // At this point we are in the second phase; we know that all the input types are fixed. TypeArray pDelegateParameters = GetFixedDelegateParameters(pDelegateType); if (pDelegateParameters == null) { return false; } ArgInfos argInfo = new ArgInfos() { carg = pDelegateParameters.size, types = pDelegateParameters, fHasExprs = false, prgexpr = null }; var argsBinder = new ExpressionBinder.GroupToArgsBinder(_binder, 0/* flags */, pSource.asMEMGRP(), argInfo, null, false, pDelegateType); bool success = argsBinder.Bind(false); if (!success) { return false; } MethPropWithInst mwi = argsBinder.GetResultsOfBind().GetBestResult(); CType pMethodReturnType = GetTypeManager().SubstType(mwi.Meth().RetType, mwi.GetType(), mwi.TypeArgs); if (pMethodReturnType.IsVoidType()) { return false; } LowerBoundInference(pMethodReturnType, pDelegateReturnType); return true; }
//////////////////////////////////////////////////////////////////////////////// // A false return means not to process the expr any further - it's totally out // of place. For example - a method group or an anonymous method. internal bool checkLvalue(EXPR expr, CheckLvalueKind kind) { if (!expr.isOK()) return false; if (expr.isLvalue()) { if (expr.isPROP()) { CheckLvalueProp(expr.asPROP()); } markFieldAssigned(expr); return true; } switch (expr.kind) { case ExpressionKind.EK_PROP: if (kind == CheckLvalueKind.OutParameter) { // passing a property as ref or out ErrorContext.Error(ErrorCode.ERR_RefProperty); return true; } if (!expr.asPROP().mwtSet) { // Assigning to a property without a setter. // If we have // bool? b = true; (bool)b = false; // then this is realized immediately as // b.Value = false; // and no ExpressionKind.EK_CAST is generated. We'd rather not give a "you're writing // to a read-only property" error in the case where the property access // is not explicit in the source code. Fortunately in this case the // cast is still hanging around in the parse tree, so we can look for it. // POSSIBLE ERROR: It would be nice to also give this error for other situations // POSSIBLE ERROR: in which the user is attempting to assign to a value, such as // POSSIBLE ERROR: an explicit (bool)b.Value = false; // POSSIBLE ERROR: Unfortunately we cannot use this trick in that situation because // POSSIBLE ERROR: we've already discarded the OperatorKind.OP_CAST node. (This is an SyntaxKind.Dot). // SPEC VIOLATION: More generally: // SPEC VIOLATION: The spec states that the result of any cast is a value, not a // SPEC VIOLATION: variable. Unfortunately we do not correctly implement this // SPEC VIOLATION: and we probably should not start implementing it because this // SPEC VIOLATION: would be a breaking change. We currently discard "no op" casts // SPEC VIOLATION: very aggressively rather than generating an ExpressionKind.EK_CAST node. ErrorContext.Error(ErrorCode.ERR_AssgReadonlyProp, expr.asPROP().pwtSlot); return true; } break; case ExpressionKind.EK_ARRAYLENGTH: if (kind == CheckLvalueKind.OutParameter) { // passing a property as ref or out ErrorContext.Error(ErrorCode.ERR_RefProperty); } else { // Special case, the length property of an array ErrorContext.Error(ErrorCode.ERR_AssgReadonlyProp, GetSymbolLoader().getPredefinedMembers().GetProperty(PREDEFPROP.PP_ARRAY_LENGTH)); } return true; case ExpressionKind.EK_BOUNDLAMBDA: case ExpressionKind.EK_UNBOUNDLAMBDA: case ExpressionKind.EK_CONSTANT: ErrorContext.Error(GetStandardLvalueError(kind)); return false; case ExpressionKind.EK_MEMGRP: { ErrorCode err = (kind == CheckLvalueKind.OutParameter) ? ErrorCode.ERR_RefReadonlyLocalCause : ErrorCode.ERR_AssgReadonlyLocalCause; ErrorContext.Error(err, expr.asMEMGRP().name, new ErrArgIds(MessageID.MethodGroup)); return false; } default: break; } return !TryReportLvalueFailure(expr, kind); }