// It would be nice to make this a virtual method on typeSym. public AggregateType GetAggTypeSym(CType typeSym) { Debug.Assert(typeSym != null); Debug.Assert(typeSym.IsAggregateType() || typeSym.IsTypeParameterType() || typeSym.IsArrayType() || typeSym.IsNullableType()); switch (typeSym.GetTypeKind()) { case TypeKind.TK_AggregateType: return(typeSym.AsAggregateType()); case TypeKind.TK_ArrayType: return(GetReqPredefType(PredefinedType.PT_ARRAY)); case TypeKind.TK_TypeParameterType: return(typeSym.AsTypeParameterType().GetEffectiveBaseClass()); case TypeKind.TK_NullableType: return(typeSym.AsNullableType().GetAts(ErrorContext)); } Debug.Assert(false, "Bad typeSym!"); return(null); }
public virtual ACCESSERROR CheckAccess2(Symbol symCheck, AggregateType atsCheck, Symbol symWhere, CType typeThru) { Debug.Assert(symCheck != null); Debug.Assert(atsCheck == null || symCheck.parent == atsCheck.getAggregate()); Debug.Assert(typeThru == null || typeThru.IsAggregateType() || typeThru.IsTypeParameterType() || typeThru.IsArrayType() || typeThru.IsNullableType() || typeThru.IsErrorType()); #if DEBUG switch (symCheck.getKind()) { default: break; case SYMKIND.SK_MethodSymbol: case SYMKIND.SK_PropertySymbol: case SYMKIND.SK_FieldSymbol: case SYMKIND.SK_EventSymbol: Debug.Assert(atsCheck != null); break; } #endif // DEBUG ACCESSERROR error = CheckAccessCore(symCheck, atsCheck, symWhere, typeThru); if (ACCESSERROR.ACCESSERROR_NOERROR != error) { return error; } // Check the accessibility of the return CType. CType CType = symCheck.getType(); if (CType == null) { return ACCESSERROR.ACCESSERROR_NOERROR; } // For members of AGGSYMs, atsCheck should always be specified! Debug.Assert(atsCheck != null); if (atsCheck.getAggregate().IsSource()) { // We already check the "at least as accessible as" rules. // Does this always work for generics? // Could we get a bad CType argument in typeThru? // Maybe call CheckTypeAccess on typeThru? return ACCESSERROR.ACCESSERROR_NOERROR; } // Substitute on the CType. if (atsCheck.GetTypeArgsAll().size > 0) { CType = SymbolLoader.GetTypeManager().SubstType(CType, atsCheck); } return CheckTypeAccess(CType, symWhere) ? ACCESSERROR.ACCESSERROR_NOERROR : ACCESSERROR.ACCESSERROR_NOACCESS; }
public virtual ACCESSERROR CheckAccess2(Symbol symCheck, AggregateType atsCheck, Symbol symWhere, CType typeThru) { Debug.Assert(symCheck != null); Debug.Assert(atsCheck == null || symCheck.parent == atsCheck.getAggregate()); Debug.Assert(typeThru == null || typeThru.IsAggregateType() || typeThru.IsTypeParameterType() || typeThru.IsArrayType() || typeThru.IsNullableType() || typeThru.IsErrorType()); #if DEBUG switch (symCheck.getKind()) { default: break; case SYMKIND.SK_MethodSymbol: case SYMKIND.SK_PropertySymbol: case SYMKIND.SK_FieldSymbol: case SYMKIND.SK_EventSymbol: Debug.Assert(atsCheck != null); break; } #endif // DEBUG ACCESSERROR error = CheckAccessCore(symCheck, atsCheck, symWhere, typeThru); if (ACCESSERROR.ACCESSERROR_NOERROR != error) { return(error); } // Check the accessibility of the return CType. CType CType = symCheck.getType(); if (CType == null) { return(ACCESSERROR.ACCESSERROR_NOERROR); } // For members of AGGSYMs, atsCheck should always be specified! Debug.Assert(atsCheck != null); if (atsCheck.getAggregate().IsSource()) { // We already check the "at least as accessible as" rules. // Does this always work for generics? // Could we get a bad CType argument in typeThru? // Maybe call CheckTypeAccess on typeThru? return(ACCESSERROR.ACCESSERROR_NOERROR); } // Substitute on the CType. if (atsCheck.GetTypeArgsAll().size > 0) { CType = SymbolLoader.GetTypeManager().SubstType(CType, atsCheck); } return(CheckTypeAccess(CType, symWhere) ? ACCESSERROR.ACCESSERROR_NOERROR : ACCESSERROR.ACCESSERROR_NOACCESS); }
public bool HasImplicitBoxingConversion(CType pSource, CType pDest) { Debug.Assert(pSource != null); Debug.Assert(pDest != null); // Certain type parameter conversions are classified as boxing conversions. if (pSource.IsTypeParameterType() && HasImplicitBoxingTypeParameterConversion(pSource.AsTypeParameterType(), pDest)) { return(true); } // The rest of the boxing conversions only operate when going from a value type // to a reference type. if (!pSource.IsValType() || !pDest.IsRefType()) { return(false); } // A boxing conversion exists from a nullable type to a reference type // if and only if a boxing conversion exists from the underlying type. if (pSource.IsNullableType()) { return(HasImplicitBoxingConversion(pSource.AsNullableType().GetUnderlyingType(), pDest)); } // A boxing conversion exists from any non-nullable value type to object, // to System.ValueType, and to any interface type implemented by the // non-nullable value type. Furthermore, an enum type can be converted // to the type System.Enum. // We set the base class of the structs to System.ValueType, System.Enum, etc, // so we can just check here. if (IsBaseClass(pSource, pDest)) { return(true); } if (HasAnyBaseInterfaceConversion(pSource, pDest)) { return(true); } return(false); }
private bool IsBaseClass(CType pDerived, CType pBase) { Debug.Assert(pDerived != null); Debug.Assert(pBase != null); // A base class has got to be a class. The derived type might be a struct. if (!pBase.isClassType()) { return(false); } if (pDerived.IsNullableType()) { pDerived = pDerived.AsNullableType().GetAts(ErrorContext); if (pDerived == null) { return(false); } } if (!pDerived.IsAggregateType()) { return(false); } AggregateType atsDer = pDerived.AsAggregateType(); AggregateType atsBase = pBase.AsAggregateType(); AggregateType atsCur = atsDer.GetBaseClass(); while (atsCur != null) { if (atsCur == atsBase) { return(true); } atsCur = atsCur.GetBaseClass(); } return(false); }
private 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); }
// // SymbolLoader forwarders (end) ///////////////////////////////////////////////////////////////////////////////// // // Utility methods // protected ACCESSERROR CheckAccessCore(Symbol symCheck, AggregateType atsCheck, Symbol symWhere, CType typeThru) { Debug.Assert(symCheck != null); Debug.Assert(atsCheck == null || symCheck.parent == atsCheck.getAggregate()); Debug.Assert(typeThru == null || typeThru.IsAggregateType() || typeThru.IsTypeParameterType() || typeThru.IsArrayType() || typeThru.IsNullableType() || typeThru.IsErrorType()); switch (symCheck.GetAccess()) { default: throw Error.InternalCompilerError(); //return ACCESSERROR.ACCESSERROR_NOACCESS; case ACCESS.ACC_UNKNOWN: return ACCESSERROR.ACCESSERROR_NOACCESS; case ACCESS.ACC_PUBLIC: return ACCESSERROR.ACCESSERROR_NOERROR; case ACCESS.ACC_PRIVATE: case ACCESS.ACC_PROTECTED: if (symWhere == null) { return ACCESSERROR.ACCESSERROR_NOACCESS; } break; case ACCESS.ACC_INTERNAL: case ACCESS.ACC_INTERNALPROTECTED: // Check internal, then protected. if (symWhere == null) { return ACCESSERROR.ACCESSERROR_NOACCESS; } if (symWhere.SameAssemOrFriend(symCheck)) { return ACCESSERROR.ACCESSERROR_NOERROR; } if (symCheck.GetAccess() == ACCESS.ACC_INTERNAL) { return ACCESSERROR.ACCESSERROR_NOACCESS; } break; } // Should always have atsCheck for private and protected access check. // We currently don't need it since access doesn't respect instantiation. // We just use symWhere.parent.AsAggregateSymbol() instead. AggregateSymbol aggCheck = symCheck.parent.AsAggregateSymbol(); // Find the inner-most enclosing AggregateSymbol. AggregateSymbol aggWhere = null; for (Symbol symT = symWhere; symT != null; symT = symT.parent) { if (symT.IsAggregateSymbol()) { aggWhere = symT.AsAggregateSymbol(); break; } if (symT.IsAggregateDeclaration()) { aggWhere = symT.AsAggregateDeclaration().Agg(); break; } } if (aggWhere == null) { return ACCESSERROR.ACCESSERROR_NOACCESS; } // First check for private access. for (AggregateSymbol agg = aggWhere; agg != null; agg = agg.GetOuterAgg()) { if (agg == aggCheck) { return ACCESSERROR.ACCESSERROR_NOERROR; } } if (symCheck.GetAccess() == ACCESS.ACC_PRIVATE) { return ACCESSERROR.ACCESSERROR_NOACCESS; } // Handle the protected case - which is the only real complicated one. Debug.Assert(symCheck.GetAccess() == ACCESS.ACC_PROTECTED || symCheck.GetAccess() == ACCESS.ACC_INTERNALPROTECTED); // Check if symCheck is in aggWhere or a base of aggWhere, // or in an outer agg of aggWhere or a base of an outer agg of aggWhere. AggregateType atsThru = null; if (typeThru != null && !symCheck.isStatic) { atsThru = SymbolLoader.GetAggTypeSym(typeThru); } // Look for aggCheck among the base classes of aggWhere and outer aggs. bool found = false; for (AggregateSymbol agg = aggWhere; agg != null; agg = agg.GetOuterAgg()) { Debug.Assert(agg != aggCheck); // We checked for this above. // Look for aggCheck among the base classes of agg. if (agg.FindBaseAgg(aggCheck)) { found = true; // aggCheck is a base class of agg. Check atsThru. // For non-static protected access to be legal, atsThru must be an instantiation of // agg or a CType derived from an instantiation of agg. In this case // all that matters is that agg is in the base AggregateSymbol chain of atsThru. The // actual AGGTYPESYMs involved don't matter. if (atsThru == null || atsThru.getAggregate().FindBaseAgg(agg)) { return ACCESSERROR.ACCESSERROR_NOERROR; } } } // the CType in which the method is being called has no relationship with the // CType on which the method is defined surely this is NOACCESS and not NOACCESSTHRU if (found == false) return ACCESSERROR.ACCESSERROR_NOACCESS; return (atsThru == null) ? ACCESSERROR.ACCESSERROR_NOACCESS : ACCESSERROR.ACCESSERROR_NOACCESSTHRU; }
/* * 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); }
/*************************************************************************************************** Determines whether there is a wrapping conversion from typeSrc to typeDst 13.7 Conversions involving nullable types The following terms are used in the subsequent sections: * The term wrapping denotes the process of packaging a value, of type T, in an instance of type T?. A value x of type T is wrapped to type T? by evaluating the expression new T?(x). ***************************************************************************************************/ public static bool FWrappingConv(CType typeSrc, CType typeDst) { return typeDst.IsNullableType() && typeSrc == typeDst.AsNullableType().GetUnderlyingType(); }
// Check the constraints of any type arguments in the given Type. public static bool CheckConstraints(CSemanticChecker checker, ErrorHandling errHandling, CType type, CheckConstraintsFlags flags) { type = type.GetNakedType(false); if (type.IsNullableType()) { CType typeT = type.AsNullableType().GetAts(checker.GetErrorContext()); if (typeT != null) type = typeT; else type = type.GetNakedType(true); } if (!type.IsAggregateType()) return true; AggregateType ats = type.AsAggregateType(); if (ats.GetTypeArgsAll().size == 0) { // Common case: there are no type vars, so there are no constraints. ats.fConstraintsChecked = true; ats.fConstraintError = false; return true; } if (ats.fConstraintsChecked) { // Already checked. if (!ats.fConstraintError || (flags & CheckConstraintsFlags.NoDupErrors) != 0) { // No errors or no need to report errors again. return !ats.fConstraintError; } } TypeArray typeVars = ats.getAggregate().GetTypeVars(); TypeArray typeArgsThis = ats.GetTypeArgsThis(); TypeArray typeArgsAll = ats.GetTypeArgsAll(); Debug.Assert(typeVars.size == typeArgsThis.size); if (!ats.fConstraintsChecked) { ats.fConstraintsChecked = true; ats.fConstraintError = false; } // Check the outer type first. If CheckConstraintsFlags.Outer is not specified and the // outer type has already been checked then don't bother checking it. if (ats.outerType != null && ((flags & CheckConstraintsFlags.Outer) != 0 || !ats.outerType.fConstraintsChecked)) { CheckConstraints(checker, errHandling, ats.outerType, flags); ats.fConstraintError |= ats.outerType.fConstraintError; } if (typeVars.size > 0) ats.fConstraintError |= !CheckConstraintsCore(checker, errHandling, ats.getAggregate(), typeVars, typeArgsThis, typeArgsAll, null, (flags & CheckConstraintsFlags.NoErrors)); // Now check type args themselves. for (int i = 0; i < typeArgsThis.size; i++) { CType arg = typeArgsThis.Item(i).GetNakedType(true); if (arg.IsAggregateType() && !arg.AsAggregateType().fConstraintsChecked) { CheckConstraints(checker, errHandling, arg.AsAggregateType(), flags | CheckConstraintsFlags.Outer); if (arg.AsAggregateType().fConstraintError) ats.fConstraintError = true; } } return !ats.fConstraintError; }
/* 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; }
///////////////////////////////////////////////////////////////////////////////// private void LiftArgument(EXPR pArgument, CType pParameterType, bool bConvertBeforeLift, out EXPR ppLiftedArgument, out EXPR ppNonLiftedArgument) { EXPR pLiftedArgument = mustConvert(pArgument, pParameterType); if (pLiftedArgument != pArgument) { MarkAsIntermediateConversion(pLiftedArgument); } EXPR pNonLiftedArgument = pArgument; if (pParameterType.IsNullableType()) { if (pNonLiftedArgument.isNull()) { pNonLiftedArgument = mustCast(pNonLiftedArgument, pParameterType); } pNonLiftedArgument = mustCast(pNonLiftedArgument, pParameterType.AsNullableType().GetUnderlyingType()); if (bConvertBeforeLift) { MarkAsIntermediateConversion(pNonLiftedArgument); } } else { pNonLiftedArgument = pLiftedArgument; } ppLiftedArgument = pLiftedArgument; ppNonLiftedArgument = pNonLiftedArgument; }
public bool HasImplicitBoxingConversion(CType pSource, CType pDest) { Debug.Assert(pSource != null); Debug.Assert(pDest != null); // Certain type parameter conversions are classified as boxing conversions. if (pSource.IsTypeParameterType() && HasImplicitBoxingTypeParameterConversion(pSource.AsTypeParameterType(), pDest)) { return true; } // The rest of the boxing conversions only operate when going from a value type // to a reference type. if (!pSource.IsValType() || !pDest.IsRefType()) { return false; } // A boxing conversion exists from a nullable type to a reference type // if and only if a boxing conversion exists from the underlying type. if (pSource.IsNullableType()) { return HasImplicitBoxingConversion(pSource.AsNullableType().GetUnderlyingType(), pDest); } // A boxing conversion exists from any non-nullable value type to object, // to System.ValueType, and to any interface type implemented by the // non-nullable value type. Furthermore, an enum type can be converted // to the type System.Enum. // We set the base class of the structs to System.ValueType, System.Enum, etc, // so we can just check here. if (IsBaseClass(pSource, pDest)) { return true; } if (HasAnyBaseInterfaceConversion(pSource, pDest)) { return true; } 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 bool IsBaseClass(CType pDerived, CType pBase) { Debug.Assert(pDerived != null); Debug.Assert(pBase != null); // A base class has got to be a class. The derived type might be a struct. if (!pBase.isClassType()) { return false; } if (pDerived.IsNullableType()) { pDerived = pDerived.AsNullableType().GetAts(ErrorContext); if (pDerived == null) { return false; } } if (!pDerived.IsAggregateType()) { return false; } AggregateType atsDer = pDerived.AsAggregateType(); AggregateType atsBase = pBase.AsAggregateType(); AggregateType atsCur = atsDer.GetBaseClass(); while (atsCur != null) { if (atsCur == atsBase) { return true; } atsCur = atsCur.GetBaseClass(); } return false; }
// It would be nice to make this a virtual method on typeSym. public AggregateType GetAggTypeSym(CType typeSym) { Debug.Assert(typeSym != null); Debug.Assert(typeSym.IsAggregateType() || typeSym.IsTypeParameterType() || typeSym.IsArrayType() || typeSym.IsNullableType()); switch (typeSym.GetTypeKind()) { case TypeKind.TK_AggregateType: return typeSym.AsAggregateType(); case TypeKind.TK_ArrayType: return GetReqPredefType(PredefinedType.PT_ARRAY); case TypeKind.TK_TypeParameterType: return typeSym.AsTypeParameterType().GetEffectiveBaseClass(); case TypeKind.TK_NullableType: return typeSym.AsNullableType().GetAts(ErrorContext); } Debug.Assert(false, "Bad typeSym!"); return null; }
public bool IsNonNullableValueType() { return((_constraints & SpecCons.Val) > 0 || _bHasValBound && !_pDeducedBaseClass.IsNullableType()); }
private static bool CheckSingleConstraint(CSemanticChecker checker, ErrorHandling errHandling, Symbol symErr, TypeParameterType var, CType arg, TypeArray typeArgsCls, TypeArray typeArgsMeth, CheckConstraintsFlags flags) { bool fReportErrors = 0 == (flags & CheckConstraintsFlags.NoErrors); if (arg.IsOpenTypePlaceholderType()) { return true; } if (arg.IsErrorType()) { // Error should have been reported previously. return false; } if (checker.CheckBogus(arg)) { if (fReportErrors) { errHandling.ErrorRef(ErrorCode.ERR_BogusType, arg); } return false; } if (arg.IsPointerType() || arg.isSpecialByRefType()) { if (fReportErrors) { errHandling.Error(ErrorCode.ERR_BadTypeArgument, arg); } return false; } if (arg.isStaticClass()) { if (fReportErrors) { checker.ReportStaticClassError(null, arg, ErrorCode.ERR_GenericArgIsStaticClass); } return false; } bool fError = false; if (var.HasRefConstraint() && !arg.IsRefType()) { if (fReportErrors) { errHandling.ErrorRef(ErrorCode.ERR_RefConstraintNotSatisfied, symErr, new ErrArgNoRef(var), arg); } fError = true; } TypeArray bnds = checker.GetSymbolLoader().GetTypeManager().SubstTypeArray(var.GetBounds(), typeArgsCls, typeArgsMeth); int itypeMin = 0; if (var.HasValConstraint()) { // If we have a type variable that is constrained to a value type, then we // want to check if its a nullable type, so that we can report the // constraint error below. In order to do this however, we need to check // that either the type arg is not a value type, or it is a nullable type. // // To check whether or not its a nullable type, we need to get the resolved // bound from the type argument and check against that. bool bIsValueType = arg.IsValType(); bool bIsNullable = arg.IsNullableType(); if (bIsValueType && arg.IsTypeParameterType()) { TypeArray pArgBnds = arg.AsTypeParameterType().GetBounds(); if (pArgBnds.size > 0) { bIsNullable = pArgBnds.Item(0).IsNullableType(); } } if (!bIsValueType || bIsNullable) { if (fReportErrors) { errHandling.ErrorRef(ErrorCode.ERR_ValConstraintNotSatisfied, symErr, new ErrArgNoRef(var), arg); } fError = true; } // Since FValCon() is set it is redundant to check System.ValueType as well. if (bnds.size != 0 && bnds.Item(0).isPredefType(PredefinedType.PT_VALUE)) { itypeMin = 1; } } for (int j = itypeMin; j < bnds.size; j++) { CType typeBnd = bnds.Item(j); if (!SatisfiesBound(checker, arg, typeBnd)) { if (fReportErrors) { // The bound isn't satisfied because of a constaint type. Explain to the user why not. // There are 4 main cases, based on the type of the supplied type argument: // - reference type, or type parameter known to be a reference type // - nullable type, from which there is a boxing conversion to the constraint type(see below for details) // - type varaiable // - value type // These cases are broken out because: a) The sets of conversions which can be used // for constraint satisfaction is different based on the type argument supplied, // and b) Nullable is one funky type, and user's can use all the help they can get // when using it. ErrorCode error; if (arg.IsRefType()) { // A reference type can only satisfy bounds to types // to which they have an implicit reference conversion error = ErrorCode.ERR_GenericConstraintNotSatisfiedRefType; } else if (arg.IsNullableType() && checker.GetSymbolLoader().HasBaseConversion(arg.AsNullableType().GetUnderlyingType(), typeBnd)) // This is inlining FBoxingConv { // nullable types do not satisfy bounds to every type that they are boxable to // They only satisfy bounds of object and ValueType if (typeBnd.isPredefType(PredefinedType.PT_ENUM) || arg.AsNullableType().GetUnderlyingType() == typeBnd) { // Nullable types don't satisfy bounds of EnumType, or the underlying type of the enum // even though the conversion from Nullable to these types is a boxing conversion // This is a rare case, because these bounds can never be directly stated ... // These bounds can only occur when one type paramter is constrained to a second type parameter // and the second type parameter is instantiated with Enum or the underlying type of the first type // parameter error = ErrorCode.ERR_GenericConstraintNotSatisfiedNullableEnum; } else { // Nullable types don't satisfy the bounds of any interface type // even when there is a boxing conversion from the Nullable type to // the interface type. This will be a relatively common scenario // so we cal it out separately from the previous case. Debug.Assert(typeBnd.isInterfaceType()); error = ErrorCode.ERR_GenericConstraintNotSatisfiedNullableInterface; } } else if (arg.IsTypeParameterType()) { // Type variables can satisfy bounds through boxing and type variable conversions Debug.Assert(!arg.IsRefType()); error = ErrorCode.ERR_GenericConstraintNotSatisfiedTyVar; } else { // Value types can only satisfy bounds through boxing conversions. // Note that the exceptional case of Nullable types and boxing is handled above. error = ErrorCode.ERR_GenericConstraintNotSatisfiedValType; } errHandling.Error(error, new ErrArgRef(symErr), new ErrArg(typeBnd, ErrArgFlags.Unique), var, new ErrArgRef(arg, ErrArgFlags.Unique)); } fError = true; } } // Check the newable constraint. if (!var.HasNewConstraint() || arg.IsValType()) { return !fError; } if (arg.isClassType()) { AggregateSymbol agg = arg.AsAggregateType().getAggregate(); // Due to late binding nature of IDE created symbols, the AggregateSymbol might not // have all the information necessary yet, if it is not fully bound. // by calling LookupAggMember, it will ensure that we will update all the // information necessary at least for the given method. checker.GetSymbolLoader().LookupAggMember(checker.GetNameManager().GetPredefName(PredefinedName.PN_CTOR), agg, symbmask_t.MASK_ALL); if (agg.HasPubNoArgCtor() && !agg.IsAbstract()) { return !fError; } } else if (arg.IsTypeParameterType() && arg.AsTypeParameterType().HasNewConstraint()) { return !fError; } if (fReportErrors) { errHandling.ErrorRef(ErrorCode.ERR_NewConstraintNotSatisfied, symErr, new ErrArgNoRef(var), arg); } return false; }
/*************************************************************************************************** * Determines whether there is a wrapping conversion from typeSrc to typeDst * * 13.7 Conversions involving nullable types * * The following terms are used in the subsequent sections: * The term wrapping denotes the process of packaging a value, of type T, in an instance of type T?. * A value x of type T is wrapped to type T? by evaluating the expression new T?(x). ***************************************************************************************************/ public static bool FWrappingConv(CType typeSrc, CType typeDst) { return(typeDst.IsNullableType() && typeSrc == typeDst.AsNullableType().GetUnderlyingType()); }
///////////////////////////////////////////////////////////////////////////////// private static EXPR GenerateOptionalArgument( SymbolLoader symbolLoader, ExprFactory exprFactory, MethodOrPropertySymbol methprop, CType type, int index) { CType pParamType = type; CType pRawParamType = type.IsNullableType() ? type.AsNullableType().GetUnderlyingType() : type; EXPR optionalArgument = null; if (methprop.HasDefaultParameterValue(index)) { CType pConstValType = methprop.GetDefaultParameterValueConstValType(index); CONSTVAL cv = methprop.GetDefaultParameterValue(index); if (pConstValType.isPredefType(PredefinedType.PT_DATETIME) && (pRawParamType.isPredefType(PredefinedType.PT_DATETIME) || pRawParamType.isPredefType(PredefinedType.PT_OBJECT) || pRawParamType.isPredefType(PredefinedType.PT_VALUE))) { // This is the specific case where we want to create a DateTime // but the constval that stores it is a long. AggregateType dateTimeType = symbolLoader.GetReqPredefType(PredefinedType.PT_DATETIME); optionalArgument = exprFactory.CreateConstant(dateTimeType, new CONSTVAL(DateTime.FromBinary(cv.longVal))); } else if (pConstValType.isSimpleOrEnumOrString()) { // In this case, the constval is a simple type (all the numerics, including // decimal), or an enum or a string. This covers all the substantial values, // and everything else that can be encoded is just null or default(something). // For enum parameters, we create a constant of the enum type. For everything // else, we create the appropriate constant. if (pRawParamType.isEnumType() && pConstValType == pRawParamType.underlyingType()) { optionalArgument = exprFactory.CreateConstant(pRawParamType, cv); } else { optionalArgument = exprFactory.CreateConstant(pConstValType, cv); } } else if ((pParamType.IsRefType() || pParamType.IsNullableType()) && cv.IsNullRef()) { // We have an "= null" default value with a reference type or a nullable type. optionalArgument = exprFactory.CreateNull(); } else { // We have a default value that is encoded as a nullref, and that nullref is // interpreted as default(something). For instance, the pParamType could be // a type parameter type or a non-simple value type. optionalArgument = exprFactory.CreateZeroInit(pParamType); } } else { // There was no default parameter specified, so generally use default(T), // except for some cases when the parameter type in metatdata is object. if (pParamType.isPredefType(PredefinedType.PT_OBJECT)) { if (methprop.MarshalAsObject(index)) { // For [opt] parameters of type object, if we have marshal(iunknown), // marshal(idispatch), or marshal(interface), then we emit a null. optionalArgument = exprFactory.CreateNull(); } else { // Otherwise, we generate Type.Missing AggregateSymbol agg = symbolLoader.GetOptPredefAgg(PredefinedType.PT_MISSING); Name name = symbolLoader.GetNameManager().GetPredefinedName(PredefinedName.PN_CAP_VALUE); FieldSymbol field = symbolLoader.LookupAggMember(name, agg, symbmask_t.MASK_FieldSymbol).AsFieldSymbol(); FieldWithType fwt = new FieldWithType(field, agg.getThisType()); EXPRFIELD exprField = exprFactory.CreateField(0, agg.getThisType(), null, 0, fwt, null); if (agg.getThisType() != type) { optionalArgument = exprFactory.CreateCast(0, type, exprField); } else { optionalArgument = exprField; } } } else { // Every type aside from object that doesn't have a default value gets // its default value. optionalArgument = exprFactory.CreateZeroInit(pParamType); } } Debug.Assert(optionalArgument != null); optionalArgument.IsOptionalArgument = true; return optionalArgument; }
//////////////////////////////////////////////////////////////////////////////// // For a base call we need to remap from the virtual to the specific override // to invoke. This is also used to map a virtual on pObject (like ToString) to // the specific override when the pObject is a simple type (int, bool, char, // etc). In these cases it is safe to assume that any override won't later be // removed.... We start searching from "typeObj" up the superclass hierarchy // until we find a method with an exact signature match. public static void RemapToOverride(SymbolLoader symbolLoader, SymWithType pswt, CType typeObj) { // For a property/indexer we remap the accessors, not the property/indexer. // Since every event has both accessors we remap the event instead of the accessors. Debug.Assert(pswt && (pswt.Sym.IsMethodSymbol() || pswt.Sym.IsEventSymbol() || pswt.Sym.IsMethodOrPropertySymbol())); Debug.Assert(typeObj != null); // Don't remap static or interface methods. if (typeObj.IsNullableType()) { typeObj = typeObj.AsNullableType().GetAts(symbolLoader.GetErrorContext()); if (typeObj == null) { VSFAIL("Why did GetAts return null?"); return; } } // Don't remap non-virtual members if (!typeObj.IsAggregateType() || typeObj.isInterfaceType() || !pswt.Sym.IsVirtual()) { return; } symbmask_t mask = pswt.Sym.mask(); AggregateType atsObj = typeObj.AsAggregateType(); // Search for an override version of the method. while (atsObj != null && atsObj.getAggregate() != pswt.Sym.parent) { for (Symbol symT = symbolLoader.LookupAggMember(pswt.Sym.name, atsObj.getAggregate(), mask); symT != null; symT = symbolLoader.LookupNextSym(symT, atsObj.getAggregate(), mask)) { if (symT.IsOverride() && (symT.SymBaseVirtual() == pswt.Sym || symT.SymBaseVirtual() == pswt.Sym.SymBaseVirtual())) { pswt.Set(symT, atsObj); return; } } atsObj = atsObj.GetBaseClass(); } }
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // 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; }
/* * BindExplicitConversion * * This is a complex routine with complex parameter. 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 explicit conversions. * * Note that this function calls BindImplicitConversion first, so the main * logic is only concerned with conversions that can be made explicitly, but * not implicitly. */ public bool Bind() { // To test for a standard conversion, call canConvert(exprSrc, typeDest, STANDARDANDCONVERTTYPE.NOUDC) and // canConvert(typeDest, typeSrc, STANDARDANDCONVERTTYPE.NOUDC). Debug.Assert((_flags & CONVERTTYPE.STANDARD) == 0); // 13.2 Explicit conversions // // The following conversions are classified as explicit conversions: // // * All implicit conversions // * Explicit numeric conversions // * Explicit enumeration conversions // * Explicit reference conversions // * Explicit interface conversions // * Unboxing conversions // * Explicit type parameter conversions // * User-defined explicit conversions // * Explicit nullable conversions // * Lifted user-defined explicit conversions // // Explicit conversions can occur in cast expressions (14.6.6). // // The explicit conversions that are not implicit conversions are conversions that cannot be // proven always to succeed, conversions that are known possibly to lose information, and // conversions across domains of types sufficiently different to merit explicit notation. // The set of explicit conversions includes all implicit conversions. // Don't try user-defined conversions now because we'll try them again later. if (_binder.BindImplicitConversion(_exprSrc, _typeSrc, _exprTypeDest, _pDestinationTypeForLambdaErrorReporting, _needsExprDest, out _exprDest, _flags | CONVERTTYPE.ISEXPLICIT)) { return(true); } if (_typeSrc == null || _typeDest == null || _typeSrc.IsErrorType() || _typeDest.IsErrorType() || _typeDest.IsNeverSameType()) { return(false); } if (_typeDest.IsNullableType()) { // This is handled completely by BindImplicitConversion. return(false); } if (_typeSrc.IsNullableType()) { return(bindExplicitConversionFromNub()); } if (bindExplicitConversionFromArrayToIList()) { return(true); } // if we were casting an integral constant to another constant type, // then, if the constant were in range, then the above call would have succeeded. // But it failed, and so we know that the constant is not in range switch (_typeDest.GetTypeKind()) { default: VSFAIL("Bad type kind"); return(false); case TypeKind.TK_VoidType: return(false); // Can't convert to a method group or anon method. case TypeKind.TK_NullType: return(false); // Can never convert TO the null type. case TypeKind.TK_TypeParameterType: if (bindExplicitConversionToTypeVar()) { return(true); } break; case TypeKind.TK_ArrayType: if (bindExplicitConversionToArray(_typeDest.AsArrayType())) { return(true); } break; case TypeKind.TK_PointerType: if (bindExplicitConversionToPointer()) { return(true); } break; case TypeKind.TK_AggregateType: { AggCastResult result = bindExplicitConversionToAggregate(_typeDest.AsAggregateType()); if (result == AggCastResult.Success) { return(true); } if (result == AggCastResult.Abort) { return(false); } break; } } // No built-in conversion was found. Maybe a user-defined conversion? if (0 == (_flags & CONVERTTYPE.NOUDC)) { return(_binder.bindUserDefinedConversion(_exprSrc, _typeSrc, _typeDest, _needsExprDest, out _exprDest, false)); } return(false); }
//////////////////////////////////////////////////////////////////////////////// private bool ExactNullableInference(CType pSource, CType pDest) { // SPEC: Otherwise, if U is the CType U1? and V is the CType V1? // SPEC: then an exact inference is made from U to V. if (!pSource.IsNullableType() || !pDest.IsNullableType()) { return false; } ExactInference(pSource.AsNullableType().GetUnderlyingType(), pDest.AsNullableType().GetUnderlyingType()); return true; }
protected bool IsNullableValueType(CType pType) { if (pType.IsNullableType()) { CType pStrippedType = pType.StripNubs(); return pStrippedType.IsAggregateType() && pStrippedType.AsAggregateType().getAggregate().IsValueType(); } return false; }
// Check the constraints of any type arguments in the given Type. public static bool CheckConstraints(CSemanticChecker checker, ErrorHandling errHandling, CType type, CheckConstraintsFlags flags) { type = type.GetNakedType(false); if (type.IsNullableType()) { CType typeT = type.AsNullableType().GetAts(checker.GetErrorContext()); if (typeT != null) { type = typeT; } else { type = type.GetNakedType(true); } } if (!type.IsAggregateType()) { return(true); } AggregateType ats = type.AsAggregateType(); if (ats.GetTypeArgsAll().Count == 0) { // Common case: there are no type vars, so there are no constraints. ats.fConstraintsChecked = true; ats.fConstraintError = false; return(true); } if (ats.fConstraintsChecked) { // Already checked. if (!ats.fConstraintError || (flags & CheckConstraintsFlags.NoDupErrors) != 0) { // No errors or no need to report errors again. return(!ats.fConstraintError); } } TypeArray typeVars = ats.getAggregate().GetTypeVars(); TypeArray typeArgsThis = ats.GetTypeArgsThis(); TypeArray typeArgsAll = ats.GetTypeArgsAll(); Debug.Assert(typeVars.Count == typeArgsThis.Count); if (!ats.fConstraintsChecked) { ats.fConstraintsChecked = true; ats.fConstraintError = false; } // Check the outer type first. If CheckConstraintsFlags.Outer is not specified and the // outer type has already been checked then don't bother checking it. if (ats.outerType != null && ((flags & CheckConstraintsFlags.Outer) != 0 || !ats.outerType.fConstraintsChecked)) { CheckConstraints(checker, errHandling, ats.outerType, flags); ats.fConstraintError |= ats.outerType.fConstraintError; } if (typeVars.Count > 0) { ats.fConstraintError |= !CheckConstraintsCore(checker, errHandling, ats.getAggregate(), typeVars, typeArgsThis, typeArgsAll, null, (flags & CheckConstraintsFlags.NoErrors)); } // Now check type args themselves. for (int i = 0; i < typeArgsThis.Count; i++) { CType arg = typeArgsThis[i].GetNakedType(true); if (arg.IsAggregateType() && !arg.AsAggregateType().fConstraintsChecked) { CheckConstraints(checker, errHandling, arg.AsAggregateType(), flags | CheckConstraintsFlags.Outer); if (arg.AsAggregateType().fConstraintError) { ats.fConstraintError = true; } } } return(!ats.fConstraintError); }
// // SymbolLoader forwarders (end) ///////////////////////////////////////////////////////////////////////////////// // // Utility methods // protected ACCESSERROR CheckAccessCore(Symbol symCheck, AggregateType atsCheck, Symbol symWhere, CType typeThru) { Debug.Assert(symCheck != null); Debug.Assert(atsCheck == null || symCheck.parent == atsCheck.getAggregate()); Debug.Assert(typeThru == null || typeThru.IsAggregateType() || typeThru.IsTypeParameterType() || typeThru.IsArrayType() || typeThru.IsNullableType() || typeThru.IsErrorType()); switch (symCheck.GetAccess()) { default: throw Error.InternalCompilerError(); //return ACCESSERROR.ACCESSERROR_NOACCESS; case ACCESS.ACC_UNKNOWN: return(ACCESSERROR.ACCESSERROR_NOACCESS); case ACCESS.ACC_PUBLIC: return(ACCESSERROR.ACCESSERROR_NOERROR); case ACCESS.ACC_PRIVATE: case ACCESS.ACC_PROTECTED: if (symWhere == null) { return(ACCESSERROR.ACCESSERROR_NOACCESS); } break; case ACCESS.ACC_INTERNAL: case ACCESS.ACC_INTERNALPROTECTED: // Check internal, then protected. if (symWhere == null) { return(ACCESSERROR.ACCESSERROR_NOACCESS); } if (symWhere.SameAssemOrFriend(symCheck)) { return(ACCESSERROR.ACCESSERROR_NOERROR); } if (symCheck.GetAccess() == ACCESS.ACC_INTERNAL) { return(ACCESSERROR.ACCESSERROR_NOACCESS); } break; } // Should always have atsCheck for private and protected access check. // We currently don't need it since access doesn't respect instantiation. // We just use symWhere.parent.AsAggregateSymbol() instead. AggregateSymbol aggCheck = symCheck.parent.AsAggregateSymbol(); // Find the inner-most enclosing AggregateSymbol. AggregateSymbol aggWhere = null; for (Symbol symT = symWhere; symT != null; symT = symT.parent) { if (symT.IsAggregateSymbol()) { aggWhere = symT.AsAggregateSymbol(); break; } if (symT.IsAggregateDeclaration()) { aggWhere = symT.AsAggregateDeclaration().Agg(); break; } } if (aggWhere == null) { return(ACCESSERROR.ACCESSERROR_NOACCESS); } // First check for private access. for (AggregateSymbol agg = aggWhere; agg != null; agg = agg.GetOuterAgg()) { if (agg == aggCheck) { return(ACCESSERROR.ACCESSERROR_NOERROR); } } if (symCheck.GetAccess() == ACCESS.ACC_PRIVATE) { return(ACCESSERROR.ACCESSERROR_NOACCESS); } // Handle the protected case - which is the only real complicated one. Debug.Assert(symCheck.GetAccess() == ACCESS.ACC_PROTECTED || symCheck.GetAccess() == ACCESS.ACC_INTERNALPROTECTED); // Check if symCheck is in aggWhere or a base of aggWhere, // or in an outer agg of aggWhere or a base of an outer agg of aggWhere. AggregateType atsThru = null; if (typeThru != null && !symCheck.isStatic) { atsThru = SymbolLoader.GetAggTypeSym(typeThru); } // Look for aggCheck among the base classes of aggWhere and outer aggs. bool found = false; for (AggregateSymbol agg = aggWhere; agg != null; agg = agg.GetOuterAgg()) { Debug.Assert(agg != aggCheck); // We checked for this above. // Look for aggCheck among the base classes of agg. if (agg.FindBaseAgg(aggCheck)) { found = true; // aggCheck is a base class of agg. Check atsThru. // For non-static protected access to be legal, atsThru must be an instantiation of // agg or a CType derived from an instantiation of agg. In this case // all that matters is that agg is in the base AggregateSymbol chain of atsThru. The // actual AGGTYPESYMs involved don't matter. if (atsThru == null || atsThru.getAggregate().FindBaseAgg(agg)) { return(ACCESSERROR.ACCESSERROR_NOERROR); } } } // the CType in whice the method is being called has no relationship with the // CType on which the method is defined surely this is NOACCESS and not NOACCESSTHRU if (found == false) { return(ACCESSERROR.ACCESSERROR_NOACCESS); } return((atsThru == null) ? ACCESSERROR.ACCESSERROR_NOACCESS : ACCESSERROR.ACCESSERROR_NOACCESSTHRU); }
private static bool isEnumToDecimalConversion(CType argtype, CType desttype) { CType strippedArgType = argtype.IsNullableType() ? argtype.StripNubs() : argtype; CType strippedDestType = desttype.IsNullableType() ? desttype.StripNubs() : desttype; return strippedArgType.isEnumType() && strippedDestType.isPredefType(PredefinedType.PT_DECIMAL); }
private static bool CheckSingleConstraint(CSemanticChecker checker, ErrorHandling errHandling, Symbol symErr, TypeParameterType var, CType arg, TypeArray typeArgsCls, TypeArray typeArgsMeth, CheckConstraintsFlags flags) { bool fReportErrors = 0 == (flags & CheckConstraintsFlags.NoErrors); if (arg.IsOpenTypePlaceholderType()) { return(true); } if (arg.IsErrorType()) { // Error should have been reported previously. return(false); } if (checker.CheckBogus(arg)) { if (fReportErrors) { errHandling.ErrorRef(ErrorCode.ERR_BogusType, arg); } return(false); } if (arg.IsPointerType() || arg.isSpecialByRefType()) { if (fReportErrors) { errHandling.Error(ErrorCode.ERR_BadTypeArgument, arg); } return(false); } if (arg.isStaticClass()) { if (fReportErrors) { checker.ReportStaticClassError(null, arg, ErrorCode.ERR_GenericArgIsStaticClass); } return(false); } bool fError = false; if (var.HasRefConstraint() && !arg.IsRefType()) { if (fReportErrors) { errHandling.ErrorRef(ErrorCode.ERR_RefConstraintNotSatisfied, symErr, new ErrArgNoRef(var), arg); } fError = true; } TypeArray bnds = checker.GetSymbolLoader().GetTypeManager().SubstTypeArray(var.GetBounds(), typeArgsCls, typeArgsMeth); int itypeMin = 0; if (var.HasValConstraint()) { // If we have a type variable that is constrained to a value type, then we // want to check if its a nullable type, so that we can report the // constraint error below. In order to do this however, we need to check // that either the type arg is not a value type, or it is a nullable type. // // To check whether or not its a nullable type, we need to get the resolved // bound from the type argument and check against that. bool bIsValueType = arg.IsValType(); bool bIsNullable = arg.IsNullableType(); if (bIsValueType && arg.IsTypeParameterType()) { TypeArray pArgBnds = arg.AsTypeParameterType().GetBounds(); if (pArgBnds.Count > 0) { bIsNullable = pArgBnds[0].IsNullableType(); } } if (!bIsValueType || bIsNullable) { if (fReportErrors) { errHandling.ErrorRef(ErrorCode.ERR_ValConstraintNotSatisfied, symErr, new ErrArgNoRef(var), arg); } fError = true; } // Since FValCon() is set it is redundant to check System.ValueType as well. if (bnds.Count != 0 && bnds[0].isPredefType(PredefinedType.PT_VALUE)) { itypeMin = 1; } } for (int j = itypeMin; j < bnds.Count; j++) { CType typeBnd = bnds[j]; if (!SatisfiesBound(checker, arg, typeBnd)) { if (fReportErrors) { // The bound isn't satisfied because of a constraint type. Explain to the user why not. // There are 4 main cases, based on the type of the supplied type argument: // - reference type, or type parameter known to be a reference type // - nullable type, from which there is a boxing conversion to the constraint type(see below for details) // - type variable // - value type // These cases are broken out because: a) The sets of conversions which can be used // for constraint satisfaction is different based on the type argument supplied, // and b) Nullable is one funky type, and user's can use all the help they can get // when using it. ErrorCode error; if (arg.IsRefType()) { // A reference type can only satisfy bounds to types // to which they have an implicit reference conversion error = ErrorCode.ERR_GenericConstraintNotSatisfiedRefType; } else if (arg.IsNullableType() && checker.GetSymbolLoader().HasBaseConversion(arg.AsNullableType().GetUnderlyingType(), typeBnd)) // This is inlining FBoxingConv { // nullable types do not satisfy bounds to every type that they are boxable to // They only satisfy bounds of object and ValueType if (typeBnd.isPredefType(PredefinedType.PT_ENUM) || arg.AsNullableType().GetUnderlyingType() == typeBnd) { // Nullable types don't satisfy bounds of EnumType, or the underlying type of the enum // even though the conversion from Nullable to these types is a boxing conversion // This is a rare case, because these bounds can never be directly stated ... // These bounds can only occur when one type paramter is constrained to a second type parameter // and the second type parameter is instantiated with Enum or the underlying type of the first type // parameter error = ErrorCode.ERR_GenericConstraintNotSatisfiedNullableEnum; } else { // Nullable types don't satisfy the bounds of any interface type // even when there is a boxing conversion from the Nullable type to // the interface type. This will be a relatively common scenario // so we cal it out separately from the previous case. Debug.Assert(typeBnd.isInterfaceType()); error = ErrorCode.ERR_GenericConstraintNotSatisfiedNullableInterface; } } else if (arg.IsTypeParameterType()) { // Type variables can satisfy bounds through boxing and type variable conversions Debug.Assert(!arg.IsRefType()); error = ErrorCode.ERR_GenericConstraintNotSatisfiedTyVar; } else { // Value types can only satisfy bounds through boxing conversions. // Note that the exceptional case of Nullable types and boxing is handled above. error = ErrorCode.ERR_GenericConstraintNotSatisfiedValType; } errHandling.Error(error, new ErrArgRef(symErr), new ErrArg(typeBnd, ErrArgFlags.Unique), var, new ErrArgRef(arg, ErrArgFlags.Unique)); } fError = true; } } // Check the newable constraint. if (!var.HasNewConstraint() || arg.IsValType()) { return(!fError); } if (arg.isClassType()) { AggregateSymbol agg = arg.AsAggregateType().getAggregate(); // Due to late binding nature of IDE created symbols, the AggregateSymbol might not // have all the information necessary yet, if it is not fully bound. // by calling LookupAggMember, it will ensure that we will update all the // information necessary at least for the given method. checker.GetSymbolLoader().LookupAggMember(checker.GetNameManager().GetPredefName(PredefinedName.PN_CTOR), agg, symbmask_t.MASK_ALL); if (agg.HasPubNoArgCtor() && !agg.IsAbstract()) { return(!fError); } } else if (arg.IsTypeParameterType() && arg.AsTypeParameterType().HasNewConstraint()) { return(!fError); } if (fReportErrors) { errHandling.ErrorRef(ErrorCode.ERR_NewConstraintNotSatisfied, symErr, new ErrArgNoRef(var), arg); } return(false); }