public ExprBoundLambda(CType type, Scope argumentScope) : base(ExpressionKind.BoundLambda, type) { Debug.Assert(type == null || type.isDelegateType()); Debug.Assert(argumentScope != null); ArgumentScope = argumentScope; }
/*************************************************************************************************** * * There exists an explicit conversion ... * From a generic delegate type S to generic delegate type T, provided all of the follow are true: * o Both types are constructed generic types of the same generic delegate type, D<X1,... Xk>.That is, * S is D<S1,... Sk> and T is D<T1,... Tk>. * o S is not compatible with or identical to T. * o If type parameter Xi is declared to be invariant then Si must be identical to Ti. * o If type parameter Xi is declared to be covariant ("out") then Si must be convertible * to Ti via an identify conversion, implicit reference conversion, or explicit reference conversion. * o If type parameter Xi is declared to be contravariant ("in") then either Si must be identical to Ti, * or Si and Ti must both be reference types. ***************************************************************************************************/ public static bool HasGenericDelegateExplicitReferenceConversion(SymbolLoader loader, CType pSource, CType pTarget) { if (!pSource.isDelegateType() || !pTarget.isDelegateType() || pSource.getAggregate() != pTarget.getAggregate() || loader.HasIdentityOrImplicitReferenceConversion(pSource, pTarget)) { return(false); } TypeArray pTypeParams = pSource.getAggregate().GetTypeVarsAll(); TypeArray pSourceArgs = ((AggregateType)pSource).GetTypeArgsAll(); TypeArray pTargetArgs = ((AggregateType)pTarget).GetTypeArgsAll(); Debug.Assert(pTypeParams.Count == pSourceArgs.Count); Debug.Assert(pTypeParams.Count == pTargetArgs.Count); for (int iParam = 0; iParam < pTypeParams.Count; ++iParam) { CType pSourceArg = pSourceArgs[iParam]; CType pTargetArg = pTargetArgs[iParam]; // If they're identical then this one is automatically good, so skip it. // If we have an error type, then we're in some fault tolerance. Let it through. if (pSourceArg == pTargetArg) { continue; } TypeParameterType pParam = (TypeParameterType)pTypeParams[iParam]; if (pParam.Invariant) { return(false); } if (pParam.Covariant) { if (!FExpRefConv(loader, pSourceArg, pTargetArg)) { return(false); } } else if (pParam.Contravariant) { if (!pSourceArg.IsRefType() || !pTargetArg.IsRefType()) { return(false); } } } return(true); }
private bool IsDelegateType(CType pSrcType, AggregateType pAggType) { CType pInstantiatedType = GetSymbolLoader().GetTypeManager().SubstType(pSrcType, pAggType, pAggType.GetTypeArgsAll()); return(pInstantiatedType.isDelegateType()); }
//////////////////////////////////////////////////////////////////////////////// // // Output types // private bool DoesOutputTypeContain(EXPR pSource, CType pDest, TypeParameterType pParam) { // SPEC: If E is a method group or an anonymous function and T is a delegate // SPEC: CType or expression tree CType then the return CType of T is an output CType // SPEC: of E with CType T. pDest = pDest.GetDelegateTypeOfPossibleExpression(); if (!pDest.isDelegateType()) { return false; } if (!pSource.isUNBOUNDLAMBDA() && !pSource.isMEMGRP()) { return false; } CType pDelegateReturn = pDest.AsAggregateType().GetDelegateReturnType(GetSymbolLoader()); if (pDelegateReturn == null) { return false; } return TypeManager.TypeContainsType(pDelegateReturn, pParam); }
//////////////////////////////////////////////////////////////////////////////// // // Input types // private bool DoesInputTypeContain(EXPR pSource, CType pDest, TypeParameterType pParam) { // SPEC: If E is a method group or an anonymous function and T is a delegate // SPEC: CType or expression tree CType then all the parameter types of T are // SPEC: input types of E with CType T. pDest = pDest.GetDelegateTypeOfPossibleExpression(); if (!pDest.isDelegateType()) { return false; // No input types. } if (!pSource.isUNBOUNDLAMBDA() && !pSource.isMEMGRP()) { return false; // No input types. } TypeArray pDelegateParameters = pDest.AsAggregateType().GetDelegateParameters(GetSymbolLoader()); if (pDelegateParameters == null) { return false; } return TypeManager.ParametersContainTyVar(pDelegateParameters, pParam); }
//////////////////////////////////////////////////////////////////////////////// private bool UpperBoundConstructedInference(CType pSource, CType pDest) { if (!pSource.IsAggregateType()) { return false; } AggregateType pConstructedSource = pSource.AsAggregateType(); TypeArray pSourceArgs = pConstructedSource.GetTypeArgsAll(); if (pSourceArgs.size == 0) { return false; } // SPEC: Otherwise, if V is a constructed CType C<V1...Vk> and U is // SPEC: C<U1...Uk> then an exact inference, // SPEC: lower bound inference or upper bound inference // SPEC: is made from each Ui to the corresponding Vi. if (pDest.IsAggregateType() && pConstructedSource.GetOwningAggregate() == pDest.AsAggregateType().GetOwningAggregate()) { if (pDest.isInterfaceType() || pDest.isDelegateType()) { UpperBoundTypeArgumentInference(pConstructedSource, pDest.AsAggregateType()); } else { ExactTypeArgumentInference(pConstructedSource, pDest.AsAggregateType()); } return true; } // SPEC: Otherwise, if U is a class CType C<U1...Uk> and V is a class CType which // SPEC: inherits directly or indirectly from C<V1...Vk> then an exact ... if (UpperBoundClassInference(pConstructedSource, pDest)) { return true; } // SPEC: Otherwise, if U is an interface CType C<U1...Uk> and V is a class CType // SPEC: or struct CType and there is a unique set V1...Vk such that V directly // SPEC: or indirectly implements C<V1...Vk> then an exact ... // SPEC: ... and U is an interface CType ... if (UpperBoundInterfaceInference(pConstructedSource, pDest)) { return true; } return false; }
//////////////////////////////////////////////////////////////////////////////// /* bool LowerBoundNullableInference(CType pSource, CType pDest) { // SPEC ISSUE: As noted above, the spec does not clearly call out how // SPEC ISSUE: to do CType inference to a nullable target. I propose the // SPEC ISSUE: following: // SPEC ISSUE: // SPEC ISSUE: Otherwise, if V is nullable CType V1? and U is a // SPEC ISSUE: non-nullable struct CType then an exact inference is made from U to V1. if (!pDest.IsNullableType() || !pSource.isStructType() || pSource.IsNullableType()) { return false; } ExactInference(pSource, pDest.AsNullableType().GetUnderlyingType()); return true; } * */ //////////////////////////////////////////////////////////////////////////////// private bool LowerBoundConstructedInference(CType pSource, CType pDest) { if (!pDest.IsAggregateType()) { return false; } AggregateType pConstructedDest = pDest.AsAggregateType(); TypeArray pDestArgs = pConstructedDest.GetTypeArgsAll(); if (pDestArgs.size == 0) { return false; } // SPEC: Otherwise, if V is a constructed class or struct CType C<V1...Vk> // SPEC: and U is C<U1...Uk> then an exact inference // SPEC: is made from each Ui to the corresponding Vi. // SPEC: Otherwise, if V is a constructed interface or delegate CType C<V1...Vk> // SPEC: and U is C<U1...Uk> then an exact inference, // SPEC: lower bound inference or upper bound inference // SPEC: is made from each Ui to the corresponding Vi. if (pSource.IsAggregateType() && pSource.AsAggregateType().GetOwningAggregate() == pConstructedDest.GetOwningAggregate()) { if (pSource.isInterfaceType() || pSource.isDelegateType()) { LowerBoundTypeArgumentInference(pSource.AsAggregateType(), pConstructedDest); } else { ExactTypeArgumentInference(pSource.AsAggregateType(), pConstructedDest); } return true; } // SPEC: Otherwise, if V is a class CType C<V1...Vk> and U is a class CType which // SPEC: inherits directly or indirectly from C<U1...Uk> then an exact ... // SPEC: ... and U is a CType parameter with effective base class ... // SPEC: ... and U is a CType parameter with an effective base class which inherits ... if (LowerBoundClassInference(pSource, pConstructedDest)) { return true; } // SPEC: Otherwise, if V is an interface CType C<V1...Vk> and U is a class CType // SPEC: or struct CType and there is a unique set U1...Uk such that U directly // SPEC: or indirectly implements C<U1...Uk> then an exact ... // SPEC: ... and U is an interface CType ... // SPEC: ... and U is a CType parameter ... if (LowerBoundInterfaceInference(pSource, pConstructedDest)) { return true; } 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; }
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // RUNTIME BINDER ONLY CHANGE // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! internal bool GetBestAccessibleType(CSemanticChecker semanticChecker, BindingContext bindingContext, CType typeSrc, out CType typeDst) { // This method implements the "best accessible type" algorithm for determining the type // of untyped arguments in the runtime binder. It is also used in method type inference // to fix type arguments to types that are accessible. // The new type is returned in an out parameter. The result will be true (and the out param // non-null) only when the algorithm could find a suitable accessible type. Debug.Assert(semanticChecker != null); Debug.Assert(bindingContext != null); Debug.Assert(typeSrc != null); typeDst = null; if (semanticChecker.CheckTypeAccess(typeSrc, bindingContext.ContextForMemberLookup())) { // If we already have an accessible type, then use it. This is the terminal point of the recursion. typeDst = typeSrc; return true; } // These guys have no accessibility concerns. Debug.Assert(!typeSrc.IsVoidType() && !typeSrc.IsErrorType() && !typeSrc.IsTypeParameterType()); if (typeSrc.IsParameterModifierType() || typeSrc.IsPointerType()) { // We cannot vary these. return false; } CType intermediateType; if ((typeSrc.isInterfaceType() || typeSrc.isDelegateType()) && TryVarianceAdjustmentToGetAccessibleType(semanticChecker, bindingContext, typeSrc.AsAggregateType(), out intermediateType)) { // If we have an interface or delegate type, then it can potentially be varied by its type arguments // to produce an accessible type, and if that's the case, then return that. // Example: IEnumerable<PrivateConcreteFoo> --> IEnumerable<PublicAbstractFoo> typeDst = intermediateType; Debug.Assert(semanticChecker.CheckTypeAccess(typeDst, bindingContext.ContextForMemberLookup())); return true; } if (typeSrc.IsArrayType() && TryArrayVarianceAdjustmentToGetAccessibleType(semanticChecker, bindingContext, typeSrc.AsArrayType(), out intermediateType)) { // Similarly to the interface and delegate case, arrays are covariant in their element type and // so we can potentially produce an array type that is accessible. // Example: PrivateConcreteFoo[] --> PublicAbstractFoo[] typeDst = intermediateType; Debug.Assert(semanticChecker.CheckTypeAccess(typeDst, bindingContext.ContextForMemberLookup())); return true; } if (typeSrc.IsNullableType()) { // We have an inaccessible nullable type, which means that the best we can do is System.ValueType. typeDst = this.GetOptPredefAgg(PredefinedType.PT_VALUE).getThisType(); Debug.Assert(semanticChecker.CheckTypeAccess(typeDst, bindingContext.ContextForMemberLookup())); return true; } if (typeSrc.IsArrayType()) { // We have an inaccessible array type for which we could not earlier find a better array type // with a covariant conversion, so the best we can do is System.Array. typeDst = this.GetReqPredefAgg(PredefinedType.PT_ARRAY).getThisType(); Debug.Assert(semanticChecker.CheckTypeAccess(typeDst, bindingContext.ContextForMemberLookup())); return true; } Debug.Assert(typeSrc.IsAggregateType()); if (typeSrc.IsAggregateType()) { // We have an AggregateType, so recurse on its base class. AggregateType aggType = typeSrc.AsAggregateType(); AggregateType baseType = aggType.GetBaseClass(); if (baseType == null) { // This happens with interfaces, for instance. But in that case, the // conversion to object does exist, is an implicit reference conversion, // and so we will use it. baseType = this.GetReqPredefAgg(PredefinedType.PT_OBJECT).getThisType(); } return GetBestAccessibleType(semanticChecker, bindingContext, baseType, out typeDst); } return false; }
/*************************************************************************************************** There exists an explicit conversion ... * From a generic delegate type S to generic delegate type T, provided all of the follow are true: o Both types are constructed generic types of the same generic delegate type, D<X1,... Xk>.That is, S is D<S1,... Sk> and T is D<T1,... Tk>. o S is not compatible with or identical to T. o If type parameter Xi is declared to be invariant then Si must be identical to Ti. o If type parameter Xi is declared to be covariant ("out") then Si must be convertible to Ti via an identify conversion, implicit reference conversion, or explicit reference conversion. o If type parameter Xi is declared to be contravariant ("in") then either Si must be identical to Ti, or Si and Ti must both be reference types. ***************************************************************************************************/ public static bool HasGenericDelegateExplicitReferenceConversion(SymbolLoader loader, CType pSource, CType pTarget) { if (!pSource.isDelegateType() || !pTarget.isDelegateType() || pSource.getAggregate() != pTarget.getAggregate() || loader.HasIdentityOrImplicitReferenceConversion(pSource, pTarget)) { return false; } TypeArray pTypeParams = pSource.getAggregate().GetTypeVarsAll(); TypeArray pSourceArgs = pSource.AsAggregateType().GetTypeArgsAll(); TypeArray pTargetArgs = pTarget.AsAggregateType().GetTypeArgsAll(); Debug.Assert(pTypeParams.size == pSourceArgs.size); Debug.Assert(pTypeParams.size == pTargetArgs.size); for (int iParam = 0; iParam < pTypeParams.size; ++iParam) { CType pSourceArg = pSourceArgs.Item(iParam); CType pTargetArg = pTargetArgs.Item(iParam); // If they're identical then this one is automatically good, so skip it. // If we have an error type, then we're in some fault tolerance. Let it through. if (pSourceArg == pTargetArg || pTargetArg.IsErrorType() || pSourceArg.IsErrorType()) { continue; } TypeParameterType pParam = pTypeParams.Item(iParam).AsTypeParameterType(); if (pParam.Invariant) { return false; } if (pParam.Covariant) { if (!FExpRefConv(loader, pSourceArg, pTargetArg)) { return false; } } else if (pParam.Contravariant) { if (!pSourceArg.IsRefType() || !pTargetArg.IsRefType()) { return false; } } } return true; }
private bool HasImplicitReferenceConversion(CType pSource, CType pDest) { Debug.Assert(pSource != null); Debug.Assert(pDest != null); Debug.Assert(!(pSource is TypeParameterType)); // The implicit reference conversions are: // * From any reference type to Object. if (pSource.IsRefType() && pDest.isPredefType(PredefinedType.PT_OBJECT)) { return(true); } if (pSource is AggregateType aggSource) { if (pDest is AggregateType aggDest) { switch (aggSource.GetOwningAggregate().AggKind()) { case AggKindEnum.Class: switch (aggDest.GetOwningAggregate().AggKind()) { case AggKindEnum.Class: // * From any class type S to any class type T provided S is derived from T. return(IsBaseClass(aggSource, aggDest)); case AggKindEnum.Interface: // ORIGINAL RULES: // // * From any class type S to any interface type T provided S implements T. // if (pSource.isClassType() && pDest.isInterfaceType() && IsBaseInterface(pSource, pDest)) // { // return true; // } // // * from any interface type S to any interface type T, provided S is derived from T. // if (pSource.isInterfaceType() && pDest.isInterfaceType() && IsBaseInterface(pSource, pDest)) // { // return true; // } // VARIANCE EXTENSIONS: // * From any class type S to any interface type T provided S implements an interface // convertible to T. // * From any interface type S to any interface type T provided S implements an interface // convertible to T. // * From any interface type S to any interface type T provided S is not T and S is // an interface convertible to T. return(HasAnyBaseInterfaceConversion(aggSource, aggDest)); } break; case AggKindEnum.Interface: if (aggDest.isInterfaceType()) { return(HasAnyBaseInterfaceConversion(aggSource, aggDest) || HasInterfaceConversion(aggSource, aggDest)); } break; case AggKindEnum.Delegate: // * From any delegate type to System.Delegate // // SPEC OMISSION: // // The spec should actually say // // * From any delegate type to System.Delegate // * From any delegate type to System.MulticastDelegate // * From any delegate type to any interface implemented by System.MulticastDelegate if (aggDest.isPredefType(PredefinedType.PT_MULTIDEL) || aggDest.isPredefType(PredefinedType.PT_DELEGATE) || IsBaseInterface( GetPredefindType(PredefinedType.PT_MULTIDEL), aggDest)) { return(true); } // VARIANCE EXTENSION: // * From any delegate type S to a delegate type T provided S is not T and // S is a delegate convertible to T return(pDest.isDelegateType() && HasDelegateConversion(aggSource, aggDest)); } } } else if (pSource is ArrayType arrSource) { // * From an array type S with an element type SE to an array type T with element type TE // provided that all of the following are true: // * S and T differ only in element type. In other words, S and T have the same number of dimensions. // * Both SE and TE are reference types. // * An implicit reference conversion exists from SE to TE. if (pDest is ArrayType arrDest) { return(HasCovariantArrayConversion(arrSource, arrDest)); } if (pDest is AggregateType aggDest) { // * From any array type to System.Array or any interface implemented by System.Array. if (aggDest.isPredefType(PredefinedType.PT_ARRAY) || IsBaseInterface(GetPredefindType(PredefinedType.PT_ARRAY), aggDest)) { return(true); } // * From a single-dimensional array type S[] to IList<T> and its base // interfaces, provided that there is an implicit identity or reference // conversion from S to T. return(HasArrayConversionToInterface(arrSource, pDest)); } } else if (pSource is NullType) { // * From the null literal to any reference type // NOTE: We extend the specification here. The C# 3.0 spec does not describe // a "null type". Rather, it says that the null literal is typeless, and is // convertible to any reference or nullable type. However, the C# 2.0 and 3.0 // implementations have a "null type" which some expressions other than the // null literal may have. (For example, (null??null), which is also an // extension to the specification.) return(pDest.IsRefType() || pDest is NullableType); } return(false); }
public bool HasImplicitReferenceConversion(CType pSource, CType pDest) { Debug.Assert(pSource != null); Debug.Assert(pDest != null); // The implicit reference conversions are: // * From any reference type to Object. if (pSource.IsRefType() && pDest.isPredefType(PredefinedType.PT_OBJECT)) { return true; } // * From any class type S to any class type T provided S is derived from T. if (pSource.isClassType() && pDest.isClassType() && IsBaseClass(pSource, pDest)) { return true; } // ORIGINAL RULES: // // * From any class type S to any interface type T provided S implements T. // if (pSource.isClassType() && pDest.isInterfaceType() && IsBaseInterface(pSource, pDest)) // { // return true; // } // // * from any interface type S to any interface type T, provided S is derived from T. // if (pSource.isInterfaceType() && pDest.isInterfaceType() && IsBaseInterface(pSource, pDest)) // { // return true; // } // VARIANCE EXTENSIONS: // * From any class type S to any interface type T provided S implements an interface // convertible to T. // * From any interface type S to any interface type T provided S implements an interface // convertible to T. // * From any interface type S to any interface type T provided S is not T and S is // an interface convertible to T. if (pSource.isClassType() && pDest.isInterfaceType() && HasAnyBaseInterfaceConversion(pSource, pDest)) { return true; } if (pSource.isInterfaceType() && pDest.isInterfaceType() && HasAnyBaseInterfaceConversion(pSource, pDest)) { return true; } if (pSource.isInterfaceType() && pDest.isInterfaceType() && pSource != pDest && HasInterfaceConversion(pSource.AsAggregateType(), pDest.AsAggregateType())) { return true; } // * From an array type S with an element type SE to an array type T with element type TE // provided that all of the following are true: // * S and T differ only in element type. In other words, S and T have the same number of dimensions. // * Both SE and TE are reference types. // * An implicit reference conversion exists from SE to TE. if (pSource.IsArrayType() && pDest.IsArrayType() && HasCovariantArrayConversion(pSource.AsArrayType(), pDest.AsArrayType())) { return true; } // * From any array type to System.Array or any interface implemented by System.Array. if (pSource.IsArrayType() && (pDest.isPredefType(PredefinedType.PT_ARRAY) || IsBaseInterface(GetReqPredefType(PredefinedType.PT_ARRAY, false), pDest))) { return true; } // * From a single-dimensional array type S[] to IList<T> and its base // interfaces, provided that there is an implicit identity or reference // conversion from S to T. if (pSource.IsArrayType() && HasArrayConversionToInterface(pSource.AsArrayType(), pDest)) { return true; } // * From any delegate type to System.Delegate // // SPEC OMISSION: // // The spec should actually say // // * From any delegate type to System.Delegate // * From any delegate type to System.MulticastDelegate // * From any delegate type to any interface implemented by System.MulticastDelegate if (pSource.isDelegateType() && (pDest.isPredefType(PredefinedType.PT_MULTIDEL) || pDest.isPredefType(PredefinedType.PT_DELEGATE) || IsBaseInterface(GetReqPredefType(PredefinedType.PT_MULTIDEL, false), pDest))) { return true; } // VARIANCE EXTENSION: // * From any delegate type S to a delegate type T provided S is not T and // S is a delegate convertible to T if (pSource.isDelegateType() && pDest.isDelegateType() && HasDelegateConversion(pSource.AsAggregateType(), pDest.AsAggregateType())) { return true; } // * From the null literal to any reference type // NOTE: We extend the specification here. The C# 3.0 spec does not describe // a "null type". Rather, it says that the null literal is typeless, and is // convertible to any reference or nullable type. However, the C# 2.0 and 3.0 // implementations have a "null type" which some expressions other than the // null literal may have. (For example, (null??null), which is also an // extension to the specification.) if (pSource.IsNullType() && pDest.IsRefType()) { return true; } if (pSource.IsNullType() && pDest.IsNullableType()) { return true; } // * Implicit conversions involving type parameters that are known to be reference types. if (pSource.IsTypeParameterType() && HasImplicitReferenceTypeParameterConversion(pSource.AsTypeParameterType(), pDest)) { return true; } return false; }
public ExprBoundLambda(CType type) : base(ExpressionKind.BoundLambda, type) { Debug.Assert(type == null || type.isDelegateType()); }